[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 <qdp@odoo.com>
This commit is contained in:
Laurent Smet
2022-05-18 16:04:49 +02:00
parent 0b75ae6adb
commit d849155c14
2 changed files with 70 additions and 73 deletions
@@ -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},
@@ -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