From 0247d2f35fdeebdb48e8eafc2ae69d1675158303 Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Mon, 24 Jun 2019 17:44:03 +0200 Subject: [PATCH] [FIX] sale_coupon, sale_coupon_delivery: Wrong discount on promotion program MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's consider a product P with a unit price = 100€ When a price list PL with a discount of 20% was applied, and a coupon program CP with a discount of 10% was also applied, system still calculated the rate based on unit price of the product and not on the discounted price. So in this example, the amount of the CP discount was 10€ instead of 8€ because the CP discount was computed on the unit price 100€ instead of the discounted price 80€ (with 20% discount of PL) opw:2006829 X-port/Cherry-pick of odoo/enterprise@23739e8a71b3a107246fdae9f7208dfcd852ca09 --- addons/sale_coupon/models/sale_order.py | 8 ++-- addons/sale_coupon/tests/common.py | 19 +++++++++ .../test_program_with_code_operations.py | 39 +++++++++++++++++++ .../sale_coupon_delivery/models/sale_order.py | 2 +- 4 files changed, 63 insertions(+), 5 deletions(-) diff --git a/addons/sale_coupon/models/sale_order.py b/addons/sale_coupon/models/sale_order.py index f19668de7b5..dd52685d1cb 100644 --- a/addons/sale_coupon/models/sale_order.py +++ b/addons/sale_coupon/models/sale_order.py @@ -79,7 +79,7 @@ class SaleOrder(models.Model): return applied_programs.filtered(lambda program: program._is_global_discount_program()) def _get_reward_values_product(self, program): - price_unit = self.order_line.filtered(lambda line: program.reward_product_id == line.product_id)[0].price_unit + price_unit = self.order_line.filtered(lambda line: program.reward_product_id == line.product_id)[0].price_reduce order_lines = (self.order_line - self._get_reward_lines()).filtered(lambda x: program._is_valid_product(x.product_id)) max_product_qty = sum(order_lines.mapped('product_uom_qty')) or 1 @@ -120,10 +120,10 @@ class SaleOrder(models.Model): def _get_cheapest_line(self): # Unit prices tax included - return min(self.order_line.filtered(lambda x: not x.is_reward_line and x.price_unit > 0), key=lambda x: x['price_unit']) + return min(self.order_line.filtered(lambda x: not x.is_reward_line and x.price_reduce > 0), key=lambda x: x['price_reduce']) def _get_reward_values_discount_percentage_per_line(self, program, line): - discount_amount = line.product_uom_qty * line.price_unit * (program.discount_percentage / 100) + discount_amount = line.product_uom_qty * line.price_reduce * (program.discount_percentage / 100) return discount_amount def _get_reward_values_discount(self, program): @@ -142,7 +142,7 @@ class SaleOrder(models.Model): if program.discount_apply_on == 'cheapest_product': line = self._get_cheapest_line() if line: - discount_line_amount = line.price_unit * (program.discount_percentage / 100) + discount_line_amount = line.price_reduce * (program.discount_percentage / 100) if discount_line_amount: taxes = line.tax_id if self.fiscal_position_id: diff --git a/addons/sale_coupon/tests/common.py b/addons/sale_coupon/tests/common.py index fec5d669aba..e6b8bd1b359 100644 --- a/addons/sale_coupon/tests/common.py +++ b/addons/sale_coupon/tests/common.py @@ -60,6 +60,14 @@ class TestSaleCouponCommon(TestSaleProductAttributeValueSetup): 'taxes_id': [(6, 0, [self.tax_15pc_excl.id])], }) + self.product_C = self.env['product.product'].create({ + 'name': 'Product C', + 'list_price': 100, + 'sale_ok': True, + 'taxes_id': [(6, 0, [])], + + }) + # Immediate Program By A + B: get B free # No Conditions self.immediate_promotion_program = self.env['sale.coupon.program'].create({ @@ -79,3 +87,14 @@ class TestSaleCouponCommon(TestSaleProductAttributeValueSetup): 'rule_products_domain': "[('id', 'in', [%s])]" % (self.product_A.id), 'active': True, }) + + self.code_promotion_program_with_discount = self.env['sale.coupon.program'].create({ + 'name': 'Buy 1 C + Enter code, 10 percent discount on C', + 'promo_code_usage': 'code_needed', + 'reward_type': 'discount', + 'discount_type': 'percentage', + 'discount_percentage': 10, + 'rule_products_domain': "[('id', 'in', [%s])]" % (self.product_C.id), + 'active': True, + 'discount_apply_on': 'on_order', + }) diff --git a/addons/sale_coupon/tests/test_program_with_code_operations.py b/addons/sale_coupon/tests/test_program_with_code_operations.py index 276b20c2221..2a19854f25e 100644 --- a/addons/sale_coupon/tests/test_program_with_code_operations.py +++ b/addons/sale_coupon/tests/test_program_with_code_operations.py @@ -66,6 +66,45 @@ class TestProgramWithCodeOperations(TestSaleCouponCommon): self.assertEqual(len(order.order_line.ids), 0) self.assertEqual(coupon.state, 'new') + def test_coupon_code_with_pricelist(self): + # Test case: Generate a coupon (10% discount) and apply it on an order with a specific pricelist (10% discount) + + self.env['sale.coupon.generate'].with_context(active_id=self.code_promotion_program_with_discount.id).create({ + 'generation_type': 'nbr_coupon', + 'nbr_coupons': 1, + }).generate_coupon() + coupon = self.code_promotion_program_with_discount.coupon_ids + + first_pricelist = self.env['product.pricelist'].create({ + 'name': 'First pricelist', + 'discount_policy': 'with_discount', + 'item_ids': [(0, 0, { + 'compute_price': 'percentage', + 'base': 'list_price', + 'percent_price': 10, + 'applied_on': '3_global', + 'name': 'First discount' + })] + }) + + order = self.empty_order + order.pricelist_id = first_pricelist + order.write({'order_line': [ + (0, False, { + 'product_id': self.product_C.id, + 'name': '1 Product C', + 'product_uom': self.uom_unit.id, + 'product_uom_qty': 1.0, + }) + ]}) + self.env['sale.coupon.apply.code'].with_context(active_id=order.id).create({ + 'coupon_code': coupon.code + }).process_coupon() + order.recompute_coupon_lines() + self.assertEqual(len(order.order_line.ids), 2) + self.assertEqual(coupon.state, 'used') + self.assertEqual(order.amount_total, 81, "SO total should be 81: (10% of 100 with pricelist) + 10% of 90 with coupon code") + def test_on_next_order_reward_promotion_program(self): # The flow: # 1. Create a program `A` that gives a free `Product B` on next order if you buy a an `product A` diff --git a/addons/sale_coupon_delivery/models/sale_order.py b/addons/sale_coupon_delivery/models/sale_order.py index e4002fa9abf..dab962d9e78 100644 --- a/addons/sale_coupon_delivery/models/sale_order.py +++ b/addons/sale_coupon_delivery/models/sale_order.py @@ -42,7 +42,7 @@ class SaleOrder(models.Model): def _get_cheapest_line(self): # Unit prices tax included - return min(self.order_line.filtered(lambda x: not x.is_reward_line and not x.is_delivery and x.price_unit > 0), key=lambda x: x['price_unit']) + return min(self.order_line.filtered(lambda x: not x.is_reward_line and not x.is_delivery and x.price_reduce > 0), key=lambda x: x['price_reduce']) class SalesOrderLine(models.Model): _inherit = "sale.order.line"