This usually happens when you specify "system" => "1" for the customer attributes. In order to make this working, you may create an upgrade script to set "system" => "0".
Here's an example:
app/code/YourVendor/YourModule/Setup/UpgradeData.php
use Magento\Framework\Setup\UpgradeDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Customer\Setup\CustomerSetupFactory
use Magento\Customer\Setup\CustomerSetup;
use Magento\Customer\Model\Customer;
...
public function __construct(
CustomerSetupFactory $customerSetupFactory
) {
$this->customerSetupFactory = $customerSetupFactory;
}
...
public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
{
if (version_compare($context->getVersion(), '0.2.0') < 0) {
$customerSetup = $this->customerSetupFactory->create();
$customerSetup->getEavConfig()
->getAttribute(Customer::ENTITY, 'example_attribute_code')
->setData('is_user_defined', 1)
->setData('system', 1)
->save();
}
}
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;
}
}
Best Answer
I solved my problem. Extra field in Shipping Address is saved to Quote first. So if you want to save new field in custom attributes in Customer details you have to do follow steps:
Extend Quote table in DB about new field Create
set-shipping-information-mixin.js and move attributes to extension attributes (in Magento docs)
Create plugin in ShippingInformationManagement before SaveAddressInformation method. Read variable from extension attributes and save it to Quote