Add custom column on Check Out Page

Objective
User will see a custom column field on the checkout.
Custom Column will be saved in the quote sales_order and sales_order_grid table.
Custom Column will be displayed in the order grid.

First, create module Structure AP => DeliveryNote with a namespace.
Step 1:
Create app/code/Ap/DeliveryNote/Setup/UpgradeSchema.php
Below Script will create a new column in quote,sales_order and sales_order_grid table.
namespace Ap\DeliveryNote\Setup;
use Magento\Framework\Setup\UpgradeSchemaInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
use Magento\Framework\Setup\ModuleContextInterface;

class UpgradeSchema implements UpgradeSchemaInterface
{
    public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $context)
    {
        $setup->startSetup();

        $quote = 'quote';
        $orderTable = 'sales_order';
        $gridTable = 'sales_order_grid';

           
          $setup->getConnection()
            ->addColumn(
                $setup->getTable($quote),
                'custom_column',
                [
                    'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
                    'length' => 255,
                    'comment' =>'Custom Collumn'
                ]
            );
        //Order table
        $setup->getConnection()
            ->addColumn(
                $setup->getTable($orderTable),
                'custom_column',
                [
                    'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
                    'length' => 255,
                    'comment' =>'Custom Collumn'
                ]
            );

           $setup->getConnection()
            ->addColumn(
                $setup->getTable($gridTable),
                'custom_column',
                [
                    'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
                    'length' => 255,
                    'comment' =>'Custom Collumn'
                ]
            );
           
        $setup->endSetup();
    }
}

Step 2:
Ap/DeliveryNote/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\Checkout\Block\Checkout\LayoutProcessor">
        <plugin name="add-customn-column-field" type="Ap\DeliveryNote\Model\Checkout\LayoutProcessorPlugin" sortOrder="10"/>
    </type>
    <type name="Magento\Checkout\Model\ShippingInformationManagement">
        <plugin name="save-in-quote" type="Ap\DeliveryNote\Model\Checkout\ShippingInformationManagementPlugin" sortOrder="10"/>
    </type>
    
    <virtualType name="Magento\Sales\Model\ResourceModel\Order\Grid" type="Magento\Sales\Model\ResourceModel\Grid">
        <arguments>
            <argument name="columns" xsi:type="array">
                <item name="custom_column" xsi:type="string">sales_order.custom_column</item>
            </argument>
        </arguments>
    </virtualType>
    
</config>





Step 3:
Below file display customn_column in the checkout process.
Ap/DeliveryNote/Model/Checkout/LayoutProcessorPlugin.php
namespace Ap\DeliveryNote\Model\Checkout;
class LayoutProcessorPlugin
{

    /**
     * @param \Magento\Checkout\Block\Checkout\LayoutProcessor $subject
     * @param array $jsLayout
     * @return array
     */

    public function afterProcess(
        \Magento\Checkout\Block\Checkout\LayoutProcessor $subject,
        array  $jsLayout
    ) {
        
         $jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']
        ['shippingAddress']['children']['shipping-address-fieldset']['children']['custom_column'] = [
            'component' => 'Magento_Ui/js/form/element/abstract',
            'config' => [
                'customScope' => 'shippingAddress',
                'template' => 'ui/form/field',
                'elementTmpl' => 'ui/form/element/input',
                'options' => [],
                'id' => 'custom-column'
            ],
            'dataScope' => 'shippingAddress.custom_column',
            'label' => 'Custom Column',
            'provider' => 'checkoutProvider',
            'visible' => true,
            'validation' => [],
            'sortOrder' => 200,
            'id' => 'custom-column'
        ];
        
        return $jsLayout;
    }
}

Step 4:
Now Set custom_column data in post array using extension attribute. For this, we have to set data in post using shipping-save-processor. Now we have to override shipping-save-processor using requirejs-config.js
Ap/DeliveryNote/view/frontend/requirejs-config.js
var config = {
    "map": {
        "*": {
            "Magento_Checkout/js/model/shipping-save-processor/default" : "Ap_DeliveryNote/js/shipping-save-processor-default-override"
        }
    }
};
Now create a file and add code in shipping-save-processor-default-override.js
Ap/DeliveryNote/etc/extension_attributes.xml this will set data in ShippingInformationInterface
payload = {
                    addressInformation: {
                       ......
                       ......  
                        extension_attributes: {
                              custom_column: jQuery('[name="custom_column"]').val()
                        }
                    }
                };
Now create extension_attributes.xml file
Ap/DeliveryNote/etc/extension_attributes.xml this will set data in ShippingInformationInterface
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd">
    <extension_attributes for="Magento\Checkout\Api\Data\ShippingInformationInterface">
        <attribute code="custom_column" type="string"/>
    </extension_attributes>
</config>
Now you can go to checkout page, and here you can review payment step.
Step 5:
Save custom_column in quote table using ShippingInformationManagement.
Create file Ap\DeliveryNote\Model\Checkout\ShippingInformationManagementPlugin.php
namespace Ap\DeliveryNote\Model\Checkout;
class ShippingInformationManagementPlugin
{

    protected $quoteRepository;

    public function __construct(
        \Magento\Quote\Model\QuoteRepository $quoteRepository
    ) {
        $this->quoteRepository = $quoteRepository;
    }

    /**
     * @param \Magento\Checkout\Model\ShippingInformationManagement $subject
     * @param $cartId
     * @param \Magento\Checkout\Api\Data\ShippingInformationInterface $addressInformation
     */
    public function beforeSaveAddressInformation(
        \Magento\Checkout\Model\ShippingInformationManagement $subject,
        $cartId,
        \Magento\Checkout\Api\Data\ShippingInformationInterface $addressInformation
    ) {
        $extAttributes = $addressInformation->getExtensionAttributes();
        $customColumn = $extAttributes->getCustomColumn();
        $quote = $this->quoteRepository->getActive($cartId);
        $quote->setCustomColumn($customColumn);
    }
}

Step 6:
When you click on final order at that you have to save that data in sales_order table.
app/code/Ap/DeliveryNote/etc/events.xml
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="sales_model_service_quote_submit_before">
        <observer name="ap_customcolumn" instance="Ap\DeliveryNote\Model\Observer\SaveDeliveryDateToOrderObserver"/>
    </event>
</config>
Ap\DeliveryNote\Model\Observer\SaveDeliveryDateToOrderObserver.php
namespace Ap\DeliveryNote\Model\Observer;
use Magento\Framework\Event\Observer as EventObserver;
use Magento\Framework\Event\ObserverInterface;

class SaveDeliveryDateToOrderObserver implements ObserverInterface
{
    /**
     * @var \Magento\Framework\ObjectManagerInterface
     */
    protected $_objectManager;

    /**
     * @param \Magento\Framework\ObjectManagerInterface $objectmanager
     */
    public function __construct(\Magento\Framework\ObjectManagerInterface $objectmanager)
    {
        $this->_objectManager = $objectmanager;
    }

    public function execute(EventObserver $observer)
    {
        $order = $observer->getOrder();
        $quoteRepository = $this->_objectManager->create('Magento\Quote\Model\QuoteRepository');
        /** @var \Magento\Quote\Model\Quote $quote */
        $quote = $quoteRepository->get($order->getQuoteId());
        $order->setCustomColumn( $quote->getCustomColumn() );

        return $this;
    }

}

Step 7:
If you want to display that data in a grid.
Ap/DeliveryNote/view/adminhtml/ui_component/sales_order_grid.xml

<?xml version="1.0" encoding="UTF-8"?>
 <listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <columns name="sales_order_columns">
        <column name="custom_column">
            <argument name="data" xsi:type="array">
                <item name="js_config" xsi:type="array">
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/column</item>
                </item>
                <item name="config" xsi:type="array">
                    <item name="visible" xsi:type="boolean">true</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="align" xsi:type="string">left</item>
                    <item name="label" xsi:type="string" translate="true">Custom Column</item>
                </item>
            </argument>
        </column>
    </columns>
</listing>

Comments