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