Magento recalculate cart total in observer

30,302

Solution 1

Thank you @Anton for your help!

The answer that ended up working for me was to make a call to session_write_close(); before the redirect (in the observer):

if (// products are out-of-stock and were removed...) {
    $this->_getSession()->addError('Error message here.');
    $this->_getSession()->getQuote()->setTotalsCollectedFlag(false)->collectTotals();
    session_write_close();
    Mage::app()->getResponse()->setRedirect('index');
}

Solution 2

Tip for the day: by observing the *_save_after and trying to force the same object to change will normally call save again and you will end up in endless loop .oO

However if you observe the collectTotals() method in quote class then you'll notice that you are missing a important flag ->setTotalsCollectedFlag(false)->collectTotals() to make the calculation possible once it has been already calculated.

Life would be something different if there were not some bugs in your path to glory so be aware of the following issue in Magento: Issue #26145

Share:
30,302
Toby Hemmerling
Author by

Toby Hemmerling

Updated on April 11, 2020

Comments

  • Toby Hemmerling
    Toby Hemmerling about 4 years

    I have an observer that removes items from the cart if they are out of stock (i.e. customer returns to their cart ofter x time, and an item in the cart has gone out of stock), and shows a message to the user.

    Removing the item(s) works, but updating the cart total does not. Any help would be much appreciated!

    My observer observes the sales_quote_save_before event:

    public function checkStockStatus($observer)
    {
        // return if disabled or observer already executed on this request
        if (!Mage::helper('stockcheck')->isEnabled() || Mage::registry('stockcheck_observer_executed')) {
            return $this;
        }
    
        $quote = $observer->getEvent()->getQuote();
        $outOfStockCount = 0;
    
        foreach ($quote->getAllItems() as $item) {
            $product = Mage::getModel('catalog/product')->load($item->getProductId());
            $stockItem = $product->getStockItem();
            if ($stockItem->getIsInStock()) {
                // in stock - for testing only
                $this->_getSession()->addSuccess(Mage::helper('stockcheck')->__('in stock'));
                $item->setData('calculation_price', null);
                $item->setData('original_price', null);
            }
            else {
                //remove item 
                $this->_getCart()->removeItem($item->getId());
                $outOfStockCount++; 
                $this->_getSession()->addError(Mage::helper('stockcheck')->__('Out of Stock'));
            }
        }
    
        if ($outOfStockCount) > 0) {       
            $quote->setTotalsCollectedFlag(false)->collectTotals();
        } 
    
        Mage::register('stockcheck_observer_executed', true);
    
        return $this;         
    }
    
    protected function _getCart()
    {
        return Mage::getSingleton('checkout/cart');
    }
    
    protected function _getSession()
    {
        return Mage::getSingleton('checkout/session');
    }  
    
  • Toby Hemmerling
    Toby Hemmerling over 12 years
    Thanks for the tip of the day! Edited the code to remove the infinite loop (observing *_save_before and not making new calls to save()). However, even when I add ->setTotalsCollectedFlag(false) before calling ->collectTotals() the totals are not updated. What else am I missing?
  • Anton S
    Anton S over 12 years
    you'll see the changes after a reload?
  • Toby Hemmerling
    Toby Hemmerling over 12 years
    yes, but the message disappears. If I do a redirect in my observer, I get the updated totals immediately, but also no message. What I'm after is both :)
  • Anton S
    Anton S over 12 years
    try to call save also and see what this ends up with
  • Toby Hemmerling
    Toby Hemmerling over 12 years
    I fail to see how that would address the issue that session messages (i.e.Mage::getSingleton('chekout/session')->addError('error message here');) get lost on a redirect / page refresh?
  • Dmytro Zavalkin
    Dmytro Zavalkin over 12 years
    I fail to see how "recalculate cart total in observer" is related to "session messages (i.e. Mage::getSingleton('chekout/session')->addError('error message here');) get lost on a redirect / page refresh?"...
  • Toby Hemmerling
    Toby Hemmerling over 12 years
    you're absolutely right, the title of my question is not as descriptive as it could have been. To make matters worse, I had edited the question, unintentionally removing additional clarification (for the sake of brevity). The challenge was being able recalculate the cart total and display a session message to the user. I had been able to achieve either removing the items and total recalculation (via a redirect in the observer after a call to collectTotals()) or removing the items and displaying the message (without redirect), but being stuck with an incorrect total.
  • Anton S
    Anton S over 12 years
    you can accept the answer then for others to know that it is solved
  • Hassan Ali Shahzad
    Hassan Ali Shahzad over 8 years
    you are awesome Anton, i was fighting this loop from last 13 hours. $quote->setTotalsCollectedFlag(false)->collectTotals();