Adding shipping costs to the cart automatically in Magento
by Dan 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.
November 10th, 2010 on 5:28 am
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
November 10th, 2010 on 9:47 am
Sure thing. Use the contact form to send me an email of what you need and we can discuss next steps forward.
Dan
December 20th, 2010 on 9:43 pm
This just saved my life. Thanks man.
February 10th, 2011 on 9:44 am
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?
February 10th, 2011 on 7:08 pm
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.
March 18th, 2011 on 8:19 am
Thanks, works great! I like!
July 18th, 2011 on 8:30 am
Great. Worked on magento 1.5.0.1. Thanks you.
September 7th, 2011 on 9:33 am
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.
September 8th, 2011 on 8:51 pm
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
September 9th, 2011 on 8:45 pm
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!
September 9th, 2011 on 8:52 pm
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
September 9th, 2011 on 9:17 pm
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
September 12th, 2011 on 12:20 pm
@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…
September 26th, 2011 on 10:09 pm
Ok, I’m not getting any errors – but there is no change. What code do I put for ‘Flat Rate’?
Thanks
September 27th, 2011 on 4:15 pm
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?
September 27th, 2011 on 7:22 pm
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
October 4th, 2011 on 9:16 am
Why’d you make the if statement “if (!empty($shippingCode)) {“?
It makes no sense, you just duplicate the code in the if/else statement.
October 5th, 2011 on 6:11 pm
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
November 18th, 2011 on 11:47 am
working perfectly on local host….
bt on server itz showing error>>>>>>>
y???????????????
November 18th, 2011 on 1:06 pm
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.
November 21st, 2011 on 4:29 am
when i click on add to cart.. itz showing an error page saying “there is some error processing your request”
November 21st, 2011 on 5:44 am
hey itz solved….
actually i made a silly mistake in .php file
December 21st, 2011 on 5:41 am
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 ?
January 1st, 2012 on 7:26 pm
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
January 4th, 2012 on 10:17 pm
Works great. How ever can it add shipping costs to the sidebar cart aswell? And can it also handle “free shipping over XX”?
January 5th, 2012 on 2:14 pm
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
January 5th, 2012 on 11:10 pm
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!
January 6th, 2012 on 10:14 am
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
January 16th, 2012 on 3:57 pm
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.
January 22nd, 2012 on 9:25 pm
Thanks Marc, gonna give it a try and let you know.
January 23rd, 2012 on 4:06 pm
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…
January 23rd, 2012 on 4:19 pm
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)?
January 30th, 2012 on 3:42 pm
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
March 11th, 2012 on 3:31 pm
@Toby. Where can I find info about your free delivery solution?
March 11th, 2012 on 5:23 pm
@Toby: You can mail me at lars at abusiness dot dk
March 13th, 2012 on 6:43 pm
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
March 14th, 2012 on 5:07 pm
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
March 14th, 2012 on 5:40 pm
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?
March 15th, 2012 on 10:30 am
No, still not working. I accidently enabled PRWD Atoshippig. No luck with this code however…
March 28th, 2012 on 2:11 pm
Please see modified version of this plugin here http://stackprogramer.com/7276474/calculate-shipping-on-cart-page. Works with Magento 1.6.1 version.
April 11th, 2012 on 6:41 am
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!
May 3rd, 2012 on 1:17 pm
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.
May 3rd, 2012 on 1:23 pm
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().
May 6th, 2012 on 1:49 pm
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
May 16th, 2012 on 4:06 pm
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!