From 5746024e192f9c76c07b1287832821fb3c44b0e9 Mon Sep 17 00:00:00 2001 From: Nils Coenen Date: Sun, 24 Aug 2025 16:10:39 +0200 Subject: [PATCH 1/3] [ADD] project_task_default_user This module automatically assigns default users to tasks: - Stage-based (priority rule): When creating a task or changing its stage to one with default users, stage users are applied with a configurable assignment mode (replace or merge). Stage defaults always take priority over project defaults. - Project-based fallback: If the stage does not define any default users, project default users are applied with the same assignment mode logic. - Assignment behavior: - replace: replaces existing task users with default users - merge: adds default users to existing task users without removing them - Multiple default users can be configured on both projects and stages. --- project_task_default_user/README.rst | 117 +++++ project_task_default_user/__init__.py | 1 + project_task_default_user/__manifest__.py | 21 + project_task_default_user/models/__init__.py | 5 + .../models/project_project.py | 33 ++ .../models/project_task.py | 53 ++ .../models/project_task_type.py | 33 ++ project_task_default_user/pyproject.toml | 3 + project_task_default_user/readme/CONFIGURE.md | 5 + .../readme/CONTRIBUTORS.md | 2 + .../readme/DESCRIPTION.md | 7 + .../static/description/icon.png | Bin 0 -> 9455 bytes .../static/description/index.html | 461 ++++++++++++++++++ project_task_default_user/tests/__init__.py | 1 + .../tests/test_project_task_default_user.py | 129 +++++ .../views/project_project_views.xml | 24 + .../views/project_task_type_views.xml | 29 ++ 17 files changed, 924 insertions(+) create mode 100644 project_task_default_user/README.rst create mode 100644 project_task_default_user/__init__.py create mode 100644 project_task_default_user/__manifest__.py create mode 100644 project_task_default_user/models/__init__.py create mode 100644 project_task_default_user/models/project_project.py create mode 100644 project_task_default_user/models/project_task.py create mode 100644 project_task_default_user/models/project_task_type.py create mode 100644 project_task_default_user/pyproject.toml create mode 100644 project_task_default_user/readme/CONFIGURE.md create mode 100644 project_task_default_user/readme/CONTRIBUTORS.md create mode 100644 project_task_default_user/readme/DESCRIPTION.md create mode 100644 project_task_default_user/static/description/icon.png create mode 100644 project_task_default_user/static/description/index.html create mode 100644 project_task_default_user/tests/__init__.py create mode 100644 project_task_default_user/tests/test_project_task_default_user.py create mode 100644 project_task_default_user/views/project_project_views.xml create mode 100644 project_task_default_user/views/project_task_type_views.xml diff --git a/project_task_default_user/README.rst b/project_task_default_user/README.rst new file mode 100644 index 0000000000..bed56cf545 --- /dev/null +++ b/project_task_default_user/README.rst @@ -0,0 +1,117 @@ +========================= +Project Task Default User +========================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:ad4071f80cb9b713b83ef081af888e37e8b3a19dd0cde6415eecb13714542822 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/licence-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/18.0/project_task_default_user + :alt: OCA/project +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/project-18-0/project-18-0-project_task_default_user + :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=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module automatically assigns default users to tasks. + +- Stage-based assignment (priority rule): When creating a task or + changing its stage to one with default users, those users are + automatically assigned according to the stage’s assignment mode + (replace or merge). Stage-based defaults always take priority over + project defaults. +- Project-based fallback: If the stage does not define any default + users, project default users are applied using the same assignment + mode logic (replace or merge). +- Assignment behavior: The assignment mode controls how users are + applied: + + - replace: replaces existing task users with default users + - merge: adds default users to the existing task users without + removing them + +- Multiple default users supported: Both project and stage can define + multiple default users. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +To use this module, you need to: + +1. Open Project settings and set default users in the "Default Users" + tab and configure the assignment mode. +2. Activate developer mode. +3. Go to Project → Configuration → Task Stages, set default users in the + "Default Users for this Stage" field and configure the assignment + mode. + +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 +------- + +* NICO SOLUTIONS - ENGINEERING & IT + +Contributors +------------ + +- `NICO SOLUTIONS - ENGINEERING & + IT `__: + + - Nils Coenen + +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-NICO-SOLUTIONS| image:: https://github.com/NICO-SOLUTIONS.png?size=40px + :target: https://github.com/NICO-SOLUTIONS + :alt: NICO-SOLUTIONS + +Current `maintainer `__: + +|maintainer-NICO-SOLUTIONS| + +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_default_user/__init__.py b/project_task_default_user/__init__.py new file mode 100644 index 0000000000..0650744f6b --- /dev/null +++ b/project_task_default_user/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/project_task_default_user/__manifest__.py b/project_task_default_user/__manifest__.py new file mode 100644 index 0000000000..4a8d23e3db --- /dev/null +++ b/project_task_default_user/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright 2025 NICO SOLUTIONS - ENGINEERING & IT +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +{ + "name": "Project Task Default User", + "summary": "Auto assign default users to tasks or when changing task stages", + "version": "18.0.1.0.0", + "category": "Project", + "author": "NICO SOLUTIONS - ENGINEERING & IT, Odoo Community Association (OCA)", + "maintainers": ["NICO-SOLUTIONS"], + "website": "https://github.com/OCA/project", + "depends": ["project"], + "data": [ + "views/project_project_views.xml", + "views/project_task_type_views.xml", + ], + "license": "AGPL-3", + "installable": True, + "application": False, + "auto_install": False, +} diff --git a/project_task_default_user/models/__init__.py b/project_task_default_user/models/__init__.py new file mode 100644 index 0000000000..4bc8de934a --- /dev/null +++ b/project_task_default_user/models/__init__.py @@ -0,0 +1,5 @@ +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from . import project_project +from . import project_task_type +from . import project_task diff --git a/project_task_default_user/models/project_project.py b/project_task_default_user/models/project_project.py new file mode 100644 index 0000000000..e755260d23 --- /dev/null +++ b/project_task_default_user/models/project_project.py @@ -0,0 +1,33 @@ +# Copyright 2025 NICO SOLUTIONS - ENGINEERING & IT +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from odoo import fields, models + + +class ProjectProject(models.Model): + _inherit = "project.project" + + default_user_ids = fields.Many2many( + "res.users", + string="Default Users for Tasks", + domain=lambda self: [ + ("groups_id", "in", [self.env.ref("project.group_project_user").id]) + ], + help="If set, tasks will automatically be assigned to these users. " + "On new tasks, this applies if no users are set. On stage change, " + "the users are replaced only if the new stage has default users.", + ) + project_task_assignment_mode = fields.Selection( + [ + ("replace", "Replace"), + ("merge", "Merge"), + ], + default="replace", + help=( + "Defines how default users are applied:\n" + "- Replace: overwrite existing users\n" + "- Merge: add default users to existing ones\n" + "\n" + "Applied on task creation and stage changes." + ), + ) diff --git a/project_task_default_user/models/project_task.py b/project_task_default_user/models/project_task.py new file mode 100644 index 0000000000..35bdec5e23 --- /dev/null +++ b/project_task_default_user/models/project_task.py @@ -0,0 +1,53 @@ +# Copyright 2025 NICO SOLUTIONS - ENGINEERING & IT +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from odoo import Command, api, models + + +class ProjectTask(models.Model): + _inherit = "project.task" + + @api.model_create_multi + def create(self, vals_list): + tasks = super().create(vals_list) + for task in tasks: + default_users, mode = task._get_default_users_and_mode() + if default_users: + task._apply_default_users(default_users, mode) + + return tasks + + def write(self, vals): + res = super().write(vals) + if "stage_id" in vals: + for task in self: + default_users, mode = task._get_default_users_and_mode() + if default_users: + task._apply_default_users(default_users, mode) + + return res + + def _get_default_users_and_mode(self): + self.ensure_one() + if self.stage_id and self.stage_id.default_user_ids: + return ( + self.stage_id.default_user_ids.ids, + self.stage_id.stage_task_assignment_mode or "replace", + ) + + if self.project_id and self.project_id.default_user_ids: + return ( + self.project_id.default_user_ids.ids, + self.project_id.project_task_assignment_mode or "replace", + ) + + return None, "replace" + + def _apply_default_users(self, default_users, mode): + self.ensure_one() + if mode == "merge": + existing = self.user_ids.ids or [] + merged = list(dict.fromkeys(existing + default_users)) + self.user_ids = [Command.set(merged)] + else: + self.user_ids = [Command.set(default_users)] diff --git a/project_task_default_user/models/project_task_type.py b/project_task_default_user/models/project_task_type.py new file mode 100644 index 0000000000..20c263eb3b --- /dev/null +++ b/project_task_default_user/models/project_task_type.py @@ -0,0 +1,33 @@ +# Copyright 2025 NICO SOLUTIONS - ENGINEERING & IT +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from odoo import fields, models + + +class ProjectTaskType(models.Model): + _inherit = "project.task.type" + + default_user_ids = fields.Many2many( + "res.users", + string="Default Users for this Stage", + domain=lambda self: [ + ("groups_id", "in", [self.env.ref("project.group_project_user").id]) + ], + help="If set, tasks will automatically be assigned to these users. " + "On new tasks, this applies if no users are set. On stage change, " + "the users are replaced only if the new stage has default users.", + ) + stage_task_assignment_mode = fields.Selection( + [ + ("replace", "Replace"), + ("merge", "Merge"), + ], + default="replace", + help=( + "Defines how default users are applied:\n" + "- Replace: overwrite existing users\n" + "- Merge: add default users to existing ones\n" + "\n" + "Applied on task creation and stage changes." + ), + ) diff --git a/project_task_default_user/pyproject.toml b/project_task_default_user/pyproject.toml new file mode 100644 index 0000000000..4231d0cccb --- /dev/null +++ b/project_task_default_user/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/project_task_default_user/readme/CONFIGURE.md b/project_task_default_user/readme/CONFIGURE.md new file mode 100644 index 0000000000..644cd0798d --- /dev/null +++ b/project_task_default_user/readme/CONFIGURE.md @@ -0,0 +1,5 @@ +To use this module, you need to: + +1. Open Project settings and set default users in the "Default Users" tab and configure the assignment mode. +2. Activate developer mode. +3. Go to Project → Configuration → Task Stages, set default users in the "Default Users for this Stage" field and configure the assignment mode. diff --git a/project_task_default_user/readme/CONTRIBUTORS.md b/project_task_default_user/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..da8a8c2abc --- /dev/null +++ b/project_task_default_user/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- [NICO SOLUTIONS - ENGINEERING & IT](https://www.nico-solutions.de): + - Nils Coenen \<\> diff --git a/project_task_default_user/readme/DESCRIPTION.md b/project_task_default_user/readme/DESCRIPTION.md new file mode 100644 index 0000000000..1bbe408c94 --- /dev/null +++ b/project_task_default_user/readme/DESCRIPTION.md @@ -0,0 +1,7 @@ +This module automatically assigns default users to tasks. +- Stage-based assignment (priority rule): When creating a task or changing its stage to one with default users, those users are automatically assigned according to the stage’s assignment mode (replace or merge). Stage-based defaults always take priority over project defaults. +- Project-based fallback: If the stage does not define any default users, project default users are applied using the same assignment mode logic (replace or merge). +- Assignment behavior: The assignment mode controls how users are applied: + - replace: replaces existing task users with default users + - merge: adds default users to the existing task users without removing them +- Multiple default users supported: Both project and stage can define multiple default users. diff --git a/project_task_default_user/static/description/icon.png b/project_task_default_user/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/project_task_default_user/static/description/index.html b/project_task_default_user/static/description/index.html new file mode 100644 index 0000000000..14cae3b990 --- /dev/null +++ b/project_task_default_user/static/description/index.html @@ -0,0 +1,461 @@ + + + + + +Project Task Default User + + + +
+

Project Task Default User

+ + +

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

+

This module automatically assigns default users to tasks.

+
    +
  • Stage-based assignment (priority rule): When creating a task or +changing its stage to one with default users, those users are +automatically assigned according to the stage’s assignment mode +(replace or merge). Stage-based defaults always take priority over +project defaults.
  • +
  • Project-based fallback: If the stage does not define any default +users, project default users are applied using the same assignment +mode logic (replace or merge).
  • +
  • Assignment behavior: The assignment mode controls how users are +applied:
      +
    • replace: replaces existing task users with default users
    • +
    • merge: adds default users to the existing task users without +removing them
    • +
    +
  • +
  • Multiple default users supported: Both project and stage can define +multiple default users.
  • +
+

Table of contents

+ +
+

Configuration

+

To use this module, you need to:

+
    +
  1. Open Project settings and set default users in the “Default Users” +tab and configure the assignment mode.
  2. +
  3. Activate developer mode.
  4. +
  5. Go to Project → Configuration → Task Stages, set default users in the +“Default Users for this Stage” field and configure the assignment +mode.
  6. +
+
+
+

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

+
    +
  • NICO SOLUTIONS - ENGINEERING & IT
  • +
+
+ +
+

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:

+

NICO-SOLUTIONS

+

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_default_user/tests/__init__.py b/project_task_default_user/tests/__init__.py new file mode 100644 index 0000000000..2cfda9c7a2 --- /dev/null +++ b/project_task_default_user/tests/__init__.py @@ -0,0 +1 @@ +from . import test_project_task_default_user diff --git a/project_task_default_user/tests/test_project_task_default_user.py b/project_task_default_user/tests/test_project_task_default_user.py new file mode 100644 index 0000000000..f67296e05e --- /dev/null +++ b/project_task_default_user/tests/test_project_task_default_user.py @@ -0,0 +1,129 @@ +from odoo.tests.common import TransactionCase + + +class TestProjectTaskDefaultUser(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + env = cls.env + + cls.user1 = env["res.users"].create( + {"name": "User 1", "login": "user1", "email": "user1@example.com"} + ) + cls.user2 = env["res.users"].create( + {"name": "User 2", "login": "user2", "email": "user2@example.com"} + ) + + cls.project_with_default = env["project.project"].create( + { + "name": "Project with Default", + "default_user_ids": [(6, 0, [cls.user1.id])], + } + ) + + cls.project_without_default = env["project.project"].create( + { + "name": "Project without Default", + } + ) + + cls.stage_with_default = env["project.task.type"].create( + { + "name": "Stage with Default", + "default_user_ids": [(6, 0, [cls.user2.id])], + "project_ids": [(6, 0, [cls.project_with_default.id])], + "stage_task_assignment_mode": "replace", + } + ) + + cls.stage_without_default = env["project.task.type"].create( + { + "name": "Stage without Default", + "project_ids": [(6, 0, [cls.project_with_default.id])], + "stage_task_assignment_mode": "replace", + } + ) + + def test_create_task_stage_default(self): + task = self.env["project.task"].create( + { + "name": "Task Stage Default", + "project_id": self.project_with_default.id, + "stage_id": self.stage_with_default.id, + } + ) + self.assertEqual(task.user_ids.ids, [self.user2.id]) + + def test_create_task_project_default(self): + task = self.env["project.task"].create( + { + "name": "Task Project Default", + "project_id": self.project_with_default.id, + "stage_id": self.stage_without_default.id, + } + ) + self.assertEqual(task.user_ids.ids, [self.user1.id]) + + def test_create_task_no_default(self): + task = self.env["project.task"].create( + { + "name": "Task No Default", + "project_id": self.project_without_default.id, + "stage_id": self.stage_without_default.id, + } + ) + self.assertFalse(set(task.user_ids.ids) & {self.user1.id, self.user2.id}) + + def test_stage_change_with_default(self): + task = self.env["project.task"].create( + { + "name": "Task Change Stage", + "project_id": self.project_with_default.id, + "stage_id": self.stage_without_default.id, + } + ) + task.stage_id = self.stage_with_default + self.assertEqual(task.user_ids.ids, [self.user2.id]) + + def test_stage_change_no_default(self): + task = self.env["project.task"].create( + { + "name": "Task Change Stage", + "project_id": self.project_with_default.id, + "stage_id": self.stage_with_default.id, + } + ) + task.stage_id = self.stage_without_default + self.assertEqual(task.user_ids.ids, [self.user1.id]) + + def test_stage_merge_with_existing_user(self): + self.stage_with_default.write( + { + "stage_task_assignment_mode": "merge", + } + ) + task = self.env["project.task"].create( + { + "name": "Merge With Existing", + "user_ids": [(6, 0, [self.user1.id])], + "project_id": self.project_without_default.id, + "stage_id": self.stage_with_default.id, + } + ) + self.assertEqual(set(task.user_ids.ids), {self.user1.id, self.user2.id}) + + def test_stage_merge_without_existing_user(self): + self.stage_with_default.write( + { + "stage_task_assignment_mode": "merge", + } + ) + task = self.env["project.task"].create( + { + "name": "Empty Merge", + "user_ids": False, + "project_id": self.project_without_default.id, + "stage_id": self.stage_with_default.id, + } + ) + self.assertEqual(set(task.user_ids.ids), {self.user2.id}) diff --git a/project_task_default_user/views/project_project_views.xml b/project_task_default_user/views/project_project_views.xml new file mode 100644 index 0000000000..47c0e392e5 --- /dev/null +++ b/project_task_default_user/views/project_project_views.xml @@ -0,0 +1,24 @@ + + + + + project.edit.project.inherit.default.user + project.project + + + + + + + + + + + + + + + diff --git a/project_task_default_user/views/project_task_type_views.xml b/project_task_default_user/views/project_task_type_views.xml new file mode 100644 index 0000000000..3c778bb5ae --- /dev/null +++ b/project_task_default_user/views/project_task_type_views.xml @@ -0,0 +1,29 @@ + + + + + project.task.type.tree.inherit.default.user + project.task.type + + + + + + + + + + project.task.type.edit.inherit.default.user + project.task.type + + + + + + + + + From 0c9c420af9c354d077567b1d286e87e0b4f8ef01 Mon Sep 17 00:00:00 2001 From: oca-ci Date: Mon, 4 May 2026 08:19:43 +0000 Subject: [PATCH 2/3] [UPD] Update project_task_default_user.pot --- .../i18n/project_task_default_user.pot | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 project_task_default_user/i18n/project_task_default_user.pot diff --git a/project_task_default_user/i18n/project_task_default_user.pot b/project_task_default_user/i18n/project_task_default_user.pot new file mode 100644 index 0000000000..34eecb141e --- /dev/null +++ b/project_task_default_user/i18n/project_task_default_user.pot @@ -0,0 +1,86 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * project_task_default_user +# +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_default_user +#: model:ir.model.fields,field_description:project_task_default_user.field_project_project__default_user_ids +msgid "Default Users for Tasks" +msgstr "" + +#. module: project_task_default_user +#: model:ir.model.fields,field_description:project_task_default_user.field_project_task_type__default_user_ids +msgid "Default Users for this Stage" +msgstr "" + +#. module: project_task_default_user +#: model_terms:ir.ui.view,arch_db:project_task_default_user.project_edit_project_inherit_default_user +msgid "Default users" +msgstr "" + +#. module: project_task_default_user +#: model:ir.model.fields,help:project_task_default_user.field_project_project__project_task_assignment_mode +#: model:ir.model.fields,help:project_task_default_user.field_project_task_type__stage_task_assignment_mode +msgid "" +"Defines how default users are applied:\n" +"- Replace: overwrite existing users\n" +"- Merge: add default users to existing ones\n" +"\n" +"Applied on task creation and stage changes." +msgstr "" + +#. module: project_task_default_user +#: model:ir.model.fields,help:project_task_default_user.field_project_project__default_user_ids +#: model:ir.model.fields,help:project_task_default_user.field_project_task_type__default_user_ids +msgid "" +"If set, tasks will automatically be assigned to these users. On new tasks, " +"this applies if no users are set. On stage change, the users are replaced " +"only if the new stage has default users." +msgstr "" + +#. module: project_task_default_user +#: model:ir.model.fields.selection,name:project_task_default_user.selection__project_project__project_task_assignment_mode__merge +#: model:ir.model.fields.selection,name:project_task_default_user.selection__project_task_type__stage_task_assignment_mode__merge +msgid "Merge" +msgstr "" + +#. module: project_task_default_user +#: model:ir.model,name:project_task_default_user.model_project_project +msgid "Project" +msgstr "" + +#. module: project_task_default_user +#: model:ir.model.fields,field_description:project_task_default_user.field_project_project__project_task_assignment_mode +msgid "Project Task Assignment Mode" +msgstr "" + +#. module: project_task_default_user +#: model:ir.model.fields.selection,name:project_task_default_user.selection__project_project__project_task_assignment_mode__replace +#: model:ir.model.fields.selection,name:project_task_default_user.selection__project_task_type__stage_task_assignment_mode__replace +msgid "Replace" +msgstr "" + +#. module: project_task_default_user +#: model:ir.model.fields,field_description:project_task_default_user.field_project_task_type__stage_task_assignment_mode +msgid "Stage Task Assignment Mode" +msgstr "" + +#. module: project_task_default_user +#: model:ir.model,name:project_task_default_user.model_project_task +msgid "Task" +msgstr "" + +#. module: project_task_default_user +#: model:ir.model,name:project_task_default_user.model_project_task_type +msgid "Task Stage" +msgstr "" From df16a288514b89ced411ab2945a3b938a0ff5d36 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Mon, 4 May 2026 08:25:47 +0000 Subject: [PATCH 3/3] [BOT] post-merge updates --- README.md | 1 + project_task_default_user/README.rst | 43 ++++++++++--------- .../static/description/index.html | 31 +++++++------ setup/_metapackage/pyproject.toml | 3 +- 4 files changed, 44 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index e03b28db4d..f260637a76 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ addon | version | maintainers | summary [project_task_code](project_task_code/) | 18.0.1.0.1 | | Sequential Code for Tasks [project_task_code_portal](project_task_code_portal/) | 18.0.1.1.1 | | Use custom task code in customer portal [project_task_default_stage](project_task_default_stage/) | 18.0.1.0.1 | | Recovery default task stages for projects from v8 +[project_task_default_user](project_task_default_user/) | 18.0.1.0.0 | NICO-SOLUTIONS | Auto assign default users to tasks or when changing task stages [project_task_description_portal](project_task_description_portal/) | 18.0.1.0.0 | | Dedicated task description for portal users [project_task_description_template](project_task_description_template/) | 18.0.1.0.0 | | Add a description template to project tasks [project_task_material](project_task_material/) | 18.0.1.0.0 | | Record products spent in a Task diff --git a/project_task_default_user/README.rst b/project_task_default_user/README.rst index bed56cf545..fcbc4f272d 100644 --- a/project_task_default_user/README.rst +++ b/project_task_default_user/README.rst @@ -1,3 +1,7 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + ========================= Project Task Default User ========================= @@ -7,13 +11,13 @@ Project Task Default User !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:ad4071f80cb9b713b83ef081af888e37e8b3a19dd0cde6415eecb13714542822 + !! source digest: sha256:2b00bf26a2b4a420fcca734cb4c09820c5f24d00842a28575c17437c96897c4d !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |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/licence-AGPL--3-blue.png +.. |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 @@ -30,23 +34,23 @@ Project Task Default User This module automatically assigns default users to tasks. -- Stage-based assignment (priority rule): When creating a task or - changing its stage to one with default users, those users are - automatically assigned according to the stage’s assignment mode - (replace or merge). Stage-based defaults always take priority over - project defaults. -- Project-based fallback: If the stage does not define any default - users, project default users are applied using the same assignment - mode logic (replace or merge). -- Assignment behavior: The assignment mode controls how users are - applied: +- Stage-based assignment (priority rule): When creating a task or + changing its stage to one with default users, those users are + automatically assigned according to the stage’s assignment mode + (replace or merge). Stage-based defaults always take priority over + project defaults. +- Project-based fallback: If the stage does not define any default + users, project default users are applied using the same assignment + mode logic (replace or merge). +- Assignment behavior: The assignment mode controls how users are + applied: - - replace: replaces existing task users with default users - - merge: adds default users to the existing task users without - removing them + - replace: replaces existing task users with default users + - merge: adds default users to the existing task users without + removing them -- Multiple default users supported: Both project and stage can define - multiple default users. +- Multiple default users supported: Both project and stage can define + multiple default users. **Table of contents** @@ -86,10 +90,9 @@ Authors Contributors ------------ -- `NICO SOLUTIONS - ENGINEERING & - IT `__: +- `NICO SOLUTIONS - ENGINEERING & IT `__: - - Nils Coenen + - Nils Coenen Maintainers ----------- diff --git a/project_task_default_user/static/description/index.html b/project_task_default_user/static/description/index.html index 14cae3b990..9594f03c78 100644 --- a/project_task_default_user/static/description/index.html +++ b/project_task_default_user/static/description/index.html @@ -3,7 +3,7 @@ -Project Task Default User +README.rst -
-

Project Task Default User

+
+ + +Odoo Community Association + +
+

Project Task Default User

-

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

+

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

This module automatically assigns default users to tasks.

  • Stage-based assignment (priority rule): When creating a task or @@ -404,7 +409,7 @@

    Project Task Default User

-

Configuration

+

Configuration

To use this module, you need to:

  1. Open Project settings and set default users in the “Default Users” @@ -416,7 +421,7 @@

    Configuration

-

Bug Tracker

+

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 @@ -424,25 +429,24 @@

Bug Tracker

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

+
diff --git a/setup/_metapackage/pyproject.toml b/setup/_metapackage/pyproject.toml index 3b3ea9bcb4..f76e5cc1c9 100644 --- a/setup/_metapackage/pyproject.toml +++ b/setup/_metapackage/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "odoo-addons-oca-project" -version = "18.0.20260410.0" +version = "18.0.20260504.0" dependencies = [ "odoo-addon-project_administrator_restricted_visibility==18.0.*", "odoo-addon-project_budget==18.0.*", @@ -30,6 +30,7 @@ dependencies = [ "odoo-addon-project_task_code==18.0.*", "odoo-addon-project_task_code_portal==18.0.*", "odoo-addon-project_task_default_stage==18.0.*", + "odoo-addon-project_task_default_user==18.0.*", "odoo-addon-project_task_description_portal==18.0.*", "odoo-addon-project_task_description_template==18.0.*", "odoo-addon-project_task_material==18.0.*",