[FIX] account: compute of amount residual in foreign currency
When having an invoice in foreign currency and a partial payment in domestic currency (in a different date with a different exchange rate), the computed amount due is incorrect. When computing the amount_residual_currency the payment (in domestic currency) is converted to the foreign currency using the exchange rate of the invoice date, and not the one of the payment date (see56d92213b8). If the account.partial.reconcile line will have a currency_id and a amount_currency the amount_residual_currency will not be converted but simply added. This behaviour was introduced withe130606602and backported to version 11.0 with 3363d7235137f286458c734a1a0417beeb1f66bb. In version 12.0 this behaviour was partially reverted ind3d2612061. opw-2156001 closes odoo/odoo#44181 X-original-commit: 94d34e052f3757c21884fd5a57cc68ea8c51aafb Signed-off-by: Jorge Pinna Puissant (jpp) <jpp@odoo.com>
This commit is contained in:
@@ -3653,6 +3653,14 @@ class AccountMoveLine(models.Model):
|
||||
currency = credit_move.currency_id.id
|
||||
amount_reconcile_currency = temp_amount_residual_currency
|
||||
amount_reconcile = temp_amount_residual
|
||||
elif bool(debit_move.currency_id) != bool(credit_move.currency_id):
|
||||
# If only one of debit_move or credit_move has a secondary currency, also record the converted amount
|
||||
# in that secondary currency in the partial reconciliation. That allows the exchange difference entry
|
||||
# to be created, in case it is needed. It also allows to compute the amount residual in foreign currency.
|
||||
currency = debit_move.currency_id or credit_move.currency_id
|
||||
currency_date = debit_move.currency_id and credit_move.date or debit_move.date
|
||||
amount_reconcile_currency = company_currency._convert(amount_reconcile, currency, debit_move.company_id, currency_date)
|
||||
currency = currency.id
|
||||
|
||||
if cash_basis:
|
||||
tmp_set = debit_move | credit_move
|
||||
|
||||
@@ -568,3 +568,62 @@ class TestPayment(AccountTestCommon):
|
||||
self.assertEqual(name, move.name)
|
||||
self.assertTrue(transfer_move.name)
|
||||
self.assertNotEqual(name, transfer_move.name)
|
||||
|
||||
def test_partial_payment_inv_foreign_payment_domestic(self):
|
||||
"""
|
||||
Invoice of 1000$ (foreign $) at 01/01 with foreign exchange rate of 0.50000
|
||||
Payment of 500 (domestic €) at 15/01 with foreign exchange rate of 1.00000
|
||||
The residuals should be 500 in foreign and 1500 in domestic.
|
||||
"""
|
||||
company = self.env.ref('base.main_company')
|
||||
self.env['res.currency.rate'].search([]).unlink()
|
||||
self.env['res.currency.rate'].create({
|
||||
'name': time.strftime('%Y') + '-01-01',
|
||||
'rate': 1.0,
|
||||
'currency_id': self.currency_eur_id,
|
||||
'company_id': company.id
|
||||
})
|
||||
self.env['res.currency.rate'].create({
|
||||
'name': time.strftime('%Y') + '-01-01',
|
||||
'rate': 0.5, # Don't change this !
|
||||
'currency_id': self.currency_usd_id,
|
||||
'company_id': self.env.ref('base.main_company').id
|
||||
})
|
||||
self.env['res.currency.rate'].create({
|
||||
'name': time.strftime('%Y') + '-01-15',
|
||||
'rate': 1.0, # Don't change this !
|
||||
'currency_id': self.currency_usd_id,
|
||||
'company_id': self.env.ref('base.main_company').id
|
||||
})
|
||||
inv1 = self.env['account.move'].create({
|
||||
'type': 'out_invoice',
|
||||
'partner_id': self.partner_agrolait.id,
|
||||
'currency_id': self.currency_usd_id,
|
||||
'invoice_date': time.strftime('%Y') + '-01-01',
|
||||
'date': time.strftime('%Y') + '-01-01',
|
||||
'invoice_line_ids': [
|
||||
(0, 0, {'product_id': self.product.id, 'quantity': 1, 'price_unit': 1000})
|
||||
],
|
||||
})
|
||||
inv1.post()
|
||||
payment = self.payment_model.create({
|
||||
'payment_date': time.strftime('%Y') + '-01-15',
|
||||
'payment_method_id': self.payment_method_manual_in.id,
|
||||
'payment_type': 'inbound',
|
||||
'partner_type': 'customer',
|
||||
'partner_id': inv1.partner_id.id,
|
||||
'amount': 500,
|
||||
'journal_id': self.bank_journal_euro.id,
|
||||
'company_id': company.id,
|
||||
'currency_id': self.currency_eur_id,
|
||||
})
|
||||
payment.post()
|
||||
inv1_receivable = inv1.line_ids.filtered(lambda l: l.account_id.internal_type == 'receivable')
|
||||
pay_receivable = payment.move_line_ids.filtered(lambda l: l.account_id.internal_type == 'receivable')
|
||||
|
||||
self.assertEqual(inv1_receivable.balance, 2000)
|
||||
self.assertEqual(pay_receivable.balance, -500)
|
||||
|
||||
(inv1_receivable + pay_receivable).reconcile()
|
||||
self.assertEqual(inv1.amount_residual, 500)
|
||||
self.assertEqual(inv1.amount_residual_signed, 1500)
|
||||
|
||||
@@ -1855,6 +1855,7 @@ class TestReconciliationExec(TestAccountReconciliationCommon):
|
||||
'partner_type': 'customer',
|
||||
'partner_id': inv1.partner_id.id,
|
||||
'amount': 372.10,
|
||||
'payment_date': time.strftime('%Y') + '-07-01',
|
||||
'journal_id': self.bank_journal_euro.id,
|
||||
'company_id': company.id,
|
||||
})
|
||||
@@ -1884,9 +1885,7 @@ class TestReconciliationExec(TestAccountReconciliationCommon):
|
||||
self.assertEqual(inv1_receivable.full_reconcile_id, pay_receivable.full_reconcile_id)
|
||||
self.assertEqual(inv1_receivable.full_reconcile_id, move_balance_receiv.full_reconcile_id)
|
||||
|
||||
# This one may be due rounding errors
|
||||
exchange_rcv = inv1_receivable.full_reconcile_id.exchange_move_id.line_ids.filtered(lambda l: l.account_id.internal_type == 'receivable')
|
||||
self.assertEqual(exchange_rcv.amount_currency, 0.01)
|
||||
self.assertFalse(inv1_receivable.full_reconcile_id.exchange_move_id)
|
||||
|
||||
self.assertEqual(inv1.payment_state, 'paid')
|
||||
self.assertEqual(inv2.payment_state, 'paid')
|
||||
@@ -1967,8 +1966,6 @@ class TestReconciliationExec(TestAccountReconciliationCommon):
|
||||
self.assertEqual(inv1_receivable.full_reconcile_id, pay_receivable.full_reconcile_id)
|
||||
self.assertEqual(inv1_receivable.full_reconcile_id, move_balance_receiv.full_reconcile_id)
|
||||
|
||||
self.assertFalse(inv1_receivable.full_reconcile_id.exchange_move_id)
|
||||
|
||||
self.assertEqual(inv1.payment_state, 'paid')
|
||||
self.assertEqual(inv2.payment_state, 'paid')
|
||||
|
||||
@@ -2099,7 +2096,8 @@ class TestReconciliationExec(TestAccountReconciliationCommon):
|
||||
self.assertEqual(inv1_receivable.full_reconcile_id, inv2_receivable.full_reconcile_id)
|
||||
self.assertEqual(inv1_receivable.full_reconcile_id, payment_receivable.full_reconcile_id)
|
||||
|
||||
self.assertFalse(inv1_receivable.full_reconcile_id.exchange_move_id)
|
||||
exchange_rcv = inv1_receivable.full_reconcile_id.exchange_move_id.line_ids.filtered(lambda l: l.account_id.internal_type == 'receivable')
|
||||
self.assertEqual(exchange_rcv.amount_currency, 0.01)
|
||||
|
||||
self.assertEqual(inv1.payment_state, 'paid')
|
||||
self.assertEqual(inv2.payment_state, 'paid')
|
||||
@@ -2296,6 +2294,7 @@ class TestReconciliationExec(TestAccountReconciliationCommon):
|
||||
self.assertEqual(inv1_receivable.full_reconcile_id, payment_receivable.full_reconcile_id)
|
||||
self.assertEqual(move_balance_receiv.full_reconcile_id, inv1_receivable.full_reconcile_id)
|
||||
|
||||
self.assertFalse(inv1_receivable.full_reconcile_id.exchange_move_id)
|
||||
exchange_rcv = inv1_receivable.full_reconcile_id.exchange_move_id.line_ids.filtered(lambda l: l.account_id.internal_type == 'receivable')
|
||||
self.assertEqual(exchange_rcv.amount_currency, 0.01)
|
||||
|
||||
self.assertEqual(inv1.payment_state, 'paid')
|
||||
|
||||
Reference in New Issue
Block a user