[FIX] website_sale_coupon: correctly handle multiwebsite

Steps to reproduce:
- Create an automatic promotional program P for Website 2.
- Place Order for Website 1.
- Promotion is applied.

Fixes https://github.com/odoo/odoo/issues/55121
Closes https://github.com/odoo/odoo/pull/57895

closes odoo/odoo#60151

X-original-commit: 8a5c53bbc771c0895c001a7f7f43591c5cde2ba6
Forward-port-of: https://github.com/odoo/enterprise/pull/13875
Signed-off-by: Jérémy Kersten (jke) <jke@openerp.com>
Co-authored-by: Romain Derie <rde@odoo.com>
Co-authored-by: Swapnesh Shah <swapneshshah111@gmail.com>
This commit is contained in:
Romain Derie
2020-10-16 08:50:13 +00:00
committed by Jeremy Kersten
co-authored by Swapnesh Shah
parent c9b133813b
commit 8a22cf8626
6 changed files with 206 additions and 39 deletions
@@ -71,6 +71,16 @@ class TestSaleCouponProgramNumbers(TestSaleCouponCommon):
'reward_product_id': self.largeMeetingTable.id,
'rule_products_domain': '[["name","ilike","drawer black"]]',
})
self.discount_coupon_program = self.env['coupon.program'].create({
'name': '$100 coupon',
'program_type': 'coupon_program',
'reward_type': 'discount',
'discount_type': 'fixed_amount',
'discount_fixed_amount': 100,
'active': True,
'discount_apply_on': 'on_order',
'rule_minimum_amount': 100.00,
})
def test_program_numbers_free_and_paid_product_qty(self):
# These tests will focus on numbers (free product qty, SO total, reduction total..)
@@ -571,18 +581,8 @@ class TestSaleCouponProgramNumbers(TestSaleCouponCommon):
'order_id': order.id,
})
self.assertEqual(order.amount_total, 165.0, "The order amount is not correct")
discount_coupon_program = self.env['coupon.program'].create({
'name': '$100 coupon',
'program_type': 'coupon_program',
'reward_type': 'discount',
'discount_type': 'fixed_amount',
'discount_fixed_amount': 100,
'active': True,
'discount_apply_on': 'on_order',
'rule_minimum_amount': 100.00,
})
self.env['coupon.generate.wizard'].with_context(active_id=discount_coupon_program.id).create({}).generate_coupon()
coupon = discount_coupon_program.coupon_ids[0]
self.env['coupon.generate.wizard'].with_context(active_id=self.discount_coupon_program.id).create({}).generate_coupon()
coupon = self.discount_coupon_program.coupon_ids[0]
self.env['sale.coupon.apply.code'].with_context(active_id=order.id).create({
'coupon_code': coupon.code
}).process_coupon()
@@ -604,17 +604,17 @@ class TestSaleCouponProgramNumbers(TestSaleCouponCommon):
})
self.assertEqual(order.amount_total, 165.0, "The order amount is not correct")
discount_promotion_program = self.env['coupon.program'].create({
'name': '$100 promotion program',
'program_type': 'promotion_program',
'promo_code_usage': 'code_needed',
'promo_code': 'testpromo',
'reward_type': 'discount',
'discount_type': 'fixed_amount',
'discount_fixed_amount': 100,
'active': True,
'discount_apply_on': 'on_order',
'rule_minimum_amount': 100.00,
self.env['coupon.program'].create({
'name': '$100 promotion program',
'program_type': 'promotion_program',
'promo_code_usage': 'code_needed',
'promo_code': 'testpromo',
'reward_type': 'discount',
'discount_type': 'fixed_amount',
'discount_fixed_amount': 100,
'active': True,
'discount_apply_on': 'on_order',
'rule_minimum_amount': 100.00,
})
self.env['sale.coupon.apply.code'].with_context(active_id=order.id).create({
@@ -624,18 +624,8 @@ class TestSaleCouponProgramNumbers(TestSaleCouponCommon):
order.recompute_coupon_lines()
self.assertEqual(order.amount_total, 65.0, "The promotion program should not be removed after recomputation")
discount_coupon_program = self.env['coupon.program'].create({
'name': '$100 coupon',
'program_type': 'coupon_program',
'reward_type': 'discount',
'discount_type': 'fixed_amount',
'discount_fixed_amount': 100,
'active': True,
'discount_apply_on': 'on_order',
'rule_minimum_amount': 100.00,
})
self.env['coupon.generate.wizard'].with_context(active_id=discount_coupon_program.id).create({}).generate_coupon()
coupon = discount_coupon_program.coupon_ids[0]
self.env['coupon.generate.wizard'].with_context(active_id=self.discount_coupon_program.id).create({}).generate_coupon()
coupon = self.discount_coupon_program.coupon_ids[0]
with self.assertRaises(UserError):
self.env['sale.coupon.apply.code'].with_context(active_id=order.id).create({
'coupon_code': coupon.code
@@ -780,7 +770,7 @@ class TestSaleCouponProgramNumbers(TestSaleCouponCommon):
'lst_price': 118.0,
})
test10 = self.env['coupon.program'].create({
self.env['coupon.program'].create({
'name': '10% discount',
'promo_code_usage': 'no_code_needed',
'program_type': 'promotion_program',
@@ -789,7 +779,7 @@ class TestSaleCouponProgramNumbers(TestSaleCouponCommon):
'rule_minimum_amount': 1500.0,
'rule_minimum_amount_tax_inclusion': 'tax_included',
})
test15 = self.env['coupon.program'].create({
self.env['coupon.program'].create({
'name': '15% discount',
'promo_code_usage': 'no_code_needed',
'program_type': 'promotion_program',
@@ -798,7 +788,7 @@ class TestSaleCouponProgramNumbers(TestSaleCouponCommon):
'rule_minimum_amount': 1750.0,
'rule_minimum_amount_tax_inclusion': 'tax_included',
})
test20 = self.env['coupon.program'].create({
self.env['coupon.program'].create({
'name': '20% discount',
'promo_code_usage': 'no_code_needed',
'program_type': 'promotion_program',
@@ -807,7 +797,7 @@ class TestSaleCouponProgramNumbers(TestSaleCouponCommon):
'rule_minimum_amount': 2000.0,
'rule_minimum_amount_tax_inclusion': 'tax_included',
})
test25 = self.env['coupon.program'].create({
self.env['coupon.program'].create({
'name': '25% discount',
'promo_code_usage': 'no_code_needed',
'program_type': 'promotion_program',
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import sale_coupon
from . import sale_coupon_program
from . import sale_order
@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
class SaleCoupon(models.Model):
_inherit = 'coupon.coupon'
def _check_coupon_code(self, order):
if self.program_id.website_id and self.program_id.website_id != order.website_id:
return {'error': 'This coupon is not valid on this website.'}
return super()._check_coupon_code(order)
@@ -21,3 +21,16 @@ class CouponProgram(models.Model):
domain += program.website_id.website_domain()
if self.search(domain):
raise ValidationError(_('The program code must be unique by website!'))
def _filter_programs_on_website(self, order):
return self.filtered(lambda program: not program.website_id or program.website_id.id == order.website_id.id)
@api.model
def _filter_programs_from_common_rules(self, order, next_order=False):
programs = self._filter_programs_on_website(order)
return super(CouponProgram, programs)._filter_programs_from_common_rules(order, next_order)
def _check_promo_code(self, order, coupon_code):
if self.website_id and self.website_id != order.website_id:
return {'error': 'This promo code is not valid on this website.'}
return super()._check_promo_code(order, coupon_code)
@@ -1 +1,2 @@
from . import test_sale_coupon_multiwebsite
from . import test_shop_sale_coupon
@@ -0,0 +1,149 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.sale_coupon.tests.test_program_numbers import TestSaleCouponProgramNumbers
from odoo.addons.website.tools import MockRequest
from odoo.exceptions import UserError
from odoo.tests import tagged
@tagged('-at_install', 'post_install')
class TestSaleCouponMultiwebsite(TestSaleCouponProgramNumbers):
def setUp(self):
super(TestSaleCouponMultiwebsite, self).setUp()
self.website = self.env['website'].browse(1)
self.website2 = self.env['website'].create({'name': 'website 2'})
def test_01_multiwebsite_checks(self):
""" Ensure the multi website compliance of programs and coupons, both in
backend and frontend.
"""
order = self.empty_order
self.env['sale.order.line'].create({
'product_id': self.largeCabinet.id,
'name': 'Large Cabinet',
'product_uom_qty': 2.0,
'order_id': order.id,
})
def _remove_reward():
order.order_line.filtered('is_reward_line').unlink()
self.assertEqual(len(order.order_line.ids), 1, "Program should have been removed")
def _apply_code(code, backend=True):
if backend:
self.env['sale.coupon.apply.code'].with_context(active_id=order.id).create({
'coupon_code': code
}).process_coupon()
else:
self.env['sale.coupon.apply.code'].sudo().apply_coupon(order, code)
# ==========================================
# ========== Programs (with code) ==========
# ==========================================
# 1. Backend - Generic
_apply_code(self.p1.promo_code)
self.assertEqual(len(order.order_line.ids), 2, "Should get the discount line as it is a generic promo program")
_remove_reward()
# 2. Frontend - Generic
with MockRequest(self.env, website=self.website):
_apply_code(self.p1.promo_code, False)
self.assertEqual(len(order.order_line.ids), 2, "Should get the discount line as it is a generic promo program (2)")
_remove_reward()
# make program specific
self.p1.website_id = self.website.id
# 3. Backend - Specific
with self.assertRaises(UserError):
_apply_code(self.p1.promo_code) # the order has no website_id so not possible to use a website specific code
# 4. Frontend - Specific - Correct website
order.website_id = self.website.id
with MockRequest(self.env, website=self.website):
_apply_code(self.p1.promo_code, False)
self.assertEqual(len(order.order_line.ids), 2, "Should get the discount line as it is a specific promo program for the correct website")
_remove_reward()
# 5. Frontend - Specific - Wrong website
self.p1.website_id = self.website2.id
with MockRequest(self.env, website=self.website):
_apply_code(self.p1.promo_code, False)
self.assertEqual(len(order.order_line.ids), 1, "Should not get the reward as wrong website")
# ==============================
# =========== Coupons ==========
# ==============================
order.website_id = False
self.env['coupon.generate.wizard'].with_context(active_id=self.discount_coupon_program.id).create({
'nbr_coupons': 4,
}).generate_coupon()
coupons = self.discount_coupon_program.coupon_ids
# 1. Backend - Generic
_apply_code(coupons[0].code)
self.assertEqual(len(order.order_line.ids), 2, "Should get the discount line as it is a generic coupon program")
_remove_reward()
# 2. Frontend - Generic
with MockRequest(self.env, website=self.website):
_apply_code(coupons[1].code, False)
self.assertEqual(len(order.order_line.ids), 2, "Should get the discount line as it is a generic coupon program (2)")
_remove_reward()
# make program specific
self.discount_coupon_program.website_id = self.website.id
# 3. Backend - Specific
with self.assertRaises(UserError):
_apply_code(coupons[2].code) # the order has no website_id so not possible to use a website specific code
# 4. Frontend - Specific - Correct website
order.website_id = self.website.id
with MockRequest(self.env, website=self.website):
_apply_code(coupons[2].code, False)
self.assertEqual(len(order.order_line.ids), 2, "Should get the discount line as it is a specific coupon program for the correct website")
_remove_reward()
# 5. Frontend - Specific - Wrong website
self.discount_coupon_program.website_id = self.website2.id
with MockRequest(self.env, website=self.website):
_apply_code(coupons[3].code, False)
self.assertEqual(len(order.order_line.ids), 1, "Should not get the reward as wrong website")
# ========================================
# ========== Programs (no code) ==========
# ========================================
order.website_id = False
self.p1.website_id = False
self.p1.promo_code = False
self.p1.promo_code_usage = 'no_code_needed'
# 1. Backend - Generic
order.recompute_coupon_lines()
self.assertEqual(len(order.order_line.ids), 2, "Should get the discount line as it is a generic promo program")
# 2. Frontend - Generic
with MockRequest(self.env, website=self.website):
order.recompute_coupon_lines()
self.assertEqual(len(order.order_line.ids), 2, "Should get the discount line as it is a generic promo program (2)")
# make program specific
self.p1.website_id = self.website.id
# 3. Backend - Specific
order.recompute_coupon_lines()
self.assertEqual(len(order.order_line.ids), 1, "The order has no website_id so not possible to use a website specific code")
# 4. Frontend - Specific - Correct website
order.website_id = self.website.id
with MockRequest(self.env, website=self.website):
order.recompute_coupon_lines()
self.assertEqual(len(order.order_line.ids), 2, "Should get the discount line as it is a specific promo program for the correct website")
# 5. Frontend - Specific - Wrong website
self.p1.website_id = self.website2.id
with MockRequest(self.env, website=self.website):
order.recompute_coupon_lines()
self.assertEqual(len(order.order_line.ids), 1, "Should not get the reward as wrong website")