diff --git a/account_move_line_tax_single/__init__.py b/account_move_line_tax_single/__init__.py new file mode 100644 index 000000000000..c0d9f3d12f7d --- /dev/null +++ b/account_move_line_tax_single/__init__.py @@ -0,0 +1,4 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import models +from .hooks import post_init_hook diff --git a/account_move_line_tax_single/__manifest__.py b/account_move_line_tax_single/__manifest__.py new file mode 100644 index 000000000000..b08a470d6f3a --- /dev/null +++ b/account_move_line_tax_single/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright 2020 CorporateHub (https://corporatehub.eu) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Accounting: Account Move Line Tax (single)", + "summary": "Allows only one tax to be applied to an account move line.", + "version": "12.0.1.0.0", + "category": "Accounting", + "website": "https://github.com/OCA/account-financial-tools", + "author": "CorporateHub, Odoo Community Association (OCA)", + "maintainers": ["alexey-pelykh"], + "license": "AGPL-3", + "application": False, + "installable": True, + "post_init_hook": "post_init_hook", + "depends": [ + "account", + ], + "data": [ + "views/account_move_line.xml", + ], +} diff --git a/account_move_line_tax_single/hooks.py b/account_move_line_tax_single/hooks.py new file mode 100644 index 000000000000..658ec89006a4 --- /dev/null +++ b/account_move_line_tax_single/hooks.py @@ -0,0 +1,11 @@ +# Copyright 2020 CorporateHub (https://corporatehub.eu) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, SUPERUSER_ID + + +def post_init_hook(cr, registry): + env = api.Environment(cr, SUPERUSER_ID, {}) + statement_lines = env["account.move.line"].search([]) + statement_lines._compute_tax_id() + statement_lines._inverse_tax_id() diff --git a/account_move_line_tax_single/models/__init__.py b/account_move_line_tax_single/models/__init__.py new file mode 100644 index 000000000000..bc7621f756ac --- /dev/null +++ b/account_move_line_tax_single/models/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import account_move_line diff --git a/account_move_line_tax_single/models/account_move_line.py b/account_move_line_tax_single/models/account_move_line.py new file mode 100644 index 000000000000..fb3c4695cbfb --- /dev/null +++ b/account_move_line_tax_single/models/account_move_line.py @@ -0,0 +1,50 @@ +# Copyright 2020 CorporateHub (https://corporatehub.eu) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import logging + +from odoo import api, fields, models, _ +from odoo.exceptions import ValidationError + +_logger = logging.getLogger(__name__) + + +class AccountMoveLine(models.Model): + _inherit = "account.move.line" + + tax_id = fields.Many2one( + string="Tax Applied", + comodel_name="account.tax", + domain=["|", ("active", "=", False), ("active", "=", True)], + compute="_compute_tax_id", + inverse="_inverse_tax_id", + store=True, + ) + + @api.multi + @api.depends("tax_ids") + def _compute_tax_id(self): + for line in self: + line.tax_id = line.tax_ids[:1] + + @api.multi + def _inverse_tax_id(self): + for line in self: + if len(line.tax_ids) > 1: + _logger.warning( + _( + "Account Move Line #%s has more than one tax applied!" + ) % ( + line.id, + ) + ) + line.tax_ids |= line.tax_id + continue + line.tax_ids = line.tax_id + + @api.constrains("tax_ids") + def _validate_single_tax(self): + if self.env.context.get("skip_validate_single_tax"): + return + if self.filtered(lambda line: len(line.tax_ids) > 1): + raise ValidationError(_("Only single tax per line is allowed.")) diff --git a/account_move_line_tax_single/readme/CONTRIBUTORS.rst b/account_move_line_tax_single/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000000..724bc1d03a2b --- /dev/null +++ b/account_move_line_tax_single/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `CorporateHub `__ + + * Alexey Pelykh diff --git a/account_move_line_tax_single/readme/DESCRIPTION.rst b/account_move_line_tax_single/readme/DESCRIPTION.rst new file mode 100644 index 000000000000..93c07d272a0b --- /dev/null +++ b/account_move_line_tax_single/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +This module alters the functionality of Accounting to use only single tax per +Account Move Line. diff --git a/account_move_line_tax_single/tests/__init__.py b/account_move_line_tax_single/tests/__init__.py new file mode 100644 index 000000000000..8c0c3d828f4f --- /dev/null +++ b/account_move_line_tax_single/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import test_account_move_line diff --git a/account_move_line_tax_single/tests/test_account_move_line.py b/account_move_line_tax_single/tests/test_account_move_line.py new file mode 100644 index 000000000000..004ebba717f5 --- /dev/null +++ b/account_move_line_tax_single/tests/test_account_move_line.py @@ -0,0 +1,108 @@ +# Copyright 2020 CorporateHub (https://corporatehub.eu) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tests.common import TransactionCase +from odoo.exceptions import ValidationError + + +class TestAccountMoveLine(TransactionCase): + + def setUp(self): + super().setUp() + + self.Account = self.env["account.account"] + self.Move = self.env["account.move"] + self.Journal = self.env["account.journal"] + self.Tax = self.env["account.tax"] + + def constraint(self): + account = self.Account.create({ + "code": "1", + "name": "Account", + }) + journal = self.Journal.create({ + "name": "Journal", + "type": "sale", + "code": "JOURNAL", + }) + tax_1 = self.Tax.create({ + "name": "Tax 1", + "amount": 15.0, + "amount_type": "percent", + "type_tax_use": "sale", + }) + tax_2 = self.Tax.create({ + "name": "Tax 2", + "amount": 15.0, + "amount_type": "percent", + "type_tax_use": "sale", + }) + with self.assertRaises(ValidationError): + self.Move.create({ + "journal_id": journal.id, + "name": "Move", + "date": "2020-11-01", + "line_ids": [ + (0, 0, { + "name": "Move Line", + "debit": 0.0, + "credit": 1000.0, + "account_id": account.id, + "tax_ids": [(6, 0, [tax_1, tax_2])], + }), + ], + }) + + def nondestructive(self): + account = self.Account.create({ + "code": "1", + "name": "Account", + }) + journal = self.Journal.create({ + "name": "Journal", + "type": "sale", + "code": "JOURNAL", + }) + tax_1 = self.Tax.create({ + "name": "Tax 1", + "amount": 15.0, + "amount_type": "percent", + "type_tax_use": "sale", + }) + tax_2 = self.Tax.create({ + "name": "Tax 2", + "amount": 15.0, + "amount_type": "percent", + "type_tax_use": "sale", + }) + tax_3 = self.Tax.create({ + "name": "Tax 3", + "amount": 15.0, + "amount_type": "percent", + "type_tax_use": "sale", + }) + move = self.Move.with_context(skip_validate_single_tax=True).create({ + "journal_id": journal.id, + "name": "Move", + "date": "2020-11-01", + "line_ids": [ + (0, 0, { + "name": "Move Line", + "debit": 0.0, + "credit": 1000.0, + "account_id": account.id, + "tax_ids": [(6, 0, [tax_1, tax_2])], + }), + ], + }) + + move.line_ids.tax_id = tax_2 + self.assertEqual(move.line_ids.tax_ids, (tax_1 | tax_2)) + + with self.assertLogs() as logger_context: + move.line_ids.tax_id = tax_3 + self.assertRegex( + logger_context.output[0], + r"Account Move Line #\d+ has more than one tax applied!" + ) + self.assertEqual(move.line_ids.tax_ids, (tax_1 | tax_2 | tax_3)) diff --git a/account_move_line_tax_single/views/account_move_line.xml b/account_move_line_tax_single/views/account_move_line.xml new file mode 100644 index 000000000000..82f012147bd6 --- /dev/null +++ b/account_move_line_tax_single/views/account_move_line.xml @@ -0,0 +1,36 @@ + + + + + + account.move.line.form + account.move.line + + + + + + + base.group_no_one + + + + + + account.move.line.filter + account.move.line + + + + + + + + + + + +