diff --git a/project_task_parent_completion_blocking/README.rst b/project_task_parent_completion_blocking/README.rst new file mode 100644 index 0000000000..89595bc490 --- /dev/null +++ b/project_task_parent_completion_blocking/README.rst @@ -0,0 +1,91 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +======================================= +Project task parent completion blocking +======================================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:be6a7d57271ec3af2f04902a6940f90e9d0d895ffc568dc3011b54cbf28f7fcf + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fproject-lightgray.png?logo=github + :target: https://github.com/OCA/project/tree/19.0/project_task_parent_completion_blocking + :alt: OCA/project +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/project-19-0/project-19-0-project_task_parent_completion_blocking + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/project&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Prevents a task from being completed if a child task isn't completed. + +**Table of contents** + +.. contents:: + :local: + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Tecnativa + +Contributors +------------ + +- `Tecnativa `__: + + - Pedro M. Baeza + - David Bañón + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-david-banon-tecnativa| image:: https://github.com/david-banon-tecnativa.png?size=40px + :target: https://github.com/david-banon-tecnativa + :alt: david-banon-tecnativa + +Current `maintainer `__: + +|maintainer-david-banon-tecnativa| + +This module is part of the `OCA/project `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/project_task_parent_completion_blocking/__init__.py b/project_task_parent_completion_blocking/__init__.py new file mode 100644 index 0000000000..0650744f6b --- /dev/null +++ b/project_task_parent_completion_blocking/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/project_task_parent_completion_blocking/__manifest__.py b/project_task_parent_completion_blocking/__manifest__.py new file mode 100644 index 0000000000..ffbf65845f --- /dev/null +++ b/project_task_parent_completion_blocking/__manifest__.py @@ -0,0 +1,15 @@ +# Copyright 2025 Tecnativa David Bañón +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +{ + "name": "Project task parent completion blocking", + "version": "19.0.1.0.0", + "category": "Project Management", + "summary": "Prevents a task from being completed if any children task isn't.", + "author": "Tecnativa, Odoo Community Association (OCA)", + "maintainers": ["david-banon-tecnativa"], + "website": "https://github.com/OCA/project", + "license": "AGPL-3", + "installable": True, + "depends": ["project"], +} diff --git a/project_task_parent_completion_blocking/i18n/es.po b/project_task_parent_completion_blocking/i18n/es.po new file mode 100644 index 0000000000..0b1771cf49 --- /dev/null +++ b/project_task_parent_completion_blocking/i18n/es.po @@ -0,0 +1,27 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * project_task_parent_completion_blocking +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 3.6\n" + +#. module: project_task_parent_completion_blocking +#: model:ir.model,name:project_task_parent_completion_blocking.model_project_task +msgid "Task" +msgstr "Tarea" + +#. module: project_task_parent_completion_blocking +#. odoo-python +#: code:addons/project_task_parent_completion_blocking/models/project_task.py +msgid "You can't close this task because it has open subtasks." +msgstr "No puede cerrar esta tarea porque tiene subtareas abiertas." diff --git a/project_task_parent_completion_blocking/i18n/it.po b/project_task_parent_completion_blocking/i18n/it.po new file mode 100644 index 0000000000..db76f99f5e --- /dev/null +++ b/project_task_parent_completion_blocking/i18n/it.po @@ -0,0 +1,28 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * project_task_parent_completion_blocking +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-05-05 14:57+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.10.4\n" + +#. module: project_task_parent_completion_blocking +#: model:ir.model,name:project_task_parent_completion_blocking.model_project_task +msgid "Task" +msgstr "Lavoro" + +#. module: project_task_parent_completion_blocking +#. odoo-python +#: code:addons/project_task_parent_completion_blocking/models/project_task.py +msgid "You can't close this task because it has open subtasks." +msgstr "Non si può chiudere il lavoro perché ha dei sotto lavori aperti." diff --git a/project_task_parent_completion_blocking/i18n/project_task_parent_completion_blocking.pot b/project_task_parent_completion_blocking/i18n/project_task_parent_completion_blocking.pot new file mode 100644 index 0000000000..01ced808a3 --- /dev/null +++ b/project_task_parent_completion_blocking/i18n/project_task_parent_completion_blocking.pot @@ -0,0 +1,25 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * project_task_parent_completion_blocking +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \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: project_task_parent_completion_blocking +#: model:ir.model,name:project_task_parent_completion_blocking.model_project_task +msgid "Task" +msgstr "" + +#. module: project_task_parent_completion_blocking +#. odoo-python +#: code:addons/project_task_parent_completion_blocking/models/project_task.py:0 +msgid "You can't close this task because it has open subtasks." +msgstr "" diff --git a/project_task_parent_completion_blocking/i18n/pt_BR.po b/project_task_parent_completion_blocking/i18n/pt_BR.po new file mode 100644 index 0000000000..0038fcfb36 --- /dev/null +++ b/project_task_parent_completion_blocking/i18n/pt_BR.po @@ -0,0 +1,26 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * project_task_parent_completion_blocking +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" + +#. module: project_task_parent_completion_blocking +#: model:ir.model,name:project_task_parent_completion_blocking.model_project_task +msgid "Task" +msgstr "" + +#. module: project_task_parent_completion_blocking +#. odoo-python +#: code:addons/project_task_parent_completion_blocking/models/project_task.py:0 +msgid "You can't close this task because it has open subtasks." +msgstr "" diff --git a/project_task_parent_completion_blocking/i18n/sl.po b/project_task_parent_completion_blocking/i18n/sl.po new file mode 100644 index 0000000000..d246ab4e1d --- /dev/null +++ b/project_task_parent_completion_blocking/i18n/sl.po @@ -0,0 +1,29 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * project_task_parent_completion_blocking +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2026-06-30 04:14+0000\n" +"Last-Translator: Matjaz Mozetic \n" +"Language-Team: none\n" +"Language: sl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || " +"n%100==4 ? 2 : 3;\n" +"X-Generator: Weblate 5.15.2\n" + +#. module: project_task_parent_completion_blocking +#: model:ir.model,name:project_task_parent_completion_blocking.model_project_task +msgid "Task" +msgstr "Opravilo" + +#. module: project_task_parent_completion_blocking +#. odoo-python +#: code:addons/project_task_parent_completion_blocking/models/project_task.py:0 +msgid "You can't close this task because it has open subtasks." +msgstr "Tega opravila ne morete zapreti, ker ima odprta podrejena opravila." diff --git a/project_task_parent_completion_blocking/models/__init__.py b/project_task_parent_completion_blocking/models/__init__.py new file mode 100644 index 0000000000..edf2d36b9c --- /dev/null +++ b/project_task_parent_completion_blocking/models/__init__.py @@ -0,0 +1 @@ +from . import project_task diff --git a/project_task_parent_completion_blocking/models/project_task.py b/project_task_parent_completion_blocking/models/project_task.py new file mode 100644 index 0000000000..2687bb059e --- /dev/null +++ b/project_task_parent_completion_blocking/models/project_task.py @@ -0,0 +1,18 @@ +from odoo import api, models +from odoo.exceptions import ValidationError + + +class ProjectTask(models.Model): + _inherit = "project.task" + + @api.constrains("stage_id") + def _check_subtasks_done_before_closing(self): + for task in self: + if task.stage_id.fold and task.child_ids.filtered( + lambda t: not t.stage_id.fold + ): + raise ValidationError( + self.env._( + "You can't close this task because it has open subtasks." + ) + ) diff --git a/project_task_parent_completion_blocking/pyproject.toml b/project_task_parent_completion_blocking/pyproject.toml new file mode 100644 index 0000000000..4231d0cccb --- /dev/null +++ b/project_task_parent_completion_blocking/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/project_task_parent_completion_blocking/readme/CONTRIBUTORS.md b/project_task_parent_completion_blocking/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..2a04310db9 --- /dev/null +++ b/project_task_parent_completion_blocking/readme/CONTRIBUTORS.md @@ -0,0 +1,3 @@ +- [Tecnativa](https://www.tecnativa.com): + - Pedro M. Baeza + - David Bañón diff --git a/project_task_parent_completion_blocking/readme/DESCRIPTION.md b/project_task_parent_completion_blocking/readme/DESCRIPTION.md new file mode 100644 index 0000000000..9677d6aaae --- /dev/null +++ b/project_task_parent_completion_blocking/readme/DESCRIPTION.md @@ -0,0 +1 @@ +Prevents a task from being completed if a child task isn't completed. diff --git a/project_task_parent_completion_blocking/static/description/icon.png b/project_task_parent_completion_blocking/static/description/icon.png new file mode 100644 index 0000000000..1dcc49c24f Binary files /dev/null and b/project_task_parent_completion_blocking/static/description/icon.png differ diff --git a/project_task_parent_completion_blocking/static/description/index.html b/project_task_parent_completion_blocking/static/description/index.html new file mode 100644 index 0000000000..546edace9c --- /dev/null +++ b/project_task_parent_completion_blocking/static/description/index.html @@ -0,0 +1,435 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Project task parent completion blocking

+ +

Beta License: AGPL-3 OCA/project Translate me on Weblate Try me on Runboat

+

Prevents a task from being completed if a child task isn’t completed.

+

Table of contents

+ +
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Tecnativa
  • +
+
+
+

Contributors

+
    +
  • Tecnativa:
      +
    • Pedro M. Baeza
    • +
    • David Bañón
    • +
    +
  • +
+
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

Current maintainer:

+

david-banon-tecnativa

+

This module is part of the OCA/project project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+
+ + diff --git a/project_task_parent_completion_blocking/tests/__init__.py b/project_task_parent_completion_blocking/tests/__init__.py new file mode 100644 index 0000000000..3419270500 --- /dev/null +++ b/project_task_parent_completion_blocking/tests/__init__.py @@ -0,0 +1 @@ +from . import test_project_task_parent_completion_blocking diff --git a/project_task_parent_completion_blocking/tests/test_project_task_parent_completion_blocking.py b/project_task_parent_completion_blocking/tests/test_project_task_parent_completion_blocking.py new file mode 100644 index 0000000000..80e5578a7f --- /dev/null +++ b/project_task_parent_completion_blocking/tests/test_project_task_parent_completion_blocking.py @@ -0,0 +1,55 @@ +from odoo.exceptions import ValidationError + +from odoo.addons.base.tests.common import BaseCommon + + +class TestParentChildBlock(BaseCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.test_project = cls.env["project.project"].create( + { + "name": "Test Project", + "privacy_visibility": "employees", + "alias_name": "project+test", + } + ) + cls.parent_task = cls.env["project.task"].create( + {"name": "Pigs UserTask", "project_id": cls.test_project.id} + ) + cls.child_task = cls.env["project.task"].create( + {"name": "Pigs ManagerTask", "project_id": cls.test_project.id} + ) + + cls.stage_pending = cls.env["project.task.type"].create( + {"name": "a", "project_ids": [(4, cls.test_project.id)], "fold": False} + ) + cls.stage_done = cls.env["project.task.type"].create( + {"name": "b", "project_ids": [(4, cls.test_project.id)], "fold": True} + ) + cls.child_task.parent_id = cls.parent_task + cls.parent_task.stage_id = cls.stage_pending + cls.child_task.stage_id = cls.stage_pending + + def test_child_blocks_parent(self): + with self.assertRaises(ValidationError): + self.parent_task.stage_id = self.stage_done + extra_task = self.env["project.task"].create( + { + "name": "Test Task 3", + "project_id": self.test_project.id, + } + ) + self.parent_task.child_ids += extra_task + extra_task.stage_id = self.stage_done + with self.assertRaises(ValidationError): + self.parent_task.stage_id = self.stage_done + + def test_child_not_blocks_parent(self): + self.child_task.stage_id = self.stage_done + self.parent_task.stage_id = self.stage_done + self.assertEqual( + self.parent_task.stage_id, + self.stage_done, + "Parent should be done if child is done", + )