Magento – Change shipping price from an observer

free-shippingmagento-1.9quoteshippingshipping-address

I have Table Rate shipping method where the shipping price is set to 5 GBP and also free shipping is set when subtotal is more than 35 GBP (a csv file is imported). Also there is an additional currency (EUR) in the store. I have to change the shipping price to 6 EUR (i.e. 4.xx pounds) and the subtotal for free shipping to 40 EUR (will be calculated in pounds again) on the fly with an observer. The logic behind this behaviour is created but the problem is that I'm not able to change the shipping price.

Note: I've found many similar questions on SE and the Magento forums but none of the solutions worked for me.

The event which is used is sales_quote_collect_totals_before.

Here is my method in the observer:

public function salesQuoteCollectTotalsBefore(Varien_Event_Observer $observer)
    {
        try {
        $quote = $observer->getQuote();
        $store = Mage::app()->getStore($quote->getStoreId());
        $address = $quote->getShippingAddress();

        if($address->getCountry() == 'IE') {
            $currencyRates = Mage::getModel('directory/currency')
                ->getCurrencyRates(Mage::app()->getBaseCurrencyCode(), array_values(Mage::getModel('directory/currency')
                    ->getConfigAllowCurrencies()));

            $shippingCostInEur = 6;
            $freeShippingSubtotalInEur = 40;
            $isFreeShipping = false;
            $isDifferentCost = false;
            $newCost = 0;

            if(Mage::app()->getStore()->getCurrentCurrencyCode() == 'EUR') {
                $subtotal = $quote->getSubtotal();

                if($subtotal >= $freeShippingSubtotalInEur) {
                    $isFreeShipping = true;
                } else {
                    $isDifferentCost = true;
                    $newCost = $shippingCostInEur;
                }
            } else {
                $baseSubtotal = $quote->getBaseSubtotal();
                $subtotalInEur = $baseSubtotal*$currencyRates['EUR'];

                if($subtotalInEur >= $freeShippingSubtotalInEur) {
                    $isFreeShipping = true;
                } else {
                    $isDifferentCost = true;
                    $newCost = $shippingCostInEur/$currencyRates['EUR'];
                }
            }

            if($isFreeShipping) {
                //$address->setFreeShipping(true);
                $address->setShippingMethod('freeshipping_freeshipping');
                //$carrierCode = 'tablerate';
                //$store->setConfig("carriers/{$carrierCode}/handling_fee", 0);
            }

            if($isDifferentCost) {
                $address->setShippingAmount($newCost);
                //$store->setConfig("carriers/{$carrierCode}/handling_fee", $newCost);
            }

            if($isDifferentCost || $isFreeShipping) {
                $quote->setTotalsCollectedFlag(true)
                      ->collectTotals();
                      //->save();
            }
        }

        } catch(Exception $e) {
            Mage::log('BOOM: '.$e->getMessage(), null, 'test.log', true);
        }

        return $this;
    }

Any ideas? I also tried [this][1] for the free shipping and it works but I'm not sure if it's a good idea to rewrite the core methods. Even if I use it for the free shipping, the problem with the another shipping price still exists, of course.

Using Magento 1.9.0.1 with Onepage checkout.

EDIT: With the following example everything seems to work but I'm not sure if it's a good approach. I'm changing just the config values which are saved in the coupled XML as I know. Not sure if it's save enough. Also I'm changing just the handling_fee and not the price. Here it is:

public function salesQuoteCollectTotalsBefore(Varien_Event_Observer $observer)
{
    $quote = $observer->getQuote();
    $address = $quote->getShippingAddress();
    $store = Mage::app()->getStore($quote->getStoreId());

    if($address->getCountry() == 'IE') {
        $currencyRates = Mage::getModel('directory/currency')
            ->getCurrencyRates(Mage::app()->getBaseCurrencyCode(), array_values(Mage::getModel('directory/currency')
                ->getConfigAllowCurrencies()));

        $shippingCostInEur = (float)$store->getConfig('carriers/eservice_shippingrates/fixed_shipping_price');
        $freeShippingSubtotalInEur = (float)$store->getConfig('carriers/eservice_shippingrates/free_shipping_min_subtotal');
        $newCost = $shippingCostInEur/$currencyRates['EUR'];

        if(Mage::app()->getStore()->getCurrentCurrencyCode() == 'EUR') {
            $subtotalInEur = $quote->getSubtotal();
        } else {
            $baseSubtotal = $quote->getBaseSubtotal();
            $subtotalInEur = $baseSubtotal*$currencyRates['EUR'];
        }

        if($subtotalInEur >= $freeShippingSubtotalInEur) {
            $store->setConfig('carriers/tablerate/name', 'Free');
            $newCost = 0;
        } else {
            $store->setConfig('carriers/tablerate/name', $store->getConfig('carriers/eservice_shippingrates/method_name'));
        }

        $store->setConfig('carriers/tablerate/title', $store->getConfig('carriers/eservice_shippingrates/title'));
        $store->setConfig("carriers/tablerate/handling_fee", $newCost);
    }

    return $this;
}

Best Answer

Usually in this circumstance devs will set up a different store view or website for each of these currencies. Then you can simply use the base currency for calculations and this will then alter for each of the currencies appropriately.

This looks overbaked and in general you are better to create a new shipping carrier rather than try to change the shipping in an observer like this. Then its happening in the right points in the code. With your approach you would have to call again into collectTotals as the tax prices, total price, etc all now need recalculating.