Skip to content

Commit df03771

Browse files
committed
[FIX] sale_loyalty: keep track of coupon changes on confirmed orders
Versions -------- - 17.0+ Steps ----- 1. Have a coupon program; 2. add a 10% discount on order reward for 1 point; 3. add a 50% discount on order reward for 5 points; 4. generate a coupon with 10 points; 5. use coupon code on a confirmed order; 6. select 10% discount reward; 7. change to a 50% discount reward; 8. check coupon point total. Issue ----- Even though the 5 point reward was used, only 4 out of 10 points remain. Cause ----- When updating the reward line of a confirmed order, it keeps track of point cost changes before & after a write. Its purpose is to restore back the point difference on the coupon record. The issue is that while point changes are stored, coupon changes are not. When updating reward lines, `_reset_loyalty` is used, which removes the `coupon_id` from the lines. As a consequence, attempting to restore the point difference on `line.coupon_id` after an update, it writes to an empty record. Solution -------- Store both coupons & their used points before write. After write, restore the previous points to the previous coupon, and subtract the current point cost from the current coupon. This way, any combination of coupon/point changes should have the points updated as expected. opw-4910922 closes odoo#231120 X-original-commit: ee7abc9 Signed-off-by: Levi Siuzdak <[email protected]> Signed-off-by: Valeriya Chuprina (vchu) <[email protected]>
1 parent bded695 commit df03771

File tree

2 files changed

+50
-4
lines changed

2 files changed

+50
-4
lines changed

addons/sale_loyalty/models/sale_order_line.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,16 @@ def create(self, vals_list):
8787
def write(self, vals):
8888
cost_in_vals = 'points_cost' in vals
8989
if cost_in_vals:
90-
previous_cost = {l: l.points_cost for l in self}
90+
previous_vals = {line: (line.points_cost, line.coupon_id) for line in self}
9191
res = super().write(vals)
9292
if cost_in_vals:
9393
# Update our coupon points if the order is in a confirmed state
94-
for line in self:
95-
if previous_cost[line] != line.points_cost and line.state == 'sale':
96-
line.coupon_id.points += (previous_cost[line] - line.points_cost)
94+
for line, (previous_cost, previous_coupon) in previous_vals.items():
95+
if line.state != 'sale':
96+
continue
97+
if line.points_cost != previous_cost or line.coupon_id != previous_coupon:
98+
previous_coupon.points += previous_cost
99+
line.coupon_id.points -= line.points_cost
97100
return res
98101

99102
def unlink(self):

addons/sale_loyalty/tests/test_program_with_code_operations.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,49 @@ def test_public_partner_updated_in_next_order_coupon(self):
373373
"The coupon's partner_id should be updated if it was created for a Public User",
374374
)
375375

376+
def test_change_reward_on_confirmed_order(self):
377+
"""Check that changing rewards on a confirmed order restores points on the coupon.
378+
Tested flow:
379+
- have a coupon program with 2 discount rewards;
380+
- have confirmed order;
381+
- apply a 10% discount reward, costing 1 point;
382+
- change to a 50% discount reward, costing 5 points;
383+
- check that there are still 5 points left on the coupon.
384+
"""
385+
program = self.code_promotion_program_with_discount
386+
program.update({
387+
'rule_ids': [Command.clear()],
388+
'reward_ids': [Command.create({
389+
'discount': 50,
390+
'discount_mode': 'percent',
391+
'discount_applicability': 'order',
392+
'required_points': 5,
393+
})],
394+
})
395+
discount10, discount50 = program.reward_ids
396+
397+
self.env['loyalty.generate.wizard'].with_context(active_id=program.id).create({
398+
'coupon_qty': 1,
399+
'points_granted': 10,
400+
}).generate_coupons()
401+
coupon = program.coupon_ids
402+
403+
order = self.empty_order
404+
order.order_line = [Command.create({'product_id': self.product_C.id})]
405+
order.action_confirm()
406+
407+
order.order_line.product_updatable = True # in case `sale_project` is installed
408+
order._apply_program_reward(discount10, coupon)
409+
reward_line = order.order_line.filtered('is_reward_line')
410+
self.assertEqual(order.amount_total, 90, "10% discount should be applied")
411+
self.assertEqual(coupon.points, 9, "10% discount reward should use 1 point")
412+
413+
order.order_line.product_updatable = True # in case `sale_project` is installed
414+
order._apply_program_reward(discount50, coupon)
415+
self.assertIn(reward_line, order.order_line, "Reward line should be re-used")
416+
self.assertEqual(order.amount_total, 50, "50% discount should be applied")
417+
self.assertEqual(coupon.points, 5, "50% discount reward should use 5 points")
418+
376419
def test_edit_and_reapply_promotion_program(self):
377420
# The flow:
378421
# 1. Create a program auto applied, giving a fixed amount discount

0 commit comments

Comments
 (0)