Magento – How to save value to customer attributes from checkout page in magento 2

customer-attributemagento2savedata

I have created a customer attribute which created dynamically from backend its fine works in customer_account_create and customer_account_edit and I stuck at saving customer attribute field data in after saving address information in the checkout form. After filling shipping address, shipping and press next button method I m getting the following error

message: "Internal Error. Details are available in Magento log file. Report ID: webapi-5d3aa1ebbf82a"

Exception log: 'Report ID: webapi-5d3aa1ebbf82a; Message: Property "TestAge" does not have corresponding setter in class "Magento\Quote\Api\Data\AddressExtensionInterface".' in D:\wamp64\www\m2\magento\vendor\magento\framework\Webapi\ErrorProcessor

and I followed this link

app/code/Cm/CustomerAttribute/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="cm__fields_layoutprocessor" xsi:type="object">Cm\CustomerAttribute\Block\Checkout\LayoutProcessor</item>
            </argument>
        </arguments>
    </type>
</config>

app/code/ Cm/CustomerAttribute/Block/Checkout/LayoutProcessor.php

<?php
namespace Cm\CustomerAttribute\Block\Checkout;

class LayoutProcessor
{
    protected $customerSession;
    protected $resultPageFactory;
    public function __construct(\Cm\CustomerAttribute\Helper\Customerattribute $helper,
    \Magento\Customer\Api\CustomerRepositoryInterface $customerRepositoryInterface,
    \Magento\Customer\Model\Session $customerSession)
    {
        $this->helper = $helper;
        $this->customerRepositoryInterface = $customerRepositoryInterface;
        $this->customerSession = $customerSession;
    }
    public function afterProcess(
        \Magento\Checkout\Block\Checkout\LayoutProcessor $subject,
        $jsLayout
    ) {
        $attributeCollection = $this->helper->getUserDefinedAttribures();

       if ($attributeCollection->getSize() > 0) 
       {
         foreach ($attributeCollection as $attribute)

         {
        if ($this->helper->isAttribureForCheckoutRegister($attribute->getAttributeCode()))
        {
        $frontEndLabel = $attribute->getStoreLabel($this->helper->getStoreId());

        $customAttributeCode = $attribute->getAttributeCode();
      if($this->customerSession->isLoggedIn()) {

        $customerId =$this->customerSession->getCustomer()->getId();
         $customer =$this->customerRepositoryInterface->getById($customerId);
        $customerAttr = $customer->getCustomAttribute($customAttributeCode)->getValue();
        }
        else
        {
          $customerAttr = NULL;
        }
        $fieldRequiredClass = ($attribute->getIsRequired()) ? 'true' : '' ;
        if($fieldRequiredClass == '')
        {
            $fieldRequiredClass = false;
        }
        $fieldFrontendClass = ($attribute->getFrontendClass()) ? $attribute->getFrontendClass() : '';
        $fieldInput =$attribute->getFrontendInput();
        $attributeId = $attribute->getAttributeId();
        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();

        $attributeOptionAll = $objectManager->create(\Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection::class)
                                    ->setPositionOrder('asc')
                                    ->setAttributeFilter($attributeId)
                                    ->setStoreFilter()
                                    ->load();
        $opt_val = array();
        $allOptions=array();


        foreach ($attributeOptionAll->getData() as $key => $v) 
        {
          // $allid = $v['attribute_id'];
          $opt_val['value'] = $v['option_id'];
          $opt_val['label'] = $v['value'];
          $allOptions[] = $opt_val;
      }
        $jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']
        ['shippingAddress']['children']['shipping-address-fieldset']['children'][$customAttributeCode] = $this->addNewField($customAttributeCode,$frontEndLabel, $fieldRequiredClass,  $fieldFrontendClass,$fieldInput,  $allOptions, $customerAttr );

        }
      }
    }
    return $jsLayout;
    }

    private function addNewField($customAttributeCode, $frontEndLabel, $fieldRequiredClass,  $fieldFrontendClass, $fieldInput, $allOptions, $customerAttr  )
    {
        if($fieldInput == 'text')
        {
            $fieldInputType = 'input';
            $fieldAbstract =  'abstract';
        }
        elseif ($fieldInput == 'date') {
           $fieldInputType = 'date'; 
           $fieldAbstract =  'date';
        }
        elseif ($fieldInput == 'select') {
           $fieldInputType = 'select'; 
           $fieldAbstract =  'select';
        }

        elseif ($fieldInput == 'boolean') {
           $fieldInputType = 'select'; 
           $fieldAbstract =  'select';
           $allOptions = [
                 ['value' => '0', 'label' => 'No'],
                 ['value' => '1', 'label' => 'Yes']
               ];

        }
         elseif ($fieldInput == 'textarea') {
           $fieldInputType = 'textarea'; 
           $fieldAbstract =  'textarea';

        }
        $customField = [
            'component' => 'Magento_Ui/js/form/element/'.$fieldAbstract.'',
            'config' => [
                'customScope' => 'shippingAddress.custom_attributes',
                'customEntry' => null,
                'template' => 'ui/form/field',
                'elementTmpl' => 'ui/form/element/'.$fieldInputType.'',
                'rows' => 5
                // 'tooltip' => [
                //     'description' => 'this is what the field is for',
                // ],
            ],
            'dataScope' => 'shippingAddress.custom_attributes' . '.' . $customAttributeCode,
            'label' => $frontEndLabel,
            'provider' => 'checkoutProvider',
            'sortOrder' => 1000,
            'validation' => [$fieldFrontendClass => true , 
                'required-entry' => $fieldRequiredClass],
            'options' =>  $allOptions,
            'filterBy' => null,
            'customEntry' => null,
            'visible' => true,
            'value' => $customerAttr
        ];

         return $customField;
    }
}

app/code/Cm/CustomerAttribute/view/frontend/requirejs-config.js

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

app/code/Cm/CustomerAttribute/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/Cm/CustomerAttribute/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/Cm/CustomerAttribute/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);
        });
    };
});

app/code/Cm/CustomerAttribute/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\Model\ShippingInformationManagement">
         <plugin name="Cm_CustomerAttribute::ShippingInformationManagement" 
 type="Cm\CustomerAttribute\Plugin\Checkout\Model\ShippingInformationManagement" sortOrder="1"/>
          </type>
      </config>

app/code/Cm/CustomerAttribute/Plugin/Checkout/Model/ShippingInformationManagement.php

   <?php
    namespace Cm\CustomerAttribute\Plugin\Checkout\Model;

  class ShippingInformationManagement
{
    /**
     * @param \Magento\Checkout\Model\ShippingInformationManagement $subject
     * @param $cartId
     * @param \Magento\Checkout\Api\Data\ShippingInformationInterface $addressInformation
     */
     protected $helper;
     protected $quoteRepository;
     public function __construct(

    \Bootsgrid\CustomerAttribute\Helper\Customerattribute $helper,
     \Magento\Quote\Model\QuoteRepository $quoteRepository
    ) {

        $this->helper = $helper;
         $this->quoteRepository = $quoteRepository;
    }


    public function beforeSaveAddressInformation(
        \Magento\Checkout\Model\ShippingInformationManagement $subject,
        $cartId,
        \Magento\Checkout\Api\Data\ShippingInformationInterface $addressInformation
    ) {
       $extensionAttributes = $addressInformation->getShippingAddress();
       $custAttributes = $extensionAttributes->getExtensionAttributes();
       if($custAttributes )
       {
         $quote = $this->quoteRepository->getActive($cartId);


        $customField = $custAttributes->getTestTextarea();
        $cus = $custAttributes->getTestAge();
        $quote->setTestAge($cus);
        $quote->setTestTextarea($customField);



      }
        // error_log(print_r($custAttributes,1));
    }
}

How to save the customer_field dynamically in sales_order and quote order in magento 2

image:

enter image description here
When using extension_attributes.xml file its saved into db but i want to pass a attribute_code dynamically in the xml file

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="test_age" type="string" /> \* how to pass "test_age" dynamically *\
        <attribute code="test_textarea" type="string" />
    </extension_attributes>
</config>

enter image description here

Best Answer

Actually, the problem is when the page is loaded your custom field is not added means it is undefined and that is why that field value length is not getting. That is why it's occurred error.

Just you will and the condition in your js file. (when field length code is written.)

if (typeof [field] === "undefined") {
 // after your actually code
}

Let me know if you still facing the issue.

Related Topic