Adding Custom Attributes to Magento Quotes and Orders

Magento provides some default attributes that are added to the quote and order item data. Here is how to go about adding custom Attributes to Magento quotes and orders.

Whenever you need to access a quote or order item’s SKU, or name, you can do so by using $item->getSku() and $item->getName().

This is because of a couple of major factors: the configuration in Magento’s Mage_Sales
config.xml file, and sku and name columns within the quote and order-related tables, such as sales_flat_quote_item.

The config.xml configuration contains default attributes that are assigned to $item.

// app/code/core/Mage/Sales/etc/config.xml

<?xml version="1.0"?>
<config>
    <global>
        <sales>
            <quote>       
                <item>
                    <product_attributes>
                        <sku/>
                        <type_id/>
                        <name/>
                        <status/>
                        <visibility/>
                        <price/>
                        <weight/>
                        <url_path/>
                        <url_key/>
                        <thumbnail/>
                        <small_image/>
                        <tax_class_id/>
                        <special_from_date/>
                        <special_to_date/>
                        <special_price/>
                        <cost/>
                        <is_recurring/>
                        <recurring_profile/>
                        <gift_message_available/>
                        <msrp_enabled/>
                        <msrp/>
                        <msrp_display_actual_price_type/>
                    </product_attributes>
                </item>
            </quote>
        </sales>
    </global>
</config>

Within your own module, add similar configuration within config.xml specifying the custom attribute(s) you would like to use.

// app/code/local/[Vendor]/[Module]/etc/config.xml

<?xml version="1.0"?>
<config>
    <modules>
        <[Vendor]_[Module]>
            <version>0.2.0</version>
        </[Vendor]_[Module]>
    </modules>
    <global>
        <sales>
            <quote>
                <item>
                    <product_attributes>
                        <custom_attribute />
                    </product_attributes>
                </item>
            </quote>
        </sales>
    </global>
</config>

The above example specifies a module <version>, and this is because the example assumes the custom attribute will be added programmatically via the setup script.

If this is not the case, you can skip over this step, however if it is, you can add a couple of install scripts, and add the attribute using the addAttribute() method.

Add the setup config in addition to the rest of the configuration in config.xml.

// app/code/local/[Vendor]/[Module]/etc/config.xml

<?xml version="1.0"?>
<config>
    <modules>
        ....
    </modules>
    <global>
        <sales>
            ....
        </sales>
        <resources>
            <[vendor]_[module]_setup>
                <setup>
                    <module>[Vendor]_[Module]</module>
                    <class>Mage_Catalog_Model_Resource_Setup</class>
                </setup>
            </[vendor]_[module]_setup>
        </resources>
    </global>
</config>

Now add the first install script, which will create a dropdown attribute with some options.

// app/code/local/[Vendor]/[Module]/sql/[vendor]_[module]_setup/install-0.1.0.php

<?php
$installer = $this;
$installer->startSetup();

$attributeSetId = Mage::getModel('catalog/product')->getDefaultAttributeSetId();
$installer->addAttribute('catalog_product', 'custom_attribute', array(
    'group'                     => '',
    'input'                     => 'select',
    'type'                      => Varien_Db_Ddl_Table::TYPE_VARCHAR,
    'label'                     => 'Custom Attribute',
    'global'                    => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_STORE,
    'visible'                   => true,
    'required'                  => false,
    'visible_on_front'          => false,
    'is_html_allowed_on_front'  => false,
    'used_in_product_listing'   => true,
    'default'                   => '',
    'filterable'                => true,
    'user_defined'              => true,
    'option'                    => array(
        'values' => array(
            'Option 1',
            'Option 2',
            'Option 3'
        )
    )
));

$installer->addAttributeToSet('catalog_product', $attributeSetId, 'General', 'custom_attribute');
$installer->endSetup();

Add the second setup script, which will add the attribute to the quote and order related tables. Should you not wish to do this for the entities specified below, you can move them from the $entities array.

// app/code/local/[Vendor]/[Module]/sql/[vendor]_[module]_setup/upgrade-0.1.0-0.2.0.php

$installer = new Mage_Sales_Model_Resource_Setup('core_setup');
/**
 * Add 'custom_attribute' attribute for entities
 */
$entities = array(
    'quote',
    'quote_address',
    'quote_item',
    'quote_address_item',
    'order',
    'order_item'
);
$options = array(
    'type'     => Varien_Db_Ddl_Table::TYPE_VARCHAR,
    'visible'  => true,
    'required' => false
);
foreach ($entities as $entity) {
    $installer->addAttribute($entity, 'custom_attribute', $options);
}
$installer->endSetup();

The next part is to add an observer onto the sales_quote_item_set_product event which will save the custom attribute’s value into the quote tables.

Don’t forget to add a <models> declaration if you haven’t done so already.

// app/code/local/[Vendor]/[Module]/etc/config.xml

<?xml version="1.0"?>
<config>
    <modules>
        ....
    </modules>
    <global>
        <sales>
            ....
        </sales>
        <resources>
            ....
        </resources>
        <events>
            <sales_quote_item_set_product>
                <observers>
                    <[vendor_module]>
                        <class>[vendor]_[module]/observer</class>
                        <method>salesQuoteItemSetCustomAttribute</method>
                    </[vendor]_[module]>
                </observers>
            </sales_quote_item_set_product>
        </events>
        <models>
            <[vendor]_[module]>
                <class>[Vendor]_[Module]_Model</class>
            </[vendor]_[module]>
        </models>
    </global>
</config>
// app/code/local/[Vendor]/[Module]/Model/Observer.php

<?php
class [Vendor]_[Module]_Model_Observer
{
    public function salesQuoteItemSetCustomAttribute(Varien_Event_Observer $observer)
    {
        $quoteItem = $observer->getQuoteItem();
        $product = $observer->getProduct();
        $quoteItem->setCustomAttribute($product->getAttributeText('custom_attribute'));
    }
}

Note that for dropdown attributes, to get the text values you need to use $product->getAttributeText('custom_attribute').

If the attribute was just a text field, using $product->getCustomAttribute() would suffice.

In order for the quote data to be copied over into the order tables when an order is placed, <fieldsets> configuration is added to config.xml which converts attribute data from the quote to the order.

This can be added for the custom attribute.

// app/code/local/[Vendor]/[Module]/etc/config.xml

<?xml version="1.0"?>
<config>
    <modules>
        ....
    </modules>
    <global>
        <fieldsets>
            <sales_convert_quote_item>
                <custom_attribute>
                    <to_order_item>*</to_order_item>
                </custom_attribute>
            </sales_convert_quote_item>
        </fieldsets>
        <sales>
            ....
        </sales>
        <resources>
            ....
        </resources>
        <events>
            ....
        </events>
    </global>
</config>

If you have followed the steps correctly, when you assign a product a value from the custom attribute and on the frontend, add the product to the basket, the quote item tables should update. Likewise the order tables should update when placing an order.

The custom attribute will then be available when looping around the quote or order items, and can be accessed using $item->getCustomAttribute().

Note: This article is based on Magento Open Source version 1.9.