diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index eaa0ff269e2..8b49cf154ad 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -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'] diff --git a/addons/account/models/account_partial_reconcile.py b/addons/account/models/account_partial_reconcile.py index 5569ee48ec4..8d5b76710c6 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 @@ -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. # ========================================================================== 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.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") \ 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")