Magento – Product in cart loses custom options & price after editing

custom-optionsevent-observermagento-1.9

On the product page, I'm programmatically setting some custom options via additional_options. This is a calculated price and some hidden details.

I use checkout_cart_product_add_after and sales_convert_quote_item_to_order_item events to properly add the product to the cart with the calculated price and other custom options data.

And it works fine.

I'm able to load the product with those options again, when using the "edit" link in the cart.

The problem is that when I've made changes to the product and press "update cart", the cart loads without any of those options and only the base price (base price is just the product price i use to calculate the custom price).

So, all custom data gone.

I've googled and tried different things. For example tried using checkout_cart_update_item_complete event.

This code fails for checkout_cart_update_item_complete but works for checkout_cart_product_add_after:

$quoteItem = $observer->getQuoteItem();
$product = $quoteItem->getProduct();

This is the error I get:

Fatal error: Call to a member function getProduct() on a non-object

Why can't I get the quote item from checkout_cart_update_item_complete?

How can I get the updated quote item and make sure that my custom options are included?

UPDATE: Code examples – Simplified. Here I just try to set the calculated price and custom width and height fields.

/app/code/local/CoffeeMaker/CustomizeProduct/etc/config.xml

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <modules>
        <CoffeeMaker_CustomizeProduct>
            <version>0.0.1</version>
        </CoffeeMaker_CustomizeProduct>
    </modules>
    <frontend>
        <events>
            <catalog_product_load_after>
                <observers>                   
                    <extra_options>
                        <type>model</type>
                        <class>CoffeeMaker_CustomizeProduct_Model_Observer</class>
                        <method>loadExtraOptions</method>
                    </extra_options>
                </observers>
            </catalog_product_load_after>
            <checkout_cart_product_add_after>
                <observers>
                    <custom_price>
                        <type>model</type>
                        <class>CoffeeMaker_CustomizeProduct_Model_Observer</class>
                        <method>customPrice</method>
                    </custom_price>
                </observers>
            </checkout_cart_product_add_after>
            <checkout_cart_update_item_complete>
                <observers>
                    <custom_price_update>
                        <type>model</type>
                        <class>CoffeeMaker_CustomizeProduct_Model_Observer</class>
                        <method>customPriceUpdate</method>
                    </custom_price_update>
                </observers>
            </checkout_cart_update_item_complete>
            <sales_convert_quote_item_to_order_item>
                <observers>
                    <extra_options>
                        <type>model</type>
                        <class>CoffeeMaker_CustomizeProduct_Model_Observer</class>
                        <method>salesConvertQuoteItemToOrderItem</method>
                    </extra_options>
                </observers>
            </sales_convert_quote_item_to_order_item>

        </events>
    </frontend>
</config>

/app/code/local/CoffeeMaker/CustomizeProduct/Model/Observer.php

<?php

class CoffeeMaker_CustomizeProduct_Model_Observer {

    // I should be able to use customPrice() but it fails on line #2 as it doesn't seem to be able to get quoteitem for updated product
    public function customPriceUpdate(Varien_Event_Observer $observer) {
        $quoteItem  = $observer->getQuoteItem();
        $product    = $quoteItem->getProduct();
        /*
        $basePrice  = $product->getPrice();
        $postData   = Mage::app()->getFrontController()->getRequest()->getParams();        
        $width      = $postData["customWidth"];
        $height     = $postData["customHeight"];

        $m2 = ($width * $height) / 10000;
        $totalPrice = (float) $m2 * $basePrice;
        $totalPrice = ceil($totalPrice);

        // Force minimum price (w/o tax)
        if ($totalPrice < 400) {
            $totalPrice = 400;
        }
        $quoteItem->setOriginalCustomPrice($totalPrice);
        Mage::log("customPriceUpdate has run!");
        */
    }

    public function customPrice(Varien_Event_Observer $observer)
    {
        $quoteItem  = $observer->getQuoteItem();
        $product    = $quoteItem->getProduct();
        $basePrice  = $product->getPrice();
        $postData   = Mage::app()->getFrontController()->getRequest()->getParams();        
        $width      = $postData["customWidth"];
        $height     = $postData["customHeight"];

        $m2 = ($width * $height) / 10000;
        $totalPrice = (float) $m2 * $basePrice;
        $totalPrice = ceil($totalPrice);

        // Force minimum price (w/o tax)
        if ($totalPrice < 400) {
            $totalPrice = 400;
        }
        $quoteItem->setOriginalCustomPrice($totalPrice);
        Mage::log("customPrice has run!");
    }

    public function loadExtraOptions(Varien_Event_Observer $observer) {

        $action = Mage::app()->getFrontController()->getAction();

        if ( $action->getFullActionName() == "checkout_cart_add" ) {

            $data = $action->getRequest()->getParams();

            $options = array(
                "customWidth"   => $data["customWidth"],
                "customHeight"  => $data["customHeight"]
            );

            $product = $observer->getProduct();

            $additionalOptions = array();

            if ( $additionalOption = $product->getCustomOption('additional_options') ) {
                $additionalOptions = (array) unserialize($additionalOption->getValue());
            }

            foreach( $options as $key => $value ) {
                $additionalOptions[] = array(
                    'label' => $key,
                    'value' => $value
                );
            }

            $observer->getProduct()
                     ->addCustomOption('additional_options', serialize($additionalOptions));

        } else if ($action->getFullActionName() == "checkout_cart_configure") {

            $id = $action->getRequest()->getParams()["id"];
            $cart = Mage::getModel('checkout/cart')->getQuote();
            $quoteItem = $cart->getItemById($id);

            $options = array(
                'customWidth'   => "Boogie johnson",
                'customHeight'  => "Super Loogie"
            );

            $product = $observer->getProduct();

            // Add to the additional options array
            $additionalOptions = array();

            if ( $additionalOption = $product->getCustomOption('additional_options') ) {
                $additionalOptions = (array) unserialize($additionalOption->getValue());
            }

            foreach( $options as $key => $value ) {
                $additionalOptions[] = array(
                    'label' => $key,
                    'value' => $value
                );
            }

            $observer->getProduct()
                     ->addCustomOption('additional_options', serialize($additionalOptions));

        }

        Mage::log("loadExtraOptions has run!");
    }

    public function salesConvertQuoteItemToOrderItem(Varien_Event_Observer $observer) {
        $quoteItem = $observer->getItem();
        if ($additionalOptions = $quoteItem->getOptionByCode('additional_options') ) {
            $orderItem = $observer->getOrderItem();
            $options = $orderItem->getProductOptions();
            $options['additional_options'] = unserialize($additionalOptions->getValue());
            $orderItem->setProductOptions($options);
        }
    }
}

my_template_path/catalog/product/view.phtml
I've just added these inputs within the tag. And it works when putting the product in the basket.

Width: <input type="text" name="customWidth"><br>
Height: <input type="text" name="customHeight"><br>

Best Answer

I realise this is quite old but I found this post when I came across the same problem myself, so hopefully if I'm too late for this to benefit you, it might still help someone else that comes across it.

Firstly, to find what is passed to an observer, I find it best to search through the magento code for the event name. Searching for checkout_cart_update_item_complete, we can find it is dispatched from Mage_Checkout_CartController with the following parameters:

array('item' => $item, 'request' => $this->getRequest(), 'response' => $this->getResponse())

This means to get the new quote item you can call $observer->getItem()

To get the old quote item, however, you need to do something like Mage::getSingleton('checkout/cart')->getQuote()->getItemById((int)Mage::app()->getRequest()->getParam('id'))

Now to copy across custom options from the old to the new, you can simply clone the option and add it to the new quote item. in total, this is the basic code you will need:

$newQuoteItem = $observer->getItem();
$oldQuoteItem = Mage::getSingleton('checkout/cart')->getQuote()->getItemById((int)Mage::app()->getRequest()->getParam('id'));
$newQuoteItem->addOption(clone $oldQuoteItem->getOptionByCode('CUSTOM_OPTION_CODE'));
$newQuoteItem->getOptionByCode('CUSTOM_OPTION_CODE')->save();

Just change the code in the last two lines to use your custom option code, or if you have multiple custom options, loop those last lines for each one you need to copy.

EDIT: You will also have to save the option once it has been added to the quote item, so I have added a line to the end of my example to do just that

Related Topic