|
1 | 1 | # Copyright 2018-22 ForgeFlow <http://www.forgeflow.com> |
2 | 2 | # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). |
| 3 | +from datetime import datetime |
| 4 | + |
| 5 | +import pytz |
| 6 | + |
3 | 7 | from odoo import api, fields, models |
4 | 8 | from odoo.osv import expression |
5 | 9 |
|
@@ -92,3 +96,77 @@ def _read_progress_bar(self, domain, group_by, progress_bar): |
92 | 96 | """ |
93 | 97 | domain = expression.AND([domain, [("activity_ids.done", "=", False)]]) |
94 | 98 | return super()._read_progress_bar(domain, group_by, progress_bar) |
| 99 | + |
| 100 | + def _search_activity_state(self, operator, value): |
| 101 | + # Totally override the method. |
| 102 | + all_states = {"overdue", "today", "planned", False} |
| 103 | + if operator == "=": |
| 104 | + search_states = {value} |
| 105 | + elif operator == "!=": |
| 106 | + search_states = all_states - {value} |
| 107 | + elif operator == "in": |
| 108 | + search_states = set(value) |
| 109 | + elif operator == "not in": |
| 110 | + search_states = all_states - set(value) |
| 111 | + |
| 112 | + reverse_search = False |
| 113 | + if False in search_states: |
| 114 | + # If we search "activity_state = False", they might be a lot of records |
| 115 | + # (million for some models), so instead of returning the list of IDs |
| 116 | + # [(id, 'in', ids)] we will reverse the domain and return something like |
| 117 | + # [(id, 'not in', ids)], so the list of ids is as small as possible |
| 118 | + reverse_search = True |
| 119 | + search_states = all_states - search_states |
| 120 | + |
| 121 | + # Use number in the SQL query for performance purpose |
| 122 | + integer_state_value = { |
| 123 | + "overdue": -1, |
| 124 | + "today": 0, |
| 125 | + "planned": 1, |
| 126 | + False: None, |
| 127 | + } |
| 128 | + |
| 129 | + search_states_int = {integer_state_value.get(s or False) for s in search_states} |
| 130 | + |
| 131 | + query = """ |
| 132 | + SELECT res_id |
| 133 | + FROM ( |
| 134 | + SELECT res_id, |
| 135 | + -- Global activity state |
| 136 | + MIN( |
| 137 | + -- Compute the state of each individual activities |
| 138 | + -- -1: overdue |
| 139 | + -- 0: today |
| 140 | + -- 1: planned |
| 141 | + SIGN(EXTRACT(day from ( |
| 142 | + mail_activity.date_deadline - DATE_TRUNC( |
| 143 | + 'day', %(today_utc)s AT TIME ZONE res_partner.tz) |
| 144 | + ))) |
| 145 | + )::INT AS activity_state |
| 146 | + FROM mail_activity |
| 147 | + LEFT JOIN res_users |
| 148 | + ON res_users.id = mail_activity.user_id |
| 149 | + LEFT JOIN res_partner |
| 150 | + ON res_partner.id = res_users.partner_id |
| 151 | + WHERE mail_activity.res_model = %(res_model_table)s |
| 152 | + and mail_activity.done=False |
| 153 | + GROUP BY res_id |
| 154 | + ) AS res_record |
| 155 | + WHERE %(search_states_int)s @> ARRAY[activity_state] |
| 156 | + """ |
| 157 | + |
| 158 | + self._cr.execute( |
| 159 | + query, |
| 160 | + { |
| 161 | + "today_utc": pytz.utc.localize(datetime.utcnow()), |
| 162 | + "res_model_table": self._name, |
| 163 | + "search_states_int": list(search_states_int), |
| 164 | + }, |
| 165 | + ) |
| 166 | + return [ |
| 167 | + ( |
| 168 | + "id", |
| 169 | + "not in" if reverse_search else "in", |
| 170 | + [r[0] for r in self._cr.fetchall()], |
| 171 | + ) |
| 172 | + ] |
0 commit comments