diff --git a/addons/account/models/account_partial_reconcile.py b/addons/account/models/account_partial_reconcile.py index 6a7ec11c25b..2209fea4d8a 100644 --- a/addons/account/models/account_partial_reconcile.py +++ b/addons/account/models/account_partial_reconcile.py @@ -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 diff --git a/addons/account/tests/test_account_move_reconcile.py b/addons/account/tests/test_account_move_reconcile.py index 6037f4513a5..0450e9cb923 100644 --- a/addons/account/tests/test_account_move_reconcile.py +++ b/addons/account/tests/test_account_move_reconcile.py @@ -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