The manual reconciliation widget is too complex. We completely remove it and replace it with a simple wizard `account.reconcile.wizard` that opens when selected lines can't be directly/silently reconciled (then we need a write-off whose data will be filled in the wizard). To do so we had to refactor reconciliation methods to allow to shadow some values and fake reconciliation to get correct values. We also now use of the _reconcile_plan instead of the previous reconciliation which should be more efficient and correct. Refactored a bit common between `test_account_reconcile_wizard` and `test_account_move_reconcile` to avoid duplication. In this process of simplification we also separate the previous `automatic_entry_wizard` in two separate interfaces from the user's perspective: one only handle the change of period and the other will handle complex transfer that are not possible through regular reconciliation process (i.e. through the wizard we are creating in Enterprise related PR). closes odoo/odoo#120832 Task: 3060792 Related: odoo/upgrade#4674 Related: odoo/enterprise#40510 Signed-off-by: Laurent Smet <las@odoo.com>
288 lines
14 KiB
Python
288 lines
14 KiB
Python
# -*- coding: utf-8 -*-
|
|
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
|
from odoo.tests import tagged, Form
|
|
import time
|
|
|
|
@tagged('post_install', '-at_install')
|
|
class TestTransferWizard(AccountTestInvoicingCommon):
|
|
|
|
@classmethod
|
|
def setUpClass(cls, chart_template_ref=None):
|
|
super().setUpClass(chart_template_ref=chart_template_ref)
|
|
|
|
cls.company = cls.company_data['company']
|
|
cls.receivable_account = cls.company_data['default_account_receivable']
|
|
cls.payable_account = cls.company_data['default_account_payable']
|
|
cls.accounts = cls.env['account.account'].search([('reconcile', '=', False), ('company_id', '=', cls.company.id)], limit=5)
|
|
cls.journal = cls.company_data['default_journal_misc']
|
|
|
|
# Set rate for base currency to 1
|
|
cls.env['res.currency.rate'].search([('company_id', '=', cls.company.id), ('currency_id', '=', cls.company.currency_id.id)]).write({'rate': 1})
|
|
|
|
# Create test currencies
|
|
cls.test_currency_1 = cls.env['res.currency'].create({
|
|
'name': "PMK",
|
|
'symbol':'P',
|
|
})
|
|
|
|
cls.test_currency_2 = cls.env['res.currency'].create({
|
|
'name': "toto",
|
|
'symbol':'To',
|
|
})
|
|
|
|
cls.test_currency_3 = cls.env['res.currency'].create({
|
|
'name': "titi",
|
|
'symbol':'Ti',
|
|
})
|
|
|
|
# Create test rates
|
|
cls.env['res.currency.rate'].create({
|
|
'name': time.strftime('%Y') + '-' + '01' + '-01',
|
|
'rate': 0.5,
|
|
'currency_id': cls.test_currency_1.id,
|
|
'company_id': cls.company.id
|
|
})
|
|
|
|
cls.env['res.currency.rate'].create({
|
|
'name': time.strftime('%Y') + '-' + '01' + '-01',
|
|
'rate': 2,
|
|
'currency_id': cls.test_currency_2.id,
|
|
'company_id': cls.company.id
|
|
})
|
|
|
|
cls.env['res.currency.rate'].create({
|
|
'name': time.strftime('%Y') + '-' + '01' + '-01',
|
|
'rate': 10,
|
|
'currency_id': cls.test_currency_3.id,
|
|
'company_id': cls.company.id
|
|
})
|
|
|
|
# Create an account using a foreign currency
|
|
cls.test_currency_account = cls.env['account.account'].create({
|
|
'name': 'test destination account',
|
|
'code': 'test.dest.acc',
|
|
'account_type': 'asset_current',
|
|
'currency_id': cls.test_currency_3.id,
|
|
})
|
|
|
|
# Create test account.move
|
|
cls.move_1 = cls.env['account.move'].create({
|
|
'journal_id': cls.journal.id,
|
|
'line_ids': [
|
|
(0, 0, {
|
|
'name': "test1_1",
|
|
'account_id': cls.receivable_account.id,
|
|
'debit': 500,
|
|
}),
|
|
(0, 0, {
|
|
'name': "test1_2",
|
|
'account_id': cls.accounts[0].id,
|
|
'credit': 500,
|
|
}),
|
|
(0, 0, {
|
|
'name': "test1_3",
|
|
'account_id': cls.accounts[0].id,
|
|
'debit': 800,
|
|
'partner_id': cls.partner_a.id,
|
|
}),
|
|
(0, 0, {
|
|
'name': "test1_4",
|
|
'account_id': cls.accounts[1].id,
|
|
'credit': 500,
|
|
}),
|
|
(0, 0, {
|
|
'name': "test1_5",
|
|
'account_id': cls.accounts[2].id,
|
|
'credit': 300,
|
|
'partner_id': cls.partner_a.id,
|
|
}),
|
|
(0, 0, {
|
|
'name': "test1_6",
|
|
'account_id': cls.accounts[0].id,
|
|
'debit': 270,
|
|
'currency_id': cls.test_currency_1.id,
|
|
'amount_currency': 540,
|
|
}),
|
|
(0, 0, {
|
|
'name': "test1_7",
|
|
'account_id': cls.accounts[1].id,
|
|
'credit': 140,
|
|
}),
|
|
(0, 0, {
|
|
'name': "test1_8",
|
|
'account_id': cls.accounts[2].id,
|
|
'credit': 160,
|
|
}),
|
|
(0, 0, {
|
|
'name': "test1_9",
|
|
'account_id': cls.accounts[2].id,
|
|
'debit': 30,
|
|
'currency_id': cls.test_currency_2.id,
|
|
'amount_currency': 15,
|
|
}),
|
|
]
|
|
})
|
|
cls.move_1.action_post()
|
|
|
|
cls.move_2 = cls.env['account.move'].create({
|
|
'journal_id': cls.journal.id,
|
|
'line_ids': [
|
|
(0, 0, {
|
|
'name': "test2_1",
|
|
'account_id': cls.accounts[1].id,
|
|
'debit': 400,
|
|
}),
|
|
(0, 0, {
|
|
'name': "test2_2",
|
|
'account_id': cls.payable_account.id,
|
|
'credit': 400,
|
|
}),
|
|
(0, 0, {
|
|
'name': "test2_3",
|
|
'account_id': cls.accounts[3].id,
|
|
'debit': 250,
|
|
'partner_id': cls.partner_a.id,
|
|
}),
|
|
(0, 0, {
|
|
'name': "test2_4",
|
|
'account_id': cls.accounts[1].id,
|
|
'debit': 480,
|
|
'partner_id': cls.partner_b.id,
|
|
}),
|
|
(0, 0, {
|
|
'name': "test2_5",
|
|
'account_id': cls.accounts[2].id,
|
|
'credit': 730,
|
|
'partner_id': cls.partner_a.id,
|
|
}),
|
|
(0, 0, {
|
|
'name': "test2_6",
|
|
'account_id': cls.accounts[2].id,
|
|
'credit': 412,
|
|
'partner_id': cls.partner_a.id,
|
|
'currency_id': cls.test_currency_2.id,
|
|
'amount_currency': -633,
|
|
}),
|
|
(0, 0, {
|
|
'name': "test2_7",
|
|
'account_id': cls.accounts[1].id,
|
|
'debit': 572,
|
|
}),
|
|
(0, 0, {
|
|
'name': "test2_8",
|
|
'account_id': cls.accounts[2].id,
|
|
'credit': 100,
|
|
'partner_id': cls.partner_a.id,
|
|
'currency_id': cls.test_currency_2.id,
|
|
'amount_currency': -123,
|
|
}),
|
|
(0, 0, {
|
|
'name': "test2_9",
|
|
'account_id': cls.accounts[2].id,
|
|
'credit': 60,
|
|
'partner_id': cls.partner_a.id,
|
|
'currency_id': cls.test_currency_1.id,
|
|
'amount_currency': -10,
|
|
}),
|
|
]
|
|
})
|
|
cls.move_2.action_post()
|
|
|
|
|
|
def test_transfer_wizard_reconcile(self):
|
|
""" Tests reconciliation when doing a transfer with the wizard
|
|
"""
|
|
active_move_lines = (self.move_1 + self.move_2).mapped('line_ids').filtered(lambda x: x.account_id.account_type in ('asset_receivable', 'liability_payable'))
|
|
|
|
# We use a form to pass the context properly to the depends_context move_line_ids field
|
|
context = {'active_model': 'account.move.line', 'active_ids': active_move_lines.ids, 'default_action': 'change_account'}
|
|
with Form(self.env['account.automatic.entry.wizard'].with_context(context)) as wizard_form:
|
|
wizard_form.destination_account_id = self.receivable_account
|
|
wizard_form.journal_id = self.journal
|
|
wizard = wizard_form.save()
|
|
|
|
transfer_move_id = wizard.do_action()['res_id']
|
|
transfer_move = self.env['account.move'].browse(transfer_move_id)
|
|
|
|
payable_transfer = transfer_move.line_ids.filtered(lambda x: x.account_id == self.payable_account)
|
|
receivable_transfer = transfer_move.line_ids.filtered(lambda x: x.account_id == self.receivable_account)
|
|
|
|
self.assertTrue(payable_transfer.reconciled, "Payable line of the transfer move should be fully reconciled")
|
|
self.assertAlmostEqual(self.move_1.line_ids.filtered(lambda x: x.account_id == self.receivable_account).amount_residual, 100, self.company.currency_id.decimal_places, "Receivable line of the original move should be partially reconciled, and still have a residual amount of 100 (500 - 400 from payable account)")
|
|
self.assertTrue(self.move_2.line_ids.filtered(lambda x: x.account_id == self.payable_account).reconciled, "Payable line of the original move should be fully reconciled")
|
|
self.assertAlmostEqual(receivable_transfer.amount_residual, 0, self.company.currency_id.decimal_places, "Receivable line from the transfer move should have nothing left to reconcile")
|
|
self.assertAlmostEqual(payable_transfer.debit, 400, self.company.currency_id.decimal_places, "400 should have been debited from payable account to apply the transfer")
|
|
self.assertAlmostEqual(receivable_transfer.credit, 400, self.company.currency_id.decimal_places, "400 should have been credited to receivable account to apply the transfer")
|
|
|
|
def test_transfer_wizard_grouping(self):
|
|
""" Tests grouping (by account and partner) when doing a transfer with the wizard
|
|
"""
|
|
active_move_lines = (self.move_1 + self.move_2).mapped('line_ids').filtered(lambda x: x.name in ('test1_3', 'test1_4', 'test1_5', 'test2_3', 'test2_4', 'test2_5', 'test2_6', 'test2_8'))
|
|
|
|
# We use a form to pass the context properly to the depends_context move_line_ids field
|
|
context = {'active_model': 'account.move.line', 'active_ids': active_move_lines.ids, 'default_action': 'change_account'}
|
|
with Form(self.env['account.automatic.entry.wizard'].with_context(context)) as wizard_form:
|
|
wizard_form.destination_account_id = self.accounts[4]
|
|
wizard_form.journal_id = self.journal
|
|
wizard = wizard_form.save()
|
|
|
|
transfer_move_id = wizard.do_action()['res_id']
|
|
transfer_move = self.env['account.move'].browse(transfer_move_id)
|
|
|
|
groups = {}
|
|
for line in transfer_move.line_ids:
|
|
key = (line.account_id, line.partner_id or None, line.currency_id)
|
|
self.assertFalse(groups.get(key), "There should be only one line per (account, partner, currency) group in the transfer move.")
|
|
groups[key] = line
|
|
|
|
self.assertAlmostEqual(groups[(self.accounts[0], self.partner_a, self.company_data['currency'])].balance, -800, self.company.currency_id.decimal_places)
|
|
self.assertAlmostEqual(groups[(self.accounts[1], None, self.company_data['currency'])].balance, 500, self.company.currency_id.decimal_places)
|
|
self.assertAlmostEqual(groups[(self.accounts[1], self.partner_b, self.company_data['currency'])].balance, -480, self.company.currency_id.decimal_places)
|
|
self.assertAlmostEqual(groups[(self.accounts[2], self.partner_a, self.company_data['currency'])].balance, 1030, self.company.currency_id.decimal_places)
|
|
self.assertAlmostEqual(groups[(self.accounts[2], self.partner_a, self.test_currency_2)].balance, 512, self.company.currency_id.decimal_places)
|
|
self.assertAlmostEqual(groups[(self.accounts[3], self.partner_a, self.company_data['currency'])].balance, -250, self.company.currency_id.decimal_places)
|
|
|
|
|
|
def test_transfer_wizard_currency_conversion(self):
|
|
""" Tests multi currency use of the transfer wizard, checking the conversion
|
|
is propperly done when using a destination account with a currency_id set.
|
|
"""
|
|
active_move_lines = self.move_1.mapped('line_ids').filtered(lambda x: x.name in ('test1_6', 'test1_9'))
|
|
|
|
# We use a form to pass the context properly to the depends_context move_line_ids field
|
|
context = {'active_model': 'account.move.line', 'active_ids': active_move_lines.ids, 'default_action': 'change_account'}
|
|
with Form(self.env['account.automatic.entry.wizard'].with_context(context)) as wizard_form:
|
|
wizard_form.destination_account_id = self.test_currency_account
|
|
wizard_form.journal_id = self.journal
|
|
wizard = wizard_form.save()
|
|
|
|
transfer_move_id = wizard.do_action()['res_id']
|
|
transfer_move = self.env['account.move'].browse(transfer_move_id)
|
|
|
|
destination_line = transfer_move.line_ids.filtered(lambda x: x.account_id == self.test_currency_account)
|
|
self.assertEqual(destination_line.currency_id, self.test_currency_3, "Transferring to an account with a currency set should keep this currency on the transfer line.")
|
|
self.assertAlmostEqual(destination_line.amount_currency, 3000, self.company.currency_id.decimal_places, "Transferring two lines with different currencies (and the same partner) on an account with a currency set should convert the balance of these lines into this account's currency (here (270 + 30) * 10 = 3000)")
|
|
|
|
|
|
def test_transfer_wizard_no_currency_conversion(self):
|
|
""" Tests multi currency use of the transfer wizard, verifying that
|
|
currency amounts are kept on distinct lines when transferring to an
|
|
account without any currency specified.
|
|
"""
|
|
active_move_lines = self.move_2.mapped('line_ids').filtered(lambda x: x.name in ('test2_9', 'test2_6', 'test2_8'))
|
|
|
|
# We use a form to pass the context properly to the depends_context move_line_ids field
|
|
context = {'active_model': 'account.move.line', 'active_ids': active_move_lines.ids, 'default_action': 'change_account'}
|
|
with Form(self.env['account.automatic.entry.wizard'].with_context(context)) as wizard_form:
|
|
wizard_form.destination_account_id = self.receivable_account
|
|
wizard_form.journal_id = self.journal
|
|
wizard = wizard_form.save()
|
|
|
|
transfer_move_id = wizard.do_action()['res_id']
|
|
transfer_move = self.env['account.move'].browse(transfer_move_id)
|
|
|
|
destination_lines = transfer_move.line_ids.filtered(lambda x: x.account_id == self.receivable_account)
|
|
self.assertEqual(len(destination_lines), 2, "Two lines should have been created on destination account: one for each currency (the lines with same partner and currency should have been aggregated)")
|
|
self.assertAlmostEqual(destination_lines.filtered(lambda x: x.currency_id == self.test_currency_1).amount_currency, -10, self.test_currency_1.decimal_places)
|
|
self.assertAlmostEqual(destination_lines.filtered(lambda x: x.currency_id == self.test_currency_2).amount_currency, -756, self.test_currency_2.decimal_places)
|