matthiask / plata

Plata - the lean and mean Django-based Shop
https://plata-django-shop.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
197 stars 63 forks source link

More flexible tax rules #53

Open Soaa- opened 11 years ago

Soaa- commented 11 years ago

The current one tax per Price object method is very specific to certain regions of the world. I propose a more flexible tax calculation architecture that can be programmed to be compliant with more tax systems around the world.

Some background: I'm building a shop for use in Quebec, subject to local laws. The Quebec tax system uses calculates two taxes independently: the federal tax, GST, and the provincial tax, PST. These taxes are calculated on the order subtotal, and not per item (makes no difference if internal calculations use extra significant digits). Additionally, Quebec's PST used to be calculated on the subtotal plus the GST (effectively compounding the rate) but this is no longer the case. From my research, there are still places that compound the tax, such as Prince Edward Island. One more quirk: all this tax mess means the current rates in Quebec are 5.0% for the GST and 9.975% for the PST. Two decimal places are not enough to store the tax rate.

As such, I will attempt to write add a new TaxProcessor class to augment the current implementation. I plan to create a new setting PLATA_TAX_CLASS to be used the same way as PLATA_SHOP_PRODUCT, to decouple the actual tax model and allow a new model to be substituted.

While we're on the subject, why not rename those two settings to PLATA_TAX_MODEL and PLATA_PRODUCT_MODEL respectively? That would be clearer and more consistent, allowing future changes of this kind to follow a naming convention.

Soaa- commented 11 years ago

I wonder, too, if I should remove TaxClass.rate and create a class TaxRate with a ForeignKey to TaxClass... TaxClass.rate could sum up its rates, but local laws here don't allow that. Taxes need to be shown separately. My new TaxClass would also include a BooleanField tax_on_order, and the PriceBase admin could be modified to exclude TaxClasses where tax_on_order is set.

Soaa- commented 11 years ago

Oops, right, PriceBase has no admin definition, so ignore that part. Granted, we could create a PriceBaseAdmin(ModelAdmin) and a PriceBaseInline(...) to be extended and registered with the concrete model.

matthiask commented 11 years ago

Wow, thanks for the work. I'm interested in the implementation, and did not know that it's not allowed to save the tax total in a single variable. I thought that we could use the JSONField fields on both the order and the order items to save the tax details.

That does not work when a tax is applied to order items, and another tax to the order total though.

(Anecdote: It was shortly after visiting Canada (BC / Northern Territories though) that I implemented Plata's tax model.)

Soaa- commented 11 years ago

Thanks for your feedback. As you can see from my changes, I'm using the data field to store tax information, but in its own "namespace". Do you think I should instead implement my changes using the existing ProcessorBase.add_tax_details mechanism?

matthiask commented 11 years ago

The add_tax_details method works quite well for what we're doing here. I'm not sure whether it's flexible enough for the implementation of the tax system you're describing.