diff --git a/.docker_files/main/__manifest__.py b/.docker_files/main/__manifest__.py index 939a924..43a45d0 100644 --- a/.docker_files/main/__manifest__.py +++ b/.docker_files/main/__manifest__.py @@ -10,8 +10,6 @@ "license": "LGPL-3", "category": "Other", "summary": "Install all addons required for testing.", - "depends": [ - "hr_timesheet", - ], + "depends": ["hr_timesheet", "hr_timesheet_project_parent"], "installable": True, } diff --git a/Dockerfile b/Dockerfile index 881fde3..2f456e2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,5 +10,7 @@ RUN gitoo install-all --conf_file /gitoo.yml --destination "${THIRD_PARTY_ADDONS USER odoo +COPY hr_timesheet_project_parent /mnt/extra-addons/hr_timesheet_project_parent + COPY .docker_files/main /mnt/extra-addons/main COPY .docker_files/odoo.conf /etc/odoo diff --git a/gitoo.yml b/gitoo.yml index 2d937c7..358d5aa 100644 --- a/gitoo.yml +++ b/gitoo.yml @@ -2,6 +2,7 @@ branch: "16.0" includes: - project_timesheet_time_control + - project_parent - url: https://github.com/OCA/timesheet branch: "16.0" @@ -15,3 +16,4 @@ includes: - web_ir_actions_act_multi - web_widget_x2many_2d_matrix + diff --git a/hr_timesheet_project_parent/README.rst b/hr_timesheet_project_parent/README.rst new file mode 100644 index 0000000..3c7c96b --- /dev/null +++ b/hr_timesheet_project_parent/README.rst @@ -0,0 +1,43 @@ +Hr Timesheet Project Parent +==================================== + +This module adds constraints and features related to timesheets and parent-child relationships between projects. It enhances the existing functionalities provided by the OCA module `project_parent`, with a focus on timesheets and their integration into project hierarchies. + +Dependencies +------------ +This module depends on the following OCA module: +- `project_parent`: https://github.com/OCA/project/tree/16.0/project_parent + +Features +-------- + +**Parent Project on Timesheets** + - Adds the `parent_project_id` field to timesheets (`account.analytic.line`) to store the parent project of the linked iteration or project. + + - Automatically computes the parent project based on the hierarchy: + + - If the project has no parent, the `parent_project_id` is the project itself. + + - Otherwise, the `parent_project_id` corresponds to the direct parent of the project. + +**Constraints on Parent Project Changes** + + - Raise ValidationError on changing parent project if timesheets already exist for the project. + +.. image:: static/description/error_change_parent_id.png + +**Enhanced Views** + - Displays the parent project in analytic line views for better visibility. + + - Allows filtering timesheets based on the parent project. + +.. image:: static/description/hr_timesheet_search_view.png + +Contributors +------------ +- Numigi (tm) and all its contributors (https://bit.ly/numigiens) + +More Information +---------------- +For more details, visit: +- https://github.com/OCA/project/tree/16.0/project_parent diff --git a/hr_timesheet_project_parent/__init__.py b/hr_timesheet_project_parent/__init__.py new file mode 100644 index 0000000..6681026 --- /dev/null +++ b/hr_timesheet_project_parent/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2024 Numigi (tm) and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from . import models diff --git a/hr_timesheet_project_parent/__manifest__.py b/hr_timesheet_project_parent/__manifest__.py new file mode 100644 index 0000000..8ee4d61 --- /dev/null +++ b/hr_timesheet_project_parent/__manifest__.py @@ -0,0 +1,17 @@ +# Copyright 2024-today Numigi and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +{ + "name": "Hr Timesheet Project Parent", + "version": "16.0.1.0.0", + "author": "Numigi", + "maintainer": "Numigi", + "website": "https://bit.ly/numigi-com", + "license": "LGPL-3", + "category": "Project", + "depends": ["hr_timesheet", "project_parent"], + "data": [ + "views/account_analytic_line_views.xml", + ], + "installable": True, +} diff --git a/hr_timesheet_project_parent/i18n/fr.po b/hr_timesheet_project_parent/i18n/fr.po new file mode 100644 index 0000000..5225ee6 --- /dev/null +++ b/hr_timesheet_project_parent/i18n/fr.po @@ -0,0 +1,47 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * hr_timesheet_project_parent +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-10 13:48+0000\n" +"PO-Revision-Date: 2024-12-10 13:48+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: hr_timesheet_project_parent +#: model:ir.model,name:hr_timesheet_project_parent.model_account_analytic_line +msgid "Analytic Line" +msgstr "Ligne analytique" + +#. module: hr_timesheet_project_parent +#: model:ir.model.fields,field_description:hr_timesheet_project_parent.field_account_analytic_line__project_id +msgid "Iteration" +msgstr "Projet" + +#. module: hr_timesheet_project_parent +#: model:ir.model.fields,field_description:hr_timesheet_project_parent.field_account_analytic_line__parent_project_id +msgid "Parent Project" +msgstr "" + +#. module: hr_timesheet_project_parent +#: model:ir.model,name:hr_timesheet_project_parent.model_project_project +msgid "Project" +msgstr "Projet" + +#. module: hr_timesheet_project_parent +#. odoo-python +#: code:addons/hr_timesheet_project_parent/models/project_project.py:0 +#, python-format +msgid "" +"Timesheet already exists on this project, to update the Parent Project " +"field, the Project must have no Timesheets." +msgstr "" +"Une feuille de temps existe déjà pour ce projet." +" Pour modifier le champ Projet Parent, le projet ne doit avoir aucune feuille de temps." \ No newline at end of file diff --git a/hr_timesheet_project_parent/models/__init__.py b/hr_timesheet_project_parent/models/__init__.py new file mode 100644 index 0000000..660c26f --- /dev/null +++ b/hr_timesheet_project_parent/models/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2024 Numigi (tm) and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from . import account_analytic_line +from . import project_project diff --git a/hr_timesheet_project_parent/models/account_analytic_line.py b/hr_timesheet_project_parent/models/account_analytic_line.py new file mode 100644 index 0000000..1756265 --- /dev/null +++ b/hr_timesheet_project_parent/models/account_analytic_line.py @@ -0,0 +1,32 @@ +# Copyright 2024 Numigi (tm) and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). +from odoo import api, fields, models + + +class AccountAnalyticLine(models.Model): + + _inherit = "account.analytic.line" + + project_id = fields.Many2one(string="Iteration") + parent_project_id = fields.Many2one( + "project.project", + "Parent Project", + compute="_compute_parent_project_id", + store=True, + index=True, + compute_sudo=True, + ) + + @api.depends("project_id", "project_id.child_ids_count", "project_id.parent_id") + def _compute_parent_project_id(self): + """ + Compute the parent project of an analytic line. + If the project has no parent, then the parent + project is the project itself. + """ + for line in self: + line.parent_project_id = ( + line.project_id.parent_id + if line.project_id.parent_id + else line.project_id + ) diff --git a/hr_timesheet_project_parent/models/project_project.py b/hr_timesheet_project_parent/models/project_project.py new file mode 100644 index 0000000..b9658e3 --- /dev/null +++ b/hr_timesheet_project_parent/models/project_project.py @@ -0,0 +1,23 @@ +# Copyright 2024 Numigi (tm) and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo import api, models, _ +from odoo.exceptions import ValidationError + + +class ProjectProject(models.Model): + + _inherit = "project.project" + + @api.constrains("parent_id") + def _check_cannot_change_parent_while_existing_timesheet(self): + analytic_line_env = self.env["account.analytic.line"] + for project in self: + if analytic_line_env.search([("project_id", "=", project.id)], limit=1): + raise ValidationError( + _( + "Timesheet already exists on this project, to update the Parent " + "Project field, the Project " + "must have no Timesheets." + ) + ) diff --git a/hr_timesheet_project_parent/static/description/error_change_parent_id.png b/hr_timesheet_project_parent/static/description/error_change_parent_id.png new file mode 100644 index 0000000..0082302 Binary files /dev/null and b/hr_timesheet_project_parent/static/description/error_change_parent_id.png differ diff --git a/hr_timesheet_project_parent/static/description/hr_timesheet_search_view.png b/hr_timesheet_project_parent/static/description/hr_timesheet_search_view.png new file mode 100644 index 0000000..4e25f58 Binary files /dev/null and b/hr_timesheet_project_parent/static/description/hr_timesheet_search_view.png differ diff --git a/hr_timesheet_project_parent/static/description/icon.png b/hr_timesheet_project_parent/static/description/icon.png new file mode 100644 index 0000000..92a86b1 Binary files /dev/null and b/hr_timesheet_project_parent/static/description/icon.png differ diff --git a/hr_timesheet_project_parent/tests/__init__.py b/hr_timesheet_project_parent/tests/__init__.py new file mode 100644 index 0000000..698bf5d --- /dev/null +++ b/hr_timesheet_project_parent/tests/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2024 Numigi (tm) and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from . import test_account_analytic_line diff --git a/hr_timesheet_project_parent/tests/test_account_analytic_line.py b/hr_timesheet_project_parent/tests/test_account_analytic_line.py new file mode 100644 index 0000000..be5e44e --- /dev/null +++ b/hr_timesheet_project_parent/tests/test_account_analytic_line.py @@ -0,0 +1,57 @@ +# Copyright 2024 Numigi (tm) and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + + +import pytest +from odoo.exceptions import ValidationError +from odoo.tests.common import TransactionCase + + +class TestProjectIterationWithTimeSheet(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.project_1 = cls.env["project.project"].create({"name": "Project 1"}) + cls.project_2 = cls.env["project.project"].create({"name": "Project 2"}) + + cls.iteration_1 = cls.env["project.project"].create( + { + "name": "Iteration 1", + "parent_id": cls.project_1.id, + } + ) + cls.iteration_2 = cls.env["project.project"].create( + { + "name": "Iteration 2", + "parent_id": cls.project_1.id, + } + ) + + def _create_timesheet(self, project): + + return self.env["account.analytic.line"].create( + { + "name": "Do something", + "unit_amount": 1, + "project_id": project.id, + "employee_id": self.env.ref("hr.employee_admin").id, + } + ) + + def test_analytic_line_parent_project_id(self): + line_1 = self._create_timesheet(self.iteration_1) + self.assertEqual(line_1.parent_project_id, self.project_1) + line_2 = self._create_timesheet(self.project_2) + self.assertEqual(line_2.parent_project_id, self.project_2) + + def test_block_setting_parent_on_project_with_timesheet(self): + self._create_timesheet(self.project_1) + with pytest.raises(ValidationError): + self.project_1.parent_id = self.project_2 + + def test_allow_setting_project_with_timesheet_as_parent(self): + self._create_timesheet(self.project_1) + self.project_2.parent_id = self.project_1 + + def test_allow_setting_project_without_timesheet_as_parent(self): + self.project_2.parent_id = self.project_1 diff --git a/hr_timesheet_project_parent/views/account_analytic_line_views.xml b/hr_timesheet_project_parent/views/account_analytic_line_views.xml new file mode 100644 index 0000000..239c21a --- /dev/null +++ b/hr_timesheet_project_parent/views/account_analytic_line_views.xml @@ -0,0 +1,26 @@ + + + + + Analytic Line Search With Parent Project + account.analytic.line + + + + + + + + + + Timesheet Line Search With Parent Project + account.analytic.line + + + + + + + + +