From d8a730f5b38963d8b5b99cb5e9ba890ec253d4c7 Mon Sep 17 00:00:00 2001 From: Jinjiu Liu Date: Wed, 31 Aug 2022 12:13:03 +0000 Subject: [PATCH] [FIX] website_sale: calculate the tax for no_variant product in cart MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reproduction: 1. Install eCommerce and accounting 2. Go to Accounting->Configuration->Taxes, add one sale tax 15%, and one 0%. Both taxes are included in the price. 3. Go to Configuration-> Fiscal Position, create a new one “test” which applies the tax mapping from 15% sale tax to the 0%. Check “Detect Automatically”, set country group as Europe 4. Go to Website->Orders->Customers, set the country of the portal user, Joel Willis, as Luxembourg. In Sales&Purchase->Fiscal Information set the fiscal position as “test” 5. Go to Website->Products->Product Variants, create a new one “test_tax_prod” with price 110, and sale tax 15%, can be sold, can be purchased and consumable. 6. Go to Website->Configuration->Attribute, create a new product attribute “test” with Display Type as Ratio, Variant Creation Mode as Never. Create a value “a” in its attribute values 7. Go to Website->Products->Products, find test_tax_prod, go to its variant tab, set attribute as “test” with value “a”, publish this product 8. Open an incognito tab, login as “portal” pwd: portal, search the product “test_tax_prod”, set the price list as “public price list”, the price is 95.65 on the page. Add it to the cart, the price is 110 Reason: When we update the order_line, we should update the price based on the fiscal position for the no_variant products too. However, based on the current workflow, the price is computed in method _website_product_id_change before an order line is created. Thus for no_variant products, the price is not updated. Fix: This issue is fixed in saas-15.3 by rewriting the workflow and calculating the price through an on_change function here: https://github.com/odoo/odoo/blob/saas-15.3/addons/sale/models/sale_order_line.py#L588-L605 In v13, we don’t have this on_change method to compute the new price, so a similar price update can be created in the cart update for website_sale. This fix solution was created here: https://github.com/odoo/odoo/commit/ea259218196b2d9dc71beffea697025580c6d696 However, new price computation issues can happen when combining two pricelists, here: https://github.com/odoo/odoo/commit/8d6a2ca0f3dcf8f8cf4679da2b3ce582b7826391 Thus the fix is eventually done in _website_product_id_change. Added a new parameter to force checking the orderlines based on the domain. The orderline is created but only can be searched based on the domain. This ensures the price is computed correctly for pricelists and for tax. Added fiscal position test for no variant product opw-2856956 Fix by re-writing in saas-15.3: https://github.com/odoo/odoo/blob/saas-15.3/addons/sale/models/sale_order_line.py#L588-L605 First related fix: https://github.com/odoo/odoo/commit/ea259218196b2d9dc71beffea697025580c6d696 Second fix to patch for the first fix: https://github.com/odoo/odoo/commit/8d6a2ca0f3dcf8f8cf4679da2b3ce582b7826391 closes odoo/odoo#99647 Signed-off-by: William Braeckman (wbr) --- .../website_event_sale/models/sale_order.py | 2 +- addons/website_sale/models/sale_order.py | 4 +- ...ite_sale_product_attribute_value_config.py | 75 +++++++++++++++++++ 3 files changed, 78 insertions(+), 3 deletions(-) diff --git a/addons/website_event_sale/models/sale_order.py b/addons/website_event_sale/models/sale_order.py index 773bbe962d2ed..cd9c37c2527fb 100644 --- a/addons/website_event_sale/models/sale_order.py +++ b/addons/website_event_sale/models/sale_order.py @@ -10,7 +10,7 @@ class SaleOrder(models.Model): def _cart_find_product_line(self, product_id=None, line_id=None, **kwargs): self.ensure_one() - lines = super(SaleOrder, self)._cart_find_product_line(product_id, line_id) + lines = super(SaleOrder, self)._cart_find_product_line(product_id, line_id, **kwargs) if line_id: return lines domain = [('id', 'in', lines.ids)] diff --git a/addons/website_sale/models/sale_order.py b/addons/website_sale/models/sale_order.py index 6afef3ba8e48c..63832de6fe613 100644 --- a/addons/website_sale/models/sale_order.py +++ b/addons/website_sale/models/sale_order.py @@ -80,7 +80,7 @@ def _cart_find_product_line(self, product_id=None, line_id=None, **kwargs): product = self.env['product.product'].browse(product_id) # split lines with the same product if it has untracked attributes - if product and (product.product_tmpl_id.has_dynamic_attributes() or product.product_tmpl_id._has_no_variant_attributes()) and not line_id: + if product and (product.product_tmpl_id.has_dynamic_attributes() or product.product_tmpl_id._has_no_variant_attributes()) and not line_id and not kwargs.get('force_search', False): return self.env['sale.order.line'] domain = [('order_id', '=', self.id), ('product_id', '=', product_id)] @@ -133,7 +133,7 @@ def _website_product_id_change(self, order_id, product_id, qty=0): else: pu = product.price if order.pricelist_id and order.partner_id: - order_line = order._cart_find_product_line(product.id) + order_line = order._cart_find_product_line(product.id, force_search=True) if order_line: pu = self.env['account.tax']._fix_tax_included_price_company(pu, product.taxes_id, order_line[0].tax_id, self.company_id) diff --git a/addons/website_sale/tests/test_website_sale_product_attribute_value_config.py b/addons/website_sale/tests/test_website_sale_product_attribute_value_config.py index 44beced3ee49f..cae7b9909011c 100644 --- a/addons/website_sale/tests/test_website_sale_product_attribute_value_config.py +++ b/addons/website_sale/tests/test_website_sale_product_attribute_value_config.py @@ -204,3 +204,78 @@ def test_cart_update_with_fpos(self): with MockRequest(self.env, website=current_website, sale_order_id=so.id): so._cart_update(product_id=test_product.product_variant_id.id, line_id=sol.id, set_qty=1) self.assertEqual(round(sol.price_total), 50, "100$ with 50% discount + 0% tax (mapped from fp 10% -> 0%)") + + def test_cart_update_with_fpos_no_variant_product(self): + # We will test that the mapping of an 10% included tax by a 0% by a fiscal position is taken into account when updating the cart for no_variant product + self.env.user.partner_id.country_id = False + current_website = self.env['website'].get_current_website() + pricelist = current_website.get_current_pricelist() + (self.env['product.pricelist'].search([]) - pricelist).write({'active': False}) + # Add 10% tax on product + tax10 = self.env['account.tax'].create({'name': "Test tax 10", 'amount': 10, 'price_include': True, 'amount_type': 'percent', 'type_tax_use': 'sale'}) + tax0 = self.env['account.tax'].create({'name': "Test tax 0", 'amount': 0, 'price_include': True, 'amount_type': 'percent', 'type_tax_use': 'sale'}) + + # Create fiscal position mapping taxes 10% -> 0% + fpos = self.env['account.fiscal.position'].create({ + 'name': 'test', + }) + self.env['account.fiscal.position.tax'].create({ + 'position_id': fpos.id, + 'tax_src_id': tax10.id, + 'tax_dest_id': tax0.id, + }) + + product = self.env['product.product'].create({ + 'name': 'prod_no_variant', + 'list_price': 110, + 'taxes_id': [(6, 0, [tax10.id])], + 'type': 'consu', + }) + + # create an attribute with one variant + product_attribute = self.env['product.attribute'].create({ + 'name': 'test_attr', + 'display_type': 'radio', + 'create_variant': 'no_variant', + }) + + # create attribute value + a1 = self.env['product.attribute.value'].create({ + 'name': 'pa_value', + 'attribute_id': product_attribute.id, + 'sequence': 1, + }) + + # set variant value to product template + product_template = self.env['product.template'].search( + [('name', '=', 'prod_no_variant')], limit=1) + + product_template.attribute_line_ids = [(0, 0, { + 'attribute_id': product_attribute.id, + 'value_ids': [(6, 0, [a1.id])], + })] + + # publish the product on website + product_template.is_published = True + + # create a so for user using the fiscal position + + so = self.env['sale.order'].create({ + 'partner_id': self.env.user.partner_id.id, + }) + sol = self.env['sale.order.line'].create({ + 'name': product_template.name, + 'product_id': product.id, + 'product_uom_qty': 1, + 'product_uom': product_template.uom_id.id, + 'price_unit': product_template.list_price, + 'order_id': so.id, + 'tax_id': [(6, 0, [tax10.id])], + }) + self.assertEqual(round(sol.price_total), 110.0, "110$ with 10% included tax") + so.pricelist_id = pricelist + so.fiscal_position_id = fpos + sol.product_id_change() + with MockRequest(self.env, website=current_website, sale_order_id=so.id): + so._cart_update(product_id=product.id, line_id=sol.id, set_qty=1) + self.assertEqual(round(sol.price_total), 100, "100$ with public price+ 0% tax (mapped from fp 10% -> 0%)")