From a3260cfcd75f2804cf6aec916fdb159cddcca74f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Theys?= Date: Wed, 31 May 2023 09:21:20 +0000 Subject: [PATCH] [FIX] mail: apply multi-company when feching systray activities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A raw query is not necessary to produce the desired result, found activities need to be kept only if the corresponding record can be found with standard search (which includes multi-company check). Part of task-3266643 closes odoo/odoo#123070 X-original-commit: 9dd7ae942aebe2cfd3e2dcd52e10b3bff5c8e0a9 Signed-off-by: Sébastien Theys (seb) --- addons/mail/models/res_users.py | 82 +++++++++---------- .../models/test_mail_corner_case_models.py | 9 ++ addons/test_mail/security/ir.model.access.csv | 2 + .../test_mail/security/test_mail_security.xml | 7 ++ .../test_mail/tests/test_mail_multicompany.py | 44 ++++++++++ 5 files changed, 103 insertions(+), 41 deletions(-) diff --git a/addons/mail/models/res_users.py b/addons/mail/models/res_users.py index 7fc78e649a2..c7b15cc097b 100644 --- a/addons/mail/models/res_users.py +++ b/addons/mail/models/res_users.py @@ -1,7 +1,8 @@ -# -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import _, api, exceptions, fields, models, modules, tools +from collections import defaultdict + +from odoo import _, api, fields, models, modules, tools from odoo.tools import email_normalize @@ -277,44 +278,43 @@ class Users(models.Model): @api.model def systray_get_activities(self): - query = """SELECT m.id, count(*), act.res_model as model, - CASE - WHEN %(today)s::date - act.date_deadline::date = 0 Then 'today' - WHEN %(today)s::date - act.date_deadline::date > 0 Then 'overdue' - WHEN %(today)s::date - act.date_deadline::date < 0 Then 'planned' - END AS states - FROM mail_activity AS act - JOIN ir_model AS m ON act.res_model_id = m.id - WHERE user_id = %(user_id)s - GROUP BY m.id, states, act.res_model; - """ - self.env.cr.execute(query, { - 'today': fields.Date.context_today(self), - 'user_id': self.env.uid, - }) - activity_data = self.env.cr.dictfetchall() - model_ids = [a['id'] for a in activity_data] - model_names = {n[0]: n[1] for n in self.env['ir.model'].sudo().browse(model_ids).name_get()} - + activities = self.env["mail.activity"].search([("user_id", "=", self.env.uid)]) + activities_by_record_by_model_name = defaultdict(lambda: defaultdict(lambda: self.env["mail.activity"])) + for activity in activities: + record = self.env[activity.res_model].browse(activity.res_id) + activities_by_record_by_model_name[activity.res_model][record] += activity + model_ids = list({self.env["ir.model"]._get(name).id for name in activities_by_record_by_model_name.keys()}) user_activities = {} - for activity in activity_data: - if not user_activities.get(activity['model']): - module = self.env[activity['model']]._original_module - icon = module and modules.module.get_module_icon(module) - user_activities[activity['model']] = { - 'id': activity['id'], - 'name': model_names[activity['id']], - 'model': activity['model'], - 'type': 'activity', - 'icon': icon, - 'total_count': 0, 'today_count': 0, 'overdue_count': 0, 'planned_count': 0, - } - user_activities[activity['model']]['%s_count' % activity['states']] += activity['count'] - if activity['states'] in ('today', 'overdue'): - user_activities[activity['model']]['total_count'] += activity['count'] - - user_activities[activity['model']]['actions'] = [{ - 'icon': 'fa-clock-o', - 'name': 'Summary', - }] + for model_name, activities_by_record in activities_by_record_by_model_name.items(): + domain = [("id", "in", list({r.id for r in activities_by_record.keys()}))] + allowed_records = self.env[model_name].search(domain) + if not allowed_records: + continue + module = self.env[model_name]._original_module + icon = module and modules.module.get_module_icon(module) + model = self.env["ir.model"]._get(model_name).with_prefetch(model_ids) + user_activities[model_name] = { + "id": model.id, + "name": model.name, + "model": model_name, + "type": "activity", + "icon": icon, + "total_count": 0, + "today_count": 0, + "overdue_count": 0, + "planned_count": 0, + "actions": [ + { + "icon": "fa-clock-o", + "name": "Summary", + } + ], + } + for record, activities in activities_by_record.items(): + if record not in allowed_records: + continue + for activity in activities: + user_activities[model_name]["%s_count" % activity.state] += 1 + if activity.state in ("today", "overdue"): + user_activities[model_name]["total_count"] += 1 return list(user_activities.values()) diff --git a/addons/test_mail/models/test_mail_corner_case_models.py b/addons/test_mail/models/test_mail_corner_case_models.py index 6bf08989f70..1159cc3c19a 100644 --- a/addons/test_mail/models/test_mail_corner_case_models.py +++ b/addons/test_mail/models/test_mail_corner_case_models.py @@ -112,6 +112,15 @@ class MailTestTrackMonetary(models.Model): company_currency = fields.Many2one("res.currency", string='Currency', related='company_id.currency_id', readonly=True, tracking=True) revenue = fields.Monetary('Revenue', currency_field='company_currency', tracking=True) +class MailTestMultiCompanyWithActivity(models.Model): + """ This model can be used in multi company tests with activity""" + _name = "mail.test.multi.company.with.activity" + _description = "Test Multi Company Mail With Activity" + _inherit = ["mail.thread", "mail.activity.mixin"] + + name = fields.Char() + company_id = fields.Many2one("res.company") + class MailTestSelectionTracking(models.Model): """ Test tracking for selection fields """ diff --git a/addons/test_mail/security/ir.model.access.csv b/addons/test_mail/security/ir.model.access.csv index 2b977ad7634..bbdaca3dc49 100644 --- a/addons/test_mail/security/ir.model.access.csv +++ b/addons/test_mail/security/ir.model.access.csv @@ -39,6 +39,8 @@ access_mail_test_multi_company_user,mail.test.multi.company.user,model_mail_test access_mail_test_multi_company_portal,mail.test.multi.company.portal,model_mail_test_multi_company,base.group_portal,1,0,0,0 access_mail_test_multi_company_read_user,mail.test.multi.company.read.user,model_mail_test_multi_company_read,base.group_user,1,1,1,1 access_mail_test_multi_company_read_portal,mail.test.multi.company.read.portal,model_mail_test_multi_company_read,base.group_portal,1,0,0,0 +access_mail_test_multi_company_with_activity_user,mail.test.multi.company.with.activity.user,model_mail_test_multi_company_with_activity,base.group_user,1,1,1,1 +access_mail_test_multi_company_with_activity_portal,mail.test.multi.company.with.activity.portal,model_mail_test_multi_company_with_activity,base.group_portal,1,0,0,0 access_mail_test_nothread_user,mail.test.nothread.user,model_mail_test_nothread,base.group_user,1,1,1,1 access_mail_test_nothread_portal,mail.test.nothread.portal,model_mail_test_nothread,base.group_portal,1,0,0,0 access_mail_test_track_all,mail.test.track.all,model_mail_test_track_all,base.group_user,1,1,1,1 diff --git a/addons/test_mail/security/test_mail_security.xml b/addons/test_mail/security/test_mail_security.xml index 0694eae7ad4..e3913b49036 100644 --- a/addons/test_mail/security/test_mail_security.xml +++ b/addons/test_mail/security/test_mail_security.xml @@ -16,6 +16,13 @@ + + Mail Test Multi Company With Activity + + + ['|', ('company_id', '=', False), ('company_id', 'in', company_ids)] + + Portal Mail Test Ticket diff --git a/addons/test_mail/tests/test_mail_multicompany.py b/addons/test_mail/tests/test_mail_multicompany.py index aa7ae17f358..e62db1746e4 100644 --- a/addons/test_mail/tests/test_mail_multicompany.py +++ b/addons/test_mail/tests/test_mail_multicompany.py @@ -383,6 +383,50 @@ class TestMultiCompanySetup(MailCommon, TestRecipients): subtype_xmlid='mail.mt_comment', ) + def test_systray_get_activities(self): + self.env["mail.activity"].search([]).unlink() + user_admin = self.user_admin.with_user(self.user_admin) + test_records = self.env["mail.test.multi.company.with.activity"].create( + [ + {"name": "Test1", "company_id": user_admin.company_id.id}, + {"name": "Test2", "company_id": self.company_2.id}, + ] + ) + test_records[0].activity_schedule("test_mail.mail_act_test_todo", user_id=user_admin.id) + test_records[1].activity_schedule("test_mail.mail_act_test_todo", user_id=user_admin.id) + res_all = user_admin.systray_get_activities() + self.assertEqual( + res_all[0], + { + "actions": [{"icon": "fa-clock-o", "name": "Summary"}], + "icon": "/base/static/description/icon.png", + "id": self.env["ir.model"]._get_id("mail.test.multi.company.with.activity"), + "model": "mail.test.multi.company.with.activity", + "name": "Test Multi Company Mail With Activity", + "overdue_count": 0, + "planned_count": 0, + "today_count": 2, + "total_count": 2, + "type": "activity", + } + ) + res_c2 = user_admin.with_context(allowed_company_ids=[self.company_2.id]).systray_get_activities() + self.assertEqual( + res_c2[0], + { + "actions": [{"icon": "fa-clock-o", "name": "Summary"}], + "icon": "/base/static/description/icon.png", + "id": self.env["ir.model"]._get_id("mail.test.multi.company.with.activity"), + "model": "mail.test.multi.company.with.activity", + "name": "Test Multi Company Mail With Activity", + "overdue_count": 0, + "planned_count": 0, + "today_count": 1, + "total_count": 1, + "type": "activity", + } + ) + @tagged('-at_install', 'post_install', 'multi_company') class TestMultiCompanyRedirect(MailCommon, HttpCase):