[FIX] sale_loyalty: wrong discount amount computation with negative amount product

Currently, when using a promotion with a reward available only on specific products and having a
maximum amount allowed, when adding another product with a negative amount, the discount could go
above the maximum discount allowed.

This was caused by the calculated discount amount that was overridden by the total amount of the
order. This new amount was then used to calculate the discounted factor. So, instead of having:
discount_amount = product_price * min(1, (max_discount / discountable))
When the total_amount of the order was under this discountable, we had:
discount_amount = product_price * min(1, (max_discount / total_amount))

Instead, we should calculate the discount amount normally but limit it to the amount_total.

opw-3217369

closes odoo/odoo#123361

X-original-commit: 977f726b67f48d4b858b1a5fe1d558a64186d332
Signed-off-by: Victor Feyens (vfe) <vfe@odoo.com>
Signed-off-by: Morgane Demesmaeker <edm@odoo.com>
This commit is contained in:
Demesmaeker
2023-06-02 17:00:18 +02:00
parent 6b10a5430d
commit 64937078cd
2 changed files with 69 additions and 2 deletions
+2 -2
View File
@@ -301,8 +301,6 @@ class SaleOrder(models.Model):
discountable, discountable_per_tax = self._discountable_specific(reward)
elif reward_applies_on == 'cheapest':
discountable, discountable_per_tax = self._discountable_cheapest(reward)
# Discountable should never surpass the order's current total amount
discountable = min(self.amount_total, discountable)
if not discountable:
if not reward.program_id.is_payment_program and any(line.reward_id.program_id.is_payment_program for line in self.order_line):
return [{
@@ -320,6 +318,8 @@ class SaleOrder(models.Model):
}]
raise UserError(_('There is nothing to discount'))
max_discount = reward.currency_id._convert(reward.discount_max_amount, self.currency_id, self.company_id, fields.Date.today()) or float('inf')
# discount should never surpass the order's current total amount
max_discount = min(self.amount_total, max_discount)
if reward.discount_mode == 'per_point':
max_discount = min(max_discount,
reward.currency_id._convert(reward.discount * self._get_real_points_for_coupon(coupon),
+67
View File
@@ -222,6 +222,73 @@ class TestLoyalty(TestSaleCouponCommon):
self.assertEqual(order.amount_tax, 15.0)
self.assertEqual(order.reward_amount, -215.0)
def test_discount_max_amount_on_specific_product(self):
product_a = self.product_A
product_b = self.product_B
product_a.write({'taxes_id': [Command.set(self.tax_20pc_excl.ids)]})
product_b.write({'list_price': -20, 'taxes_id': [Command.set(self.tax_20pc_excl.ids)]})
self.env['loyalty.program'].search([]).write({'active': False})
promotion = self.env['loyalty.program'].create({
'name': '10% Discount',
'program_type': 'promotion',
'trigger': 'auto',
'rule_ids': [Command.create({'reward_point_amount': 1, 'reward_point_mode': 'unit'})],
'reward_ids': [Command.create({
'discount': 10.0,
'discount_max_amount': 9,
'discount_applicability': 'specific',
'discount_product_ids': [product_a.id],
})],
})
order = self.env['sale.order'].create({
'partner_id': self.partner_a.id,
'order_line': [Command.create({'product_id': product_a.id})],
})
self.assertEqual(order.reward_amount, 0)
self._auto_rewards(order, promotion)
reward_amount_tax_included = sum(l.price_total for l in order.order_line if l.reward_id)
msg = "Max discount amount reached, the reward amount should be the max amount value."
self.assertEqual(reward_amount_tax_included, -9, msg)
order.order_line = [Command.clear(), Command.create({'product_id': product_b.id})]
self._auto_rewards(order, promotion)
reward_amount_tax_included = sum(l.price_total for l in order.order_line if l.reward_id)
msg = "This product is not eligible to the discount."
self.assertEqual(reward_amount_tax_included, 0, msg=msg)
order.order_line = [
Command.clear(),
Command.create({'product_id': product_a.id}), # price_total = 120
Command.create({'product_id': product_b.id}), # price_total = -20
]
self._auto_rewards(order, promotion)
reward_amount_tax_included = sum(l.price_total for l in order.order_line if l.reward_id)
msg = "Reward amount above the max amount, the reward should be the max amount value."
self.assertEqual(reward_amount_tax_included, -9, msg)
order.order_line = [
Command.clear(),
Command.create({'product_id': product_a.id}), # price_total = 120
Command.create({'product_id': product_b.id, 'price_unit': -95}), # price_total = -114
]
self._auto_rewards(order, promotion)
reward_amount_tax_included = sum(l.price_total for l in order.order_line if l.reward_id)
msg = "Reward amount should never surpass the order's current total amount."
self.assertEqual(reward_amount_tax_included, -6, msg)
order.order_line = [
Command.clear(),
Command.create({'product_id': product_a.id, 'price_unit': 50}), # price_total = 60
Command.create({'product_id': product_b.id, 'price_unit': -5}), # price_total = -6
]
self._auto_rewards(order, promotion)
reward_amount_tax_included = sum(l.price_total for l in order.order_line if l.reward_id)
msg = "Reward amount should be the percentage one if under the max amount discount."
self.assertEqual(reward_amount_tax_included, -6, msg)
def test_multiple_discount_specific(self):
"""
Check the discount calculation if it is based on the remaining amount