Post by Qphoria » Thu Feb 23, 2012 12:41 am

Ah yea.. i see.. it can be more than 1 cent.

The issue is like you said.. the 4 decimal point rounding in the pre-conversion math.. or the lack there of.

Perhaps the simple fix is that when totals are being summed, they need to be rounded to 2 first.
So all order totals will need to be adjusted to change (for example) :

Code: Select all

$total += $value;
to

Code: Select all

$total += round($value, 2);
etc

Gonna try that theory now

Image


User avatar
Administrator

Posts

Joined
Tue Jul 22, 2008 3:02 am

Post by dizarter » Thu Feb 23, 2012 1:14 am

Q, since you are using round() with no 3rd param, it defaults to PHP_ROUND_HALF_UP, meaning
1.000
1.001
1.002
1.003
1.004

rounds down to 1.00

1.005
1.006
1.007
1.008
1.009

rounds up to 1.01

Because of this, difference can never be more then 1 (either + or -).

public function format already does rounding to whatever is set in currency settings for the chosen currency.

Because all model order totals first do the calculation, then round everything up when they format the results, my theory is that you should do the same, first calculate everything, without any rounding, then round when you are to submit to paypal (that is, round when you format the reuslt)

New member

Posts

Joined
Tue Oct 11, 2011 7:33 am

Post by Qphoria » Thu Feb 23, 2012 3:14 am

dizarter wrote:Q, since you are using round() with no 3rd param, it defaults to PHP_ROUND_HALF_UP, meaning
1.000
1.001
1.002
1.003
1.004

rounds down to 1.00

1.005
1.006
1.007
1.008
1.009

rounds up to 1.01

Because of this, difference can never be more then 1 (either + or -).

public function format already does rounding to whatever is set in currency settings for the chosen currency.

Because all model order totals first do the calculation, then round everything up when they format the results, my theory is that you should do the same, first calculate everything, without any rounding, then round when you are to submit to paypal (that is, round when you format the reuslt)
Well of course. But you answered your own point, since I'm not passing "PHP_ROUND_HALF_DOWN" in any case.. it is already rounding up for everything. And those constants weren't added until php 5.3 so they wouldn't work on 5.2 which is where many people still are. But I don't think that is the area of the problem.

The exchange rate is the main source of the varied ranges.

Back to the calculation example:
Product 1: (19.99 * 0.637532) = 12.74
Product 2: (27.99 * 0.637532) = 17.84
Product 3: (32.49 * 0.637532) = 20.71

Subtotal: (80.47 * 0.637532) = 51.30

Paypal does its own math on the post converted prices:
12.74 + 17.84 + 20.71 = 51.29
which is 1 cent less.
In reality, the opencart prices are
Product 1: (19.99 * 0.637532) = 12.74426468
Product 2: (27.99 * 0.637532) = 17.84452068
Product 3: (32.49 * 0.637532) = 20.71341468

Subtotal: (80.47 * 0.637532) = 51.30220004

Then we round to the currency's decimal point of 2 which causes the math breakage. Maybe because we use the "currency::format()" function as a lazy man's way to convert AND format. We should just be converting the full float and using that to generate the values for paypal. However, we still need to round at some point as paypal only allows 2 digit prices. It's like we need the "total" order total part to use the post-converted totals for its value, instead of converting its own pre-conversion total.

Currently Total does:
$total = round((19.99 + 27.99 + 32.49) * 0.637532), 2) = 51.30

But maybe needs to do:
$total = ( round((19.99 * 0.637532), 2) + round((27.99 * 0.637532), 2) + round((32.49 * 0.637532), 2) ) = 51.29

It comes down to rounding before math or rounding after math...
But that could break a lot more. This is a bastard.

Image


User avatar
Administrator

Posts

Joined
Tue Jul 22, 2008 3:02 am

Post by dizarter » Thu Feb 23, 2012 4:00 am

I am only poitning the math behind this, difference can never be more then +/-1, and that is correct.
I mentioned PHP_ROUND_HALF_UP because that is not the standard way to round floats in math, other rules are used in everyday math, but as you say, that is not the source of the problem.

Shop I'm working on uses only USD (with value of 1.0000...) - same thing happens. And I have only noticed this when markman-b posted about it.

OC math and PP math are both correct, with the difference that PP calculates with 2 decimal points while OC calculates internally with 4 decimal points and rounds up for display at the end using format method.

New member

Posts

Joined
Tue Oct 11, 2011 7:33 am

Post by Qphoria » Thu Feb 23, 2012 5:17 am

dizarter wrote:I am only poitning the math behind this, difference can never be more then +/-1, and that is correct.
I mentioned PHP_ROUND_HALF_UP because that is not the standard way to round floats in math, other rules are used in everyday math, but as you say, that is not the source of the problem.

Shop I'm working on uses only USD (with value of 1.0000...) - same thing happens. And I have only noticed this when markman-b posted about it.

OC math and PP math are both correct, with the difference that PP calculates with 2 decimal points while OC calculates internally with 4 decimal points and rounds up for display at the end using format method.
Its 1 cent per few hundred dollars.. add 500 items to the cart and it goes off by about 62 cents. I never tested that high until markman mentioned it so I also thought it was a 1 cent only thing.. but when you factor qty into the price, it starts to add up.

Image


User avatar
Administrator

Posts

Joined
Tue Jul 22, 2008 3:02 am

Post by markman-b » Thu Feb 23, 2012 5:48 am

Finally we are on the same frequency!

OC version 1.5.4.1


Active Member

Posts

Joined
Wed Aug 24, 2011 7:13 pm

Post by dizarter » Thu Feb 23, 2012 5:59 am

This bug is in OC itself.

Set test product with price 13.9840.

OC rounds it's price to 13.98 when displaying.

When calculating price for, say 3 products, it uses price with 4 digits, not the rounded price, so
13.9840 * 3 = 41.9520 which is then rounded to 41.95.

However, it clearly does not make sense to use 41.95 when displaying product price in item total column because

13.98 * 3 = 41.94.

PP module sends the correct price of 41.94.

We should raise this issue higher, because per product total calculation obviosly needs correction - all calculation throughout the shop should be based on how many decimal digits are displayed (if prices are formated using 2 digits, use 2 digits for calculation everywherec)

New member

Posts

Joined
Tue Oct 11, 2011 7:33 am

Post by dizarter » Thu Feb 23, 2012 6:53 am

Mind my last post, I usually talk gibbersih this late without my daily dosage of beer.
Let me try to rethink this now with a can of beer on my side :)

This is the debug when setting USD currency to 3 decimal places

SHIPPINGAMT=0.000
TAXAMT=0.000
ITEMAMT=41.95
AMT=41.952

New member

Posts

Joined
Tue Oct 11, 2011 7:33 am

Post by dizarter » Thu Feb 23, 2012 7:18 am

Ok, I think I figured the biatch.

This is my testing scenario

Product price is 13.984. Currency is set to be displayed with 3 decimal digits.

Product price shows as 13.984, I add 3 products and have
13.984 * 3 = 41.952 in product row
Sub-total 41.952
Total 41.952

With original code, I get PP errors about invalid amounts.

After rounding L_AMT0, SHIPPINGAMT, TAXAMT, AMT to 2 digits, I get to PP login page.

Totals shown there are off by 0.012, as expected
pptotal.jpg

pptotal.jpg (21.85 KiB) Viewed 11523 times

So, it's now completely obvious - if we use more then 2 decimal places for prices, this is inevitable, because there will always be this error in rounding.

The only solution is to remember the error after rounding and then correct the final price using shipping or tax or whatever.

Example,

13.984 rounds to 13.98, we remember +0.004 (+ is indicating we have to add to final price because there are situations when it will round to higher number (13.987 will round to 13.99), and then we have to remember -0.003 and deduct it later in order not to rip off customer).

Then we have 13.98 * 3 = 41.94 as the item total and 3 * +0.004 = +0.012 remainder

We then round the remainder to two digits so +0.012 becomes +0.01.

Finally we add 0.01 as shipping or tax for correction, thus total will be 41.94 + 0.01 = 41.95.

There is still an error when rouding remainder, but that error is limited to 10th parts of a cent, which is tolerable.

More beer :)

New member

Posts

Joined
Tue Oct 11, 2011 7:33 am

Post by dizarter » Thu Feb 23, 2012 8:52 am

And here is a quick example of what I am gibbering about

Code: Select all

<?php
$price = 13.1154;
$number_of_items = 23;

$total_1 = round($price * $number_of_items, 4);

$total_2 = round($price, 2) * $number_of_items;

$diff = round($total_1 - $total_2, 2);

echo "Item price: <b>" . $price . "</b> (this will be displayed as " . round($price, 2) .")<br />";
echo "# of items: <b>$number_of_items</b><br /><br />";

echo "OC Total: <b>$total_1</b>" . " <=> round(" . $price . " * " . $number_of_items . ", 4)" . " (this will be displayed as " . round($total_1, 2) ." because OC rounds product price for display, but it computes with 4 digit price)<br /><br />";

echo "PP Total: <b>" . $total_2 . "</b> <=> round(" . $price . ", 2) * " . $number_of_items . " = " . round($price, 2) . " * " . $number_of_items . " (PP price has to be rounded before sending and we have to use that price when calculating total for amounts to match)<br /><br />";

echo "Diff: <b>" . $diff . '</b> (the difference between OC and PP price also needs to be rounded so we can include it as a tax or discount)'; echo "<br /><br />";

echo "Corrected PP Total: <b>" . ($diff + $total_2) . '</b> (final price to be sent to PP with difference applied)'; echo "<br />";

?>
These are a few examples, just as a referrence

Code: Select all

Item price: 13.1154 (this will be displayed as 13.12)
# of items: 23

OC Total: 301.6542 <=> round(13.1154 * 23, 4) (this will be displayed as 301.65 because OC rounds product price for display, but it computes with 4 digit price)

PP Total: 301.76 <=> round(13.1154, 2) * 23 = 13.12 * 23 (PP price has to be rounded before sending and we have to use that price when calculating total for amounts to match)

Diff: -0.11 (the difference between OC and PP price also needs to be rounded so we can include it as a tax or discount)

Corrected PP Total: 301.65 (final price to be sent to PP with difference applied)

Code: Select all

Item price: 13.4949 (this will be displayed as 13.49)
# of items: 100

OC Total: 1349.49 <=> round(13.4949 * 100, 4) (this will be displayed as 1349.49 because OC rounds product price for display, but it computes with 4 digit price)

PP Total: 1349 <=> round(13.4949, 2) * 100 = 13.49 * 100 (PP price has to be rounded before sending and we have to use that price when calculating total for amounts to match)

Diff: 0.49 (the difference between OC and PP price also needs to be rounded so we can include it as a tax or discount)

Corrected PP Total: 1349.49 (final price to be sent to PP with difference applied)

Code: Select all

Item price: 13.4951 (this will be displayed as 13.5)
# of items: 100

OC Total: 1349.51 <=> round(13.4951 * 100, 4) (this will be displayed as 1349.51 because OC rounds product price for display, but it computes with 4 digit price)

PP Total: 1350 <=> round(13.4951, 2) * 100 = 13.5 * 100 (PP price has to be rounded before sending and we have to use that price when calculating total for amounts to match)

Diff: -0.49 (the difference between OC and PP price also needs to be rounded so we can include it as a tax or discount)

Corrected PP Total: 1349.51 (final price to be sent to PP with difference applied)
Now, if someone would be kind enough to do the dirty work ( ::) dizarter friendly points to Q)

New member

Posts

Joined
Tue Oct 11, 2011 7:33 am

Post by markman-b » Sat Mar 03, 2012 6:43 pm

Have the PP rounding issues been solved in OC 1.5.2?

I've noticed that the field "PDT Token", has been removed in PP Standard for OC 1.5.2.
Is this setting not needed anymore????

OC version 1.5.4.1


Active Member

Posts

Joined
Wed Aug 24, 2011 7:13 pm

Post by Qphoria » Sun Mar 04, 2012 5:28 am

markman-b wrote:Have the PP rounding issues been solved in OC 1.5.2?
No it hasn't been resolved
I've noticed that the field "PDT Token", has been removed in PP Standard for OC 1.5.2.
Is this setting not needed anymore????
I guess daniel is keeping it simple. PDT support added too much clutter.
IPN should be enough

Image


User avatar
Administrator

Posts

Joined
Tue Jul 22, 2008 3:02 am

Post by markman-b » Sun Mar 11, 2012 10:06 pm

I know you have been working on the new OC 1.5.2 lately. But I hope you will pay some attention to this topic again.

In the Netherlands, the VAT value is 19%. If we divide number 19 by 5, than The outcome is 3.800.

The only solution, to avoid the rounding issues between OC and PP, is by using prices ex VAT, which are the multiple of 0.19 by a factor 5. If so, the VAT amount only contains 2 decimals.

Example 1
5 x 0.19= 0.9500 VAT
Since we know the VAT, we can calculate the price ex VAT this way:
(0.95/19) * 100 = 5,00
Price inc VAT: 5.00 + 0.95 = 5.95

Example 2
25 x 0.19 = 4.7500 VAT
(4.75/19) * 100 = 25.00
Price inc VAT = 25.00 + 4.75 = 29.75

You can imagine hopefully, that I'm absolute unhappy with this.
1. I'm very restricted in using product prices. In fact, each price step is 5,95 higher. (5.95, 11.90, 17.85, 23.80, 29.75 etc)
2. It is not a good solution. When a coupon code is being applied, the problem returns.
(Order Totals: sorting order discount is 2, VAT is 3)

There is a VQMod fix to avoid the rounding issues. http://forum.opencart.com/viewtopic.php?f=113&t=36916
The mod passes only the cart total to PayPal. But I'm convinced that using this mod, will result in abandoned orders, since customers can''t see the content of their shopping cart on PayPal.

Please let me know if you can fix this.

OC version 1.5.4.1


Active Member

Posts

Joined
Wed Aug 24, 2011 7:13 pm

Post by markman-b » Mon Mar 12, 2012 7:34 pm

I need to make a correction on my previous post. It is not mandatory to use prices ex VAT, that are multiple of 0.19 by a factor 5, to avoid rounding issues, The prices have to be round, like: € 1.00, € 2.00 etc.

Unfortunately this results in rather uncommon prices inc 19% VAT, like € 2.38, € 3.57 etc.
The image will make it more clear hopefully. As you can see, prices ex VAT like € 5.00, € 10.00 etc (price step is € 5,00), result in more common store prices inc 19% VAT: € 5.95, € 11.90 etc

Image

OC version 1.5.4.1


Active Member

Posts

Joined
Wed Aug 24, 2011 7:13 pm

Post by markman-b » Tue Mar 20, 2012 5:06 pm

Hi Simon,

Can you tell me please if you are willing to resolve this issue? It's pretty quiet since 2 weeks :(

OC version 1.5.4.1


Active Member

Posts

Joined
Wed Aug 24, 2011 7:13 pm

Post by Qphoria » Tue Mar 20, 2012 11:52 pm

who is simon? This will be revisited

Image


User avatar
Administrator

Posts

Joined
Tue Jul 22, 2008 3:02 am

Post by markman-b » Wed Mar 21, 2012 12:09 am

Simon...from Duran Duran hahaha.

I thought your name was Simon. My apologies for the mistake.

(uksitebuilder = Simon...)

OC version 1.5.4.1


Active Member

Posts

Joined
Wed Aug 24, 2011 7:13 pm

Post by obiwahn » Mon Jul 09, 2012 7:59 pm

Hi there,

is this issue solved by now?

And if yes, how?

Cheers
Obi

Newbie

Posts

Joined
Wed Nov 16, 2011 1:45 am

Post by markman-b » Mon Jul 09, 2012 8:17 pm

No! Still not >:(

OC version 1.5.4.1


Active Member

Posts

Joined
Wed Aug 24, 2011 7:13 pm

Post by Qphoria » Tue Jul 10, 2012 12:04 pm

I think daniel will revisit this in 1.5.4

Image


User avatar
Administrator

Posts

Joined
Tue Jul 22, 2008 3:02 am
Who is online

Users browsing this forum: Bing [Bot] and 28 guests