Question

I'm extending shipping method section in checkout page, and it's showing well.

But it's throwing error while clicking next button after filling shipping address and shipping method information in checkout page.

Error: Fatal Error: 'Call to a member function getCommentCode() on null'

for this I have written below code:

Learning/ABlock/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="save-in-quote" type="Learning\ABlock\Model\Checkout\ShippingInformationManagementPlugin" sortOrder="10"/>
    </type>
</config>

Learning/ABlock/etc/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="Learning_ABlock" instance="Learning\ABlock\Model\Observer\SaveAccountNoObserver"/>
    </event>
</config>

Learning/ABlock/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\Checkout\Api\Data\ShippingInformationInterface">
        <attribute code="account_no" type="string"/>
    </extension_attributes>
</config>

Learning/ABlock/Model/Checkout/ShippingInformationManagementPlugin.php

<?php

namespace Learning\ABlock\Model\Checkout;

class ShippingInformationManagementPlugin
{

    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
    ) {
        $extAttributes = $addressInformation->getExtensionAttributes();
        $AccountNo = $extAttributes->getAccountNo();
        $quote = $this->quoteRepository->getActive($cartId);
        $quote->setAccountNo($AccountNo);
    }
}

Learning/ABlock/Model/Observer/SaveAccountNoObserver.php

<?php

namespace VLCSolutions\ABlock\Model\Observer;

use Magento\Framework\Event\Observer as EventObserver;
use Magento\Framework\Event\ObserverInterface;

class SaveAccountNoObserver 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->setAccountNo( $quote->getAccountNo() );

        return $this;
    }

}

Learning/ABlock/view/frontend/layout/checkout_index_index.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
    <referenceBlock name="checkout.root">
        <arguments>
            <argument name="jsLayout" xsi:type="array">
                <item name="components" xsi:type="array">
                    <item name="checkout" xsi:type="array">
                        <item name="children" xsi:type="array">
                            <item name="steps" xsi:type="array">
                                <item name="children" xsi:type="array">
                                    <item name="shipping-step" xsi:type="array">
                                        <item name="children" xsi:type="array">
                                            <item name="shippingAddress" xsi:type="array">
                                                <item name="children" xsi:type="array">
                                                    <item name="shippingAdditional" xsi:type="array">
                                                        <item name="component" xsi:type="string">uiComponent</item>
                                                        <item name="displayArea" xsi:type="string">shippingAdditional</item>
                                                        <item name="children" xsi:type="array">
                                                            <item name="additional_block" xsi:type="array">
                                                                <item name="component" xsi:type="string">Learning_ABlock/js/view/checkout/shipping/additional-block</item>
                                                            </item>
                                                        </item>
                                                    </item>
                                                </item>
                                            </item>
                                        </item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </item>
            </argument>
        </arguments>
    </referenceBlock>
</body>
</page>

Learning/ABlock/view/web/js/view/checkout/shipping/additional-block.js

define([
    'uiComponent'

], function (Component) {
    'use strict';

    return Component.extend({
        defaults: {
            template: 'Learning_ABlock/checkout/shipping/additional-block'
        }    
    });
});

Learning/ABlock/view/web/template/checkout/shipping/additional-block.html

<div xmlns="http://www.w3.org/1999/html">
    <div class="field">
        <label class="label" for="account_no">
            <span data-bind="i18n: 'Enter Account Number'"></span>
        </label>
        <div class="control">
            <input class="input-text" id="account_no" name="account-no" type="text" ></input>
        </div>
    </div>
</div>

see below image showing my custom field

enter image description here

when I debugging $addressInformation->getExtensionAttributes(); is returning null.

Could you please suggest me where I went wrong?

Was it helpful?

Solution

So you need to pass value through js, in this case you need to overwrite following js Magento/Checkout/view/frontend/web/js/model/shipping-save-processor/default.js

So you go with following way:

Create Learning/ABlock/view/frontend/requirejs-config.js


var config = {
    "map": {
        "*": {
            'Magento_Checkout/js/model/shipping-save-processor/default': 'Learning_ABlock/js/model/shipping-save-processor/default'
        }
    }
};

Create Learning/ABlock/view/frontend/web/js/model/shipping-save-processor/default.js


/*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());
                }

                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:{
                            account_no: $('[name="account_no"]').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();
                    }
                );
            }
        };
    }
);

Actually you need to pass input value by extension_attributes


extension_attributes:{
                            account_no: $('[name="account_no"]').val()
                        }

One more things, modify your Learning/ABlock/view/web/template/checkout/shipping/additional-block.html


<div xmlns="http://www.w3.org/1999/html">
    <div class="field">
        <label class="label" for="account_no">
            </span>
        </label>
        <div class="control">
            <input class="input-text" id="account_no" name="account_no" type="text" />
        </div>
    </div>
</div>

You can see here as an example.

Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top