diff --git a/odoo/addons/base/models/res_currency.py b/odoo/addons/base/models/res_currency.py index b7f5f5ab06c..cf3289adddb 100644 --- a/odoo/addons/base/models/res_currency.py +++ b/odoo/addons/base/models/res_currency.py @@ -139,18 +139,19 @@ class Currency(models.Model): currency.is_current_company_currency = self.env.company.root_id.currency_id == currency @api.depends('rate_ids.rate') + @api.depends_context('to_currency', 'date', 'company', 'company_id') def _compute_current_rate(self): date = self._context.get('date') or fields.Date.context_today(self) company = self.env['res.company'].browse(self._context.get('company_id')) or self.env.company company = company.root_id + to_currency = self.browse(self.env.context.get('to_currency')) or company.currency_id # the subquery selects the last rate before 'date' for the given currency/company - currency_rates = self._get_rates(company, date) - last_rate = self.env['res.currency.rate']._get_last_rates_for_companies(company) + currency_rates = (self + to_currency)._get_rates(self.env.company, date) for currency in self: - currency.rate = (currency_rates.get(currency.id) or 1.0) / last_rate[company] + currency.rate = currency_rates.get(to_currency.id) / currency_rates.get(currency.id) currency.inverse_rate = 1 / currency.rate if currency != company.currency_id: - currency.rate_string = '1 %s = %.6f %s' % (company.currency_id.name, currency.rate, currency.name) + currency.rate_string = '1 %s = %.6f %s' % (to_currency.name, currency.rate, currency.name) else: currency.rate_string = '' @@ -255,12 +256,14 @@ class Currency(models.Model): return tools.float_is_zero(amount, precision_rounding=self.rounding) @api.model - def _get_conversion_rate(self, from_currency, to_currency, company, date): - currency_rates = (from_currency + to_currency)._get_rates(company, date) - res = currency_rates.get(to_currency.id) / currency_rates.get(from_currency.id) - return res + def _get_conversion_rate(self, from_currency, to_currency, company=None, date=None): + if from_currency == to_currency: + return 1 + company = company or self.env.company + date = date or fields.Date.context_today(self) + return from_currency.with_company(company).with_context(to_currency=to_currency.id, date=str(date)).rate - def _convert(self, from_amount, to_currency, company, date, round=True): + def _convert(self, from_amount, to_currency, company=None, date=None, round=True): # noqa: A002 builtin-argument-shadowing """Returns the converted amount of ``from_amount``` from the currency ``self`` to the currency ``to_currency`` for the given ``date`` and company. @@ -272,12 +275,8 @@ class Currency(models.Model): self, to_currency = self or to_currency, to_currency or self assert self, "convert amount from unknown currency" assert to_currency, "convert amount to unknown currency" - assert company, "convert amount from unknown company" - assert date, "convert amount from unknown date" # apply conversion rate - if self == to_currency: - to_amount = from_amount - elif from_amount: + if from_amount: to_amount = from_amount * self._get_conversion_rate(self, to_currency, company, date) else: return 0.0 diff --git a/odoo/addons/base/tests/test_res_currency.py b/odoo/addons/base/tests/test_res_currency.py index 6a4fda7b8c3..741a93e9fce 100644 --- a/odoo/addons/base/tests/test_res_currency.py +++ b/odoo/addons/base/tests/test_res_currency.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. from lxml import etree +from odoo import Command from odoo.tests.common import TransactionCase -class TestResConfig(TransactionCase): +class TestResCurrency(TransactionCase): def test_view_company_rate_label(self): """Tests the label of the company_rate and inverse_company_rate fields are well set according to the company currency in the currency form view and the currency rate list view. @@ -21,3 +22,48 @@ class TestResConfig(TransactionCase): node_inverse_company_rate = tree.xpath('//field[@name="inverse_company_rate"]')[0] self.assertEqual(node_company_rate.get('string'), f'Unit per {expected_currency}') self.assertEqual(node_inverse_company_rate.get('string'), f'{expected_currency} per Unit') + + def test_currency_cache(self): + currencyA, currencyB = self.env['res.currency'].create([{ + 'name': 'AAA', + 'symbol': 'AAA', + 'rate_ids': [Command.create({'name': '2010-10-10', 'rate': 1})] + }, { + 'name': 'BBB', + 'symbol': 'BBB', + 'rate_ids': [ + Command.create({'name': '2010-10-10', 'rate': 1}), + Command.create({'name': '2011-11-11', 'rate': 2}), + ], + }]) + + self.assertEqual(currencyA._convert( + from_amount=100, + to_currency=currencyB, + company=self.env.company, + date='2010-10-10', + ), 100) + + # only one query is done when changing the convert params + with self.assertQueryCount(1): + self.assertEqual(currencyA._convert( + from_amount=100, + to_currency=currencyB, + company=self.env.company, + date='2011-11-11', + ), 200) + + # cache holds multiple values + with self.assertQueryCount(0): + self.assertEqual(currencyA._convert( + from_amount=100, + to_currency=currencyB, + company=self.env.company, + date='2010-10-10', + ), 100) + self.assertEqual(currencyA._convert( + from_amount=100, + to_currency=currencyB, + company=self.env.company, + date='2011-11-11', + ), 200)