8

I am using Magento-2.1.7. I am trying to move where the billing address is set by default it is set on the payment page and I would like to move this to the shipping methods section.

Here is my checkout_index_index.xml:

<item name="component" xsi:type="string">uiComponent</item>
<item name="displayArea" xsi:type="string">before-shipping-method-form</item>
<item name="children" xsi:type="array">
    <item name="billing-address" xsi:type="array">
        <item name="component" xsi:type="string">Magento_Checkout/js/view/payment/default</item>
        <item name="component" xsi:type="string">Magento_Checkout/js/view/billing-address</item>
        <item name="displayArea" xsi:type="string">billing-address</item>
        <item name="dataScope" xsi:type="string">afterMethods</item>
        <item name="provider" xsi:type="string">checkoutProvider</item>
        <item name="config" xsi:type="array">
            <item name="template" xsi:type="string">Magento_Checkout/billing-address</item>
        </item>
    </item>
</item>

This is loading the checkbox and label but the form is empty

https://i.sstatic.net/uH5B7.png

3
  • hi , any update on this?? Commented Dec 9, 2017 at 7:01
  • No sorry no luck @gajjalasandeep Commented Dec 11, 2017 at 5:50
  • Bounty +50 is not enough for this :D @gajjalasandeep . I will help you when have time.
    – Sohel Rana
    Commented Dec 11, 2017 at 6:36

4 Answers 4

17
+25

Try to follow all step:

Step 1: SR/ModifiedCheckout/etc/module.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="SR_ModifiedCheckout" setup_version="2.0.0">
        <sequence>
            <module name="Magento_Checkout"/>
        </sequence>
    </module>
</config>

Step 2: SR/ModifiedCheckout/registration.php


\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'SR_ModifiedCheckout',
    __DIR__
);

Step 3: SR/ModifiedCheckout/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 name="sr_modified_checkout_layout_processor" type="SR\ModifiedCheckout\Plugin\Block\LayoutProcessor" sortOrder="1"/>
    </type>
</config>

Step 4: SR/ModifiedCheckout/Plugin/Block/LayoutProcessor.php


namespace SR\ModifiedCheckout\Plugin\Block;

use Magento\Customer\Model\AttributeMetadataDataProvider;
use Magento\Ui\Component\Form\AttributeMapper;
use Magento\Checkout\Block\Checkout\AttributeMerger;
use Magento\Checkout\Model\Session as CheckoutSession;

class LayoutProcessor
{
    /**
     * @var AttributeMetadataDataProvider
     */
    public $attributeMetadataDataProvider;

    /**
     * @var AttributeMapper
     */
    public $attributeMapper;

    /**
     * @var AttributeMerger
     */
    public $merger;

    /**
     * @var CheckoutSession
     */
    public $checkoutSession;

    /**
     * @var null
     */
    public $quote = null;

    /**
     * LayoutProcessor constructor.
     *
     * @param AttributeMetadataDataProvider $attributeMetadataDataProvider
     * @param AttributeMapper $attributeMapper
     * @param AttributeMerger $merger
     * @param CheckoutSession $checkoutSession
     */
    public function __construct(
        AttributeMetadataDataProvider $attributeMetadataDataProvider,
        AttributeMapper $attributeMapper,
        AttributeMerger $merger,
        CheckoutSession $checkoutSession
    ) {
        $this->attributeMetadataDataProvider = $attributeMetadataDataProvider;
        $this->attributeMapper = $attributeMapper;
        $this->merger = $merger;
        $this->checkoutSession = $checkoutSession;
    }

    /**
     * Get Quote
     *
     * @return \Magento\Quote\Model\Quote|null
     */
    public function getQuote()
    {
        if (null === $this->quote) {
            $this->quote = $this->checkoutSession->getQuote();
        }

        return $this->quote;
    }

    /**
     * @param \Magento\Checkout\Block\Checkout\LayoutProcessor $subject
     * @param array $jsLayout
     * @return array
     */
    public function aroundProcess(
        \Magento\Checkout\Block\Checkout\LayoutProcessor $subject,
        \Closure $proceed,
        array $jsLayout
    ) {

        $jsLayoutResult = $proceed($jsLayout);

        if($this->getQuote()->isVirtual()) {
            return $jsLayoutResult;
        }

        if(isset($jsLayoutResult['components']['checkout']['children']['steps']['children']['shipping-step']['children']
            ['shippingAddress']['children']['shipping-address-fieldset'])) {

            $jsLayoutResult['components']['checkout']['children']['steps']['children']['shipping-step']
            ['children']['shippingAddress']['children']['shipping-address-fieldset']['children']['street']['children'][0]['placeholder'] = __('Street Address');
            $jsLayoutResult['components']['checkout']['children']['steps']['children']['shipping-step']
            ['children']['shippingAddress']['children']['shipping-address-fieldset']['children']['street']['children'][1]['placeholder'] = __('Street line 2');

            $elements = $this->getAddressAttributes();
            $jsLayoutResult['components']['checkout']['children']['steps']['children']['shipping-step']
            ['children']['shippingAddress']['children']['billing-address'] = $this->getCustomBillingAddressComponent($elements);

            $jsLayoutResult['components']['checkout']['children']['steps']['children']['shipping-step']
            ['children']['shippingAddress']['children']['billing-address']['children']['form-fields']['children']['street']['children'][0]['placeholder'] = __('Street Address');
            $jsLayoutResult['components']['checkout']['children']['steps']['children']['shipping-step']
            ['children']['shippingAddress']['children']['billing-address']['children']['form-fields']['children']['street']['children'][1]['placeholder'] = __('Street line 2');
        }

        return $jsLayoutResult;
    }

    /**
     * Get all visible address attribute
     *
     * @return array
     */
    private function getAddressAttributes()
    {
        /** @var \Magento\Eav\Api\Data\AttributeInterface[] $attributes */
        $attributes = $this->attributeMetadataDataProvider->loadAttributesCollection(
            'customer_address',
            'customer_register_address'
        );

        $elements = [];
        foreach ($attributes as $attribute) {
            $code = $attribute->getAttributeCode();
            if ($attribute->getIsUserDefined()) {
                continue;
            }
            $elements[$code] = $this->attributeMapper->map($attribute);
            if (isset($elements[$code]['label'])) {
                $label = $elements[$code]['label'];
                $elements[$code]['label'] = __($label);
            }
        }
        return $elements;
    }

    /**
     * Prepare billing address field for shipping step for physical product
     *
     * @param $elements
     * @return array
     */
    public function getCustomBillingAddressComponent($elements)
    {
        return [
            'component' => 'SR_ModifiedCheckout/js/view/billing-address',
            'displayArea' => 'billing-address',
            'provider' => 'checkoutProvider',
            'deps' => ['checkoutProvider'],
            'dataScopePrefix' => 'billingAddress',
            'children' => [
                'form-fields' => [
                    'component' => 'uiComponent',
                    'displayArea' => 'additional-fieldsets',
                    'children' => $this->merger->merge(
                        $elements,
                        'checkoutProvider',
                        'billingAddress',
                        [
                            'country_id' => [
                                'sortOrder' => 115,
                            ],
                            'region' => [
                                'visible' => false,
                            ],
                            'region_id' => [
                                'component' => 'Magento_Ui/js/form/element/region',
                                'config' => [
                                    'template' => 'ui/form/field',
                                    'elementTmpl' => 'ui/form/element/select',
                                    'customEntry' => 'billingAddress.region',
                                ],
                                'validation' => [
                                    'required-entry' => true,
                                ],
                                'filterBy' => [
                                    'target' => '${ $.provider }:${ $.parentScope }.country_id',
                                    'field' => 'country_id',
                                ],
                            ],
                            'postcode' => [
                                'component' => 'Magento_Ui/js/form/element/post-code',
                                'validation' => [
                                    'required-entry' => true,
                                ],
                            ],
                            'company' => [
                                'validation' => [
                                    'min_text_length' => 0,
                                ],
                            ],
                            'fax' => [
                                'validation' => [
                                    'min_text_length' => 0,
                                ],
                            ],
                            'telephone' => [
                                'config' => [
                                    'tooltip' => [
                                        'description' => __('For delivery questions.'),
                                    ],
                                ],
                            ],
                        ]
                    ),
                ],
            ],
        ];
    }
}

Step 5: SR/ModifiedCheckout/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="config" xsi:type="array">
                                                        <item name="template" xsi:type="string">SR_ModifiedCheckout/address</item>
                                                    </item>
                                                </item>
                                                <item name="overwriteShippingComponent" xsi:type="array">
                                                    <item name="component" xsi:type="string">SR_ModifiedCheckout/js/view/shipping</item>
                                                </item>
                                            </item>
                                        </item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>
   </body>
</page>

Step 6:SR/ModifiedCheckout/view/frontend/web/js/view/billing-address.js


/*jshint browser:true*/
/*global define*/
define(
    [
        'ko',
        'underscore',
        'jquery',
        'Magento_Ui/js/form/form',
        'Magento_Customer/js/model/customer',
        'Magento_Customer/js/model/address-list',
        'Magento_Checkout/js/model/quote',
        'Magento_Checkout/js/action/create-billing-address',
        'Magento_Checkout/js/action/select-billing-address',
        'Magento_Checkout/js/checkout-data',
        'Magento_Checkout/js/model/checkout-data-resolver',
        'Magento_Customer/js/customer-data',
        'Magento_Checkout/js/action/set-billing-address',
        'Magento_Ui/js/model/messageList',
        'mage/translate'
    ],
    function (
        ko,
        _,
        $,
        Component,
        customer,
        addressList,
        quote,
        createBillingAddress,
        selectBillingAddress,
        checkoutData,
        checkoutDataResolver,
        customerData,
        setBillingAddressAction,
        globalMessageList,
        $t

    ) {
        'use strict';

        var newAddressOption = {
                /**
                 * Get new address label
                 * @returns {String}
                 */
                getAddressInline: function () {
                    return $t('New Address');
                },
                customerAddressId: null
            },
            countryData = customerData.get('directory-data'),
            addressOptions = addressList().filter(function (address) {
                return address.getType() == 'customer-address';
            });

        addressOptions.push(newAddressOption);

        return Component.extend({
            defaults: {
                template: 'SR_ModifiedCheckout/billing-address'
            },
            currentBillingAddress: quote.billingAddress,
            addressOptions: addressOptions,
            customerHasAddresses: addressOptions.length > 1,

            /**
             * Init component
             */
            initialize: function () {
                this._super();
            },

            /**
             * @return {exports.initObservable}
             */
            initObservable: function () {
                this._super()
                    .observe({
                        selectedAddress: null,
                        isAddressFormVisible: false,
                        isAddressSameAsShipping: true,
                        saveInAddressBook: 1,
                        isAddressFormListVisible:false
                    });

                return this;
            },

            canUseShippingAddress: ko.computed(function () {
                return !quote.isVirtual() && quote.shippingAddress() && quote.shippingAddress().canUseForBilling();
            }),

            /**
             * @param {Object} address
             * @return {*}
             */
            addressOptionsText: function (address) {
                return address.getAddressInline();
            },

            /**
             * @return {Boolean}
             */
            useShippingAddress: function () {
                if (this.isAddressSameAsShipping()) {
                    this.isAddressFormVisible(false);
                    this.isAddressFormListVisible(false);
                } else {
                    if(addressOptions.length == 1) {
                        this.isAddressFormVisible(true);
                    } else {
                        this.isAddressFormListVisible(true);
                    }
                }
                return true;
            },
            /**
             * @param {Object} address
             */
            onAddressChange: function (address) {
                if(address) {
                    this.isAddressFormVisible(false);
                } else {
                    this.isAddressFormVisible(true);
                }
            },

            /**
             * @param {int} countryId
             * @return {*}
             */
            getCountryName: function (countryId) {
                return countryData()[countryId] != undefined ? countryData()[countryId].name : '';
            },

            /**
             * Get code
             * @param {Object} parent
             * @returns {String}
             */
            getCode: function (parent) {
                return _.isFunction(parent.getCode) ? parent.getCode() : 'shared';
            }
        });
    }
);

Step 7:SR/ModifiedCheckout/view/frontend/web/js/view/shipping.js


define(
    [
        'jquery',
        'underscore',
        'Magento_Ui/js/form/form',
        'ko',
        'Magento_Customer/js/model/customer',
        'Magento_Customer/js/model/address-list',
        'Magento_Checkout/js/model/address-converter',
        'Magento_Checkout/js/model/quote',
        'Magento_Checkout/js/action/create-shipping-address',
        'Magento_Checkout/js/action/select-shipping-address',
        'Magento_Checkout/js/view/shipping',
        'Magento_Checkout/js/action/create-billing-address',
        'Magento_Checkout/js/action/select-billing-address',
        'Magento_Checkout/js/action/set-shipping-information',
        'Magento_Checkout/js/model/step-navigator',
        'Magento_Checkout/js/checkout-data'
    ],
    function(
        $,
        _,
        Component,
        ko,
        customer,
        addressList,
        addressConverter,
        quote,
        createShippingAddress,
        selectShippingAddress,
        shipping,
        createBillingAddress,
        selectBillingAddress,
        setShippingInformationAction,
        stepNavigator,
        checkoutData
    ) {
        'use strict';

        _.extend(shipping.prototype, {
            setShippingInformation: function () {
                if (this.validateShippingInformation() && this.validateBillingInformation()) {
                    setShippingInformationAction().done(
                        function () {
                            stepNavigator.next();
                        }
                    );
                }
            },
            validateBillingInformation: function() {

                if($('[name="billing-address-same-as-shipping"]').is(":checked")) {
                    if (this.isFormInline) {
                        var shippingAddress = quote.shippingAddress();
                        var addressData = addressConverter.formAddressDataToQuoteAddress(
                            this.source.get('shippingAddress')
                        );
                        //Copy form data to quote shipping address object
                        for (var field in addressData) {

                            if (addressData.hasOwnProperty(field) &&
                                shippingAddress.hasOwnProperty(field) &&
                                typeof addressData[field] != 'function' &&
                                _.isEqual(shippingAddress[field], addressData[field])
                            ) {
                                shippingAddress[field] = addressData[field];
                            } else if (typeof addressData[field] != 'function' &&
                                !_.isEqual(shippingAddress[field], addressData[field])) {
                                shippingAddress = addressData;
                                break;
                            }
                        }

                        if (customer.isLoggedIn()) {
                            shippingAddress.save_in_address_book = 1;
                        }
                        var newBillingAddress = createBillingAddress(shippingAddress);
                        selectBillingAddress(newBillingAddress);
                    } else {
                        selectBillingAddress(quote.shippingAddress());
                    }

                    return true;
                }

                var selectedAddress = $('[name="billing_address_id"]').val();
                if(selectedAddress) {
                    var res = addressList.some(function (addressFromList) {
                        if (selectedAddress == addressFromList.customerAddressId) {
                            selectBillingAddress(addressFromList);
                            return true;
                        }
                        return false;
                    });

                    return res;
                }

                this.source.set('params.invalid', false);
                this.source.trigger('billingAddress.data.validate');

                if (this.source.get('params.invalid')) {
                    return false;
                }

                var addressData = this.source.get('billingAddress'),
                    newBillingAddress;

                if ($('#billing-save-in-address-book').is(":checked")) {
                    addressData.save_in_address_book = 1;
                }

                newBillingAddress = createBillingAddress(addressData);
                selectBillingAddress(newBillingAddress);

                return true;
            }
        });

        return Component.extend({});
    }
);

Download full module from here

6
  • Hey Buddy, It works fine but how to keep the data persist after filling the billing address form like we are having it in the shipping section. I would like to keep the data prefilled if we refresh the checkout page.
    – Rushit
    Commented May 23, 2018 at 6:36
  • after use, your module Name Prefix convert into the textbox. That should be shown in a drop-down. Please advise! Commented May 30, 2018 at 18:51
  • Hi @Sohel, this solution was working fine until 2.2.4, after upgrading magento to 2.2.5 version it started showing following error. "Please check the billing address information. regionId is a required field."
    – karthik
    Commented Jul 2, 2018 at 9:14
  • I will check and update this as well, thanks for letting me know.
    – Sohel Rana
    Commented Jul 2, 2018 at 9:22
  • Its working fine but it not storing db so it's not reflecting in myorders Sohel Rana Commented Oct 31, 2018 at 7:02
7

Continue

Step 8:SR/ModifiedCheckout/view/frontend/web/template/billing-address.html


<div class="checkout-billing-address">

    <div class="billing-address-same-as-shipping-block field choice" data-bind="visible: canUseShippingAddress()">
        <input type="checkbox" name="billing-address-same-as-shipping"
               data-bind="checked: isAddressSameAsShipping, click: useShippingAddress, attr: {id: 'billing-address-same-as-shipping'}"/>
        <label data-bind="attr: {for: 'billing-address-same-as-shipping'}">
            <span data-bind="i18n: 'This address is also my billing address'"></span>
        </label>
    </div>
    <fieldset class="fieldset">
        <!-- ko template: 'SR_ModifiedCheckout/billing-address/list' --><!-- /ko -->
        <!-- ko template: 'SR_ModifiedCheckout/billing-address/form' --><!-- /ko -->
    </fieldset>

</div>

Step 9: SR/ModifiedCheckout/view/frontend/web/template/address.html


<li id="shipping" class="checkout-shipping-address" data-bind="fadeVisible: visible()">
    <div class="step-title" data-bind="i18n: 'Shipping Address'" data-role="title"></div>
    <div id="checkout-step-shipping"
         class="step-content"
         data-role="content">

        <!-- ko if: (!quoteIsVirtual) -->
            <!-- ko foreach: getRegion('customer-email') -->
                <!-- ko template: getTemplate() --><!-- /ko -->
            <!--/ko-->
        <!--/ko-->

        <!-- ko foreach: getRegion('address-list') -->
        <!-- ko template: getTemplate() --><!-- /ko -->
        <!--/ko-->

        <!-- ko foreach: getRegion('address-list-additional-addresses') -->
        <!-- ko template: getTemplate() --><!-- /ko -->
        <!--/ko-->

        <!-- Address form pop up -->
        <!-- ko if: (!isFormInline) -->
        <button type="button"
                data-bind="click: showFormPopUp, visible: !isNewAddressAdded()"
                class="action action-show-popup">
            <span data-bind="i18n: 'New Address'"></span></button>
        <div id="opc-new-shipping-address" data-bind="visible: isFormPopUpVisible()">
            <!-- ko template: 'Magento_Checkout/shipping-address/form' --><!-- /ko -->
        </div>
        <!-- /ko -->

        <!-- ko foreach: getRegion('before-form') -->
        <!-- ko template: getTemplate() --><!-- /ko -->
        <!--/ko-->

        <!-- Inline address form -->
        <!-- ko if: (isFormInline) -->
        <!-- ko template: 'Magento_Checkout/shipping-address/form' --><!-- /ko -->
        <!-- /ko -->

        <!-- ko if: (isFormInline) -->
        <span class="mandatory" data-bind="i18n: 'Mandatory fields'"></span>
        <!-- /ko -->
        <!-- Inline Billing address form -->
        <!-- ko foreach: getRegion('billing-address') -->
        <!-- ko template: getTemplate() --><!-- /ko -->
        <!-- /ko -->
    </div>
</li>


<!--Shipping method template-->
<li id="opc-shipping_method"
    class="checkout-shipping-method"
    data-bind="fadeVisible: visible(), blockLoader: isLoading"
    role="presentation">
    <div class="checkout-shipping-method">
        <div class="step-title" data-bind="i18n: 'Shipping Methods'" data-role="title"></div>
        <!-- ko foreach: getRegion('before-shipping-method-form') -->
        <!-- ko template: getTemplate() --><!-- /ko -->
        <!-- /ko -->
        <div id="checkout-step-shipping_method"
             class="step-content"
             data-role="content"
             role="tabpanel"
             aria-hidden="false">
            <!-- ko if: rates().length  -->
            <form class="form methods-shipping" id="co-shipping-method-form" data-bind="submit: setShippingInformation" novalidate="novalidate">
                <div id="checkout-shipping-method-load">
                    <table class="table-checkout-shipping-method">
                        <thead>
                            <tr class="row">
                                <th class="col col-method" data-bind="i18n: 'Select Method'"></th>
                                <th class="col col-price" data-bind="i18n: 'Price'"></th>
                                <th class="col col-method" data-bind="i18n: 'Method Title'"></th>
                                <th class="col col-carrier" data-bind="i18n: 'Carrier Title'"></th>
                            </tr>
                        </thead>
                        <tbody>

                        <!--ko foreach: { data: rates(), as: 'method'}-->
                        <tr class="row" data-bind="click: $parent.selectShippingMethod">
                            <td class="col col-method">
                                <!-- ko ifnot: method.error_message -->
                                <!-- ko if: $parent.rates().length == 1 -->
                                <input class="radio"
                                       type="radio"
                                       data-bind="attr: {
                                                    checked: $parent.rates().length == 1,
                                                    'value' : method.carrier_code + '_' + method.method_code,
                                                    'id': 's_method_' + method.method_code,
                                                    'aria-labelledby': 'label_method_' + method.method_code + '_' + method.carrier_code + ' ' + 'label_carrier_' + method.method_code + '_' + method.carrier_code
                                                 }" />
                                <!-- /ko -->
                                <!--ko ifnot: ($parent.rates().length == 1)-->
                                <input type="radio"
                                       data-bind="
                                                value: method.carrier_code + '_' + method.method_code,
                                                checked: $parent.isSelected,
                                                attr: {
                                                    'id': 's_method_' + method.carrier_code + '_' + method.method_code,
                                                    'aria-labelledby': 'label_method_' + method.method_code + '_' + method.carrier_code + ' ' + 'label_carrier_' + method.method_code + '_' + method.carrier_code
                                                },
                                                click: $parent.selectShippingMethod"
                                       class="radio"/>
                                <!--/ko-->
                                <!-- /ko -->
                            </td>
                            <td class="col col-price">
                                <!-- ko foreach: $parent.getRegion('price') -->
                                <!-- ko template: getTemplate() --><!-- /ko -->
                                <!-- /ko -->
                            </td>

                            <td class="col col-method"
                                    data-bind="text: method.method_title, attr: {'id': 'label_method_' + method.method_code + '_' + method.carrier_code}"></td>

                            <td class="col col-carrier"
                                    data-bind="text: method.carrier_title, attr: {'id': 'label_carrier_' + method.method_code + '_' + method.carrier_code}"></td>
                        </tr>

                        <!-- ko if:  method.error_message -->
                        <tr class="row row-error">
                            <td class="col col-error" colspan="4">
                                <div class="message error">
                                    <div data-bind="text: method.error_message"></div>
                                </div>
                                <span class="no-display">
                                    <input type="radio" data-bind="attr: {'value' : method.method_code, 'id': 's_method_' + method.method_code}"/>
                                </span>
                            </td>
                        </tr>
                        <!-- /ko -->

                        <!-- /ko -->
                        </tbody>
                    </table>
                </div>

                <div id="onepage-checkout-shipping-method-additional-load">
                    <!-- ko foreach: getRegion('shippingAdditional') -->
                    <!-- ko template: getTemplate() --><!-- /ko -->
                    <!-- /ko -->
                </div>
                <!-- ko if: errorValidationMessage().length > 0 -->
                <div class="message notice">
                    <span><!-- ko text: errorValidationMessage()--><!-- /ko --></span>
                </div>
                <!-- /ko -->
                <div class="actions-toolbar" id="shipping-method-buttons-container">
                    <div class="primary">
                        <button data-role="opc-continue" type="submit" class="button action continue primary">
                            <span><!-- ko i18n: 'Next'--><!-- /ko --></span>
                        </button>
                    </div>
                </div>
            </form>
            <!-- /ko -->
            <!-- ko ifnot: rates().length > 0 --><div class="no-quotes-block"><!-- ko i18n: 'Sorry, no quotes are available for this order at this time'--><!-- /ko --></div><!-- /ko -->
        </div>
    </div>
</li>

Step 10:SR/ModifiedCheckout/view/frontend/web/template/billing-address/form.html


<div class="billing-address-form" data-bind="fadeVisible: isAddressFormVisible">
    <div class="step-title" data-bind="i18n: 'Billing Address'" data-role="title">
    <!-- ko foreach: getRegion('before-fields') -->
    <!-- ko template: getTemplate() --><!-- /ko -->
    <!--/ko-->
    <fieldset id="billing-new-address-form" class="fieldset address">
        <!-- ko foreach: getRegion('additional-fieldsets') -->
        <!-- ko template: getTemplate() --><!-- /ko -->
        <!--/ko-->
        <!-- ko if: (isCustomerLoggedIn) -->
        <div class="field save-address">
            <input type="checkbox" class="checkbox" id="billing-save-in-address-book" data-bind="checked: saveInAddressBook" />
            <label class="label" for="billing-save-in-address-book">
                <span data-bind="i18n: 'Save in address book'"></span>
            </label>
        </div>
        <!-- /ko -->
        <span class="mandatory" data-bind="i18n: 'Mandatory fields'">
    </fieldset>
</div>

Step 11:SR/ModifiedCheckout/view/frontend/web/template/billing-address/list.html


<!-- ko if: (customerHasAddresses && isAddressFormListVisible)-->
<div class="field field-select-billing">
    <div class="control" data-bind="if: (addressOptions.length > 1)">
        <select class="select" id="billing_address_id" name="billing_address_id" data-bind="
        options: addressOptions,
        optionsText: addressOptionsText,
        optionsValue: 'customerAddressId',
        value: selectedAddress,
        event: {change: onAddressChange(selectedAddress())};
    "></select>
    </div>
</div>
<!-- /ko -->
3
  • Thanks for your code, works great. But i'm having an issue with autofill because it won't work anymore for billing address. It will autofill but magento doesn't see the values. For the shipping form it still works great. Any idea on this? Can it be that there isn't a form attribute around it?
    – Sanne
    Commented Jan 8, 2019 at 9:16
  • Showing error in console - Uncaught ReferenceError: Unable to process binding "if: function(){return (addressOptions.length > 1) }" Commented Oct 21, 2020 at 6:06
  • Check github.com/sohelrana09/modified-checkout
    – Sohel Rana
    Commented Oct 21, 2020 at 6:14
1

I am not sure if you have achieved what you were looking to do. However, I tried the approach as in the question and the thing which was missing was the LayoutProcessor plugin which actually loads the billing-address-form when you uncheck the 'my billing address is not the same as shipping address' checkbox. My solution to this would be:

....
<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="shipping-address-fieldset" xsi:type="array">
                                                        <item name="children" xsi:type="array">
                                                            <item name="billing-address" xsi:type="array">
                                                                <item name="component" xsi:type="string">uiComponent</item>
                                                                <item name="displayArea" xsi:type="string">billing-address</item>
                                                                <item name="children" xsi:type="array">
                                                                    <!-- merge additional data after shipping address here -->
                                                                </item>
                                                            </item>
                                                        </item>
                                                    </item>
                                                </item>
                                            </item>
                                        </item>
                                    </item>

And then \etc\di.xml create a plugin reference

<type name="Magento\Checkout\Block\Checkout\LayoutProcessor">
    <plugin name="<module>_move_billing_address_component_core" type="<module>\Plugin\Block\LayoutProcessor" />
</type>

Also, your plugin can look like this(you will have to add the missing constructor and the function calls from the core LayoutProcessor):

 /**
 * Process js Layout of block
 *
 * @param array $jsLayout
 * @return array
 * @throws \Magento\Framework\Exception\LocalizedException
 */
public function afterProcess(BaseLayoutProcessor $subject, $jsLayout)
{
    //this reference is deprecated, but I am using the same
    $attributesToConvert = [
        'prefix' => [$this->getOptions(), 'getNamePrefixOptions'],
        'suffix' => [$this->getOptions(), 'getNameSuffixOptions'],
    ];

    $elements = $this->getAddressAttributes();
    $elements = $this->convertElementsToSelect($elements, $attributesToConvert);
    //unset the existing billing-address component
    unset($jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children']
        ['payment']['children']['afterMethods']);

    if (isset($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']
        ['shippingAddress']['children']['shipping-address-fieldset']['children'])) {
        //reference to the component where you want the billing address added
        $billingAddressLayout = &$jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']
        ['shippingAddress']['children']['shipping-address-fieldset']['children'];

        if (!isset($billingAddressLayout['billing-address']['children'])) {
            $billingAddressLayout['billing-address']['children'] = [];
        }
        $component['billing-address-form']                   = $this->getBillingAddressComponent('shared', $elements);
        $billingAddressLayout['billing-address']['children'] =
            array_merge_recursive(
                $component,
                $billingAddressLayout['billing-address']['children']
            );

    }

    return $jsLayout;
}
0

You can try this below extension given here in my blog:

https://marketplace.magento.com/ananta-module-improvedcheckout.html

Tested and worked with 2.4.5

enter image description here enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.