Magento: Free shipping for registered customers in combination with OneStepCheckout

Tweet about this on TwitterShare on LinkedInShare on Google+Share on FacebookEmail this to someone

In my last project, I had to deal with the following task: All registered customers and all guest customers who tick the “Create an account for later use” option on the checkout page to get free shipping in combination with the OneStepCheckout extension of Magento. This was the first time I had to manage a task of the kind, and in situations like this, I first decided to ask Google for help.

And I came across this page: Free shipping only for registered users where Srdjan Bajic had provided a solution. As I mentioned at the start, this should work with OneStepCheckout and now I am going to show you how I implemented it in OneStepCheckout.

Basically, I used a great part of Srdjan’s solution, but some “spicing up” was necessary in order to suit the specific workflow of OneStepCheckout. Below I will elaborate on some of the differences between OneStepCheckout and the standard checkout method in Magento (One page checkout).

OneStepCheckout:

All checkout steps are clearly outlined. Depending on the extension configuration, we have AJAX requests on choosing country, entering city, zip/post code and etc. So in this request we send the address data and in response we get the available shipping and payment methods. We also do AJAX requests when we choose shipping method, payment method, when coupon is applied etc. Usually, after each AJAX request, the totals are updated.

The standard Magento checkout page (Onepage checkout):

In the checkout process, only one step is visible at any given moment. We have AJAX requests when we fill all required information and click the “Continue” button.

In my case, I need to perform an AJAX request when “Create an account for later use” is clicked, so I have created a custom extension and I am going to explain how it works:

You can download the extension from here: https://github.com/ceckoslab/OneStepCheckout—Registered-Free-Shipping

I have created a JavaScript code that observes the click event of “Create an account for later use” checkbox and triggers the AJAX request:

Event.observe(window, 'load', function() {
    var registerCheckbox = $('id_create_account');

    if(registerCheckbox != undefined) {
        Event.observe('id_create_account', 'click', function(event) {
            var shippingMethodsBlock = $$('.onestepcheckout-shipping-method-block .shipment-methods')[0];

            var element = Event.element(event);

            var param = '';
            if(element.checked) {
                param = 'register/1';
            } else {
                param = 'register/0';
            }

            var url = '/oscregisteredfreeshipping/ajax/update/' + param;

            shippingMethodsBlock.update('
 
'); new Ajax.Request(url, { method: 'get', onSuccess: function(transport) { if(transport.status == 200) { var data = transport.responseText.evalJSON(); shippingMethodsBlock.update(data.shipping); $$('dl.shipment-methods input').invoke('observe', 'click', get_separate_save_methods_function('/onestepcheckout/ajax/set_methods_separate', true)); } } }); }); } });

Also, when I get the response, I update the shipping section, so it is necessary to once again bind the click event of all option inputs in this section:

...
$$('dl.shipment-methods input').invoke('observe', 'click', get_separate_save_methods_function('/onestepcheckout/ajax/set_methods_separate', true));
...

Depending on the state of the “Create an account for later use” checkbox (not checked / checked) we do an AJAX GET request and we pass the “register” parameter with a value of either 0 or 1.

class CeckosLab_OscRegisteredFreeShipping_AjaxController extends Mage_Core_Controller_Front_Action
{
    /**
     * Get one page checkout model
     *
     * @return Mage_Checkout_Model_Type_Onepage
     */
    public function getOnepage()
    {
        return Mage::getSingleton('checkout/type_onepage');
    }

    /**
     * Update shopping cart data action
     */
    public function updateAction()
    {
        $registerState = $this->getRequest()->getParam('register');

        $customerSession = Mage::getSingleton('customer/session');
        $freeshippingSession = Mage::getSingleton('oscregisteredfreeshipping/session');


        if (!$customerSession->isLoggedIn() && $registerState == 1) {
            $freeshippingSession->setData('will_register',true);
        } else {
            $freeshippingSession->setData('will_register',false);
        }

        $this->getOnepage()->getQuote()->getShippingAddress()->setCollectShippingRates(true);
        $this->getOnepage()->getQuote()->collectTotals()->save();

        // Add updated shipping HTML to the output
        $shippingHtml = $this->getLayout()
            ->createBlock('checkout/onepage_shipping_method_available')
            ->setTemplate('onestepcheckout/shipping_method.phtml')
            ->toHtml();

        $response = array();
        $response['shipping'] = $shippingHtml;

        $this->getResponse()->setBody(Zend_Json::encode($response));
    }

}

I use the session to pass this argument to the Free Shipping method:

class CeckosLab_OscRegisteredFreeShipping_Model_Session extends Mage_Core_Model_Session_Abstract
{

    public function __construct()
    {
        $namespace = 'oscregisteredfreeshipping';
        $namespace .= '_' . (Mage::app()->getStore()->getWebsite()->getCode());

        $this->init($namespace);
        Mage::dispatchEvent('oscregisteredfreeshipping_session_init', array('oscregisteredfreeshipping_session'=>$this));
    }

}

Then I check if the customer is logged in or is about to register:

class CeckosLab_OscRegisteredFreeShipping_Model_Carrier_Freeshipping extends Mage_Shipping_Model_Carrier_Freeshipping
{
    /**
     * FreeShipping Rates Collector
     *
     * @param Mage_Shipping_Model_Rate_Request $request
     * @return Mage_Shipping_Model_Rate_Result
     */
    public function collectRates(Mage_Shipping_Model_Rate_Request $request)
    {
        if (!$this->getConfigFlag('active')) {
            return false;
        }

        $result = Mage::getModel('shipping/rate_result');

        $this->_updateFreeMethodQuote($request);

        $session = Mage::getSingleton('customer/session');
        $freeshippingSession =  Mage::getSingleton('oscregisteredfreeshipping/session');

        $allow = ($request->getFreeShipping())
            || ($freeshippingSession->getWillRegister())
            || ($session->isLoggedIn());

        if ($allow) {
            $method = Mage::getModel('shipping/rate_result_method');

            $method->setCarrier('freeshipping');
            $method->setCarrierTitle($this->getConfigData('title'));

            $method->setMethod('freeshipping');
            $method->setMethodTitle($this->getConfigData('name'));

            $method->setPrice('0.00');
            $method->setCost('0.00');

            $result->append($method);
        }

        return $result;
    }
    
}

It is worth mentioning that I am rewriting the Free Shipping model and inserting the logic to detect if the customer is registered or will be registered after a successful checkout.

I have also discovered that it is necessary to clear the session variable that I set in AjaxController on each hit of Idev_OneStepCheckout_IndexController indexAction() and to update the available shipping methods. I observe the controller_action_predispatch_onestepcheckout_index_index event and here is my observer code:

class CeckosLab_OscRegisteredFreeShipping_Model_Observer
{
    public function clearSession(Varien_Event_Observer $observer) {

        $quote = Mage::getSingleton('checkout/type_onepage')->getQuote();

        $freeshippingSession = Mage::getSingleton('oscregisteredfreeshipping/session');
        $freeshippingSession->setData('will_register',false);

        $quote->getShippingAddress()->setCollectShippingRates(true);
        $quote->collectTotals()->save();

        return $this;
    }
}

My client also requested that only the free shipping method be visible, if the free shipping method is listed in the available shipping methods. Here is an example of how the shipping template file should be modified:

<?php if (!($_shippingRateGroups = $this->getShippingRates())): ?>
    <?php echo $this->__('Sorry, no quotes are available for this order at this time.') ?>
<?php else: ?>
    <?php if (count($_shippingRateGroups)=='1' && Mage::getStoreConfig('onestepcheckout/general/hide_shipping_method')):?>
        
    <?php else: ?>
        <?php
            if (array_key_exists('freeshipping', $_shippingRateGroups)) {
                $_shippingRateGroups = array(
                    'freeshipping' => $_shippingRateGroups['freeshipping']
                );
            }

            $methodsCount = count($_shippingRateGroups);
        ?>
        
<?php foreach ($_shippingRateGroups as $code => $_rates): ?>
<?php echo $this->getCarrierName($code) ?>
<?php foreach ($_rates as $_rate): ?>
<?php if ($_rate->getErrorMessage()): ?>
    • <?php echo $_rate->getErrorMessage() ?>
<?php else: ?> getCode()===$this->getAddressShippingMethod()) echo ' checked="checked"' ?> /> <?php endif ?>
<?php endforeach; ?> <?php endforeach; ?>
<?php endif; ?> <?php endif; ?>

That’s it basically and it works in combination with the free shipping method that we get out of the box in Magento. Do ensure that the free shipping method is activated if you want this to work. I guess that you are going to like the solution and I will be happy if you share a better approach to this task.

I see some room for improvement in this extension:

  • Adding some dependencies rules, if the Free Shipping method and OneStepCheckout are activated
  • Currently I am using the following URL: “/oscregisteredfreeshipping/ajax/update/” for the AJAX request but depending on the server configuration, we may encounter situations where the URL needs to be changed to “/index.php/oscregisteredfreeshipping/ajax/update/”, so I need to take care of this.
  • I have found out that if the page is not fully loaded and I tick the “Create an account for later use” checkbox, the extension doesn’t work as expected.

Thanks again to Srdjan Bajic and I hope that you’ve enjoyed this article.

Tweet about this on TwitterShare on LinkedInShare on Google+Share on FacebookEmail this to someone

Tsvetan Stoychev

Tsvetan aka. Cecko is the founder of Cecko's Lab. He is Magento addicted since Magento CE 1.2.1.2 and has worked on over 30 Magento projects. At the moment he is in charge to take care about the money flow of the company, to keep constant communication with the clients and to keep the people in the office busy.

More Posts

Follow Me:
TwitterLinkedIn

  • Anonymous

    You couldn’t just make the free shipping extension to check in it’s availability call if registering parameters are submitted? 

  • Anonymous

    @speedupmate:disqus Thanks for the hint, I guess, that you are right. I have client on the line, but will check it shortly.

  • Anonymous

    Thanks for the hint! I changed my code and kicked off the part where I use the session to activate the free shipping method. I also had problem with AJAX request queue, because if we change the post code and click on “CREATE AN ACCOUNT FOR FREE SHIPPING” we have 2 AJAX request … so I get the response of second request, before the response of first request and displayed wrong shipping information. I am going to edit my post ASAP :)

  • Great find, I’m going to have to check this one out. Thanks for sharing. Very informative….