[FIX] sale_coupon: upgrade discount when possible
/!\ Cherry-pick of odoo/enterprise@8b85628c65 that was lost between coupon move from OE to OC and forward-port It is possible to setup multiple global promotions, i.e. 5% discount after 5 products and 10% discount after 10 products. Buying 10 products gives the 10% discount. But if you select 5 products (5% is applied) then add 5 more products, the 10% discount is not automatically applied, the custommer must remove the 5% discount in order to get the 10% one. This commit automatically upgrades the promotion to use the best applicable discount. opw-1953224 closes odoo/odoo#32314 Signed-off-by: Christophe Simonis <chs@odoo.com>
This commit is contained in:
@@ -146,6 +146,19 @@ class SaleCouponProgram(models.Model):
|
||||
self.discount_type == 'percentage' and \
|
||||
self.discount_apply_on == 'on_order'
|
||||
|
||||
def _keep_only_most_interesting_auto_applied_global_discount_program(self):
|
||||
'''Given a record set of programs, remove the less interesting auto
|
||||
applied global discount to keep only the most interesting one.
|
||||
We should not take promo code programs into account as a 10% auto
|
||||
applied is considered better than a 50% promo code, as the user might
|
||||
not know about the promo code.
|
||||
'''
|
||||
programs = self.filtered(lambda p: p._is_global_discount_program() and p.promo_code_usage == 'no_code_needed')
|
||||
if not programs: return self
|
||||
most_interesting_program = max(programs, key=lambda p: p.discount_percentage)
|
||||
# remove least interesting programs
|
||||
return self - (programs - most_interesting_program)
|
||||
|
||||
def _check_promo_code(self, order, coupon_code):
|
||||
message = {}
|
||||
applicable_programs = order._get_applicable_programs()
|
||||
|
||||
@@ -287,6 +287,7 @@ class SaleOrder(models.Model):
|
||||
self.ensure_one()
|
||||
order = self
|
||||
programs = order._get_applicable_no_code_promo_program()
|
||||
programs = programs._keep_only_most_interesting_auto_applied_global_discount_program()
|
||||
for program in programs:
|
||||
error_status = program._check_promo_code(order, False)
|
||||
if not error_status.get('error'):
|
||||
@@ -355,6 +356,7 @@ class SaleOrder(models.Model):
|
||||
order = self
|
||||
|
||||
applicable_programs = order._get_applicable_no_code_promo_program() + order._get_applicable_programs() + order._get_valid_applied_coupon_program()
|
||||
applicable_programs = applicable_programs._keep_only_most_interesting_auto_applied_global_discount_program()
|
||||
applied_programs = order._get_applied_programs_with_rewards_on_current_order() + order._get_applied_programs_with_rewards_on_next_order()
|
||||
programs_to_remove = applied_programs - applicable_programs
|
||||
products_to_remove = programs_to_remove.mapped('discount_line_product_id')
|
||||
|
||||
@@ -240,3 +240,54 @@ class TestProgramRules(TestSaleCouponCommon):
|
||||
|
||||
self.assertEqual(len(order.order_line.ids), 2, "The discount line should have been removed as we don't meet the program requirements")
|
||||
self.assertEqual(coupon.state, 'new', "The coupon should be reset to Valid as it's reward got removed")
|
||||
|
||||
|
||||
def test_program_rules_promotion_use_best(self):
|
||||
''' This test will:
|
||||
* Verify the best global promotion according to the
|
||||
current sale order is used.
|
||||
'''
|
||||
self.immediate_promotion_program.active = False # Avoid having this program to add rewards on this test
|
||||
order = self.empty_order
|
||||
|
||||
program_5pc = self.env['sale.coupon.program'].create({
|
||||
'name': 'Get 5% discount if buy at least 2 Product',
|
||||
'program_type': 'promotion_program',
|
||||
'reward_type': 'discount',
|
||||
'discount_type': 'percentage',
|
||||
'discount_percentage': 5.0,
|
||||
'rule_min_quantity': 2,
|
||||
'promo_code_usage': 'no_code_needed',
|
||||
})
|
||||
program_10pc = self.env['sale.coupon.program'].create({
|
||||
'name': 'Get 10% discount if buy at least 4 Product',
|
||||
'program_type': 'promotion_program',
|
||||
'reward_type': 'discount',
|
||||
'discount_type': 'percentage',
|
||||
'discount_percentage': 10.0,
|
||||
'rule_min_quantity': 4,
|
||||
'promo_code_usage': 'no_code_needed',
|
||||
})
|
||||
sol = self.env['sale.order.line'].create({
|
||||
'product_id': self.product_A.id,
|
||||
'name': 'Product A',
|
||||
'product_uom_qty': 1.0,
|
||||
'order_id': order.id,
|
||||
})
|
||||
|
||||
order.recompute_coupon_lines()
|
||||
self.assertEqual(len(order.order_line.ids), 1, "The order should only contains the Product A line")
|
||||
|
||||
sol.product_uom_qty = 3
|
||||
order.recompute_coupon_lines()
|
||||
discounts = set(order.order_line.mapped('name')) - {'Product A'}
|
||||
self.assertEqual(len(discounts), 1, "The order should contains the Product A line and a discount")
|
||||
# The name of the discount is dynamically changed to smth looking like:
|
||||
# "Discount: Get 5% discount if buy at least 2 Product - On product with following tax: Tax 15.00%"
|
||||
self.assertTrue('Get 5% discount' in discounts.pop(), "The discount should be a 5% discount")
|
||||
|
||||
sol.product_uom_qty = 5
|
||||
order.recompute_coupon_lines()
|
||||
discounts = set(order.order_line.mapped('name')) - {'Product A'}
|
||||
self.assertEqual(len(discounts), 1, "The order should contains the Product A line and a discount")
|
||||
self.assertTrue('Get 10% discount' in discounts.pop(), "The discount should be a 10% discount")
|
||||
|
||||
Reference in New Issue
Block a user