It’s been a long time, isn’t it? Recently, there has been less feedback from you on Magento Certificate tutorials, thus I don’t really know what you think about them or whether my posts helpful. It’s complicated in my mind but I decide to keep this series on due to my Magento passion.
Well, now I’m back with more energy, ideas and things to show you. My Magento tutorial today will help you dive deeper into the world of Magento, showing how to register custom total model with the system as well as method to write your custom total model.
Read on now!
As you may know, Magento uses a set of total models to collect the shopping cart total (calculate shipping fee, discount, tax, grand total…). The total model system is dynamical for developer to change or add new custom total model to the shopping cart.
Don’t miss our great resources:
The “must-have” extensions for your Magento 1 and Magento 2 store.
Magento Developer: Let Magestore build your e-Commerce Website while you sleep.
1. Register custom total model with the system
Magento uses configuration to determine total models of system. Thus, to register your custom total model, you need to add configuration (through your module config file: config.xml):
<config> ... <global> ... <sales> <quote> <totals> <[total_identify]><!-- Identify of the total model --> <class>[your_extension]/[quote_total_model]</class> <after>wee,discount,tax,tax_subtotal,grand_total</after> </[total_identify]> </totals> </quote> <order_invoice> <totals> <[total_identify]> <class>[your_extension]/[invoice_total_model]</class> </[total_identify]> </totals> </order_invoice> <order_creditmemo> <totals> <[total_identify]> <class>[your_extension]/[creditmemo_total_model]</class> </[total_identify]> </totals> </order_creditmemo> </sales> </global> </config>
As you can see, we register with three types of total models:
- Quote total: used to calculate the custom total before you place order. The sort order of this total is very important because it can depend on other total models.
- Invoice total: used to prepare total for invoice
- Creditmemo total: used to prepare total for refund items.
2. Write your custom total model
A quote total model needs to be extended from the class Mage_Sales_Model_Quote_Address_Total_Abstract and you can write two methods: collect and fetch for this model.
The method collect is used to calculate total for the shopping cart, and the method fetch is used to fetch data to display in frontend. Below is an example of a quote total model:
<?php class [Your_Quote_Total_Model] extends Mage_Sales_Model_Quote_Address_Total_Abstract { public function collect(Mage_Sales_Model_Quote_Address $address){ /* discount 10 dollar for shopping cart */ $baseDiscount = 10; $discount = Mage::app()->getStore()->convertPrice($baseDiscount); $address->setCustomDiscount($baseDiscount); $address->setBaseGrandTotal($address->getBaseGrandTotal() - $baseDiscount); $address->setGrandTotal($address->getGrandTotal() - $discount); return $this; } public function fetch(Mage_Sales_Model_Quote_Address $address){ $amount = $address->getCustomDiscount(); if ($amount != 0){ $address->addTotal(array( 'code' => $this->getCode(), 'title' => 'Custom Discount', 'value' => $amount, )); } return $this; } }
The invoice total model is similar to creditmemo total model. This model is only used to calculate the order total, so we only write the method collect for this model.
For instance:
<?php class [Your_Invoice_Total_Model] extends Mage_Sales_Model_Order_Invoice_Total_Abstract { public function collect(Mage_Sales_Model_Order_Invoice $invoice){ $baseDiscount = 10; $discount = Mage::app()->getStore()->convertPrice($baseDiscount); $invoice->setBaseGrandTotal($invoice->getBaseGrandTotal() - $baseDiscount); $invoice->setGrandTotal($invoice->getGrandTotal() - $discount); return $this; } }
In the next post, I will talk about how to show the total in all areas that you see the order total.
Just leave comments below if you find my post helpful. Your ideas and feedback encourage me a lot to write the next articles of Magento Certificate tutorials.
26 Comments
hi david,
actually there are so many followers waiting your blog but they expext as before every friday your new blog comes but they have to disappoint after checking blog because you guys are not regular as before.
so please be regular in this series we always keep our eyes every friday in this series.
Hi jaimin,
Thanks much for your comment. I’m very glad to hear that many people follow my tutorials. I’ll try to post frequently on Friday. Nice day!
Hi,
I would like to thank you for your great tutorials in magento Certificate ! its incredibly amazing! I have just passed my magento developer plus exam today ! and i wanted to thank you deeply for your efforts and great tutorials !!
Thank you
Hi Mohamed,
Congratulations! We are so delighted to hear that you passed the exam. I wonder if you can share your experience in this exam. Many other followers will be very happy about that.
Hello David, I’ve followed your tutorial (and many other articles) and I’m in trouble, as I’ve got a strange issue (maybe it’s only a mistake from my part, but I’m quite sure about my code…).
I’m trying to create a custom Total collector, as I need to add a custom addition to the global total when an order is placed (before grand total).
I’ve created my local module and my custom model, but every time I specify the model class in the config.xml an error is raised telling:
a:5:{i:0;s:94:”The address total model should be extended from Mage_Sales_Model_Quote_Address_Total_Abstract.”;i:1;s:2174:”#0 /chroot/home/myuser/mydomain.net/html/test/includes/src/Mage_Sales_Model_Quote_Address_Total_Collector.php(106): Mage::throwException(‘The address tot…’) […]
It looks like it does not find the class in the path specified, but the path is correct and the directories tree is correctly formed.
Have you any idea on what’s going on? Any help or suggestion will be appreciated!
Thanks in advance!
Hi,
Many thanks for your following. About your issue, your custom total needs to be extended from the class: Mage_Sales_Model_Quote_Address_Total_Abstract. Please try again. If the error still stays, you can give me your xml config and the class code, I’ll check it for you.
Nice day, Alessandro.
Hello David, thanks for your answer.
The problem is that I’ve already extended my class from Mage_Sales_Model_Quote_Address_Total_Abstract.
Now I could make a new Magento install and then try again.
In the case this won’t be the solution, how could I send you my files? Thanks again 🙂
Hi Alessandro,
If there won’t be the solution, you can post the contents of your config.xml and custom total files right here. I’ll help you check them.
Nice day!
Hi David,
sorry to bother you again. I’ve tried to do it on a new Magento installation and it seems to work: I’ve used your code in my class and it seems to work. I’ve only two questions: why the discount is calculated twice? I’ve only one item in my cart and a fixed shipping amount. The discount has been placed after subtotal in the confi.xml. The second question is: how could I get the current selected payment method from my custom model? I’d like the discount to be calculated only if the payment method is a specific one. And is it possible to set this discount not as a custom discount but as something other? I mean, in the order total it’s called Custom Discount, but if I’d like to have it not cathegorized as a custom discount but as something other I’ll decide to use to keep discounts separated from this one? Do you think this could be possible?
I hope you will put me in the right direction.
Thanks again and in advance!
Hi Alessandro,
About the first question: Maybe your custom total is calculated for both billing address and shipping address, so it shows twice. You can check it on the collect method by using command
if ($address->getAddressType() == ‘billing’ && !$quote->isVirtual()) {
return $this;
}
Regarding the second question: You can get the current method by the command
$payment = $address->getQuote()->getPayment();
$payment->getMethod();
Hope my answers help. If having any other questions, feel free to let me know. You’re welcome!
Hello David, thanks for sharing your knowledge, you’ve really helped me!
I’ve one last question, it’s a strange behaviour I’ve encountered while customizing the custom discount…
I’d like to apply the discount as a percentage on the order total. I’ve modified your code like this:
$basePerc = 3.5;
$baseDiscount = ($address->getGrandTotal() * $basePerc) / 100;
$discount = Mage::app()->getStore()->convertPrice($baseDiscount);
$address->setCustomDiscount($baseDiscount);
$address->setBaseGrandTotal($address->getBaseGrandTotal() – $discount);
$address->setGrandTotal($address->getGrandTotal() – $discount);
But for some strange reasons the $address->getGrandTotal() in the second line always return 0 and the discount is always 0 as well… I’ve tried forcing the value using a number and it works… (for example, $baseDiscount = 12;)
Any ideas? Thanks again!
I’m sorry, it was my fault, I changed the config.xml from
wee,discount,tax,tax_subtotal,grand_total
to
subtotal
Restoring it now it works correctly!
i am waiting for the topics on checkout.
Will you write something on the overall checkout flow in programming level?
I am quite curious about how magento gathering quote/total/payment/shipping together.
e.g. paypal express/ standard has different flow, how to determine which shippment method being shown
Hello,
I completed the Checkout topic already. You can follow my posts in this blog, Magento Certificate section for more details. Thank you!
Hello david i want to updating total by item for different item i am adding extra charge how can i calculate that in collect mehod
Hi,
It’s possible to update total by item in collect method. In that method, you can use $items = $address->getAllItems(); to get item for your calculation.
Hope you succeed!
This Tutorial Save My Live
The address total model should be extended from Mage_Sales_Model_Quote_Address_Total_Abstract. an error occured pls help
Hi guy,
I answered this kind of question above. Please read previous comments and you will find the answer.
Hello David,
The error shows up in Magento 1.9 and above. Its working fine in Magento 1.8. Kindly help.
$address->setCustomDiscount($baseDiscount);
$address->setBaseGrandTotal($address->getBaseGrandTotal() – $baseDiscount);
$address->setGrandTotal($address->getGrandTotal() – $discount);
Hi david i want to apply this only if cart sub total is greate than 10 so wat to do that
Hi pankaj,
About your issue, You need add some conditions before:
Example:
$items = $address->getAllItems();
if (!count($items)) return $this;
if($address->getSubtotal() <= 10) return $this; $address->setCustomDiscount($baseDiscount);
$address->setBaseGrandTotal($address->getBaseGrandTotal() – $baseDiscount);
$address->setGrandTotal($address->getGrandTotal() – $discount);
Hope it would be useful with you.
Hi,
Thank you for a nice tutorial. I’m using it to add some extra charges for handling.
But I have a problem.
Extra charges are added with to the total, but not to the “tax” row (which is must have in our country – Slovenia)
Example:
This is with out extra charges applied (everything fine):
—–
Item 1 (with tax 20%): 120$
Item 2 (with tax 20%): 120$
Total (with tax 20%): $240
Tax (20%): 40$
—–
With extra charges applied (see problem with tax):
—–
Item 1 (with tax 20%): 120$
Item 2 (with tax 20%): 120$
Handling (with tax 20%): 120$
Total (with tax 20%): $360
Tax (20%): 40$ /// HERE IS THE PROBLEM – tax stays unchanged – it should be increased to 60$.
—-
I would appreciate any hint how to solve this.
Thank you and best regards,
Gregor
Hi Gregor,
I can provide you a solution: Rewrite Mage_Tax_Model_Sales_Total_Quote_Tax
Step1: Add this source code in config.xml
< models>
...
< tax>
< rewrite>
< sales_total_quote_tax>[Namespace]_[Module_Name]_Model_Tax < /sales_total_quote_tax>
< /rewrite>
< /tax>
...
< /models>
Step2: In Tax.php file, you need rewrite 4 function:
protected function _aggregateTaxPerRate($item, $rate, &$taxGroups)
protected function _calcUnitTaxAmount(Mage_Sales_Model_Quote_Item_Abstract $item, $rate)
protected function _calcRowTaxAmount($item, $rate)
protected function _calculateShippingTax(Mage_Sales_Model_Quote_Address $address, $taxRateRequest)
This is examples:
protected function _aggregateTaxPerRate($item, $rate, &$taxGroups) {
$discount = $item->getDiscountAmount();
$baseDiscount = $item->getBaseDiscountAmount();
$customDiscount = 10;
$item->setDiscountAmount($discount+$customDiscount);
$item->setBaseDiscountAmount($baseDiscount+$customDiscount);
parent::_aggregateTaxPerRate($item, $rate, $taxGroups);
$item->setDiscountAmount($discount);
$item->setBaseDiscountAmount($baseDiscount);
return $this;
}
protected function _calcUnitTaxAmount(Mage_Sales_Model_Quote_Item_Abstract $item, $rate) {
$discount = $item->getDiscountAmount();
$baseDiscount = $item->getBaseDiscountAmount();
$customDiscount = 10;
$item->setDiscountAmount($discount+$customDiscount);
$item->setBaseDiscountAmount($baseDiscount+$customDiscount);
parent::_calcUnitTaxAmount($item, $rate);
$item->setDiscountAmount($discount);
$item->setBaseDiscountAmount($baseDiscount);
return $this;
}
protected function _calcRowTaxAmount($item, $rate) {
$discount = $item->getDiscountAmount();
$baseDiscount = $item->getBaseDiscountAmount();
$customDiscount = 10;
$item->setDiscountAmount($discount+$customDiscount);
$item->setBaseDiscountAmount($baseDiscount+$customDiscount);
parent::_calcRowTaxAmount($item, $rate);
$item->setDiscountAmount($discount);
$item->setBaseDiscountAmount($baseDiscount);
return $this;
}
protected function _calculateShippingTax(Mage_Sales_Model_Quote_Address $address, $taxRateRequest) {
$discount = $address->getShippingDiscountAmount();
$baseDiscount = $address->getBaseShippingDiscountAmount();
$customDiscount = 10;
$address->setShippingDiscountAmount($discount+$customDiscount);
$address->setBaseShippingDiscountAmount($baseDiscount+$customDiscount);
parent::_calculateShippingTax($address, $taxRateRequest);
$address->setShippingDiscountAmount($discount);
$address->setBaseShippingDiscountAmount($baseDiscount);
return $this;
}
Hello David,
Great tutorial….
I want to take a help to develop extra fee module. I have added classes and everything and extra fee is applying on cart total.
Now i want to use textbox on Extra fee row line. If customer un tick checkbox then Extra fee will be removed and if customer tick again then Extra fee will be adding again to grand total.
Can you please help me here to how can i achieve checkbox tick and untick process.
Thanks in advance.
Hi,
I want add extra custom fee (delivery charges )to cart like if user select night delivery i have to add night delivery charge and if user select fixed time delivery i have to charge in cart how to do that please help me