[FIX] account: cash basis entry rate in multi-currencies

Flow:
- Make an invoice in foreign currency with a given rate
- Register a payment for that invoice, in company currency, worth the invoice amount_residual but with a different rate
=> The cash basis entry was wrongly taking a rate of 1 to make the conversion between the amount in foreign currency and the company currency (see new test)
=> Additionally, the amounts to report on the tax report should always be equal to the ones from the original invoice when it's fully paid. Some tests were wrong in that regard.

In order to fix the issue, the way the cash basis is handling the percentage and the way used to fix all rounding issues at the generation of the exchange difference entry is different:
=> The condition triggering the exchange difference items for the cash basis entry is now handling correctly the case when the payment is made using another currency but the amount in company currency is fully paying the invoice.
=> The complexe method '_fix_cash_basis_full_balance_coverage' defined in order to manage rounding issues on the tax report when the invoice becomes fully paid is no longer necessary since the balance of each account (base + tax account) is automatically fixed by the extra journal items added to the exchange difference.
Indeed, after the generation of the exchange difference journal entry:
- the balance of tax transfer account is now reset to zero.
- the balance of the tax account is exactly the balance of the tax transfer account defined on the invoice.
- the journal items containing the tax base amount is exactly equals to the balance of the invoice lines.

closes odoo/odoo#65939

X-original-commit: 6db073e8f1865991b79b5b31b4a4a77158aa0d15
Signed-off-by: Quentin De Paoli (qdp) <qdp@openerp.com>
Signed-off-by: Laurent Smet <smetl@users.noreply.github.com>
This commit is contained in:
qdp-odoo
2021-02-11 10:31:09 +00:00
committed by Laurent Smet
parent 78e44a23c9
commit 8f5e137df7
3 changed files with 263 additions and 178 deletions
+93 -75
View File
@@ -1960,7 +1960,6 @@ class AccountMove(models.Model):
values = {
'move': self,
'to_process_lines': self.env['account.move.line'],
'payment_term_lines': self.env['account.move.line'],
'total_balance': 0.0,
'total_residual': 0.0,
'total_amount_currency': 0.0,
@@ -1995,17 +1994,9 @@ class AccountMove(models.Model):
# Don't support the case where there is multiple involved currencies.
return None
if len(values['payment_term_lines'].account_id) > 1:
# Don't support the case where there is multiple involved receivable/payable accounts.
# It could lead to some weird situation regarding the cash basis exchange difference
# journal items.
return None
# Determine is the move is now fully paid.
if values['currency'] == self.company_id.currency_id:
values['is_fully_paid'] = values['currency'].is_zero(values['total_residual'])
else:
values['is_fully_paid'] = values['currency'].is_zero(values['total_residual_currency'])
values['is_fully_paid'] = self.company_id.currency_id.is_zero(values['total_residual']) \
or values['currency'].is_zero(values['total_residual_currency'])
return values
@@ -4364,7 +4355,7 @@ class AccountMoveLine(models.Model):
:param exchange_diff_move_vals: The current vals of the exchange difference journal entry.
'''
for move in lines.move_id:
transfer_account_vals_to_fix = {}
account_vals_to_fix = {}
move_values = move._collect_tax_cash_basis_values()
@@ -4373,40 +4364,42 @@ class AccountMoveLine(models.Model):
if not move_values or not move_values['is_fully_paid']:
continue
# The percentage of the tax cash basis entries are expressed using the company's currency and then,
# there is no exchange difference to make for such journal entry.
if move_values['currency'] == move.company_id.currency_id:
continue
# ==========================================================================
# Add the balance of all tax lines of the current move in order in order
# to compute the residual amount for each of them.
# ==========================================================================
is_exchange_diff_needed = False
for line in move_values['to_process_lines']:
if not line.tax_repartition_line_id:
continue
vals = {
'currency_id': line.currency_id.id,
'partner_id': line.partner_id.id,
'tax_ids': [(6, 0, line.tax_ids.ids)],
'tax_tag_ids': [(6, 0, line.tax_tag_ids.ids)],
'debit': line.debit,
'credit': line.credit,
}
if not line.account_id.reconcile:
is_exchange_diff_needed = True
if line.tax_repartition_line_id:
# Tax line.
grouping_key = self.env['account.partial.reconcile']._get_cash_basis_tax_line_grouping_key_from_record(line)
transfer_account_vals_to_fix[grouping_key] = {
account_vals_to_fix[grouping_key] = {
**vals,
'account_id': line.account_id.id,
'currency_id': line.currency_id.id,
'partner_id': line.partner_id.id,
'tax_base_amount': line.tax_base_amount,
'tax_repartition_line_id': line.tax_repartition_line_id.id,
'tax_ids': [(6, 0, line.tax_ids.ids)],
'tax_tag_ids': [(6, 0, line.tax_tag_ids.ids)],
'debit': line.debit,
'credit': line.credit,
}
elif line.tax_ids:
# Base line.
account_to_fix = line.company_id.account_cash_basis_base_account_id
if not account_to_fix:
continue
# No tax line on the current move.
if not is_exchange_diff_needed:
continue
grouping_key = self.env['account.partial.reconcile']._get_cash_basis_base_line_grouping_key_from_record(line, account=account_to_fix)
account_vals_to_fix[grouping_key] = {
**vals,
'account_id': account_to_fix.id,
}
# ==========================================================================
# Subtract the balance of all previously generated cash basis journal entries
@@ -4415,61 +4408,86 @@ class AccountMoveLine(models.Model):
cash_basis_moves = self.env['account.move'].search([('tax_cash_basis_move_id', '=', move.id)])
for line in cash_basis_moves.line_ids:
if not line.tax_repartition_line_id:
grouping_key = None
if line.tax_repartition_line_id:
# Tax line.
grouping_key = self.env['account.partial.reconcile']._get_cash_basis_tax_line_grouping_key_from_record(
line,
account=line.tax_line_id.cash_basis_transition_account_id,
)
elif line.tax_ids:
# Base line.
grouping_key = self.env['account.partial.reconcile']._get_cash_basis_base_line_grouping_key_from_record(
line,
account=line.company_id.account_cash_basis_base_account_id,
)
if grouping_key not in account_vals_to_fix:
continue
grouping_key = self.env['account.partial.reconcile']._get_cash_basis_tax_line_grouping_key_from_record(
line,
account=line.tax_line_id.cash_basis_transition_account_id,
)
if grouping_key not in transfer_account_vals_to_fix:
continue
transfer_account_vals_to_fix[grouping_key]['debit'] -= line.debit
transfer_account_vals_to_fix[grouping_key]['credit'] -= line.credit
account_vals_to_fix[grouping_key]['debit'] -= line.debit
account_vals_to_fix[grouping_key]['credit'] -= line.credit
# ==========================================================================
# Generate the exchange difference journal items to reset the balance of
# all transfer account to zero.
# Generate the exchange difference journal items:
# - to reset the balance of all transfer account to zero.
# - fix rounding issues on the tax account/base tax account.
# ==========================================================================
for values in transfer_account_vals_to_fix.values():
for values in account_vals_to_fix.values():
balance = values['debit'] - values['credit']
values.update({
'debit': balance if balance > 0.0 else 0.0,
'credit': -balance if balance < 0.0 else 0.0,
})
account = self.env['account.account'].browse(values['account_id'])
if account.company_id.currency_id.is_zero(balance):
if move.company_currency_id.is_zero(balance):
continue
journal = account.company_id.currency_exchange_journal_id
if values.get('tax_repartition_line_id'):
# Tax line.
tax_repartition_line = self.env['account.tax.repartition.line'].browse(values['tax_repartition_line_id'])
account = tax_repartition_line.account_id or self.env['account.account'].browse(values['account_id'])
if balance > 0.0:
exchange_line_account = journal.company_id.expense_currency_exchange_account_id
sequence = len(exchange_diff_move_vals['line_ids'])
exchange_diff_move_vals['line_ids'] += [
(0, 0, {
**values,
'name': _('Currency exchange rate difference (cash basis)'),
'debit': balance if balance > 0.0 else 0.0,
'credit': -balance if balance < 0.0 else 0.0,
'account_id': account.id,
'sequence': sequence,
}),
(0, 0, {
**values,
'name': _('Currency exchange rate difference (cash basis)'),
'debit': -balance if balance < 0.0 else 0.0,
'credit': balance if balance > 0.0 else 0.0,
'account_id': values['account_id'],
'tax_ids': [],
'tax_tag_ids': [],
'tax_repartition_line_id': False,
'sequence': sequence + 1,
}),
]
else:
exchange_line_account = journal.company_id.income_currency_exchange_account_id
sequence = len(exchange_diff_move_vals['line_ids'])
exchange_diff_move_vals['line_ids'] += [
(0, 0, {
**values,
'name': _('Currency exchange rate difference (cash basis)'),
'debit': values['credit'],
'credit': values['debit'],
'amount_currency': 0.0,
'sequence': sequence,
}),
(0, 0, {
**values,
'name': _('Currency exchange rate difference (cash basis)'),
'amount_currency': 0.0,
'account_id': exchange_line_account.id,
'sequence': sequence + 1,
}),
]
# Base line.
sequence = len(exchange_diff_move_vals['line_ids'])
exchange_diff_move_vals['line_ids'] += [
(0, 0, {
**values,
'name': _('Currency exchange rate difference (cash basis)'),
'debit': balance if balance > 0.0 else 0.0,
'credit': -balance if balance < 0.0 else 0.0,
'sequence': sequence,
}),
(0, 0, {
**values,
'name': _('Currency exchange rate difference (cash basis)'),
'debit': -balance if balance < 0.0 else 0.0,
'credit': balance if balance > 0.0 else 0.0,
'tax_ids': [],
'tax_tag_ids': [],
'sequence': sequence + 1,
}),
]
if not self:
return self.env['account.move']
@@ -167,11 +167,15 @@ class AccountPartialReconcile(models.Model):
partial_amount_currency += partial.debit_amount_currency
rate_amount -= partial.credit_move_id.balance
rate_amount_currency -= partial.credit_move_id.amount_currency
source_line = partial.debit_move_id
counterpart_line = partial.credit_move_id
if partial.credit_move_id.move_id == move:
partial_amount += partial.amount
partial_amount_currency += partial.credit_amount_currency
rate_amount += partial.debit_move_id.balance
rate_amount_currency += partial.debit_move_id.amount_currency
source_line = partial.credit_move_id
counterpart_line = partial.debit_move_id
if move_values['currency'] == move.company_id.currency_id:
# Percentage made on company's currency.
@@ -180,7 +184,16 @@ class AccountPartialReconcile(models.Model):
# Percentage made on foreign currency.
percentage = partial_amount_currency / move_values['total_amount_currency']
if rate_amount:
if source_line.currency_id != counterpart_line.currency_id:
# When the invoice and the payment are not sharing the same foreign currency, the rate is computed
# on-the-fly using the payment date.
payment_rate = self.env['res.currency']._get_conversion_rate(
counterpart_line.company_currency_id,
source_line.currency_id,
counterpart_line.company_id,
counterpart_line.date,
)
elif rate_amount:
payment_rate = rate_amount_currency / rate_amount
else:
payment_rate = 0.0
@@ -363,6 +376,7 @@ class AccountPartialReconcile(models.Model):
:param pending_cash_basis_lines: The previously generated lines during this reconciliation but not yet created.
:param partial_lines_to_create: The generated lines for the current and last partial making the move fully paid.
'''
# DEPRECATED: TO BE REMOVED IN MASTER
residual_amount_per_group = {}
move = move_values['move']
@@ -460,9 +474,8 @@ class AccountPartialReconcile(models.Model):
move = move_values['move']
pending_cash_basis_lines = []
for i, partial_values in enumerate(move_values['partials']):
for partial_values in move_values['partials']:
partial = partial_values['partial']
is_last_partial = i == len(move_values['partials']) - 1
# Init the journal entry.
move_vals = {
@@ -532,19 +545,6 @@ class AccountPartialReconcile(models.Model):
'vals': cb_base_line_vals,
}
# ==========================================================================
# Ensure the full coverage by replacing the balance of the journal items
# created by the last partial.
# ==========================================================================
if move_values['is_fully_paid'] and is_last_partial:
self._fix_cash_basis_full_balance_coverage(
move_values,
partial_values,
pending_cash_basis_lines,
partial_lines_to_create,
)
# ==========================================================================
# Create the counterpart journal items.
# ==========================================================================
@@ -762,6 +762,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
(self.cash_basis_transfer_account, -33.34, -33.34),
(self.tax_account_1, 0.0, 0.0),
(self.tax_account_2, 0.0, 0.0),
(self.cash_basis_base_account, 0.0, 0.0),
])
# There is 44.45 + 44.45 + 44.45 + 0.01 = 133.36 to reconcile on 'cash_basis_move'.
@@ -874,8 +875,13 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
{'debit': 0.0, 'credit': 0.0, 'account_id': self.cash_basis_transfer_account.id},
{'debit': 0.0, 'credit': 0.0, 'account_id': self.tax_account_1.id},
# tax_2:
{'debit': 0.01, 'credit': 0.0, 'account_id': self.cash_basis_transfer_account.id},
{'debit': 0.0, 'credit': 0.01, 'account_id': self.tax_account_2.id},
{'debit': 0.0, 'credit': 0.0, 'account_id': self.cash_basis_transfer_account.id},
{'debit': 0.0, 'credit': 0.0, 'account_id': self.tax_account_2.id},
])
self.assertRecordValues(res['full_reconcile'].exchange_move_id.line_ids, [
{'account_id': self.tax_account_2.id, 'debit': 0.0, 'credit': 0.01, 'tax_ids': [], 'tax_line_id': self.cash_basis_tax_tiny_amount.id},
{'account_id': self.cash_basis_transfer_account.id, 'debit': 0.01, 'credit': 0.0, 'tax_ids': [], 'tax_line_id': False},
])
self.assertAmountsGroupByAccount([
@@ -889,6 +895,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
''' Same as before with a foreign currency. '''
currency_id = self.currency_data['currency'].id
taxes = self.cash_basis_tax_a_third_amount + self.cash_basis_tax_tiny_amount
cash_basis_move = self.env['account.move'].create({
'move_type': 'entry',
@@ -901,7 +908,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'amount_currency': -100.0,
'currency_id': currency_id,
'account_id': self.company_data['default_account_revenue'].id,
'tax_ids': [(6, 0, (self.cash_basis_tax_a_third_amount + self.cash_basis_tax_tiny_amount).ids)],
'tax_ids': [(6, 0, taxes.ids)],
'tax_exigible': False,
}),
@@ -1085,8 +1092,8 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
self.assertEqual(len(res.get('tax_cash_basis_moves', [])), 1)
self.assertRecordValues(res['tax_cash_basis_moves'].line_ids, [
# Base amount of tax_1 & tax_2:
{'debit': 0.01, 'credit': 0.0, 'amount_currency': 0.008, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id},
{'debit': 0.0, 'credit': 0.01, 'amount_currency': -0.008, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id},
{'debit': 0.01, 'credit': 0.0, 'amount_currency': 0.007, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id},
{'debit': 0.0, 'credit': 0.01, 'amount_currency': -0.007, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id},
# tax_1:
{'debit': 0.0, 'credit': 0.0, 'amount_currency': 0.002, 'currency_id': currency_id, 'account_id': self.cash_basis_transfer_account.id},
{'debit': 0.0, 'credit': 0.0, 'amount_currency': -0.002, 'currency_id': currency_id, 'account_id': self.tax_account_1.id},
@@ -1095,19 +1102,26 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
{'debit': 0.0, 'credit': 0.0, 'amount_currency': 0.0, 'currency_id': currency_id, 'account_id': self.tax_account_2.id},
])
self.assertRecordValues(res['full_reconcile'].exchange_move_id.line_ids, [
{'account_id': self.cash_basis_base_account.id, 'debit': 16.71, 'credit': 0.0, 'tax_ids': taxes.ids, 'tax_line_id': False},
{'account_id': self.cash_basis_base_account.id, 'debit': 0.0, 'credit': 16.71, 'tax_ids': [], 'tax_line_id': False},
{'account_id': self.tax_account_1.id, 'debit': 5.58, 'credit': 0.0, 'tax_ids': [], 'tax_line_id': self.cash_basis_tax_a_third_amount.id},
{'account_id': self.cash_basis_transfer_account.id, 'debit': 0.0, 'credit': 5.58, 'tax_ids': [], 'tax_line_id': False},
{'account_id': self.tax_account_2.id, 'debit': 0.0, 'credit': 0.01, 'tax_ids': [], 'tax_line_id': self.cash_basis_tax_tiny_amount.id},
{'account_id': self.cash_basis_transfer_account.id, 'debit': 0.01, 'credit': 0.0, 'tax_ids': [], 'tax_line_id': False},
])
self.assertAmountsGroupByAccount([
# Account Balance Amount Currency
(self.cash_basis_transfer_account, 0.0, 0.0),
(self.tax_account_1, -16.68, -33.33),
(self.tax_account_2, 0.0, -0.01),
(self.tax_account_1, -11.1, -33.33),
(self.tax_account_2, -0.01, -0.01),
])
def test_reconcile_cash_basis_exchange_difference_transfer_account_not_reconcile(self):
def test_reconcile_cash_basis_exchange_difference_transfer_account_check_entries_1(self):
''' Test the generation of the exchange difference for a tax cash basis journal entry when the transfer
account is not a reconcile one.
'''
self.cash_basis_transfer_account.reconcile = False
currency_id = self.currency_data['currency'].id
# Rate 1/3 in 2016.
@@ -1234,27 +1248,30 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
self.assertAmountsGroupByAccount([
# Account Balance Amount Currency
(self.cash_basis_transfer_account, 0.0, 0.0),
(self.tax_account_1, -50.0, -100.0),
(self.tax_account_1, -33.33, -100.0),
])
def test_reconcile_cash_basis_exchange_difference_transfer_account_reconcile(self):
def test_reconcile_cash_basis_exchange_difference_transfer_account_check_entries_2(self):
''' Test the generation of the exchange difference for a tax cash basis journal entry when the transfer
account is a reconcile one.
account is not a reconcile one.
'''
self.cash_basis_transfer_account.reconcile = True
currency_id = self.setup_multi_currency_data(default_values={
'name': 'bitcoin',
'symbol': 'bc',
'currency_unit_label': 'Bitcoin',
'currency_subunit_label': 'Tiny bitcoin',
}, rate2016=0.5, rate2017=0.66666666666666)['currency'].id
currency_id = self.currency_data['currency'].id
# Rate 1/3 in 2016.
cash_basis_move = self.env['account.move'].create({
# Rate 2/1 in 2016.
caba_inv = self.env['account.move'].create({
'move_type': 'entry',
'date': '2016-01-01',
'line_ids': [
# Base Tax line
(0, 0, {
'debit': 0.0,
'credit': 100.0,
'amount_currency': -300.0,
'credit': 200.0,
'amount_currency': -100.0,
'currency_id': currency_id,
'account_id': self.company_data['default_account_revenue'].id,
'tax_ids': [(6, 0, self.cash_basis_tax_a_third_amount.ids)],
@@ -1264,8 +1281,8 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
# Tax line
(0, 0, {
'debit': 0.0,
'credit': 33.33,
'amount_currency': -100.0,
'credit': 20.0,
'amount_currency': -10.0,
'currency_id': currency_id,
'account_id': self.cash_basis_transfer_account.id,
'tax_repartition_line_id': self.cash_basis_tax_a_third_amount.invoice_repartition_line_ids.filtered(lambda line: line.repartition_type == 'tax').id,
@@ -1274,87 +1291,137 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
# Receivable lines
(0, 0, {
'debit': 133.33,
'debit': 220.0,
'credit': 0.0,
'amount_currency': 400.0,
'amount_currency': 110.0,
'currency_id': currency_id,
'account_id': self.extra_receivable_account_1.id,
}),
]
})
caba_inv.action_post()
# Rate 1/2 in 2017.
payment_move = self.env['account.move'].create({
'move_type': 'entry',
'date': '2017-01-01',
'line_ids': [
(0, 0, {
'debit': 0.0,
'credit': 200.0,
'amount_currency': -400.0,
'currency_id': currency_id,
'account_id': self.extra_receivable_account_1.id,
}),
(0, 0, {
'debit': 200.0,
'credit': 0.0,
'account_id': self.company_data['default_account_revenue'].id,
}),
]
# Rate 3/2 in 2017. Full payment of 110 in foreign currency
pmt_wizard = self.env['account.payment.register'].with_context(active_model='account.move', active_ids=caba_inv.ids).create({
'payment_date': '2017-01-01',
'journal_id': self.company_data['default_journal_bank'].id,
'payment_method_id': self.env.ref('account.account_payment_method_manual_in').id,
})
pmt_wizard._create_payments()
partial_rec = caba_inv.mapped('line_ids.matched_credit_ids')
caba_move = self.env['account.move'].search([('tax_cash_basis_rec_id', 'in', partial_rec.ids)])
(cash_basis_move + payment_move).action_post()
self.assertAmountsGroupByAccount([
# Account Balance Amount Currency
(self.cash_basis_transfer_account, -33.33, -100.0),
(self.tax_account_1, 0.0, 0.0),
self.assertRecordValues(caba_move.line_ids, [
{'account_id': self.cash_basis_base_account.id, 'debit': 150.0, 'credit': 0.0, 'amount_currency': 100.0, 'tax_ids': [], 'tax_line_id': False},
{'account_id': self.cash_basis_base_account.id, 'debit': 0.0, 'credit': 150.0, 'amount_currency': -100.0, 'tax_ids': self.cash_basis_tax_a_third_amount.ids, 'tax_line_id': False},
{'account_id': self.cash_basis_transfer_account.id, 'debit': 15.0, 'credit': 0.0, 'amount_currency': 10.0, 'tax_ids': [], 'tax_line_id': False},
{'account_id': self.tax_account_1.id, 'debit': 0.0, 'credit': 15.0, 'amount_currency': -10.0, 'tax_ids': [], 'tax_line_id': self.cash_basis_tax_a_third_amount.id},
])
receivable_lines = (cash_basis_move + payment_move).line_ids\
.filtered(lambda line: line.account_id == self.extra_receivable_account_1)
res = receivable_lines.reconcile()
receivable_line = caba_inv.line_ids.filtered(lambda x: x.account_id.internal_type == 'receivable')
self.assertTrue(receivable_line.full_reconcile_id, "Invoice should be fully paid")
self.assertFullReconcile(res['full_reconcile'], receivable_lines)
self.assertEqual(len(res.get('tax_cash_basis_moves', [])), 1)
self.assertRecordValues(res['tax_cash_basis_moves'].line_ids, [
# Base amount:
{'debit': 150.0, 'credit': 0.0, 'amount_currency': 300.0, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id},
{'debit': 0.0, 'credit': 150.0, 'amount_currency': -300.0, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id},
# tax:
{'debit': 50.0, 'credit': 0.0, 'amount_currency': 100.0, 'currency_id': currency_id, 'account_id': self.cash_basis_transfer_account.id},
{'debit': 0.0, 'credit': 50.0, 'amount_currency': -100.0, 'currency_id': currency_id, 'account_id': self.tax_account_1.id},
])
transfer_lines = (cash_basis_move + res['tax_cash_basis_moves']).line_ids\
.filtered(lambda line: line.account_id == self.cash_basis_transfer_account)
self.assertTrue(transfer_lines.full_reconcile_id)
self.assertFullReconcile(transfer_lines.full_reconcile_id, transfer_lines)
transfer_exchange_diff = transfer_lines.full_reconcile_id.exchange_move_id
transfer_exchange_diff_lines = transfer_exchange_diff.line_ids.sorted(lambda line: (line.account_id, -line.balance))
self.assertRecordValues(transfer_exchange_diff_lines, [
{
'debit': 0.0,
'credit': 16.67,
'amount_currency': 0.0,
'currency_id': currency_id,
'account_id': self.cash_basis_transfer_account.id,
},
{
'debit': 16.67,
'credit': 0.0,
'amount_currency': 0.0,
'currency_id': currency_id,
'account_id': transfer_exchange_diff.journal_id.company_id.expense_currency_exchange_account_id.id,
},
exchange_move = receivable_line.full_reconcile_id.exchange_move_id
self.assertTrue(exchange_move, "There should be an exchange difference move created")
self.assertRecordValues(exchange_move.line_ids, [
{'account_id': receivable_line.account_id.id, 'debit': 0.0, 'credit': 55.0, 'amount_currency': 0.0, 'tax_ids': [], 'tax_line_id': False},
{'account_id': caba_move.company_id.expense_currency_exchange_account_id.id, 'debit': 55.0, 'credit': 0.0, 'amount_currency': 0.0, 'tax_ids': [], 'tax_line_id': False},
{'account_id': self.cash_basis_base_account.id, 'debit': 0.0, 'credit': 50.0, 'amount_currency': 0.0, 'tax_ids': self.cash_basis_tax_a_third_amount.ids, 'tax_line_id': False},
{'account_id': self.cash_basis_base_account.id, 'debit': 50.0, 'credit': 0.0, 'amount_currency': 0.0, 'tax_ids': [], 'tax_line_id': False},
{'account_id': self.tax_account_1.id, 'debit': 0.0, 'credit': 5.0, 'amount_currency': 0.0, 'tax_ids': [], 'tax_line_id': self.cash_basis_tax_a_third_amount.id},
{'account_id': self.cash_basis_transfer_account.id, 'debit': 5.0, 'credit': 0.0, 'amount_currency': 0.0, 'tax_ids': [], 'tax_line_id': False},
])
self.assertAmountsGroupByAccount([
# Account Balance Amount Currency
(self.cash_basis_transfer_account, 0.0, 0.0),
(self.tax_account_1, -50.0, -100.0),
(self.tax_account_1, -20.0, -10.0),
])
def test_reconcile_cash_basis_exchange_difference_transfer_account_check_entries_3(self):
''' Test the generation of the exchange difference for a tax cash basis journal entry when the transfer
account is not a reconcile one.
'''
currency_id = self.setup_multi_currency_data(default_values={
'name': 'bitcoin',
'symbol': 'bc',
'currency_unit_label': 'Bitcoin',
'currency_subunit_label': 'Tiny bitcoin',
'rounding': 0.01,
}, rate2016=0.5, rate2017=0.66666666666666)['currency'].id
# Rate 2/1 in 2016.
caba_inv = self.env['account.move'].create({
'move_type': 'entry',
'date': '2016-01-01',
'line_ids': [
# Base Tax line
(0, 0, {
'debit': 0.0,
'credit': 200.0,
'amount_currency': -100.0,
'currency_id': currency_id,
'account_id': self.company_data['default_account_revenue'].id,
'tax_ids': [(6, 0, self.cash_basis_tax_a_third_amount.ids)],
'tax_exigible': False,
}),
# Tax line
(0, 0, {
'debit': 0.0,
'credit': 20.0,
'amount_currency': -10.0,
'currency_id': currency_id,
'account_id': self.cash_basis_transfer_account.id,
'tax_repartition_line_id': self.cash_basis_tax_a_third_amount.invoice_repartition_line_ids.filtered(lambda line: line.repartition_type == 'tax').id,
'tax_exigible': False,
}),
# Receivable lines
(0, 0, {
'debit': 220.0,
'credit': 0.0,
'amount_currency': 110.0,
'currency_id': currency_id,
'account_id': self.extra_receivable_account_1.id,
}),
]
})
caba_inv.action_post()
# Rate 3/2 in 2017. Full payment of 220 in company currency
pmt_wizard = self.env['account.payment.register'].with_context(active_model='account.move', active_ids=caba_inv.ids).create({
'payment_date': '2017-01-01',
'journal_id': self.company_data['default_journal_bank'].id,
'payment_method_id': self.env.ref('account.account_payment_method_manual_in').id,
'currency_id': self.company_data['currency'].id,
'amount': 220.0,
})
pmt_wizard._create_payments()
caba_move = self.env['account.move'].search([('tax_cash_basis_rec_id', 'in', caba_inv.line_ids.matched_credit_ids.ids)])
self.assertRecordValues(caba_move.line_ids, [
{'account_id': self.cash_basis_base_account.id, 'debit': 200.01, 'credit': 0.0, 'amount_currency': 133.34, 'tax_ids': [], 'tax_line_id': False},
{'account_id': self.cash_basis_base_account.id, 'debit': 0.0, 'credit': 200.01, 'amount_currency': -133.34, 'tax_ids': self.cash_basis_tax_a_third_amount.ids, 'tax_line_id': False},
{'account_id': self.cash_basis_transfer_account.id, 'debit': 20.0, 'credit': 0.0, 'amount_currency': 13.33, 'tax_ids': [], 'tax_line_id': False},
{'account_id': self.tax_account_1.id, 'debit': 0.0, 'credit': 20.0, 'amount_currency': -13.33, 'tax_ids': [], 'tax_line_id': self.cash_basis_tax_a_third_amount.id},
])
receivable_line = caba_inv.line_ids.filtered(lambda x: x.account_id.internal_type == 'receivable')
self.assertTrue(receivable_line.full_reconcile_id, "Invoice should be fully paid")
exchange_move = receivable_line.full_reconcile_id.exchange_move_id
self.assertRecordValues(exchange_move.line_ids, [
{'account_id': self.extra_receivable_account_1.id, 'debit': 0.0, 'credit': 0.0, 'amount_currency': 36.67, 'tax_ids': [], 'tax_line_id': False},
{'account_id': caba_move.company_id.income_currency_exchange_account_id.id, 'debit': 0.0, 'credit': 0.0, 'amount_currency': -36.67, 'tax_ids': [], 'tax_line_id': False},
{'account_id': self.cash_basis_base_account.id, 'debit': 0.01, 'credit': 0.0, 'amount_currency': 0.0, 'tax_ids': self.cash_basis_tax_a_third_amount.ids, 'tax_line_id': False},
{'account_id': self.cash_basis_base_account.id, 'debit': 0.0, 'credit': 0.01, 'amount_currency': 0.0, 'tax_ids': [], 'tax_line_id': False},
])
self.assertAmountsGroupByAccount([
# Account Balance Amount Currency
(self.cash_basis_transfer_account, 0.0, 3.33),
(self.tax_account_1, -20.0, -13.33),
])
def test_reconcile_cash_basis_revert(self):
@@ -1767,4 +1834,4 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
lines_to_reconcile.reconcile()
# Check full reconciliation
self.assertTrue(all(line.full_reconcile_id for line in lines_to_reconcile), "All tax lines should be fully reconciled")
self.assertTrue(all(line.full_reconcile_id for line in lines_to_reconcile), "All tax lines should be fully reconciled")