From 1e57df85ad042f467b2811702808d102414c876a Mon Sep 17 00:00:00 2001 From: "Hugo Carlier (Huca)" Date: Tue, 20 Jun 2023 10:26:59 +0000 Subject: [PATCH] [FIX] project: avoid warning when selecting tags of a task Currently, when selecting tags for a task with no project (private task) on mobile, a warning is displayed. On top of that, the amount available tags is way more limited than in classical view and the selection of some exesting tags is impossible. Steps ===== 1. Warning when selecting tags - Install module project - In mobile view, go to the view "Tasks -> My Tasks" - Open or create a private task (task with no project) - Click on the tags field A warning is displayed (on the server logs) stating: "The domain term '('project_ids', 'in', False)' should use the '=' or '!=' operator." 2. Limited tag choice - Install module project - In mobile view, go to the view "Tasks -> My Tasks" - Open or create a private task (task with no project) - Click on the tags field - The tags that are only related to project(s) are not displayed in this view - Try to create a tag with the same name than an existing tag that does not appear. - A traceback is displayed because this tag already exists. Cause ===== As noted from the test of expression.py, M2M fields in the domain should be compared to list of elements. It is not the case here. When a project_id exits in the context, nothing happens, but there exists a check in the ORM to ensure that '=' or '!=' operator is used if the second element of the domain leaf is a boolean. In this case, there is not project_id set in the context for private task, resulting in the previous warning (False used instead of project_id). Fix === We use the improved implementation of _name_search to get the ordered list of id of records project.tags to display. This list has the advantage to not be restricted to the current project, while still presenting the tags of the current project at the top of the list. The mobile view uses the method search_read to get the list of project.tags record values, therefore we call the _name_search in this method and use an optimized sorting function to sort the resulting records accordingly. task-3302370 closes odoo/odoo#126091 X-original-commit: a03a93aa8cf5567f79dd66ced5911721b82a3802 Signed-off-by: Xavier Bol (xbo) --- addons/project/models/project_tags.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/addons/project/models/project_tags.py b/addons/project/models/project_tags.py index 5ababfccdd1..1ab86ea6dc2 100644 --- a/addons/project/models/project_tags.py +++ b/addons/project/models/project_tags.py @@ -27,22 +27,38 @@ class ProjectTags(models.Model): ] def _get_project_tags_domain(self, domain, project_id): - tag_ids = list(self.with_user(SUPERUSER_ID)._search( - ['|', ('task_ids.project_id', '=', project_id), ('project_ids', 'in', project_id)])) - return expression.AND([domain, [('id', 'in', tag_ids)]]) + # TODO: Remove in master + return domain @api.model def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True): if 'project_id' in self.env.context: - domain = self._get_project_tags_domain(domain, self.env.context.get('project_id')) + tag_ids = self._name_search('') + domain = expression.AND([domain, [('id', 'in', tag_ids)]]) return super().read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy) @api.model def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None): if 'project_id' in self.env.context: - domain = self._get_project_tags_domain(domain, self.env.context.get('project_id')) + tag_ids = self._name_search('') + domain = expression.AND([domain, [('id', 'in', tag_ids)]]) + return self.arrange_tag_list_by_id(super().search_read(domain=domain, fields=fields, offset=offset, limit=limit), tag_ids) return super().search_read(domain=domain, fields=fields, offset=offset, limit=limit, order=order) + @api.model + def arrange_tag_list_by_id(self, tag_list, id_order): + """arrange_tag_list_by_id re-order a list of record values (dict) following a given id sequence + complexity: O(n) + param: + - tag_list: ordered (by id) list of record values, each record being a dict + containing at least an 'id' key + - id_order: list of value (int) corresponding to the id of the records to re-arrange + result: + - Sorted list of record values (dict) + """ + tags_by_id = {tag['id']: tag for tag in tag_list} + return [tags_by_id[id] for id in id_order if id in tags_by_id] + @api.model def _name_search(self, name, domain=None, operator='ilike', limit=None, order=None): if self.env.context.get('project_id') and operator == 'ilike':