Creating Custom option and prize to just added product to cart using observer

@Tim gave a talk about this issue on the weekend: https://docs.google.com/presentation/d/1efPznQSVTrT1HAD1xQvCPC-Tgvr8jYok4X7ZEJhm9jE/edit

What you want is Method 2: Add Following Event in Config.xml

<sales_quote_collect_totals_before>
<observers>
<hackathon_presentation>
<type>singleton</type>
<class>modulename/observer</class>
<method>salesQuoteAddressCollectTotalsBefore</method>
</hackathon_presentation>
</observers>
</sales_quote_collect_totals_before>

In Observer.php add following Method

   public function salesQuoteAddressCollectTotalsBefore($observer)
    {
        $quote = $observer->getQuote();
        $quote_items = $quote->getItemsCollection();
        foreach ($quote_items as $item) {
            $additionalOptions = array(
                array(
                    'code'  => 'my_code',
                    'label' => 'This text is displayed through additional options',
                    'value' => 'ID is ' . $item->getProductId() . ' and SKU is ' . $item->getSku()
                )
            );
            $item->addOption(
                array(
                     'code'  => 'additional_options',
                     'value' => serialize($additionalOptions),
                )
            );
        }
    }

Here is more about this topic:

https://stackoverflow.com/questions/9334115/magento-change-custom-option-value-before-adding-it-to-cart/9344336#9344336

and more:

https://stackoverflow.com/questions/9412074/magento-quote-order-product-item-attribute-based-on-user-input/9496266#9496266


The appropiate event to add custom options on the fly is catalog_product_type_prepare_full_options, which is triggered just before the product with its custom options is converted to a quote item.

Should the own buyRequest data have effect on product attributes or options, an observer on the event catalog_product_type_prepare_{$processMode}_options is a good choice, where $processMode is the validation mode and can be „full“ or „lite“. The „full“ mode is used when a product is regularly added to the cart and validates if all required options are set and the whole configuration is valid. In the „lite“ mode only options contained in the request are validated, it is used when adding a product to the wishlist, but also possible when creating an order from the backend. To process the data in any case you can register the observer for both events. Should there be validation you should differentiate the events of course.

The events are triggered in Mage_Catalog_Model_Product_Type_Abstract::_prepareOptions() and the following parameters are available:

  • transport: Transport object for all custom options (but no other options, for example bundle options), so you can change them in the observer. transport->options is an array in the form option_id => option_value. Attention, transport itself is a stdClass object, not an instance of Varien_Object, as you might expect. So there are no getter and setter methods for transport->options.
  • buy_request: The buyRequest object, you can read it here and still modify it as well.
  • product: The product that will be converted to a quote item later on. Here you can manipulate attributes or add them dynamically. But you still need to consider them in the conversion process. The event used for this, sales_quote_product_add_after, is triggered later only.

Source: The Magento buyRequest Object – A Reference

So an observer might look like this:

public function addCustomOption(Varien_Event_Observer $observer)
{
    $transport = $observer->getTransport();
    if (this_item_should_be_free()) { // implement your condition here
        $transport->options['Free Gifts'] = 'Spend $50 and get gift product worth $9.99';
    }
}

You cannot set a price for this dynamically added custom option, but you can change the price of the quote item using a second observer for catalog_product_get_final_price like this:

public function adjustFinalPrice($observer) {

    $product = $observer->getProduct();
    // Set price to "0" if custom option "Free Gift" has been set
    if ($product->getCustomOption('Free Gift')) {
        $product->setFinalPrice(0);
    }
}