diff --git a/account_analytic_line_split/__manifest__.py b/account_analytic_line_split/__manifest__.py index c825de160..4a5906623 100644 --- a/account_analytic_line_split/__manifest__.py +++ b/account_analytic_line_split/__manifest__.py @@ -16,5 +16,4 @@ "wizard/analytic_line_split_wizard.xml", "security/ir.model.access.csv", ], - "demo": [], } diff --git a/account_analytic_line_split/models/account_analytic_line.py b/account_analytic_line_split/models/account_analytic_line.py index 8dfd3e350..dbbc3c55c 100644 --- a/account_analytic_line_split/models/account_analytic_line.py +++ b/account_analytic_line_split/models/account_analytic_line.py @@ -1,32 +1,25 @@ # Copyright 2024 - TODAY, Wesley Oliveira # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import api, fields, models +from odoo import fields, models class AccountAnalyticLine(models.Model): _inherit = "account.analytic.line" - parent_id = fields.Many2one( + parent_line_id = fields.Many2one( comodel_name="account.analytic.line", string="Origin", ondelete="cascade", readonly=True, ) - child_ids = fields.One2many( + child_line_ids = fields.One2many( comodel_name="account.analytic.line", - inverse_name="parent_id", + inverse_name="parent_line_id", string="Child Lines", - compute="_compute_child_ids", - store=True, readonly=True, ) - @api.depends("parent_id") - def _compute_child_ids(self): - for line in self: - line.child_ids = self.search([("parent_id", "=", line.id)]) - def action_edit_analytic_line(self): context = dict(self.env.context) context["form_view_initial_mode"] = "edit" diff --git a/account_analytic_line_split/tests/__init__.py b/account_analytic_line_split/tests/__init__.py new file mode 100644 index 000000000..f145efd67 --- /dev/null +++ b/account_analytic_line_split/tests/__init__.py @@ -0,0 +1,3 @@ +from . import test_account_analytic_line +from . import test_account_move +from . import test_analytic_line_split_wizard diff --git a/account_analytic_line_split/tests/test_account_analytic_line.py b/account_analytic_line_split/tests/test_account_analytic_line.py new file mode 100644 index 000000000..03cecd199 --- /dev/null +++ b/account_analytic_line_split/tests/test_account_analytic_line.py @@ -0,0 +1,34 @@ +# Copyright 2024 - TODAY, Wesley Oliveira +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tests.common import SavepointCase + + +class TestAccountAnalyticLine(SavepointCase): + @classmethod + def setUpClass(cls): + super(TestAccountAnalyticLine, cls).setUpClass() + + cls.analytic_account = cls.env["account.analytic.account"].create( + { + "name": "Test Analytic Account", + } + ) + cls.analytic_line = cls.env["account.analytic.line"].create( + { + "name": "Test Analytic Line", + "amount": 100, + "account_id": cls.analytic_account.id, + } + ) + + def test_action_edit_analytic_line(self): + action = self.analytic_line.action_edit_analytic_line() + self.assertEqual(action["res_id"], self.analytic_line.id) + self.assertEqual(action["views"], [(False, "form")]) + + def test_action_split_analytic_line(self): + action = self.analytic_line.action_split_analytic_line() + self.assertEqual(action["context"]["active_id"], self.analytic_line.id) + self.assertEqual(action["context"]["account_id"], self.analytic_account.id) + self.assertEqual(action["context"]["amount"], self.analytic_line.amount) diff --git a/account_analytic_line_split/tests/test_account_move.py b/account_analytic_line_split/tests/test_account_move.py new file mode 100644 index 000000000..a518b3331 --- /dev/null +++ b/account_analytic_line_split/tests/test_account_move.py @@ -0,0 +1,72 @@ +# Copyright 2024 - TODAY, Wesley Oliveira +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tests.common import SavepointCase + + +class TestAccountMove(SavepointCase): + @classmethod + def setUpClass(cls): + super(TestAccountMove, cls).setUpClass() + + cls.account = cls.env["account.account"].create( + { + "code": "test_account_01", + "name": "test account", + "user_type_id": cls.env.ref("account.data_account_type_receivable").id, + "reconcile": True, + } + ) + cls.account_2 = cls.env["account.account"].create( + { + "code": "test_account_02", + "name": "test account 2", + "user_type_id": cls.env.ref("account.data_account_type_payable").id, + "reconcile": True, + } + ) + cls.analytic_account = cls.env["account.analytic.account"].create( + { + "name": "Test Analytic Account", + } + ) + cls.analytic_line = cls.env["account.analytic.line"].create( + { + "name": "Test Analytic Line", + "amount": 100, + "account_id": cls.analytic_account.id, + } + ) + cls.account_move = cls.env["account.move"].create( + { + "name": "Test Move", + "line_ids": [ + ( + 0, + 0, + { + "name": "Test Move Line", + "debit": 50, + "credit": 0, + "account_id": cls.account.id, + "analytic_account_id": cls.analytic_account.id, + "analytic_line_ids": [(6, 0, [cls.analytic_line.id])], + }, + ), + ( + 0, + 0, + { + "name": "Test Move Line", + "debit": 0, + "credit": 50, + "account_id": cls.account_2.id, + }, + ), + ], + } + ) + + def test_compute_analytic_line_ids(self): + self.account_move._compute_analytic_line_ids() + self.assertIn(self.analytic_line, self.account_move.analytic_line_ids) diff --git a/account_analytic_line_split/tests/test_analytic_line_split_wizard.py b/account_analytic_line_split/tests/test_analytic_line_split_wizard.py new file mode 100644 index 000000000..69e4ba711 --- /dev/null +++ b/account_analytic_line_split/tests/test_analytic_line_split_wizard.py @@ -0,0 +1,99 @@ +# Copyright 2024 - TODAY, Wesley Oliveira +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.exceptions import ValidationError +from odoo.tests.common import SavepointCase + + +class TestAnalyticLineSplitWizard(SavepointCase): + @classmethod + def setUpClass(cls): + super(TestAnalyticLineSplitWizard, cls).setUpClass() + + cls.wizard_obj = cls.env["analytic.line.split.wizard"] + cls.split_obj = cls.env["analytic.line.split"] + cls.analytic_account = cls.env["account.analytic.account"].create( + { + "name": "Test Analytic Account", + } + ) + cls.analytic_line = cls.env["account.analytic.line"].create( + { + "name": "Test Analytic Line", + "amount": 100, + "account_id": cls.analytic_account.id, + } + ) + cls.wizard = cls.wizard_obj.with_context(active_id=cls.analytic_line.id).create( + { + "amount_total": 100, + } + ) + + def test_compute_percentage(self): + line_split = self.split_obj.create( + { + "wizard_id": self.wizard.id, + "account_id": self.analytic_account.id, + "percentage": 30, + } + ) + self.wizard._compute_percentage() + self.assertEqual(self.wizard.percentage, 70) + self.assertEqual(line_split.percentage, 30) + + def test_compute_amount(self): + line_split = self.split_obj.create( + { + "wizard_id": self.wizard.id, + "account_id": self.analytic_account.id, + "percentage": 30, + } + ) + self.wizard._compute_amount() + self.assertEqual(self.wizard.amount, 70) + self.assertEqual(line_split.amount, 30) + + def test_action_split_line(self): + line_split = self.split_obj.create( + { + "wizard_id": self.wizard.id, + "account_id": self.analytic_account.id, + "percentage": 30, + } + ) + self.wizard.action_split_line() + self.assertEqual(self.analytic_line.amount, 70) + self.assertEqual(line_split.amount, 30) + new_line = self.env["account.analytic.line"].search( + [("parent_line_id", "=", self.analytic_line.id)] + ) + self.assertIn(new_line, self.analytic_line.child_line_ids) + + def test_check_percentage(self): + with self.assertRaises(ValidationError): + self.split_obj.create( + { + "wizard_id": self.wizard.id, + "account_id": self.analytic_account.id, + "percentage": 110, + } + ) + + def test_onchange_analytic_line_split_ids(self): + with self.assertRaises(ValidationError): + self.split_obj.create( + { + "wizard_id": self.wizard.id, + "account_id": self.analytic_account.id, + "percentage": 50, + } + ) + self.split_obj.create( + { + "wizard_id": self.wizard.id, + "account_id": self.analytic_account.id, + "percentage": 60, + } + ) + self.wizard._onchange_analytic_line_split_ids() diff --git a/account_analytic_line_split/views/account_analytic_line.xml b/account_analytic_line_split/views/account_analytic_line.xml index e9c27b1bf..c74dd7c33 100644 --- a/account_analytic_line_split/views/account_analytic_line.xml +++ b/account_analytic_line_split/views/account_analytic_line.xml @@ -24,14 +24,14 @@ - + - + diff --git a/account_analytic_line_split/wizard/__init__.py b/account_analytic_line_split/wizard/__init__.py index a2c2051c2..d061e02fb 100644 --- a/account_analytic_line_split/wizard/__init__.py +++ b/account_analytic_line_split/wizard/__init__.py @@ -1 +1,2 @@ from . import analytic_line_split_wizard +from . import analytic_line_split diff --git a/account_analytic_line_split/wizard/analytic_line_split.py b/account_analytic_line_split/wizard/analytic_line_split.py new file mode 100644 index 000000000..fec79c989 --- /dev/null +++ b/account_analytic_line_split/wizard/analytic_line_split.py @@ -0,0 +1,60 @@ +# Copyright 2024 - TODAY, Wesley Oliveira +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class AnalyticLineSplit(models.TransientModel): + _name = "analytic.line.split" + _description = "Analytic Line Split" + + def _get_default_line_id(self): + analytic_line_id = self.env.context.get("line_id") + return self.env["account.analytic.line"].browse(analytic_line_id) + + wizard_id = fields.Many2one( + comodel_name="analytic.line.split.wizard", + required=True, + ondelete="cascade", + ) + line_id = fields.Many2one( + comodel_name="account.analytic.line", + string="Analytic Line", + readonly=True, + default=_get_default_line_id, + ) + name = fields.Char( + string="Name", + related="account_id.name", + readonly=True, + ) + account_id = fields.Many2one( + comodel_name="account.analytic.account", + string="Analytic Account", + required=True, + ) + percentage = fields.Float( + string="Percentage", + required=True, + default=0.0, + ) + amount = fields.Float( + string="Amount", + readonly=True, + compute="_compute_amount", + store=True, + ) + + @api.constrains("percentage") + def _check_percentage(self): + for line in self: + if not (0 < line.percentage < 100): + raise ValidationError( + _("Percentage must be greater than 0 and less than 100.") + ) + + @api.depends("wizard_id.amount_total", "percentage") + def _compute_amount(self): + for line in self: + line.amount = line.wizard_id.amount_total * (line.percentage / 100) diff --git a/account_analytic_line_split/wizard/analytic_line_split_wizard.py b/account_analytic_line_split/wizard/analytic_line_split_wizard.py index 1eeba7efa..eb56f7342 100644 --- a/account_analytic_line_split/wizard/analytic_line_split_wizard.py +++ b/account_analytic_line_split/wizard/analytic_line_split_wizard.py @@ -73,78 +73,17 @@ def _compute_amount(self): @api.onchange("analytic_line_split_ids") def _onchange_analytic_line_split_ids(self): total_percentage = sum(line.percentage for line in self.analytic_line_split_ids) - if total_percentage > 100: - raise ValidationError(_("The total percentage cannot exceed 100%!")) + if total_percentage >= 100: + raise ValidationError(_("The total percentage cannot exceed or be 100%!")) def action_split_line(self): - self.line_id.write( - { - "amount": self.amount, - } - ) + self.line_id.write({"amount": self.amount}) for line in self.analytic_line_split_ids: copy_line_id = self.line_id.copy() copy_line_id.write( { "account_id": line.account_id, "amount": line.amount, - "parent_id": self.line_id, + "parent_line_id": self.line_id, } ) - - -class AnalyticLineSplit(models.TransientModel): - _name = "analytic.line.split" - _description = "Analytic Line Split" - - def _get_default_line_id(self): - analytic_line_id = self.env.context.get("line_id") - return self.env["account.analytic.line"].browse(analytic_line_id) - - wizard_id = fields.Many2one( - comodel_name="analytic.line.split.wizard", - required=True, - ondelete="cascade", - ) - line_id = fields.Many2one( - comodel_name="account.analytic.line", - string="Analytic Line", - readonly=True, - default=_get_default_line_id, - ) - name = fields.Char( - string="Name", - related="account_id.name", - readonly=True, - ) - account_id = fields.Many2one( - comodel_name="account.analytic.account", - string="Analytic Account", - required=True, - ) - percentage = fields.Float( - string="Percentage", - required=True, - default=0.0, - ) - amount = fields.Float( - string="Amount", - readonly=True, - compute="_compute_amount", - store=True, - ) - - @api.constrains("percentage") - def _check_percentage(self): - for line in self: - if not (0 < line.percentage <= 100): - raise ValidationError( - _( - "Percentage must be greater than 0 and less than or equal to 100." - ) - ) - - @api.depends("wizard_id.amount_total", "percentage") - def _compute_amount(self): - for line in self: - line.amount = line.wizard_id.amount_total * (line.percentage / 100)