diff options
| author | qdp-odoo <qdp@odoo.com> | 2021-01-13 16:43:11 +0000 |
|---|---|---|
| committer | Laurent Smet <las@odoo.com> | 2021-02-10 08:17:31 +0000 |
| commit | f8916ab0c6ca0a426eb67e2c4e46ede0b9fdadad (patch) | |
| tree | 3b0c21380b4f674741776d094e0c4b3bcf84adcf /addons | |
| parent | a8680ac8815575762b702917940c0b2d4dc0c4ea (diff) | |
[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#64601
Signed-off-by: Quentin De Paoli (qdp) <qdp@openerp.com>
Diffstat (limited to 'addons')
| -rw-r--r-- | addons/account/models/account_move.py | 174 | ||||
| -rw-r--r-- | addons/account/models/account_partial_reconcile.py | 32 | ||||
| -rw-r--r-- | addons/account/tests/test_account_move_reconcile.py | 221 |
3 files changed, 256 insertions, 171 deletions
diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 4ddf4c9b00a..e8cde6df744 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -1955,7 +1955,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, @@ -1990,17 +1989,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 @@ -4312,7 +4303,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() @@ -4321,40 +4312,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 - - if not line.account_id.reconcile: - is_exchange_diff_needed = True + 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._convert_tags_for_cash_basis(line.tax_tag_ids).ids)], + 'debit': line.debit, + 'credit': line.credit, + } + + 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._convert_tags_for_cash_basis(line.tax_tag_ids).ids)], - 'debit': line.debit, - 'credit': line.credit, } - - # No tax line on the current move. - if not is_exchange_diff_needed: - continue + elif line.tax_ids: + # Base line. + account_to_fix = line.company_id.account_cash_basis_base_account_id + if not account_to_fix: + 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 @@ -4363,61 +4356,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: - 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: + 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 - 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 balance > 0.0: - exchange_line_account = journal.company_id.expense_currency_exchange_account_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']) + + 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'] diff --git a/addons/account/models/account_partial_reconcile.py b/addons/account/models/account_partial_reconcile.py index 0d48a08b458..a7e4aaf4d4c 100644 --- a/addons/account/models/account_partial_reconcile.py +++ b/addons/account/models/account_partial_reconcile.py @@ -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 @@ -361,6 +374,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'] @@ -458,9 +472,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 = { @@ -531,19 +544,6 @@ class AccountPartialReconcile(models.Model): } # ========================================================================== - # 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. # ========================================================================== diff --git a/addons/account/tests/test_account_move_reconcile.py b/addons/account/tests/test_account_move_reconcile.py index 8030359f49b..4c17a415c42 100644 --- a/addons/account/tests/test_account_move_reconcile.py +++ b/addons/account/tests/test_account_move_reconcile.py @@ -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.currency_data['currency'].id - - # Rate 1/3 in 2016. - cash_basis_move = self.env['account.move'].create({ + 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 + + # 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({ + # 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)]) + + 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_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.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, -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': '2017-01-01', + 'date': '2016-01-01', 'line_ids': [ + # Base Tax line (0, 0, { 'debit': 0.0, 'credit': 200.0, - 'amount_currency': -400.0, + 'amount_currency': -100.0, 'currency_id': currency_id, - 'account_id': self.extra_receivable_account_1.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': 200.0, + 'debit': 220.0, 'credit': 0.0, - 'account_id': self.company_data['default_account_revenue'].id, + 'amount_currency': 110.0, + 'currency_id': currency_id, + 'account_id': self.extra_receivable_account_1.id, }), ] }) + caba_inv.action_post() - (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), - ]) - - receivable_lines = (cash_basis_move + payment_move).line_ids\ - .filtered(lambda line: line.account_id == self.extra_receivable_account_1) - res = receivable_lines.reconcile() + # 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() - 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}, + 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}, ]) - 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)) + 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.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.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, 0.0), - (self.tax_account_1, -50.0, -100.0), + (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")
\ No newline at end of file + self.assertTrue(all(line.full_reconcile_id for line in lines_to_reconcile), "All tax lines should be fully reconciled") |
