From e4eb845af59d45fdd13f7cc88d083549b890427e Mon Sep 17 00:00:00 2001 From: Laurent Smet Date: Fri, 13 Dec 2019 08:01:19 +0000 Subject: [PATCH] [FIX] account: Fix missing constrains about multi-companies. There is actually nothing preventing changing the company of some records even there is some journal entries/items that are using them. closes odoo/odoo#41945 X-original-commit: b3b66304eb98819cd87d6e4c7a29ca4803ffc3ff Signed-off-by: oco-odoo --- addons/account/i18n/account.pot | 30 +++++++++ addons/account/models/account.py | 59 +++++++++++++++++- .../account/models/account_analytic_line.py | 49 ++++++++++++++- addons/account/tests/__init__.py | 4 ++ addons/account/tests/test_account_account.py | 39 ++++++++++++ addons/account/tests/test_account_analytic.py | 62 +++++++++++++++++++ addons/account/tests/test_account_journal.py | 30 +++++++++ addons/account/tests/test_account_tax.py | 40 ++++++++++++ 8 files changed, 311 insertions(+), 2 deletions(-) create mode 100644 addons/account/tests/test_account_account.py create mode 100644 addons/account/tests/test_account_analytic.py create mode 100644 addons/account/tests/test_account_journal.py create mode 100644 addons/account/tests/test_account_tax.py diff --git a/addons/account/i18n/account.pot b/addons/account/i18n/account.pot index 2ce5f729127..ba0010f0906 100644 --- a/addons/account/i18n/account.pot +++ b/addons/account/i18n/account.pot @@ -12082,3 +12082,33 @@ msgstr "" #: model_terms:ir.ui.view,arch_db:account.view_bank_statement_form msgid "→ Count" msgstr "" + +#. module: account +#: code:addons/account/models/account.py:0 +#, python-format +msgid "You can't change the company of your account since there are some journal items linked to it." +msgstr "" + +#. module: account +#: code:addons/account/models/account.py:0 +#, python-format +msgid "You can't change the company of your journal since there are some journal entries linked to it." +msgstr "" + +#. module: account +#: code:addons/account/models/account.py:0 +#, python-format +msgid "You can't change the company of your tax since there are some journal items linked to it." +msgstr "" + +#. module: account +#: code:addons/account/models/account_analytic_line.py:0 +#, python-format +msgid "You can't set a different company on your analytic account since there are some journal items linked to it." +msgstr "" + +#. module: account +#: code:addons/account/models/account_analytic_line.py:0 +#, python-format +msgid "You can't set a different company on your analytic tags since there are some journal items linked to it." +msgstr "" diff --git a/addons/account/models/account.py b/addons/account/models/account.py index 524efd3adc6..f335bd66354 100644 --- a/addons/account/models/account.py +++ b/addons/account/models/account.py @@ -406,6 +406,22 @@ class AccountAccount(models.Model): if ids: raise ValidationError(_('Some journal items already exist with this account but in other journals than the allowed ones.')) + @api.constrains('company_id') + def _check_company_consistency(self): + if not self: + return + + self.flush(['company_id']) + self._cr.execute(''' + SELECT line.id + FROM account_move_line line + JOIN account_account account ON account.id = line.account_id + WHERE line.account_id IN %s + AND line.company_id != account.company_id + ''', [tuple(self.ids)]) + if self._cr.fetchone(): + raise UserError(_("You can't change the company of your account since there are some journal items linked to it.")) + @api.depends('code') def _compute_account_root(self): # this computes the first 2 digits of the account. @@ -1059,6 +1075,22 @@ class AccountJournal(models.Model): if journal.bank_account_id.partner_id != journal.company_id.partner_id: raise ValidationError(_('The holder of a journal\'s bank account must be the company (%s).') % journal.company_id.name) + @api.constrains('company_id') + def _check_company_consistency(self): + if not self: + return + + self.flush(['company_id']) + self._cr.execute(''' + SELECT move.id + FROM account_move move + JOIN account_journal journal ON journal.id = move.journal_id + WHERE move.journal_id IN %s + AND move.company_id != journal.company_id + ''', [tuple(self.ids)]) + if self._cr.fetchone(): + raise UserError(_("You can't change the company of your journal since there are some journal entries linked to it.")) + @api.onchange('default_debit_account_id') def onchange_debit_account_id(self): if not self.default_credit_account_id: @@ -1541,7 +1573,32 @@ class AccountTax(models.Model): if not tax._check_m2m_recursion('children_tax_ids'): raise ValidationError(_("Recursion found for tax '%s'.") % (tax.name,)) if not all(child.type_tax_use in ('none', tax.type_tax_use) for child in tax.children_tax_ids): - raise ValidationError(_('The application scope of taxes in a group must be either the same as the group or left empty.')) + raise ValidationError(_('The application scope of taxes in a group must be either the same as the group or left empty.'))\ + + @api.constrains('company_id') + def _check_company_consistency(self): + if not self: + return + + self.flush(['company_id']) + self._cr.execute(''' + SELECT line.id + FROM account_move_line line + JOIN account_tax tax ON tax.id = line.tax_line_id + WHERE line.tax_line_id IN %s + AND line.company_id != tax.company_id + + UNION ALL + + SELECT line.id + FROM account_move_line_account_tax_rel tax_rel + JOIN account_tax tax ON tax.id = tax_rel.account_tax_id + JOIN account_move_line line ON line.id = tax_rel.account_move_line_id + WHERE tax_rel.account_tax_id IN %s + AND line.company_id != tax.company_id + ''', [tuple(self.ids)] * 2) + if self._cr.fetchone(): + raise UserError(_("You can't change the company of your tax since there are some journal items linked to it.")) @api.returns('self', lambda value: value.id) def copy(self, default=None): diff --git a/addons/account/models/account_analytic_line.py b/addons/account/models/account_analytic_line.py index 60e80ab015a..3b02d93e5b2 100644 --- a/addons/account/models/account_analytic_line.py +++ b/addons/account/models/account_analytic_line.py @@ -1,7 +1,54 @@ # -*- coding: utf-8 -*- from odoo import api, fields, models, _ -from math import copysign +from odoo.exceptions import UserError + + +class AccountAnalyticAccount(models.Model): + _inherit = 'account.analytic.account' + + @api.constrains('company_id') + def _check_company_consistency(self): + analytic_accounts = self.filtered('company_id') + + if not analytic_accounts: + return + + self.flush(['company_id']) + self._cr.execute(''' + SELECT line.id + FROM account_move_line line + JOIN account_analytic_account account ON account.id = line.analytic_account_id + WHERE line.analytic_account_id IN %s + AND line.company_id != account.company_id + ''', [tuple(analytic_accounts.ids)]) + + if self._cr.fetchone(): + raise UserError(_("You can't set a different company on your analytic account since there are some journal items linked to it.")) + + +class AccountAnalyticTag(models.Model): + _inherit = 'account.analytic.tag' + + @api.constrains('company_id') + def _check_company_consistency(self): + analytic_tags = self.filtered('company_id') + + if not analytic_tags: + return + + self.flush(['company_id']) + self._cr.execute(''' + SELECT line.id + FROM account_analytic_tag_account_move_line_rel tag_rel + JOIN account_analytic_tag tag ON tag.id = tag_rel.account_analytic_tag_id + JOIN account_move_line line ON line.id = tag_rel.account_move_line_id + WHERE tag_rel.account_analytic_tag_id IN %s + AND line.company_id != tag.company_id + ''', [tuple(analytic_tags.ids)]) + + if self._cr.fetchone(): + raise UserError(_("You can't set a different company on your analytic tags since there are some journal items linked to it.")) class AccountAnalyticLine(models.Model): diff --git a/addons/account/tests/__init__.py b/addons/account/tests/__init__.py index 4614179269f..f160b87453f 100644 --- a/addons/account/tests/__init__.py +++ b/addons/account/tests/__init__.py @@ -5,6 +5,10 @@ from . import test_account_move_out_refund from . import test_account_move_in_invoice from . import test_account_move_in_refund from . import test_account_move_entry +from . import test_account_journal +from . import test_account_account +from . import test_account_tax +from . import test_account_analytic from . import test_account_payment from . import test_account_move_partner_count from . import test_account_move_rounding diff --git a/addons/account/tests/test_account_account.py b/addons/account/tests/test_account_account.py new file mode 100644 index 00000000000..61ed7666cbb --- /dev/null +++ b/addons/account/tests/test_account_account.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +from odoo.addons.account.tests.common import AccountTestInvoicingCommon +from odoo.tests import tagged +from odoo.exceptions import UserError + + +@tagged('post_install', '-at_install') +class TestAccountAccount(AccountTestInvoicingCommon): + + @classmethod + def setUpClass(cls): + super().setUpClass() + + # Create another company. + cls.company_data_2 = cls.setup_company_data('company_2_data') + + # By default, tests are run with the current user set on the first company. + cls.env.user.company_id = cls.company_data['company'] + + def test_changing_account_company(self): + ''' Ensure you can't change the company of an account.account if there are some journal entries ''' + + self.env['account.move'].create({ + 'type': 'entry', + 'date': '2019-01-01', + 'line_ids': [ + (0, 0, { + 'name': 'line_debit', + 'account_id': self.company_data['default_account_revenue'].id, + }), + (0, 0, { + 'name': 'line_credit', + 'account_id': self.company_data['default_account_revenue'].id, + }), + ], + }) + + with self.assertRaises(UserError), self.cr.savepoint(): + self.company_data['default_account_revenue'].company_id = self.company_data_2['company'] diff --git a/addons/account/tests/test_account_analytic.py b/addons/account/tests/test_account_analytic.py new file mode 100644 index 00000000000..c8251f71838 --- /dev/null +++ b/addons/account/tests/test_account_analytic.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +from odoo.addons.account.tests.common import AccountTestInvoicingCommon +from odoo.tests import tagged +from odoo.exceptions import UserError + + +@tagged('post_install', '-at_install') +class TestAccountAnalyticAccount(AccountTestInvoicingCommon): + + @classmethod + def setUpClass(cls): + super().setUpClass() + + cls.env.user.write({ + 'groups_id': [ + (4, cls.env.ref('analytic.group_analytic_accounting').id), + (4, cls.env.ref('analytic.group_analytic_tags').id), + ], + }) + + # Create another company. + cls.company_data_2 = cls.setup_company_data('company_2_data') + + # By default, tests are run with the current user set on the first company. + cls.env.user.company_id = cls.company_data['company'] + + cls.test_analytic_account = cls.env['account.analytic.account'].create({'name': 'test_analytic_account'}) + cls.test_analytic_tag = cls.env['account.analytic.tag'].create({'name': 'test_analytic_tag'}) + + def test_changing_analytic_company(self): + ''' Ensure you can't change the company of an account.analytic.account if there are some journal entries ''' + + self.env['account.move'].create({ + 'type': 'entry', + 'date': '2019-01-01', + 'line_ids': [ + (0, 0, { + 'name': 'line_debit', + 'account_id': self.company_data['default_account_revenue'].id, + 'analytic_account_id': self.test_analytic_account.id, + 'analytic_tag_ids': [(6, 0, self.test_analytic_tag.ids)], + }), + (0, 0, { + 'name': 'line_credit', + 'account_id': self.company_data['default_account_revenue'].id, + }), + ], + }) + + # Set a different company on the analytic account. + with self.assertRaises(UserError), self.cr.savepoint(): + self.test_analytic_account.company_id = self.company_data_2['company'] + + # Making the analytic account not company dependent is allowed. + self.test_analytic_account.company_id = False + + # Set a different company on the analytic tag. + with self.assertRaises(UserError), self.cr.savepoint(): + self.test_analytic_tag.company_id = self.company_data_2['company'] + + # Making the analytic tag not company dependent is allowed. + self.test_analytic_tag.company_id = False diff --git a/addons/account/tests/test_account_journal.py b/addons/account/tests/test_account_journal.py new file mode 100644 index 00000000000..10d2c58d9e5 --- /dev/null +++ b/addons/account/tests/test_account_journal.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +from odoo.addons.account.tests.common import AccountTestInvoicingCommon +from odoo.tests import tagged +from odoo.exceptions import UserError + + +@tagged('post_install', '-at_install') +class TestAccountJournal(AccountTestInvoicingCommon): + + @classmethod + def setUpClass(cls): + super().setUpClass() + + # Create another company. + cls.company_data_2 = cls.setup_company_data('company_2_data') + + # By default, tests are run with the current user set on the first company. + cls.env.user.company_id = cls.company_data['company'] + + def test_changing_journal_company(self): + ''' Ensure you can't change the company of an account.journal if there are some journal entries ''' + + self.env['account.move'].create({ + 'type': 'entry', + 'date': '2019-01-01', + 'journal_id': self.company_data['default_journal_sale'].id, + }) + + with self.assertRaises(UserError), self.cr.savepoint(): + self.company_data['default_journal_sale'].company_id = self.company_data_2['company'] diff --git a/addons/account/tests/test_account_tax.py b/addons/account/tests/test_account_tax.py new file mode 100644 index 00000000000..6a81294bcec --- /dev/null +++ b/addons/account/tests/test_account_tax.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +from odoo.addons.account.tests.common import AccountTestInvoicingCommon +from odoo.tests import tagged +from odoo.exceptions import UserError + + +@tagged('post_install', '-at_install') +class TestAccountTax(AccountTestInvoicingCommon): + + @classmethod + def setUpClass(cls): + super().setUpClass() + + # Create another company. + cls.company_data_2 = cls.setup_company_data('company_2_data') + + # By default, tests are run with the current user set on the first company. + cls.env.user.company_id = cls.company_data['company'] + + def test_changing_tax_company(self): + ''' Ensure you can't change the company of an account.tax if there are some journal entries ''' + + # Avoid duplicate key value violates unique constraint "account_tax_name_company_uniq". + self.company_data['default_tax_sale'].name = 'test_changing_account_company' + + self.env['account.move'].create({ + 'type': 'out_invoice', + 'date': '2019-01-01', + 'invoice_line_ids': [ + (0, 0, { + 'name': 'invoice_line', + 'quantity': 1.0, + 'price_unit': 100.0, + 'tax_ids': [(6, 0, self.company_data['default_tax_sale'].ids)], + }), + ], + }) + + with self.assertRaises(UserError), self.cr.savepoint(): + self.company_data['default_tax_sale'].company_id = self.company_data_2['company']