Our Work

Address used in Last Order in Checkout page for Customer in Magento 2

Updated today


Concepts we are using: mixins(for overriding js components ),plugins(for overriding public methods)
This article will explain how to show customer previous order data in checkout and how to override core javascript files in magento

  • Create a module
  • Assign Previous order data to quote
  • Add our Quote data to Window.CheckoutConfig
  • Override core js files to preselect checkout data

Flow Diagram 

1.Module

1.1 Create module

Create module which is  extended from magento_checkout module.Please refer this article for Module Creation.
https://ktree.com/create-magento-2-module-custom.html

1.2 Folder structure

2.Assign Previous order data to customer quote

By using InitCheckout() method magento will assign customer default shipping and billing addresses  to quote.
We need to modify InitCheckout() method to assign previous order data to quote so, Instead of overriding InitCheckout() method created after plugin method to InitCheckout() method.

2.1 Plugin In magento2

A plugin is a great way to expand or edit a public method’s behavior by using code before, after or around method.
By using this Magento 2 Plugin Interception, you can modify the behavior of a class while there is no need to change the class directly.
In our example we are using after plugin interceptor to modify InitCheckout() method

2.2 After Method

Magento runs all after methods following the completion of the observed method. Magento requires these methods have a return value and they must have the same name as the observed method with ‘after’ as the prefix.
Below after method will copy the customer previous order data to quote

Note: after* methods will also supports parameters of observed methods (if magento version >2.2)

To create after plugin to method to InitCheckout() we need to follow following steps.

Create di.xml file in etc/frontend folder
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
  <type name="Magento\Checkout\Model\Type\Onepage">
       <plugin name="assign_address_to_quote" type="KTree\Custom\Model\Type\OnepagePlugin" />
   </type>
</config>

Here: we are modifying  ”initCheckout” in “Magento\Checkout\Model\Type\Onepage” 
and updating customer last order data to  Quote Addresses.

Create plug-in for modify after method in OnepagePlugin.php file under KTree/Custom/Model/Type/OnepagePlugin folder.

File: app/code/KTree/Custom/Model/Type/OnepagePlugin.php

<?php
namespace KTree\Custom\Model\Type;
use KTree\Custom\Helper\Data;
class OnepagePlugin
{
  protected $_localHelper;
  public function __construct(Data $localhelper){
    $this->_localHelper = $localhelper;
  }
  public function afterInitCheckout(\Magento\Checkout\Model\Type\Onepage $checkout, $result)
  {
      $customerSession = $checkout->getCustomerSession();
      /*
       * want to load the correct customer information by assigning to address
       * instead of just loading from sales/quote_address
       */
      $customer = $customerSession->getCustomerDataObject();
      if ($customer) {
          $order = $this->_localHelper->getLastOrder();
          if($order && $order->getId()){
            $this->_localHelper ->saveCustomerAddressFromOrder($order);
          }
      }
      return $result;
  }
}
?>
  • saveCustomerAddressFromOrder() method will add customer latest order data to quote (like shipping ,billing address and payment method to customer current quote) using checkout session object.
  • getLastOrder() method return customer latest order.

File: app/code/KTree/Custom/Helper/Data.php

<?php
namespace KTree\Custom\Helper;
use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Checkout\Model\Session;
use Magento\Customer\Api\AddressRepositoryInterface;
class Data extends AbstractHelper{
  protected $_ordersFactory;
  protected $_session;
  protected $_checkoutSession;
  /**
   * @var AddressRepositoryInterface
   */
  protected $addressRepository;
  protected $_lastOrder ;
  public function __construct(\Magento\Customer\Model\Session $session,
  Session $checkoutSession,
  \Magento\Sales\Model\ResourceModel\Order\CollectionFactory $ordersFactory,
    AddressRepositoryInterface $addressRepository
  )
  {
    $this->_session = $session;
    $this->_ordersFactory = $ordersFactory;
    $this->_checkoutSession = $checkoutSession;
    $this->addressRepository = $addressRepository;
  }
  public function saveCustomerAddressFromOrder($order)
    {
        if ($this->_checkoutSession->getQuote() && $order->getId()) {
          $shippingAddressObj = $billingAddressObj = $order->getBillingAddress()->getData();
          $paymentDetails = $order->getPayment()->getData();
          if(!$order->getIsVirtual()){
            $shippingAddressObj = $order->getShippingAddress()->getData();
          }
          $quote = $this->_checkoutSession->getQuote();
          $quote->getBillingAddress()->addData($billingAddressObj);
          $quote->getShippingAddress()->addData($shippingAddressObj);
          $quote->getPayment()->addData($paymentDetails);
          $quote->save();
          return;
        }
    }
    public function getLastOrder()
    {
      if(!$this->_lastOrder){
      $customerId = $this->_session->getCustomer()->getId();
        $collection = $this->_ordersFactory->create()->addFieldToFilter(
            'customer_id',
            $customerId
        )->setOrder(
            'created_at',
            'desc'
        )->setPageSize(
            1
        )->load();
        foreach ($collection as $order) {
            $this->_lastOrder = $order;
            break;
        }
      }
        return $this->_lastOrder;
    }
}
?>

3. Add our Quote data to Window.CheckoutConfig

  • Magento 2 all required data saved in window.checkoutConfig so at the time of checkout it can be fetched by js models easily.
  • Display previous order data in frontend we need to save data in window.CheckoutConfig provider . In order to achieve this we need to add one more after plugin to GetConfig() method.
  • In this example we are adding 3 more attributes('quote_shipping_address','quote_shipping_address','quote_paymentmethod') to window.CheckoutConfig.
  • Add below configuration to di.xml file to get the customer config information
<type name="Magento\Checkout\Model\DefaultConfigProvider">
        <plugin name="add_new_attribute_to_quote_data" type="KTree\Custom\Model\DefaultConfigProviderPlugin" /> </type>

The final  app/code/KTree/Custom/etc/frontend/di.xml file will be like below
File: app/code/KTree/Custom/etc/frontend/di.xml

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
  <type name="Magento\Checkout\Model\Type\Onepage">
       <plugin name="assign_address_to_quote" type="KTree\Custom\Model\Type\OnepagePlugin" />
   </type>
   <type name="Magento\Checkout\Model\DefaultConfigProvider">
        <plugin name="add_new_attribute_to_quote_data" type="KTree\Custom\Model\DefaultConfigProviderPlugin" />
    </type>
</config>

File: app/code/KTree/Custom/Model/DefaultConfigProviderPlugin.php

<?php
namespace KTree\Custom\Model;
use Magento\Checkout\Model\Session as CheckoutSession;
class DefaultConfigProviderPlugin
{
  /**
   * @var CheckoutSession
   */
  private $checkoutSession;
  /**
   * @var \Magento\Quote\Api\CartRepositoryInterface
   */
  private $quoteRepository;
    public function __construct(
      CheckoutSession $checkoutSession,
      \Magento\Quote\Api\CartRepositoryInterface $quoteRepository
  ){
    $this->checkoutSession = $checkoutSession;
    $this->quoteRepository = $quoteRepository;
  }
  public function afterGetConfig(\Magento\Checkout\Model\DefaultConfigProvider $config,
  $output){
    $output= $this->getCustomQuoteData($output);
    return $output;
  }
  private function getCustomQuoteData($output)
  {
      if ($this->checkoutSession->getQuote()->getId()) {
          $quote = $this->quoteRepository->get($this->checkoutSession->getQuote()->getId());
          $output['quoteData']['quote_shipping_address'] =    $quote->getShippingAddress()->getData()['customer_address_id'];
          $output['quoteData']['quote_billing_address'] = $quote->getBillingAddress()->getData()['customer_address_id'];
          $output['quoteData']['quote_paymentmethod'] = $quote->getPayment()->getMethod();
      }
      return $output;
  }
}
?>

Now run “php bin/magento cache:clean” command to clear cache and refresh page , 
press F12 and run the window.checkoutConfig.quoteData.quote_shipping_address. You see,

4. Override core js files to preselect checkout data

We need to override core js files to select shipping  billing addresses and payment method in frontend. 
In order to override js files we need to create requirejs-config.js in app/code/KTree/Custom/view/frontend folder.

4.1 Mixins

  • A mixin is a class whose methods are added to, or mixed in, with another class. A base class includes the methods from a mixin instead of inheriting from it. This allows you to add to or augment the behavior of the base class by adding different mixins to it.
  • Mixins are declared in the mixins property in the requirejs-config.js configuration file. This file must be created in the same area specific directory the mixin is defined in.
  • The mixins configuration in the requirejs-config.js associates a target component with a mixin using their paths.
  • Mixin accepts a target component as an argument and returns a component.This allow us to return a new instance of the target component with our modifications attached to it before it is used in the application.
  • In our Example, we are adding mixins to checkout-data-resolver and quote js components.

4.2 Create requirejs-config.js file

  • As Magento has a modular architecture we have an ability to define requirejs-config.js for each module, separately for each area: frontend or admin or base(same for both admin and frontend).
  • Following is the conventional location of requirejs-config.js.
    • For modules: //view//requirejs-config.js
    • For themes: /requirejs-config.js
var config = {
    config: {
        'mixins': {
            'Magento_Checkout/js/model/checkout-data-resolver': {
                'KTree_Custom/js/model/checkout-data-resolver-mixin': true
            },
            'Magento_Checkout/js/model/quote' : {
              'KTree_Custom/js/model/quote-mixin': true
            }
        }
    }
};

In our example, we are creating requirejs-config.js file in app/code/KTree/Custom/view/frontend folder

File:  app/code/KTree/Custom/view/frontend/requirejs-config.js


define([
    'ko',
    'underscore'
], function (ko, _) {
    'use strict';
    return function (quote) {
      /**
       * @return {quote shipping address_id or null}
       */
      var quoteShippingAddress = function () {
          return (window.checkoutConfig.quoteData['quote_shipping_address']);
      };
      /**
       * @return {quote billing address_id or null}
       */
      var quoteBillingAddress = function () {
          return (window.checkoutConfig.quoteData['quote_billing_address']);
      };
      /**
       * @return {previous order payment method or null}
       */
      var quoteOrderPaymentMethod = function () {
          return (window.checkoutConfig.quoteData['quote_paymentmethod']);
      };
      quote.quoteShippingAddress = quoteShippingAddress;
      quote.quoteBillingAddress = quoteBillingAddress;
      quote.quoteOrderPaymentMethod = quoteOrderPaymentMethod;
      return quote;
    };
  });

Override   checkout-data-resolver js component functions  in  checkout-data-resolver-mixin.js file and create the  file under KTree/Custom/view/frontend/web/js/model folder to preselect customer previous order data instead of customer default data.

File: app/code/KTree/Custom/view/frontend/web/js/model/checkout-data-resolver-mixin.js
/*magento getconfig() method will create config object with current customer and get that config data using below js code*/

define([
  'Magento_Customer/js/model/address-list',
  'Magento_Checkout/js/model/quote',
  'Magento_Checkout/js/checkout-data',
  'Magento_Checkout/js/action/create-shipping-address',
  'Magento_Checkout/js/action/select-shipping-address',
  'Magento_Checkout/js/action/select-shipping-method',
  'Magento_Checkout/js/model/payment-service',
  'Magento_Checkout/js/action/select-payment-method',
  'Magento_Checkout/js/model/address-converter',
  'Magento_Checkout/js/action/select-billing-address',
  'Magento_Checkout/js/action/create-billing-address',
  'underscore'],function (
    addressList,
  quote,
  checkoutData,
  createShippingAddress,
  selectShippingAddress,
  selectShippingMethodAction,
  paymentService,
  selectPaymentMethodAction,
  addressConverter,
  selectBillingAddress,
  createBillingAddress,
  _){
    'use strict';
    return function (checkoutDataResolver) {
         var applyShippingAddress =  function(isEstimatedAddress){
              var address,
                  shippingAddress,
                  isConvertAddress,
                  addressData,
                  isShippingAddressInitialized;
              if (addressList().length === 0) {
                  address = addressConverter.formAddressDataToQuoteAddress(
                      checkoutData.getShippingAddressFromData()
                  );
                  selectShippingAddress(address);
              }
              shippingAddress = quote.shippingAddress();
              isConvertAddress = isEstimatedAddress || false;
              if (!shippingAddress) {
             //addressList will return all addresses of customer
                  isShippingAddressInitialized = addressList.some(function (addressFromList) {
               //get shipping and billing address from cookies and compare with customer address
                      if (checkoutData.getSelectedShippingAddress() == addressFromList.getKey()) { //eslint-disable-line
                          addressData = isConvertAddress ?
                              addressConverter.addressToEstimationAddress(addressFromList)
                              : addressFromList;
                          selectShippingAddress(addressData);
                          return true;
                      }
                      return false;
                  });
                  if (!isShippingAddressInitialized) {
                      isShippingAddressInitialized = addressList.some(function (addrs) {
                        /*edit shipping*/
                        if (quote.quoteShippingAddress()){
                          if(addrs.customerAddressId == quote.quoteShippingAddress()) {
                              addressData = isConvertAddress ?
                                  addressConverter.addressToEstimationAddress(addrs)
                                  : addrs;
                              selectShippingAddress(addressData);
                              return true;
                          }
                        }
                    //if customer did not have order history show customer default address
                        else if(addrs.isDefaultShipping()){
                           addressData = isConvertAddress ?
                              addressConverter.addressToEstimationAddress(addrs)
                              : addrs;
                            selectShippingAddress(addressData);
                             return true;
                           }
                      });
                  }
                  if (!isShippingAddressInitialized && addressList().length === 1) {
                      addressData = isConvertAddress ?
                          addressConverter.addressToEstimationAddress(addressList()[0])
                          : addressList()[0];
                      selectShippingAddress(addressData);
                  }
              }
          };
        var resolveBillingAddress = function(){
          var selectedBillingAddress = checkoutData.getSelectedBillingAddress(),
              newCustomerBillingAddressData = checkoutData.getNewCustomerBillingAddress();
          if (selectedBillingAddress) {
              if (selectedBillingAddress == 'new-customer-address' && newCustomerBillingAddressData) { //eslint-disable-line
                  selectBillingAddress(createBillingAddress(newCustomerBillingAddressData));
              } else {
                  addressList.some(function (address) {
                      if (selectedBillingAddress == address.getKey()) { //eslint-disable-line eqeqeq
                          selectBillingAddress(address);
                      }
                  });
              }
          } else {
            /*assign billing address*/
            if(quote.quoteBillingAddress()){
              addressList.some(function (addrs) {
                if (addrs.customerAddressId == quote.quoteBillingAddress()) {
                      selectBillingAddress(addrs);
                  }
              });
            }
            this.applyBillingAddress();
          }
        };
        var resolvePaymentMethod = function(){
          var availablePaymentMethods = paymentService.getAvailablePaymentMethods(),
              selectedPaymentMethod = checkoutData.getSelectedPaymentMethod();
          if (selectedPaymentMethod) {
              availablePaymentMethods.some(function (payment) {
                  if (payment.method == selectedPaymentMethod) { //eslint-disable-line eqeqeq
                      selectPaymentMethodAction(payment);
                  }
              });
          }
          else{
              if(quote.quoteOrderPaymentMethod()){
                availablePaymentMethods.some(function (payment) {
   //get customer previous payment method from last order
                  if (payment.method == quote.quoteOrderPaymentMethod()) { dat
                      selectPaymentMethodAction(payment);
                  }
                });
            }
          }
        };
      checkoutDataResolver.applyShippingAddress = applyShippingAddress;
      checkoutDataResolver.resolveBillingAddress = resolveBillingAddress;
      checkoutDataResolver.resolvePaymentMethod = resolvePaymentMethod;
        //});
        return checkoutDataResolver;
    }
});
  • The full source code can be downloaded from this Github link Address used in Last order in checkout page for customer.
  • Once you have completed the above two steps, remove pub/static/frontend/ folder and  the “php bin/magento setup:static-content:deploy” command to pick up your overridden Javascript files instead of the original Magento 2 core Javascript file.
  • You can check the result by adding product to cart and navigate to checkout page, here you can observe that previous order data will prefilled in checkout steps.

Looking for Magento Developer?

Please Contact us if you have any Magento Implementation requirements. Hire dedicated Magento developers or Magento Development services from KTree. KTree is Best offshore Magento E-commerce development company with extensive experience in E-commerce development and Magento Plugins.

Request For Quote