Odoo18-Base/addons/sale_loyalty/tests/test_loyalty.py
2025-01-06 10:57:38 +07:00

1068 lines
42 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.exceptions import ValidationError
from odoo.fields import Command
from odoo.tests import new_test_user, tagged
from odoo.tools.float_utils import float_compare
from odoo.addons.sale_loyalty.tests.common import TestSaleCouponCommon
@tagged('post_install', '-at_install')
class TestLoyalty(TestSaleCouponCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env['loyalty.program'].search([]).write({'active': False})
cls.product_a, cls.product_b = cls.env['product.product'].create([
{
'name': 'Product C',
'list_price': 100,
'sale_ok': True,
'taxes_id': [Command.set([])],
},
{
'name': "Product B",
'sale_ok': True,
}
])
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.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')
cls.promotion_code_10pc = cls.env['loyalty.program'].create({
'name': "Code for 10% on orders",
'trigger': 'with_code',
'program_type': 'promotion',
'applies_on': 'current',
'rule_ids': [Command.create({
'mode': 'with_code',
'code': 'test_10pc',
})],
'reward_ids': [Command.create({
'reward_type': 'discount',
'discount_mode': 'percent',
'discount': 10,
'discount_applicability': 'order',
'required_points': 1,
})],
})
def test_nominative_programs(self):
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.empty_order
order._update_programs_and_rewards()
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 = self.env['loyalty.card'].create({
'program_id': loyalty_program.id,
'partner_id': self.partner.id,
'points': 10,
})
self.ewallet.points = 0
order.write({
'order_line': [(0, 0, {
'product_id': self.product_a.id,
'product_uom_qty': 1,
})]
})
order._update_programs_and_rewards()
claimable_rewards = order._get_claimable_rewards()
self.assertEqual(len(claimable_rewards), 1, "The ewallet program should not be applicable since the card has no points.")
vals = order._get_reward_values_discount(loyalty_program.reward_ids[0], loyalty_card)
self.assertEqual(
vals[0]['points_cost'] % loyalty_program.reward_ids.required_points,
0,
"Can only use a whole number of required points",
)
self.assertEqual(vals[0]['points_cost'], 9, "Use maximum available points for the reward")
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.")
def test_cancel_order_with_coupons(self):
"""This test ensure that creating an order with coupons will not
raise an access error on POS line modele when canceling the order."""
self.env['loyalty.program'].create({
'name': '10% Discount',
'program_type': 'coupons',
'applies_on': 'current',
'trigger': 'auto',
'rule_ids': [(0, 0, {})],
'reward_ids': [(0, 0, {
'reward_type': 'discount',
'discount': 10,
'discount_mode': 'percent',
'discount_applicability': 'order',
})]
})
order = self.env['sale.order'].with_user(self.user_salemanager).create({
'partner_id': self.partner.id,
'order_line': [
(0, 0, {
'product_id': self.product_a.id,
})
]
})
order._update_programs_and_rewards()
self.assertTrue(order.coupon_point_ids)
# Canceling the order should not raise an access error:
# During the cancel process, we are trying to get `use_count` of the coupon,
# and we call the `_compute_use_count` that is also in pos_loyalty.
# This last one will try to find related POS lines while user have not access to POS.
order._action_cancel()
self.assertFalse(order.coupon_point_ids)
def test_distribution_amount_payment_programs(self):
"""
Check how the amount of a payment reward is distributed.
An ewallet should not be used to refund taxes.
Its amount must be distributed between the products.
"""
# Create two products
product_a, product_b = self.env['product.product'].create([
{
'name': 'Product A',
'list_price': 100,
'sale_ok': True,
'taxes_id': [Command.set(self.tax_15pc_excl.ids)],
},
{
'name': 'Product B',
'list_price': 100,
'sale_ok': True,
'taxes_id': [Command.set(self.tax_15pc_excl.ids)],
},
])
# Create a coupon and a ewallet
coupon_program, ewallet_program = self.env['loyalty.program'].create([
{
'name': 'Coupon Program',
'program_type': 'coupons',
'trigger': 'with_code',
'applies_on': 'both',
'reward_ids': [Command.create({
'reward_type': 'discount',
'discount': 100.0,
'discount_applicability': 'specific',
'discount_product_domain': '[("name", "=", "Product A")]',
})],
},
{
'name': 'eWallet Program',
'program_type': 'ewallet',
'applies_on': 'future',
'trigger': 'auto',
'rule_ids': [Command.create({
'reward_point_mode': 'money',
})],
'reward_ids': [Command.create({
'discount_mode': 'per_point',
'discount': 1,
'discount_applicability': 'order',
})],
}
])
coupon_partner, _ = self.env['loyalty.card'].create([
{
'program_id': coupon_program.id,
'partner_id': self.partner.id,
'points': 1,
'code': '5555',
},
{
'program_id': ewallet_program.id,
'partner_id': self.partner.id,
'points': 115,
},
])
# Create the order
order = self.env['sale.order'].with_user(self.user_salemanager).create({
'partner_id': self.partner.id,
'order_line': [
Command.create({
'product_id': product_a.id,
}),
Command.create({
'product_id': product_b.id,
}),
]
})
self.assertEqual(order.amount_total, 230.0)
self.assertEqual(order.amount_untaxed, 200.0)
self.assertEqual(order.amount_tax, 30.0)
# Apply the eWallet
order._update_programs_and_rewards()
self._claim_reward(order, ewallet_program)
self.assertEqual(order.amount_total, 115.0)
self.assertEqual(order.amount_untaxed, 85.0)
self.assertEqual(order.amount_tax, 30.0)
self.assertEqual(order.reward_amount, -115.0)
# Apply the coupon
self._apply_promo_code(order, coupon_partner.code)
self.assertEqual(order.amount_total, 0.0)
self.assertEqual(order.amount_untaxed, -15.0)
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.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_points_awarded_global_discount_code_no_domain_program(self):
"""
Check the calculation for points awarded when there is a global discount applied and the
loyalty program applies on all products (no domain).
"""
LoyaltyProgram = self.env['loyalty.program']
loyalty_program = LoyaltyProgram.create(LoyaltyProgram._get_template_values()['loyalty'])
loyalty_card = self.env['loyalty.card'].create({
'program_id': loyalty_program.id,
'partner_id': self.partner.id,
'points': 0,
})
order = self.env['sale.order'].with_user(self.user_salemanager).create({
'partner_id': self.partner.id,
'order_line': [
Command.create({
'product_id': self.product_A.id,
'tax_id': False,
}),
]
})
promotion_program = self.env['loyalty.program'].create([{
'name': "Coupon Program",
'program_type': 'promotion',
'trigger': 'auto',
'applies_on': 'current',
'rule_ids': [Command.create({
'reward_point_amount': 1,
'reward_point_mode': 'order',
'minimum_amount': 10,
})],
'reward_ids': [Command.create({
'reward_type': 'discount',
'discount': 10.0,
'discount_applicability': 'order',
'required_points': 1,
})],
}])
self.assertEqual(order.amount_total, 100)
self._auto_rewards(order, promotion_program)
self.assertEqual(order.amount_total, 90)
order.action_confirm()
self.assertEqual(loyalty_card.points, 90)
def test_points_awarded_discount_code_no_domain_program(self):
"""
Check the calculation for points awarded when there is a discount coupon applied and the
loyalty program applies on all products (no domain).
"""
LoyaltyProgram = self.env['loyalty.program']
loyalty_program = LoyaltyProgram.create(LoyaltyProgram._get_template_values()['loyalty'])
loyalty_card = self.env['loyalty.card'].create({
'program_id': loyalty_program.id,
'partner_id': self.partner.id,
'points': 0,
})
order = self.env['sale.order'].with_user(self.user_salemanager).create({
'partner_id': self.partner.id,
'order_line': [
Command.create({
'product_id': self.product_A.id,
'tax_id': False,
}),
]
})
self.assertEqual(order.amount_total, 100)
self._apply_promo_code(order, "test_10pc")
self.assertEqual(order.amount_total, 90)
order.action_confirm()
self.assertEqual(loyalty_card.points, 90)
def test_points_awarded_general_discount_code_specific_domain_program(self):
"""
Check the calculation for points awarded when there is a discount coupon applied and the
loyalty program applies on a specific domain. The discount code has no domain. The product
related to that discount is not in the domain of the loyalty program.
Expected behavior: The discount is not included in the computation of points
"""
product_category_base = self.env.ref('product.product_category_1')
product_category_food = self.env['product.category'].create({
'name': "Food",
'parent_id': product_category_base.id
})
self.product_A.categ_id = product_category_food
self.product_B.list_price = 50
LoyaltyProgram = self.env['loyalty.program']
loyalty_program = LoyaltyProgram.create(LoyaltyProgram._get_template_values()['loyalty'])
loyalty_program.rule_ids.product_category_id = product_category_food.id
loyalty_card = self.env['loyalty.card'].create({
'program_id': loyalty_program.id,
'partner_id': self.partner.id,
'points': 0,
})
order = self.env['sale.order'].with_user(self.user_salemanager).create({
'partner_id': self.partner.id,
'order_line': [
Command.create({
'product_id': self.product_A.id,
'tax_id': False,
}),
Command.create({
'product_id': self.product_B.id,
'tax_id': False,
}),
]
})
self.assertEqual(order.amount_total, 150)
self._apply_promo_code(order, "test_10pc")
self.assertEqual(order.amount_total, 135) # (product_A + product_B) * 0.9
order.action_confirm()
self.assertEqual(loyalty_card.points, 100)
def test_points_awarded_specific_discount_code_specific_domain_program(self):
"""
Check the calculation for points awarded when there is a discount coupon applied and the
loyalty program applies on a specific domain. The discount code has the same domain as the
loyalty program. The product related to that discount code is set up to be included in the
domain of the loyalty program.
Expected behavior: The discount is included in the computation of points
"""
product_category_base = self.env.ref('product.product_category_1')
product_category_food = self.env['product.category'].create({
'name': "Food",
'parent_id': product_category_base.id
})
self.product_A.categ_id = product_category_food
self.product_B.list_price = 50
LoyaltyProgram = self.env['loyalty.program']
loyalty_program = LoyaltyProgram.create(LoyaltyProgram._get_template_values()['loyalty'])
loyalty_program.rule_ids.product_category_id = product_category_food.id
loyalty_card = self.env['loyalty.card'].create({
'program_id': loyalty_program.id,
'partner_id': self.partner.id,
'points': 0,
})
self.promotion_code_10pc.rule_ids.product_category_id = product_category_food.id
self.promotion_code_10pc.reward_ids.discount_applicability = 'specific'
self.promotion_code_10pc.reward_ids.discount_product_category_id = product_category_food.id
discount_product = self.env['product.product'].search([('id', '=', self.promotion_code_10pc.reward_ids.discount_line_product_id.id)])
discount_product.categ_id = product_category_food.id
order = self.env['sale.order'].with_user(self.user_salemanager).create({
'partner_id': self.partner.id,
'order_line': [
Command.create({
'product_id': self.product_A.id,
'tax_id': False,
}),
Command.create({
'product_id': self.product_B.id,
'tax_id': False,
}),
]
})
self.assertEqual(order.amount_total, 150)
self._apply_promo_code(order, "test_10pc")
self.assertEqual(order.amount_total, 140) # (product_A * 0.9 ) + product_B
order.action_confirm()
self.assertEqual(loyalty_card.points, 90)
def test_points_awarded_ewallet(self):
"""
Check the calculation for point awarded when using ewallet
"""
LoyaltyProgram = self.env['loyalty.program']
loyalty_program = LoyaltyProgram.create(LoyaltyProgram._get_template_values()['loyalty'])
loyalty_card = self.env['loyalty.card'].create({
'program_id': loyalty_program.id,
'partner_id': self.partner.id,
'points': 0,
})
order = self.env['sale.order'].with_user(self.user_salemanager).create({
'partner_id': self.partner.id,
'order_line': [
Command.create({
'product_id': self.product_A.id,
'tax_id': False,
}),
]
})
self.assertEqual(order.amount_total, 100)
order._update_programs_and_rewards()
self._claim_reward(order, self.ewallet_program, coupon=self.ewallet)
self.assertEqual(order.amount_total, 90)
order.action_confirm()
self.assertEqual(loyalty_card.points, 100)
def test_points_awarded_giftcard(self):
"""
Check the calculation for point awarded when using a gift card
"""
LoyaltyProgram = self.env['loyalty.program']
loyalty_program = LoyaltyProgram.create(LoyaltyProgram._get_template_values()['loyalty'])
loyalty_card = self.env['loyalty.card'].create({
'program_id': loyalty_program.id,
'partner_id': self.partner.id,
'points': 0,
})
program_gift_card = self.env['loyalty.program'].create({
'name': "Gift Cards",
'applies_on': 'future',
'program_type': 'gift_card',
'trigger': 'auto',
'reward_ids': [(0, 0, {
'reward_type': 'discount',
'discount': 1,
'discount_mode': 'per_point',
'discount_applicability': 'order',
})]
})
self.env['loyalty.generate.wizard'].with_context(active_id=program_gift_card.id).create({
'coupon_qty': 1,
'points_granted': 50,
}).generate_coupons()
gift_card = program_gift_card.coupon_ids[0]
order = self.env['sale.order'].with_user(self.user_salemanager).create({
'partner_id': self.partner.id,
'order_line': [
Command.create({
'product_id': self.product_A.id,
'tax_id': False,
}),
]
})
self.assertEqual(order.amount_total, 100)
self._apply_promo_code(order, gift_card.code)
self.assertEqual(order.amount_total, 50)
order.action_confirm()
self.assertEqual(loyalty_card.points, 100)
def test_multiple_discount_specific(self):
"""
Check the discount calculation if it is based on the remaining amount
"""
product_A = self.env['product.product'].create({
'name': 'Product A',
'list_price': 100,
'sale_ok': True,
'taxes_id': [],
})
coupon_program = self.env['loyalty.program'].create([{
'name': 'Coupon Program',
'program_type': 'promotion',
'trigger': 'auto',
'applies_on': 'current',
'rule_ids': [Command.create({
'reward_point_amount': 1,
'reward_point_mode': 'unit',
})],
'reward_ids': [Command.create({
'reward_type': 'discount',
'discount': 10.0,
'discount_applicability': 'specific',
'required_points': 1,
})],
}])
order = self.env['sale.order'].with_user(self.user_salemanager).create({
'partner_id': self.partner.id,
'order_line': [Command.create({
'product_id': product_A.id,
'product_uom_qty': 3,
})]
})
self.assertEqual(float_compare(order.amount_total, 300, precision_rounding=3), 0)
order._update_programs_and_rewards()
self._claim_reward(order, coupon_program)
self.assertEqual(float_compare(order.amount_total, 270, precision_rounding=3), 0, "300 * 0.9 = 270")
order._update_programs_and_rewards()
self._claim_reward(order, coupon_program)
self.assertEqual(float_compare(order.amount_total, 243, precision_rounding=3), 0, "300 * 0.9 * 0.9 = 243")
order._update_programs_and_rewards()
self._claim_reward(order, coupon_program)
self.assertEqual(float_compare(order.amount_total, 218.7, precision_rounding=3), 0, "300 * 0.9 * 0.9 * 0.9 = 218.7")
def test_promotion_program_restricted_to_pricelists(self):
self.env['product.pricelist'].search([]).action_archive()
company_currency = self.env.company.currency_id
pricelist_1, pricelist_2 = self.env['product.pricelist'].create([
{'name': 'Basic company_currency pricelist', 'currency_id': company_currency.id},
{'name': 'Other company_currency pricelist', 'currency_id': company_currency.id},
])
self.immediate_promotion_program.active = True
order = self.empty_order.copy()
order.write({'order_line': [
(0, False, {
'product_id': self.product_A.id,
'name': '1 Product A',
'product_uom': self.uom_unit.id,
'product_uom_qty': 1.0,
}),
(0, False, {
'product_id': self.product_B.id,
'name': '2 Product B',
'product_uom': self.uom_unit.id,
'product_uom_qty': 1.0,
}),
]})
applied_message = "The promo offer should have been applied."
not_applied_message = "The promo offer should not have been applied because the order's " \
"pricelist is not eligible to this promotion."
order.pricelist_id = self.env['product.pricelist']
order._update_programs_and_rewards()
self._claim_reward(order, self.immediate_promotion_program)
self.assertEqual(len(order.order_line.ids), 3, applied_message)
order.pricelist_id = pricelist_1
order._update_programs_and_rewards()
self._claim_reward(order, self.immediate_promotion_program)
self.assertEqual(len(order.order_line.ids), 3, applied_message)
self.immediate_promotion_program.pricelist_ids = [pricelist_1.id]
order.pricelist_id = self.env['product.pricelist']
order._update_programs_and_rewards()
self._claim_reward(order, self.immediate_promotion_program)
self.assertEqual(len(order.order_line.ids), 2, not_applied_message)
order.pricelist_id = pricelist_1
order._update_programs_and_rewards()
self._claim_reward(order, self.immediate_promotion_program)
self.assertEqual(len(order.order_line.ids), 3, applied_message)
order.pricelist_id = pricelist_2
order._update_programs_and_rewards()
self._claim_reward(order, self.immediate_promotion_program)
self.assertEqual(len(order.order_line.ids), 2, not_applied_message)
self.immediate_promotion_program.pricelist_ids = [pricelist_1.id, pricelist_2.id]
order.pricelist_id = self.env['product.pricelist']
order._update_programs_and_rewards()
self._claim_reward(order, self.immediate_promotion_program)
self.assertEqual(len(order.order_line.ids), 2, not_applied_message)
order.pricelist_id = pricelist_1
order._update_programs_and_rewards()
self._claim_reward(order, self.immediate_promotion_program)
self.assertEqual(len(order.order_line.ids), 3, applied_message)
def test_coupon_program_restricted_to_pricelists(self):
self.env['product.pricelist'].search([]).action_archive()
company_currency = self.env.company.currency_id
pricelist_1, pricelist_2 = self.env['product.pricelist'].create([
{'name': 'Basic company_currency pricelist', 'currency_id': company_currency.id},
{'name': 'Other company_currency pricelist', 'currency_id': company_currency.id},
])
self.code_promotion_program.active = True
self.env['loyalty.generate.wizard'].with_context(
active_id=self.code_promotion_program.id
).create({'coupon_qty': 7, 'points_granted': 1}).generate_coupons()
coupons = self.code_promotion_program.coupon_ids
order_no_pricelist = self.empty_order.copy()
order_no_pricelist.write({'pricelist_id': None, 'order_line': [
(0, False, {
'product_id': self.product_A.id,
'name': '1 Product A',
'product_uom': self.uom_unit.id,
'product_uom_qty': 1.0,
}),
]})
order_pricelist_1 = order_no_pricelist.copy()
order_pricelist_1.pricelist_id = pricelist_1
order_pricelist_2 = order_no_pricelist.copy()
order_pricelist_2.pricelist_id = pricelist_2
applied_message = "The coupon code should have been applied."
not_applied_message = "The coupon code should not have been applied because the order's " \
"pricelist is not eligible to this promotion."
order_0 = order_no_pricelist.copy()
self._apply_promo_code(order_0, coupons[0].code)
self.assertEqual(len(order_0.order_line.ids), 2, applied_message)
order_1 = order_pricelist_1.copy()
self._apply_promo_code(order_1, coupons[1].code)
self.assertEqual(len(order_1.order_line.ids), 2, applied_message)
self.code_promotion_program.pricelist_ids = [pricelist_1.id]
order_2 = order_no_pricelist.copy()
with self.assertRaises(ValidationError):
self._apply_promo_code(order_2, coupons[2].code)
self.assertEqual(len(order_2.order_line.ids), 1, not_applied_message)
order_3 = order_pricelist_1.copy()
self._apply_promo_code(order_3, coupons[3].code)
self.assertEqual(len(order_3.order_line.ids), 2, applied_message)
order_4 = order_pricelist_2.copy()
with self.assertRaises(ValidationError):
self._apply_promo_code(order_4, coupons[4].code)
self.assertEqual(len(order_4.order_line.ids), 1, not_applied_message)
self.code_promotion_program.pricelist_ids = [pricelist_1.id, pricelist_2.id]
order_5 = order_no_pricelist.copy()
with self.assertRaises(ValidationError):
self._apply_promo_code(order_5, coupons[5].code)
self.assertEqual(len(order_5.order_line.ids), 1, not_applied_message)
order_6 = order_pricelist_1.copy()
self._apply_promo_code(order_6, coupons[6].code)
self.assertEqual(len(order_6.order_line.ids), 2, applied_message)
def test_specific_promotion_on_free_product(self):
product_A = self.env['product.product'].create({
'name': 'Product A',
'list_price': 100,
'sale_ok': True,
'taxes_id': [],
})
promotion_program = self.env['loyalty.program'].create([{
'name': 'Promotion Program',
'program_type': 'promotion',
'trigger': 'auto',
'applies_on': 'current',
'rule_ids': [Command.create({
'reward_point_amount': 1,
'reward_point_mode': 'unit',
})],
'reward_ids': [Command.create({
'reward_type': 'discount',
'discount': 10.0,
'discount_applicability': 'specific',
'discount_product_ids': [product_A.id],
'required_points': 1,
})],
}])
order = self.env['sale.order'].with_user(self.user_salemanager).create({
'partner_id': self.partner.id,
'order_line': [
Command.create({
'product_id': product_A.id,
}),
Command.create({
'product_id': product_A.id,
'discount': 100,
}),
]
})
order._update_programs_and_rewards()
self._claim_reward(order, promotion_program)
self.assertEqual(order.amount_total, 90)
def test_gift_card_program_without_product(self):
product_A = self.env['product.product'].create({
'name': 'Product A',
'list_price': 100,
'sale_ok': True,
'taxes_id': [],
})
giftcard_program = self.env['loyalty.program'].create([{
'name': 'Gift Card Program',
'program_type': 'gift_card',
'trigger': 'auto',
'applies_on': 'current',
'rule_ids': [Command.create({
'reward_point_amount': 1,
'reward_point_mode': 'unit',
})],
}])
order = self.env['sale.order'].with_user(self.user_salemanager).create({
'partner_id': self.partner.id,
'order_line': [
Command.create({
'product_id': product_A.id,
}),
]
})
order._update_programs_and_rewards()
self._claim_reward(order, giftcard_program)
self.assertEqual(giftcard_program.coupon_count, 0)
def test_ewallet_code_use_restriction(self):
self.env['loyalty.generate.wizard'].with_context(active_id=self.ewallet_program.id).create({
'coupon_qty': 1,
'points_granted': 100,
}).generate_coupons()
order = self.env['sale.order'].with_user(self.user_salemanager).create({
'partner_id': self.partner.id,
'order_line': [
Command.create({
'product_id': self.product_a.id,
}),
],
})
with self.assertRaises(ValidationError):
self._apply_promo_code(order, self.ewallet_program.coupon_ids[0].code)
def test_100_percent_discount(self):
"""
Check whether a program offering 100% discount on an order reduces the order's total amount
to zero.
Assumes global tax rounding, as there's no good way to ensure the tax of the reward product
equals the sum of taxes of the lines when each of them gets rounded.
"""
self.env.company.tax_calculation_rounding_method = 'round_globally'
loyalty_program = self.env['loyalty.program'].create([{
'name': 'Full Discount',
'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': 100,
'discount_mode': 'percent',
'discount_applicability': 'order',
'required_points': 1,
})],
}])
self.env['loyalty.card'].create({
'program_id': loyalty_program.id, 'partner_id': self.partner.id, 'points': 2
})
order = self.env['sale.order'].create({
'partner_id': self.partner.id,
'order_line': [(0, 0, {
'product_id': self.product_A.id, 'product_uom_qty': 1, 'price_unit': price
}) for price in (5.60, 8.92, 44.91, 217.26, 2400.00)],
})
order._update_programs_and_rewards()
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_discount_on_taxes_with_child_tax(self):
"""
Check whether a program discount properly apply when product contain group of tax.
"""
self.env.company.tax_calculation_rounding_method = 'round_globally'
loyalty_program = self.env['loyalty.program'].create([{
'name': '90% Discount',
'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': 90,
'discount_mode': 'percent',
'discount_applicability': 'order',
'required_points': 1,
})],
}])
self.env['loyalty.card'].create({'program_id': loyalty_program.id, 'partner_id': self.partner.id, 'points': 2})
order = self.env['sale.order'].create({
'partner_id': self.partner.id,
'order_line': [(0, 0, {'product_id': self.product_D.id, 'product_uom_qty': 1})],
})
order._update_programs_and_rewards()
self._claim_reward(order, loyalty_program)
msg = "Discountable should take child tax amount into account"
self.assertEqual(order.amount_total, 10, 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.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.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)
def test_discount_reward_claimable_only_once(self):
"""
Check that discount rewards already applied won't be shown in the claimable rewards anymore.
"""
program = self.env['loyalty.program'].create({
'name': "10% Discount & Gift",
'applies_on': 'current',
'trigger': 'with_code',
'program_type': 'promotion',
'rule_ids': [Command.create({'mode': 'with_code', 'code': "10PERCENT&GIFT"})],
'reward_ids': [
Command.create({
'reward_type': 'product',
'reward_product_id': self.product_B.id,
'reward_product_qty': 1,
}),
Command.create({
'reward_type': 'discount',
'discount': 10,
'discount_mode': 'percent',
'discount_applicability': 'specific',
}),
],
})
coupon = self.env['loyalty.card'].create({
'program_id': program.id, 'points': 20, 'code': 'GIFT_CARD'
})
order = self.env['sale.order'].create({
'partner_id': self.partner.id,
'order_line': [Command.create({'product_id': self.product_a.id})]
})
product_reward = program.reward_ids.filtered(lambda reward: reward.reward_type == 'product')
discount_reward = program.reward_ids - product_reward
order._apply_program_reward(discount_reward, coupon)
rewards = order._get_claimable_rewards()[coupon]
msg = "Only the free product should be applicable, as the discount was already applied."
self.assertEqual(rewards, product_reward, msg)
def test_archived_reward_products(self):
"""
Check that we do not use loyalty rewards that have no active reward product.
In the case where the reward is based on reward_product_tag_id we also check
the case where at least one reward is active.
"""
LoyaltyProgram = self.env['loyalty.program']
loyalty_program = LoyaltyProgram.create(LoyaltyProgram._get_template_values()['loyalty'])
loyalty_program_tag = LoyaltyProgram.create(LoyaltyProgram._get_template_values()['loyalty'])
free_product_tag = self.env['product.tag'].create({'name': 'Free Product'})
self.product_b.write({'product_tag_ids': [(4, free_product_tag.id)]})
product_c = self.env['product.template'].create(
{
'name': 'Free Product C',
'list_price': 1,
'product_tag_ids': [(4, free_product_tag.id)],
}
)
loyalty_program.reward_ids[0].write({
'reward_type': 'product',
'required_points': 1,
'reward_product_id': self.product_b,
})
loyalty_program_tag.reward_ids[0].write({
'reward_type': 'product',
'required_points': 1,
'reward_product_tag_id': free_product_tag.id,
})
self.product_b.active = False
product_c.active = False
order = self.env['sale.order'].create({
'partner_id': self.partner.id,
'order_line': [
Command.create({
'product_id': self.product_a.id,
}),
]
})
order._update_programs_and_rewards()
rewards = [value.ids for value in order._get_claimable_rewards().values()]
self.assertTrue(all(loyalty_program.reward_ids[0].id not in r for r in rewards))
self.assertTrue(all(loyalty_program_tag.reward_ids[0].id not in r for r in rewards))
product_c.active = True
order._update_programs_and_rewards()
rewards = [value.ids for value in order._get_claimable_rewards().values()]
self.assertTrue(any(loyalty_program_tag.reward_ids[0].id in r for r in rewards))