1

I want to add one extra text field in checkout billing form and also save that field data in order so I can show that field data in admin order view and also in order email.

I already tried to add in shipping and it works properly but I want to add in billing.

Please help me.

0

Step 1: Create File: Adamsmage/Checkoutattribute/Setup/InstallData.php

<?php
namespace Adamsmage\Checkoutattribute\Setup;

use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Customer\Model\Customer;
use Magento\Customer\Setup\CustomerSetupFactory;

class InstallData implements InstallDataInterface
{

    private $customerSetupFactory;

    /**
     * Constructor
     *
     * @param \Magento\Customer\Setup\CustomerSetupFactory $customerSetupFactory
     */
    public function __construct(
        CustomerSetupFactory $customerSetupFactory
    ) {
        $this->customerSetupFactory = $customerSetupFactory;
    }

    /**
     * {@inheritdoc}
     */
    public function install(
        ModuleDataSetupInterface $setup,
        ModuleContextInterface $context
    ) {
        $customerSetup = $this->customerSetupFactory->create(['setup' => $setup]);

        $customerSetup->addAttribute('customer_address', 'example', [
            'label' => 'example',
            'input' => 'text',
            'type' => 'varchar',
            'source' => '',
            'required' => false,
            'position' => 333,
            'visible' => true,
            'system' => false,
            'is_used_in_grid' => false,
            'is_visible_in_grid' => false,
            'is_filterable_in_grid' => false,
            'is_searchable_in_grid' => false,
            'backend' => ''
        ]);


        $attribute = $customerSetup->getEavConfig()->getAttribute('customer_address', 'example')
            ->addData(['used_in_forms' => [
                'customer_address_edit',
                'customer_register_address'
            ]]);
        $attribute->save();

        $installer->getConnection()->addColumn(
            $installer->getTable('quote_address'),
            'example',
            [
                'type' => 'varchar',
                'length' => 255
            ]
        );

        $installer->getConnection()->addColumn(
            $installer->getTable('sales_order_address'),
            'example',
            [
                'type' => 'varchar',
                'length' => 255
            ]
        );
    }
}

Step 2: Create File: Adamsmage/Checkoutattribute/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\Customer\Api\Data\AddressInterface">
        <attribute code="example" type="string"/>
    </extension_attributes>
</config>

Step 3: Create File: Adamsmage/Checkoutattribute/etc/frontend/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\Onepage">
        <arguments>
            <argument name="layoutProcessors" xsi:type="array">
                <item name="adamsmage_extra_checkout_address_fields_layoutprocessor" xsi:type="object">Adamsmage\Checkoutattribute\Block\Checkout\LayoutProcessor</item>
            </argument>
        </arguments>
    </type>
</config>

Step4 : Create File: Adamsmage/Checkoutattribute/Block/Checkout/LayoutProcessor.php

<?php

namespace Adamsmage\Checkoutattribute\Block\Checkout;

class LayoutProcessor implements \Magento\Checkout\Block\Checkout\LayoutProcessorInterface
{

    public function process($result) {
        $result = $this->getShippingFormFields($result);
        $result = $this->getBillingFormFields($result);
        return $result;
    }

    public function getAdditionalFields($addressType='shipping'){
        if($addressType=='shipping') {
            return ['example'];
        }
        return  ['example'];
    }

    public function getShippingFormFields($result){
        if(isset($result['components']['checkout']['children']['steps']['children']
                ['shipping-step']['children']['shippingAddress']['children']
                ['shipping-address-fieldset'])
        ){

            $shippingPostcodeFields = $this->getFields('shippingAddress.custom_attributes','shipping');

            $shippingFields = $result['components']['checkout']['children']['steps']['children']
            ['shipping-step']['children']['shippingAddress']['children']
            ['shipping-address-fieldset']['children'];

            if(isset($shippingFields['street'])){
                unset($shippingFields['street']['children'][1]['validation']);
                unset($shippingFields['street']['children'][2]['validation']);
            }

            $shippingFields = array_replace_recursive($shippingFields,$shippingPostcodeFields);

            $result['components']['checkout']['children']['steps']['children']
            ['shipping-step']['children']['shippingAddress']['children']
            ['shipping-address-fieldset']['children'] = $shippingFields;

        }

        return $result;
    }

    public function getBillingFormFields($result){
        if(isset($result['components']['checkout']['children']['steps']['children']
            ['billing-step']['children']['payment']['children']
            ['payments-list'])) {

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

            foreach ($paymentForms as $paymentMethodForm => $paymentMethodValue) {

                $paymentMethodCode = str_replace('-form', '', $paymentMethodForm);

                if (!isset($result['components']['checkout']['children']['steps']['children']['billing-step']['children']['payment']['children']['payments-list']['children'][$paymentMethodCode . '-form'])) {
                    continue;
                }

                $billingFields = $result['components']['checkout']['children']['steps']['children']
                ['billing-step']['children']['payment']['children']
                ['payments-list']['children'][$paymentMethodCode . '-form']['children']['form-fields']['children'];

                $billingPostcodeFields = $this->getFields('billingAddress' . $paymentMethodCode . '.custom_attributes','billing');

                $billingFields = array_replace_recursive($billingFields, $billingPostcodeFields);

                $result['components']['checkout']['children']['steps']['children']
                ['billing-step']['children']['payment']['children']
                ['payments-list']['children'][$paymentMethodCode . '-form']['children']['form-fields']['children'] = $billingFields;
            }
        }

        return $result;
    }

    public function getFields($scope,$addressType){
        $fields = [];
        foreach($this->getAdditionalFields($addressType) as $field){
            $fields[$field] = $this->getField($field,$scope);
        }
        return $fields;
    }

    public function getField($attributeCode,$scope) {
        $field = [
            'config' => [
                'customScope' => $scope,
            ],
            'dataScope' => $scope . '.'.$attributeCode,
        ];

        return $field;
    }

Step5 : Modify file: Adamsmage/Checkoutattribute/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="Adamsmage_Checkoutattribute" setup_version="1.0.0">
        <sequence>
            <module name="Magento_Checkout"/>
        </sequence>
    </module>
</config>

Step6 : Create file: Adamsmage/Checkoutattribute/view/frontend/requirejs-config.js

var config = {
    config: {
        mixins: {
            'Magento_Checkout/js/action/set-billing-address': {
                'Adamsmage_Checkoutattribute/js/action/set-billing-address-mixin': true
            },
            'Magento_Checkout/js/action/set-shipping-information': {
                'Adamsmage_Checkoutattribute/js/action/set-shipping-information-mixin': true
            },
            'Magento_Checkout/js/action/create-shipping-address': {
                'Adamsmage_Checkoutattribute/js/action/create-shipping-address-mixin': true
            },
            'Magento_Checkout/js/action/place-order': {
                'Adamsmage_Checkoutattribute/js/action/set-billing-address-mixin': true
            },
            'Magento_Checkout/js/action/create-billing-address': {
                'Adamsmage_Checkoutattribute/js/action/set-billing-address-mixin': true
            }
        }
    }
};

Step7 : Create file: Adamsmage/Checkoutattribute/view/frontend/web/js/action/create-shipping-address-mixin.js

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

    return function (setShippingInformationAction) {
        return wrapper.wrap(setShippingInformationAction, function (originalAction, messageContainer) {

            if (messageContainer.custom_attributes != undefined) {
                $.each(messageContainer.custom_attributes , function( key, value ) {
                    messageContainer['custom_attributes'][key] = {'attribute_code':key,'value':value};
                });
            }

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

Step8 : Create file: view/frontend/web/js/action/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, messageContainer) {

            var billingAddress = quote.billingAddress();

            if(billingAddress != undefined) {

                if (billingAddress['extension_attributes'] === undefined) {
                    billingAddress['extension_attributes'] = {};
                }

                if (billingAddress.customAttributes != undefined) {
                    $.each(billingAddress.customAttributes, function (key, value) {

                        if($.isPlainObject(value)){
                            value = value['value'];
                        }

                        billingAddress['extension_attributes'][key] = value;
                    });
                }

            }

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

Step9 : Create file: Adamsmage/Checkoutattribute/view/frontend/web/js/action/set-shipping-information-mixin.js

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

    return function (setShippingInformationAction) {
        return wrapper.wrap(setShippingInformationAction, function (originalAction, messageContainer) {

            var shippingAddress = quote.shippingAddress();

            if (shippingAddress['extension_attributes'] === undefined) {
                shippingAddress['extension_attributes'] = {};
            }

            if (shippingAddress.customAttributes != undefined) {
                $.each(shippingAddress.customAttributes , function( key, value ) {

                    if($.isPlainObject(value)){
                        value = value['value'];
                    }

                    shippingAddress['customAttributes'][key] = value;
                    shippingAddress['extension_attributes'][key] = value;

                });
            }

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

Step10 : Create file: Adamsmage/Checkoutattribute/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\Quote\Model\BillingAddressManagement">
        <plugin disabled="false" name="Adamsmage_Checkoutattribute_Plugin_Magento_Quote_Model_BillingAddressManagement" sortOrder="10" type="Adamsmage_Checkoutattribute\Plugin\Magento\Quote\Model\BillingAddressManagement"/>
    </type>
    <type name="Magento\Quote\Model\ShippingAddressManagement">
        <plugin disabled="false" name="Adamsmage_Checkoutattribute_Plugin_Magento_Quote_Model_ShippingAddressManagement" sortOrder="10" type="Adamsmage_Checkoutattribute\Plugin\Magento\Quote\Model\ShippingAddressManagement"/>
    </type>
</config>

Step11 : Create file: Adamsmage/Checkoutattribute/Plugin/Magento/Quote/Model/BillingAddressManagement.php

<?php


namespace Adamsmage\Checkoutattribute\Plugin\Magento\Quote\Model;

class BillingAddressManagement
{

    protected $logger;

    public function __construct(
        \Psr\Log\LoggerInterface $logger
    ) {
        $this->logger = $logger;
    }

    public function beforeAssign(
        \Magento\Quote\Model\BillingAddressManagement $subject,
        $cartId,
        \Magento\Quote\Api\Data\AddressInterface $address,
        $useForShipping = false
    ) {

        $extAttributes = $address->getExtensionAttributes();
        if (!empty($extAttributes)) {

            try {
                $address->setExample($extAttributes->getExample());
            } catch (\Exception $e) {
                $this->logger->critical($e->getMessage());
            }

        }

    }
}

Step12 : Create file: Adamsmage/Checkoutattribute/Plugin/Magento/Quote/Model/ShippingAddressManagement.php

<?php


namespace Adamsmage\Adamsmage_Checkoutattribute\Plugin\Magento\Quote\Model;

class ShippingAddressManagement
{

    protected $logger;

    public function __construct(
        \Psr\Log\LoggerInterface $logger
    ) {
        $this->logger = $logger;
    }

    public function beforeAssign(
        \Magento\Quote\Model\ShippingAddressManagement $subject,
        $cartId,
        \Magento\Quote\Api\Data\AddressInterface $address
    ) {

        $extAttributes = $address->getExtensionAttributes();
        if (!empty($extAttributes)) {

            try {
                $address->setExample($extAttributes->getExample());
            } catch (\Exception $e) {
                $this->logger->critical($e->getMessage());
            }

        }

    }
}
0

Please check below code to save custom address attribute in customer,checkout shipping and billing form and also save in order table.

Module Name : Ccc_Checkout

Script to create custom attribute for address and order

app/code/Ccc/Checkout/Setup/UpgradeData.php

<?php
namespace Ccc\Checkout\Setup;

use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\UpgradeDataInterface;
use Magento\Eav\Model\Config;
use Magento\Eav\Model\Entity\Attribute\SetFactory as AttributeSetFactory;

class UpgradeData implements UpgradeDataInterface
{

    private $eavSetupFactory;

    /**
     * @var Config
     */
    private $eavConfig;

    /**
     * @var AttributeSetFactory
     */
    private $attributeSetFactory;

    public function __construct(
        Config $eavConfig,
        EavSetupFactory $eavSetupFactory,
        AttributeSetFactory $attributeSetFactory
    )
    {
        $this->eavSetupFactory = $eavSetupFactory;
        $this->eavConfig            = $eavConfig;
        $this->attributeSetFactory  = $attributeSetFactory;
    }

    /**
     * {@inheritdoc}
     */
    public function upgrade(
        ModuleDataSetupInterface $setup,
        ModuleContextInterface $context
    ) {
        $setup->startSetup();
        if (version_compare($context->getVersion(), '0.0.2','<')) { 
            $this->addUnitNumberFieldToAddress($setup);
        }
        if (version_compare($context->getVersion(), '0.0.3','<')) { 
            $this->updateUnitAttribute($setup);
        }
        $setup->endSetup();


    }

    /**
    * put your comment there...
    * 
    * @param mixed $setup
    */
    protected function addUnitNumberFieldToAddress($setup)
    {
        $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);
        $eavSetup->addAttribute('customer_address', 'unit_number', [
            'type' => 'varchar',
            'input' => 'text',
            'label' => 'Unit Number',
            'visible' => true,
            'required' => false,
            'user_defined' => true,
            'system'=> false,
            'group'=> 'General',
            'sort_order' => 71,
            'global' => true,
            'visible_on_front' => true,
        ]);       

        $customAttribute = $this->eavConfig->getAttribute('customer_address', 'unit_number');

        $customAttribute->setData(
            'used_in_forms',
            ['adminhtml_customer_address','customer_address_edit','customer_register_address'] 
        );
        $customAttribute->save();


        $installer = $setup;


        $installer->getConnection()->addColumn(
            $installer->getTable('quote_address'),
            'unit_number',
            [
                'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, 
                'length' => 255,
                'comment' => 'Unit Number'
            ]
        );

        $installer->getConnection()->addColumn(
            $installer->getTable('sales_order_address'),
            'unit_number',
            [
                'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, 
                'length' => 255,
                'comment' => 'Unit Number'
            ]
        );
    }

    public function updateUnitAttribute($setup)
    {
        $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);
        $eavSetup->updateAttribute('customer_address', 'unit_number', 'sort_order', '71');
    }
}

app/code/Ccc/Checkout/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 disabled="false" name="BillingLayoutProcessor" sortOrder="99" type="Ccc\Checkout\Plugin\Block\Checkout\LayoutProcessor"/>
  </type>
  <type name="Magento\Quote\Model\BillingAddressManagement">
    <plugin disabled="false" name="Ccc_Checkout_Plugin_Magento_Quote_Model_BillingAddressManagement" sortOrder="10" type="Ccc\Checkout\Plugin\Magento\Quote\Model\BillingAddressManagement"/>
  </type>
  <type name="Magento\Quote\Model\Quote\Address\BillingAddressPersister">
    <plugin disabled="false" name="BillingAddressSave" sortOrder="10" type="Ccc\Checkout\Plugin\Magento\Quote\Model\Quote\Address\BillingAddressPersister"/>
  </type>
  <type name="Magento\Quote\Model\ShippingAddressManagement">
    <plugin disabled="false" name="Ccc_Checkout_Plugin_Magento_Quote_Model_ShippingAddressManagement" sortOrder="10" type="Ccc\Checkout\Plugin\Magento\Quote\Model\ShippingAddressManagement"/>
  </type>
</config>

app/code/Ccc/Checkout/etc/events.xml

<?xml version="1.0"?>
<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_success">
        <observer name="custome_address_attribute_save" instance="Ccc\Checkout\Observer\SaveUnitNumberInOrder"/>
    </event>
</config>

app/code/Ccc/Checkout/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="unit_number" type="string"/>
    </extension_attributes>
</config>

Create Plugin to display custom attribute in checkout billing and shipping form

app/code/Ccc/Checkout/Plugin/Block/Checkout/LayoutProcessor

<?php
namespace Ccc\Checkout\Plugin\Block\Checkout;
use \Magento\Checkout\Block\Checkout\LayoutProcessor as MageLayoutProcessor;
class LayoutProcessor
{
   protected $_customAttributeCode = 'unit_number';
    public function afterProcess(MageLayoutProcessor $subject, $jsLayout)
    {
        if (isset($jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children']
        ['payment']['children']['payments-list']['children'])) 
        {
            foreach ($jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children']['payment']['children']['payments-list']['children'] as $key => $payment) 
            {                
                $paymentCode = 'billingAddress'.str_replace('-form','',$key);
                $jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children']['payment']['children']['payments-list']['children'][$key]['children']['form-fields']['children'][$this->_customAttributeCode] = $this->getUnitNumberAttributeForAddress($paymentCode);                
            } 

        }   

         if(isset($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']['shippingAddress']['children']['shipping-address-fieldset'])
        ){
            $jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']['shippingAddress']['children']['shipping-address-fieldset']['children'][$this->_customAttributeCode] = $this->getUnitNumberAttributeForAddress('shippingAddress');
         }

        return $jsLayout;     
    }   

    public function getUnitNumberAttributeForAddress($addressType)
    {        
        return $customField = [
            'component' => 'Magento_Ui/js/form/element/abstract',
            'config' => [                
                'customScope' => $addressType.'.custom_attributes',
                'customEntry' => null,
                'template' => 'ui/form/field',
                'elementTmpl' => 'ui/form/element/input'
            ],
            'dataScope' => $addressType.'.custom_attributes' . '.' . $this->_customAttributeCode,
            'label' => 'Unit Number',
            'provider' => 'checkoutProvider',
            'sortOrder' => 71,
            'validation' => [
               'required-entry' => false
            ],
            'options' => [],
            'filterBy' => null,
            'customEntry' => null,
            'visible' => true,
        ];
    }    
}

To save custom attribute in checkout

app/code/Ccc/Checkout/Plugin/Magento/Quote/Model/ShippingAddressManagement

<?php
namespace Ccc\Checkout\Plugin\Magento\Quote\Model;

class ShippingAddressManagement
{
    protected $logger;

    public function __construct(
        \Psr\Log\LoggerInterface $logger
    ) {
        $this->logger = $logger;
    }

    public function beforeAssign(
        \Magento\Quote\Model\ShippingAddressManagement $subject,
        $cartId,
        \Magento\Quote\Api\Data\AddressInterface $address
    ) {

        $extAttributes = $address->getExtensionAttributes();        
        if (!empty($extAttributes)) {
            try {
                $address->setUnitNumber($extAttributes->getUnitNumber());
            } catch (\Exception $e) {
                $this->logger->critical($e->getMessage());
            }
        }
    }
}

app/code/Ccc/Checkout/Plugin/Magento/Quote/Model/BillingAddressManagement

<?php
namespace Ccc\Checkout\Plugin\Magento\Quote\Model;

class BillingAddressManagement
{

    protected $logger;

    public function __construct(
        \Psr\Log\LoggerInterface $logger
    ) {
        $this->logger = $logger;
    }

    public function beforeAssign(
        \Magento\Quote\Model\BillingAddressManagement $subject,
        $cartId,
        \Magento\Quote\Api\Data\AddressInterface $address,
        $useForShipping = false
    ) {

        $extAttributes = $address->getExtensionAttributes();
        if (!empty($extAttributes)) {
            try {
                $address->setUnitNumber($extAttributes->getUnitNumber());
            } catch (\Exception $e) {
                $this->logger->critical($e->getMessage());
            }
        }
    }
}

app/code/Ccc/Checkout/Ccc/Checkout/Plugin/Magento/Quote/Model/Quote/Address

<?php
namespace Ccc\Checkout\Plugin\Magento\Quote\Model\Quote\Address;

class BillingAddressPersister
{

    protected $logger;

    public function __construct(
        \Psr\Log\LoggerInterface $logger
    ) {
        $this->logger = $logger;
    }

    public function beforeSave(
        \Magento\Quote\Model\Quote\Address\BillingAddressPersister $subject,
        $quote,
        \Magento\Quote\Api\Data\AddressInterface $address,
        $useForShipping = false
    ) {

        $extAttributes = $address->getExtensionAttributes();
        if (!empty($extAttributes)) {
            try {
                $address->setUnitNumber($extAttributes->getUnitNumber());
            } catch (\Exception $e) {
                $this->logger->critical($e->getMessage());
            }
        }
    }
}

To set custom attribute in extension attribute

app/code/Ccc/Checkout/view/frontend/requirejs-config.js

var config = {
    config: {
        mixins: {
            'Magento_Checkout/js/model/payment/method-group': {
                'Ccc_Checkout/js/model/payment/method-group-mixin': true
            },
             'Magento_Checkout/js/action/set-billing-address': {
                'Ccc_Checkout/js/action/set-billing-address-mixin': true
            },
            'Magento_Checkout/js/action/set-shipping-information': {
                'Ccc_Checkout/js/action/set-shipping-information-mixin': true
            },
            'Magento_Checkout/js/action/create-shipping-address': {
                'Ccc_Checkout/js/action/create-shipping-address-mixin': true
            },
            'Magento_Checkout/js/action/place-order': {
                'Ccc_Checkout/js/action/set-billing-address-mixin': true
            },
            'Magento_Checkout/js/action/create-billing-address': {
                'Ccc_Checkout/js/action/set-billing-address-mixin': true
            }
        }
    }
};

app/code/Ccc/Checkout/view/frontend/web/js/action/create-shipping-address-mixin.js

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

    return function (setShippingInformationAction) {
        return wrapper.wrap(setShippingInformationAction, function (originalAction, messageContainer) {

            if (messageContainer.custom_attributes != undefined) {
                $.each(messageContainer.custom_attributes , function( key, value ) {
                    messageContainer['custom_attributes'][key] = {'attribute_code':key,'value':value};
                });
            }

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

app/code/Ccc/Checkout/view/frontend/web/js/action/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, messageContainer) {

            var billingAddress = quote.billingAddress();            
            if(billingAddress != undefined) {

                if (billingAddress['extension_attributes'] === undefined) {
                    billingAddress['extension_attributes'] = {};
                }

                if (billingAddress.customAttributes != undefined) {
                    $.each(billingAddress.customAttributes, function (key, value) {                        
                        if($.isPlainObject(value)){
                            value = value['value'];
                        }

                        billingAddress['extension_attributes'][key] = value;
                    });
                }

            }

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

app/code/Ccc/Checkout/view/frontend/web/js/action/set-shipping-information-mixin.js

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

    return function (setShippingInformationAction) {
        return wrapper.wrap(setShippingInformationAction, function (originalAction, messageContainer) {

            var shippingAddress = quote.shippingAddress();

            if (shippingAddress['extension_attributes'] === undefined) {
                shippingAddress['extension_attributes'] = {};
            }

            if (shippingAddress.customAttributes != undefined) {
                $.each(shippingAddress.customAttributes , function( key, value ) {

                    if($.isPlainObject(value)){
                        value = value['value'];
                    }
                    shippingAddress['customAttributes'][key] = value;
                    shippingAddress['extension_attributes'][key] = value;

                });
            }

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

To save custom attribute in orders

app/code/Ccc/Checkout/Observer/SaveUnitNumberInOrder.php

<?php
namespace Ccc\Checkout\Observer;

class SaveUnitNumberInOrder implements \Magento\Framework\Event\ObserverInterface
{
    public function execute(\Magento\Framework\Event\Observer $observer) {
        $order = $observer->getEvent()->getOrder();
        $quote = $observer->getEvent()->getQuote();
         if ($quote->getBillingAddress()) {
              $order->getBillingAddress()->setUnitNumber($quote->getBillingAddress()->getExtensionAttributes()->getUnitNumber());
          }
          if (!$quote->isVirtual()) {            
              $order->getShippingAddress()->setUnitNumber($quote->getShippingAddress()->getUnitNumber());
          }
        return $this;
    }
}

To display custom attribute in customer account you need to overright customer edit.phtml file from vendor to your theme like below :

app/design/frontend/custom_theme/theme_name/Magento_Customer/templates/address/edit.phtml

<div class="field unit_number">
            <label class="label" for="unit_number"><span><?php echo $block->escapeHtml(__('Unit Number')) ?></span></label>
            <div class="control">

                <input type="text" name="unit_number" value="<?= $block->escapeHtmlAttr($block->getAddress()->getCustomAttribute('unit_number') ? $block->getAddress()->getCustomAttribute('unit_number')->getValue() : '') ?>" title="<?= $block->escapeHtmlAttr($block->getAddress()->getCustomAttribute('unit_number') ? $block->getAddress()->getCustomAttribute('unit_number')->getValue() : '') ?>" class="input-text <?= $block->escapeHtmlAttr($block->getAddress()->getCustomAttribute('unit_number') ? $block->getAddress()->getCustomAttribute('unit_number')->getValue() : '') ?>" id="unit_number">
            </div>
        </div>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.