You could do the following:
- overwrite a default js file checkout-data-resolver.js
- add extra lines to the applyBillingAddress function to check if default
billing address exists and if true to use it
step 1. Overwritting a default magento checkout-data-resolver.js
with following content
var config = {
map: {
'*': {
'Magento_Checkout/js/model/checkout-data-resolver':
'YourCompany_Checkout/js/model/checkout-data-resolver'
}
}
};
- 1.3 Copy magento file \vendor\magento\module-checkout\view\frontend\web\js\model\checkout-data-resolver.js
to the destination
\app\code\YourCompany\Checkout\view\frontend\web\js\model\checkout-data-resolver.js
step 2. Modify applyBillingAddress function
2.1 in new checkout-data-resolver.js file find applyBillingAddress. Insert following cod after var shippingAddress;
and before if (quote.billingAddress())
....
var isBillingAddressInitialized;
isBillingAddressInitialized = addressList.some(function (addressFromList) {
if (addressFromList.isDefaultBilling()) {
selectBillingAddress(addressFromList);
return true;
}
return false;
});
if(isBillingAddressInitialized){
return;
}
....
This code actually goes through customer addresses and checks if any of them has defaultBilling set to true. If such an address exists it will be used as the billing address. If not the script will fall back to the default magento behavior
As Aaron pointed out the form is added in Magento/Checkout/Block/Checkout/LayoutProcessor.php
.
With this information I developed a module with an after plugin that hooks onto that processor:
app/code/<vendor>/<module>/Model/Checkout/LayoutProcessorPlugin.php
<?php
namespace <vendor>\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;
}
}
app/code/<vendor>/<module>/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="<vendor>_<module>" setup_version="1.0.0"/>
</config>
app/code/<vendor>/<module>/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="reorder-billing-form"
type="<vendor>\<module>\Model\Checkout\LayoutProcessorPlugin" sortOrder="<yourOrder>"/>
</type>
</config>
app/code/<vendor>/<module>/registration.php
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'<vendor>_<module>',
__DIR__
);
This successfully reorders the billing address form (and hopefully saves some headaches for other people). But there is still work needed on the javascript(?) that handles the billing address is the same as shipping address
mechanism. As this still works the "standard" way.
Additional info:
I saw that in the backend if you create a new order the layout is exactly as wanted. The billing form is "before" the shipping form and the logic is the other way around too.
If I can find the time I think it might be beneficial to look at the code there. Maybe it is possible to use it in frontend too.
Best Answer
You can use the standard controller action events:
'controller_action_predispatch_' . $this->getFullActionName()
In the case of the
saveBilling
action the event iscontroller_action_predispatch_checkout_onepage_saveBilling
and for saveShipping is
controller_action_predispatch_checkout_onepage_saveShiping
.You can access the values sent to that action by