3

I have followed this:

https://www.siphor.com/adding-custom-attributes-to-magento-2-quotes-and-orders/

It works well when creating custom attributes and adding data quote but after checkout, quote is not saved in table sales_orders_item.

Please help me ! I have been thinking a lot but the problem is still not resolved.

Thanks very much

2
  • Can you confirm in which table (quote or quote-item) you want to save custom attribute? Jun 4, 2019 at 14:50
  • yes, i can. data saved in table quote and quote-item but not save in table sales_orders_item after checkout success
    – TN98
    Jun 5, 2019 at 2:11

2 Answers 2

3

You can follow the steps below to save the quote-item custom field to sales_orders_item custom fields

** Note:**

1) Assume custom field data is successfully saved in quote_item table. So the steps only describe how to save quote-item custom field value to sales_orders_item

2) Assume your custom filed name is my_custom_attribute in both quote_item and sales_orders_item table

2) Assume you are using a custom module names "Company_MyModule"

step 1)

Create a di.xml under MAGENTO-ROOT/app/code/Company/MyModule/etc/di.xml

File: MAGENTO-ROOT/app/code/Company/MyModule/etc/di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Quote\Model\Quote\Item\ToOrderItem">
        <plugin name="company_mymodule\Sales_Quote_Item_ToOrderItem" type="Company\MyModule\Plugin\QuoteItemToOrderItemPlugin" />
    </type>    
</config>

step 2)

Create the plugin class QuoteItemToOrderItemPlugin.php under MAGENTO-ROOT/app/code/Company/MyModule/Plugin

File: MAGENTO-ROOT/app/code/Company/MyModule/Plugin/QuoteItemToOrderItemPlugin.php

<?php

namespace Company\MyModule\Plugin;

use Magento\Framework\Serialize\SerializerInterface;


class QuoteItemToOrderItemPlugin
{



    public function aroundConvert(\Magento\Quote\Model\Quote\Item\ToOrderItem $subject, callable $proceed, $quoteItem, $data)
    {

        // get order item
        $orderItem = $proceed($quoteItem, $data);

        // get your custom attribute from quote_item . 
        $quoteItemMycustomAttribValue = $quoteItem->getMyCustomAttribite();

        //set custom attribute to sales_order_item
        $orderItem->setMyCustomAttribite($quoteItemMycustomAttribValue);

        return $orderItem;
    }
}

step 3)

Run Di compile commands

php bin/magento setup:di:compile
1
  • That's freaking verbose, wtf happened to the sales_convert_quote to_order that we had in M1?! Feb 10, 2022 at 20:20
14

app/code/Vendor/Module/etc/catalog_attributes.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Catalog:etc/catalog_attributes.xsd">
    <group name="quote_item">
        <attribute name="custom_attribute"/>
    </group>
</config>

app/code/Vendor/Module/Setup/InstallData.php

<?php
namespace [Vendor]\[Module]\Setup;

use Magento\Eav\Setup\EavSetup;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\DB\Ddl\Table;
use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Sales\Setup\SalesSetupFactory;
use Magento\Quote\Setup\QuoteSetupFactory;

/**
 * @codeCoverageIgnore
 */
class InstallData implements InstallDataInterface
{
    /**
     * EAV setup factory
     *
     * @var EavSetupFactory
     */
    private $eavSetupFactory;

    /**
     * @var QuoteSetupFactory
     */
    private $quoteSetupFactory;

    /**
     * @var SalesSetup
     */
    private $salesSetupFactory;

    /**
     * InstallData constructor.
     * @param EavSetupFactory $eavSetupFactory
     * @param QuoteSetupFactory $quoteSetupFactory
     */
    public function __construct(
        EavSetupFactory $eavSetupFactory,
        QuoteSetupFactory $quoteSetupFactory,
        SalesSetupFactory $salesSetupFactory
    )
    {
        $this->eavSetupFactory = $eavSetupFactory;
        $this->quoteSetupFactory = $quoteSetupFactory;
        $this->salesSetupFactory = $salesSetupFactory;
    }

    public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {
        /** @var EavSetup $eavSetup */
        $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);

        /** @var QuoteSetup $quoteSetup */
        $quoteSetup = $this->quoteSetupFactory->create(['setup' => $setup]);

        /** @var SalesSetup $salesSetup */
        $salesSetup = $this->salesSetupFactory->create(['setup' => $setup]);

        /**
         * Add attributes to the eav/attribute
         */
        $eavSetup->addAttribute(
            \Magento\Catalog\Model\Product::ENTITY,
            'custom_attribute',
            [
                'type'                    => 'int',
                'label'                   => 'Custom Attribute',
                'input'                   => 'select',
                'global'                  => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL,
                'visible'                 => true,
                'required'                => false,
                'user_defined'            => true,
                'default'                 => '',
                'searchable'              => false,
                'filterable'              => false,
                'comparable'              => false,
                'visible_on_front'        => false,
                'used_in_product_listing' => false,
                'unique'                  => false,
                'option'                  => [
                    'values' => [
                        'Option 1',
                        'Option 2',
                        'Option 3'
                    ],
                ]
            ]
        );

        $attributeSetId = $eavSetup->getDefaultAttributeSetId('catalog_product');
        $eavSetup->addAttributeToSet(
            'catalog_product',
            $attributeSetId,
            'General',
            'custom_attribute'
        );

        $attributeOptions = [
            'type'     => Table::TYPE_TEXT,
            'visible'  => true,
            'required' => false
        ];
        $quoteSetup->addAttribute('quote_item', 'custom_attribute', $attributeOptions);
        $salesSetup->addAttribute('order_item', 'custom_attribute', $attributeOptions);
    }
}

app/code/Vendor/Module/etc/events.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="sales_quote_item_set_product">
        <observer name="set_item_custom_attribute" instance="[Vendor]\[Module]\Observer\SetItemCustomAttribute" />
    </event>
</config>

app/code/Vendor/Module/Observer/SetItemCustomAttribute.php

<?php
namespace [Vendor]\[Module]\Observer;

use Magento\Framework\Event\ObserverInterface;

class SetItemCustomAttribute implements ObserverInterface
{
    /**
     * @param \Magento\Framework\Event\Observer $observer
     * @return void
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    public function execute(\Magento\Framework\Event\Observer $observer)
    {
        $quoteItem = $observer->getQuoteItem();
        $product = $observer->getProduct();
        $quoteItem->setCustomAttribute($product->getAttributeText('custom_attribute'));
    }
}

app/code/Vendor/Module/etc/fieldset.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="lib/internal/Magento/Framework/Object/etc/fieldset.xsd">
    <scope id="global">
        <fieldset id="quote_convert_item">
            <field name="custom_attribute">
                <aspect name="to_order_item" />
            </field>
        </fieldset>
    </scope>
</config>

app/code/Vendor/Module/etc/di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Quote\Model\Quote\Item\ToOrderItem">
        <plugin name="custom_attribute_quote_to_order_item" type="[Vendor]\[Module]\Plugin\CustomAttributeQuoteToOrderItem"/>
    </type>
</config>

app/code/Vendor/Module/Plugin/CustomAttributeQuoteToOrderItem.php

<?php
namespace [Vendor]\[Module]\Plugin;

class CustomAttributeQuoteToOrderItem
{
    public function aroundConvert(
        \Magento\Quote\Model\Quote\Item\ToOrderItem $subject,
        \Closure $proceed,
        \Magento\Quote\Model\Quote\Item\AbstractItem $item,
        $additional = []
    ) {
        /** @var $orderItem \Magento\Sales\Model\Order\Item */
        $orderItem = $proceed($item, $additional);
        $orderItem->setCustomAttribute($item->getCustomAttribute());
        return $orderItem;
    }
}
1
  • To use db_schema.xml instead of QuoteSetup and SalesSetup would be an improvement here
    – roman204
    Jan 2 at 10:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.