Prior art:
I recently added some coupons to my app, and when using the PercentageDiscountHandler , I ran into problems when it came to the calculation of the order total as well as the invoice that was generated. It's easiest to explain with examples:
Order without coupon
Item subtotal: €150
Subtotal: €150
VAT 21%: €31.50
Total: €181.50
Order with 50% coupon (expectation)
Item subtotal: €150
Coupon: -€75
Subtotal: €75
VAT 21%: €15.75
Total: €90.75
Order with 50% coupon (actual, with no_tax = true)
Item subtotal: €150
Coupon: -€90.75
Subtotal: €59.25
VAT 21%: €31.50
Total: €90.75
In this example, we can see that the total is correct, but the subtotal and VAT calculation are incorrect. With no_tax set to true, the PercentageDiscountHandler is calculating the value of the coupon including VAT (90.75) and subtracting it from the subtotal excluding VAT (150). It then calculates the total VAT (31.50) not by using the resulting [incorrect] subtotal (59.25), but by using the item subtotal (150). This results in the correct total, but we'd then need to pay 34.7% VAT instead of 21%.
Order with 50% coupon (actual, with no_tax = false)
Item subtotal: €150
Coupon: -€90.75
Subtotal: €59.25
VAT 21%: €12.44
Total: €71.69
In this example, the total and the VAT calculation are incorrect. Like in the previous example, the value of the coupon includes VAT (90.75) and is being subtracted from the item subtotal excluding VAT (150), resulting in the wrong overall subtotal (59.25). However, with no_tax set to false, it does properly use the resulting subtotal (59.25) to calculate the VAT, which ends up being 12.44. However, because the calculated subtotal was incorrect, the VAT is also incorrect.
I don't understand the logic behind either of these behaviors, and I can't imagine that they're working as intended. For now, I've created a custom PercentageDiscountHandler which undoes the addition of VAT to the coupon:
class PercentageDiscountHandler extends CashierPercentageDiscountHandler
{
protected function unitPrice(Money $base)
{
return parent::unitPrice($base)->divide('1.21');
}
}
However, this feels a bit hacky, and I would prefer if the library was fixed. I know it's a breaking change, so perhaps it could be done for the next major version or something. At the very least, there should be an option that we can set to calculate the coupon value correctly. There was a PR to address this years ago (linked above), but it went ignored.
Out of curiosity, is this actually working as intended? If so, what's the logic? I can't imagine that this is the behavior that most people want or expect, given that they would result in either reporting a higher percentage of VAT to the government or increasing the value of coupons (thus decreasing revenue).
It's worth noting that the FixedDiscountHandler does not behave like this. With no_tax set to false, everything is calculated correctly.
Prior art:
I recently added some coupons to my app, and when using the
PercentageDiscountHandler, I ran into problems when it came to the calculation of the order total as well as the invoice that was generated. It's easiest to explain with examples:Order without coupon
Item subtotal: €150
Subtotal: €150
VAT 21%: €31.50
Total: €181.50
Order with 50% coupon (expectation)
Item subtotal: €150
Coupon: -€75
Subtotal: €75
VAT 21%: €15.75
Total: €90.75
Order with 50% coupon (actual, with
no_tax = true)Item subtotal: €150
Coupon: -€90.75
Subtotal: €59.25
VAT 21%: €31.50
Total: €90.75
In this example, we can see that the total is correct, but the subtotal and VAT calculation are incorrect. With
no_taxset totrue, thePercentageDiscountHandleris calculating the value of the coupon including VAT (90.75) and subtracting it from the subtotal excluding VAT (150). It then calculates the total VAT (31.50) not by using the resulting [incorrect] subtotal (59.25), but by using the item subtotal (150). This results in the correct total, but we'd then need to pay 34.7% VAT instead of 21%.Order with 50% coupon (actual, with
no_tax = false)Item subtotal: €150
Coupon: -€90.75
Subtotal: €59.25
VAT 21%: €12.44
Total: €71.69
In this example, the total and the VAT calculation are incorrect. Like in the previous example, the value of the coupon includes VAT (90.75) and is being subtracted from the item subtotal excluding VAT (150), resulting in the wrong overall subtotal (59.25). However, with
no_taxset tofalse, it does properly use the resulting subtotal (59.25) to calculate the VAT, which ends up being 12.44. However, because the calculated subtotal was incorrect, the VAT is also incorrect.I don't understand the logic behind either of these behaviors, and I can't imagine that they're working as intended. For now, I've created a custom
PercentageDiscountHandlerwhich undoes the addition of VAT to the coupon:However, this feels a bit hacky, and I would prefer if the library was fixed. I know it's a breaking change, so perhaps it could be done for the next major version or something. At the very least, there should be an option that we can set to calculate the coupon value correctly. There was a PR to address this years ago (linked above), but it went ignored.
Out of curiosity, is this actually working as intended? If so, what's the logic? I can't imagine that this is the behavior that most people want or expect, given that they would result in either reporting a higher percentage of VAT to the government or increasing the value of coupons (thus decreasing revenue).
It's worth noting that the
FixedDiscountHandlerdoes not behave like this. Withno_taxset tofalse, everything is calculated correctly.