23 January, 2022

Magento 2 – Add Extra Fee To Order Totals

Magento 2 Add Fee Order

As an online store selling goods to customers located across a geographical region, there will be instances when an extra charge for shipping, packing or delivery has to charged in addition to the cart total.

Magento 2 provides for such extra addition to order totals.

I have described the process in a step-by-step manner to add extra fee to order totals along with coding as below:

Step 1:

Define Extra fee model in xml schema in your module config
Create sales.xml file in module etc folder.




Step 2:

Same as core , create new js as fee.js which used to display it in cart summary section

app\code\Modulename\view\frontend\web\js\view\checkout\cart\totals\fee.js


define(
    [
        'Module_Name/js/view/checkout/summary/fee'
    ],
    function (Component) {
        'use strict';

        return Component.extend({

            isDisplayed: function () {
                return true;
            }
        });
    }
);

Step 3:

Same as core , create new js as fee.js under web which used to display the fee with tax and grand total in checkout review section

app\code\Modulename\view\frontend\web\js\view\checkout\summary\fee.js

define(
    [
        'Magento_Checkout/js/view/summary/abstract-total',
        'Magento_Checkout/js/model/quote',
        'Magento_Catalog/js/price-utils',
        'Magento_Checkout/js/model/totals'
    ],
    function (Component, quote, priceUtils, totals) {
        "use strict";
        return Component.extend({
            defaults: {
                isFullTaxSummaryDisplayed: window.checkoutConfig.isFullTaxSummaryDisplayed || false,
                template: 'Module_Name/checkout/summary/fee'
            },
            totals: quote.getTotals(),
            isTaxDisplayedInGrandTotal: window.checkoutConfig.includeTaxInGrandTotal || false,
            isDisplayed: function() {
                return this.isFullMode();
            },
            getValue: function() {
                var price = 0;
                if (this.totals()) {
                    price = totals.getSegment('fee').value;
                }
                return this.getFormattedPrice(price);
            },
            getBaseValue: function() {
                var price = 0;
                if (this.totals()) {
                    price = this.totals().base_fee;
                }
                return priceUtils.formatPrice(price, quote.getBasePriceFormat());
            }
        });
    }
);

Step 4:

Now, create a view file for that fee value display (Checkout Summary)

app\code\Modulename\view\frontend\web\template\checkout\summary\fee.html


        
            
            
        
        
            
           
        
  

Step 5:

Create view file for that fee value display (Checkout cart total)

app\code\Modulename\view\frontend\web\template\checkout\cart\totals\fee.html


         
    
        
    


Step 6:

Model Fee.php where collect total based on address collection and add our fee to total and subtotal

app\code\Modulename\Model\Total\Fee.php

namespace Modulename\Model\Total;


class Fee extends \Magento\Quote\Model\Quote\Address\Total\AbstractTotal
{
   /**
     * Collect grand total address amount
     *
     * @param \Magento\Quote\Model\Quote $quote
     * @param \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment
     * @param \Magento\Quote\Model\Quote\Address\Total $total
     * @return $this
     */
    protected $quoteValidator = null; 

    public function __construct(\Magento\Quote\Model\QuoteValidator $quoteValidator)
    {
        $this->quoteValidator = $quoteValidator;
    }
  public function collect(
        \Magento\Quote\Model\Quote $quote,
        \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment,
        \Magento\Quote\Model\Quote\Address\Total $total
    ) {
        parent::collect($quote, $shippingAssignment, $total);


        $exist_amount = 0; //$quote->getFee(); 
        $fee = 100; //Excellence_Fee_Model_Fee::getFee();
        $balance = $fee - $exist_amount;

        $total->setTotalAmount('fee', $balance);
        $total->setBaseTotalAmount('fee', $balance);

        $total->setFee($balance);
        $total->setBaseFee($balance);

        $total->setGrandTotal($total->getGrandTotal() + $balance);
        $total->setBaseGrandTotal($total->getBaseGrandTotal() + $balance);


        return $this;
    } 

    protected function clearValues(Address\Total $total)
    {
        $total->setTotalAmount('subtotal', 0);
        $total->setBaseTotalAmount('subtotal', 0);
        $total->setTotalAmount('tax', 0);
        $total->setBaseTotalAmount('tax', 0);
        $total->setTotalAmount('discount_tax_compensation', 0);
        $total->setBaseTotalAmount('discount_tax_compensation', 0);
        $total->setTotalAmount('shipping_discount_tax_compensation', 0);
        $total->setBaseTotalAmount('shipping_discount_tax_compensation', 0);
        $total->setSubtotalInclTax(0);
        $total->setBaseSubtotalInclTax(0);
    }
    /**
     * @param \Magento\Quote\Model\Quote $quote
     * @param Address\Total $total
     * @return array|null
     */
    /**
     * Assign subtotal amount and label to address object
     *
     * @param \Magento\Quote\Model\Quote $quote
     * @param Address\Total $total
     * @return array
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    public function fetch(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Quote\Address\Total $total)
    {
        return [
            'code' => 'fee',
            'title' => 'Fee',
            'value' => 100
        ];
    }

    /**
     * Get Subtotal label
     *
     * @return \Magento\Framework\Phrase
     */
    public function getLabel()
    {
        return __('Fee');
    }
}

Step 7:

Core rewrite for sales,quote,invoice,checkout
app\code\Modulename\etc\module.xml












Step 8:

checkout_cart_index.xml add fee view in cart page layout
app\code\Modulename\view\frontend\layout\checkout_cart_index.xml












Module_Name/js/view/checkout/cart/totals/fee
20

Module_Name/checkout/cart/totals/fee
Fee







 

Step 9:

checkout_index_index.xml add fee view in checkout page layout
app\code\Modulename\view\frontend\layout\checkout_index_index.xml


















Module_Name/js/view/checkout/carttotals/fee
20

Module_Name/checkout/cart/totals/fee
Fee









Magento_Tax/js/view/checkout/summary/item/details/subtotal

















Step 10:

sales_order_view.xml display fee to totals in sales order view
app\code\Modulename\view\frontend\layout\sales_order_view.xml






            
        
            
        
    

Step 11:

Add fee to sales order , so add model fee.php under sales order which add fee to total and subtotal
app\code\Modulename\Block\Sales\Order\Fee.php

namespace Modulename\Block\Sales\Order;



class Fee extends \Magento\Framework\View\Element\Template
{
    /**
     * Tax configuration model
     *
     * @var \Magento\Tax\Model\Config
     */
    protected $_config;

    /**
     * @var Order
     */
    protected $_order;

    /**
     * @var \Magento\Framework\DataObject
     */
    protected $_source;

    /**
     * @param \Magento\Framework\View\Element\Template\Context $context
     * @param \Magento\Tax\Model\Config $taxConfig
     * @param array $data
     */
    public function __construct(
        \Magento\Framework\View\Element\Template\Context $context,
        \Magento\Tax\Model\Config $taxConfig,
        array $data = []
    ) {
        $this->_config = $taxConfig;
        parent::__construct($context, $data);
    }

    /**
     * Check if we nedd display full tax total info
     *
     * @return bool
     */
    public function displayFullSummary()
    {
        return true;
    }

    /**
     * Get data (totals) source model
     *
     * @return \Magento\Framework\DataObject
     */
    public function getSource()
    {
        return $this->_source;
    } 
    public function getStore()
    {
        return $this->_order->getStore();
    }

      /**
     * @return Order
     */
    public function getOrder()
    {
        return $this->_order;
    }

    /**
     * @return array
     */
    public function getLabelProperties()
    {
        return $this->getParentBlock()->getLabelProperties();
    }

    /**
     * @return array
     */
    public function getValueProperties()
    {
        return $this->getParentBlock()->getValueProperties();
    }

    /**
     * Initialize all order totals relates with tax
     *
     * @return \Magento\Tax\Block\Sales\Order\Tax
     */
     public function initTotals()
    {

        $parent = $this->getParentBlock();
        $this->_order = $parent->getOrder();
        $this->_source = $parent->getSource();
        $store = $this->getStore();
        $fee = new \Magento\Framework\DataObject(
                [
                    'code' => 'fee',
                    'strong' => false,
                    'value' => 100,                  
                    'label' => __('Fee'),
                ]
            );
            $parent->addTotal($fee, 'fee');         
            $parent->addTotal($fee, 'fee');
            return $this;
    }

}

That’s it folks. Hope you are now clear on how to add Extra Fee to order totals.

If you need any clarifications in the code or any of the technicalities, feel free to get in touch with us.

One Comment

  1. James Reply

    Hello,
    This is very good article to add extra fee to the order total. However, I would like to know, if this can be an optional. Like, the customer can choose whether they need to pay the extra fee(say the fee is for shipping insurance) by ticking a checkbox or selecting a radio button.

Leave a Reply

Your email address will not be published. Required fields are marked *