Magento – Additional custom field in billing address for virtual products Magento 2.1

magento2

I am trying to add an extra custom field in checkout billing address.
By refering magento2 documentationhere I am able to add a custom field in shipping address and value can be stored in DB by observing the event sales_model_service_quote_submit_before.

I am trying the same for billing address but it throws an extension_attributes is null error . Please find the code below.

For shipping address its all working fine . In the billing address custom field can be seen but throws error upon saving

Steps
1. Added fieldset.xml
2. Added extension_attributes.xml
3. Added plugin for LayoutProcessor
4. Added requirejs-config.js
5. Added set-billing-address-mixin.js
6. Added installData

Pls refer LayoutProcessor.php , InstallData and set-billing-address-mixin.js

installData

added customer_address attribute and used in forms adminhtml_customer_address', 'customer_address_edit', 'customer_register_address and added column in quote_address and sales_order_address.

set-billing-address-mixin.js

 define([
        'jquery',
        'mage/utils/wrapper',
        'Magento_Checkout/js/model/quote'
    ], function ($, wrapper,quote) {
        'use strict';

        return

 function (setBillingAddressAction) {

        return wrapper.wrap(setBillingAddressAction, function (originalAction) {
            var billingAddress = quote.billingAddress();
            if (billingAddress['extension_attributes'] === undefined) {
                billingAddress['extension_attributes'] = {};
            }

            /***
             * custom field
             */
            billingAddress['extension_attributes']['customfield'] = billingAddress.customAttributes['customfield'];


            return originalAction();
        });
    };
});

LayoutProcessor

<?php
namespace Example\CheckoutField\Plugin\Checkout\Model\Checkout;
use Example\CheckoutField\Helper\Data as ExampleHelperData;
use Magento\Framework\App\ObjectManager;
class LayoutProcessor
{
    protected $helper;

    public $customAttributePosition = 'customfield';

    public function __construct(
        ExampleHelperData $helper
    ){
        $this->helper = $helper;
    }

    /**
     * @param \Magento\Checkout\Block\Checkout\LayoutProcessor $subject
     * @param array $jsLayout
     * @return array
     */
    public function afterProcess(
        \Magento\Checkout\Block\Checkout\LayoutProcessor $subject, array $jsLayout
    )
    {
        if (isset($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']
            ['children']['shippingAddress']['children']['shipping-address-fieldset']['children']
        )) {
            $jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']['shippingAddress']['children']['shipping-address-fieldset']['children'][$this->customAttributePosition] = $this->getCustomfield('shippingAddress');

        }


        if(isset($jsLayout['components']['checkout']['children']['steps']['children']
            ['billing-step']['children']['payment']['children']
            ['payments-list'])) {

            $paymentForms = $jsLayout['components']['checkout']['children']['steps']['children']
            ['billing-step']['children']['payment']['children']
            ['payments-list']['children'];

            foreach ($paymentForms as $paymentGroup => $groupConfig) {
                if (isset($groupConfig['component']) AND $groupConfig['component'] === 'Magento_Checkout/js/view/billing-address') {

                    $jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children']
                    ['payment']['children']['payments-list']['children'][$paymentGroup]['children']['form-fields']['children'][$this->customAttributePosition]= $this->getCustomfieldBilling($paymentGroup);


                }
            }
        }

        return $jsLayout;
    }

    public function getCustomfield($type){
        return [
            'component' => 'Magento_Ui/js/form/element/abstract',
            'config' => [
                // customScope is used to group elements within a single form (e.g. they can be validated separately)
                'customScope' => $type.'.custom_attributes',
                'customEntry' => null,
                'template' => 'ui/form/field',
                'elementTmpl' => 'ui/form/element/input',
                'id' => 'customfield'
            ],
            'dataScope' => $type.'.custom_attributes' . '.' . $this->customAttributePosition,
            'label' => 'customfield',
            'provider' => 'checkoutProvider',
            'sortOrder' => 55,
            'id' => 'customfield',
            'validation' => [],
            'options' => [],
            'filterBy' => null,
            'customEntry' => null,
            'visible' => true,
        ];
    }
    public function getCustomfieldBilling($paymentMethodCode){
        $paymentMethodCode = str_replace('-form', '', $paymentMethodCode);
        return [
            'component' => 'Magento_Ui/js/form/element/abstract',
            'config' => [
                // customScope is used to group elements within a single form (e.g. they can be validated separately)
                'customScope' => 'billingAddress' . $paymentMethodCode .'.custom_attributes',
                'customEntry' => null,
                'template' => 'ui/form/field',
                'elementTmpl' => 'ui/form/element/input',
                'id' => 'customfield'
            ],
            'dataScope' => 'billingAddress' . $paymentMethodCode .'.custom_attributes' . '.' . $this->customAttributePosition,
            'label' => 'customfield',
            'provider' => 'checkoutProvider',
            'sortOrder' => 55,
            'id' => 'customfield',
            'validation' => [],
            'options' => [],
            'filterBy' => null,
            'customEntry' => null,
            'visible' => true,
        ];
    }
}

Best Answer

I have solved the mentioned issue by changing code in set-billing-address-mixin.js

define([
    'jquery',
    'mage/utils/wrapper',
    'Magento_Checkout/js/model/quote',
    'Magento_Checkout/js/action/get-payment-information'
], function ($, wrapper,quote) {
    'use strict';

    return function (setBillingAddressAction) {

        return wrapper.wrap(setBillingAddressAction, function (originalAction) {            
            var billingAddress = quote.billingAddress();
            var getPaymentInformationAction = getPaymentInformationAction;
            if (billingAddress && billingAddress.customAttributes) {
                billingAddress['extension_attributes'] = {};
                billingAddress['extension_attributes']['customfield'] = billingAddress.customAttributes['customfield'];

            }
            return originalAction();
        });
    };
});
Related Topic