4

I want to move the billing address form below the shipping address in the checkout, on the first page/step.

Therefore I created a custom module with the following LayoutProcessorPlugin.php.

But now the billing address field is displayed above the shipping address instead of below. And the default checkbox 'My billing and shipping address are the same' is now unchecked by default.

Also the line to remove the billing address on the billing step is not working. So now this is displayed on both places.

EDIT:

We also tried to use the Tigren method (https://www.tigren.com/billing-under-shipping-address-m2/) or the SR module (https://github.com/sohelrana09/modified-checkout). But these both have the same problem, the prefix is changed into an input field, instead of a select field. So it does not match the options from the backend anymore.

Image of the prefix input field (should be a select with values from backend):

enter image description here

Any idea how to solve this issue?

My LayoutProcessorPlugin.php code;

<?php

namespace Redable\ReorderBillingForm\Model\Checkout;

class LayoutProcessorPlugin
{

    /**
     * @param \Magento\Checkout\Block\Checkout\LayoutProcessor $subject
     * @param array $jsLayout
     * @return array
     */

    public function afterProcess(
        \Magento\Checkout\Block\Checkout\LayoutProcessor $subject,
        array $jsLayout
    )
    {
        // get billing address form at billing step
        $billingAddressForm = $jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children']['payment']['children']['afterMethods']['children']['billing-address-form'];

        // move address form to shipping step
        $jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']['billing-address-form'] = $billingAddressForm;

        // remove form from billing step
        unset($jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children']['payment']['children']['afterMethods']['children']['billing-address-form']);

        return $jsLayout;
    }
}
  • I'm asking this as I've seen a lot of posts where people write layout changes in PHP like this (the core does it sometimes to), it's exactly what layout XML is for so what is the advantage of writing it in PHP? – Ben Crook Feb 13 at 9:49
  • @BenCrook It's not possible to change this using the layout XML file, because the billing address can not be moved by changing the layout XML. That's why everybody is using PHP changes for this. – JGeer Feb 13 at 13:04
  • 1
    @BenCrook, Would it hurt you to post answer using XML !! I would love to follow standards :) – anonymous Feb 20 at 11:16
  • 1
    so you want to move billing address from payment to below shipping address in shipping step. right? – sarvesh Dineshkumar Patel Feb 21 at 8:46
  • 1
    @anonymous - It would definitely hurt me mentally, I try to avoid touching Magento 2 unless I have to. – Ben Crook Feb 21 at 9:11
3
+200

I was able to resolve the issue for Sohel's module with below changes

Go to file app/code/SR/ModifiedCheckout/Plugin/Block/LayoutProcessor.php and update as per below.

Update your constructor as below.

public function __construct(
    AttributeMetadataDataProvider $attributeMetadataDataProvider,
    AttributeMapper $attributeMapper,
    AttributeMerger $merger,
    CheckoutSession $checkoutSession,
    \Magento\Customer\Model\Options $options = null
) {
    $this->attributeMetadataDataProvider = $attributeMetadataDataProvider;
    $this->attributeMapper = $attributeMapper;
    $this->merger = $merger;
    $this->checkoutSession = $checkoutSession;
    $this->options = $options ?: \Magento\Framework\App\ObjectManager::getInstance()
        ->get(\Magento\Customer\Model\Options::class);
}

Now update your aroundProcess function with below code.

public function aroundProcess(
    \Magento\Checkout\Block\Checkout\LayoutProcessor $subject,
    \Closure $proceed,
    array $jsLayout
) {

    $jsLayoutResult = $proceed($jsLayout);

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

    $attributesToConvert = [
        'prefix' => [$this->options, 'getNamePrefixOptions'],
        'suffix' => [$this->options, 'getNameSuffixOptions'],
    ];

    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();
        $elements = $this->convertElementsToSelect($elements, $attributesToConvert);
        $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');
    }

    if (isset($jsLayoutResult['components']['checkout']['children']['steps']['children']['billing-step']['children']
            ['payment']['children']['payments-list']['children']
        )) {
            foreach ($jsLayoutResult['components']['checkout']['children']['steps']['children']['billing-step']['children']
                     ['payment']['children']['payments-list']['children'] as $key => $payment) {

                unset($jsLayoutResult['components']['checkout']['children']['steps']['children']['billing-step']['children']
                    ['payment']['children']['payments-list']['children'][$key]);

            }
        }

    return $jsLayoutResult;
}

Once you are done with above changes, add a new function convertElementsToSelect to convert prefix and suffix into a select dropdown.

private function convertElementsToSelect($elements, $attributesToConvert)
{
    $codes = array_keys($attributesToConvert);
    foreach (array_keys($elements) as $code) {
        if (!in_array($code, $codes)) {
            continue;
        }
        // phpcs:ignore Magento2.Functions.DiscouragedFunction
        $options = call_user_func($attributesToConvert[$code]);
        if (!is_array($options)) {
            continue;
        }
        $elements[$code]['dataType'] = 'select';
        $elements[$code]['formElement'] = 'select';

        foreach ($options as $key => $value) {
            $elements[$code]['options'][] = [
                'value' => $key,
                'label' => $value,
            ];
        }
    }

    return $elements;
}
| improve this answer | |
  • Seems to work almost perfect, but in fact we want to change the select field into a radio button. This is quite easy by changing the code into $elements[$code]['dataType'] = 'checkbox-set'; $elements[$code]['formElement'] = 'checkbox-set'; The only problem we have is that we want to check the first value by default. Any idea how to solve this? – JGeer Mar 5 at 20:43
  • And it also seems that the SR module does not remove the Billing Address checkbox on the payment step. So now there are two checkboxes in the checkout for the billing address. – JGeer Mar 5 at 21:06
  • Sure, @JGeer, I will check with above details. Would you please let me know if you are using default Magento checkout or any third party plugin for checkout page? – Jaimin Sutariya Mar 6 at 4:29
  • @JGeer, Please check updated aroundProcess method, I have added code to remove billing addresses from payment section. I will check if there is any way to select radio button by default. – Jaimin Sutariya Mar 6 at 5:04
  • Thanks again! Seems that we also needed to change the default M2 setting "Display Billing Address On" in the backend to "Payment Method" instead of below. Otherwise it still shows the billing address. Also any idea how to translate the Address: Line 1 phrase? – JGeer Mar 6 at 7:52
0

Please follow the below URL. hope it useful for you.

https://www.tigren.com/billing-under-shipping-address-m2/

if you still need to change just let me know.

| improve this answer | |
  • Thanks for the reply. I already tried this, but that creates a entire new module and form. I only want to move the default form, instead of creating a new module and form. – JGeer Feb 21 at 11:33
  • Okay, I can Understand thanks – sarvesh Dineshkumar Patel Feb 21 at 12:03
  • Thanks again! The only problem we have with this that the prefix does not work, it is displayed as an input field instead of a select box. Any idea how to solve this? – JGeer Feb 25 at 8:06
0

Hi many developers want to perform this : , You can take a help from this open source code available over github : https://github.com/sohelrana09/modified-checkout

I hope it helps..

| improve this answer | |
  • Thanks! The only problem we have with this that the prefix does not work, it is displayed as an input field instead of a select box. Any idea how to solve this? – JGeer Feb 25 at 8:06
0

You can use the Js for that!

map: {
        '*': {
            "Magento_Checkout/js/model/shipping-save-processor/default":"Magento_ShippingChange/js/shipping-save-processor"
        }
    }

Then overwrite the address like this

define([
    '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',
    'Magento_Checkout/js/action/create-shipping-address',
    'Magento_Checkout/js/action/select-shipping-address',
    'Magento_Checkout/js/checkout-data',
], function (
    ko,
    quote,
    resourceUrlManager,
    storage,
    paymentService,
    methodConverter,
    errorProcessor,
    fullScreenLoader,
    selectBillingAddressAction,
    createShippingAddress,
    selectShippingAddress,
    checkoutData,
) {
    'use strict';

    return {
        saveShippingInformation: function () {
            var payload,
                addressData,
                newShippingAddress;
            //Use this step if you want to change only field
            quote.billingAddress().firstname = quote.shippingAddress().firstname;
            //Use  this step if you want to replace whole address.
            quote.billingAddress()= 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: {

                        }
                    }
                };
            }else{
                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: {

                        }
                    }
                };
            }

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

);

| improve this answer | |

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.