Adding shipping costs to the cart automatically in Magento

Advertisements

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:


	
		
			true
			local
		
	

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:



	
		
			0.0.1
		
	
	
		
			
				BTS_Checkout_Model
			
		
	
	
		
			
				
					
						singleton
						bts_checkout/observer
						addShipping
					
				
			
		
	

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 September 2011: I’ve updated this article based on feedback relating to the code not working correctly, or preventing coupons from being applied.

Updated 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.

Updated August 2017: Thanks for Sam for confirming that the above still works in Magento 1.9.

Join the Conversation

62 Comments

  1. Hi Mate

    Thanks for the nice post.
    I’m using Magento CE 1.6.1.

    I have two customer groups agents(group id 4), retailers(group id 3).

    I have two shipping methods
    1. Free shipping when subtotal >= $300.
    2. Flat Rate when subtotal < $300.
    By default they will be selected based on subtotal of cart.

    The above case is working well for retailers.
    but To agents I want to display both the shipping options when subtotal is < $300. it is not there in magento.

    Could you please tell me how to achieve this thing.

    Thanks in advance.

  2. Thanks so much, I had trouble initially as I didn’t know the correct shipping code for ‘Table Rates’ after a little digging I found that it was ‘tablerate_bestway’
    then after entering that value the module worked like a charm on Magento community Edition 1.7
    I owe you man!!!!

  3. Wow, this really is the first extension that actually works for me! I’m using this for a Magento CE 1.7.0.2 installation and my table rates [private $_shippingCode = ‘tablerate_bestway’;].

    It always shows the correct shipping costs in cart automatically. On first load, after adding cross sell items and when deleting items from cart. Absolutely flawless…

    @Dan Thank you, keep up the good work!!!

  4. i create all files but when i open my cart page there is nothing dipslaying, confusing please help!!!!!!
    how to debug this observer class , how i will call this observer class in cart page ??????

  5. Thankyou – as others have else said, worked first time with no issues in Magento CE 1.7.0.2 – thanks for continuing to update this page.

    Neil

Leave a comment

Your email address will not be published. Required fields are marked *