[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:
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user