From b6688b1a475ee6155328c2c282b83291e23164e0 Mon Sep 17 00:00:00 2001 From: Victor Feyens Date: Fri, 22 Mar 2024 17:16:15 +0100 Subject: [PATCH] [IMP] account: split _get_tax_included_unit_price_from_price util separate the true computation of the tax value from the computation of the base unit price (uom & curr conversion, ...). That way, the risks of unwanted side-effects is reduced when the base unit price is already computed (sale, website_sale). Also allows preprocessing the taxes mapping, which doesn't have to be done multiple times when we compute different amounts for the same product/template. original commit: bc79770d1e00cf86a46002d633afd8b32921610b Part-of: odoo/odoo#160198 --- addons/account/models/product.py | 87 +++++++++++++++++---------- addons/sale/models/sale_order_line.py | 13 ++-- 2 files changed, 62 insertions(+), 38 deletions(-) diff --git a/addons/account/models/product.py b/addons/account/models/product.py index 296ab735348..8482bcb2504 100644 --- a/addons/account/models/product.py +++ b/addons/account/models/product.py @@ -149,9 +149,9 @@ class ProductProduct(models.Model): return self.product_tmpl_id._get_product_accounts() def _get_tax_included_unit_price(self, company, currency, document_date, document_type, - is_refund_document=False, product_uom=None, product_currency=None, - product_price_unit=None, product_taxes=None, fiscal_position=None - ): + is_refund_document=False, product_uom=None, product_currency=None, + product_price_unit=None, product_taxes=None, fiscal_position=None + ): """ Helper to get the price unit from different models. This is needed to compute the same unit price in different models (sale order, account move, etc.) with same parameters. """ @@ -187,34 +187,13 @@ class ProductProduct(models.Model): # Apply fiscal position. if product_taxes and fiscal_position: - product_taxes_after_fp = fiscal_position.map_tax(product_taxes) - flattened_taxes_after_fp = product_taxes_after_fp._origin.flatten_taxes_hierarchy() - flattened_taxes_before_fp = product_taxes._origin.flatten_taxes_hierarchy() - taxes_before_included = all(tax.price_include for tax in flattened_taxes_before_fp) - - if set(product_taxes.ids) != set(product_taxes_after_fp.ids) and taxes_before_included: - taxes_res = flattened_taxes_before_fp.with_context(round=False, round_base=False).compute_all( - product_price_unit, - quantity=1.0, - currency=currency, - product=product, - is_refund=is_refund_document, - ) - product_price_unit = taxes_res['total_excluded'] - - if any(tax.price_include for tax in flattened_taxes_after_fp): - taxes_res = flattened_taxes_after_fp.with_context(round=False, round_base=False).compute_all( - product_price_unit, - quantity=1.0, - currency=currency, - product=product, - is_refund=is_refund_document, - handle_price_include=False, - ) - for tax_res in taxes_res['taxes']: - tax = self.env['account.tax'].browse(tax_res['id']) - if tax.price_include: - product_price_unit += tax_res['amount'] + product_price_unit = self._get_tax_included_unit_price_from_price( + product_price_unit, + currency, + product_taxes, + fiscal_position=fiscal_position, + is_refund_document=is_refund_document, + ) # Apply currency rate. if currency != product_currency: @@ -222,6 +201,52 @@ class ProductProduct(models.Model): return product_price_unit + @api.model # the product is optional for `compute_all` + def _get_tax_included_unit_price_from_price( + self, product_price_unit, currency, product_taxes, + fiscal_position=None, + product_taxes_after_fp=None, + is_refund_document=False, + ): + if not product_taxes: + return product_price_unit + + if product_taxes_after_fp is None: + if not fiscal_position: + return product_price_unit + + product_taxes_after_fp = fiscal_position.map_tax(product_taxes) + + flattened_taxes_after_fp = product_taxes_after_fp._origin.flatten_taxes_hierarchy() + flattened_taxes_before_fp = product_taxes._origin.flatten_taxes_hierarchy() + taxes_before_included = all(tax.price_include for tax in flattened_taxes_before_fp) + + if set(product_taxes.ids) != set(product_taxes_after_fp.ids) and taxes_before_included: + taxes_res = flattened_taxes_before_fp.with_context(round=False, round_base=False).compute_all( + product_price_unit, + quantity=1.0, + currency=currency, + product=self, + is_refund=is_refund_document, + ) + product_price_unit = taxes_res['total_excluded'] + + if any(tax.price_include for tax in flattened_taxes_after_fp): + taxes_res = flattened_taxes_after_fp.with_context(round=False, round_base=False).compute_all( + product_price_unit, + quantity=1.0, + currency=currency, + product=self, + is_refund=is_refund_document, + handle_price_include=False, + ) + for tax_res in taxes_res['taxes']: + tax = self.env['account.tax'].browse(tax_res['id']) + if tax.price_include: + product_price_unit += tax_res['amount'] + + return product_price_unit + @api.depends('lst_price', 'product_tmpl_id', 'taxes_id') def _compute_tax_string(self): for record in self: diff --git a/addons/sale/models/sale_order_line.py b/addons/sale/models/sale_order_line.py index f3530107460..9ae598cfbab 100644 --- a/addons/sale/models/sale_order_line.py +++ b/addons/sale/models/sale_order_line.py @@ -471,14 +471,13 @@ class SaleOrderLine(models.Model): else: 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, - line.order_id.date_order, - 'sale', + line.price_unit = line.product_id._get_tax_included_unit_price_from_price( + price, + line.currency_id or line.order_id.currency_id, + product_taxes=line.product_id.taxes_id.filtered( + lambda tax: tax.company_id == line.env.company + ), fiscal_position=line.order_id.fiscal_position_id, - product_price_unit=price, - product_currency=line.currency_id ) def _get_display_price(self):