[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:
Romain Derie
2019-04-01 15:47:40 +00:00
parent b327f2f3c3
commit 9f5cdd86f2
3 changed files with 66 additions and 0 deletions
@@ -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()
+2
View File
@@ -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")