Fixing Magento’s Configurable Attribute Ordering

Somewhere along the way in a 1.9.x update, configurable attribute sorting stopped working on the fronted. Magento should use an attribute’s position parameter (in the options tab when editing a product attribute), to order the dropdown on a product detail page.

The issue appears to be where Magento assigns prices to an attribute collection in Mage_Catalog_Model_Resource_Product_Type_Configurable_Attribute_Collection

Magento first gets the options for the attribute here:

 $options = $productAttribute->getFrontend()->getSelectOptions();

At this point, all is good. In Magento’s SQL to get the options, sort_order is used correctly and the array of options is in the correct order.

The problem seems to occur in the loop on line 257:

 foreach ($this->getProduct()->getTypeInstance(true)
->getUsedProducts(array($productAttribute->getAttributeCode()), $this->getProduct())
   as $associatedProduct) {
...
}

The order of the attributes will now be determined by the order of items in this loop, which is the order of associated products.

What we need to do is re-order the $values array which is populated in the above loop, we can do this using the order of the $options array:

$order = 0;
$optionsByValue = array();
foreach ($options as $option) {
    $optionsByValue[$option['value']] = $option['label'];
    $orders[$option['value']] = $order;
    $order++;
}

Here we utilise the existing loop which populates the $optionsByValue array to log the ordering of each option. We can then use those values by passing them into our custom sort method just before Magento adds the prices to each item:

/* Resort the values array using the order we've logged against options */
usort($values, array($this, 'sortValues'));

foreach ($values as $data) {
    $this->getItemById($data['product_super_attribute_id'])->addPrice($data);
}

Here’s the sort method which puts the array back to the correct sort order:

public function sortValues($value1, $value2){
    if(isset($value1['sort_order']) && isset($value2['sort_order'])){
        if($value1['sort_order'] > $value2['sort_order']){
            return 1;
        }

        if($value1['sort_order'] < $value2['sort_order']) {
            return -1;
        }

        return 0;
    }
}

Magento’s Configurable Swatches

Create the Attribute

Go to Catalog > Attributes and create the attribute to be used to create the configurable product. This would be typically something like “Colour”. Make sure “Use to create configurable product” is set to “Yes”.

In “Manage Label / Options” add the options for the swatch, E.g. “Red”, “Yellow”, “Blue”. Magento will use these values when looking for the image files for the swatches.

Setting Up The Swatches Config

Go to System > Configuration > Configurable Swatches and set “Enabled” to “Yes”, and select the newly created attribute to be used as a swatch. Set any of the swatch dimensions required if required.

Create the Products

Create the configurable product, and add associated products to it using the attribute we’ve created above. You should now find that when you view your product you get text links output as the colour swatches. This is because Magento currently can’t find any images to use for the colour swatch itself.

Add the Images

There are two ways to get Magento to show images as swatches. The first is to add global swatches which are simply images which are placed in Magento’s **Media/wysiwyg/swatches/” directory using the option label as the filename. So, for example, you may have “blue.png”, “red.png” etc. The extension for image files is PNG.

The second, and this will supersede the first, will allow the setting of a configurable swatch on the product level. To do this, upload images to the configurable product, and then set image’s label field to the attribute’s option value with the suffix “-swatch”, E.g. “blue-swatch”. This image will now be resized and used as the image for that option.

Using Magento’s Configurable Attribute Renderers

Magento decides whether to use a renderer instead of simply outputting configurable options as a select element in catalog/product/view/type/configurable.phtml:

  • All renderers are loaded from the text/list block product.info.options.configurable.renderers
  • Each product attribute is looped over, and each of the renderers are looped over within and checked whether it should be rendered for that attribute.
  • The shouldRender of the renderer object is passed in the attribute and jsonConfig. In the case of swatches, Magento returns true if the attribute in question has been defined as a swatch type in System > Configuration > Configurable Swatches > General Settings.

Using this method, we can add other renderers to the product.info.options.configurable.renderers block, implement the shouldRender method, and decide whether the use that renderer based on the attribute we’re passed in.

Setting Category & Product Attribute Defaults in Magento 1

When creating a new attribute, we can specify a default value to be saved alongside that attribute. However, these values do not propagate to categories or products which already exist. We can, however use load_after events to remedy this.

Confix XML

<global>
    <events>
        <catalog_category_load_after>
        	<observers>
        		<observer_name>
        			<type>singleton</type>
        			<class>module/observer</class>
        			<method>setDefaults</method>
        		</observer_name>
        	</observers>
        </catalog_category_load_after>
    </events>
</global>

Note: To set the default of a category use catalog_product_load_after instead

<?php
class Namespace_Module_Model_Observer
{
    public function setDefaults($observer)
    {
        $category = $observer->getCategory();

        if($category->getShowSizeRollover() === NULL){
            $category->setShowSizeRollover(true);
        }
    }
}

In this example we set the attribute ‘show_size_rollover’ to true if it’s not already been given a value. Because we’ve added this event in the global space, this value will propagate for both frontend and adminhtml.

Getting Attribute Values

There are a few ways to get get attribute values in Magento:

<?php
$product->getAttributeCode();

$product->getData(attribute_code);

These two methods do the same thing, however if the attribute is a type of select or multiselect then the ID of the value will be returned rather than the text of the attribute.

For these methods, the frontend attribute can be obtained from the product and used to get the attribute value:

<?php
// Get the type of the attribute as dictated by the attribute's *frontend_model* field.
// This will be a Mage_Catalog_Model_Resource_Eav_Attribute model:
$attr = $product->getResource()->getAttribute($attributeName);

// Get the value from the frontend type of the attribute. The frontend type is dictated by the frontend_model column in the eav_attribute table.  By default, this will be Mage_Eav_Model_Entity_Frontend_Default if the frontend_model field is empty.

$val = $attr->getFrontend()->getValue($product);

Note If an attribute value is not set, (I.e. NULL) the frontend attribute will return “No”. This happens in the Mage_Eav_Model_Entity_Attribute_Frontend_Abstract class’ getValue method:

 if (!$valueOption) {
    $opt     = Mage::getModel('eav/entity_attribute_source_boolean');
    $options = $opt->getAllOptions();

    if ($options) {
        foreach ($options as $option) {
            if ($option['value'] == $value) {
                $valueOption = $option['label'];
            }
        }
    }
}

– It uses the “eav/entity_attribute_source_boolean” and defaults to one of the values set within in.

getAttributeText

Another way to get the value of a select attribute is to use

<?php
$product->getAttributeText(attribute_name);

This will use the attribute’s source_model (typically used in getting all possible values in adminhtml), and looks up the text of the attribute based on the value’s ID (this is set on the product’s data array):

<?php
   public function getAttributeText($attributeCode)
    {
        return $this->getResource()
            ->getAttribute($attributeCode)
                ->getSource()
                    ->getOptionText($this->getData($attributeCode));
    }

Outputting Attributes

Product attributes have various non-standard EAV options to control how they’re output, such as escaping HTML characters. To honour these settings, product attributes should be passed through the catalog/output helper:

<?php
$helper = Mage::helper('catalog/output');

echo $helper->productAttribute($product, $value, 'attribute_name');

Outputting a list of Attributes

Magento provides a block to output product attributes on the product detail page, provided they’re set to be visible on front. Use the Mage_Catalog_Block_Product_View_Attributes. If an attribute isn’t set, it will return “N/A”, if this is not required then this could be overridden and this line replaced with a continue;.

Adding Extra Attributes to All Product Collections

This can be very useful for attributes which need to be available on all product collections. This is to be used sparingly, however – adding a lot of attributes to a product collection can drastically impact performance; all attributes should also be indexed into the flat tables so that they’re available when flat catalog is on.

Config

<frontend>
	<events>
		<catalog_product_collection_load_before>
			<observers>
				<llapgoch_pcattributes_add>
					<model>llapgoch_addpcattributes/observer</model>
					<method>addAttributes</method>
					<type>model</type>
				</llapgoch_pcattributes_add>
			</observers>
		</catalog_product_collection_load_before>
	</events>
</frontend>

<default>
	<llapgoch_addpcattributes>
		<general>
			<attributes></attributes>
		</general>
	</llapgoch_addpcattributes>
</default>

– We add an observer to the catalog_product_collection_load_before event. This will make sure our attributes are added to the collection wherever it’s instantiated from.
– We also add a default node as a placeholder – we’ll allow loaded attributes to be set via the admin’s system configuration.

Observer

<?php
class Llapgoch_AddProductCollectionAttributes_Model_Observer{
	public function addAttributes($observer){
		$attrs = Mage::helper('llapgoch_addpcattributes')->getAttributesToAdd();
		
		if(is_array($attrs) && count($attrs)){
			$observer->getCollection()->addAttributeToSelect($attrs);
		}
	}
}

– All we need to do is get the attributes we’d like to add to the collection and add them to the select object.

Helper

<?php
class Llapgoch_AddProductCollectionAttributes_Helper_Data extends Mage_Core_Helper_Abstract{
	const XML_PATH_PRODUCT_ATTRIBUTES = "llapgoch_addpcattributes/general/attributes";
	
	public function getAttributesToAdd(){
		$attrs = explode(" ", Mage::getStoreConfig(self::XML_PATH_PRODUCT_ATTRIBUTES));
		
		if(count($attrs)){
			return $attrs;
		}
		
		return false;
	}
		
}

– We load the attributes which can either be set in the admin area (see System Configuration) or overridden by another module’s config.xml.
– We split the attributes into an array using spaces, but this could be any character.

System Configuration

<?xml version="1.0"?>
<config>
	<tabs>
		<llapgoch translate="label">
			<label>LLAP-Goch</label>
			<sort_order>100</sort_order>
		</llapgoch>
	</tabs>
	<sections>
		<llapgoch_addpcattributes translate="label" module="llapgoch_addpcattributes">
			<label>Product Attributes</label>
			<tab>llapgoch</tab>
			
			<sort_order>1</sort_order>
			<show_in_default>1</show_in_default>
			<show_in_website>1</show_in_website>
			<show_in_store>1</show_in_store>
			
			<groups>
				<general translate="label">
					<label>General</label>

					<sort_order>10</sort_order>
					<show_in_default>1</show_in_default>
					<show_in_website>1</show_in_website>
					<show_in_store>1</show_in_store>
					<fields>
						<attributes translate="label">
							<label>Collection Attributes To Add</label>
							<comment>Space separate attributes to be added to every product collection</comment>
							<frontend_type>text</frontend_type>
							<backend_model>llapgoch_addpcattributes/system_config_backend_attributestring</backend_model>
							<sort_order>1</sort_order>
							<show_in_default>1</show_in_default>
							<show_in_website>1</show_in_website>
							<show_in_store>1</show_in_store>
						</attributes>
					</fields>
				</general>
			</groups>
		</llapgoch_addpcattributes>
	</sections>
</config>

– We just add our field as a text area to the system config.
– We use a backend model which will be used to warn the user if commas are detected in the string because we’ve chosen to split attributes on spaces.

Backend Model

<?php
class Llapgoch_AddProductCollectionAttributes_Model_System_Config_Backend_Attributestring extends Mage_Core_Model_Config_Data{
	public function _afterSave(){
		$helper = Mage::helper('llapgoch_addpcattributes');
		
		if(strpos($this->getValue(), ",") !== false){
			 Mage::getSingleton('core/session')->addNotice($helper->__('Please use spaces to separate your attribute names instead of commas'));
		}
		
		parent::_afterSave();
		
	}
}

– Checks the string for the existence of spaces and adds a notice for the user if so.

This code is available as a complete module here: https://github.com/llapgoch/magento-add-attributes-to-product-collection

Product Attributes

List of Parameters

Standard EAV Parameters

  • backend The backend model to use for saving values. These should extend Mage_Eav_Model_Entity_Attribute_Backend_Abstract
  • type default: varchar
  • frontend The frontend model to use for rendering values
  • input default: text
  • label The label to display in the admin
  • frontend_class
  • source The source model
  • required
  • user_defined
  • default The default value
  • unique
  • note Will be displayed under the input in the admin area

Catalog Product Specific
These are stored in the catalog_eav_attribute table, and the saving of them is catered for by the Mage_Catalog_Model_Resource_Setup class.

  • global The scope of the attributes. Default: Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_GLOBAL
  • visible default: 1
  • searchable default: 0
  • filterable default 0
  • comparable default 0
  • visible_on_front default 0
  • wysiwyg_enabled default 0
  • is_html_allowed_on_front default 0
  • visible_in_advanced_search default 0
  • filterable_in_search default 0
  • used_in_product_listing default 0
  • used_for_sort_by default 0
  • apply_to default 0
  • is_configurable default 1
  • used_for_promo_rules default 0

Attribute Sets and Groups

Attribute groups live in the eav_attribute_group table. They are assigned to attribute sets via their ID and have a default_id column to denote a default attribute group. Attribute sets can have multiple groups, and to change the order of the groups, use the sort_order column.

Getting All Attributes For An Entity (Raw SQL)

SELECT attribute_code, frontend_label
	FROM eav_attribute ea
	JOIN eav_entity_type eet 
		USING(entity_type_id)
	WHERE eet.entity_type_code = 'customer_address'

Running Setup Scripts

Adding product attributes need to be run through the Mage_Catalog_Model_Resource_Setup script. This extends Mage_Eav_Model_Entity_Setup and includes the code to deal with the extra options available in the catalog_eav_attribute table. If your setup scripts aren’t running as this and you can’t change it, if for example other setup scripts rely on a different class, you can directly instantiate it. This instantiates it with the core_setup resource which uses the default_setup connection.

$setup = new Mage_Catalog_Model_Resource_Setup('core_setup');

Adding A Product Attribute with Options

The following will add a dropdown field with the name ‘Product Overlay Logo’. It will have two options – Playline and Horse. Options added in this manner will have a blank option so that saving a product with one of these options is not required.

<?php
$installer = $this;

$installer->startSetup();

$setup = new Mage_Catalog_Model_Resource_Setup('core_setup');
$setup->addAttribute('catalog_product', 'product_overlay_logo', array(
    'attribute_set' => 'Default',
    'group'         => 'Hand Made Places',
    'input'         => 'select',
    'type'          => 'int',
    'label'         => 'Product Overlay Logo',
    'visible'       => true,
    'required'      => false,
    'visible_on_front' => true,
    'global'        => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_GLOBAL,
    'sort_order' => 10,
    'option' => array(
        'values' => array(
            0 => 'Playline',
            1 => 'Horse'
        )
    )
));
$installer->endSetup();

Adding Options with Translations

Use the Store IDs as the array keys for each language translation.

<?php
$installer = $this;

$installer->startSetup();

$setup = new Mage_Eav_Model_Entity_Setup('core_setup');
$setup->addAttribute('catalog_product', 'product_overlay_logo', array(
    'attribute_set' => 'Default',
    'group'         => 'Hand Made Places',
    'input'         => 'select',
    'type'          => 'int',
    'label'         => 'Product Overlay Logo',
    'visible'       => true,
    'required'      => false,
    'visible_on_front' => true,
    'global'        => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_GLOBAL,
    'sort_order' => 10,
    'option' => array(
        'values' => array(
            0 => array(
                0 => 'Playline',
                1 => 'Playline French',
                2 => 'Playline German'
            ),
            1 => array(
                0 => 'Horse',
                1 => 'Horse French',
                2 => 'Horse German'
            )
        )
    )
));
$installer->endSetup();

Updating Attributes

It’s important to note that when using the EAV’s update attribute method that the attribute code is not converted to another code — the column should be referenced as it is in the database. E.g. visible_on_front should be is_visible_on_front:

<?php
$entityTypeId = $this->getEntityTypeId('catalog_product');
$this->updateAttribute($entityTypeId, 'is_visible_on_front', 'is_visible_on_front', 0);

Updating a Set of Product Attributes

$attrData = array(
    'attribute_code_here'=> 'Value Here',
);

$storeId = 0;

$productIds = Mage::getModel('catalog/product')->getCollection()->getAllIds();
Mage::getModel("catalog/product_action")->updateAttributes(
    $productIds, 
    $attrData, 
    $storeId
);

Mage_Eav_Model_Setup – Useful Methods

  • getEntityTypeId($entity_code); Gets the Database ID of the entity type with the given code (Eg. Customer)
  • getDefaultAttributeSetId($entityTypeId); Gets the default attribute set ID for the entity
  • getDefaultAttributeGroupId($entityTypeId, $attributeSetId = null); * Gets the default group for the entity‚Äôs attribute set. If no attribute set Id is passed in then the default set is used.
  • addAttribute($entity_code, $attribute_code, array)

Adding options to an existing attribute

Sometimes, options need to be added to existing attributes:


$eav = new Mage_Eav_Model_Entity_Setup('core_setup');
$attr = Mage::getSingleton('eav/config')->getAttribute($entityTypeId, $attributeCode);
 

$eav->addAttributeOption(array(
	'value' => array( 
		'small' => array(
			0 => 'Small'
		),
		'large' => array(
			0 => 'Large'
		)
	),
	'attribute_id' => $attr->getId(),
	'order' => array(
		'small' => 100,
		'large' => 200
	)
));

Adding Attribures to a Configurable Product’s Collection










Adding an Image Attribute To A Product

This post deals with adding an image (or any other type of media) to a product. This is useful where an image is required to be separate from Magento’s built in product media system.

The Installer

First, we want to create our attribute.

<?php
$installer = $this;

$installer->startSetup();

$this->addAttribute('catalog_product', 'poster_frame', array(
    'attribute_set' => 'Default',
    'group'         => 'iWeb Media',
    'input'         => 'image',
    'type'          => 'varchar',
    'label'         => 'Poster Frame',
    'visible'       => true,
    'required'      => false,
    'visible_on_front' => true,
    'global'        => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_GLOBAL,
    'backend'       => 'iwebmedia/product_attribute_backend_image',
    'input_renderer'=> 'iwebmedia/adminhtml_product_image',
    'sort_order' => 10
));

$this->endSetup();
  • Because we’re saving an image, we need to create our own backend class to deal with the saving of the image and setting the filename of the image on the product object.
  • We also need to create a custom renderer for the input. This is a block class which we’ll create in our module. By default, Magento will use its own image renderer which is Mage_Adminhtml_Block_Catalog_Category_Helper_Image. This extends Varien_Data_Form_Element_Image and hardcodes the getUrl() method to look in media/catalog/product.

Note: The input_renderer is a special field for the catalog_product EAV type and is stored in the catalog_eav_attribute table as frontend_input_renderer.

The Input Renderer

<?php
class Iweb_Media_Block_Adminhtml_Product_Image extends Varien_Data_Form_Element_Image{
    protected function _getUrl()
    {
        $url = false;
        if ($this->getValue()) {
            $url = Mage::helper('iwebmedia')->getPlaceholderUrl() . $this->getValue();
        }
        return $url;
    }
}

– We just needed to change the getUrl() method to return the path to our image location, which we’ve got a helper to do so that’s only in one location.

The Helper Class

<?php
class Iweb_Media_Helper_Data extends Mage_Core_Helper_Abstract {
    const PLACEHOLDER_UPLOAD_DIR = "iwebmedia";
    
    public function getPlaceholderDir(){
        return Mage::getBaseDir('media') . DS . self::PLACEHOLDER_UPLOAD_DIR . DS;
    }
    
    public function getPlaceholderUrl(){
        return Mage::getBaseUrl('media') . '/' . self::PLACEHOLDER_UPLOAD_DIR . '/';
    }
}

The Attribute’s Backend Model

This is the class which deals with the saving of our data and image

<?php
class Iweb_Media_Model_Product_Attribute_Backend_Image extends Mage_Eav_Model_Entity_Attribute_Backend_Abstract{        
    public function beforeSave($object){
        parent::beforeSave($object);
        
        $name = $this->_getName();
        $imageData = $object->getData($name);
        
        if(isset($imageData['delete']) && (bool) $imageData['delete']){
            return $this->_removeImage($object, $imageData['value']);
        }else{
            return $this->_uploadImage($object);
        }
    }
    
    protected function _getHelper(){
        return Mage::helper('iwebmedia');
    }
    
    protected function _getName(){
        return $this->getAttribute()->getName();
    }
    
    protected function _removeImage($object, $fileName){
        $file = $this->_getHelper()->getPlaceholderDir() . $fileName;
        $name = $this->_getName();
        
        if(file_exists($file)){
            unlink($file);
        }
        
        $object->setData($name, '');
    }
    
    protected function _uploadImage($object){
        $name = $this->_getName();
         
        if(!isset($_FILES[$name]) || (int) $_FILES[$name]['size'] <= 0){
            return;
        }
        
        $path = $this->_getHelper()->getPlaceholderDir();
        
        $uploader = new Varien_File_Uploader($_FILES[$name]);
        $uploader->setAllowedExtensions(array('jpg','jpeg','gif','png'));
        // Allow Magento to create a name for this upload!
        $uploader->setAllowRenameFiles(true);
        
        $result = $uploader->save($path);
        
        $object->setData($name, $result['file']);
    }
    
}

– We use the beforeSave() method which then uses the delete input (provided by Magento’s Varien_Data_Form_Element_Image class) to check whether we should remove the existing image or upload a new one.
– The Varien_File_Uploader class deals with the validation of the image and will throw an error in case of any failure.
– setAllowRenameFiles(true) allows the file uploader to create a new file name if the current one already exists.
$object->setData($name, $result[‘file’]); sets the new filename on the product for saving.

Getting an Attribute’s Default Value if a Product Doesn’t Have a Value Saved

<?php
public function getValue($product, $attributeCode){
	if(($val = $product->getData($attributeCode)) === null){
		return $product->getResource()->getAttribute($attributeCode)->getDefaultValue();
	}

	return $val;
}
  • If the attribute has never been saved on a product, it’ll return the default for the attribute.
  • If the attribute has been saved for a product, it’ll return that value.
  • If the attribute was saved as an empty string, it’ll get deleted from the product (instead of being saved as an empty string. If the value is required to be blank, it could be saved as a single space, and then a trim check could be added when outputting to the block.

Copying Product Attributes to Quote & Order Items

Add this to your XML;

<global>
    <sales>
        <quote>
            <item>
                <product_attributes>
                    <location/>
                </product_attributes>
            </item>
        </quote>
    </sales>
</global>

  • This makes the product attributes accessible to the Mage_Sales_Model_Quote_Config class’ getProductAttributes() method. This reads in the sales/quote/item/product_attributes node.

  • This is called by the _assignProducts method of the Mage_Sales_Model_Resource_Quote_Item_Collection‘s _assignProducts() method, where it adds all of the attributes to the product collection of the quote item collection.

Add the columns to the quote and order item tables

  • Create an installer with the following:

    $installer = new Mage_Sales_Model_Resource_Setup('core_setup');
    
    $entities = array(
        'quote_item',
        'order_item'
    );
    
    $options = array(
        // For some reason, VARCHAR works here, whereas elsewhere it must be TYPE_TEXT with a length of 255.
        'type'     => Varien_Db_Ddl_Table::TYPE_VARCHAR,
        'visible'  => true,
        'required' => false
    );
    
    foreach ($entities as $entity) {
        $installer->addAttribute($entity, 'location', $options);
    }
    

  • The addAttribute() method of the Mage_Sales_Model_Resource_Setup creates the columns on the quote_item and order_item tables.

Copy the attributes to the quote item

Unlike copying from quote items to order items, there isn’t an XML method to do this – it has to be accomplished through an observer. Add the following to the config (with the observer / method substituted):

<frontend>
    <events>
        <sales_quote_item_set_product>
            <observers>
                <observer_name>
                    <class>model/observer</class>
                    <method>addAttributesToQuoteItem</method>
                </observer_name>
            </observers>
        </sales_quote_item_set_product>
    </events>
</frontend>

Then create the following method for the observer above:

    public function addAttributesToQuoteItem($observer){
        $quoteItem = $observer->getQuoteItem();
        $product = $observer->getProduct();
        $quoteItem->setLocation($product->getLocation());
    }

Note: Values Don’t have to come from the product item

Any arbitrary information could be set on the quote or quote item; it does not have to come from the product itself. E.g. A different type of ship note could be set by a different observer. The associated columns just need to exist on the quote or quote item tables.

Copying quote item attributes to the order item

  • This can be achieved with a small amount of XML:
  <global>
    <fieldsets>
        <sales_convert_quote_item>
            <location>
                <to_order_item>*</to_order_item>
            </location>
        </sales_convert_quote_item>
    </fieldsets>
</global>

Note: This will not copy the data to the sales_flat_shipment_item table.

Converting from an order item to other types of item

The following will copy the object’s attributes when converting the quote item back to a quote item, an invoice item, and a credit memo item

<global>
    <fieldsets>
        <sales_convert_order_item>
            <location>
                <to_quote_item>*</to_quote_item>
                <to_invoice_item>*</to_invoice_item>
                <to_cm_item>*</to_cm_item>
            </location>
        </sales_convert_order_item>
    </fieldsets>
</global>

These are used in the Mage_Sales_Model_Convert_Order class’ itemToQuoteItem, itemToInvoiceItem, and itemToCreditmemoItem methods. Note: In default Magento, it does not appear that the itemToQuoteItem is used, so this should be included for third party extensions which may rely on this method.