0

As i need custom fields for billing address under shipping address in shipping page and text entered should display as billing address all over website where billing address will display

enter image description here

2
  • You mean you need to add one custom field for billing/shipping address which should display where billing and shipping address display in customer/admin side right? – Hitesh Oct 30 '18 at 6:21
  • Thanks for comment Hit's as I need text fields for billing address where the customer can enter billing address and shipping address separately and text entered should display as billing address all over the website where billing address will display – trilok kumar Oct 30 '18 at 6:40
1

Create a plugin that will override the Checkout Layoutpreprocessor -

Magento\Checkout\Block\Checkout\LayoutProcessor

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.'),
                                    ],
                                ],
                            ],
                        ]
                    ),
                ],
            ],
        ];
    }

If you see the file has components used for JS - So in order to do that you will have to create JS files for 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';
            }
        });
    }
);

Similarly shipping-mixin.js within mixin folder -

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/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,
    createBillingAddress,
    selectBillingAddress,
    setShippingInformationAction,
    stepNavigator,
    checkoutData
) {
    'use strict';

    return function (target) {
        return target.extend({
            defaults: {
                template: 'SR_ModifiedCheckout/address'
            },
            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;
            }
        });
    }
});

You will be required to add the requirejs-config.js for mapping -

var config = {
    config: {
        mixins: {
            'Magento_Checkout/js/view/shipping': {
                'SR_ModifiedCheckout/js/mixin/shipping-mixin': true
            }
        }
    }
};

The within 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>

Similarly - 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>

For adding the billing address form add - 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"></div>
    <!-- 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'"></span>
    </fieldset>
</div>

And for billing addresses list - 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 -->

The above code has been written by Sohel Rana, and the link to the module is this You can also directly install the plugin

2
  • Thanks for comment LAW but it not storing db so it's not reflecting in myorders – trilok kumar Oct 30 '18 at 7:09
  • how to get billing address same as shipping it will work for billing address? – Jitendra prajapat Oct 30 '20 at 13:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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