Replies: 15 comments 9 replies
-
thanks @cruftex for your work on this huge subject ! |
Beta Was this translation helpful? Give feedback.
-
Hi @cruftex, I like your initiative, these are some points that need more thought. :-)
You cannot store the prices with tax. Tax is could be different for every country you are selling in and you need to calculate it from the base price.
There already is our own Decimal library if I am right, that should handle all number calculations precisely.
There is a problem with this. Imagine you display both prices per product.
|
Beta Was this translation helpful? Give feedback.
-
You are welcome.
At the moment, you can enter the tax inclusive price and the tax rate in the normal product entry. So there is only one tax rate, the one that was entered, which defines the rate for the tax inclusive price that was entered. Store the price exactly as entered, e.g.: (incl = 15, excl = null, rate = 19%) and then convert the price only if it is actually needed without tax. So for an end user shop in Germany, you would never need to calculate or display the tax exclusive price. If your prices are typically VAT exclusive, then you enter those and calculate with them. My first ERP system that I started using in 1994 was doing it that way and every other ERP system I saw meanwhile likewise. Fun fact, you can read about the history of this ERP system here: https://www.gandke.de/gshistorie.html There is the feature of specific prices. I have not looked into that in particular, but I assume that you can enter prices for a specific country.
As I said. You need to calculate exactly with the price you are offering the product. Let's take your example, the price was entered with tax, but then it is offered without tax. With 2 digits precision, you offered the product for a price of 0,12, which is 0,12000000000 Euros and not a single centi-cent more ;) If you want to calculate with fractional cents, then this needs to be able to reproduced by the customer. So you need to show more precision on the product price, invoice positions and then round the final invoiced to something that actually could be transferred. In that example, the tax inclusive price of 0,15 Euro would be never displayed to the customer, since you show prices tax exclusive. That price is therefor not relevant for the transaction itself. Only if we need to charge the customer with the VAT, the invoice needs to have a tax inclusive amount for the final total sum.
You calculate either with tax exclusive or tax inclusive prices. If you want to display both prices, the other price would only be informative and imprecise and MUST not be used for any calculation. For template variables, I recommend, that there is only one price on product level and position level. If the other price, or the included tax is needed, e.g. for informative purposes, that could be calculated in that template. 99% it is not needed and just having one price makes things easier. Example as it is now from ...
<th class="product header-right small" width="15%">{l s='Unit price' d='Shop.Pdf' pdf='true'}<br />{if $tax_excluded_display}{l s='(Tax Excl.)' d='Shop.Pdf' pdf='true'}{else}{l s='(Tax Incl.)' d='Shop.Pdf' pdf='true'}{/if}</th>
<th class="product header-right small" width="15%">{l s='Price' d='Shop.Pdf' pdf='true'}<br />{if $tax_excluded_display}{l s='(Tax Excl.)' d='Shop.Pdf' pdf='true'}{else}{l s='(Tax Incl.)' d='Shop.Pdf' pdf='true'}{/if}</th>
....
<td class="product right">
{if $tax_excluded_display}
- {displayPrice currency=$order->id_currency price=$order_detail.unit_price_tax_excl}
{else}
- {displayPrice currency=$order->id_currency price=$order_detail.unit_price_tax_incl}
{/if}
</td>
<td class="product right">
{if $tax_excluded_display}
- {displayPrice currency=$order->id_currency price=$order_detail.total_price_tax_excl}
{else}
- {displayPrice currency=$order->id_currency price=$order_detail.total_price_tax_incl}
{/if}
</td> |
Beta Was this translation helpful? Give feedback.
-
@cruftex the more I work on the subject the more I understand your pertinent remarks.
I totally agree with this point
Considering the switch of currency possible and the number of decimals is different by currency. IMO what is important is the price that the user sees on the FO is used for the calculations. So the price on the will be rounding
If you are available let me know I have a spreadsheet which simulate the futur behavior. It could be interesting to test it with you :-) I will be on holidays a few days. If you are available. I will send you my calendar to position a point :-) to test around this point first and point after point we could validate weeks after weeks :-) |
Beta Was this translation helpful? Give feedback.
-
That is correct, if you round to the number of displayed decimals, that is solving the problem that the price is a bot off, like 99.999999 in the above example. You are calculating with the same price that is displayed. But there could be merchants that display unit prices with higher precision, lite in the gas stations the litre price would be 1.895 Euros, for example. So I think that we need two settings for the decimals, a separate one for the precision of displayed prices. The requirement would be:
|
Beta Was this translation helpful? Give feedback.
-
@cruftex @RosaBenouamer The more I think about it, I think that the topic would not actually be THAT difficult.
|
Beta Was this translation helpful? Give feedback.
-
I quickly checked with this setting and a product with the price of 0.01 Euro. It behaves as I had expected. The invoice says: The tax section is omitted, because the tax total is 0. Tax excl. total and total is the same. This is because the tax on each item is less than 0.005 Euro. The "Round each item" is actually the worst and produces the most tax calculation errors. However, at least it gives consistent results and the tax total does not vary when you have 100 lines with one product or one line with 100 products. Fun fact: The PayPal module requires to set the rounding to "Round each item".
You always need to apply the tax on the total. |
Beta Was this translation helpful? Give feedback.
-
@cruftex could you please give an example with the two settings and with where each settings will influence the calculation ? Steps for calculation and invoice :-) Thank you ! |
Beta Was this translation helpful? Give feedback.
-
If you are available. I will send you my calendar to position a point :-) to test around this point first and point after point we could validate weeks after weeks :-) What do you think about this ? |
Beta Was this translation helpful? Give feedback.
-
I will be in Paris next week for the conference. Do you have time for 1-2 hours on Friday, 1st December? |
Beta Was this translation helpful? Give feedback.
-
@RosaBenouamer @cruftex any news on this issue? I have a client that has the same problems with tax calculations. Because he uses alot of % discounts, we use rounding on every item, then the total ex vat is corrext, but vat calculations seem to be based on not rounded prices and not on the total ex vat. Very problematic. |
Beta Was this translation helpful? Give feedback.
-
@wzzly AFIAK the idea is to improve things in PS 9. It will not be possible to do a fix for PS 8, because changes need to be quite fundamental for everything about prices, which is not possible to introduce without breaking existing interfaces. So this can only be done with a major release. I will ping Rosa. |
Beta Was this translation helpful? Give feedback.
-
@wzzly @cruftex Well, given that apparently Rosa is no longer on the subject, I don't think we can expect any changes from Prestashop SA side. 😄 |
Beta Was this translation helpful? Give feedback.
-
@cruftex @Hlavtox is it just me, or isn't it so strange that such huge bugs/issues are still in Prestashop? These are fundamental features for any webshop software right? How can any webshop platform have problems with calculating prices/taxes? The last couple of years I came a cross multiple basic features that are just not working and I wonder how it's possible these things don't get priority from prestashop SA instead of releasing new features... |
Beta Was this translation helpful? Give feedback.
-
@matks wrote:
Well, I did exactly that. I already outlined implementation steps. @matks wrote:
That is correct. I spend roughly 4000 Euros on the topic so far. @matks wrote:
Mind the contradiction: "hire more people", "cannot parallelize". I am an experienced software engineer, wrote a complete eCommerce shop by myself in the year 2000. That shop was specifically designed to sell theatre and concert tickets, so not a general shop like PS. Now, I do a startup selling travel stuff and I choose PS for several reasons. Our total shop revenue, at this point, is far less than the money I spend fixing bugs and sending PRs for PS. On the PS website, I can read:
This was appealing to me. So far, I did not see any developer involvement on the topic from the PS side. I don't see that my very fundamental business reality is respected. I did send money, by using some services PrestaShop SA is providing. I spend time worth a lot of money. I don't like being sold a "great car" and then asked for extra money to put in round tires instead of square ones.
Prices and dealing with money is a cross-cutting concern in PS. It is not a heart surgery. You need to replace it anyway. You cannot just analyse for years. At some point, you need to start hacking. You will break things. That is usual. When doing a new major version, it is okay to break with existing interfaces. While it is desirable to be compatible, I have the feeling that it is actually a good idea to intentionally break everything dealing with prices to ensure correctness. @matks Or shorter: Maybe I made the wrong choice. @matks |
Beta Was this translation helpful? Give feedback.
-
The problem
A collection of all problems related to price, tax and invoice calculations are in these EPICs and closely related to each other:
The solution
Because of its interrelations, I try to cover the whole area completely and in a bottom up manner. From database representations, calculations to the final invoiced price. This is meant to establish a bigger picture on the topic and help with coordinating improvements.
Disclaimer: I have yet to explore PS more and I did not see everything in the area of discounts, specific prices and currency conversion.
I found previous efforts here:
Updates
I try to update this text every now and then to make it more concise, add more details and incorporate feedback.
Structure
Each topic has the structure:
The expectations and solution ideas are my own, so, let me know when I am wrong or whether there is a more elegant solution.
Price Entry: Imprecision with VAT included prices
Currently, PrestaShop is storing product price as net price without VAT. The price including VAT is calculated from the net price plus the VAT rate. Since the database is storing the price with 6 digits precision, the VAT included price that the merchant has entered cannot exactly be reproduced.
Example:
I have a product that costs exactly 100 Euro, including 19% VAT. When I enter 100 Euro (8.0.4) and view the product list PS displays 100 Euro. When I edit the product, PS displays 99.999999. At some later point, the price also changed to 99.99, but unfortunately I cannot reproduce that. I assume that there was some additional floored rounding applied to the 99.999999 when the product was copied or edited.
Although PS is calculating with a price of 99.999999, the price of 100 Euro is shown.
Requirements / Expectations:
The best way is that prices are stored exactly as these were entered. If the user is entering a VAT inclusive price, this is
stored in the database and the VAT excluded price is empty and vice versa.
Alternatively, I can think of a workaround, and introduce a flag that determines what price the user actually entered was the inclusive or exclusive price. That flag can be introduced backwards compatible and allows to reproduce the price entered exactly, as long as the stored price format has more decimals than the price we want to show and calculate with. However, this is error prone and might lead to code that is using an incorrect price.
Float calculations
The database stores the price as decimal. Calculations in PHP are with floats.
The float format is not able to represent fractional decimal numbers correctly. Here is a good read: https://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency.
The float type has roughly a precision of 14 digits. The price has 6 fractional digits at the moment. With one digit for rounding, that leaves 7 digits to the left of the decimal point for calculations. We will hardly get product prices or orders beyond a Million. But with every calculation step the error adds up.
I recommend changing to decimal calculation (e.g. with BCMath). This also has the advantage that testing becomes easier since values must match exactly to a decimal value.
VAT setting
The VAT setting is currently at the level of the the customer group.
The invoice in 8.0.4 does show prices exclusive, while the credit slip is respecting the setting and does show prices including VAT if this is specified.
Requirements:
Correct calculation of order position totals
Example of current behaviour:
I set product tax inclusive price to 0.005 Euro. The shown product price is 0.01 Euro. When I order 10 products the total price is 0.05 Euro.
I set a product with tax inclusive price to 0.014 Euro. The shown product price is 0.01 Euro. When I order 10 products the total price is 0.14 Euro.
Requirement:
The position total needs to be calculated based on the advertised (shown) unit price to the user.
Precision
As shown above, the prices shown to the user, differ with the price that is used for calculations.
In the currency setup, the number of decimal digits is defined.
Requirements:
However, since the exclusive/inclusive VAT is per customer group, it makes sense to define it separately on the shop level depending on the VAT setting.
So the idea would be two settings per shop for precision:
The offer precision is used to round calculation results, if needed.
If unset, the currency precision is used. If offer and currency precision differ, the precision in the currency is used to round the invoice total
With multi store, one store could have the offer precision of two digits, while the other has the precision of three digits, allowing for a B2B shop with a "finer" pricing.
The merchant cannot enter a more precise price then the offer precision setting.
TODO:
More thoughts are needed on how this correlates with B2B/wholesale features.
Tax calculation
The tax calculation needs to be done with minimal error possible according to accounting standards.
Depending whether the prices are tax inclusive (B2C) or tax exclusive (B2B), either the tax including position totals
or tax excluding position totals are summed up to the final total.
More formal in pseudo code:
My recommendation: remove the tax_excl and tax_incl prices on the position level and likewise in the order details. There is only one price which needs to be displayed and calculated with on the level of a position. This price is either tax inclusive or tax exclusive.
The shipping costs need to be included as well in the total.
See:
TODO: ecotax?
https://github.com/PrestaShop/prestashop-specs/blob/master/content/1.7/broader-topics/ecotax.md
Discount
As said above, I did not have seen everything in PS regarding this.
There is no accounting requirement on how the discount is to be applied. This is up to the merchant or mutual agreement.
Requirements:
Rounding settings
PS has too different rounding settings. It is not documented where these settings are actually applied (tax, discounts, currency conversion, etc.).
Requirements:
I think it is best to remove the rounding settings altogether and use mercantile rounding if rounding is needed.
There might be still a wish for different rounding for certain purposes. Maybe for currency conversion?
This could be covered by a hook instead.
Obvious bugs
The above conceptual changes, should cover all issues that report unexpected calculation differences. Besides that, there are issues which also imply the solution already.
Compatibility
This might be realised for PS 9 or 10. The new major version would allow breaking changes.
One major step would be to change the representation of a price from float to a decimal format.
However, changing the representation of prices without backwards compatibility would break a lot of third party code.
Idea:
Be backwards compatible and use floats at interface boundaries for code that consumes prices, e.g. payment modules and templates. Don't be backwards compatible for code that is "hooking" into the price calculations.
TODO: think/check more about the hook interface changes
Implementation steps
The first steps could look like this:
Beta Was this translation helpful? Give feedback.
All reactions