Skip to main content

Today I am going to explain how to add custom fields to all steps of checkout page and save it after order is placed and also how to use posted data after placing order

1st fields delivery_date :- where customer will mention at the delivery date in shipping step

2nd fields order Comments :- will be in Payment step and after placing order this comments will be add to order comment history

Step 1 :- make sure delivery_date is added in all need table like quote, sales_order and sales_order_grid through install or upgrade script

<?php namespace Sugarcode\Deliverydate\Setup; use Magento\Framework\Setup\InstallSchemaInterface; use Magento\Framework\Setup\ModuleContextInterface; use Magento\Framework\Setup\SchemaSetupInterface; /** * @codeCoverageIgnore */ class InstallSchema implements InstallSchemaInterface { /** * {@inheritdoc} * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function install(SchemaSetupInterface $setup, ModuleContextInterface $context) { $installer = $setup; $installer->startSetup(); $installer->getConnection()->addColumn( $installer->getTable('quote'), 'delivery_date', [ 'type' => 'datetime', 'nullable' => false, 'comment' => 'Delivery Date', ] ); $installer->getConnection()->addColumn( $installer->getTable('sales_order'), 'delivery_date', [ 'type' => 'datetime', 'nullable' => false, 'comment' => 'Delivery Date', ] ); $installer->getConnection()->addColumn( $installer->getTable('sales_order_grid'), 'delivery_date', [ 'type' => 'datetime', 'nullable' => false, 'comment' => 'Delivery Date', ] ); $setup->endSetup(); } } 

Step 2 :- Adding custom fields in shipping and payment steps , we can achieve in two way one from layout xml and other one is plugin below is the way how to add the fields through plugin

We create a di.xml file in our module - Sugarcode/Deliverydate/etc/frontend/di.xml

We use the frontend area to keep it clean, our plugin should run only on the frontend.

<?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-delivery-date-field" type="Sugarcode\Deliverydate\Plugin\Checkout\LayoutProcessorPlugin" sortOrder="10"/> </type> </config> 

Sugarcode\Plugin\Checkout\LayoutProcessor.php

<?php namespace Sugarcode\Plugin\Checkout; class LayoutProcessor { /** * @var \Magento\Framework\App\Config\ScopeConfigInterface */ protected $scopeConfig; /** * @var \Magento\Checkout\Model\Session */ protected $checkoutSession; /** * @var \Magento\Customer\Model\AddressFactory */ protected $customerAddressFactory; /** * @var \Magento\Framework\Data\Form\FormKey */ protected $formKey; public function __construct( \Magento\Framework\View\Element\Template\Context $context, \Magento\CheckoutAgreements\Model\ResourceModel\Agreement\CollectionFactory $agreementCollectionFactory, \Magento\Checkout\Model\Session $checkoutSession, \Magento\Customer\Model\AddressFactory $customerAddressFactory ) { $this->scopeConfig = $context->getScopeConfig(); $this->checkoutSession = $checkoutSession; $this->customerAddressFactory = $customerAddressFactory; } /** * @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']['before-form']['children']['delivery_date'] = [ 'component' => 'Magento_Ui/js/form/element/abstract', 'config' => [ 'customScope' => 'shippingAddress', 'template' => 'ui/form/field', 'elementTmpl' => 'ui/form/element/date', 'options' => [], 'id' => 'delivery-date' ], 'dataScope' => 'shippingAddress.delivery_date', 'label' => 'Delivery Date', 'provider' => 'checkoutProvider', 'visible' => true, 'validation' => [], 'sortOrder' => 200, 'id' => 'delivery-date' ]; $jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children'] ['payment']['children']['payments-list']['children']['before-place-order']['children']['comment'] = [ 'component' => 'Magento_Ui/js/form/element/textarea', 'config' => [ 'customScope' => 'shippingAddress', 'template' => 'ui/form/field', 'options' => [], 'id' => 'comment' ], 'dataScope' => 'ordercomment.comment', 'label' => 'Order Comment', 'notice' => __('Comments'), 'provider' => 'checkoutProvider', 'visible' => true, 'sortOrder' => 250, 'id' => 'comment' ]; return $jsLayout; } } 

Now all the fields are in checkout page , now how to save the data

unlike M1 in M2 all Checkout page is completely knockout JS and API

We have two steps first one is shipping and second step is payment where we need to save both fields

Below is how to save the data after shipping addresses are saved

Shipping Step

To save shipping information in M2 uses

app/code/Magento/Checkout/view/frontend/web/js/model/shipping-save-processor/default.js

to prepare json and the call api so we need to override this js and in php side save will happened

\Magento\Checkout\Model\ShippingInformationManagement::SaveAddressInformation() and ShippingInformationManagement implemented by Magento\Checkout\Api\Data\ShippingInformationInterface

M2 has one powerful concept called extension_attributes which is used for dynamic data to core tables lets make it use of that

step 3 :- create a file Deliverydate/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\AddressInterface"> <attribute code="delivery_date" type="string"/> </extension_attributes> <extension_attributes for="Magento\Quote\Api\Data\PaymentInterface"> <attribute code="comment" type="string"/> </extension_attributes> </config> 

to override js create a js file Deliverydate/view/frontend/requirejs-config.js we need to use mixns

var config = { config: { mixins: { 'Magento_Checkout/js/action/place-order': { 'Sugarcode_Deliverydate/js/order/place-order-mixin': true }, 'Magento_Checkout/js/action/set-payment-information': { 'Sugarcode_Deliverydate/js/order/set-payment-information-mixin': true }, 'Magento_Checkout/js/action/set-shipping-information': { 'Sugarcode_Deliverydate/js/order/set-shipping-information-mixin': true } } }; 

js/order/set-shipping-information-mixin.js delivery_date

/** * @author aakimov */ /*jshint browser:true jquery:true*/ /*global alert*/ define([ 'jquery', 'mage/utils/wrapper', 'Magento_Checkout/js/model/quote' ], function ($, wrapper, quote) { 'use strict'; return function (setShippingInformationAction) { return wrapper.wrap(setShippingInformationAction, function (originalAction) { var shippingAddress = quote.shippingAddress(); if (shippingAddress['extension_attributes'] === undefined) { shippingAddress['extension_attributes'] = {}; } // you can extract value of extension attribute from any place (in this example I use customAttributes approach) shippingAddress['extension_attributes']['delivery_date'] = jQuery('[name="delivery_date"]').val(); // pass execution to original action ('Magento_Checkout/js/action/set-shipping-information') return originalAction(); }); }; }); 

The next step is saving this custom field post data to the quote. Let's make another plugin by adding an xml node in our etc/di.xml

<type name="Magento\Checkout\Model\ShippingInformationManagement"> <plugin name="save-in-quote" type="Sugarcode\Deliverydate\Plugin\Checkout\ShippingInformationManagementPlugin" sortOrder="10"/> </type> 

Create a file Sugarcode\Deliverydate\Plugin\Checkout\ShippingInformationManagementPlugin.php

<?php namespace Sugarcode\Deliverydate\Plugin\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->getShippingAddress()->getExtensionAttributes(); $deliveryDate = $extAttributes->getDeliveryDate(); $quote = $this->quoteRepository->getActive($cartId); $quote->setDeliveryDate($deliveryDate); } } 

soon after when you move to payment steps data will be saved in quote table

to save same data after placing order we need to use fieldset

etc/fieldset.xml

<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Object/etc/fieldset.xsd"> <scope id="global"> <fieldset id="sales_convert_quote"> <field name="delivery_date"> <aspect name="to_order"/> </field> </fieldset> </scope> </config> 

Now lets Save the payment steps field

if we have extra fields in payment step and we need to post that data then we need to override the other js as we done for shipping step

as like shipping information we have payment information

ww can achive by override is Magento_Checkout/js/action/place-order.js (but it will have issue when agreement is enabled so we need to use mixins as mention in re)

Sugarcode_Deliverydate/js/order/place-order-mixin.js /** * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ define([ 'jquery', 'mage/utils/wrapper', 'Sugarcode_Deliverydate/js/order/ordercomment-assigner' ], function ($, wrapper, ordercommentAssigner) { 'use strict'; return function (placeOrderAction) { /** Override default place order action and add comments to request */ return wrapper.wrap(placeOrderAction, function (originalAction, paymentData, messageContainer) { ordercommentAssigner(paymentData); return originalAction(paymentData, messageContainer); }); }; }); 

Sugarcode_Deliverydate/js/order/ordercomment-assigner.js

/*jshint browser:true jquery:true*/ /*global alert*/ define([ 'jquery' ], function ($) { 'use strict'; /** Override default place order action and add comment to request */ return function (paymentData) { if (paymentData['extension_attributes'] === undefined) { paymentData['extension_attributes'] = {}; } paymentData['extension_attributes']['comment'] = jQuery('[name="ordercomment[comment]"]').val(); }; }); 

Sugarcode_Deliverydate/js/order/set-payment-information-mixin.js

/*jshint browser:true jquery:true*/ /*global alert*/ define([ 'jquery', 'mage/utils/wrapper', 'Sugarcode_Deliverydate/js/order/ordercomment-assigner' ], function ($, wrapper, ordercommentAssigner) { 'use strict'; return function (placeOrderAction) { /** Override place-order-mixin for set-payment-information action as they differs only by method signature */ return wrapper.wrap(placeOrderAction, function (originalAction, messageContainer, paymentData) { ordercommentAssigner(paymentData); return originalAction(messageContainer, paymentData); }); }; }); 

and need to create a plugin for Magento\Checkout\Model\PaymentInformationManagement

so in etc/di add below code

 <type name="Magento\Checkout\Model\PaymentInformationManagement"> <plugin name="order_comments_save-in-order" type="Sugarcode\Deliverydate\Plugin\Checkout\PaymentInformationManagementPlugin" sortOrder="10"/> </type> 

and then create a file Sugarcode\Deliverydate\Plugin\Checkout\PaymentInformationManagementPlugin.php

orderRepository = $orderRepository; } public function aroundSavePaymentInformationAndPlaceOrder( \Magento\Checkout\Model\PaymentInformationManagement $subject, \Closure $proceed, $cartId, \Magento\Quote\Api\Data\PaymentInterface $paymentMethod, \Magento\Quote\Api\Data\AddressInterface $billingAddress = null ) { $result = $proceed($cartId, $paymentMethod, $billingAddress); if($result){ $orderComment =$paymentMethod->getExtensionAttributes(); if ($orderComment->getComment()) $comment = trim($orderComment->getComment()); else $comment = ''; $history = $order->addStatusHistoryComment($comment); $history->save(); $order->setCustomerNote($comment); } return $result; } } Note :- if the field in payment step need to save in quote table then use beofore plugin for same function and follow as did in ShippingInformationManagementPlugin
Pradeep Kumar
  • 8.7k
  • 12
  • 64
  • 86