Solving Magento

Solutions for Magento E-Commerce Platform

by Oleg Ishenko

Taxes in Magento: Module Mage_Tax

Albert Einstein once said: “The hardest thing in the world to understand is the income tax.” It is sales tax you have to deal with in Magento, but these words can be just as true. Magento implements sales tax (or value-added tax – VAT – in some countries) with a purpose to cover maximum possible cases in different countries and locations; and the result is as complex as one can expect. This complexity is encapsulated in one module Mage_Tax that provides helpers and tax calculator models to the rest of the system.

When setting up a shop, you typically have one or several tax rates that have to be applied to products. Depending on the tax code, some products may have different tax rates than others. Also, some customers can be eligible for different rates, such as reduced sales tax for non-profit organizations. Some tax codes may require you to apply taxes based not on the location of your store but on the customer’s address – and all those locations can have different tax rates! To tackle these problems Magento operates a system comprising customer and product tax classes, zones and rates, and tax rules that must be matched to each other to produce the right tax rate.

Tax Classes

Magento has two types of tax classes: Product and Customer. You create product tax classes and assign them to products to tell the system that, for example, some products are to be taxed with a standard rate, some – with a reduced rate, and some are tax exempt. Thus, tax classes are a sort of a grouping instrument that allows you to define what tax rate applies to what product group. Customer tax classes function in the same way, but instead of products you assign them to customer groups: one rate for retail customers, another rate for wholesale buyers, and a zero rate for non-profit entities (concrete tax rules are defined in your local tax code – ask your accountant). Of course, it could be that you have only one rate for every product and every customer – lucky you – but, more often than not, you’d have to deal with multiple tax rates.

Tax classes are simple Magento models and can be accessed in the back-end by selecting in the main menu Sales > Tax > Product Tax Classes (or Customer Tax Classes). Creating a tax class is simple: click the “Add New” button and you’ll see the following form:

tax_class_create

Figure 1. Creating a product tax class.

The only thing you need to provide here is a unique tax class name. Clicking the “Save Class” button creates a new product or customer tax class. Tax classes are saved in table tax_class and are encapsulated in class Mage_Tax_Model_Class that is a standard Magento model and is nothing special in terms of functionality.

Customer tax class is a required attribute for customer groups. After doing a clean install of Magento you’ll notice that the system has created a customer tax class and assigned it to every existing customer group. This class cannot be deleted – the system also creates a tax rule that uses this class. Thus, Magento always has at least one customer tax class.

Product tax class is also a required attribute for most product types except those that don’t use prices, e.g., Grouped. A clean installation of Magento creates two product tax classes: Taxable Goods and Shipping, and both can be assigned to products. The Tax Class product attribute has one more option – None. Selecting it sets a product’s tax class to 0 – a valid value even though it does not correspond to any existing product tax class. As a result, such product gets a tax rate of 0 because there can be no tax rule with such tax class ID and no tax rate matching is possible.

tax_class_product_select

Figure 2. Selecting a tax class for a product.

Tax Rates

Tax rate selection in Magento is address-based. Depending on the shop settings, it is possible to request a tax rate based on a customer address (shipping or billing) or on the address of the shop (default tax address or shipping origin address). When creating a tax rate you must specify to what location it applies by providing a country, a state or province (if applicable) and a Zip/Post code. You can see these inputs in the tax rate creation form that you can access by going to the “Manage Tax Zones and Rates” view (Sales > Tax > Manage Tax Zones & Rates) and clicking the Add New Tax Rate button:

tax_rate_create

Figure 3. Creating a tax rate.

The attributes you have to set are:

  • Tax Identifier – a unique descriptive code used to identify the rate in the back-end. It can be used in the front-end as well if you don’t specify a title for the rate.
  • Country – one of the world’s countries from a drop-down list. You can’t set one rate for all countries at once – you must create a rate for each country you are doing business in.
  • State – some countries can have different rates in different states or provinces. If your country has no states, or if you need to set up a country-wide rate, you should select *.
  • Zip/Post is Range – a yes or no choice. If you have a specific post code range where your rate applies – select yes and the form will display two additional fields where you can enter the range values. Setting this attribute back to no will display one field, Zip/Post Code.
  • Zip/Post Code – here you can restrict a tax rate to one specific post code. But more often you will use this field to enter * that means “no post code restriction”.
  • Rate Percent – here you specify the actual rate value. It is in percent, which means you must enter 10 for 10% and not .1.

The last attribute to set is Tax Title – but its input fields are shown only if your shop has multiple store views configured. If you are operating a multilingual store, you can use this attribute to set store-specific rate titles.

A clean install of Magento creates two sample tax rates: US-CA--Rate 1* (8.25%) and US-NY--Rate 1* (8.375%). The first rate applies to the state of California and the second one – to New York. Both rates have no Zip code restrictions. This is how the New York rate settings look like:

tax_rate_ny

Figure 4. A sample tax rate for New York.

Another example is a German reduced tax rate that applies to every German federal state and to every postcode:

tax_rate_german_reduced

Figure 5. German reduced VAT (7% MwSt.) rate.

The rates are saved to table tax_calculation_rate and are encapsulated in class Mage_Tax_Model_Calculation_Rate, whose method _beforeDelete() ensures that rates used by tax rules can’t be deleted. Rates titles are stored in a separate table tax_calculation_rate_title.

Tax Rules

So, on the one hand, we have tax rates bound to a location (address) and on the other hand we have tax classes assigned to products and customer groups. But, how does the system know which rate to use for which product and which customer?

Imagine, your store has three customer groups: retail customers, corporate customers, and tax-exempt organizations, each assigned to a different customer tax class of the same name. You also have two product tax classes: full rate taxable products and reduced rate taxable products. Finally, there are three tax rates: full, reduced, and zero. Given all this, you must ensure that for every possible scenario the right rate is chosen:

  • Retail customers buying full rate products: full rate.
  • Retail customers buying reduced rate products: reduced rate.
  • Corporate customers buying full rate products: full rate.
  • Corporate customers buying reduced rate products: reduced rate.
  • Tax-exempt organizations buying full and reduced rate products: zero rate.

Magento uses Tax Rules to implement these matches. Tax rules are entities that bring together tax classes and rates: when creating a rule you must select one or more product and customer tax classes and one or more tax rates. Here is an example of a rule designed to implement the first scenario:

tax_rule_1

Figure 6. A tax rule matching retail customer, full rate products and a full tax rate.

You can see, I am matching the Retail Customer tax class to the Full Rate Taxable Goods product tax class and to the Full Rate Goods DE 19% German VAT tax rate. This tax rate is set to match addresses from Germany (regardless federal state or postcode). It is possible to select more than one customer or product class if your tax scenario requires a broader matching (such as my example of tax-exempt organizations buying full and reduced rate products where I would have to select two product classes: full and reduced rate goods):

tax_rule_2

Figure 7. A tax rule matching a tax-exempt customer, full and reduced rate products and a zero tax rate.

I could also select more than one rate, which would make sense when different rates had been set up to cover different locations; and it is generally expected that only rate in a rule is matched to an address. But if there were a tax request matching multiple rates in one rule, the system would use the highest rate.

Note the priority attribute – the system uses it to sort matched rules in a descending order when looking for the right tax rate. If an address matches multiple rules with the same priority, their rates are added together. Consider an example: I have two rules, both with priority 1. These rules have one rate each – 10% and 15% respectively. The rates were set up to match every address in my shop’s country. So, if my shop uses its address to get tax rates, the resulting rate would be 10% + 15% = 25%. It may seem illogical, but with tax codes you never know – some might require just that. So, unless this is a desired setting – don’t set the same priority to tax rules to avoid accidental rate addition.

Tax rule functionality in encapsulated in class Mage_Tax_Model_Calculation_Rule. If you look into it, you’ll notice that it has a method saveCalculationData() that is called whenever a rule is saved.

public function saveCalculationData()
{
    $ctc = $this->getData('tax_customer_class');
    $ptc = $this->getData('tax_product_class');
    $rates = $this->getData('tax_rate');

    Mage::getSingleton('tax/calculation')->deleteByRuleId($this->getId());
    foreach ($ctc as $c) {
        foreach ($ptc as $p) {
            foreach ($rates as $r) {
                $dataArray = array(
                    'tax_calculation_rule_id'   =>$this->getId(),
                    'tax_calculation_rate_id'   =>$r,
                    'customer_tax_class_id'     =>$c,
                    'product_tax_class_id'      =>$p,
                );
                Mage::getSingleton('tax/calculation')->setData($dataArray)->save();
            }
        }
    }
}

Listing 1. Saving tax calculations, /app/code/core/Mage/Tax/Model/Calculation/Rule.php, line 89.

This method saves data to table tax_calculation – an entry for every customer and product tax class, and for every rate in the rule.

Tax Request

I’ve already mentioned that Magento’s tax rate selection is based on address information. Whenever a product needs a tax rate, the system generates a tax request – a Varien_Object instance containing address data and a tax class ID of the current customer. To specify what address data to base a tax request on, you must select one of three different options: customer billing address, customer shipping address, and shipping origin. This setting is found at System > Configuration > Sales > Tax > Calculation Settings > Tax Calculation Based On:

tax_calculation_based_on

Figure 8. Selecting an address option to base tax requests on.

The first two options fetch address data of the current logged-in customer provided that he has a default billing or a default shipping address in his account. For customers without a default shipping or billing address, or for guest customers (not logged-in) the system will use a “default tax destination” address that is set at System > Configuration > Sales > Tax > Default Tax Calculation Destination. The “shipping origin” option does not rely on customer addresses and uses the shop’s shipping origin address that you can set at System > Configuration > Shipping Settings > Origin.

Price Display Settings

Magento configuration allows you to choose how product prices are displayed in the shop – with or without tax. This choice depends on your business rules or the local regulations (some countries require showing full prices and applied tax rate). To specify your preferences in this matter, go to System > Configuration > Tax > Price Display Settings. In this tab you can set tax display mode for product and shipping prices. Magento offers three price display modes: including tax, excluding tax, and including and excluding tax (both prices are displayed together). The actual price displayed depends not only on these modes but also on tax calculation setting Catalog Prices. This setting (found under System > Configuration > Tax > Calculation Settings) tells the system if the product prices entered in the back-end include tax. For example, if catalog prices contain 10% tax then the system shows a product price of $100 in the front-end as:

  • “Excluding tax”: $90.91 ($90.91 + 10 / 100 * $90.91 = $100)
  • “Including tax”: $100

If catalog prices do not include tax then the display of a $100 price and 10% tax is:

  • “Excluding tax”: $100
  • “Including tax”: $110 ($100 + 10 / 100 * $100 = $110)

This how it looks with a sample product, a laptop that costs $1799.99:

tax_price_display_modes_including

Figure 9. Price display modes for a $1799.99 price that includes 8.25% tax.

If you’ve configured catalog prices not to include tax then the price display is this:

tax_price_display_modes_excluding

Figure 10. Price display modes for a net $1799.99 price and a 8.25% tax.

Conclusion

Diverse tax codes and regulations force Magento to implement a flexible tax system that is capable to function in most scenarios. This is achieved at a cost of high complexity for developers and shop managers. Setting up taxes in Magento manually can be quite difficult as you must correctly identify all potential scenarios and reflect them in tax classes, rates, and rules. It is possible to find modules that can take this load off you by automatically adjusting your shop setting to conform to your local tax regulations. One such example is module FireGento German Setup that configures your shop to operate in Germany, Austria, or Switzerland. Part of this configuration is setting up taxes (classes, rates, rules) that are used in the EU, and adjusting tax display modes according to the requirements in those countries. This module is available as a free download. If there is no such module for your country, you can use German Setup as a base for your development, just as the authors of module Danish Configuration did (module page). Magento Connect also hosts many modules that extend various aspects of the tax system, e.g., VAT verification, tax exemption, or rounding error correction. These modules can be found in the Site Management > Taxes category.

10 thoughts on “Taxes in Magento: Module Mage_Tax

  1. Hello,
    I want to know that how to calculate taxes Orders will be shipped from ours closest distribution center. We have distributors in OHIO, ALABAMA, MASSACHUSETTS, NEW YORK, FLORIDA, OKLAHOMA, PENNSYLVANIA, CALIFORNIA & OREGON.
    So I want create a rule for billing address which is entered by customer means taxes are calculated by in which origin that billing address nearly.

    thanks.

    • Hi Pawan,

      I think, you will need a custom extension for that. Standard Magento allows you to calculate tax based on:

      • customer shipping address (irrelevant in your case),
      • customer billing address, or
      • shipping origin.

      Your case is a combination of the last two, and standard Magento can’t handle that. You will have to write an extension that will rewrite the tax request processing to try finding a shipping origin using the customer billing address. Once the shipping origin is found, the extension will have to access tax rules to get the right tax rates.

  2. Hi,

    I have a customer group that can purchase a selection of products at zero rate but most must be at full rate.

    Any pointers?

    Many thanks,
    Ben

    • Hi Ben,

      My advice is as follows:

      1. Create a customer tax class and assign it to your customer group.
      2. Create a product tax class and assign it to the portion of the catalog products that must have a 0% tax rate for that customer group.
      3. Create a tax rate with 0%.
      4. Create a tax rule that combines your new customer and product tax classes, and the 0% rate.

      See Figure 6 for an example of a tax rule.

  3. Hello,

    I have some other issue with the calculation of the tax for products.

    I use magento 1.9.0.1

    My products are inserted with the prices without tax.

    So in configuration I have set
    Catalog price -> not including tax

    And all is fine on the catalog, product list, product page (the prices are displayed with tax added), but when I add product to the cart, or go to the cart page or checkout page the prices of products are without tax added.

    Do you maybe know solution for this problem?

    Thanks in advance,
    Robert

    • Hi Robert,

      did you found a solution?

      I also have all correct except in cart and checkout in SUBTOTAL and TOTAL prices with and without tax are the same… There are no TAX included

  4. How about setting up Asian Countries in your latest software version? You only seem to discuss US and EU on any searches I’ve done. Asia is more complex, in that you have different tax percentages for different APAC countries, for different product types. Not just one set percentage.
    As well, isn’t there an easy way to import the needed product/tax percentage? I’ve found no easy solution for this and can’t imagine it has to be such a menial, yet not complete, input process.

  5. I want to implement VAT validation in magento frontend for Europe base store, please suggest some explanation and procedure for that

Leave a Reply

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

Theme: Esquire by Matthew Buchanan.