Skip to content

Commit 0ea256c

Browse files
committed
[16.0][ADD] project_scoped_tags
New module to modify tags behavior for projects and tasks, inspired by GitLab’s tag scoping. This allow to limit tag availability by projects Sould be used with "Optional quick create" module to force projects managers to manage tags from the dedicated view. This module does not modify the upstream operation of the tags, it should to be installed and uninstalled without affecting the data already in place
1 parent 996f2f2 commit 0ea256c

21 files changed

+1151
-0
lines changed

project_scoped_tags/README.rst

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
===================
2+
Project Scoped Tags
3+
===================
4+
5+
..
6+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7+
!! This file is generated by oca-gen-addon-readme !!
8+
!! changes will be overwritten. !!
9+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10+
!! source digest: sha256:8ed495a053b959b9ae69195240cfe2f605bea46049cca4bdcb79d127e7c4d649
11+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
12+
13+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
14+
:target: https://odoo-community.org/page/development-status
15+
:alt: Beta
16+
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
17+
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
18+
:alt: License: LGPL-3
19+
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fproject-lightgray.png?logo=github
20+
:target: https://github.com/OCA/project/tree/16.0/project_scoped_tags
21+
:alt: OCA/project
22+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
23+
:target: https://translation.odoo-community.org/projects/project-16-0/project-16-0-project_scoped_tags
24+
:alt: Translate me on Weblate
25+
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
26+
:target: https://runboat.odoo-community.org/builds?repo=OCA/project&target_branch=16.0
27+
:alt: Try me on Runboat
28+
29+
|badge1| |badge2| |badge3| |badge4| |badge5|
30+
31+
Modify tags behavior for projects and tasks, inspired by GitLab’s tag
32+
scoping.
33+
34+
## Overview
35+
36+
This module enhances Odoo’s project tagging by introducing the concept
37+
of scoped tags:
38+
39+
- Tags can be project-specific (available only to selected projects and
40+
their tasks) or global (available everywhere).
41+
- The same tag (by name) can be shared among multiple projects without
42+
duplication.
43+
- Tag access is enforced throughout the project and task forms.
44+
45+
## Features
46+
47+
- Project-scoped tags: Each tag has an Available in Projects field; if
48+
empty, the tag is global.
49+
- Tag uniqueness: Tag names are unique. If a tag already exists, it is
50+
shared with the project rather than duplicated.
51+
- Automatic access management: Removing a tag from a project/task only
52+
hide the tag for that project. If no project uses the tag, it is
53+
deleted (unless it is made global by a manager).
54+
- Initialization hook: On installation, all tags used by existing
55+
projects/tasks are automatically linked to their respective projects’
56+
access list.
57+
- Full domain enforcement: Tags visible for selection in project and
58+
task forms are limited to those available for the current project or
59+
global tags.
60+
61+
**Table of contents**
62+
63+
.. contents::
64+
:local:
65+
66+
Usage
67+
=====
68+
69+
Go to Project > COnfiguration > Tags to scope your tags
70+
71+
A new column allow you to choose projects for wich tags are availables.
72+
No scoped tags are available for all projects/tasks
73+
74+
Creating a tag:
75+
---------------
76+
77+
::
78+
79+
- When you create a tag from a project, it will be automatically scoped to that project.
80+
81+
- If a tag with the same name already exists, it will be used your project will be allowed to use it instead of creating a duplicate.
82+
83+
- Tag with no scoped project are globals
84+
85+
Removing a tag:
86+
---------------
87+
88+
::
89+
90+
- Removing a tag from a project only revokes that project's access.
91+
92+
## Access in forms:
93+
94+
::
95+
96+
Only tags available for the project (or global tags) are selectable in project/task forms.
97+
98+
Use it with Optional quick create
99+
---------------------------------
100+
101+
::
102+
103+
Use this module in combination with "Optional quick create" module help to keep your tags concistancy
104+
( https://odoo-community.org/shop/optional-quick-create-667 )
105+
106+
Bug Tracker
107+
===========
108+
109+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/project/issues>`_.
110+
In case of trouble, please check there if your issue has already been reported.
111+
If you spotted it first, help us to smash it by providing a detailed and welcomed
112+
`feedback <https://github.com/OCA/project/issues/new?body=module:%20project_scoped_tags%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
113+
114+
Do not contact contributors directly about support or help with technical issues.
115+
116+
Credits
117+
=======
118+
119+
Authors
120+
-------
121+
122+
* OpenStudio SAS
123+
124+
Contributors
125+
------------
126+
127+
- Simon Maillard [email protected]
128+
129+
Maintainers
130+
-----------
131+
132+
This module is maintained by the OCA.
133+
134+
.. image:: https://odoo-community.org/logo.png
135+
:alt: Odoo Community Association
136+
:target: https://odoo-community.org
137+
138+
OCA, or the Odoo Community Association, is a nonprofit organization whose
139+
mission is to support the collaborative development of Odoo features and
140+
promote its widespread use.
141+
142+
This module is part of the `OCA/project <https://github.com/OCA/project/tree/16.0/project_scoped_tags>`_ project on GitHub.
143+
144+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

project_scoped_tags/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from . import models
2+
from .hooks import post_init_set_scoped_tags

project_scoped_tags/__manifest__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "Project Scoped Tags",
3+
"version": "16.0.1.0.0",
4+
"website": "https://github.com/OCA/project",
5+
"author": "OpenStudio SAS, Odoo Community Association (OCA)",
6+
"depends": ["project"],
7+
"data": [
8+
"views/project_tag_views.xml",
9+
"views/project_project_views.xml",
10+
"views/project_task_views.xml",
11+
],
12+
"license": "LGPL-3",
13+
"application": False,
14+
"auto_install": False,
15+
"installable": True,
16+
"post_init_hook": "post_init_set_scoped_tags",
17+
}

project_scoped_tags/hooks.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
def post_init_set_scoped_tags(cr, registry):
2+
"""
3+
After module installation, ensure that every tag already used
4+
by a project or its tasks is listed in the 'available_in_project_ids'
5+
of that tag for the corresponding project.
6+
This keeps tag access consistent with existing usage.
7+
"""
8+
9+
from odoo.api import SUPERUSER_ID, Environment
10+
11+
env = Environment(cr, SUPERUSER_ID, {})
12+
13+
# Iterate over all projects
14+
projects = env["project.project"].search([])
15+
for project in projects:
16+
# Tags already on projects are directly assigned to the project
17+
# Let's add Tags assigned to tasks of the project
18+
tasks = env["project.task"].search([("project_id", "=", project.id)])
19+
for task in tasks:
20+
for tag in task.tag_ids:
21+
if project not in tag.available_in_project_ids:
22+
tag.available_in_project_ids = [(4, project.id)]

project_scoped_tags/i18n/fr.po

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Translation of Odoo Server.
2+
# This file contains the translation of the following modules:
3+
# * project_scoped_tags
4+
#
5+
msgid ""
6+
msgstr ""
7+
"Project-Id-Version: Odoo Server 16.0\n"
8+
"Report-Msgid-Bugs-To: \n"
9+
"Last-Translator: \n"
10+
"Language-Team: \n"
11+
"MIME-Version: 1.0\n"
12+
"Content-Type: text/plain; charset=UTF-8\n"
13+
"Content-Transfer-Encoding: \n"
14+
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
15+
"Language: fr\n"
16+
17+
#. module: project_scoped_tags
18+
#: model:ir.model.fields,field_description:project_scoped_tags.field_project_tags__available_in_project_ids
19+
msgid "Available in Projects"
20+
msgstr "Disponible dans les projets"
21+
22+
#. module: project_scoped_tags
23+
#: model_terms:ir.ui.view,arch_db:project_scoped_tags.view_project_tags_tree_scoped_tags
24+
msgid "Available in all projects"
25+
msgstr "Disponible dans tous les projets"
26+
27+
#. module: project_scoped_tags
28+
#: model:ir.model.fields,field_description:project_scoped_tags.field_project_tags__globally_available
29+
msgid "Globally Available"
30+
msgstr "Disponible globalement"
31+
32+
#. module: project_scoped_tags
33+
#: model:ir.model.fields,help:project_scoped_tags.field_project_tags__available_in_project_ids
34+
msgid "If empty, the tag is global and available for all projects."
35+
msgstr "Si vide, l'étiquette est globale et disponible pour tous les projets."
36+
37+
#. module: project_scoped_tags
38+
#: model_terms:ir.ui.view,arch_db:project_scoped_tags.view_project_tags_search_scoped_tags
39+
msgid "Project"
40+
msgstr "Projet"
41+
42+
#. module: project_scoped_tags
43+
#: model:ir.model,name:project_scoped_tags.model_project_tags
44+
msgid "Project Tags"
45+
msgstr "Étiquettes de projet"
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Translation of Odoo Server.
2+
# This file contains the translation of the following modules:
3+
# * project_scoped_tags
4+
#
5+
msgid ""
6+
msgstr ""
7+
"Project-Id-Version: Odoo Server 16.0\n"
8+
"Report-Msgid-Bugs-To: \n"
9+
"Last-Translator: \n"
10+
"Language-Team: \n"
11+
"MIME-Version: 1.0\n"
12+
"Content-Type: text/plain; charset=UTF-8\n"
13+
"Content-Transfer-Encoding: \n"
14+
"Plural-Forms: \n"
15+
16+
#. module: project_scoped_tags
17+
#: model:ir.model.fields,field_description:project_scoped_tags.field_project_tags__available_in_project_ids
18+
msgid "Available in Projects"
19+
msgstr ""
20+
21+
#. module: project_scoped_tags
22+
#: model_terms:ir.ui.view,arch_db:project_scoped_tags.view_project_tags_tree_scoped_tags
23+
msgid "Available in all projects"
24+
msgstr ""
25+
26+
#. module: project_scoped_tags
27+
#: model:ir.model.fields,field_description:project_scoped_tags.field_project_tags__globally_available
28+
msgid "Globally Available"
29+
msgstr ""
30+
31+
#. module: project_scoped_tags
32+
#: model:ir.model.fields,help:project_scoped_tags.field_project_tags__available_in_project_ids
33+
msgid "If empty, the tag is global and available for all projects."
34+
msgstr ""
35+
36+
#. module: project_scoped_tags
37+
#: model_terms:ir.ui.view,arch_db:project_scoped_tags.view_project_tags_search_scoped_tags
38+
msgid "Project"
39+
msgstr ""
40+
41+
#. module: project_scoped_tags
42+
#: model:ir.model,name:project_scoped_tags.model_project_tags
43+
msgid "Project Tags"
44+
msgstr ""
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import project_tag
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
from logging import getLogger
2+
3+
from odoo import api, fields, models
4+
5+
_logger = getLogger(__name__)
6+
7+
8+
class ProjectTags(models.Model):
9+
_inherit = "project.tags"
10+
11+
available_in_project_ids = fields.Many2many(
12+
"project.project",
13+
"project_tags_available_project_rel",
14+
"tag_id",
15+
"project_id",
16+
string="Available in Projects",
17+
help="If empty, the tag is global and available for all projects.",
18+
compute="_compute_available_in_project_ids",
19+
readonly=False,
20+
store=True,
21+
)
22+
23+
globally_available = fields.Boolean(
24+
compute="_compute_globally_available",
25+
store=True,
26+
)
27+
28+
@api.depends("project_ids")
29+
def _compute_available_in_project_ids(self):
30+
for tag in self:
31+
# When a tag is added to a project, it becomes available
32+
# in that project and this tasks
33+
tag.available_in_project_ids |= tag.project_ids
34+
35+
@api.depends("available_in_project_ids")
36+
def _compute_globally_available(self):
37+
for tag in self:
38+
# A tag is globally available if it is not linked to any project
39+
tag.globally_available = not tag.available_in_project_ids
40+
41+
@api.model_create_multi
42+
def create(self, vals_list):
43+
tags = self.browse()
44+
for vals in vals_list:
45+
46+
_logger.debug("Creating tag with values: %s", vals)
47+
48+
name = vals.get("name")
49+
50+
existing_tag = self.search([("name", "=", name)], limit=1)
51+
if existing_tag:
52+
tags += existing_tag
53+
else:
54+
tags += super(ProjectTags, self).create([vals])
55+
return tags
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* Simon Maillard <[email protected]>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
Modify tags behavior for projects and tasks, inspired by GitLab’s tag scoping.
3+
4+
## Overview
5+
6+
This module enhances Odoo’s project tagging by introducing the concept of scoped tags:
7+
8+
- Tags can be project-specific (available only to selected projects and their tasks) or global (available everywhere).
9+
- The same tag (by name) can be shared among multiple projects without duplication.
10+
- Tag access is enforced throughout the project and task forms.
11+
12+
## Features
13+
14+
- Project-scoped tags: Each tag has an Available in Projects field; if empty, the tag is global.
15+
- Tag uniqueness: Tag names are unique. If a tag already exists, it is shared with the project rather than duplicated.
16+
- Automatic access management: Removing a tag from a project/task only hide the tag for that project. If no project uses the tag, it is deleted (unless it is made global by a manager).
17+
- Initialization hook: On installation, all tags used by existing projects/tasks are automatically linked to their respective projects’ access list.
18+
- Full domain enforcement: Tags visible for selection in project and task forms are limited to those available for the current project or global tags.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
Upon installation, the post-init hook will scan all existing projects and tasks to ensure tag access is consistent.

project_scoped_tags/readme/USAGE.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
2+
Go to Project > COnfiguration > Tags to scope your tags
3+
4+
A new column allow you to choose projects for wich tags are availables. No scoped tags are available for all projects/tasks
5+
6+
7+
## Creating a tag:
8+
9+
- When you create a tag from a project, it will be automatically scoped to that project.
10+
11+
- If a tag with the same name already exists, it will be used your project will be allowed to use it instead of creating a duplicate.
12+
13+
- Tag with no scoped project are globals
14+
15+
## Removing a tag:
16+
17+
- Removing a tag from a project only revokes that project's access.
18+
19+
## Access in forms:
20+
21+
Only tags available for the project (or global tags) are selectable in project/task forms.
22+
23+
24+
## Use it with Optional quick create
25+
26+
Use this module in combination with "Optional quick create" module help to keep your tags concistancy
27+
( https://odoo-community.org/shop/optional-quick-create-667 )

0 commit comments

Comments
 (0)