Sometimes we need to modify the totals of transactions in Magento, to add extra fees or extra discounts. Totals are the calculation of the product, shipping costs, taxes, discounts, etc.
In this article, we will show you how to modify totals by adding total collectors to the calculation process.
Steps to create a new item in quote total calculation from declaration to display would consist of the following steps:
- Step 1: Declare A Total Collector
- Step 2: Implement The Total Collector Class
- Step 3: Modify Cart Layout
- Step 4: Modify Checkout Layout
- Step 5: Create Js Components For Total Item
- Step 6: Create Templates For Total Item
1. Declare a Total Collector
Collectors are declared in app/code/Vendor/Module/etc/sales.xml
:
<?xml version="1.0"?> <config> <section name="quote"> <group name="totals"> <item name="discount" instance="Vendor\Module\Model\Total\Quote\Custom" sort_order="300"/> </group> </section> </config>
Here we’ve added a total item for quote section. Possible sections include “quote
”, “order_invoice
”, “order_creditmemo
”.
- “
instance
”: the class to handle the calculation and collection of this item. - “
sort_order
”: the order in which the item will be collected.
2. Implement The Total Collector Class
Here we specify Vendor\Module\Model\Totals\Custom
class as a model of our custom total collector.
This class must extend the Magento\Quote\Model\Quote\Address\Total\AbstractTotal
and implement the collect and fetch methods.
The collect method is used to calculate the value of our total, while the fetch method returns the result along with the total collector’s code and its name.
For this example we’ll add a collector for ‘discount’ adding 10% discount to quote total:
class Custom extends \Magento\Quote\Model\Quote\Address\Total\AbstractTotal { 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); $discount = 0.1 * $quote->getGrandTotal(); $total->addTotalAmount($this->getCode(), -$discount); $total->addBaseTotalAmount($this->getCode(), -$discount); $quote->setDiscount($discount); return $this; } public function fetch( \Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Quote\Address\Total $total ) { $discount = 0.1 * $quote->getGrandTotal(); return [ 'code' => $this->getCode(), 'title' => $this->getLabel(), 'value' => -$discount //You can change the reduced amount, or replace it with your own variable ]; } }
3. Modify Cart Layout
To add the new total item to cart layout, we need to create the file Vendor/Module/view/frontend/layout/checkout_cart_index.xml
:
<?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="checkout.cart.totals"> <arguments> <argument name="jsLayout" xsi:type="array"> <item name="components" xsi:type="array"> <item name="block-totals" xsi:type="array"> <item name="children" xsi:type="array"> <!-- Start of the main content that needs to be added--> <item name="discount" xsi:type="array"> <!-- The path to our js file--> <item name="component" xsi:type="string">Vendor_Module/js/view/checkout/summary/discount-fee</item> <item name="sortOrder" xsi:type="string">20</item> <item name="config" xsi:type="array"> <!-- Show custom discount on order summary--> <item name="discount" xsi:type="string" translate="true">Custom Discount</item> </item> </item> <!-- End--> </item> </item> </item> </argument> </arguments> </referenceBlock> </body> </page>
4. Modify Checkout Layout
Just like the previous step, we need to add the discount item to the checkout layout. Create the file Vendor/Module/view/frontend/layout/checkout_index_index.xml
:
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="checkout.root"> <arguments> <argument name="jsLayout" xsi:type="array"> <item name="components" xsi:type="array"> <item name="checkout" xsi:type="array"> <item name="children" xsi:type="array"> <item name="sidebar" xsi:type="array"> <item name="children" xsi:type="array"> <item name="summary" xsi:type="array"> <item name="children" xsi:type="array"> <item name="totals" xsi:type="array"> <item name="children" xsi:type="array"> <!-- Start of the main content that needs to be added--> <item name="discount" xsi:type="array"> <!-- The path to our js file--> <item name="component" xsi:type="string">Vendor_Module/js/view/checkout/cart/totals/discount</item> <item name="sortOrder" xsi:type="string">20</item> <item name="config" xsi:type="array"> <item name="template" xsi:type="string">Vendor_Module/checkout/cart/totals/discount</item> <!-- Show custom discount on order summary--> <item name="title" xsi:type="string" translate="true">Custom Discount</item> </item> </item> </item> </item> <item name="cart_items" xsi:type="array"> <item name="children" xsi:type="array"> <item name="details" xsi:type="array"> <item name="children" xsi:type="array"> <item name="subtotal" xsi:type="array"> <item name="component" xsi:type="string">Magento_Tax/js/view/checkout/summary/item/details/subtotal</item> </item> </item> </item> </item> </item> <!-- End--> </item> </item> </item> </item> </item> </item> </item> </argument> </arguments> </referenceBlock> </body> </page>
5. Create JS Components For Total Item
Create the file Vendor/Module/view/frontend/web/js/view/checkout/cart/totals/discount.js
:
define( [ ‘Vendor_Module’/js/view/checkout/summary/discount-fee' ], function (Component) { 'use strict'; return Component.extend({ /** * @override */ isDisplayed: function () { return true; } }); } );
and Vendor/Module/view/frontend/web/js/view/checkout/summary/discount-fee.js
:
define( [ 'jquery', 'Magento_Checkout/js/view/summary/abstract-total', 'Magento_Checkout/js/model/quote', 'Magento_Checkout/js/model/totals', 'Magento_Catalog/js/price-utils' ], function ($,Component,quote,totals,priceUtils) { "use strict"; return Component.extend({ defaults: { template: ‘Vendor_Module’/checkout/summary/discount-fee' }, totals: quote.getTotals(), isDisplayedDiscountTotal : function () { return true; }, getDiscountTotal : function () { var price = totals.getSegment('discount').value; return this.getFormattedPrice(price); } }); } );
6. Create Templates For Total Item
For this step we’ll create HTML templates to display the discount on cart and checkout pages:
a/ Create Vendor/Module/view/frontend/web/template/checkout/cart/totals/discount.html
<!-- ko if: isDisplayedDiscountTotal() --> <tr class="totals discount excl"> <th class="mark" colspan="1" scope="row" data-bind="text: title"></th> <td class="amount"> <span class="price" data-bind="text: getDiscountTotal()"></span> </td> </tr> <!-- /ko -->
b/ Create Vendor/Module/view/frontend/web/template/checkout/summary/discount-fee.html
<!-- ko if: isDisplayedDiscountTotal() --> <tr class="totals coupon-fee excl"> <th class="mark" colspan="1" scope="row" data-bind="text: discount"></th> <td class="amount"> <span class="price" data-bind="text: getDiscountTotal(), attr: {'data-th': discount}"></span> </td> </tr> <!-- /ko →
Here’s our result:
You can download example Magenest_Discount module here.
Can you please share the solution as soon as possible?
Thank you.
so!!
PayPal gateway has rejected request.The totals of the cart item amounts do not match order amounts (#10413: Transaction refused because of an invalid argument. See additional error messages for details).
This is annoying. Is there any way?
thanks!