Magento – Copy custom data from quote to order and order item once order is placed in magento2

fieldsetsmagento2quote

I am building a custom module in magento 2 that have a custom discount. I am trying to copy the discount from quote, quote item to order and order item.
In magento 1, I declare in config.xml like this:

<fieldsets>
    <sales_convert_quote_address>
        <custom_discount_amount><to_order>*</to_order></custome_discount_amount>
        <base_custom_discount_amount><to_order>*</to_order></base_custome_discount_amount>
    </sales_convert_quote_address>
    <sales_convert_quote_item>
        <custome_discount_amount><to_order_item>*</to_order_item></custome_discount_amount>
        <base_custom_discount_amount><to_order_item>*</to_order_item></base_custom_discount_amount>
    </sales_convert_quote_item>
</fieldsets> 

and my custom discount amount was copied to tables: sales_flat_order and sales_flat_order_item as expected.

In Magento 2, I created a file name: fieldset.xml with this code:

<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="sales_convert_quote_item">
        <field name="custom_discount_amount">
            <aspect name="to_order_item" />
        </field>
        <field name="base_custom_discount_amount">
            <aspect name="to_order_item" />
        </field>
    </fieldset>         
      <fieldset id="sales_convert_quote_address">
        <field name="custom_discount_amount">
            <aspect name="to_order" />
        </field>
        <field name="base_custom_discount_amount">
            <aspect name="to_order" />
        </field>
    </fieldset>   
</scope>

but there is no success.
What else I need to do in magento 2 to make it works? Can you guys please help me?

Best Answer

It appears fieldsets are no longer an option for moving custom data fields around. Not clear if this is intentional, or a side-effect of other changes.

I just ran into the same problem with payments. I traced that back to \Magento\Quote\Model\Quote\Payment\ToOrderPayment::convert():

public function convert(Payment $object, $data = [])
{
    $paymentData = $this->objectCopyService->getDataFromFieldset(
        'quote_convert_payment',
        'to_order_payment',
        $object
    );

    $orderPayment = $this->orderPaymentFactory->create();
    $this->dataObjectHelper->populateWithArray(
        $orderPayment,
        array_merge($paymentData, $data),
        '\Magento\Sales\Api\Data\OrderPaymentInterface'
    );
    $orderPayment->setAdditionalInformation(
        array_merge(
            $object->getAdditionalInformation(),
            [Substitution::INFO_KEY_TITLE => $object->getMethodInstance()->getTitle()]
        )
    );
    // set directly on the model
    $orderPayment->setCcNumber($object->getCcNumber());
    $orderPayment->setCcCid($object->getCcCid());

    return $orderPayment;
}

It starts by processing the fieldset to copy data from $object into the $paymentData array, but then feeds that array into \Magento\Framework\Api\DataObjectHelper::populateWithArray() to actually set the values on the target object (here, $orderPayment).

Rather than directly adding the data array to the object as in Magento 1, DataObjectHelper looks for 'set__' or 'setIs__' methods corresponding to each key, and then calls those methods with the value. If such method does not exist, the data key is skipped entirely.

Quote addresses and items are processed the same way, in \Magento\Quote\Model\Quote\Address\ToOrderAddress and \Magento\Quote\Model\Quote\Item\ToOrderItem respectively.

What does that mean?

  1. Adding the key to a fieldset is not sufficient, unless a set method already exists on the object corresponding to that key.
  2. It appears that copying custom fields from quote to order will require either overriding the target class to add the necessary setter method(s) (via dependency injection), or using observers like sales_model_service_quote_submit_before to copy the data manually.

Also note that the quote module has its own fieldsets defined, including quote_convert_address and quote_convert_item. It does not appear that the sales_convert_quote* fieldsets are in use anymore.