[FIX] account: Reconciling multi-currency lines with fx difference

journal items does not create Tax Cash Basis Journal Entries.
[IMP] Test Case for Fully reconciling multi-currency lines and one or
more are Exchange Rate Differences which are not creating Tax Cash
Basis Journal Entries if there were any Taxed line.

Main
-

Tax Cash Basis Journal Entry creation shall not be prompted by the
reconciliation of exchange rate difference journal items.

Explanation
-

    Company's Currency EUR

    Having issued an invoice at date Nov-21-2018 as:

    Accounts         Amount Currency         Debit(EUR)       Credit(EUR)
    ---------------------------------------------------------------------
    Expenses            5,301.00 USD         106,841.65              0.00
    Taxes                 848.16 USD          17,094.66              0.00
        Payables       -6,149.16 USD               0.00        123,936.31

    On Dec-20-2018 user issues an FX Journal Entry as:

    Accounts         Amount Currency         Debit(EUR)       Credit(EUR)
    ---------------------------------------------------------------------
    Payables                0.00 USD             167.86             0.00
        FX Gains            0.00 USD               0.00           167.86

    On Same day user records a payment for:

    Accounts         Amount Currency         Debit(EUR)       Credit(EUR)
    ---------------------------------------------------------------------
    Payables            6,149.16 USD         123,768.45              0.00
        Bank           -6,149.16 USD               0.00        123,768.45

    And then reconciles the Payables Items which shall render only one Tax
    Cash Basis Journal Entry because of the actual payment, i.e.
    amount_currency != 0:

    Accounts         Amount Currency         Debit(EUR)       Credit(EUR)
    ---------------------------------------------------------------------
    Tax Base Acc.           0.00 USD         106,841.65              0.00
        Tax Base Acc.       0.00 USD               0.00        106,841.65
    Creditable Taxes      848.16 USD          17,094.66              0.00
        Taxes            -848.16 USD               0.00         17,094.66

closes odoo/odoo#30130
This commit is contained in:
Humberto Arocha
2019-02-08 18:24:39 +00:00
parent 1bec017c15
commit 785e5f149c
2 changed files with 378 additions and 3 deletions
+14 -2
View File
@@ -823,6 +823,7 @@ class AccountMoveLine(models.Model):
to_create = []
cash_basis = debit_moves and debit_moves[0].account_id.internal_type in ('receivable', 'payable') or False
cash_basis_percentage_before_rec = {}
dc_vals ={}
while (debit_moves and credit_moves):
debit_move = debit_moves[0]
credit_move = credit_moves[0]
@@ -830,6 +831,7 @@ class AccountMoveLine(models.Model):
# We need those temporary value otherwise the computation might be wrong below
temp_amount_residual = min(debit_move.amount_residual, -credit_move.amount_residual)
temp_amount_residual_currency = min(debit_move.amount_residual_currency, -credit_move.amount_residual_currency)
dc_vals[(debit_move.id, credit_move.id)] = (debit_move, credit_move, temp_amount_residual_currency)
amount_reconcile = min(debit_move[field], -credit_move[field])
#Remove from recordset the one(s) that will be totally reconciled
@@ -867,11 +869,21 @@ class AccountMoveLine(models.Model):
'currency_id': currency,
})
cash_basis_subjected = []
part_rec = self.env['account.partial.reconcile']
with self.env.norecompute():
for partial_rec_dict in to_create:
new_rec = self.env['account.partial.reconcile'].create(partial_rec_dict)
part_rec += new_rec
debit_move, credit_move, amount_residual_currency = dc_vals[partial_rec_dict['debit_move_id'], partial_rec_dict['credit_move_id']]
# /!\ NOTE: Exchange rate differences shouldn't create cash basis entries
# i. e: we don't really receive/give money in a customer/provider fashion
# Since those are not subjected to cash basis computation we process them first
if not amount_residual_currency and debit_move.currency_id and credit_move.currency_id:
part_rec.create(partial_rec_dict)
else:
cash_basis_subjected.append(partial_rec_dict)
for after_rec_dict in cash_basis_subjected:
new_rec = part_rec.create(after_rec_dict)
if cash_basis:
new_rec.create_tax_cash_basis_entry(cash_basis_percentage_before_rec)
self.recompute()
+364 -1
View File
@@ -39,7 +39,8 @@ class TestReconciliation(AccountingTestCase):
self.bank_journal_usd = self.env['account.journal'].create({'name': 'Bank US', 'type': 'bank', 'code': 'BNK68', 'currency_id': self.currency_usd_id})
self.account_usd = self.bank_journal_usd.default_debit_account_id
self.fx_journal = self.env['res.users'].browse(self.env.uid).company_id.currency_exchange_journal_id
self.diff_income_account = self.env['res.users'].browse(self.env.uid).company_id.income_currency_exchange_account_id
self.diff_expense_account = self.env['res.users'].browse(self.env.uid).company_id.expense_currency_exchange_account_id
@@ -1444,3 +1445,365 @@ class TestReconciliation(AccountingTestCase):
expected['tax_10']
)
index += 1
def test_reconciliation_cash_basis_fx_01(self):
"""
Company's Currency EUR
Having issued an invoice at date Nov-21-2018 as:
Accounts Amount Currency Debit(EUR) Credit(EUR)
---------------------------------------------------------------------
Expenses 5,301.00 USD 106,841.65 0.00
Taxes 848.16 USD 17,094.66 0.00
Payables -6,149.16 USD 0.00 123,936.31
On Dec-20-2018 user issues an FX Journal Entry as:
Accounts Amount Currency Debit(EUR) Credit(EUR)
---------------------------------------------------------------------
Payables 0.00 USD 167.86 0.00
FX Gains 0.00 USD 0.00 167.86
On Same day user records a payment for:
Accounts Amount Currency Debit(EUR) Credit(EUR)
---------------------------------------------------------------------
Payables 6,149.16 USD 123,768.45 0.00
Bank -6,149.16 USD 0.00 123,768.45
And then reconciles the Payables Items which shall render only one Tax
Cash Basis Journal Entry because of the actual payment, i.e.
amount_currency != 0:
Accounts Amount Currency Debit(EUR) Credit(EUR)
---------------------------------------------------------------------
Tax Base Acc. 0.00 USD 106,841.65 0.00
Tax Base Acc. 0.00 USD 0.00 106,841.65
Creditable Taxes 848.16 USD 17,094.66 0.00
Taxes -848.16 USD 0.00 17,094.66
"""
company = self.env.ref('base.main_company')
company.country_id = self.ref('base.us')
company.tax_cash_basis_journal_id = self.cash_basis_journal
aml_obj = self.env['account.move.line'].with_context(
check_move_validity=False)
# Purchase
purchase_move = self.env['account.move'].create({
'name': 'purchase',
'journal_id': self.purchase_journal.id,
})
aml_obj.create({
'name': 'expenseTaxed',
'account_id': self.expense_account.id,
'debit': 106841.65,
'move_id': purchase_move.id,
'tax_ids': [(4, self.tax_cash_basis.id, False)],
'currency_id': self.currency_usd_id,
'amount_currency': 5301.00,
})
aml_obj.create({
'name': 'TaxLine',
'account_id': self.tax_waiting_account.id,
'debit': 17094.66,
'move_id': purchase_move.id,
'tax_line_id': self.tax_cash_basis.id,
'currency_id': self.currency_usd_id,
'amount_currency': 848.16,
})
purchase_payable_line0 = aml_obj.create({
'name': 'Payable',
'account_id': self.account_rsa.id,
'credit': 123936.31,
'move_id': purchase_move.id,
'currency_id': self.currency_usd_id,
'amount_currency': -6149.16,
})
purchase_move.post()
# FX 01 Move
fx_move_01 = self.env['account.move'].create({
'name': 'FX 01',
'journal_id': self.fx_journal.id,
})
fx_01_payable_line = aml_obj.create({
'account_id': self.account_rsa.id,
'debit': 167.86,
'move_id': fx_move_01.id,
'currency_id': self.currency_usd_id,
'amount_currency': 0.00,
})
aml_obj.create({
'account_id': self.diff_income_account.id,
'credit': 167.86,
'move_id': fx_move_01.id,
'currency_id': self.currency_usd_id,
'amount_currency': 0.00,
})
fx_move_01.post()
# Payment Move
payment_move = self.env['account.move'].create({
'name': 'payment',
'journal_id': self.bank_journal_usd.id,
})
payment_payable_line = aml_obj.create({
'account_id': self.account_rsa.id,
'debit': 123768.45,
'move_id': payment_move.id,
'currency_id': self.currency_usd_id,
'amount_currency': 6149.16,
})
aml_obj.create({
'account_id': self.account_usd.id,
'credit': 123768.45,
'move_id': payment_move.id,
'currency_id': self.currency_usd_id,
'amount_currency': -6149.16,
})
payment_move.post()
to_reconcile = (
(purchase_move + payment_move + fx_move_01).mapped('line_ids')
.filtered(lambda l: l.account_id.internal_type == 'payable'))
to_reconcile.reconcile()
# check reconciliation in Payable account
self.assertTrue(purchase_payable_line0.full_reconcile_id.exists())
self.assertEqual(
purchase_payable_line0.full_reconcile_id.reconciled_line_ids,
purchase_payable_line0 + fx_01_payable_line + payment_payable_line)
# check cash basis
cash_basis_moves = self.env['account.move'].search(
[('journal_id', '=', self.cash_basis_journal.id)])
self.assertEqual(len(cash_basis_moves), 1)
cash_basis_aml_ids = cash_basis_moves.mapped('line_ids')
self.assertEqual(len(cash_basis_aml_ids), 4)
# check amounts
cash_basis_move1 = cash_basis_moves.filtered(
lambda m: m.amount == 123936.31)
self.assertTrue(cash_basis_move1.exists())
# For first move
move_lines = cash_basis_move1.line_ids
base_amount_tax_lines = move_lines.filtered(
lambda l: l.account_id == self.tax_base_amount_account)
self.assertEqual(len(base_amount_tax_lines), 2)
self.assertAlmostEqual(
sum(base_amount_tax_lines.mapped('credit')), 106841.65)
self.assertAlmostEqual(
sum(base_amount_tax_lines.mapped('debit')), 106841.65)
self.assertAlmostEqual(
(move_lines - base_amount_tax_lines)
.filtered(lambda l: l.account_id == self.tax_waiting_account)
.credit, 17094.66)
self.assertAlmostEqual(
(move_lines - base_amount_tax_lines)
.filtered(lambda l: l.account_id == self.tax_final_account)
.debit, 17094.66)
def test_reconciliation_cash_basis_fx_02(self):
"""
Company's Currency EUR
Having issued an invoice at date Nov-21-2018 as:
Accounts Amount Currency Debit(EUR) Credit(EUR)
---------------------------------------------------------------------
Expenses 5,301.00 USD 106,841.65 0.00
Taxes 848.16 USD 17,094.66 0.00
Payables -6,149.16 USD 0.00 123,936.31
On Nov-30-2018 user issues an FX Journal Entry as:
Accounts Amount Currency Debit(EUR) Credit(EUR)
---------------------------------------------------------------------
FX Losses 0.00 USD 1.572.96 0.00
Payables 0.00 USD 0.00 1.572.96
On Dec-20-2018 user issues an FX Journal Entry as:
Accounts Amount Currency Debit(EUR) Credit(EUR)
---------------------------------------------------------------------
Payables 0.00 USD 1.740.82 0.00
FX Gains 0.00 USD 0.00 1.740.82
On Same day user records a payment for:
Accounts Amount Currency Debit(EUR) Credit(EUR)
---------------------------------------------------------------------
Payables 6,149.16 USD 123,768.45 0.00
Bank -6,149.16 USD 0.00 123,768.45
And then reconciles the Payables Items which shall render only one Tax
Cash Basis Journal Entry because of the actual payment, i.e.
amount_currency != 0:
Accounts Amount Currency Debit(EUR) Credit(EUR)
---------------------------------------------------------------------
Tax Base Acc. 0.00 USD 106,841.65 0.00
Tax Base Acc. 0.00 USD 0.00 106,841.65
Creditable Taxes 848.16 USD 17,094.66 0.00
Taxes -848.16 USD 0.00 17,094.66
"""
company = self.env.ref('base.main_company')
company.country_id = self.ref('base.us')
company.tax_cash_basis_journal_id = self.cash_basis_journal
aml_obj = self.env['account.move.line'].with_context(
check_move_validity=False)
# Purchase
purchase_move = self.env['account.move'].create({
'name': 'purchase',
'journal_id': self.purchase_journal.id,
})
aml_obj.create({
'name': 'expenseTaxed',
'account_id': self.expense_account.id,
'debit': 106841.65,
'move_id': purchase_move.id,
'tax_ids': [(4, self.tax_cash_basis.id, False)],
'currency_id': self.currency_usd_id,
'amount_currency': 5301.00,
})
aml_obj.create({
'name': 'TaxLine',
'account_id': self.tax_waiting_account.id,
'debit': 17094.66,
'move_id': purchase_move.id,
'tax_line_id': self.tax_cash_basis.id,
'currency_id': self.currency_usd_id,
'amount_currency': 848.16,
})
purchase_payable_line0 = aml_obj.create({
'name': 'Payable',
'account_id': self.account_rsa.id,
'credit': 123936.31,
'move_id': purchase_move.id,
'currency_id': self.currency_usd_id,
'amount_currency': -6149.16,
})
purchase_move.post()
# FX 01 Move
fx_move_01 = self.env['account.move'].create({
'name': 'FX 01',
'journal_id': self.fx_journal.id,
})
fx_01_payable_line = aml_obj.create({
'account_id': self.account_rsa.id,
'credit': 1572.96,
'move_id': fx_move_01.id,
'currency_id': self.currency_usd_id,
'amount_currency': 0.00,
})
aml_obj.create({
'account_id': self.diff_expense_account.id,
'debit': 1572.96,
'move_id': fx_move_01.id,
'currency_id': self.currency_usd_id,
'amount_currency': 0.00,
})
fx_move_01.post()
# FX 02 Move
fx_move_02 = self.env['account.move'].create({
'name': 'FX 02',
'journal_id': self.fx_journal.id,
})
fx_02_payable_line = aml_obj.create({
'account_id': self.account_rsa.id,
'debit': 1740.82,
'move_id': fx_move_02.id,
'currency_id': self.currency_usd_id,
'amount_currency': 0.00,
})
aml_obj.create({
'account_id': self.diff_income_account.id,
'credit': 1740.82,
'move_id': fx_move_02.id,
'currency_id': self.currency_usd_id,
'amount_currency': 0.00,
})
fx_move_02.post()
# Payment Move
payment_move = self.env['account.move'].create({
'name': 'payment',
'journal_id': self.bank_journal_usd.id,
})
payment_payable_line = aml_obj.create({
'account_id': self.account_rsa.id,
'debit': 123768.45,
'move_id': payment_move.id,
'currency_id': self.currency_usd_id,
'amount_currency': 6149.16,
})
aml_obj.create({
'account_id': self.account_usd.id,
'credit': 123768.45,
'move_id': payment_move.id,
'currency_id': self.currency_usd_id,
'amount_currency': -6149.16,
})
payment_move.post()
to_reconcile = (
(purchase_move + payment_move + fx_move_01 + fx_move_02)
.mapped('line_ids')
.filtered(lambda l: l.account_id.internal_type == 'payable'))
to_reconcile.reconcile()
# check reconciliation in Payable account
self.assertTrue(purchase_payable_line0.full_reconcile_id.exists())
self.assertEqual(
purchase_payable_line0.full_reconcile_id.reconciled_line_ids,
purchase_payable_line0 + fx_01_payable_line + fx_02_payable_line +
payment_payable_line)
# check cash basis
cash_basis_moves = self.env['account.move'].search(
[('journal_id', '=', self.cash_basis_journal.id)])
self.assertEqual(len(cash_basis_moves), 1)
cash_basis_aml_ids = cash_basis_moves.mapped('line_ids')
self.assertEqual(len(cash_basis_aml_ids), 4)
# check amounts
cash_basis_move1 = cash_basis_moves.filtered(
lambda m: m.amount == 123936.31)
self.assertTrue(cash_basis_move1.exists())
# For first move
move_lines = cash_basis_move1.line_ids
base_amount_tax_lines = move_lines.filtered(
lambda l: l.account_id == self.tax_base_amount_account)
self.assertEqual(len(base_amount_tax_lines), 2)
self.assertAlmostEqual(
sum(base_amount_tax_lines.mapped('credit')), 106841.65)
self.assertAlmostEqual(
sum(base_amount_tax_lines.mapped('debit')), 106841.65)
self.assertAlmostEqual(
(move_lines - base_amount_tax_lines)
.filtered(lambda l: l.account_id == self.tax_waiting_account)
.credit, 17094.66)
self.assertAlmostEqual(
(move_lines - base_amount_tax_lines)
.filtered(lambda l: l.account_id == self.tax_final_account)
.debit, 17094.66)