From 64d1febc43084c819bfdbf7cb1c9f4f3892b54f3 Mon Sep 17 00:00:00 2001 From: mathysPaul Date: Fri, 9 Feb 2024 14:57:15 +0100 Subject: [PATCH] [FIX] sale_loyalty: prevent coupon removal on double apply Addresses the issue where reapplying an already applied coupon in the website shop led to the disappearance of the discount. With this fix, the discount remains applied, and the system continues to inform the user that the coupon has already been used, preventing confusion and maintaining consistency in the discount application process. task-3621246 closes odoo/odoo#153485 Signed-off-by: Victor Feyens (vfe) --- addons/sale_loyalty/models/sale_order.py | 4 +- .../tests/test_shop_sale_coupon.py | 60 ++++++++++++++++++- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/addons/sale_loyalty/models/sale_order.py b/addons/sale_loyalty/models/sale_order.py index 5f897f49f5d..4e777948027 100644 --- a/addons/sale_loyalty/models/sale_order.py +++ b/addons/sale_loyalty/models/sale_order.py @@ -992,7 +992,7 @@ class SaleOrder(models.Model): if not program.filtered_domain(self._get_program_domain()): return {'error': _('The program is not available for this order.')} elif program in self._get_applied_programs(): - return {'error': _('This program is already applied to this order.')} + return {'error': _('This program is already applied to this order.'), 'already_applied': True} # Check for applicability from the program's triggers/rules. # This step should also compute the amount of points to give for that program on that order. status = self._program_check_compute_points(program)[program] @@ -1058,7 +1058,7 @@ class SaleOrder(models.Model): if 'error' in apply_result and (not program.is_nominative or (program.is_nominative and not coupon)): if rule: self.code_enabled_rule_ids -= rule - if coupon: + if coupon and not apply_result.get('already_applied', False): self.applied_coupon_ids -= coupon return apply_result coupon = apply_result.get('coupon', self.env['loyalty.card']) 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 6801758812e..65d3062bc47 100644 --- a/addons/website_sale_loyalty/tests/test_shop_sale_coupon.py +++ b/addons/website_sale_loyalty/tests/test_shop_sale_coupon.py @@ -1,10 +1,14 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from datetime import timedelta -from odoo import fields +from odoo import fields, http from odoo.exceptions import ValidationError -from odoo.tests import HttpCase, tagged, TransactionCase +from odoo.fields import Command +from odoo.tests import HttpCase, tagged + from odoo.addons.sale.tests.test_sale_product_attribute_value_config import TestSaleProductAttributeValueCommon +from odoo.addons.website.tools import MockRequest +from odoo.addons.website_sale_loyalty.controllers.main import WebsiteSale @tagged('post_install', '-at_install') @@ -366,3 +370,55 @@ class TestWebsiteSaleCoupon(HttpCase): msg = "The coupon should've been removed from the order" self.assertEqual(len(order.applied_coupon_ids), 0, msg=msg) + + def test_04_apply_coupon_code_twice(self): + """This test ensures that applying a coupon with code twice will: + 1. Raise an error + 2. Not delete the coupon + """ + website = self.env['website'].browse(1) + + # Create product + product = self.env['product.product'].create({ + 'name': 'Product', + 'list_price': 100, + 'sale_ok': True, + 'taxes_id': [], + }) + + order = self.empty_order + order.write({ + 'website_id': website.id, + 'order_line': [ + Command.create({ + 'product_id': product.id, + }), + ] + }) + + WebsiteSaleController = WebsiteSale() + + installed_modules = set(self.env['ir.module.module'].search([ + ('state', '=', 'installed'), + ]).mapped('name')) + for _ in http._generate_routing_rules(installed_modules, nodb_only=False): + pass + + with MockRequest(self.env, website=website, sale_order_id=order.id) as request: + # Check the base cart value + self.assertEqual(order.amount_total, 100.0, "The base cart value is incorrect.") + + # Apply coupon for the first time + WebsiteSaleController.pricelist(promo=self.coupon.code) + + # Check that the coupon has been applied + self.assertEqual(order.amount_total, 90.0, "The coupon is not applied.") + + # Apply the coupon again + WebsiteSaleController.pricelist(promo=self.coupon.code) + WebsiteSaleController.cart() + error_msg = request.session.get('error_promo_code') + + # Check that the coupon stay applied + self.assertEqual(bool(error_msg), True, "Apply a coupon twice should display an error message") + self.assertEqual(order.amount_total, 90.0, "Apply a coupon twice shouldn't delete it")