[FIX] sale_loyalty: skip paid calc if ewallet type
**Current behavior:**
Creating an ewallet loyalty program and removing the default
value for `trigger_product_ids` without adding another in its
place will result in any ewallet created using that program to
not have its balance deducted after being used in a sale order.
**Expected behavior:**
The default product should not be required here, nor should any
product, seeing as there is no `required` constraint on this
field nor any related ones.
**Steps to reproduce:**
1. Create an ewallet loyalty program and remove the Top-up
ewallet product
2. Create an ewallet, give it some balance, and use it in an
order
3. Check the balance of the wallet to see the erroneous behavior
**Cause of the issue:**
In the _program_check_compute_points() method of `sale.order` in
`sale_loyalty`, the conditional block:
`if not products_per_rule.get(rule):
continue`
will normally prevent rules belonging to ewallet program types
from going further in the method because they have the default
ewallet top-up product in their domain. When the continue is not
reached, they will reach this line:
`amount_paid = sum(max(0, line.price_total) for
line in order_lines if line.product_id in rule_products)`
which will end up offsetting the actual subtraction of a SOLs
`points_cost` from an ewallet `loyalty.card`'s balance.
**Fix:**
Add another check prior to the calculation of order points which
prevents ewallet coupons without any top-up products from
reaching the problematic code. If an ewallet program doesn't
have any `trigger_product_ids`, it shouldn't ever need to
perform such calculations.
opw-3756134
closes odoo/odoo#159957
X-original-commit: 695b3a687a3d160451339a675aab8986682389eb
Signed-off-by: Vincent Ethan <etvi@odoo.com>
This commit is contained in:
@@ -881,6 +881,9 @@ class SaleOrder(models.Model):
|
||||
rule_points = []
|
||||
program_result = result.setdefault(program, dict())
|
||||
for rule in program.rule_ids:
|
||||
# prevent bottomless ewallet spending
|
||||
if program.program_type == 'ewallet' and not program.trigger_product_ids:
|
||||
break
|
||||
if rule.mode == 'with_code' and rule not in self.code_enabled_rule_ids:
|
||||
continue
|
||||
code_matched = True
|
||||
|
||||
@@ -26,40 +26,52 @@ class TestLoyalty(TestSaleCouponCommon):
|
||||
'taxes_id': [(6, 0, [])],
|
||||
})
|
||||
|
||||
cls.ewallet_program = cls.env['loyalty.program'].create({
|
||||
'name': 'eWallet Program',
|
||||
'program_type': 'ewallet',
|
||||
'trigger': 'auto',
|
||||
'applies_on': 'future',
|
||||
'reward_ids': [Command.create({
|
||||
'reward_type': 'discount',
|
||||
'discount_mode': 'per_point',
|
||||
'discount': 1,
|
||||
})],
|
||||
'rule_ids': [Command.create({
|
||||
'reward_point_amount': '1',
|
||||
'reward_point_mode': 'money',
|
||||
'product_ids': cls.env.ref('loyalty.ewallet_product_50'),
|
||||
})],
|
||||
'trigger_product_ids': cls.env.ref('loyalty.ewallet_product_50'),
|
||||
})
|
||||
|
||||
cls.ewallet = cls.env['loyalty.card'].create({
|
||||
'program_id': cls.ewallet_program.id,
|
||||
'partner_id': cls.partner_a.id,
|
||||
'points': 10,
|
||||
})
|
||||
cls.ewallet_program.coupon_ids = [Command.set([cls.ewallet.id])]
|
||||
|
||||
cls.user_salemanager = new_test_user(cls.env, login='user_salemanager', groups='sales_team.group_sale_manager')
|
||||
|
||||
def test_nominative_programs(self):
|
||||
loyalty_program, ewallet_program = self.env['loyalty.program'].create([
|
||||
{
|
||||
'name': 'Loyalty Program',
|
||||
'program_type': 'loyalty',
|
||||
'trigger': 'auto',
|
||||
'applies_on': 'both',
|
||||
'rule_ids': [(0, 0, {
|
||||
'reward_point_mode': 'unit',
|
||||
'reward_point_amount': 1,
|
||||
'product_ids': [self.product_a.id],
|
||||
})],
|
||||
'reward_ids': [(0, 0, {
|
||||
'reward_type': 'discount',
|
||||
'discount': 1.5,
|
||||
'discount_mode': 'per_point',
|
||||
'discount_applicability': 'order',
|
||||
'required_points': 3,
|
||||
})],
|
||||
},
|
||||
{
|
||||
'name': 'eWallet Program',
|
||||
'program_type': 'ewallet',
|
||||
'applies_on': 'future',
|
||||
'trigger': 'auto',
|
||||
'rule_ids': [(0, 0, {
|
||||
'reward_point_mode': 'money',
|
||||
'reward_point_amount': 10,
|
||||
})],
|
||||
'reward_ids': [(0, 0, {})],
|
||||
}
|
||||
])
|
||||
loyalty_program = self.env['loyalty.program'].create({
|
||||
'name': 'Loyalty Program',
|
||||
'program_type': 'loyalty',
|
||||
'trigger': 'auto',
|
||||
'applies_on': 'both',
|
||||
'rule_ids': [(0, 0, {
|
||||
'reward_point_mode': 'unit',
|
||||
'reward_point_amount': 1,
|
||||
'product_ids': [self.product_a.id],
|
||||
})],
|
||||
'reward_ids': [(0, 0, {
|
||||
'reward_type': 'discount',
|
||||
'discount': 1.5,
|
||||
'discount_mode': 'per_point',
|
||||
'discount_applicability': 'order',
|
||||
'required_points': 3,
|
||||
})],
|
||||
})
|
||||
|
||||
order = self.env['sale.order'].create({
|
||||
'partner_id': self.partner_a.id,
|
||||
@@ -68,18 +80,13 @@ class TestLoyalty(TestSaleCouponCommon):
|
||||
claimable_rewards = order._get_claimable_rewards()
|
||||
# Should be empty since we do not have any coupon created yet
|
||||
self.assertFalse(claimable_rewards, "No program should be applicable")
|
||||
loyalty_card, ewallet_coupon = self.env['loyalty.card'].create([
|
||||
{
|
||||
'program_id': loyalty_program.id,
|
||||
'partner_id': self.partner_a.id,
|
||||
'points': 10,
|
||||
},
|
||||
{
|
||||
'program_id': ewallet_program.id,
|
||||
'partner_id': self.partner_a.id,
|
||||
'points': 0,
|
||||
},
|
||||
])
|
||||
loyalty_card = self.env['loyalty.card'].create({
|
||||
'program_id': loyalty_program.id,
|
||||
'partner_id': self.partner_a.id,
|
||||
'points': 10,
|
||||
})
|
||||
self.ewallet.points = 0
|
||||
|
||||
order.write({
|
||||
'order_line': [(0, 0, {
|
||||
'product_id': self.product_a.id,
|
||||
@@ -96,7 +103,7 @@ class TestLoyalty(TestSaleCouponCommon):
|
||||
"Can only use a whole number of required points",
|
||||
)
|
||||
self.assertEqual(vals[0]['points_cost'], 9, "Use maximum available points for the reward")
|
||||
ewallet_coupon.points = 50
|
||||
self.ewallet.points = 50
|
||||
order._update_programs_and_rewards()
|
||||
claimable_rewards = order._get_claimable_rewards()
|
||||
self.assertEqual(len(claimable_rewards), 2, "Now that the ewallet has some points they should both be applicable.")
|
||||
@@ -605,3 +612,41 @@ class TestLoyalty(TestSaleCouponCommon):
|
||||
self._claim_reward(order, loyalty_program)
|
||||
msg = "100% discount on order should reduce total amount to 0"
|
||||
self.assertEqual(order.amount_total, 0, msg=msg)
|
||||
|
||||
def test_ewallet_program_without_trigger_product(self):
|
||||
self.ewallet_program.trigger_product_ids = [Command.clear()]
|
||||
self.ewallet.points = 1000
|
||||
|
||||
order = self.env['sale.order'].create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'order_line': [Command.create({
|
||||
'product_id': self.product_a.id,
|
||||
'points_cost': 100,
|
||||
'product_uom_qty': 1,
|
||||
})],
|
||||
})
|
||||
order._update_programs_and_rewards()
|
||||
self._claim_reward(order, self.ewallet_program, coupon=self.ewallet)
|
||||
order.action_confirm()
|
||||
|
||||
self.assertEqual(self.ewallet.points, 900)
|
||||
|
||||
def test_ewallet_applied_ewallet_topup_in_order(self):
|
||||
self.ewallet.points = 10
|
||||
|
||||
order = self.env['sale.order'].create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'order_line': [Command.create({
|
||||
'product_id': self.product_a.id,
|
||||
'points_cost': 100,
|
||||
'product_uom_qty': 1,
|
||||
}), Command.create({
|
||||
'product_id': self.env.ref('loyalty.ewallet_product_50').id,
|
||||
'product_uom_qty': 1,
|
||||
})],
|
||||
})
|
||||
order._update_programs_and_rewards()
|
||||
self._claim_reward(order, self.ewallet_program, coupon=self.ewallet)
|
||||
order.action_confirm()
|
||||
|
||||
self.assertEqual(self.ewallet.points, 50)
|
||||
|
||||
Reference in New Issue
Block a user