From 7ce905b02a8e6af21d6705c22a36b7e348aa48d4 Mon Sep 17 00:00:00 2001 From: Rivo Lalaina Date: Fri, 22 Nov 2024 09:48:37 +0300 Subject: [PATCH] [MIG][16.0] project_task_id_in_display_name --- .docker_files/main/__manifest__.py | 1 + Dockerfile | 1 + project_task_id_in_display_name/README.rst | 12 +++ project_task_id_in_display_name/__init__.py | 6 ++ .../__manifest__.py | 20 +++++ .../controllers/__init__.py | 4 + .../controllers/portal.py | 39 +++++++++ project_task_id_in_display_name/init_hook.py | 21 +++++ .../migrations/11.0.1.1.0/post-migration.py | 8 ++ .../models/__init__.py | 4 + .../models/project_task.py | 47 +++++++++++ .../static/description/icon.png | Bin 0 -> 7982 bytes .../tests/__init__.py | 5 ++ .../tests/test_project_task.py | 38 +++++++++ .../views/project_task_views.xml | 77 ++++++++++++++++++ 15 files changed, 283 insertions(+) create mode 100644 project_task_id_in_display_name/README.rst create mode 100644 project_task_id_in_display_name/__init__.py create mode 100644 project_task_id_in_display_name/__manifest__.py create mode 100644 project_task_id_in_display_name/controllers/__init__.py create mode 100644 project_task_id_in_display_name/controllers/portal.py create mode 100644 project_task_id_in_display_name/init_hook.py create mode 100644 project_task_id_in_display_name/migrations/11.0.1.1.0/post-migration.py create mode 100644 project_task_id_in_display_name/models/__init__.py create mode 100644 project_task_id_in_display_name/models/project_task.py create mode 100644 project_task_id_in_display_name/static/description/icon.png create mode 100644 project_task_id_in_display_name/tests/__init__.py create mode 100644 project_task_id_in_display_name/tests/test_project_task.py create mode 100644 project_task_id_in_display_name/views/project_task_views.xml diff --git a/.docker_files/main/__manifest__.py b/.docker_files/main/__manifest__.py index 539880fc..335550c4 100644 --- a/.docker_files/main/__manifest__.py +++ b/.docker_files/main/__manifest__.py @@ -14,6 +14,7 @@ "project", "project_task_date_planned", "project_task_full_text_search", + "project_task_id_in_display_name", "project_type_advanced", "project_default_task_stage", "project_stage_allow_timesheet", diff --git a/Dockerfile b/Dockerfile index 5396fbbb..86d0b37c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,6 +16,7 @@ USER odoo COPY project_stage_allow_timesheet mnt/extra-addons/project_stage_allow_timesheet COPY project_task_date_planned /mnt/extra-addons/project_task_date_planned COPY project_task_full_text_search /mnt/extra-addons/project_task_full_text_search +COPY project_task_id_in_display_name /mnt/extra-addons/project_task_id_in_display_name COPY project_type_advanced /mnt/extra-addons/project_type_advanced COPY project_default_task_stage /mnt/extra-addons/project_default_task_stage diff --git a/project_task_id_in_display_name/README.rst b/project_task_id_in_display_name/README.rst new file mode 100644 index 00000000..b1fa99af --- /dev/null +++ b/project_task_id_in_display_name/README.rst @@ -0,0 +1,12 @@ +Project Task ID In Display Name +=============================== +This module displays the ID of a task in its displayed name. + +Searching a task by its ID +-------------------------- +When searching a task in kanban view, you may enter its ID instead of its name. +The system will return the task if the entered expression matches its ID. + +Contributors +------------ +* Numigi (tm) and all its contributors (https://bit.ly/numigiens) diff --git a/project_task_id_in_display_name/__init__.py b/project_task_id_in_display_name/__init__.py new file mode 100644 index 00000000..41d2281c --- /dev/null +++ b/project_task_id_in_display_name/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# © 2022 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, controllers +from .init_hook import post_init_hook diff --git a/project_task_id_in_display_name/__manifest__.py b/project_task_id_in_display_name/__manifest__.py new file mode 100644 index 00000000..4c9d1c1e --- /dev/null +++ b/project_task_id_in_display_name/__manifest__.py @@ -0,0 +1,20 @@ +# 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": "Project Task ID In Display Name", + "version": "16.0.1.0.0", + "author": "Numigi", + "maintainer": "Numigi", + "website": "https://bit.ly/numigi-com", + "license": "LGPL-3", + "category": "Project", + "summary": "Add the ID of a task to its displayed name.", + "depends": ["project"], + "data": [ + "views/project_task_views.xml", + ], + "installable": True, + "post_init_hook": "post_init_hook", +} diff --git a/project_task_id_in_display_name/controllers/__init__.py b/project_task_id_in_display_name/controllers/__init__.py new file mode 100644 index 00000000..ad04cc80 --- /dev/null +++ b/project_task_id_in_display_name/controllers/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2024-today Numigi and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from . import portal diff --git a/project_task_id_in_display_name/controllers/portal.py b/project_task_id_in_display_name/controllers/portal.py new file mode 100644 index 00000000..41096acb --- /dev/null +++ b/project_task_id_in_display_name/controllers/portal.py @@ -0,0 +1,39 @@ +# Copyright 2024-today Numigi and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import urllib.parse + +from odoo import http +from odoo.addons.project.controllers.portal import CustomerPortal + + +class ProjectPortalWithSearchTaskByID(CustomerPortal): + """ + Allow the portal user to find a task directly by the task id. + + If the task with the given id is found, + the user is redirected to the task form. + """ + + @http.route( + ["/my/tasks", "/my/tasks/page/"], + type="http", + auth="user", + website=True, + ) + def portal_my_tasks(self, search=None, **kw): + is_searching_by_task_id = isinstance(search, str) and search.strip().isdigit() + + if is_searching_by_task_id: + task_id = search.strip() + task = http.request.env["project.task"].search( + [("id_string", "=", task_id)], limit=1 + ) + if task: + query = urllib.parse.urlencode(dict(kw)) + redirect_url = "/my/task/{task_id}?{query}".format( + task_id=task.id, query=query + ) + return http.request.redirect(redirect_url) + + return super().portal_my_tasks(search=search, **kw) diff --git a/project_task_id_in_display_name/init_hook.py b/project_task_id_in_display_name/init_hook.py new file mode 100644 index 00000000..4156b87b --- /dev/null +++ b/project_task_id_in_display_name/init_hook.py @@ -0,0 +1,21 @@ +# Copyright 2024-today Numigi and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import logging + +from odoo import SUPERUSER_ID +from odoo.api import Environment + +_logger = logging.getLogger(__name__) + + +def post_init_hook(cr, pool): + setup_task_id_string(cr) + + +def setup_task_id_string(cr): + """Setup the field id_string on all tasks.""" + _logger.info("Setting field id_string on model project.task.") + env = Environment(cr, SUPERUSER_ID, {}) + for task in env["project.task"].with_context(active_test=False).search([]): + task.id_string = str(task.id) diff --git a/project_task_id_in_display_name/migrations/11.0.1.1.0/post-migration.py b/project_task_id_in_display_name/migrations/11.0.1.1.0/post-migration.py new file mode 100644 index 00000000..20c5be27 --- /dev/null +++ b/project_task_id_in_display_name/migrations/11.0.1.1.0/post-migration.py @@ -0,0 +1,8 @@ +# Copyright 2024-today Numigi and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo.addons.project_task_id_in_display_name.init_hook import setup_task_id_string + + +def migrate(cr, version): + setup_task_id_string(cr) diff --git a/project_task_id_in_display_name/models/__init__.py b/project_task_id_in_display_name/models/__init__.py new file mode 100644 index 00000000..fb08bf9b --- /dev/null +++ b/project_task_id_in_display_name/models/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2024-today Numigi and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from . import project_task diff --git a/project_task_id_in_display_name/models/project_task.py b/project_task_id_in_display_name/models/project_task.py new file mode 100644 index 00000000..c522f171 --- /dev/null +++ b/project_task_id_in_display_name/models/project_task.py @@ -0,0 +1,47 @@ +# Copyright 2024-today Numigi 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 ProjectTask(models.Model): + """ + Add a field to allow searching a task by its ID. + Odoo does not allow to properly search an integer value from a search bar. + This results in an exceptions because Odoo sends the searched value + right to the database without checking if the given string only contains digits. + This is why we copy the id value into a varchar column. + """ + + _inherit = "project.task" + + id_string = fields.Char("ID (String)", readonly=True) + + def name_get(self): + return [(t.id, t._get_complete_name()) for t in self] + + def _get_complete_name(self): + """Get the complete name of the task. + + :return: a string containing the id and the name of the task. + """ + return "[{id}] {name}".format(id=self.id, name=self.name) + + @api.model + def name_search(self, name, args=None, operator="ilike", limit=100): + args = args or [] + tasks = self.browse() + + if name and name.isdigit(): + tasks = self.search([("id", "=", int(name))] + args, limit=limit) + + if not tasks: + tasks = self.search([("name", operator, name)] + args, limit=limit) + + return tasks.name_get() + + @api.model + def create(self, vals): + task = super().create(vals) + task.id_string = str(task.id) + return task diff --git a/project_task_id_in_display_name/static/description/icon.png b/project_task_id_in_display_name/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..92a86b10ed6e2f6a40b286805c8c92c5dc775111 GIT binary patch literal 7982 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 + + + + Task Form With Display Name In Read Mode + project.task + + + + + + + oe_edit_only + + + + + + Task Kanban With Display Name Instead Of Name + project.task + + + + + + + 1 + + + + + + 1 + + + + + + Task List: Display Name Instead Of Name + project.task + + + + + + + 1 + + + + + + Task Next Activities List: Display Name Instead Of Name + project.task + + + + + + + 1 + + + + + + Task Search With ID Searched In Name + project.task + + + + ['|', ('id_string', '=', self), ('name', 'ilike', self)] + + + + +