From f72751dbfbf415b4490dbd2ff984c8c53de8edad Mon Sep 17 00:00:00 2001 From: AH-Yussef Date: Tue, 20 Feb 2024 02:21:36 +0000 Subject: [PATCH] [FIX] website_sale_loyalty: allow to remove coupon from cart Steps to reproduce - Install eCommerce app - Go to Settings and enable Discounts, Loyalty & Gift Card option - Go to Website app > eCommerce > Loyalty > Discount & Loyalty - Create a new coupon by setting the Program Type to Coupons and then save. - Generate a new coupon by clicking the GENERATE COUPONS button. - Go to Website > site > Homepage - Go to Shop page - Add a product to the cart - Apply the coupon code created - Remove the coupon code by clicking the bin icon next to the coupon code line - The coupon code is NOT removed. Investigation First, we'll take a look on what happens when a coupon code is applied - When a coupon code is applied, a new sale.order.line is created for it. - That triggers `super()._compute_website_order_line()` https://github.com/odoo/odoo/blob/d8ec7735aa8759fa94fc0223945ac9b19e05312e/addons/website_sale_loyalty/models/sale_order.py#L111 which filters out the discount order line https://github.com/odoo/odoo/blob/d8ec7735aa8759fa94fc0223945ac9b19e05312e/addons/website_sale/models/sale_order.py#L66 via the `_show_in_cart()` method https://github.com/odoo/odoo/blob/d8ec7735aa8759fa94fc0223945ac9b19e05312e/addons/website_sale_loyalty/models/sale_order_line.py#L12 - Then a new temporary sale.order.line is created by https://github.com/odoo/odoo/blob/d8ec7735aa8759fa94fc0223945ac9b19e05312e/addons/website_sale_loyalty/models/sale_order.py#L121 meaning that is has no `id` - Note that we can only apply one coupon from the same program type. Second, Let's take a look on what happens when we try to remove a coupon code - A call is made to `cart_update_json()` which calls https://github.com/odoo/odoo/blob/d8ec7735aa8759fa94fc0223945ac9b19e05312e/addons/website_sale/controllers/main.py#L811-L819 which calls https://github.com/odoo/odoo/blob/d8ec7735aa8759fa94fc0223945ac9b19e05312e/addons/website_sale_loyalty/models/sale_order.py#L159 - But as noted above the coupon order line has no id being a temporary record. Hence the `line_id` is None which basically cancels the cart update. opw-3744910 closes odoo/odoo#156553 X-original-commit: 1657d6f165f78f971d5bf975a8b4e366655d1ab0 Signed-off-by: Morgane Demesmaeker Signed-off-by: Ali Hassan Youssef (alhy) --- .../website_sale_loyalty/models/sale_order.py | 8 ++++++ .../tests/test_shop_sale_coupon.py | 28 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/addons/website_sale_loyalty/models/sale_order.py b/addons/website_sale_loyalty/models/sale_order.py index 9efa1a22368..b5df8e15d49 100644 --- a/addons/website_sale_loyalty/models/sale_order.py +++ b/addons/website_sale_loyalty/models/sale_order.py @@ -162,6 +162,14 @@ class SaleOrder(models.Model): return code def _cart_update(self, *args, **kwargs): + product_id, set_qty = kwargs['product_id'], kwargs.get('set_qty') + + line = self.order_line.filtered(lambda l: l.product_id.id == product_id) + reward_id = line.reward_id + if set_qty == 0 and line.coupon_id and reward_id and reward_id.reward_type == 'discount': + # Force the deletion of the line even if it's a temporary record created by new() + kwargs['line_id'] = line.id + res = super(SaleOrder, self)._cart_update(*args, **kwargs) self._update_programs_and_rewards() self._auto_apply_rewards() diff --git a/addons/website_sale_loyalty/tests/test_shop_sale_coupon.py b/addons/website_sale_loyalty/tests/test_shop_sale_coupon.py index 43cef393266..6801758812e 100644 --- a/addons/website_sale_loyalty/tests/test_shop_sale_coupon.py +++ b/addons/website_sale_loyalty/tests/test_shop_sale_coupon.py @@ -338,3 +338,31 @@ class TestWebsiteSaleCoupon(HttpCase): ], }) self.start_tour('/', 'apply_discount_code_program_multi_rewards', login='admin') + + def test_03_remove_coupon(self): + # 1. Simulate a frontend order (website, product) + order = self.empty_order + order.website_id = self.env['website'].browse(1) + self.env['sale.order.line'].create({ + 'product_id': self.env['product.product'].create({ + 'name': 'Product A', 'list_price': 100, 'sale_ok': True + }).id, + 'name': 'Product A', + 'order_id': order.id, + }) + + # 2. Apply the coupon + self._apply_promo_code(order, self.coupon.code) + + # 3. Remove the coupon + coupon_line = order.website_order_line.filtered( + lambda l: l.coupon_id and l.coupon_id.id == self.coupon.id + ) + + kwargs = { + 'line_id': None, 'product_id': coupon_line.product_id.id, 'add_qty': None, 'set_qty': 0 + } + order._cart_update(**kwargs) + + msg = "The coupon should've been removed from the order" + self.assertEqual(len(order.applied_coupon_ids), 0, msg=msg)