The Tax module should invoke hook_commerce_tax_info() to get information on tax rates from modules. The Tax UI module can provide a user interface for adding new tax rates, but it would be just as reasonable for sites to define their taxes in custom modules (esp. for sites depending on third party services for tax calculation). Tax rates should contain at least the following properties:

  • name - the machine-name of the tax rate
  • title - the display title of the tax rate (i.e. KY Sales Tax)
  • rate - decimal value representing the percentage of the tax rate

Tax rates will be applied on a per-line item basis, so we'll need some way to specify the tax class of a line item. When it comes to applying taxes to line items based on the tax rate, I'm going to go out on a limb and say we can configure this via Rules... but that just might not be possible. Perhaps we can use Rules for simple rate <-> class matching but provide a hook for more complex needs. : ?

It's still unclear to me if we'll store the calculated tax amount per line item or simply have a Tax line item type that calculates the tax for each item and totals it into a single price field on the tax line item.

CommentFileSizeAuthor
#19 vat-order.png71.65 KBchaugi

Comments

rszrama’s picture

Looking in the issue queue, I noticed we had an issue linking to a very helpful blog post. It can be referenced along with any appropriate threads on drupalcommerce.org:

http://blog.klavsen.info/content/designing-proper-taxes-support-webshop

rszrama’s picture

Component: Taxes » Tax

In yet another older Tax issue I see I specced out the following component list, which I believe is represented well in the original post for this issue:

  1. Defining the tax line item type.
  2. Defining a tax rate info structure.
  3. Using Rules to determine which taxes apply to items on an order. (It's important taxes are applied on a per-line item basis, including the possibility of adding to other tax line items.)
  4. Sorting out display / data entry issues for tax-inclusive pricing...
jhodgdon’s picture

I would like to request that whatever structure you use, you make an API that other modules can hook into, that doesn't depend on rules to determine the taxes.

I maintain two tax modules for US states (WA and KS) for Ubercart. The issue in both of those states is that you have to charge tax whose rate is based on the shipping destination. So a simple rule for the rate is not enough to figure out the tax -- you have to use a rate lookup service to get the rate based on the address.

Also, in WA, shipping is taxable, but food isn't, so what you said above is right -- the tax calculation needs to be fairly flexible as far as seeing the whole order and deciding which parts of it to tax.

So, whatever API you set up, it would be good if it was flexible enough to let other modules examine the order and determine whether they want to charge tax and how much tax they need to charge, rather than requiring it go through Rules.

Also, in these states, the commerce module would need to have a reporting mechanism that would list out how much tax was paid to each taxing jurisdiction. In Washington state, there are several hundred possible jurisdictions. So when the tax is calculated, the juristdiction name and code need to be saved with the tax line item, and the tax report needs to find that information and collect it into a report, with totals by jurisdiction.

Just some thoughts, since it looks like you are designing the structure/API for taxing...

steffenr’s picture

An API would be perfect - in germany and some other european countries the tax-system is more complex. longwave already created the uc_vat module to help handle taxes for european shops.
In ubercart 2.4 it works perfect for most purposes..

rszrama’s picture

Alrighty... here goes. I believe the basic functionality is implemented through Rules and a simple API, but we'll need to run some use cases by it to ensure it is flexible enough. There's also still some UI work to be done to integrate tax data into the order / line item displays and to allow for tax inclusive price input. Should be simple, right?

So, lemme start with a few use cases, and please don't hesitate to offer up edge cases for consideration:

  • Basic sales tax configured via Rules - in the U.S., you just have to apply any sales taxes levied by the state(s) in which you do business if you are shipping to someone in state. The tax doesn't have to be displayed during the cart phase but does need to be included in the order total.

    Such a tax would be configured by adding a "Sales tax" tax type via hook_commerce_tax_type_info() that requires the order context to be calculated, adding a "[State] sales tax" tax rate via hook_commerce_tax_rate_info() in that type, and editing the tax rate's default Rules component to include a condition checking to see if the shipping address matches the [State].

  • Complex sales tax calculated via a module - in some states, this model isn't realistic because as Jennifer mentions above they have a large number of tax rates based on cities, zip codes, counties, etc. For these you'd either maintain a table you can cross-reference in the DB or ping a web service to find the appropriate tax rate.

    To accommodate such a tax system, you'd add a "Sales tax" tax type that requires the order context to be calculated, but you wouldn't define any tax rates right away. Because you really only need tax rates defined when they're used in this scenario, you'd use hook_commerce_tax_type_calculate_rates() to look for rates and then when one applies, you'd save it at that point to the DB w/ a unique name and use hook_tax_rate_info() to expose these used rates to the system.

More to come...

  • VAT included in product price - ...
  • Cumulative tax - ...
jhodgdon’s picture

Saving a rate to the db and then exposing the saved rates separately sounds a bit odd. It really just needs to be calculated dynamically at the time of the order, because tax rates can and do change, and obviously if in the course of placing the order the buyer changes their delivery address, the tax will need to be recalculated. But I guess this rate calculation will be triggered when the order changes?

Also, the decision about what is and isn't taxable may also change based on the delivery location (and over time). For instance, in WA food is not taxable but shipping and handling charges are. In ID, food is taxable. The system needs to be flexible enough to handle that too -- letting the tax module and/or static tax setup figure out which line items and products (in Ubercart terminology) should be taxed.

Some thought about reporting needs is also needed here, I think. In WA, you need to report (monthly, quarterly, or annually) the amount of sales tax you collected for each jurisdiction, and a jurisdiction consists of a name and a code. So this information needs to be saved and available somewhere for reporting. I guess most states and countries must have similar requirements, although the details of what a "jurisdiction" is will vary.

rszrama’s picture

Yeah, the idea of a local rate is a little weird, but I see it mostly as a caching thing. For example, if you're pinging a web service to get a rate for a particular order, you'll want to have some sort of local ID for the tax you apply and the title so you don't have to ping the web service every time you want to display the rate information. It could be that you just always keep a local cache of all the possible rates... I think the main thing is that a tax would have some way to be identified later (i.e. a unique ID - like ky_sales_tax, wa_sales_tax_ZIP, etc.). The rate itself can change as necessary.

As for updating the taxes on line items, we need to have a more intelligent system than we had with Ubercart. For some taxes, it currently operates through the product pricing system which calculates a product price and/or tax prior to checkout. Naturally, once someone has paid for an order, you don't want the tax to change next year when the rate adjusts or all your old orders will be out of sync. That tells me we need to take a snapshot of the tax ID / rate at least that is associated with that line item. I'm also continually updating shopping cart orders to catch changes in product information prior to checkout, so I think I'll use that same process to invoke a "Calculating taxes for an order" event that let's you check for taxes that require order information (instead of just the product information available on the "Calculating the sell price of a product" event). We'll have to have some way to invoke this hook on the back-end for administrator created orders... the problem w/ Ubercart was any time you edited an order the tax would recalculate. I think we'll probably want a more manual process... there are many various reasons that an order could be updated when you don't want to recalculate taxes.

I haven't really nailed down a good reporting solution, because tax information is presently being stored in a price field on a line item. We're going to have to figure out something that can scale... even if it just means setting up a table that, on payment receipt, is populated with the amount of tax collected for each line item of each tax type. I think we can run with that for starters, but let me know if you have any better ideas. : )

jhodgdon’s picture

In Washington, the jurisdictions do not actually correspond to zip codes. For instance, I live in 98133, and that zip code (for historical reasons) is half in the city of Seattle and half in the city of Shoreline. Since cities can have different tax rates, there might be two different tax rates within that one zip code. It's quite complex, which is why WA provides the tax lookup service by address.

For that reason, I really think you actually *do* want to do the web lookup every time you want to display the rate information, unless you are sure that no part of the address has changed, and I don't see any real way to cache the information except perhaps by order ID, and why I think your proposed architecture is not generic enough to handle this situation.

I will also note that Kansas has recently gone to a similar system, so it's not just WA that's an oddball. Then there are other states like California, which have adopted a truly byzantine system without providing a web lookup, basically making it impossible for anyone to calculate the correct tax, as far as I can tell. (Someone recently asked me, since I maintain the UC WA and KS tax modules, to look into California and I decided it wasn't really feasible.)

I agree that the tax rate, jurisdiction (or other reporting information), taxable amount, and amount of tax all need to be cached at payment time.

As far as reporting goes, as I recall the tax report that I worked on for Ubercart just looked at the line item table and used that to figure out the amounts for the report. I guess I don't see why that wouldn't be possible in Commerce also? On the other hand, a separate table of tax transactions wouldn't be a terrible thing. But you would have to be careful about what to do about updates of orders, refunds, etc., especially if for instance the order was on Dec 20 and the refund was Jan 10 of the next year. I guess that's an argument for why a separate transaction table would be good, come to think of it -- when an order is revised, you could just add an entry for the revision date saying x amount of additional tax was collected or refunded or whatever. ???

chaugi’s picture

Hi, my 5 cents about tax usage in some European online stores. The main point is that mostly price is defined tax inclusive, so there is nothing new. But when it comes to billing, the story changes depending to which country your are billing an order. If you bill within EU (at Least in Latvia), than your order should list all item without VAT, Subtotal without VAT, VAT separately, Total including VAT. If you bill to non EU countries, than your order should list items with VAT inclusive price (which was shown to clients during the purchase), but without extra rows: Subtotal without VAT, VAT, Total including VAT; just one row: Total.

rszrama’s picture

Issue tags: +beta blocker

Tagging.

antiorario’s picture

subscribe

TheWizz’s picture

subscribe

aidanlis’s picture

rszrama’s picture

Status: Active » Needs review

I think this issue is done. I've spent the last 15 hours or so on the "finishing touches", and we now have the ability to track taxes in the components array of a price. We can also track whether or not they've already been included in the displayed price. There's a display formatter for fields like the order total that will list out the components of the price, so you can see the various components that went into making the total price.

Only local images are allowed.

Additionally, I just committed code that gives price widgets the ability to accept prices including VAT. Since I store in the product price's data array that the price was entered including VAT, it won't get duplicated later in the normal tax calculation process.

Only local images are allowed.

Only local images are allowed.

I'm not being very coherent because of the time, so please send any questions my way and give the system a shot. Or in the other order. : P

(I should add, the latest commits are still just in my GitHub repo... will export in the AM. Hmm... later in the AM.)

aidanlis’s picture

Wooo awesome!

pixelsweatshop’s picture

Rockin!

rszrama’s picture

fwiw, just committed a Views area handler to get that fancy order totals table into the checkout process - no more surprise order total changes after you've completed checkout. : P

Also, some Twitter feedback from Richard at i-Kos - it appears to handle VAT adequately for his British clients. Yay. : )

rszrama’s picture

Status: Needs review » Fixed

People have been putting this through its paces today and yesterday. I think the current behavior is a go for 1.0.

chaugi’s picture

StatusFileSize
new71.65 KB

rszrama
Looks awesome, is it possible to have setting allowing to see price in table with VAT excluded for product items (even if it was entered with VAT inclusive), please see attachment. This will meet all needed EU requirements for Baltic countries for invoices.

rszrama’s picture

I think I might need to put a patch into the price display formatters to render prices excluding certain price components (like an included tax). I'll open a separate issue for that.

chaugi’s picture

Precision: I meant only invoice display. So price for product entered VAT inclusive, displayed in catalog/cart/checkout also inclusive (as on your screenshots above), only in invoice it is displayed VAT exclusive as on my previous attachment.

If this combination of displaying VAT inclusive/exclusive vary for different countries then it is good idea to have granularity for certain components as you said.

Thank you.

brst t’s picture

How can the rules module be used to determine sales tax based on info in the customer profile when the Customer Profile entity isn't available to the event, 'Calculating the sell price of a product'

I'd like to see sales tax be calculated like shipping and calculated after customer profile is entered.
Like this one http://artistshoppe.threadless.com/product/40747/prints?cl=7

If it can't be updated via AJAX on the checkout form itself, then it would make sense to move the shopping cart contents pane to the top of the review order page at admin/commerce/config/checkout.

I'd want to handle customer specific discounts the same way -- a special discount field on the customer profile.

I don't see how rules can accomplish any of this if the customer profile entity is excluded from that event, 'Calculating the sell price of a product'.

Unless I'm missing something in exposing entities or xyz with the new version of the rules module ?

rszrama’s picture

It's not missing, you just have to add a condition to ensure the data is available. So, make the first condition a Data comparison that ensures the "Type" of the order is "Order". That will make any "Order" fields available in your Rules data selectors. Alternately you can use the "Entity has field" condition on a per-field basis.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

essbee’s picture

Category: feature » support

Hi Ryan,

Nice work on the tax system, however I seem to be lost trying to get a double tax system implemented.

Basically I have two taxes WET and GST.
WET only applies to a specific product type (currently all my products are this type).
GST applies to all line items.

Prices are entered inclusive of WET, this is functioning fine, and is displaying correctly in the order view.
GST needs to be applied on top of the WET inclusive price and shown as a component of the Order Total

So basically I want the order to display as follows:

Product 1 $10 1 $10
Subtotal $7.75
WET $2.25
GST $1.00
Order Total $11.00

Does this make sense, it seems quite simple, I just can't get the second TAX to apply.

lsrosales’s picture

HI
Since I am new to Drupal and my boss had asked me to make sure that all our items are using the tax rate we have in placed to ALL orders, no matter what state the customer is from. Any advice on how to quickly make this change.

Thanks for the help.

rfay’s picture

@Isrosales, please take a look at http://www.drupalcommerce.org/node/454 -

also, since you're new to Drupal: Putting comments on closed issues doesn't usually get you anywhere. Opening a new support request is probably the best. But first, read the admin manual on Drupalcommerce.org.

Thanks, and Welcome.

lsrosales’s picture

Thank you