diff --git a/mail_activity_done/models/mail_activity.py b/mail_activity_done/models/mail_activity.py index 5730f40e79..bfb10cee1c 100644 --- a/mail_activity_done/models/mail_activity.py +++ b/mail_activity_done/models/mail_activity.py @@ -1,5 +1,9 @@ # Copyright 2018-22 ForgeFlow # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +from datetime import datetime + +import pytz + from odoo import api, fields, models from odoo.osv import expression @@ -92,3 +96,77 @@ def _read_progress_bar(self, domain, group_by, progress_bar): """ domain = expression.AND([domain, [("activity_ids.done", "=", False)]]) return super()._read_progress_bar(domain, group_by, progress_bar) + + def _search_activity_state(self, operator, value): + # Totally override the method. + all_states = {"overdue", "today", "planned", False} + if operator == "=": + search_states = {value} + elif operator == "!=": + search_states = all_states - {value} + elif operator == "in": + search_states = set(value) + elif operator == "not in": + search_states = all_states - set(value) + + reverse_search = False + if False in search_states: + # If we search "activity_state = False", they might be a lot of records + # (million for some models), so instead of returning the list of IDs + # [(id, 'in', ids)] we will reverse the domain and return something like + # [(id, 'not in', ids)], so the list of ids is as small as possible + reverse_search = True + search_states = all_states - search_states + + # Use number in the SQL query for performance purpose + integer_state_value = { + "overdue": -1, + "today": 0, + "planned": 1, + False: None, + } + + search_states_int = {integer_state_value.get(s or False) for s in search_states} + + query = """ + SELECT res_id + FROM ( + SELECT res_id, + -- Global activity state + MIN( + -- Compute the state of each individual activities + -- -1: overdue + -- 0: today + -- 1: planned + SIGN(EXTRACT(day from ( + mail_activity.date_deadline - DATE_TRUNC( + 'day', %(today_utc)s AT TIME ZONE res_partner.tz) + ))) + )::INT AS activity_state + FROM mail_activity + LEFT JOIN res_users + ON res_users.id = mail_activity.user_id + LEFT JOIN res_partner + ON res_partner.id = res_users.partner_id + WHERE mail_activity.res_model = %(res_model_table)s + and mail_activity.done=False + GROUP BY res_id + ) AS res_record + WHERE %(search_states_int)s @> ARRAY[activity_state] + """ + + self._cr.execute( + query, + { + "today_utc": pytz.utc.localize(datetime.utcnow()), + "res_model_table": self._name, + "search_states_int": list(search_states_int), + }, + ) + return [ + ( + "id", + "not in" if reverse_search else "in", + [r[0] for r in self._cr.fetchall()], + ) + ] diff --git a/mail_activity_done/tests/test_mail_activity_done.py b/mail_activity_done/tests/test_mail_activity_done.py index f481de3ec8..2c78758b15 100644 --- a/mail_activity_done/tests/test_mail_activity_done.py +++ b/mail_activity_done/tests/test_mail_activity_done.py @@ -14,6 +14,7 @@ def setUp(self): "company_id": self.env.ref("base.main_company").id, "name": "Test User", "login": "testuser", + "tz": "Europe/Brussels", "groups_id": [(6, 0, [self.env.ref("base.group_user").id])], } ) @@ -56,3 +57,14 @@ def test_read_progress_bar(self): self.assertEqual(self.act1.state, "done") result = res_partner._read_progress_bar(**params) self.assertEqual(len(result), 0) + + def test_activity_state_search(self): + today_activities = self.env["res.partner"].search( + [("activity_state", "=", "today")] + ) + self.assertEqual(len(today_activities), 1) + self.act1._action_done() + today_activities = self.env["res.partner"].search( + [("activity_state", "=", "today")] + ) + self.assertEqual(len(today_activities), 0)