Magento2 add custom data on checkout and show on order.

Magento2 programmatically add custom data to checkout and show on order.

Step 1. Register your module Vendor_Module:
etc/module.xml

<?xml version="1.0"?>


<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Vendor_Module" setup_version="1.3.1">
       
    </module>
</config>

registration.php

<?php


// @codeCoverageIgnoreStart
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Vendor_Module',
    __DIR__
);
// @codeCoverageIgnoreEnd

Step 2 Create a installData.php or upgradeData.php and addAttribute to quote and order. I use custom data name “contact_type”.

<?php
/**
 * Created by PhpStorm.
 * User: thelightsp
 * Date: 6/30/19
 * Time: 7:45 AM
 */

namespace Vendor\Module\Setup;

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

class UpgradeData implements UpgradeDataInterface
{
    /**
     * @var EavSetupFactory
     */
    public $eavSetupFactory;

    /**
     * @var \Magento\Framework\ObjectManagerInterface
     */
    public $objectManager;

    /**
     * @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute
     */
    public $eavAttribute;

    /**
     * @var \Magento\Framework\App\State
     */
    protected $state;
    /**
     * @var QuoteSetupFactory
     */
    protected $quoteSetupFactory;
    /**
     * @var SalesSetupFactory
     */
    protected $salesSetupFactory;
    /**
     * UpgradeData constructor.
     * @param EavSetupFactory $eavSetupFactory
     * @param \Magento\Framework\ObjectManagerInterface $objectManager
     * @param \Magento\Framework\App\State $state
     * @param \Magento\Catalog\Model\ResourceModel\Eav\Attribute $eavAttribute
     * @param QuoteSetupFactory $quoteSetupFactory
     * @param SalesSetupFactory $salesSetupFactory
     */
    public function __construct(
        EavSetupFactory $eavSetupFactory,
        \Magento\Framework\ObjectManagerInterface $objectManager,
        \Magento\Framework\App\State $state,
        \Magento\Catalog\Model\ResourceModel\Eav\Attribute $eavAttribute,
        QuoteSetupFactory $quoteSetupFactory,
        SalesSetupFactory $salesSetupFactory
    ) {
        $this->eavSetupFactory = $eavSetupFactory;
        $this->objectManager = $objectManager;
        $this->eavAttribute = $eavAttribute;
        $this->state = $state;
        $this->quoteSetupFactory = $quoteSetupFactory;
        $this->salesSetupFactory = $salesSetupFactory;

    }


    /**
     * @param ModuleDataSetupInterface $setup
     * @param ModuleContextInterface $context
     */
    public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {
        $setup->startSetup();
        if (version_compare($context->getVersion(), '1.3.1', '<')) {
            /** @var QuoteSetup $quoteSetup */
            $quoteSetup = $this->quoteSetupFactory->create(['setup' => $setup]);
            $quoteSetup
                ->addAttribute(
                    'quote',
                    'contact_type',
                    ['type' => Table::TYPE_TEXT, 'required' => false]
                );

            /** @var SalesSetup $salesSetup */
            $salesSetup = $this->salesSetupFactory->create(['setup' => $setup]);
            $salesSetup
                ->addAttribute(
                    'order',
                    'contact_type',
                    ['type' => Table::TYPE_TEXT, 'required' => false]
                );
        }

    }

}

Step 3: Add custom data component on layout: view/frontend/layout/checkout_index_index.xml

<?xml version="1.0"?>

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="checkout" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="checkout.root">
            <arguments>
                <argument name="jsLayout" xsi:type="array">
                    <item name="components" xsi:type="array">
                        <item name="checkout" xsi:type="array">
                            <item name="children" xsi:type="array">
                                <item name="sidebar" xsi:type="array">
                                    <item name="children" xsi:type="array">
                                        <item name="summary" xsi:type="array">
                                            <item name="children" xsi:type="array">
                                                <item name="itemsAfter" xsi:type="array">
                                                    <item name="children" xsi:type="array">
                                                        <item name="contact-type" xsi:type="array">
                                        <item name="component" xsi:type="string">Vendor_Module/js/view/sidebar/contact-type</item>
                                        <item name="sortOrder" xsi:type="string">12</item>
                                        <item name="displayArea" xsi:type="string">contact-type</item>
                                        <item name="dataScope" xsi:type="string">contactType</item>
                                        <item name="provider" xsi:type="string">checkoutProvider</item>
                                    </item>
                                                        </item>
                                                    </item>
                                                </item>
                                            </item>
                                        </item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

Step 4: Add component of your field: view/frontend/web/js/view/sidebar/contact-type.js

define(
    [
        'ko',
        'uiComponent'
    ],
    function (ko, Component ) {
        'use strict';

        return Component.extend({
            defaults: {
                template: 'Vendor_Module/sidebar/contact-type',
                inputValue: ''
            },
            isVisible: true,

            /**
             * @inheritdoc
             */
            initObservable: function () {
                this._super().observe({
                    CheckVal1: ko.observable(false)
                    
                });
                var contactType= [];

                this.CheckVal1.subscribe(function (newValue) {
                    
                    window.checkoutConfig.contact_type = newValue;

                });

                return this;
            }


        });
    }
);

Step 5: add component template: view/frontend/web/template/sidebar/contact-type.html

<div class="aw-sidebar_contact-type" data-bind="visible: isVisible">
    <span data-role="title" class="toggle" data-bind="i18n:'Contacts'"></span>
    <div class="content" data-role="content">

        <div class="contact">
            <div>
                <div class="control">
                    <input type="checkbox" data-bind='checked: CheckVal1'  id="contact[phone]" name="contact_phone" value="1" class="checkbox"> <label></label>
                </div>
                <label for="contact[phone]">Your checkbox</label>
                
            </div>
         </div>   
    </div>
</div>

Step 6: Add your attribute value to payment extension attribute:view/frontend/web/js/model/place-order-mixin.js


define([
    'jquery',
    'mage/utils/wrapper',
    'Magento_Checkout/js/model/full-screen-loader'
], function (
    $,
    wrapper,
    fullScreenLoader
) {
    'use strict';

    return function (placeOrderAction) {
        return wrapper.wrap(placeOrderAction, function (originalAction, paymentData, messageContainer) {
          
            paymentData['extension_attributes']['contact_type'] = window.checkoutConfig.contact_type;

            return originalAction(paymentData, messageContainer).fail(
                function () {
                    fullScreenLoader.stopLoader();
                }
            );
        });
    };
});

Step 7: add your extension_attributes in Vendor_Modules/etc/extension_attributes.xml

<?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\Quote\Api\Data\PaymentInterface">
        <attribute code="contact_type" type="string" />
    </extension_attributes>
    <extension_attributes for="Magento\Quote\Api\Data\CartInterface">
        <attribute code="contact_type" type="string" />
    </extension_attributes>
    <extension_attributes for="Magento\Sales\Api\Data\OrderInterface">
        <attribute code="contact_type" type="string" />
    </extension_attributes>
</config>

Step 8: Add plugin to modify data payment from extension attribute and order interface:

<?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\Api\PaymentInformationManagementInterface">
       <plugin name="contactType" type="Vendor\Module\Model\Plugin\ContactType"/>
  </type>
 <type name="Magento\Sales\Api\OrderRepositoryInterface">
        <plugin name="contacttype-order-repository-plugin" type="Vendor\Module\Plugin\Order\OrderRepositoryPlugin"/>
    </type>
</config>
<?php

namespace Vendor\Module\Model\Plugin;

use Magento\Checkout\Api\PaymentInformationManagementInterface;
use Magento\Quote\Api\Data\PaymentInterface;


class ContactType
{
    /**
     * @param PaymentInformationManagementInterface $subject
     * @param int $cartId
     * @param PaymentInterface $paymentMethod
     * @return void
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    public function beforeSavePaymentInformationAndPlaceOrder(
        PaymentInformationManagementInterface $subject,
        $cartId,
        PaymentInterface $paymentMethod
    ) {
       if($paymentData->getExtensionAttributes()->getContactType()){
                $quote = $this->quoteRepository->getActive($cartId);
                $quote->setContactType($paymentData->getExtensionAttributes()->getContactType());
                $this->quoteRepository->save($quote);
            }
    }

    /**
     * @param PaymentInformationManagementInterface $subject
     * @param int $cartId
     * @param PaymentInterface $paymentMethod
     * @return void
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    public function beforeSavePaymentInformation(
        PaymentInformationManagementInterface $subject,
        $cartId,
        PaymentInterface $paymentMethod
    ) {
        if($paymentData->getExtensionAttributes()->getContactType()){
                $quote = $this->quoteRepository->getActive($cartId);
                $quote->setContactType($paymentData->getExtensionAttributes()->getContactType());
                $this->quoteRepository->save($quote);
            }
    }
}

Step 9: add data to event quote submit before: Vendor_Module/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="aw_osc_quote_submit_before" instance="Vendor\Module\Observer\QuoteSubmitBeforeObserver" />
    </event>
</config>

<?php

namespace Vendor\Module\Observer;

use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Quote\Model\Quote;
use Magento\Sales\Api\Data\OrderInterface;

class QuoteSubmitBeforeObserver implements ObserverInterface
{
    /**
     * {@inheritdoc}
     */
    public function execute(Observer $observer)
    {
        $event = $observer->getEvent();
        /** @var OrderInterface $order */
        $order = $event->getOrder();
        /** @var Quote $quote */
        $quote = $event->getQuote();
        $order->setContactType($quote->getContactType());
      
        return $this;
    }
}

Step 10: Set your custom data to order:

<?php

namespace Vendor\Module\Plugin\Order;

use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Api\Data\OrderInterface;
use Magento\Sales\Api\Data\OrderExtensionFactory;
use Magento\Sales\Api\Data\OrderExtensionInterface;

class OrderRepositoryPlugin
{
    /**
     * @var OrderExtensionFactory
     */
    private $orderExtensionFactory;

    /**
     * @param OrderExtensionFactory $orderExtensionFactory
     */
    public function __construct(
        OrderExtensionFactory $orderExtensionFactory
    ) {
        $this->orderExtensionFactory = $orderExtensionFactory;
    }

    /**
     * Add extension attributes to order
     *
     * @param OrderRepositoryInterface $subject
     * @param OrderInterface $order
     * @return OrderInterface
     */
    public function afterGet(OrderRepositoryInterface $subject, OrderInterface $order)
    {
        $this->setExtensionAttributes($order);
        return $order;
    }

    /**
     * Set extension attributes to order entity
     *
     * @param OrderInterface $order
     */
    private function setExtensionAttributes(OrderInterface $order)
    {
        /** @var OrderExtensionInterface $extensionAttributes */
        $extensionAttributes = $order->getExtensionAttributes();
        if ($extensionAttributes === null) {
            $extensionAttributes = $this->orderExtensionFactory->create();
        }
        $extensionAttributes->setContactType($order->getContactType());
        $order->setExtensionAttributes($extensionAttributes);
    }
}

That is all step add your custom data to checkout and order via checkout and database.

Step 11: Show custom data to order view.

Create layout referer and show your block: Vendor/Module/view/adminhtml/layout/sales_order_view.xml

Create your block: Vendor/Module/Block/Adminhtml/Order/View/ContactType.php

Create your template: Vendor/Module/view/adminhtml/templates/order/view/contacttype.phtml

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        
        <referenceBlock name="order_additional_info">
            <block class="Vendor\Module\Block\Adminhtml\Order\View\ContactType" name="order.view.contacttype" template="Vendor_Module::order/view/contacttype.phtml"/>
        </referenceBlock>
    </body>
</page>
<?php
/**
 * Created by PhpStorm.
 * User: thelightsp
 * Date: 6/30/19
 * Time: 4:00 PM
 */

namespace Vendor\Module\Block\Adminhtml\Order\View;


class ContactType extends \Magento\Sales\Block\Adminhtml\Order\AbstractOrder
{
    /**
     * ContactType constructor.
     * @param \Magento\Backend\Block\Template\Context $context
     * @param \Magento\Framework\Registry $registry
     * @param \Magento\Sales\Helper\Admin $adminHelper
     * @param array $data
     */
    public function __construct(
        \Magento\Backend\Block\Template\Context $context,
        \Magento\Framework\Registry $registry,
        \Magento\Sales\Helper\Admin $adminHelper,
        array $data = [])
    {
        parent::__construct($context, $registry, $adminHelper, $data);
    }

    /**
     * @return string
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    public function getContactType(){
        return $this->getOrder()->getContactType();
    }
}
<div class="admin__page-section-item" style="display: block;width: 100%">
    <div class="admin__page-section-item-title">
        <span class="title"><?= $block->escapeHtml(__('Customer Communication')) ?></span>
    </div>
    <address class="admin__page-section-item-content">
        <?= /* @noEscape */ $block->getContactType(); ?>
    </address>
</div>

That is all step create custom data add to checkout and show on your order. Hope it will help you well.

Leave a Reply