Skip to content

Commit 1cece3d

Browse files
committed
[FIX] pos_loyalty: fix corrupted product specific discount
Steps to reproduce ------------------ - Create a product with price $80 - Create a fixed discount reward for this product with $40 - In PoS config, enable global discount - In a new PoS order, add the product created in the first step and observe that we now have 2 orderlines, the product at $80 and the discount at $40. Total amount remaining to pay is $40 - Add a global discount of 50% -> At this point, we are expecting to apply the 50% discount on the remaining $40, and hence, a discount of $20, which changes the order's total price to $20. We also expect the fixed discount line of $40 to remain at $40, as it's a fixed price discount. Indeed, we now see a $20 global discount as an orderline, but the $40 fixed discount has changed its value from $40 to $53.33, and thus, the total amount has decreased to $6.67 instead of $20 -> A corrupted final amount!! In some other pricing configs, this final total amount will even become negative. Reason ------ After applying the global discount, we recalculate the value of the discounts to keep everything in sync, so we recompute the discount value of the fixed $40 discount in `_getRewardLineValuesDiscount`, and since now the total amount to pay, i.e. `order.get_total_with_tax()` is less then the value of the product (due to the global discount), `discountFactor` [1] of the fixed discount increases since `discountable` decreases. However, at the end when we calculate the new `unit_price` of this fixed discount, we multiply the `discountFactor` by the intial product price, i.e. by the price before the global discount, and hence, the amount of the fixed tax will be higher than expected, causing this buggy behavior. Below is a math representation of the bad (current) vs good (new) behavior: Bad: discountable = $60 = $80 - $20, where $20 is (total - fixed) * discount, i.e. (80-40)*0.5 discountFactor = $40 / $60 = 0.666666 price_unit = 0.666666 * $80 = $53.3333 (instead of $40) Good should be: discountable = $60 = $80 - $20, where $20 is (total - fixed) * discount, i.e. (80-40)*0.5 discountFactor = $40 / $60 = 0.666666 price_unit = 0.666666 * $60 = $40 I.e the good behavior should be multiplying the `discountFactor` by the the min of the remaining total price (after applying the global discount, so $60), and the initial value of the product (here $80). The formula has been updated accordingly. [1]: https://github.com/odoo/odoo/blob/76e2aa6b70d1fb03b8ce2f8e4a7e9c3d9425270f/addons/pos_loyalty/static/src/overrides/models/pos_order.js#L1163 opw-4622428 closes odoo#206752 X-original-commit: 86bd047 Signed-off-by: Adrien Guilliams (adgu) <[email protected]> Signed-off-by: Hadi El Yakhni (hael) <[email protected]>
1 parent 3eb7fe7 commit 1cece3d

File tree

3 files changed

+62
-1
lines changed

3 files changed

+62
-1
lines changed

addons/pos_loyalty/static/src/app/models/pos_order.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1170,7 +1170,7 @@ patch(PosOrder.prototype, {
11701170

11711171
lst.push({
11721172
product_id: discountProduct,
1173-
price_unit: -(entry[1] * discountFactor),
1173+
price_unit: -(Math.min(this.getTotalWithTax(), entry[1]) * discountFactor),
11741174
qty: 1,
11751175
reward_id: reward,
11761176
is_reward_line: true,

addons/pos_loyalty/static/tests/tours/pos_loyalty_tour.js

+14
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,20 @@ registry.category("web_tour.tours").add("PosLoyalty2DiscountsSpecificGlobal", {
514514
].flat(),
515515
});
516516

517+
registry.category("web_tour.tours").add("PosLoyaltySpecificProductDiscountWithGlobalDiscount", {
518+
steps: () =>
519+
[
520+
Chrome.startPoS(),
521+
Dialog.confirm("Open Register"),
522+
ProductScreen.addOrderline("Product A", "1"),
523+
PosLoyalty.hasRewardLine("$ 40 on Product A", "-40.00"),
524+
PosLoyalty.clickDiscountButton(),
525+
Dialog.confirm(),
526+
PosLoyalty.hasRewardLine("$ 40 on Product A", "-40.00"),
527+
PosLoyalty.orderTotalIs("20.00"),
528+
].flat(),
529+
});
530+
517531
registry.category("web_tour.tours").add("PosRewardProductScan", {
518532
steps: () =>
519533
[

addons/pos_loyalty/tests/test_frontend.py

+47
Original file line numberDiff line numberDiff line change
@@ -1080,6 +1080,53 @@ def test_2_discounts_specific_global(self):
10801080
self.main_pos_config.open_ui()
10811081
self.start_pos_tour("PosLoyalty2DiscountsSpecificGlobal")
10821082

1083+
def test_specific_product_discount_with_global_discount(self):
1084+
self.env['loyalty.program'].search([]).write({'active': False})
1085+
1086+
self.discount_product = self.env["product.product"].create({
1087+
"name": "Discount Product",
1088+
"type": "service",
1089+
"list_price": 0,
1090+
"available_in_pos": True,
1091+
})
1092+
1093+
self.main_pos_config2 = self.main_pos_config.copy({
1094+
'module_pos_discount': True,
1095+
'discount_product_id': self.discount_product.id,
1096+
'discount_pc': 50,
1097+
})
1098+
1099+
self.product_a = self.env['product.product'].create({
1100+
'name': "Product A",
1101+
'is_storable': True,
1102+
'list_price': 80,
1103+
'available_in_pos': True,
1104+
'taxes_id': False,
1105+
})
1106+
1107+
self.env['loyalty.program'].create({
1108+
'name': 'Discount on Specific Products',
1109+
'program_type': 'promotion',
1110+
'trigger': 'auto',
1111+
'applies_on': 'current',
1112+
'rule_ids': [(0, 0, {
1113+
'reward_point_mode': 'order',
1114+
'minimum_qty': 0
1115+
})],
1116+
'reward_ids': [(0, 0, {
1117+
'reward_type': 'discount',
1118+
'required_points': 1,
1119+
'discount': 40,
1120+
'discount_mode': 'per_order',
1121+
'discount_applicability': 'specific',
1122+
'discount_product_ids': [Command.set(self.product_a.ids)],
1123+
})],
1124+
'pos_config_ids': [Command.link(self.main_pos_config2.id)],
1125+
})
1126+
1127+
self.main_pos_config2.with_user(self.pos_user).open_ui()
1128+
self.start_pos_tour("PosLoyaltySpecificProductDiscountWithGlobalDiscount", pos_config=self.main_pos_config2)
1129+
10831130
def test_point_per_money_spent(self):
10841131
"""Test the point per $ spent feature"""
10851132
LoyaltyProgram = self.env['loyalty.program']

0 commit comments

Comments
 (0)