Skip to content

Custom shipping and billing field in the checkout issue

I want to add a custom shipping and billing address field in the checkout and of course save it, in the database.
I’ve managed to render it in the shipping and billing address forms, however I’m facing some issues saving it.

Please see my code below, data patch:

namespace VendorCheckoutSetupPatchData;

use MagentoFrameworkSetupPatchDataPatchInterface;
use MagentoEavModelConfig;
use MagentoEavSetupEavSetupFactory;
use MagentoCustomerSetupCustomerSetupFactory;
class RegComAddressAttribute implements DataPatchInterface{

    const REGISTRU_COMERTULUI_ATTRIBUTE = 'regcom';
    
    public function __construct(
        public CustomerSetupFactory $customerSetupFactory,
        public EavSetupFactory $eavSetupFactory,
        public Config $eavConfig
    ) { }

    public function apply()
    {
        $customerSetup = $this->customerSetupFactory->create();

        // Add custom address attribute
        $customerSetup->addAttribute('customer_address',
            self::REGISTRU_COMERTULUI_ATTRIBUTE,
            [
            'label' => 'Registrul Comertului',
            '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' => ''
        ]);

        // Add the attribute to forms
        $attribute = $this->eavConfig->getAttribute('customer_address', self::REGISTRU_COMERTULUI_ATTRIBUTE);
        $attribute->setData(
            'used_in_forms',
            [
                'customer_address_edit',
                'customer_register_address',
                'adminhtml_customer_address'
            ]
        );
        $attribute->save();
    }

    public static function getDependencies()
    {
        return [];
    }
    public function getAliases()
    {
        return [];
    } 
}

From the db_schema.xml, i have added 2 columns, where i want to store the regcom value”

<?xml version="1.0"?>
<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
    <table name="quote_address" resource="default" engine="innodb" comment="Quote Address">
        <column xsi:type="varchar" name="regcom" nullable="true" length="255" comment="RegCom Shipping Field"/>
    </table>

    <table name="sales_order_address" resource="default" engine="innodb" comment="Sales Order Address">
        <column xsi:type="varchar" name="regcom" nullable="true" length="255" comment="RegCom Billing Field"/>
    </table>
</schema>

Until now, I have my custom field in the shipping and billing address forms, but it is not saved in the quote_address table (regcom column) Also from the backoffice, if I edit an order shipping or billing address and add manually a value for my regcom field. It is saved correctly.

For the next steps, I’ve made my extension_attributes.xml file and it’s content is:

<?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="MagentoQuoteApiDataAddressInterface">
        <attribute code="regcom" type="string"/>
    </extension_attributes>
</config>

Next, I’ve made my plugins:
In the di.xml , i have:

<?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="MagentoQuoteModelBillingAddressManagement">
        <plugin disabled="false"
                name="Vendor_Checkout_Plugin_Magento_Quote_Model_BillingAddressManagement" sortOrder="10"
                type="VendorCheckoutPluginMagentoQuoteModelBillingAddressManagement"/>
    </type>
    <type name="MagentoQuoteModelShippingAddressManagement">
        <plugin disabled="false"
                name="Vendor_Checkout_Plugin_Magento_Quote_Model_ShippingAddressManagement" sortOrder="10"
                type="VendorCheckoutPluginMagentoQuoteModelShippingAddressManagement"/>
    </type>
</config>

From the BillingAddressManagement , i have this content:

namespace VendorCheckoutPluginMagentoQuoteModel;
use PsrLogLoggerInterface;
use MagentoQuoteModelBillingAddressManagement as Subject;
use MagentoQuoteApiDataAddressInterface;
use Exception;
class BillingAddressManagement
{
    public function __construct(
        protected LoggerInterface $logger
    ) {}

    public function beforeAssign(
        Subject $subject,
        $cartId,
        AddressInterface $address,
        $useForShipping = false
    ) {
        $extAttributes = $address->getExtensionAttributes();
        if (!empty($extAttributes)) {
            try {
                $address->setRegcom($extAttributes->getRegcom());
            } catch (Exception $error) {
                $this->logger->critical($error->getMessage());
            }

        }

    }
}

And from the ShippingAddressManagement class i have:

namespace VendorCheckoutPluginMagentoQuoteModel;

use MagentoQuoteApiDataAddressInterface;
use MagentoQuoteModelShippingAddressManagement as Subject;
use PsrLogLoggerInterface;
use Exception;

class ShippingAddressManagement
{
    public function __construct(
        protected LoggerInterface $logger
    ) {}

    public function beforeAssign(
        Subject $subject,
        $cartId,
        AddressInterface $address,
        $useForShipping = false
    ) {
        $extAttributes = $address->getExtensionAttributes();
        if (!empty($extAttributes)) {
            try {
                $address->setRegcom($extAttributes->getRegcom());
            } catch (Exception $error) {
                $this->logger->critical($error->getMessage());
            }
        }
    }
}

From my events.xml:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="sales_quote_address_save_before">
        <observer name="vendor_checkout_save_quote_address_regcom" instance="VendorCheckoutObserverSaveQuoteAddressRegcom"/>
    </event>
</config>

and my observer class -> SaveQuoteAddressRegcom:

use MagentoFrameworkEventObserver;
use MagentoFrameworkEventObserverInterface;
use PsrLogLoggerInterface;
use MagentoQuoteModelQuoteAddress;
use VendorCheckoutSetupPatchDataRegComAddressAttribute;

class SaveQuoteAddressRegcom implements ObserverInterface
{
    public function __construct(
        public LoggerInterface $logger
    ){}

    public function execute(Observer $observer)
    {
        /** @var Address $quoteAddress */
        $quoteAddress = $observer->getEvent()->getQuoteAddress();
        $extensionAttributes = $quoteAddress->getExtensionAttributes();
        if ($extensionAttributes && $extensionAttributes->getRegcom()) {
            try {
                $quoteAddress->setData(RegComAddressAttribute::REGISTRU_COMERTULUI_ATTRIBUTE, $extensionAttributes->getRegcom());
            } catch (Exception $e) {
                $this->logger->critical('Error saving regcom in quote address: ' . $e->getMessage());
            }
        }
    }
}

The last part, the JS part:
In the requirejs-config.js i have:

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

And in the 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) {
            console.log('set-shipping');
            var shippingAddress = quote.shippingAddress();

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

            if (shippingAddress.customAttributes != undefined) {
                console.log('set-shipping2');
                $.each(shippingAddress.customAttributes , function( key, value ) {

                    console.log('set-shipping3');
                    if($.isPlainObject(value)){
                        value = value['value'];
                    }
                    shippingAddress['customAttributes'][key] = value;
                    shippingAddress['extension_attributes'][key] = value;

                });
            }

            return originalAction(messageContainer);
        });

    };
});

Here I don’t see the console.log('set-shipping2'); !!!!!!!!, maybe here is the issue!.

from the set-billing-address-mixin.js file , i have:

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) {
            console.log('set-billing');
            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);
        });
    };
});

and the last js file: 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) {
            console.log('set-shipping');
            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);
        });
    };
});

Not sure what I am missing, why I don’t see the regcom value saved in the table.
I followed these examples:

Trying this on Magento 2.4.7-p2

Please let me know your thoughts, hints etc .

Thank you in advance!