Magento – Magento 2 checkout – add a custom field between shipping address and shipping method

custom-fieldmagento2onepage-checkout

I'm trying to add a custom field in between the shipping address and shipping method sections. And I want values of this field to be stored in both quote and sales_order tables ultimately. This is something similar to adding an "order comment" field but this field should appear right after the shipping address section and before the shipping method section.

I went through Magento dev guides on how to add a custom field and a custom form to the checkout and implemented a solution to a certain extent.

What I have done so far:

  1. Updated the checkout_index_index.xml layout, added a new uiComponent (a container) under the item "shippingAddress".
  2. Added the element (field) I require inside the container.
  3. Override the /js/view/shipping.js and shipping.phtmlin my custom module.
  4. Invoked the above made container inside shipping.phtml in between checkout shipping address and shipping method (something similar to adding a new static form)

Now the field I want is being rendered on the onepage checkout exactly where I want. But I have met with below problems.

  1. How to access the value of the custom field I have added above? I'm trying to save the value to a shippingAddress extension attribute. I added a mixin to setShippingInformationAction inside that try to do the below

    shippingAddress['extension_attributes']['custom_field'] = shippingAddress.customAttributes['custom_field'];

But above code actually fails since the element is not in the shipping-address-fieldset. I might be able to get the value through the window element. But is there a way to access this through Magento?

  1. Is there a way to save the value of this element in local cache storage (Magento_Checkout/js/checkout-data) so the value will persist even after a page refresh?

Best Answer

Based on your question I'm under the assumption you already have your extension attributes set up. I have carried out a similar modification and hopefully my answer helps.

In your Custom module create a requirejs-config file to extend the default shipping processor/default

Namespace/CustomModule/view/frontend/requirejs-config.js
var config = {
    "map": {
        "*": {
            'Magento_Checkout/js/model/shipping-save-processor/default': 'Namespace_CustomModule/js/model/shipping-save-processor/default'
        }
    }
};

Add your extension attribute to the payload.

/*global define,alert*/
define(
    [
        'jquery',
        'ko',
        'Magento_Checkout/js/model/quote',
        'Magento_Checkout/js/model/resource-url-manager',
        'mage/storage',
        'Magento_Checkout/js/model/payment-service',
        'Magento_Checkout/js/model/payment/method-converter',
        'Magento_Checkout/js/model/error-processor',
        'Magento_Checkout/js/model/full-screen-loader',
        'Magento_Checkout/js/action/select-billing-address'
    ],
    function (
        $,
        ko,
        quote,
        resourceUrlManager,
        storage,
        paymentService,
        methodConverter,
        errorProcessor,
        fullScreenLoader,
        selectBillingAddressAction
    ) {
        'use strict';

        return {
            saveShippingInformation: function () {
                var payload;

                if (!quote.billingAddress()) {
                    selectBillingAddressAction(quote.shippingAddress());
                }
                //Adding the extension attributes to your shipping address
                payload = {
                    addressInformation: {
                        shipping_address: quote.shippingAddress(),
                        billing_address: quote.billingAddress(),
                        shipping_method_code: quote.shippingMethod().method_code,
                        shipping_carrier_code: quote.shippingMethod().carrier_code,
                        extension_attributes:{
                            custom_field: $('#custom_field').val(), 
                        }
                    }
                };

                fullScreenLoader.startLoader();

                return storage.post(
                    resourceUrlManager.getUrlForSetShippingInformation(quote),
                    JSON.stringify(payload)
                ).done(
                    function (response) {
                        quote.setTotals(response.totals);
                        paymentService.setPaymentMethods(methodConverter(response.payment_methods));
                        fullScreenLoader.stopLoader();
                    }
                ).fail(
                    function (response) {
                        errorProcessor.process(response);
                        fullScreenLoader.stopLoader();
                    }
                );
            }
        };
    }
);

Save the Attribute to your Quote with a plugin (Not sure if you could use an observer here I have not checked).

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\Model\ShippingInformationManagement">
        <plugin name="Namespace_CustomModule_save_delivery_date_in_quote" type="Namespace\CustomModule\Plugin\Checkout\SaveAddressInformation" />
    </type>

</config>

SaveAddressInformation.php

class SaveAddressInformation
{
    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
    ) {
        $extensionAttributes = $addressInformation->getExtensionAttributes();
        $customField = $extensionAttributes->getCustomField();
        $quote = $this->quoteRepository->getActive($cartId);
        $quote->setCustomField($customField);

    }
}

Save the Attribute to your order with an Observer 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="unique_observer_name" instance="Namespace\CustomModule\Observer\SaveCustomFieldToOrder"/>
    </event>
</config>

SaveCustomFieldToOrder.php

class SaveCustomFieldToOrder 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->setCustomField( $quote->getCustomField() );

        return $this;
    }
}

Related Topic