From cd165e299d3e36b5d231810f194003522dd432fd Mon Sep 17 00:00:00 2001 From: Florian da Costa Date: Tue, 8 Oct 2024 12:44:09 +0200 Subject: [PATCH] [FIX] account_reconcile_oca : foreign currency reconcile with late currency rate It is possible that the statement line in foreign currency is created before the rate of the day is updated in Odoo. In this case we need to take the real rate of the statement line to comput the exchange rate --- .../models/account_bank_statement_line.py | 15 +++- .../tests/test_bank_account_reconcile.py | 72 +++++++++++++++++++ 2 files changed, 85 insertions(+), 2 deletions(-) diff --git a/account_reconcile_oca/models/account_bank_statement_line.py b/account_reconcile_oca/models/account_bank_statement_line.py index ad1d298c..a80eec19 100644 --- a/account_reconcile_oca/models/account_bank_statement_line.py +++ b/account_reconcile_oca/models/account_bank_statement_line.py @@ -8,6 +8,7 @@ from dateutil.relativedelta import relativedelta from odoo import Command, _, api, fields, models from odoo.exceptions import UserError +from odoo.fields import first from odoo.tools import float_is_zero @@ -415,7 +416,11 @@ class AccountBankStatementLine(models.Model): @api.onchange("manual_amount_in_currency") def _onchange_manual_amount_in_currency(self): - if self.manual_line_id.exists() and self.manual_line_id: + if ( + self.manual_line_id.exists() + and self.manual_line_id + and self.manual_kind != "liquidity" + ): self.manual_amount = self.manual_in_currency_id._convert( self.manual_amount_in_currency, self.company_id.currency_id, @@ -1046,7 +1051,7 @@ class AccountBankStatementLine(models.Model): return reconcile_auxiliary_id, new_vals def _get_exchange_rate_amount(self, amount, currency_amount, currency, line): - if self.foreign_currency_id: + if self.foreign_currency_id == currency: # take real rate of statement line to compute the exchange rate gain/loss real_rate = self.amount / self.amount_currency to_amount_journal_currency = currency_amount * real_rate @@ -1057,6 +1062,12 @@ class AccountBankStatementLine(models.Model): self.date, ) to_amount = self.company_id.currency_id.round(to_amount_company_currency) + elif self.currency_id == currency and not self.foreign_currency_id: + liquidity_lines, _suspense_lines, _other_lines = self._seek_for_lines() + real_rate = ( + first(liquidity_lines).balance / first(liquidity_lines).amount_currency + ) + to_amount = self.company_id.currency_id.round(currency_amount * real_rate) else: to_amount = currency._convert( currency_amount, diff --git a/account_reconcile_oca/tests/test_bank_account_reconcile.py b/account_reconcile_oca/tests/test_bank_account_reconcile.py index 72649a15..f9533ea3 100644 --- a/account_reconcile_oca/tests/test_bank_account_reconcile.py +++ b/account_reconcile_oca/tests/test_bank_account_reconcile.py @@ -1195,3 +1195,75 @@ class TestReconciliationWidget(TestAccountReconciliationCommon): self.assertFalse(f.add_account_move_line_id) self.assertTrue(f.can_reconcile) self.assertEqual(3, len(f.reconcile_data_info["data"])) + + def test_invoice_foreign_currency_late_change_of_rate(self): + # Test we can reconcile lines in foreign currency even if the rate was updated + # late in odoo, meaning the statement line was created and the rate was updated + # in odoo after that. + self.env["res.currency.rate"].create( + { + "currency_id": self.env.ref("base.USD").id, + "name": time.strftime("%Y-07-14"), + "rate": 1.15, + } + ) + self.env["res.currency.rate"].create( + { + "currency_id": self.env.ref("base.USD").id, + "name": time.strftime("%Y-07-15"), + "rate": 1.2, + } + ) + inv1 = self._create_invoice( + currency_id=self.currency_usd_id, + invoice_amount=100, + date_invoice=time.strftime("%Y-07-14"), + auto_validate=True, + ) + bank_stmt = self.acc_bank_stmt_model.create( + { + "journal_id": self.bank_journal_usd.id, + "date": time.strftime("%Y-07-15"), + "name": "test", + } + ) + bank_stmt_line = self.acc_bank_stmt_line_model.create( + { + "name": "testLine", + "journal_id": self.bank_journal_usd.id, + "statement_id": bank_stmt.id, + "amount": 100, + "date": time.strftime("%Y-07-16"), + } + ) + # rate of 07-16 is create after the statement line, meaning the rate of the + # statement line is the one of the 07-15 + self.env["res.currency.rate"].create( + { + "currency_id": self.env.ref("base.USD").id, + "name": time.strftime("%Y-07-16"), + "rate": 1.25, + } + ) + with Form( + bank_stmt_line, + view="account_reconcile_oca.bank_statement_line_form_reconcile_view", + ) as f: + line = f.reconcile_data_info["data"][0] + self.assertEqual( + line["currency_amount"], + 100, + ) + self.assertEqual( + line["amount"], + 83.33, + ) + f.manual_reference = "account.move.line;%s" % line["id"] + # simulate click on statement line, check amount does not recompute + self.assertEqual(f.manual_amount, 83.33) + f.add_account_move_line_id = inv1.line_ids.filtered( + lambda l: l.account_id.account_type == "asset_receivable" + ) + self.assertEqual(3, len(f.reconcile_data_info["data"])) + self.assertTrue(f.can_reconcile) + self.assertEqual(f.reconcile_data_info["data"][-1]["amount"], 3.63)