I have tried to add new field in magento2 billing address section. I have followed below link to add new field in shipping address block
http://oyenetwork.com/articles/magento2-devliery-date-module-creation-from-scratch/
I have added new field to shipping address section successfully. But in my site, I have been using "Virtual products". So I want to add my new custom field into billing section. I just modified that "LayoutProcessorPlugin.php" code like below
$jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children']['payment']['children']['payments-list']['children']['delivery_date']
instead of
$jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']['shippingAddress']['children']['shipping-address-fieldset']['children']['delivery_date']
But it doesn't working. How to add my new custom field to Billing address block in magento2?
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>