diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index c729a0f9933..e8d61edd853 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -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 diff --git a/addons/account/tests/test_payment.py b/addons/account/tests/test_payment.py index 1c9da598ffd..08235a14369 100644 --- a/addons/account/tests/test_payment.py +++ b/addons/account/tests/test_payment.py @@ -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) diff --git a/addons/account/tests/test_reconciliation.py b/addons/account/tests/test_reconciliation.py index 8d5c5500416..d28a673c0e7 100644 --- a/addons/account/tests/test_reconciliation.py +++ b/addons/account/tests/test_reconciliation.py @@ -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')