[FIX] website_sale: consistent tax computation

While fixing a non deterministic test failing
on nightly l10n builds, slight incoherences between
sale & website_sale tax computation have been noticed
in the computation of the contextual price (used in
some snippets).

This commit fixes the test, making sure it doesn't fail on
l10n builds, but also uses the same tax util in website_sale
than in sale, to make sure the displayed amounts are coherent
(and supposedly correct).

runbot error: 52831 (& a bunch of others)

closes odoo/odoo#153299

X-original-commit: 91f9057c73810c34a4b0b85a60c84bb1c482efa4
Signed-off-by: Victor Feyens (vfe) <vfe@odoo.com>
This commit is contained in:
Victor Feyens
2024-02-13 10:07:53 +00:00
parent 6aa487ecd8
commit 3d8be5aa4c
3 changed files with 170 additions and 10 deletions
+2 -1
View File
@@ -469,7 +469,8 @@ class SaleOrderLine(models.Model):
if not line.product_uom or not line.product_id:
line.price_unit = 0.0
else:
price = line.with_company(line.company_id)._get_display_price()
line = line.with_company(line.company_id)
price = line._get_display_price()
line.price_unit = line.product_id._get_tax_included_unit_price(
line.company_id or line.env.company,
line.order_id.currency_id,
+20 -7
View File
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
@@ -109,9 +108,23 @@ class Product(models.Model):
def _get_contextual_price_tax_selection(self):
self.ensure_one()
price = self._get_contextual_price()
website = self.env['website'].get_current_website()
line_tax_type = website.show_line_subtotals_tax_selection
company_taxes = self.taxes_id.filtered(lambda tax: tax.company_id in self.env.company.parent_ids)
if line_tax_type == "tax_included" and company_taxes:
price = company_taxes.compute_all(price, product=self, partner=self.env['res.partner'])['total_included']
product_taxes = self.sudo().taxes_id.filtered(lambda x: x.company_id in self.env.company.parent_ids)
if product_taxes:
website = self.env['website'].get_current_website()
fiscal_position = website.sudo().fiscal_position_id
price = self._get_tax_included_unit_price(
website.company_id,
website.currency_id,
fields.Date.context_today(self),
'sale',
fiscal_position=fiscal_position,
product_price_unit=price,
product_currency=website.currency_id,
)
line_tax_type = website.show_line_subtotals_tax_selection
tax_display = "total_included" if line_tax_type == "tax_included" else "total_excluded"
taxes = fiscal_position.map_tax(product_taxes)
price = taxes.compute_all(price, product=self, partner=self.env['res.partner'])[tax_display]
return price
@@ -1,7 +1,16 @@
# coding: utf-8
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import itertools
from odoo.fields import Command
from odoo.tests import tagged
from odoo.addons.sale.tests.test_sale_product_attribute_value_config import (
TestSaleProductAttributeValueCommon,
)
from odoo.addons.website.tools import MockRequest
from odoo.addons.sale.tests.test_sale_product_attribute_value_config import TestSaleProductAttributeValueCommon
from odoo.addons.website_sale.controllers.main import WebsiteSale
@tagged('post_install', '-at_install')
class WebsiteSaleProductTests(TestSaleProductAttributeValueCommon):
@@ -12,6 +21,39 @@ class WebsiteSaleProductTests(TestSaleProductAttributeValueCommon):
cls.website = cls.env.ref('website.default_website')
cls.website.company_id = cls.env.company
cls.tax_5 = cls.env['account.tax'].create({
'name': '5% Tax',
'amount_type': 'percent',
'amount': 5,
'price_include': False,
'include_base_amount': False,
'type_tax_use': 'sale',
})
cls.tax_10 = cls.env['account.tax'].create({
'name': '10% Tax',
'amount_type': 'percent',
'amount': 10,
'price_include': False,
'include_base_amount': False,
'type_tax_use': 'sale',
})
cls.tax_15 = cls.env['account.tax'].create({
'name': '15% Tax',
'amount_type': 'percent',
'amount': 15,
'price_include': False,
'include_base_amount': False,
'type_tax_use': 'sale',
})
cls.fiscal_country = cls.env['res.country'].create({
'name': "Super Fiscal Position",
'code': 'SFP',
})
cls.product = cls.env['product.product'].create({
'name': 'Super Product',
'list_price': 100.0,
})
def test_website_sale_contextual_price(self):
contextual_price = self.computer._get_contextual_price()
self.assertEqual(
@@ -78,3 +120,107 @@ class WebsiteSaleProductTests(TestSaleProductAttributeValueCommon):
pricelist.discount_policy = 'without_discount'
res = product_tmpl._get_sales_prices(pricelist, self.env['account.fiscal.position'])
self.assertEqual(res[product_tmpl.id]['base_price'], 75)
def test_get_contextual_price_tax_selection(self):
"""
`_get_contextual_price_tax_selection` is used to display the price on the website (e.g. in the carousel).
We test that the contextual price is correctly computed. That is, it is coherent with the price displayed on the product when in the cart.
"""
param_main_product_tax_included = [True, False]
param_show_line_subtotals_tax_selection = ['tax_included', 'tax_excluded']
param_extra_tax = [False, 'included', 'excluded']
param_fpos = [False, 'to_tax_excluded', 'to_tax_included']
parameters = itertools.product(param_main_product_tax_included, param_show_line_subtotals_tax_selection, param_extra_tax, param_fpos)
self.product.taxes_id = self.tax_15
fiscal_position = self.env['account.fiscal.position'].create({
'name': 'Super Fiscal Position',
'auto_apply': True,
'country_id': self.fiscal_country.id,
'tax_ids': [
Command.create({
'tax_src_id': self.tax_15.id,
'tax_dest_id': self.tax_10.id,
})
]
})
self.env.user.partner_id.country_id = self.fiscal_country
self.WebsiteSaleController = WebsiteSale()
for main_product_tax_included, show_line_subtotals_tax_selection, extra_tax, fpos in parameters:
with self.subTest(main_product_tax_included=main_product_tax_included, show_line_subtotals_tax_selection=show_line_subtotals_tax_selection, extra_tax=extra_tax, fpos=fpos):
# set "show_line_subtotals_tax_selection" parameter
self.website.invalidate_recordset(['fiscal_position_id'], flush=False)
config = self.env['res.config.settings'].create({})
config.show_line_subtotals_tax_selection = show_line_subtotals_tax_selection
config.execute()
self.assertEqual(self.website.show_line_subtotals_tax_selection, show_line_subtotals_tax_selection)
tax_ids = [self.tax_15.id]
# set "main_product_tax_included" parameter
if main_product_tax_included:
self.tax_15.price_include = True
self.tax_15.include_base_amount = True
else:
self.tax_15.price_include = False
self.tax_15.include_base_amount = False
# set "extra_tax" parameter
if extra_tax:
if extra_tax == 'included':
self.tax_5.price_include = True
self.tax_5.include_base_amount = True
else:
self.tax_5.price_include = False
self.tax_5.include_base_amount = False
tax_ids.append(self.tax_5.id)
self.product.taxes_id = tax_ids
# set "fpos" parameter
if fpos:
if fpos == 'to_tax_included':
self.tax_10.price_include = True
self.tax_10.include_base_amount = True
else:
self.tax_10.price_include = False
self.tax_10.include_base_amount = False
fiscal_position.action_unarchive()
else:
fiscal_position.action_archive()
with MockRequest(self.env, website=self.website):
self.assertEqual(
self.website.fiscal_position_id,
fpos and fiscal_position or self.env['account.fiscal.position']
)
contextual_price = self.product.with_context(
website_id=self.website.id,
)._get_contextual_price_tax_selection()
self.WebsiteSaleController.cart_update(product_id=self.product.id, add_qty=1)
sale_order = self.website.sale_get_order()
self.assertEqual(sale_order.website_id, self.website)
self.assertEqual(sale_order.company_id, self.website.company_id)
self.assertEqual(sale_order.currency_id, self.website.currency_id)
self.assertFalse(sale_order.pricelist_id)
if fpos:
self.assertEqual(sale_order.fiscal_position_id, fiscal_position)
else:
self.assertFalse(sale_order.fiscal_position_id)
sol = sale_order.order_line
if fpos:
self.assertEqual(sol.tax_id, fiscal_position.map_tax(self.product.taxes_id))
else:
self.assertEqual(sol.tax_id, self.product.taxes_id)
if show_line_subtotals_tax_selection == 'tax_included':
self.assertAlmostEqual(sol.price_reduce_taxinc, contextual_price)
self.assertAlmostEqual(sale_order.amount_total, contextual_price)
else:
self.assertAlmostEqual(sol.price_reduce_taxexcl, contextual_price)
self.assertAlmostEqual(sale_order.amount_untaxed, contextual_price)