Shipping Method Dropdown and Set Default Shipping Method on Cart Page

In this tutorial we will see how to show a shipping method drop down in the cart totals and how to set a default shipping method in cart page.

There are many ways to do this but i am sharing with you the best method i know of.
Attached is screenshot of the module output

Shipping Dropdown Cart Page

Shipping Dropdown Cart Page

Attached is source code of this module
[dm]29[/dm]
Lets see step by step the explanation of this module. Also name of the module currently used is Excellence_Shipdrop

Step1: Adding dropdown to cart page

The first step in this module is to add a dropdown in place of the default magento shipping total display. To best way to do this in magento is as follows, in your config.xml file

<global>
<sales>
            <quote>
                <totals>
                    <shipping>
                    	<class>shipdrop/sales_quote_address_total_shipping</class>
                        <renderer>shipdrop/checkout_total_shipping</renderer>
                    </shipping>
                </totals>
            </quote>
        </sales>
</global>

the main thing to notice here is the value of <renderer> tag here, i have put here path to a block class. Using the <renderer> tag we can specify which block class magento should use to display the shipping cart totals. The code inside the block class is

<?php
class Excellence_Shipdrop_Block_Checkout_Total_Shipping extends Mage_Checkout_Block_Total_Default
{
	protected $_template = 'shipdrop/checkout/total/shipping.phtml';
}

The code is simple here, just specifies the path of the template file. In the template file shipdrop/checkout/total/shipping.phtml the code is

    <tr>
        <th colspan="2" style="<?php echo $this->getTotal()->getStyle() ?>" class="a-right">
           <?php
				$block = $this->getLayout()->createBlock('checkout/cart_shipping');
				$block->setTemplate('shipdrop/checkout/total/shipping/dropdown.phtml');
				echo $block->_toHtml(); 
			?>
        </th>
    </tr>

and in file shipdrop/checkout/total/shipping/dropdown.phtml the code is

<?php if (($_shippingRateGroups = $this->getEstimateRates())): ?>
<form id="co-shipping-method-form-drop" action="<?php echo $this->getUrl('checkout/cart/estimateUpdatePost') ?>">
	<select id='drop_estimate_method' name="estimate_method">
		<option value='-1'>Please Select Shipping Method</option>
		<?php 
		foreach ($_shippingRateGroups as $code => $_rates) { 
		$str = $this->getCarrierName($code);
		foreach ($_rates as $_rate){ 
			$str2 = $str . ' - ' . $_rate->getMethodTitle();
			
			$_excl = $this->getShippingPrice($_rate->getPrice(), $this->helper('tax')->displayShippingPriceIncludingTax());
			$_incl = $this->getShippingPrice($_rate->getPrice(), true);
			$str2 .= ' ['. $_excl;
			if ($this->helper('tax')->displayShippingBothPrices() && $_incl != $_excl) { 
				$str2 .= ' '. $this->__('Incl. Tax'); echo $_incl;
			}
			$str2 .= '] ';
		?>
		<option <?php if($_rate->getCode()===$this->getAddressShippingMethod()) echo ' selected="selected"' ?> value="<?php echo $this->htmlEscape($_rate->getCode()) ?>"><?php echo $str2;?></option>
		<?php } 
		} ?>
	</select>
</form>
<script type="text/javascript">
	document.observe("dom:loaded", function() {
		$('drop_estimate_method').observe('change',function(){
			if($('drop_estimate_method').getValue() == -1){
				alert('Please Select Shipping Method');
			}else{
				$('co-shipping-method-form-drop').submit();
			}
		});
	});
</script>
<?php endif; ?>

The above code generates the dropdown. Next we also need to make changes to the shipping totals collector class, because by default if shipping amount is zero no totals show up. So in the collector class we need to make a change to show totals even when shipping amount is zero. So the class which we mentioned in the config.xml file shipdrop/sales_quote_address_total_shipping we add this code

<?php
class Excellence_Shipdrop_Model_Sales_Quote_Address_Total_Shipping extends Mage_Sales_Model_Quote_Address_Total_Shipping{
	public function fetch(Mage_Sales_Model_Quote_Address $address)
	{
		$amount = $address->getShippingAmount();
		$title = Mage::helper('sales')->__('Shipping & Handling');
		if ($address->getShippingDescription()) {
			$title .= ' (' . $address->getShippingDescription() . ')';
		}
		$address->addTotal(array(
				'code' => $this->getCode(),
				'title' => $title,
				'value' => $address->getShippingAmount()
		));
		return $this;
	}
}

After doing this the shipping method dropdown should show up in totals. But now there is one more problem we need to fix, when a product is first added to shopping cart there no country select by user hence no shipping method will show up. So we need to add a code select the shipping country by default.

Step2: Select shipping country by default and shipping method

To do this we will add code in the events as below

<events>
    		<controller_action_predispatch_checkout_cart_index >
    			<observers>
    				<preIndex>
    					<class>shipdrop/observer</class>
                        <method>setShipping</method>
    				</preIndex>
    			</observers>
    		</controller_action_predispatch_checkout_cart_index>
    	</events>

So this event is execute before the indexAction() of CartController is called. In the setShipping() method we will put in this code to select a default country

<?php
class Excellence_Shipdrop_Model_Observer {
	public function setShipping($evt){
		$controller = $evt->getControllerAction();
		if(!Mage::getSingleton('checkout/type_onepage')->getQuote()->getShippingAddress()->getCountryId()  && Mage::getSingleton('checkout/type_onepage')->getQuote()->getItemsCount()){
			$country_id = 'IN';
			$region_id = false;
			$country = Mage::getModel('directory/country')->loadByCode($country_id);
			$regions = $country->getRegions();
			if(sizeof($regions) > 0){
				$region = $regions->getFirstItem();
				$region_id = $region->getId();
			}
			$customerSession=Mage::getSingleton("customer/session");
			if($customerSession->isLoggedIn()){
				$customerAddress=$customerSession->getCustomer()->getDefaultShippingAddress();
				if($customerAddress->getId()){
					$customerCountry=$customerAddress->getCountryId();
					$region_id = $customerAddress->getRegionId();
					$region = $customerAddress->getRegion();
					$quote = Mage::getSingleton('checkout/type_onepage')->getQuote();
					$shipping = $quote->getShippingAddress();
					$shipping->setCountryId($customerCountry);
					if($region_id){
						$shipping->setRegionId($region_id);
					}
					if($region){
						$shipping->setRegion($region);
					}
					$quote->save();
					$controller->getResponse()->setRedirect(Mage::getUrl('*/*/*',array('_current'=>true)));
				}else{
					$quote = Mage::getSingleton('checkout/type_onepage')->getQuote();
					$shipping = $quote->getShippingAddress();
					$shipping->setCountryId($country_id);
					if($region_id){
						$shipping->setRegionId($region_id);
					}
					$quote->save();
					$controller->getResponse()->setRedirect(Mage::getUrl('*/*/*',array('_current'=>true)));
				}
			}else{
				$quote = Mage::getSingleton('checkout/type_onepage')->getQuote();
				$shipping = $quote->getShippingAddress();
				$shipping->setCountryId($country_id);
				if($region_id){
					$shipping->setRegionId($region_id);
				}
				$quote->save();
				$controller->getResponse()->setRedirect(Mage::getUrl('*/*/*',array('_current'=>true)));
			}
		}

	}
}

The above code can also be easily extended to set a default shipping method as well. Just add a single line of code before $quote->save()

$quote->setShippingMethod('flatrate_flatrate');
$quote->save();