[FIX] account: don't cross exchange rates when reconciling an invoice with a refund

Before this fix, when reconciling an invoice with a refund (both using cash basis taxes) in a multicurrency setup, the two cash basis entries that got created were made so that the invoice's used the refund's rate, and the refund's used the invoice's one.

This caused confustion, and forced Odoo to always add cash basis adjustment journal items in the exchange difference move, in order to balance the accounts properly.

By not swapping the rates anymore, we avoid the creation of these journal items.

OPW 2888592

X-original-commit: dc27baac2b39abcd77cfe20290393b246f7b2a9c
Part-of: odoo/odoo#104564
This commit is contained in:
oco-odoo
2022-10-31 10:41:51 +01:00
parent 753636a412
commit 27622ecbbf
2 changed files with 166 additions and 1 deletions
@@ -160,6 +160,7 @@ class AccountPartialReconcile(models.Model):
partial_amount_currency = 0.0
rate_amount = 0.0
rate_amount_currency = 0.0
if partial.debit_move_id.move_id == move:
partial_amount += partial.amount
partial_amount_currency += partial.debit_amount_currency
@@ -167,6 +168,7 @@ class AccountPartialReconcile(models.Model):
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
@@ -175,6 +177,16 @@ class AccountPartialReconcile(models.Model):
source_line = partial.credit_move_id
counterpart_line = partial.debit_move_id
if partial.debit_move_id.move_id.is_invoice(include_receipts=True) and partial.credit_move_id.move_id.is_invoice(include_receipts=True):
# Will match when reconciling a refund with an invoice.
# In this case, we want to use the rate of each businness document to compute its cash basis entry,
# not the rate of what it's reconciled with.
rate_amount = source_line.balance
rate_amount_currency = source_line.amount_currency
payment_date = move.date
else:
payment_date = counterpart_line.date
if move_values['currency'] == move.company_id.currency_id:
# Ignore the exchange difference.
if move.company_currency_id.is_zero(partial_amount):
@@ -197,7 +209,7 @@ class AccountPartialReconcile(models.Model):
counterpart_line.company_currency_id,
source_line.currency_id,
counterpart_line.company_id,
counterpart_line.date,
payment_date,
)
elif rate_amount:
payment_rate = rate_amount_currency / rate_amount
@@ -3208,6 +3208,159 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
{'debit': 0.0, 'credit': 50.0, 'currency_id': currency_id, 'account_id': self.cash_basis_base_account.id},
])
def test_reconcile_cash_basis_refund_multicurrency(self):
self.env.company.tax_exigibility = True
rates_data = self.setup_multi_currency_data(default_values={
'name': 'Playmock',
'symbol': '🦌',
'rounding': 0.01,
'currency_unit_label': 'Playmock',
'currency_subunit_label': 'Cent',
}, rate2016=0.5, rate2017=0.33333333333333333)
invoice = self.env['account.move'].create({
'move_type': 'out_invoice',
'partner_id': self.partner_a.id,
'currency_id': rates_data['currency'].id,
'invoice_date': '2016-01-01',
'invoice_line_ids': [(0, 0, {
'name': 'dudu',
'account_id': self.company_data['default_account_revenue'].id,
'price_unit': 100.0,
'tax_ids': [(6, 0, self.cash_basis_tax_a_third_amount.ids)],
})],
})
refund = self.env['account.move'].create({
'move_type': 'out_refund',
'partner_id': self.partner_a.id,
'currency_id': rates_data['currency'].id,
'invoice_date': '2017-01-01',
'invoice_line_ids': [(0, 0, {
'name': 'dudu',
'account_id': self.company_data['default_account_revenue'].id,
'price_unit': 100.0,
'tax_ids': [(6, 0, self.cash_basis_tax_a_third_amount.ids)],
})],
})
invoice.action_post()
refund.action_post()
(refund + invoice).line_ids.filtered(lambda x: x.account_id.account_type == 'asset_receivable').reconcile()
# Check the cash basis moves
self.assertRecordValues(
self.env['account.move'].search([('tax_cash_basis_origin_move_id', '=', invoice.id)]).line_ids,
[
{
'debit': 200,
'credit': 0,
'amount_currency': 100,
'currency_id': rates_data['currency'].id,
'tax_ids': [],
'tax_repartition_line_id': None,
'tax_tag_ids': [],
},
{
'debit': 0,
'credit': 200,
'amount_currency': -100,
'currency_id': rates_data['currency'].id,
'tax_ids': self.cash_basis_tax_a_third_amount.ids,
'tax_repartition_line_id': None,
'tax_tag_ids': self.tax_tags[0].ids,
},
{
'debit': 66.66,
'credit': 0,
'amount_currency': 33.33,
'currency_id': rates_data['currency'].id,
'tax_ids': [],
'tax_repartition_line_id': None,
'tax_tag_ids': [],
},
{
'debit': 0,
'credit': 66.66,
'amount_currency': -33.33,
'currency_id': rates_data['currency'].id,
'tax_ids': [],
'tax_repartition_line_id': self.cash_basis_tax_a_third_amount.invoice_repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax').id,
'tax_tag_ids': self.tax_tags[1].ids,
},
]
)
self.assertRecordValues(
self.env['account.move'].search([('tax_cash_basis_origin_move_id', '=', refund.id)]).line_ids,
[
{
'debit': 0,
'credit': 300,
'amount_currency': -100,
'currency_id': rates_data['currency'].id,
'tax_ids': [],
'tax_repartition_line_id': None,
'tax_tag_ids': [],
},
{
'debit': 300,
'credit': 0,
'amount_currency': 100,
'currency_id': rates_data['currency'].id,
'tax_ids': self.cash_basis_tax_a_third_amount.ids,
'tax_repartition_line_id': None,
'tax_tag_ids': self.tax_tags[2].ids,
},
{
'debit': 0,
'credit': 99.99,
'amount_currency': -33.33,
'currency_id': rates_data['currency'].id,
'tax_ids': [],
'tax_repartition_line_id': None,
'tax_tag_ids': [],
},
{
'debit': 99.99,
'credit': 0,
'amount_currency': 33.33,
'currency_id': rates_data['currency'].id,
'tax_ids': [],
'tax_repartition_line_id': self.cash_basis_tax_a_third_amount.refund_repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax').id,
'tax_tag_ids': self.tax_tags[3].ids,
},
]
)
# Check the exchange difference move, to be sure no cash basis rounding data was added into it
self.assertRecordValues(
invoice.line_ids.filtered(lambda x: x.account_id.account_type == 'asset_receivable').matched_credit_ids.exchange_move_id.line_ids,
[
{
'debit': 133.33,
'credit': 0,
'amount_currency': 0,
'currency_id': rates_data['currency'].id,
'tax_ids': [],
'tax_repartition_line_id': None,
'tax_tag_ids': [],
},
{
'debit': 0,
'credit': 133.33,
'amount_currency': 0,
'currency_id': rates_data['currency'].id,
'tax_ids': [],
'tax_repartition_line_id': None,
'tax_tag_ids': [],
},
]
)
self.assertFalse(invoice.line_ids.full_reconcile_id.exchange_move_id)
def test_reconcile_cash_basis_revert(self):
''' Ensure the cash basis journal entry can be reverted. '''
self.env.company.tax_exigibility = True