Hello folks!

If you are going through what I have been through, then you are in right place! How to add custom fields in admin order create form to extend functionality to save data to order attributes. This will save you time and last but not least money and will put you in a good shape in your team!

Let’s dive in to this. To show the custom field in admin_order_create_index page, I defined two methods:

  1. Using Plugin (using plugin on specific block which are already available in core sales_order_create_index.xml)
  2. Overriding Layout XML (overriding sales_order_create_index.xml and insert custom block using <referenceBlock name=“block_name”>)

Each method has its own pros and cons which depends on your project requirements. I leave this part on each of you to figure out.

Step 1: The first thing would be to create an order attribute my_attribute. I am referencing adding a column in sales_order table and NOT referencing adding an extension attribute. There are plenty of tutorial available on how to add custom order attribute on google.

Step 2: Now choose one of the following options based on your requirements.

Plugin method:

This will work if the intercepted block extends from AbstractForm and then further extends from Magento\Sales\Block\Adminhtml\Order\Create\AbstractCreate.

For example, if we wanted to add any custom fields inside Account Information block, we need to create an after plugin for toHtml method for  Magento\Sales\Block\Adminhtml\Order\Create\Account like below. I am assuming you already created a module and the plugin:

<?php

namespace Namespace\Modulename\Plugin\Sales\Block\Adminhtml\Order\Create;

class MediaCode
{
    public function afterToHtml(
        \Magento\Sales\Block\Adminhtml\Order\Create\Form\Account $subject, $result
    ) {
        $orderAttributesForm = $subject->getLayout()->createBlock(
            'Namespace\Modulename\Block\Adminhtml\Order\Create\MyAttribute'
        );
        $orderAttributesForm->setTemplate('Namespace_Modulename::sales/adminhtml/order/my_attribute.phtml');
        $orderAttributesForm->setStore($subject->getStore());
        $orderAttributesFormHtml = $orderAttributesForm->toHtml();

        return $result . $orderAttributesFormHtml;
    }
}

This will load up the block class Namespace\Modulename\Block\Adminhtml\Order\Create\MyAttribute and its associated phtml as set inside this method. The block class can either extend from \Magento\Backend\Block\Widget\Form\Genreic or from \Magento\Sales\Block\Adminhtml\Order\Create\AbstractCreate.

Overriding Layout XML method:

If we want to add any custom field inside gift_options block which directly extends from \Magento\Sales\Block\Adminhtml\Order\Create\AbstractCreate, we need to add our own block in sales_order_create_index.xml like below:

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="gift_options">
            <block class="Namespace\Modulename\Block\Adminhtml\Order\Create\MyAttribute" name="namespace_modulename_my_attribute"
                   template="Namespace_Modulename::sales/adminhtml/order/my_attribute.phtml" after="-"
            />
        </referenceBlock>
    </body>
</page>

The block class can either extend from \Magento\Backend\Block\Widget\Form\Generic or from \Magento\Sales\Block\Adminhtml\Order\Create\AbstractCreate

Step 3: Now, this step is to show the field regardless whichever method you used earlier.  The next thing will be to add some html element inside of this phtml. A good example would be to see how Magento used textfield for its coupon, textarea for comment. Take a look inside sales_order_create_index.xml in coupons block’s phtml, in comment block’s phtml, in gift_options block’s phtml. The most important things are, how Magento added class for <div>, <label>, <section> and the name of any input element should be same as attribute code.

Step 4: This step is to save the data in db. When admin order is placed, the easy way to to save attributes’ values is to create an after plugin for createOrder method for Magento\Sales\Model\AdminOrder\Create. This plugin should be in adminhtml scope and should be declared in etc/adminhtml/di.xml. Once afterCreateOrder method is created inside your plugin, the attribute my_attribute value can be read from  $subject->getData(‘my_attribute’). Here, $subject is the intercepted object of class \Magento\Sales\Model\AdminOrder\Create.

Please suggest better options if you already implemented in your project!

Cheers!