6

I am programming a module where I need to set shipping to be free of charge if certain conditions are satisfied. These conditions are very specific and cannot be modelled using "Shopping cart price rules". The logic to determine whether a user should get free shipping is done but I don't know how to actually make the shipping free. I have tried this:

$address = $quote->getShippingAddress();
$address->setFreeShipping(true);

I am calling this from event handler of checkout_cart_update_items_after

It is based on the freeshipping sales rule that works with data variable free_shipping. However, it does not work. After updating the cart, shipping keeps its nonzero cost.

I have also tried this:

$address = $quote->getShippingAddress();
$address->setShippingMethod('freeshipping_freeshipping');

It works but the problem is that free shipping must be enabled in backend for this to work. That means a user can pick it in checkout anytime - even if he shouldn't get free shipping based on my conditions.

Is there any good way set free shipping from an observer (event handler)?

  • @sonassi: My conditions are very specific and can't be described using the rules. – clime May 12 '13 at 22:39
  • Could you elaborate on the conditions? – Ben Lessani - Sonassi May 12 '13 at 22:41
  • @sonassi: sorry, they are complicated and very specific to the module I have written. I really need to do it from the module. – clime May 12 '13 at 23:33
7

So here is how I did it.

First, I have used more appropriate event for that purpose, that is sales_quote_collect_totals_before. And second, I needed to comment out (in local copy of course), one line in Mage_SalesRule_Model_Quote_Freeshipping:

public function collect(Mage_Sales_Model_Quote_Address $address)
{
    parent::collect($address);
    $quote = $address->getQuote();
    $store = Mage::app()->getStore($quote->getStoreId());

    //$address->setFreeShipping(0); # clime: we set this in module
    ...
 }

That is it. The following now works well:

$address = $quote->getShippingAddress();
$address->setFreeShipping(true); # the value must be true, not 1

It works well in single shipping mode. Multishipping is probably going to need some adjustments.

1

Do you wanna skip specifying the shipping method?

If yes, override the one page controller and override the following functions instead of (in public function saveBillingAction()):

if (!isset($result['error'])) {
    /* check quote for virtual */
    if ($this->getOnepage()->getQuote()->isVirtual()) {
        $result['goto_section'] = 'payment';
        $result['update_section'] = array(
            'name' => 'payment-method',
            'html' => $this->_getPaymentMethodsHtml()
        );
    } elseif (isset($data['use_for_shipping']) && $data['use_for_shipping'] == 1) {
        $result['goto_section'] = 'shipping_method';
        $result['update_section'] = array(
            'name' => 'shipping-method',
            'html' => $this->_getShippingMethodsHtml()
        );

        $result['allow_sections'] = array('shipping');
        $result['duplicateBillingInfo'] = 'true';
    } else {
        $result['goto_section'] = 'shipping';
    }
}

paste this code:

if (!isset($result['error'])) {
    if($data['use_for_shipping'] != 0){
        $method = 'freeshipping_freeshipping';
        $result = $this->getOnepage()->saveShippingMethod($method);
    }

    if (!isset($result['error'])) {

        if ($this->getOnepage()->getQuote()->isVirtual()) {
            $result['goto_section'] = 'payment';
            $result['update_section'] = array(
                'name' => 'payment-method',
                'html' => $this->_getPaymentMethodsHtml()
            );
        } elseif (isset($data['use_for_shipping']) && $data['use_for_shipping'] == 1) {
            $result['goto_section'] = 'payment';
            $result['update_section'] = array(
                'name' => 'payment-method',
                'html' => $this->_getPaymentMethodsHtml()
            );

            $result['allow_sections'] = array('shipping');
            $result['duplicateBillingInfo'] = 'true';
        } else {
            $result['goto_section'] = 'shipping';
        }
    }
}

and in public function saveShippingAction() add this:

if (!isset($result['error'])) {

    $method = 'freeshipping_freeshipping';
    $result = $this->getOnepage()->saveShippingMethod($method);

    if (!isset($result['error'])) {

        $result['goto_section'] = 'payment';
        $result['update_section'] = array(
            'name' => 'payment-method',
            'html' => $this->_getPaymentMethodsHtml()
        );
    }
}

instead of this:

if (!isset($result['error'])) {
    $result['goto_section'] = 'shipping_method';
    $result['update_section'] = array(
        'name' => 'shipping-method',
        'html' => $this->_getShippingMethodsHtml()
    );
}
  • I would rather need to do it on a certain events in an observer. Sry, my question wasn't clear. But that freeshiping method gave me an idea (see my edit please). – clime May 13 '13 at 8:18
  • u can use the shopping cart rules to show free shipping on the frontend only on a practically impossible condition. so that users won't have free shipping to select, but you can use it in the frontend. this may not be the perfect way, but i'll try to figure out how to use a shipping method when it is disabled. – Shathish May 13 '13 at 11:12
  • +1 but changing shipping info is not necessary. Only saveBilling works fine. – Kingshuk Deb Aug 3 '14 at 10:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.