From c11225c68175d4c18fdd2ca11da5c56d69fb7281 Mon Sep 17 00:00:00 2001 From: Enric Tobella Date: Sat, 19 Aug 2023 08:58:12 +0200 Subject: [PATCH] [FIX] account_reconcile_oca: Make it work with foreign currency --- .../models/account_account_reconcile.py | 4 +- .../models/account_bank_statement_line.py | 35 +++++++++----- .../models/account_reconcile_abstract.py | 46 ++++++++++++++----- .../js/widgets/reconcile_data_widget.esm.js | 19 ++++++++ .../static/src/xml/reconcile.xml | 9 ++++ .../tests/test_bank_account_reconcile.py | 33 +++++++++++++ .../views/account_account_reconcile.xml | 3 ++ .../views/account_bank_statement_line.xml | 2 + .../views/account_move_line.xml | 3 ++ 9 files changed, 130 insertions(+), 24 deletions(-) diff --git a/account_reconcile_oca/models/account_account_reconcile.py b/account_reconcile_oca/models/account_account_reconcile.py index 5129f53856..8ce3130113 100644 --- a/account_reconcile_oca/models/account_account_reconcile.py +++ b/account_reconcile_oca/models/account_account_reconcile.py @@ -21,7 +21,6 @@ class AccountAccountReconcile(models.Model): account_id = fields.Many2one("account.account", readonly=True) name = fields.Char(readonly=True) is_reconciled = fields.Boolean(readonly=True) - currency_id = fields.Many2one("res.currency", readonly=True) @property def _table_query(self): @@ -50,7 +49,8 @@ def _select(self): a.id as account_id, FALSE as is_reconciled, aml.currency_id as currency_id, - a.company_id + a.company_id, + false as foreign_currency_id """ def _from(self): diff --git a/account_reconcile_oca/models/account_bank_statement_line.py b/account_reconcile_oca/models/account_bank_statement_line.py index 64c2cf2ce0..4a55cd4e84 100644 --- a/account_reconcile_oca/models/account_bank_statement_line.py +++ b/account_reconcile_oca/models/account_bank_statement_line.py @@ -144,6 +144,9 @@ def _onchange_add_account_move_line_id(self): self.add_account_move_line_id = False def _recompute_suspense_line(self, data, reconcile_auxiliary_id, manual_reference): + reconcile_auxiliary_id = self._compute_exchange_rate( + data, reconcile_auxiliary_id + ) can_reconcile = True total_amount = 0 new_data = [] @@ -188,7 +191,9 @@ def _recompute_suspense_line(self, data, reconcile_auxiliary_id, manual_referenc "credit": total_amount if total_amount > 0 else 0.0, "debit": -total_amount if total_amount < 0 else 0.0, "kind": "suspense", - "currency_id": self.currency_id.id, + "currency_id": self.company_id.currency_id.id, + "line_currency_id": self.company_id.currency_id.id, + "currency_amount": -total_amount, } reconcile_auxiliary_id += 1 new_data.append(suspense_line) @@ -204,7 +209,7 @@ def _check_line_changed(self, line): return ( not float_is_zero( self.manual_amount - line["amount"], - precision_digits=self.currency_id.decimal_places, + precision_digits=self.company_id.currency_id.decimal_places, ) or self.manual_account_id.id != line["account_id"][0] or self.manual_name != line["name"] @@ -366,6 +371,9 @@ def _reconcile_data_by_model(self, data, reconcile_model, reconcile_auxiliary_id .browse(line["account_id"]) .name_get()[0], "date": fields.Date.to_string(self.date), + "line_currency_id": self.company_id.currency_id.id, + "currency_id": self.company_id.currency_id.id, + "currency_amount": amount, } ) reconcile_auxiliary_id += 1 @@ -376,15 +384,16 @@ def _reconcile_data_by_model(self, data, reconcile_model, reconcile_auxiliary_id new_data.append(new_line) return new_data, reconcile_auxiliary_id - def _compute_exchange_rate(self, data): - reconcile_auxiliary_id = 1 - if not self.foreign_currency_id or self.is_reconciled: + def _compute_exchange_rate(self, data, reconcile_auxiliary_id): + foreign_currency = ( + self.currency_id != self.company_id.currency_id + or self.foreign_currency_id + or any(line["currency_id"] != line["line_currency_id"] for line in data) + ) + if not foreign_currency or self.is_reconciled: return reconcile_auxiliary_id currency = self.journal_id.currency_id or self.company_id.currency_id - currency_amount = self.foreign_currency_id._convert( - self.amount_currency, currency, self.company_id, self.date - ) - amount = sum(d["amount"] for d in data) - currency_amount + amount = sum(d.get("net_amount", 0) for d in data) if not currency.is_zero(amount): account = self.company_id.expense_currency_exchange_account_id if amount > 0: @@ -402,6 +411,8 @@ def _compute_exchange_rate(self, data): "debit": -amount if amount < 0 else 0.0, "kind": "other", "currency_id": self.currency_id.id, + "line_currency_id": self.currency_id.id, + "currency_amount": -amount, } ) reconcile_auxiliary_id += 1 @@ -410,7 +421,7 @@ def _compute_exchange_rate(self, data): def _default_reconcile_data(self): liquidity_lines, suspense_lines, other_lines = self._seek_for_lines() data = [self._get_reconcile_line(line, "liquidity") for line in liquidity_lines] - reconcile_auxiliary_id = self._compute_exchange_rate(data) + reconcile_auxiliary_id = 1 res = ( self.env["account.reconcile.model"] .search([("rule_type", "in", ["invoice_matching", "writeoff_suggestion"])]) @@ -605,7 +616,7 @@ def create(self, mvals): record._get_reconcile_line(line, "liquidity") for line in liquidity_lines ] - reconcile_auxiliary_id = record._compute_exchange_rate(data) + reconcile_auxiliary_id = 1 if res.get("status", "") == "write_off": data = record._recompute_suspense_line( *record._reconcile_data_by_model( @@ -664,6 +675,8 @@ def button_manual_reference_full_paid(self): "debit": -total_amount if total_amount < 0 else 0.0, "kind": "other", "currency_id": line["currency_id"], + "line_currency_id": line["currency_id"], + "currency_amount": -total_amount, } ) reconcile_auxiliary_id += 1 diff --git a/account_reconcile_oca/models/account_reconcile_abstract.py b/account_reconcile_oca/models/account_reconcile_abstract.py index 59fdc03f76..719967f744 100644 --- a/account_reconcile_oca/models/account_reconcile_abstract.py +++ b/account_reconcile_oca/models/account_reconcile_abstract.py @@ -27,21 +27,42 @@ class AccountReconcileAbstract(models.AbstractModel): default=False, prefetch=False, ) + currency_id = fields.Many2one("res.currency", readonly=True) + foreign_currency_id = fields.Many2one("res.currency") + company_currency_id = fields.Many2one( + "res.currency", related="company_id.currency_id" + ) def _get_reconcile_line(self, line, kind, is_counterpart=False, max_amount=False): - original_amount = amount = line.debit - line.credit - if is_counterpart: - original_amount = amount = ( - line.amount_residual_currency or line.amount_residual - ) - if max_amount: - if amount > max_amount > 0: - amount = max_amount - if amount < max_amount < 0: - amount = max_amount + date = self.date if "date" in self._fields else line.date + original_amount = amount = net_amount = line.debit - line.credit + amount_currency = self.company_id.currency_id if is_counterpart: + amount = line.amount_residual_currency or line.amount_residual + amount_currency = line.currency_id or self.company_id.currency_id + original_amount = net_amount = line.amount_residual + if max_amount: + currency_max_amount = self.company_id.currency_id._convert( + max_amount, amount_currency, self.company_id, line.date + ) + if amount > currency_max_amount > 0: + amount = currency_max_amount + net_amount = max_amount + if amount < currency_max_amount < 0: + amount = currency_max_amount + net_amount = max_amount amount = -amount original_amount = -original_amount + net_amount = -net_amount + else: + amount_currency = line.currency_id + amount = self.company_id.currency_id._convert( + amount, amount_currency, self.company_id, date + ) + currency_amount = amount + amount = amount_currency._convert( + amount, self.company_id.currency_id, self.company_id, date + ) vals = { "reference": "account.move.line;%s" % line.id, "id": line.id, @@ -52,7 +73,10 @@ def _get_reconcile_line(self, line, kind, is_counterpart=False, max_amount=False "debit": amount if amount > 0 else 0.0, "credit": -amount if amount < 0 else 0.0, "amount": amount, - "currency_id": line.currency_id.id, + "net_amount": amount - net_amount, + "currency_id": self.company_id.currency_id.id, + "line_currency_id": line.currency_id.id, + "currency_amount": currency_amount, "analytic_distribution": line.analytic_distribution, "kind": kind, } diff --git a/account_reconcile_oca/static/src/js/widgets/reconcile_data_widget.esm.js b/account_reconcile_oca/static/src/js/widgets/reconcile_data_widget.esm.js index 177f51a332..0f64280bbe 100644 --- a/account_reconcile_oca/static/src/js/widgets/reconcile_data_widget.esm.js +++ b/account_reconcile_oca/static/src/js/widgets/reconcile_data_widget.esm.js @@ -7,6 +7,18 @@ import session from "web.session"; const {Component} = owl; export class AccountReconcileDataWidget extends Component { + setup() { + super.setup(...arguments); + this.foreignCurrency = + this.props && + this.props.record && + (this.props.record.data.foreign_currency_id || + this.props.record.data.currency_id[0] !== + this.props.record.data.company_currency_id[0] || + this.props.record.data[this.props.name].data.some( + (item) => item.line_currency_id !== item.currency_id + )); + } getReconcileLines() { var data = this.props.record.data[this.props.name].data; for (var line in data) { @@ -31,6 +43,13 @@ export class AccountReconcileDataWidget extends Component { currency: session.get_currency(data[line].currency_id), } ); + data[line].amount_currency_format = fieldUtils.format.monetary( + data[line].currency_amount, + undefined, + { + currency: session.get_currency(data[line].line_currency_id), + } + ); if (data[line].original_amount) { data[line].original_amount_format = fieldUtils.format.monetary( data[line].original_amount, diff --git a/account_reconcile_oca/static/src/xml/reconcile.xml b/account_reconcile_oca/static/src/xml/reconcile.xml index 9da79bdee5..b8af126106 100644 --- a/account_reconcile_oca/static/src/xml/reconcile.xml +++ b/account_reconcile_oca/static/src/xml/reconcile.xml @@ -62,6 +62,9 @@ Partner Date Label + + Amount in currency + Debit Credit @@ -90,6 +93,12 @@ t-if="reconcile_line.name" /> + + + diff --git a/account_reconcile_oca/tests/test_bank_account_reconcile.py b/account_reconcile_oca/tests/test_bank_account_reconcile.py index 1071f452f8..dd965c044d 100644 --- a/account_reconcile_oca/tests/test_bank_account_reconcile.py +++ b/account_reconcile_oca/tests/test_bank_account_reconcile.py @@ -920,3 +920,36 @@ def test_partner_name_with_parent(self): .partner_id, parent_partner, ) + + def test_journal_foreign_currency(self): + inv1 = self.create_invoice(currency_id=self.currency_usd_id, invoice_amount=100) + bank_stmt = self.acc_bank_stmt_model.create( + { + "company_id": self.env.ref("base.main_company").id, + "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-15"), + } + ) + with Form( + bank_stmt_line, + view="account_reconcile_oca.bank_statement_line_form_reconcile_view", + ) as f: + self.assertFalse(f.can_reconcile) + f.add_account_move_line_id = inv1.line_ids.filtered( + lambda l: l.account_id.account_type == "asset_receivable" + ) + self.assertFalse(f.add_account_move_line_id) + self.assertTrue(f.can_reconcile) + self.assertTrue(bank_stmt_line.can_reconcile) + bank_stmt_line.reconcile_bank_line() + self.assertEqual(0, inv1.amount_residual) diff --git a/account_reconcile_oca/views/account_account_reconcile.xml b/account_reconcile_oca/views/account_account_reconcile.xml index 63cf568765..5650c43ca9 100644 --- a/account_reconcile_oca/views/account_account_reconcile.xml +++ b/account_reconcile_oca/views/account_account_reconcile.xml @@ -12,6 +12,9 @@
+ + +