11

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
  • 569
  • 1
  • 8
  • 17

3 Answers3

21

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

Anton S
  • 12,750
  • 2
  • 35
  • 37
  • 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? – Toby Hemmerling Oct 04 '11 at 18:10
  • you'll see the changes after a reload? – Anton S Oct 04 '11 at 19:09
  • 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 :) – Toby Hemmerling Oct 04 '11 at 19:16
  • try to call save also and see what this ends up with – Anton S Oct 04 '11 at 19:35
  • you are awesome Anton, i was fighting this loop from last 13 hours. $quote->setTotalsCollectedFlag(false)->collectTotals(); – Hassan Ali Shahzad Nov 25 '15 at 12:45
6

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');
}
Toby Hemmerling
  • 569
  • 1
  • 8
  • 17
0

What about next flow:

  1. Remove items in observer on sales_quote_save_before and add some flag to registry: Mage::register('ooops_we_need_a_redirect', $url)

  2. In observer on sales_quote_save_after do redirect if needed:

    if (Mage::registry('ooops_we_need_a_redirect')) { // do redirect }

Dmytro Zavalkin
  • 5,265
  • 1
  • 30
  • 34
  • 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? – Toby Hemmerling Oct 04 '11 at 21:43
  • 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?"... – Dmytro Zavalkin Oct 05 '11 at 07:31
  • 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. – Toby Hemmerling Oct 05 '11 at 14:42