danneh.org

Adding shipping costs to the cart automatically in Magento

by on Aug.11, 2010, under Magento, PHP

I’ve come across a handful of sites now that either do (or want to) automatically add a default shipping charge to the cart when it’s created or updated. As I had a bit of trouble finding a method for this, I thought I’d post my exact method here for usefulness and future reference.

In order to get this working, you need a module which hooks into the event ‘sales_quote_save_before’. We’ll use the namespace BTS.

First, create a module definition file; in app/etc/modules, create the file BTS_Checkout.xml:

<config>
	<modules>
		<BTS_Checkout>
			<active>true</active>
			<codePool>local</codePool>
		</BTS_Checkout>
	</modules>
</config>

Now we create our namespace. Create the following directory hierarchy:

app/code/local/BTS
app/code/local/BTS/Checkout
app/code/local/BTS/Checkout/etc
app/code/local/BTS/Checkout/Model

Under “app/code/local/BTS/Checkout/etc”, create a file “config.xml” with the following contents:

<?xml version="1.0"?>
<config>
	<modules>
		<BTS_Checkout>
			<version>0.0.1</version>
		</BTS_Checkout>
	</modules>
	<global>
		<models>
			<bts_checkout>
				<class>BTS_Checkout_Model</class>
			</bts_checkout>
		</models>
	</global>
	<frontend>
		<events>
			<checkout_cart_save_before>
				<observers>
					<bts_checkout_observer>
						<type>singleton</type>
						<class>bts_checkout/observer</class>
						<method>addShipping</method>
					</bts_checkout_observer>
				</observers>
			</checkout_cart_save_before>
		</events>
	</frontend>
</config>

Now under “app/code/local/BTS/Checkout/Model”, create a file “Observer.php” with the following contents:

class BTS_Checkout_Model_Observer {
 
	private $_shippingCode = 'royalmail';
	private $_country = 'GB';
 
	public function addShipping($params = null) {
		if (Mage::registry('checkout_addShipping')) {
			Mage::unregister('checkout_addShipping');
			return;
		}
		Mage::register('checkout_addShipping',true);
 
		$cart = Mage::getSingleton('checkout/cart');
		$quote = $cart->getQuote();
 
		if ($quote->getCouponCode() != '') {
			$c = Mage::getResourceModel('salesrule/rule_collection');
			$c->getSelect()->where("code=?", $quote->getCouponCode());
			foreach ($c->getItems() as $item) { $coupon = $item; }
 
			if ($coupon->getSimpleFreeShipping() > 0) {
				$quote->getShippingAddress()->setShippingMethod($this->_shippingCode)->save();
				return true;
			}
		}
 
		try {
			$method = $quote->getShippingAddress()->getShippingMethod();
			if ($method) return; // don't overwrite what's already there if we have one set already
 
			if ($quote->getShippingAddress()->getCountryId() == '') {
				$quote->getShippingAddress()->setCountryId($this->_country);
			}
 
			$quote->getShippingAddress()->setCollectShippingRates(true);
			$quote->getShippingAddress()->collectShippingRates();
 
			$rates = $quote->getShippingAddress()->getAllShippingRates();
			$allowed_rates = array();
			foreach ($rates as $rate) {
				array_push($allowed_rates,$rate->getCode());
			}
 
			if (!in_array($this->_shippingCode,$allowed_rates) && count($allowed_rates) > 0) {
				$shippingCode = $allowed_rates[0];
			}
 
            if (!empty($shippingCode)) {
                $address = Mage::getSingleton('checkout/session')->getQuote()->getShippingAddress();
                if ($address->getCountryId() == '') $address->setCountryId($this->_country);
                if ($address->getCity() == '') $address->setCity('');
                if ($address->getPostcode() == '') $address->setPostcode('');
                if ($address->getRegionId() == '') $address->setRegionId('');
                if ($address->getRegion() == '') $address->setRegion('');
                $address->setShippingMethod($this->_shippingCode)->setCollectShippingRates(true);
                Mage::getSingleton('checkout/session')->getQuote()->save();
            } else {
                $address = Mage::getSingleton('checkout/session')->getQuote()->getShippingAddress();
                if ($address->getCountryId() == '') $address->setCountryId($this->_country);
                if ($address->getCity() == '') $address->setCity('');
                if ($address->getPostcode() == '') $address->setPostcode('');
                if ($address->getRegionId() == '') $address->setRegionId('');
                if ($address->getRegion() == '') $address->setRegion('');
                $address->setShippingMethod($this->_shippingCode)->setCollectShippingRates(true);
                Mage::getSingleton('checkout/session')->getQuote()->save();
            }
 
            Mage::getSingleton('checkout/session')->resetCheckout();
 
		}
		catch (Mage_Core_Exception $e) {
			Mage::getSingleton('checkout/session')->addError($e->getMessage());
		}
		catch (Exception $e) {
			Mage::getSingleton('checkout/session')->addException($e, Mage::helper('checkout')->__('Load customer quote error'));
		}
 
 
	}
 
	public function getQuote() {
        if (empty($this->_quote)) {
            $this->_quote = Mage::getSingleton('checkout/session')->getQuote();
        }
        return $this->_quote;
    }
 
}

Note at the top of this file 2 private variables. Set $_country to the default ISO code for your shipping method, and set $_shippingCode to the default/preferred shipping method’s code internally to Magento (fedex/ups/usps etc..). In this instance it’s ‘royalmail’, as I’m also using Meanbee’s Royal Mail module to auto-calculate shipping based on destination country and total weight of cart.

Various other choices could be (I’ll fill this out as and when I experiment with other methods in this code):

  • flatrate_flatrate: Selected flatrate shipping method

Save all this then add an item to your cart. A shipping method should appear in the cart’s cost breakdown at the bottom with the relevant shipping costs included.

Updated 27th September 2011: I’ve updated this article based on feedback relating to the code not working correctly, or preventing coupons from being applied.

Updated 8th January 2012: Thanks to Toby Lerone for pointing out the error with the XML causing the cart to not update on first load (after the method got added to the cart in the first instance). Changing the event from sales_quote_save_before to checkout_cart_save_before apparently solves that problem (I’ll remove the “apparently” from that statement when I can independently verify it). I’ve updated the code above though to take this into account.

:, , , ,

45 Comments for this entry

  • dpcdpc11

    thanks for this tutorial man! I’ve been looking for this all night, now it’s morning! but it was all worth it!
    Can I contact you regarding Magento and stuff?

    thanks again,
    Cristian

  • Dan

    Sure thing. Use the contact form to send me an email of what you need and we can discuss next steps forward.

    Dan

  • AliB

    This just saved my life. Thanks man.

  • Florian

    the solution doesnt show me any shipping costs in cart :( i dont know why. can anybody help? how i can find out the shippingCode for tablerates?

  • Dan

    It probably doesn’t work because the shipping method code and country code defined at the top of the Observer.php attempt to instantiate a shipping method which fails to pass validation and apply to the cart successfully (i.e; instantiate a shipping method that would otherwise not be available during the checkout process).

    If you simply have table rates, then you’ll need to apply another method to do this (not listed here) to loop through a list of available rates, validate them then apply whichever one passes. It’s a lot more complex operation.

  • Manuel

    Thanks, works great! I like! :-)

  • razvantim

    Great. Worked on magento 1.5.0.1. Thanks you.

  • George

    Hi,

    Many thanks for the great post, saved me a lot of trouble, but I have a small problem if you have any ideas what it might be: the delivery cost in the totals block of the cart appears only if you refresh the page, not from the first time when you add a product to the cart.

    Any ideas will be greately appreciated :)

    Thank you.

  • Dan

    I’ve come across this one myself. The problem is, when I wrote them snippets in this post, I couldn’t replicate it, and it appeared in the cart the first load every time. But I have seen this, and can’t quite understand why. The code hooks into the order model just before it saves, so when the order’s pulled back by the cart, it pulls it back from the database – *after* it’s saved. But for some reason it seems to ignore it, or bizarrely save it after the cart loads. I really can’t work it out.

    I have a question though. When you add a product to your cart, does it take your straight to the shopping cart, or do you have it take you back to where you came from (i.e; the product view page)?

    Dan

  • Crystal

    Maybe I am just really tired, but this isn’t working for me. I have followed the steps but when I add the item to the cart, instead of seeing the shipping amount in the cart- I see the Observer.php code in the header. I’m on v. 1.5 community

    Any ideas?

    Thanks!

  • Dan

    Erm, let me just take this opportunity to exclaim: Wtf?! That just..shouldn’t happen. It suggests some kind of encoding issue perhaps. Are you sure the opening <?php tag at the top of the file is in tact, with no odd characters (or any characters at all) before it? I can't say I've ever come across this before, let alone in Magento. Bad file encoding, a malformed opening php tag, or no tag at all, perhaps. That’s all I can think of!

    Hope this helps.

    Dan

  • Crystal

    I’m an idiot lol. I had an extra character in my opening tag. That’s what I get for trying to do this on no sleep. Thanks for the tutorial! You sir are awesome :)

  • George

    @Dan,

    Sorry for the delay, no, it takes me straight to the cart page, not back to the product view page. I didn’t figure this one yet, still trying different scenarios maybe I can come up with something useful…

  • Michael

    Ok, I’m not getting any errors – but there is no change. What code do I put for ‘Flat Rate’?

    Thanks

  • johnboy

    I have implemented this, it works well apart from it seems to have had a knock on effect in preventing coupon codes from being added.

    If I remove this module from the code/local file then a coupon will add fine, but when i add it back in then the coupon constantly returns ‘Cannot apply the coupon code.’

    Anyone got any ideas?

  • Dan

    Hi,

    Michael; Use “flatrate_flatrate” in the $_shippingCode variable. I’ve updated the post with this information, and I’ll put other methods that I use and find that work well in the same list.

    Johnboy; I’ve resolved the problem you were having with the coupons. I think the database layouts/structure has changed since I posted this originally. The “coupon_code” in the select statement was causing it to fall over with an “unknown column”. If you swap out the coupon code if statement with the one updated above (or just -repaste the code from above into Observer.php), your coupon woes should vanish. This only broke I think because I got a bit lazy with the coupon checking for free shipping by talking almost directly with the database than via Magento’s API. Maybe I’ll re-write it completely one day, but for now, this works just as well :)

    Hope this helps.

    Dan

  • C4rter

    Why’d you make the if statement “if (!empty($shippingCode)) {“?
    It makes no sense, you just duplicate the code in the if/else statement.

  • Dan

    There was a reason for that, in an early iteration of that code. I think it was to preserve the shipping method of the quote if the customer had been through checkout, and gone back to the cart. The else{} part of the statement used to do something else. I guess I changed it at some point (probably before publishing), and never noticed that the code between the 2 conditionals is actually now the same!

    Well spotted :)

  • magento

    working perfectly on local host….
    bt on server itz showing error>>>>>>>

    y???????????????

  • Dan

    you’ll need to help out a bit here by telling me what the error message you’re receiving is. otherwise i have no clue how to start helping you.

  • magento

    when i click on add to cart.. itz showing an error page saying “there is some error processing your request”

  • magento

    hey itz solved….
    actually i made a silly mistake in .php file

  • sohail

    Work a treat for me. thanks.

    I have another concern besides this, Hope you would help.
    I am new to Magento, and I mostly stuck in finding the right functions to do some operations, the module structures etc. Is there some online resource where I can learn Magento Module Development.
    And another thing I am looking for is Class Reference Guide of magento classes.
    Can you provide me these few things ?

  • Simon

    Hi, thank a lot for your code.
    So, what if I am using flat rate?
    I also have Webshopapps Matrixrate but can’t find out what the name code for this is? any clue?

    thanks again for the contribution

  • Lars

    Works great. How ever can it add shipping costs to the sidebar cart aswell? And can it also handle “free shipping over XX”?

  • Toby Lerone

    NOT UPDATING FIRST TIME
    ———————–
    @Dan or anyone else having problems with the cart totals not updating the first time, I have the solution!

    Change the observer from “sales_quote_save_before” to “checkout_cart_save_before”.

    This observer is only called on the cart page.

    If you need it to calculate costings on emails and other sections of the site then use observer “sales_quote_item_collection_products_after_load”.

    Free Shipping
    =============
    @Lars get in touch and I have now also modified to include two delivery options, one of which is free delivery!

    Cheers

  • Simon

    Hi! I’m very interested in Lars’ idea too. Specially the shipping cost in sidebar cart and including the free shipping.

    Also would like to know if table-rates is possible to use and if, what code to use. I tried ‘tablerates’ and ‘table_rates’ but doesn’t seem to work…

    thank you!

  • Toby Lerone

    Hello, I can’t post code in the comment box, but by all means send me your email. Or the site owner contact me to get the code….?

    Cheers

  • Marc

    Hi Toby, i’am interested in the 2 delivery options with free delivery to. Could you post how you did it?

    @Simon, the shipping code for table rates is “tablerate_bestway”. Easy way to find out the shipping codes is to inspect the shipping element in the checkout and look for the id, thats the shipping code.
    Tried it myself with table rates, it works.

  • Simon

    Thanks Marc, gonna give it a try and let you know.

  • Simon

    Hi, nothing happened
    Is there a way to know if the module has been correctly inserted or if it is there? because I went to System/Configuration/Advanced but cannot see the module there. I’ve had some problems with other modules, that I had to put in core files because it wouldn’t work in local…

  • Simon

    Sorry, was looking at wrong place. It’s there, but can’t see changes in sidebar cart. Maybe my custom theme (it’s based on modern)?

  • Toby Lerone

    Hello Simon. You need to ensure that the template/tax/shipping.phtml template is being included in your sidebar template. This outputs the shipping estimate. Cheers

  • Lars

    @Toby. Where can I find info about your free delivery solution?

  • Lars

    @Toby: You can mail me at lars at abusiness dot dk

  • Nick

    Hello,

    Great help and its almost perfect for me but I have a problem in getting it working with WebShopApps Shipping Matrix rates extension when adding a configurable product to the cart, sometimes it will be added sometimes not. I have changed the id to ‘matrixrate_matrixrate_7’. Simple products are fine it’s just the configurable ones. Has anyone else tried this or can any one offer an alternative solution for me.
    I need to have table rates for: destination > weight with free shipping over a certain amount of UK as well as free collect from store. Unfortunately I can’t seem to get the free shipping to work (with the standard table rates and a promotion code) with the collect from store because as soon as the value of the free shipping is reached you cant select the collection option?
    Thanks

  • nikl

    While the module is being shown as active in my backend, nothing is shown in my cart. I copied everything as described but no luck.
    I’m on Magento 1.5.1.0

  • nikl

    Ok I think I know what my problem was. I have tablerates configured as my top priority method. Even though I am also using flatrate for some products, entering “flatrate_flatrate” wouldn’t work. I changed it to “tablerate_bestway” and it is working! The only thing I have found out sofar is that, freeshipping will only be displayed after I refresh the cart. I have a cartrule that applies freeshipping when ordering for more than 100€, so I am not using the freeeship method – maybe this is the problem?

  • nikl

    No, still not working. I accidently enabled PRWD Atoshippig. No luck with this code however…

  • Dmitry

    Please see modified version of this plugin here http://stackprogramer.com/7276474/calculate-shipping-on-cart-page. Works with Magento 1.6.1 version.

  • noel

    hi, i have using shippingTablerate extension, is this code possible to apply? and id possible, what $_shippingCode im going to put this variable.

    Thanks in advance!

  • Christopher

    Hey,

    why saving the quote inside the observer? We are right before saving the quote anyway, since we are hooking in with “sales_quote_save_before”…

    The additional save will just call the observer again.

    It seems that leaving out the extra save also takes care of the “no shipping cost shown in the cart”-Bug. I had that one, too.

  • Christopher

    Addition: the code is not using the event “sales_quote_save_before”, but “checkout_cart_save_before”. Anyway, Mage_Checkout_Model_Cart::save() is calling Mage_Sales_Model_Quote::save().

  • Sam

    Hi,
    I created this module and it shows “enable” in Configuration -> Advance. But nothing happened to my cart. NO shipping display no Error.
    I am using Magento 1.5. Please somebody help.

    Thanks

  • Rob

    Did anyone figure out the first load problem? I tried Lars’ solution to no avail. I’m using Magento v1.69.

    And many thanks to the poster! You saved my week!

Leave a Reply

 

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!

Blogroll

A few highly recommended websites...