The Inevitable Tax Discussion

So, taxes suck, we all know this. The problem is that they're really hard to implement in Ubercart. The folks that wrote the Washington tax module kick ass BTW. I'm sure there are other good ones out there too.

First, some background. I'm only talking about US taxes here, and only sales taxes. Use tax / excise tax isn't as big of an ecommerce issue. Florida has hellish tax rules, as does California and Tennessee, so they may have to have some special case stuff done for them, but I think there is at least a semi-consistent way to implement taxes for most other states. I was the PM for a huge project to create a tax management system for SAP, so here is what my experience taught me.

Let's see if we can figure out how to make this work for Ubercart.

How taxes work

What determines tax rates

Taxes are typically destination based. Really, its where ever the "Point of Title Transfer" (POTT) occurs for a good. This means that if someone comes up to your shop in Oregon (where there is no sales tax), they can buy a good tax free, but if they ship it to you in Nevada, you're required to pay sales tax (technically speaking - more on this later) in Nevada. If I drove to northern California to meet at a location there, I technically have to pay California sales tax.

If you're a business buying something for resale, you get that good tax free, provided that you've filled out and filed the appropriate reseller exemption in the state where the POTT will occur. This typically has to be on file both with the state (and sometimes the municipality) and the vendor of the good.

Tax rates are based upon jurisdictions and product types. Jurisdictions are typically 2 or 3 levels in the US, and 1 or 2 levels in Canada (CST and GST). In the US, we assign jurisdictions primarily based on which state the good is being delivered to. Secondly, we can add additional tax based on the county or city. And sadistic states like Washington and California make us go down to the zip-code level (and sometimes even zip + 4 level). There are over 2500 tax rate changes yearly within the US just related to jurisdictions. Whole companies dedicate their entire business models to either finding tax offenders or protecting companies from losing in a tax audit.

Product types are a lot harder. There are literally thousands upon thousands of product types that exist from the more generic like "agricultural products" to "dietary supplements". These are also state dependent, as California taxes "baby diapers - natural" and "baby diapers - synthetic" differently (I'm totally not making that up). Often these also come with max taxes, graduated taxes, and other kinds of crappy tax schemes that are really difficult to define in software.

Finally, you have the idea of "Nexus". Nexus means that your business is on the radar of that state as being eligible to remit sales tax. This is where there is a big ecommerce gotcha. Many states require a business to either have a physical location in that state, or be marketing specifically to customers in that state to be given nexus. Ecommerce was traditionally exempted from this, but it's changing more and more each day - especially in B2B ecommerce. This is a huge tax liability concern, and we need to be thinking about how to implement it now before states that are struggling (like California) start giving everyone nexus just for shipping to that state (which they are perfectly capable of doing).

Some considerations

Effective dates are essential for this system to work. Tax changes occur with great regularity - to the point where compliance becomes a nearly full time job after having to keep track of more than 2 or 3 states. Tax codes need to be able to be turned off and on based upon dates in the system.

An additive hierarchy of jurisdictions should be implemented. Ideally, this would allow for an infinite amount of jurisdiction types or levels. This would suffice for most countries that I know of - including many VAT scenarios.

A reseller certificate system would be awesome for B2B ecommerce. This could be as simple as a checkbox that says "reseller certificate on file for product type X" and link that to a product class or taxonomy.

Tax functionality needs to be included in the API. Companies with nexus in many states almost always look to outside "tax engine" software to manage their tax compliance. An integration pathway to these systems is essential.

Some problems

I can't think of a good way to handle the product type issue. There are literally thousands. Most require research to actually figure out fully or the knowledge of an auditor.

Holy crap tax in Tennessee, Florida and California is really really complicated (especially Florida... WTF "discretionary sales surtax")

Reseller certificate management is a complex issue - this could be a can of worms. Regardless SOME implementation of it is really important.

Posted: Nov 12, 2009


Garrett Albright on November 12, 2009

Rather than get all this working in Ubercart, I think it might be easier to just overthrow the government.

All joking (?) aside, I wonder if there's momentum for a REST-ish central service where one could ping with "from" and "to" ZIP+4s and classifications of the products involved, and the service would reply with a description of what the definitive tax rate would be. That way, all these stupid arbitrary rules would only have to be tracked and updated once, in one central place (and the service would be of use to other e-commerce packages besides Ubercart as well).

It's pretty sad that it's come to that, though.

RSTaylor on December 8, 2009

Nexus is particularly troublesome. As an example, Massachusetts declared that certain businesses in New Hampshire were obligated to collect and pay Massachusetts state taxes (on transactions that took place completely within New Hampshire), on the grounds that the cashiers could not prove that the customers would not someday in the future use or store the product in Massachusetts.

But they didn't declare this to be in effect going forward, rather, they declared that back taxes were owed dating from years ago. So although this may not have affected e-commerce yet, if/when it does, it could be a real problem.

Naturally, with international transactions, multiple sellers, or multiple shipping destinations, it gets even more complex.

The more I think about it, the more I feel that taxes should be handled on the back end, rather than on-site in the shopping cart. Taxes should not even be a line item on an order. Rather, they should be thought of as a cost of doing business that a company accounts for and adjusts their prices accordingly to cover their costs. Not sure how well that would go over, though, or what the legalities involved are in terms of accounting. "Taxes are the responsibility of the seller" seems to work for some multiseller sites which are mostly just people selling a few things from home in their spare time, but what about a sizable business?

chrisstrahl on December 9, 2009

The problem with the statement "taxes are the responsibility of the seller" is in the case of large tax liability scenarios. These typically exist within B2B transactions, where you're talking about high volume with lower margins than most B2C sales.

In effect, when you're not accounting for tax liability of a few percentage points difference (say, NY versus ND - about a 4% difference), you're destroying the margin on a product or not remaining price competitive in the marketplace.

There are also a lot of good audit reasons to do this. When in an audit scenario, you still need to show the tax "collected" on individual invoices - even if it's just built into your margin. This means that you could potentially have to go through years of Ubercart transactions to calculate sale-by-sale taxes. I'm not sure, but I think some states also have legal requirements that taxes be shown on an invoice.

IMO - there has to be a way to handle tax rates. The WA state tax module that currently exists for UC is a good example of how to do this well. Having tax stuff exposed in the API is a minimum solution - but it's adequate to do what's needed if you use external modules. Creating a new "tax jurisdiction" object is a better way of doing it, but it also leads into issues of reseller certificates and problems I mentioned above with Florida (and the one just mentioned about Mass).

Regardless, there is a need to handle this *somehow*, and I'd prefer to do it in as robust a way as possible. However, I understand the need to balance development time with features.

Reinhard Glogge... (not verified) on February 11, 2010

I think it is really important for the tax system to be flexible in the way taxes are handled during display and checkout of products. Basically, there are two ways to do this:
1) The US way: Display product prices exclusive of taxes , add all the items of an order up, and then apply a tax on top of it all depending on the location of the sale.
2) The Euro (and Australian and some other) way: Display product prices either with or without tax, add everything up either with or without tax, and then display the order total with a separate line indicating the amount of taxes within the total. This is further complicated by the existence of different tax rates for different products even within a single order and so on.

It took a long while to get Ubercart to a point where this second way was supported (uc_vat module) and that was and is still quite buggy.

I would really really appreciate it if a way of handling these different ways of handling taxes could be designed in to dc from the beginning, maybe with a taxes API or something (I'm no coder myself, so please excuse the terminology). It would be sad if Drupal commerce would repeat the story of Ubercart which was basically useless for a long time in much of the world due to this issue.

sammys on February 13, 2010

Hi everyone,

When designing software I like to try describing the problem concisely yet generally. I have found that being able to describe it this way leads to a complete design. Especially when it's vastly complicated like taxes are.

Here's what I'm thinking describes what I know about taxes.

Taxes are a charge added to an order where the charge is made up of component tax charges. A component tax charge (CTC) is calculated based on: order total, line-item sub-totals or a combination of both. An order total CTC might be calculated before, after or, before and after, a line-item sub-total CTC is applied to each line-item. Each CTC calculation can be a percentage or fixed amount of what it applies to. The actual percentage or fixed amount used depends on the reseller's postal code and the purchaser's postal code.

I feel this is just a starting point for the definition. I'd like people to read it with their situation in mind and then post a reply saying whether it fits and, if not, what doesn't fit. In the latter case, please supply an example scenario where it doesn't fit.


Reinhard Glogge... (not verified) on February 16, 2010

Taxes are a charge added to an order ...
Not really. Taxes are not always added to an order but can in many instances (Europe, Australia etc.) be merely a percentage of an order. In these places, an order always consists of a sum of line-items whose prices are displayed including taxes. Taxes are then not added to the order but merely displayed as a fraction f the order total like this:

Order summary:

Item 1 $a.aa
Item 2 $b.bb
Item 3 $c.cc
Sum of Items $d.dd
- including $e.ee of Tax y
Shipping $f.ff
Order total: $g.gg (sum of $d.dd + $f.ff )

The actual percentage or fixed amount used depends on the reseller's postal code and the purchaser's postal code
No, also only true in the US AFAIK. In Europe the tax rate may depend on a whole host of different factors among which are:
- tax rate per product (books have different tax rates from software, taxes on foodstuffs are different from cars...)
- country of store and purchaser and tax status of purchaser (if I sell a product from Germany to France and the purchaser is a business, he usually won't have to pay taxes on the product, if the purchaser is in Switzerland however, he has to pay taxes)

I know it's complicated, so I think that it would be best to expose tax calculation and price display as a hook on a product basis so that contributed modules could go in and modify prices and orders in ways that are not predetermined by the Commerce module.

harrisben on February 16, 2010

I was meaning to comment on this myself but forgot.

It is considered a dubious act to not clearly display tax on a product/service in Australia. As Reinhard mentioned, postal codes are not a factor when calculating tax for stores outside the US, the product/service is the determining factor.

My dream of a perfect solution to the way tax is handled in the future would be that all products have a tax field/classifier so that there is no hack required to insert one when it isn't there.

sammys on February 17, 2010

Thank you for the responses. When I wrote the first version I knew about these situations and yet wrote the description poorly. I've now added these in the new description.

Taxes are charges added to an order where each charge is a component tax charge (CTC). CTCs are calculated based on: line-item sub-totals, order total or a combination of both. A line-item sub-total CTC is calculated using the product price. An order total CTC might be calculated before, after or, before and after, a line-item sub-total CTC is applied to each line-item. Each CTC calculation can be a percentage or fixed amount of what it applies to. The actual percentage or fixed amount used can depend on the seller's postal zone and entity type, the purchaser's postal zone and entity type or neither. A postal zone is the combination of country and postal code. Examples of entity types are businesses, not-for-profits or individuals.

I agree that tax calculations should be exposed through hooks. I usually try to ensure that hook implementations repeat as little code as possible.

Ideally, I'd like the system to use easily updateable metadata that describes the rules needed in each jurisdiction. The system then implements the rules and applies each one when it's meant to. Whether this happens in the next version is up to the community, whether we have a developer willing to do it and all the information to do it. This is what my description is for. :)

Re: Australian GST. It is calculated by product in properly designed systems. You aren't meant to pay GST on a bottle of milk at the supermarket. You are meant to pay GST on a glass of milk at a cafe. Eating in at the cafe of course! They wouldn't give you a glass if you were ordering to go! :p

svendecabooter on February 20, 2010

This will probably be a difficult implementation, given the fact that the tax system seems to vary widly across the globe.

I assume the best solution would be to have a pluggable system (based on hooks i assume) that gets a lot of input parameters, and calculates taxes based on that.
So far the input parameters mentioned in this thread seem to be product type (taxonomy?), country, postal code, ...

This hook would thus have to pass through the complete order, containing each individual line item (products), regional info about the shipping & receiving entities (users?), and perhaps more input objects.

A plugin system could enable an ecosystem of tax rules & calculation modules per country or region (e.g. US, Europe, ...) that can easily be plugged into the commerce site.

I'm still working my way through the architectural setup of Drupal Commerce, so forgive me for my ignorance :)

I think it's important to provide enough ways to customise the tax calculation based on specific conditions / rules.

AeM (not verified) on February 23, 2010


moreover, could it be possible to have for example :

for one role (say, "public") the taxes
and for another role (say, "companies") the taxes excluded ?

for doing B2B and B2C in the same site ?

AeM (not verified) on February 23, 2010

addendum :

Reinhard Gloggengiesser :
shipping costs could be with or without VAT, too

markalosey on April 9, 2010

We are currently using an outside service to calculate taxes in our d5 and d6 installations.

We have another situation though. We have b2b buyers that use some of the products they purchase and resell other products. Those buyers need to be able to mark some products as taxable and others as non-taxable...provided we have a cert on hand for them.

AeM (not verified) on December 27, 2010

i second the "rules" proposition !

- per country (for example take quebec/canada which could
have from 1 upto 3 taxes following where the buyer/seller is)
- per role (public/company/b2b/...)
- per product type (for example in france you have 3 VAT percentages following the kind of product, and of course
an ecommerce can sell books as well as other kind of products
isn't it ?)

Sean on October 24, 2011

It's funny you mention the WA State Tax module, because I was looking at that just the other day while thinking about this. I don't have a need to use that module, but the way it is designed is a great way of handling the complicated system.

In brief, it basically checks the address, if true on match to WA it drills it down to the location it needs, accesses the WA.gov site to check the tax rate for that location, and then applies the tax.

This is the best way to handle WA and I think this is the best way to handle ALL states.

What we should really consider, is building a module that will access EVERY state.gov site. Every state has a tax reference just like WA but not all states are as strict or complicated. So as long as you're checking the state.gov you will be up to date on any changes for any state. If they go complicated, you already have the system setup and you don't have to worry about it.

The next thing is to create the UI where the site owner can select which states to access. It may be just your state, but if you get nexused in CA, you can easily just click a boolean next to CA in the interface and now your store will check against the CA.gov site for all matching addresses in CA.

I fully understand that this type of system would be complicated (at best), difficult to code, and overkill for some sites. In that case, perhaps a frame API and then submodule all the states. So you have Commerce Tax Framework Module and then Commerce California Tax Module which is dependent on Commerce Tax Framework Module, and so on.

In that way you could ask the creator of WA state module (sorry I can't remember her name) to build the framework and then each developer here could build their own state's module based on that framework. Or, everyone here could split a sponsorship for her to build the entire thing. There are enough people just in this discussion that I think a small amount each would go a long way.

Just my two cents.

mausolos on August 19, 2012

I'm not sure if this works for the rest of the world (I think it might), so I will speak to what I know, which is the chaotic and varied US tax system.

Here are the problems you face:
1) Postal codes (your best identifier for tax laws) change, and sometimes new postal codes are created.
2) Tax codes change.
3) The determination of tax on any single, line item can be boiled down to this:
---a) given a list of zip codes that pertain to the seller;
---b) given a receipt zip code that applies to the buyer;
---c) what city, county, district, state and/or federal taxes apply?

Sure, if you have a site devoted to a single shop out of one or a small handful of states, you could spend some time making Rules (a la http://www.drupalcommerce.org/user-guide/tax-configuration); the problem is, you are on the hook for managing all that data, keeping it up-to-date, etc. You could write a module for every state (for instance, http://drupal.org/project/uc_tax_wa), but when you consider the dozen or more states that might have different rules, plus federal districts, etc, it seems a bit unwieldy. And now with some tax districts requiring merchants that don't even exist in the buyers' state to collect and report taxes, it has become especially crazy-making.

Here's what I think needs to happen:
1) there is a Commerce Postal-Tax API layer. You, the Drupal Web Manager, should not have to busy yourself with all these details. You simply have a list of postal:country codes where you are located and selling/shipping from (12345:US, 123 ABC:CA, etc). You pass this list and the destination zip associated with the line item to the CPT API layer, and you magically get back the calculated tax rate. This might even get cached and held onto for a specified time, according to some setting in a control panel. It gets flushed based on either size, or whenever the postal/tax data gets synchronized.

2) there is a Commerce Postal-Tax Data Set. You begin with an initial set of current, up-to-date postal codes, tax codes, and look-up tables. Postal codes, at the least, are consumable by other entities in Drupal, perhaps as a flat Vocabulary, or something. Simple views look-ups are provided, perhaps tie-ins to location/addressfield/GeoIP modules, etc.

3) there is a Commerce Postal-Tax Service layer. This interacts with the Drupal data layer, the CPT API and acts as a client service to some [likely commercial] external Postal-Tax Server. The Postal-Tax Server is (hopefully) some simple RESTful service that negotiates the synchronization of updates to the CPT Data Set on a configurable heartbeat.

Of course, the weakest link in this concept is the reliance on an external service - surely there is already someone or something out there that does something like this, what with all the shopping-cart software?

zeta1600 on October 12, 2012

Just wanted to subscribe to this thread. For some reason, I thought it may be as simple as asking the customer's ship to zip code (plus 4 or whatever locale if necessary), and serve a rate from a tax table db. I guess not huh? For those non-taxable rate products/service, those should be determined when the product is created. no?