Unravelling Magento’s collectTotals: An Example
Unravelling Magento’s collectTotals: An Example

Unravelling Magento’s collectTotals: An Example

Published May 15, 2013 in Development
Unravelling Magento’s collectTotals: Orders and Caveats
May 8, 2013
Save Credit Cards in Magento with Our New PayPal Extension
August 15, 2013

Previous Entries

In the previous entries in this series, we’ve dived into the process Magento uses for calculating and displaying cart totals. With this conclusion, we’ll walk through an example of a new total collector, step by step.

I’ll re-emphasize that collectTotals can be useful to you in more ways than one. Implementing a new total collector can serve various purposes. For a complete example, however, I’ll focus on the most straightforward application: Creating a new, distinct total to be added to and displayed alongside the rest.

Not being an online merchant with a truly legitimate use case for such a customization, I’ll have to settle for dreaming up a half-baked scenario of my own. Let’s say I run the website Cozy Crafts, where I sell my own hand-crafted items. An important offering of my site is the ability for the customer to give instructions for customizing my products, beyond the typical scenarios of monograms or color choices. To keep it simple, I’m just going to present a custom option textarea where customers can enter their instructions, and I will apply a flat surcharge for customization per SKU (not per item), but increasing the flat fee for greater quantities.

Okay, so perhaps it’s not the soundest way to structure my pricing, but it serves well enough as a justification for a “custom surcharge” total. Keeping our example as simple as possible, we’ll assume that for products where customization is available, I’ll simply create a separate version of the product with “-CUSTOM” appended to the SKU. Customers wishing to give customization instructions will place this version of the item in their cart.

I’ll start with the complete config.xml for our module:

The relevant bits, starting from the top:

  • The “fieldsets” node is responsible for making sure values from our new database fields get transferred from quote to order.
  • Our surcharge totals, along with their model classes, are defined in the “global/sales/quote/totals”, “global/sales/order_invoice/totals” and “global/sales/order_creditmemo/totals” nodes. Note the use of <after> to position our collector’s calculations directly after the subtotal collector. We also define <renderer> in the quote node, because we will be using a custom block for our total in the cart and checkout.

The next step is to add the database fields for our total on the appropriate tables. We’ll be adding the same two fields – “custom_surcharge_amount” and “base_custom_surcharge_amount” – to the following tables: sales_flat_quote_item, sales_flat_quote_address_item (used for multi-shipping), sales_flat_quote_address, sales_flat_order_item, sales_flat_order, sales_flat_invoice and sales_flat_creditmemo. (It would be common to add these fields to sales_flat_invoice_item and sales_flat_creditmemo_item as well, but we are avoiding this in our example for simplicity.) Here is a snippet from the setup script located in our module at sql/cozycraft_surcharge_setup/install-1.0.0.php, which would be repeated for both fields and for all tables:

Next we’ll look at our surcharge quote total model:

Our model extends Mage_Sales_Model_Quote_Address_Total_Abstract. Here, we’ve set up the quantity tiers for our surcharge. (For example, if we have between 5 and 14 of a custom product in our cart, a single surcharge of $20 will be applied.) In our constructor, we set the total code on the model, matching the code we used in config.xml.

So far, we’ve let Magento know where to find our total model and when to use it in the sequence of totals collection. Immediately after the “collect” method is run on the subtotal collector model, the same method will be called on ours. Here is the code that will execute:

The “collect” method accepts an instance of Mage_Sales_Model_Quote_Address, and our call to the parent method ensures this is set on our model (which is important for the _addAmount methods). From the address, we get the collection of items and apply our surcharge to each that matches the “-CUSTOM” suffix on the SKU. Applying our surcharge involves setting both custom_surcharge and base_custom_surcharge on the item, as well as using the built-in _addBaseAmount and _addAmount, which will take care of setting the appropriate fields on the address and summing them with the rest of its totals.

With this much in place, we’re ready to test adding a custom product to our cart. We should see the surcharge reflected in our grand total when we do (though not itself displayed – that comes next). A look at the sales_flat_quote_address and sales_flat_quote_item tables will show the appropriate values have been saved on the quote records. Next is the total model’s “fetch” method:

Recall that “fetch” is called on each total model during the execution of Mage_Checkout_Block_Cart_Totals::renderTotals. No calculations are being done here; those already took place in “collect.” Rather, we are simply returning an array with our total’s code and a title and value to display.

If we hadn’t defined a custom renderer block in config.xml, this would be all that’s needed to make sure our total shows up along with the others. Since we did, we have a bit more to do. Here’s our block, which actually exists merely to define the separate template used to display our surcharge:

And here is the template, which we’ve customized from the default by adding some styling and including a link that will open a pop-up window with an explanation of our surcharge:

Now we can see our total displayed in the cart. The following is what is shown when we have 2 of one custom product (for a surcharge of $10) and 5 of another (for a surcharge of $20) in the cart:

Custom Totals

The “fieldset” nodes we saw in config.xml will take care of transferring the surcharge from the quote items to the order items and from the quote address to the order itself. For the surcharge to actually be displayed in order confirmation emails and in the admin, we’ll have to complete our layout update piece, but before doing so we’ll look at the respective invoice and credit memo total models as well.

For invoicing, we decide to use the following rule: For each order item, we will invoice the full surcharge amount only once the fullquantity has been invoiced. A strategy like this would typically make tracking our surcharge amount on a per item basis a good idea, but we’re going to content ourselves with tracking it only on the entire invoice. Here’s our invoice total class. (Note the use of our own logic for determining if it’s the last invoice rather than relying on “isLast”, as alluded to in the previous article regarding invoices and credit memos.)

Finally, we need to deal with credit memo calculation. Our logic for refunds is even simpler than for invoices: We’ll consider our surcharge non-refundable unless the entire order has been refunded. (What we’d likely want in a similar real life scenario is for the administrator to be able to choose whether to refund this amount or not on the final credit memo, but this would require some customization to the admin form itself. And of course, if we simply want the surcharge to be non-refundable in all cases, there’s no need for a total model at all.) Our credit memo total model:

All that’s left from here is to define the block class that will handle displaying our total for orders, invoices, and credit memos, and to add it to the appropriate areas in layout XML.

Thankfully, we can use the same class for all three cases. With the block’s “getSource” method, we’ll fetch the source model from the parent block, which may be an order, invoice, or credit memo. Our total amount is fetched from any of the three with the same method, so we won’t care which is being returned.

Our layout XML is going to be lengthy. It’s one little block, but it needs to be added in a lot of places. At least we can define each of our layout updates once and then apply them multiple times with a single line. Here’s the front-end version of cozycrafts_customsurcharge.xml:

And here’s the admin version:

And with that, our module is complete. We’ve successfully applied our own custom surcharge to the cart and made sure it makes it through to the final cart. After seeing the complexity of the code Magento implements to handle totals, the volume of code we actually had to write to implement our own actually doesn’t seem half bad!

I hope this series has done something to bolster your confidence in dealing with the totals collection process. The more you deal with it, the less you’ll dread those times when a client’s requirements involve customizing totals, and the less readily you’ll resort to a hack when a new total collector might be just the thing to save the day!

8 Comments

  1. Marcin says:

    Hey Chris,
    great article! It helped me a lot to deal with the additional surcharge amount.
    But I have encountered one problem. I need Magento to calculate the surcharge tax and add it to the final tax amount. I tried changing the values for and tags in my module config.xml, but… still nothing 🙁

    Could you help me, please?

    • Marcin says:

      It was supposed to be: “I tried changing the values for ‘before’ and ‘after’ tags in my module config.xml”

    • Chris Nanninga says:

      From the sound of it, I’m not sure a separate total collector is really what you’re looking for. (Might make more sense to override the tax collector.) But at any rate, simply making your collector run after the native tax collector won’t be enough; inside your collector you’d need to fetch and directly manipulate the native tax total fields.

      Cheers!

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Most Recent PostsView all
July 15, 2019

Five Reasons to Use Magento for Your Auto Parts eCommerce Site

At Classy Llama, in addition to developing on Magento for over a decade, we’ve worked with many automotive industry merchants over the years. Because of this […]
May 24, 2019

Five Key Ways to Increase the Customer-Generated Content on Your eCommerce Site

It’s easy to let your eCommerce site’s strategy around Customer-Generated Content (CGC) fall from your list of top priorities. But there are some great reasons why […]
May 23, 2019

More Sites, Same Staff, Less Maintenance