NC160: European tax calculations over orders.

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
13 years ago
For NC160

Description:
A better (correct) way to calculate taxes over the order and discounts. Particularly (Dutch, European) way of tax calculation. Code base and changes are for reference usage.

Algorithm perfected to calculate tax percentage over discount when assigned to whole order or order level discount has been added. (see function GetDiscountAmount() in price helper) This calculation can work on shopping cart or on order (reverse calculation proof).

When we have the tax component over discount amount ( as there is no tax category for discount on order level), this should be deducted from OrderTotal. Based on user selection for Incl. or Excl tax display GetDiscount amount can be called to get discount amout incl or excl tax.

In the Order table we always store discount amount incl tax. So if a user selection was incl. tax, then this amount is displayed as is, otherwise recalculation can be done using function GetDiscountAmount() function on order level.
Details tax calculation table displays this on the OrderTotal page and on the PDF invoice. The table is not required and can be removed if needed,  it is placed there for testing purposes for checking if the algorithm works consistently on shopping cart and on saved order.


Files affected:
/Libraries/Nop.BusinessLogic/OrderManager.cs
/Libraries/Nop.BusinessLogic/ShoppingCartManager.cs
/Libraries/Nop.BusinessLogic/TaxManager.cs
/Libraries/Nop.BusinessLogic/PDFHelper.cs
/Libraries/Nop.BusinessLogic/PriceHelper.cs

/NopCommerceStore/Modules/OrderDetails.ascx (cs)
/NopCommerceStore/Modules/Ordersummary.ascx (cs)
/NopCommerceStore/Modules/OrderTotals.ascx (cs)


changes:

1.  Function GetDiscountAmount(…) is added to calculate discount with tax in PriceHelper.cs file (Line 327) as
public static decimal GetDiscountAmount(ShoppingCart Cart, Customer customer, bool includesTax)

2.  Function GetOrderDiscount(..) in ShoppingCartManager.cs line 1284. has been overloaded with extra parameter includeTax to get order discount with or without tax.
3.  Function GetDiscountAmount(Order order, Customer customer, bool includingTax) in PriceHelper.cs, its same as function added in point 1 above except it works on order instead of shopping cart reverse engineering (for re-calculation). Line number 474.
4.   Function  GetOrderBTWCalculation(Order order) has been added in PriceHelper.cs at line number 588, it gives a datatable to describe how the calculation is done. (Placed on invoice for tax administration).
5.  Function  BindData() in OrderDetails.cs adjusted to get discountStr with or without tax line number  176.
6.  OrderControl.ascx and OrderTotals.ascx.cs has been changed to display tax calculation detail box  on page and calculation is done in .cs file.
7.  Function  GetTaxTotal (..) has been changed (line number 276) to call PriceHelper.GetDiscountAmount() function to get discount amount incl. and excl. tax.
8.  Some other cosmetic changes has been made in OrderSummary.ascx, OrderDetails.ascx and respective .cs  file to show tax suffix string at relevant places and also show vat % in form of article.
9.  After all these changes order can be saved as incl. and excl tax and displayed accordingly on PDF invoice.
10.  For displaying tax calculation detail box some new resource strings have been added(should be added accordingly in new source).
11.  Changes in above changed source files can be searched with “Archifact SSA” text.
12.  Function PlaceOrder(..) in OrderManager has been changed (line number 2837)to call ShoppingCartManager.GetShoppingCartSubTotal() with including tax (true), in order to get discout amout incl tax (we always store discount amount incl tax in order).
13.  ShoppingCartManager.GetShoppingCartSubTotal() has been changed(line number 563)to return discount amount incl or excl tax.  



Screen Prints (cosmatic changes)

Shopping cart order totals:
http://www.archifact.nl/fotos/416_37.png


Customer order details page:
http://www.archifact.nl/fotos/417_37.png


PDF invoice:
http://www.archifact.nl/fotos/418_37.png


You can download the whole package here.
13 years ago
Thanks,

it's already on our roadmap. Please go and vote here
13 years ago
Yes it is on the roadmap and in a work-item. And above is the solution ;)
13 years ago
USRFobiwan wrote:
Yes it is on the roadmap and in a work-item. And above is the solution ;)

Yes, thanks a lot
13 years ago
Sorry, there's a bug in it:


http://www.postme.com/i/DiscountError.jpg

I'm working on solving it, but it really seems that this code it buggy.

I have to have all my prices ex vat in my store - the reason for this is that if we sell stuff out of EU - we can sell things without VAT.

I've testet it with the setting: "Tax.PricesIncludeTax=true" and it seems to work fine with this - but not the other way around...
13 years ago
stoffer wrote:
Sorry, there's a bug in it

See the following topic (with a link to a changeset) - https://www.nopcommerce.com/boards/t/5249/tax-is-calculated-on-pre-discount-amount.aspx?p=1
13 years ago
Hi. I now fixed it with the following scenario:

1) All prices in system are without tax
2) Customer can be outside or inside a tax category (in my case outside/inside eu)

Complicated stuff :)

Anyways. It was a bit trial and error, I used a combination.

In pricehelper i did the following:

if ((includesTax) || (!priceIncludesTax))
                {
                    taxOverDiscount[productIndex] = discountPerProduct[productIndex] - (discountPerProduct[productIndex] / ((100 + taxRate) / 100));//(discountPerProduct[productIndex] * taxRate) / 100;
                    discountPerProductExclTax[productIndex] = discountPerProduct[productIndex] - taxOverDiscount[productIndex];
                }

                else
                {
                    //=121*25/100
                    taxOverDiscount[productIndex] = (discountPerProduct[productIndex] * taxRate) / 100;
                    discountPerProductExclTax[productIndex] = discountPerProduct[productIndex];// -taxOverDiscount[productIndex];

                }

(that means if prices are not with tax, then always use the first calculation.)

Also changed a little here:

     if (includesTax)
            {
                discountFinal = discountAmountInDataBase;
            }
            else
            {
                discountFinal = discountAmountInDataBase - totalTaxOverDiscount;
                //discountFinal = discountAmountInDataBase + totalTaxOverDiscount;
            }

(meaning if price does not include tax - then substract it again.)


I used this fix also: https://www.nopcommerce.com/boards/t/6273/tax-calculated-before-discount-taken-customer-pays-tax-on-nothing.aspx

It is not a elegant fix, but just a fix. I think it's my reason to finally take that step to 1.8 and send you all my stuff Andrei :). Seems that it's fixed here...

/chris
13 years ago
Stoffer, did you see the VAT support that I submitted and which is in Nop 1.8? It doesn't get round the discount bug issue but it will charge VAT based on customer's location (i.e. inside or outside EU) and whether or not they are VAT registered, you can have different VAT rates (one per tax category), and it can check customers' web numbers against the EU web service.

I will get round to writing a setup guide at some point!
13 years ago
Hi'

Nope. Sorry. Did not have a chance yet to look at 1.8. But your approach sounds good.

My requirements are really simple (and so is alot of other EU countries) as there is only one tax on all items whatever you buy. So I basically only need VAT, yes or no :). It is only if you reach some annual amounts (usually around $100K) that you need to consider paying vat in the export country. It could actually be really cool to make some logic for this also at some point..

Hope to see a solid solution that takes care of all this. And it could be really cool if somehow the basic calculations could accept native lists (instead of just shoppingcart or the like). So I only need to calculate the numbers one place. But that is another matter.

After going through the code and debug, I think that this area actually is the most complicated area in the whole system...

B.R

Chris
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.