From d849155c1406f9c6c95ee4ec821f08730a665bf3 Mon Sep 17 00:00:00 2001 From: Laurent Smet Date: Tue, 17 May 2022 13:40:04 +0000 Subject: [PATCH] [FIX] account: Fix the register payment wizard in multi-currencies - Create an invoice of 1200 MXN = 300 USD - Register a payment, select USD at a date at which 1200 MXN = 400 USD => The amount is incorrect, a difference is computed but it should be zero. closes odoo/odoo#91634 X-original-commit: d88f630dcb7f98a45c9efe7f2439a8732832c77c Signed-off-by: Quentin De Paoli --- .../tests/test_account_payment_register.py | 37 ++++-- .../wizard/account_payment_register.py | 106 ++++++++---------- 2 files changed, 70 insertions(+), 73 deletions(-) diff --git a/addons/account/tests/test_account_payment_register.py b/addons/account/tests/test_account_payment_register.py index 94fa9b8e7ed..f277ffc8f1e 100644 --- a/addons/account/tests/test_account_payment_register.py +++ b/addons/account/tests/test_account_payment_register.py @@ -364,13 +364,8 @@ class TestAccountPaymentRegister(AccountTestInvoicingCommon): 'group_payment': False, 'amount': 1200.0 }) + self.assertRecordValues(payment_register, [{'payment_difference': 1800.0}]) payments = payment_register._create_payments() - self.assertRecordValues(payment_register, [ - { - 'payment_difference': 1800.0 # Test _compute_payment_difference - } - ]) - self.assertRecordValues(payments, [ { 'ref': 'INV/2017/00001', @@ -1041,11 +1036,20 @@ class TestAccountPaymentRegister(AccountTestInvoicingCommon): # Payment of 600 USD (equivalent to 1200 Gol in 2017). # 600.0 USD should be computed correctly to fully paid the invoices. - payment = self.env['account.payment.register']\ + wizard = self.env['account.payment.register']\ .with_context(active_model='account.move', active_ids=invoice.ids)\ - .create({'currency_id': self.company_data['currency'].id})\ - ._create_payments() + .create({ + 'currency_id': self.company_data['currency'].id, + 'payment_date': '2017-01-01', + }) + self.assertRecordValues(wizard, [{ + 'amount': 600.0, + 'payment_difference': 0.0, + 'currency_id': self.company_data['currency'].id, + }]) + + payment = wizard._create_payments() lines = (invoice + payment.move_id).line_ids.filtered(lambda x: x.account_internal_type == 'receivable') self.assertRecordValues(lines, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'currency_id': self.currency_data['currency'].id, 'reconciled': True}, @@ -1066,11 +1070,20 @@ class TestAccountPaymentRegister(AccountTestInvoicingCommon): # Payment of 600 USD = 1200 Gol. # 1200.0 Gol should be computed correctly to fully paid the invoices. - payment = self.env['account.payment.register']\ + wizard = self.env['account.payment.register']\ .with_context(active_model='account.move', active_ids=invoice.ids)\ - .create({'currency_id': self.currency_data['currency'].id})\ - ._create_payments() + .create({ + 'currency_id': self.currency_data['currency'].id, + 'payment_date': '2017-01-01', + }) + self.assertRecordValues(wizard, [{ + 'amount': 1200.0, + 'payment_difference': 0.0, + 'currency_id': self.currency_data['currency'].id, + }]) + + payment = wizard._create_payments() lines = (invoice + payment.move_id).line_ids.filtered(lambda x: x.account_internal_type == 'receivable') self.assertRecordValues(lines, [ {'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'currency_id': self.company_data['currency'].id, 'reconciled': True}, diff --git a/addons/account/wizard/account_payment_register.py b/addons/account/wizard/account_payment_register.py index 6ba1ca4dc96..969dc509785 100644 --- a/addons/account/wizard/account_payment_register.py +++ b/addons/account/wizard/account_payment_register.py @@ -414,42 +414,52 @@ class AccountPaymentRegister(models.TransientModel): wizard.show_partner_bank_account = wizard.payment_method_line_id.code in self.env['account.payment']._get_method_codes_using_bank_account() wizard.require_partner_bank_account = wizard.payment_method_line_id.code in self.env['account.payment']._get_method_codes_needing_bank_account() + def _get_total_amount_in_wizard_currency_to_full_reconcile(self, batch_result): + """ Compute the total amount needed in the currency of the wizard to fully reconcile the batch of journal + items passed as parameter. + + :param batch_result: A batch returned by '_get_batches'. + :return: An amount in the currency of the wizard. + """ + self.ensure_one() + comp_curr = self.company_id.currency_id + if self.source_currency_id == self.currency_id: + # Same currency. + return self.source_amount_currency + elif self.source_currency_id != comp_curr and self.currency_id == comp_curr: + # Foreign currency on source line but the company currency one on the opposite line. + return self.source_currency_id._convert( + self.source_amount_currency, + comp_curr, + self.company_id, + self.payment_date, + ) + elif self.source_currency_id == comp_curr and self.currency_id != comp_curr: + # Company currency on source line but a foreign currency one on the opposite line. + return abs(sum( + comp_curr._convert( + aml.amount_residual, + self.currency_id, + self.company_id, + aml.date, + ) + for aml in batch_result['lines'] + )) + else: + # Foreign currency on payment different than the one set on the journal entries. + return comp_curr._convert( + self.source_amount, + self.currency_id, + self.company_id, + self.payment_date, + ) + @api.depends('can_edit_wizard', 'source_amount', 'source_amount_currency', 'source_currency_id', 'company_id', 'currency_id', 'payment_date') def _compute_amount(self): for wizard in self: if wizard.source_currency_id and wizard.can_edit_wizard: - comp_curr = wizard.company_id.currency_id - if wizard.source_currency_id == wizard.currency_id: - # Same currency. - wizard.amount = wizard.source_amount_currency - elif wizard.source_currency_id != comp_curr and wizard.currency_id == comp_curr: - # Foreign currency on source line but the company currency one on the opposite line. - wizard.amount = wizard.source_currency_id._convert( - wizard.source_amount_currency, - comp_curr, - wizard.company_id, - wizard.payment_date, - ) - elif wizard.source_currency_id == comp_curr and wizard.currency_id != comp_curr: - # Company currency on source line but a foreign currency one on the opposite line. - batch = wizard._get_batches()[0] - wizard.amount = abs(sum( - comp_curr._convert( - aml.amount_residual, - wizard.currency_id, - wizard.company_id, - aml.date, - ) - for aml in batch['lines'] - )) - else: - # Foreign currency on payment different than the one set on the journal entries. - wizard.amount = comp_curr._convert( - wizard.source_amount, - wizard.currency_id, - wizard.company_id, - wizard.payment_date, - ) + batch_result = wizard._get_batches()[0] + wizard.amount = wizard._get_total_amount_in_wizard_currency_to_full_reconcile(batch_result) else: # The wizard is not editable so no partial payment allowed and then, 'amount' is not used. wizard.amount = None @@ -458,35 +468,9 @@ class AccountPaymentRegister(models.TransientModel): def _compute_payment_difference(self): for wizard in self: if wizard.can_edit_wizard: - comp_curr = wizard.company_id.currency_id - if wizard.source_currency_id == wizard.currency_id: - # Same currency. - wizard.payment_difference = wizard.source_amount_currency - wizard.amount - elif wizard.source_currency_id != comp_curr and wizard.currency_id == comp_curr: - # Foreign currency on source line but the company currency one on the opposite line. - rate = self.env['res.currency']._get_conversion_rate( - wizard.company_id.currency_id, - wizard.source_currency_id, - wizard.company_id, - wizard.payment_date, - ) - amount_payment_foreign_curr = wizard.source_currency_id.round(wizard.amount * rate) - residual_foreign_curr = wizard.source_amount_currency - amount_payment_foreign_curr - wizard.payment_difference = wizard.company_id.currency_id.round(residual_foreign_curr / rate) - elif wizard.source_currency_id == comp_curr and wizard.currency_id != comp_curr: - # Company currency on source line but a foreign currency one on the opposite line. - rate = self.env['res.currency']._get_conversion_rate( - wizard.company_id.currency_id, - wizard.source_currency_id, - wizard.company_id, - wizard.payment_date, - ) - amount_to_pay_foreign_curr = wizard.currency_id.round(wizard.source_amount * rate) - wizard.payment_difference = amount_to_pay_foreign_curr - wizard.amount - else: - # Foreign currency on payment different than the one set on the journal entries. - amount_payment_currency = wizard.company_id.currency_id._convert(wizard.source_amount, wizard.currency_id, wizard.company_id, wizard.payment_date) - wizard.payment_difference = amount_payment_currency - wizard.amount + batch_result = wizard._get_batches()[0] + total_amount_residual_in_wizard_currency = wizard._get_total_amount_in_wizard_currency_to_full_reconcile(batch_result) + wizard.payment_difference = total_amount_residual_in_wizard_currency - wizard.amount else: wizard.payment_difference = 0.0