From f938f00877a4989b8bb2720167de0e717cd6834a Mon Sep 17 00:00:00 2001 From: Victor Feyens Date: Tue, 13 Dec 2022 16:22:42 +0000 Subject: [PATCH] [IMP] sale,*: tooling for conversion of amounts to the SO currency Harmonize currency conversion of unit prices: * Make sure no rounding is applied during the conversion (it'll be managed by the price_unit decimal precision and the currency rounding will only be applied by the monetary fields later in the computations). * consistent parameters for the conversion (date, company, currency). * code simplification * consistent behavior when the SO has no currency specified ... Part-of: odoo/odoo#110833 --- .../models/sale_order_line.py | 7 +--- addons/event_sale/models/sale_order.py | 6 +-- addons/sale/models/sale_order_line.py | 25 ++++++++++++ .../models/sale_order_line.py | 14 ++----- addons/sale_margin/models/sale_order_line.py | 38 +++++-------------- .../models/sale_order_line.py | 11 ++---- .../models/sale_order_line.py | 16 ++++++-- 7 files changed, 58 insertions(+), 59 deletions(-) diff --git a/addons/event_booth_sale/models/sale_order_line.py b/addons/event_booth_sale/models/sale_order_line.py index a25988791bc..0c4278622df 100644 --- a/addons/event_booth_sale/models/sale_order_line.py +++ b/addons/event_booth_sale/models/sale_order_line.py @@ -93,15 +93,12 @@ class SaleOrderLine(models.Model): def _get_display_price(self): if self.event_booth_pending_ids and self.event_id: company = self.event_id.company_id or self.env.company - currency = company.currency_id product = self.event_booth_pending_ids.booth_category_id.product_id pricelist = product.product_tmpl_id._get_contextual_pricelist() if pricelist.discount_policy == "with_discount": total_price = sum([booth.booth_category_id.price_reduce for booth in self.event_booth_pending_ids]) else: total_price = sum([booth.price for booth in self.event_booth_pending_ids]) - return currency._convert( - total_price, self.order_id.currency_id, - self.order_id.company_id or self.env.company.id, - self.order_id.date_order or fields.Date.today()) + + return self._convert_to_sol_currency(total_price, company.currency_id) return super()._get_display_price() diff --git a/addons/event_sale/models/sale_order.py b/addons/event_sale/models/sale_order.py index 05c9ffa4b27..2f100d33ef9 100644 --- a/addons/event_sale/models/sale_order.py +++ b/addons/event_sale/models/sale_order.py @@ -173,14 +173,10 @@ class SaleOrderLine(models.Model): uom=self.product_uom.id ) company = event_ticket.company_id or self.env.company - currency = company.currency_id pricelist = self.order_id.pricelist_id if pricelist.discount_policy == "with_discount": price = event_ticket.price_reduce else: price = event_ticket.price - return currency._convert( - price, self.order_id.currency_id, - self.order_id.company_id or self.env.company.id, - self.order_id.date_order or fields.Date.today()) + return self._convert_to_sol_currency(price, company.currency_id) return super()._get_display_price() diff --git a/addons/sale/models/sale_order_line.py b/addons/sale/models/sale_order_line.py index 01eef8dc153..8a6d66ce7a3 100644 --- a/addons/sale/models/sale_order_line.py +++ b/addons/sale/models/sale_order_line.py @@ -1149,3 +1149,28 @@ class SaleOrderLine(models.Model): def _is_not_sellable_line(self): # True if the line is a computed line (reward, delivery, ...) that user cannot add manually return False + + #=== TOOLING ===# + + def _convert_to_sol_currency(self, amount, currency): + """Convert the given amount from the given currency to the SO(L) currency. + + :param float amount: the amount to convert + :param currency: currency in which the given amount is expressed + :type currency: `res.currency` record + :returns: converted amount + :rtype: float + """ + self.ensure_one() + to_currency = self.currency_id or self.order_id.currency_id + if currency and to_currency and currency != to_currency: + conversion_date = self.order_id.date_order or fields.Date.context_today(self) + company = self.company_id or self.order_id.company_id or self.env.company + return currency._convert( + from_amount=amount, + to_currency=to_currency, + company=company, + date=conversion_date, + round=False, + ) + return amount diff --git a/addons/sale_expense_margin/models/sale_order_line.py b/addons/sale_expense_margin/models/sale_order_line.py index 47c1272b22b..f0c1bd7b48f 100644 --- a/addons/sale_expense_margin/models/sale_order_line.py +++ b/addons/sale_expense_margin/models/sale_order_line.py @@ -18,16 +18,8 @@ class SaleOrderLine(models.Model): else: product_cost = line.expense_id.untaxed_amount - from_currency = line.expense_id.currency_id - to_currency = line.currency_id or line.order_id.currency_id + line.purchase_price = line._convert_to_sol_currency( + product_cost, + line.expense_id.currency_id) - if to_currency and product_cost and from_currency != to_currency: - line.purchase_price = from_currency._convert( - from_amount=product_cost, - to_currency=to_currency, - company=line.company_id or self.env.company, - date=line.order_id.date_order or date_today, - round=False) - else: - line.purchase_price = product_cost return super(SaleOrderLine, self - expense_lines)._compute_purchase_price() diff --git a/addons/sale_margin/models/sale_order_line.py b/addons/sale_margin/models/sale_order_line.py index c99f5e71850..18b02dd9762 100644 --- a/addons/sale_margin/models/sale_order_line.py +++ b/addons/sale_margin/models/sale_order_line.py @@ -24,37 +24,19 @@ class SaleOrderLine(models.Model): line.purchase_price = 0.0 continue line = line.with_company(line.company_id) - product_cost = line.product_id.standard_price - line.purchase_price = line._convert_price(product_cost, line.product_id.uom_id) + + # Convert the cost to the line UoM + product_cost = line.product_id.uom_id._compute_price( + line.product_id.standard_price, + line.product_uom, + ) + + line.purchase_price = line._convert_to_sol_currency( + product_cost, + line.product_id.cost_currency_id) @api.depends('price_subtotal', 'product_uom_qty', 'purchase_price') def _compute_margin(self): for line in self: line.margin = line.price_subtotal - (line.purchase_price * line.product_uom_qty) line.margin_percent = line.price_subtotal and line.margin/line.price_subtotal - - def _convert_price(self, product_cost, from_uom): - self.ensure_one() - if not product_cost: - # If the standard_price is 0 - # Avoid unnecessary computations - # and currency conversions - if not self.purchase_price: - return product_cost - from_currency = self.product_id.cost_currency_id - to_cur = self.currency_id or self.order_id.currency_id - to_uom = self.product_uom - if to_uom and to_uom != from_uom: - product_cost = from_uom._compute_price( - product_cost, - to_uom, - ) - return from_currency._convert( - from_amount=product_cost, - to_currency=to_cur, - company=self.company_id or self.env.company, - date=self.order_id.date_order or fields.Date.today(), - round=False, - ) if to_cur and product_cost else product_cost - # The pricelist may not have been set, therefore no conversion - # is needed because we don't know the target currency.. diff --git a/addons/sale_stock_margin/models/sale_order_line.py b/addons/sale_stock_margin/models/sale_order_line.py index 16db4ef4242..938d357e59c 100644 --- a/addons/sale_stock_margin/models/sale_order_line.py +++ b/addons/sale_stock_margin/models/sale_order_line.py @@ -19,11 +19,8 @@ class SaleOrderLine(models.Model): if line.product_uom and line.product_uom != product.uom_id: purch_price = product.uom_id._compute_price(purch_price, line.product_uom) to_cur = line.currency_id or line.order_id.currency_id - line.purchase_price = product.cost_currency_id._convert( - from_amount=purch_price, - to_currency=to_cur, - company=line.company_id or self.env.company, - date=line.order_id.date_order or fields.Date.today(), - round=False, - ) if to_cur and purch_price else purch_price + line.purchase_price = line._convert_to_sol_currency( + purch_price, + product.cost_currency_id, + ) return super(SaleOrderLine, lines_without_moves)._compute_purchase_price() diff --git a/addons/sale_timesheet_margin/models/sale_order_line.py b/addons/sale_timesheet_margin/models/sale_order_line.py index 21dc60e7fa7..09012c7b538 100644 --- a/addons/sale_timesheet_margin/models/sale_order_line.py +++ b/addons/sale_timesheet_margin/models/sale_order_line.py @@ -23,10 +23,20 @@ class SaleOrderLine(models.Model): for line in timesheet_sols: line = line.with_company(line.company_id) product_cost = mapped_sol_timesheet_amount.get(line.id, line.product_id.standard_price) - if line.product_id.uom_id != line.company_id.project_time_mode_id and\ - line.product_id.uom_id.category_id.id == line.company_id.project_time_mode_id.category_id.id: + if ( + line.product_id.uom_id != line.company_id.project_time_mode_id + and line.product_id.uom_id.category_id.id == line.company_id.project_time_mode_id.category_id.id + ): product_cost = line.company_id.project_time_mode_id._compute_quantity( product_cost, line.product_id.uom_id ) - line.purchase_price = line._convert_price(product_cost, line.product_id.uom_id) + + # Convert the cost to the line UoM + product_cost = line.product_id.uom_id._compute_price( + line.product_id.standard_price, + line.product_uom, + ) + + line.purchase_price = line._convert_to_sol_currency( + product_cost, line.product_id.cost_currency_id)