# Part of Odoo. See LICENSE file for full copyright and licensing details. from collections import defaultdict from odoo import api, fields, models, tools class MailActivity(models.Model): _inherit = "mail.activity" phone = fields.Char("Phone", compute="_compute_phone_numbers", readonly=False, store=True) mobile = fields.Char("Mobile", compute="_compute_phone_numbers", readonly=False, store=True) @api.depends("res_model", "res_id", "activity_type_id") def _compute_phone_numbers(self): call_activities = self.filtered( lambda activity: activity.id and activity.res_model and activity.res_id and activity.activity_category == "phonecall" ) (self - call_activities).phone = False (self - call_activities).mobile = False phone_numbers_by_activity = call_activities._get_phone_numbers_by_activity() for activity in call_activities: activity.mobile = phone_numbers_by_activity[activity]["mobile"] activity.phone = phone_numbers_by_activity[activity]["phone"] @api.model_create_multi def create(self, vals_list): activities = super().create(vals_list) call_activities = activities.filtered( lambda activity: (activity.phone or activity.mobile) and activity.activity_category == "phonecall" ) notifications = [[user.partner_id, "refresh_call_activities", {}] for user in call_activities.user_id] self.env["bus.bus"]._sendmany(notifications) return activities def write(self, values): if "date_deadline" in values and self.user_id: self.env["bus.bus"]._sendmany( [[partner, "refresh_call_activities", {}] for partner in self.user_id.partner_id] ) return super().write(values) @api.model def get_today_call_activities(self): """Retrieve the list of activities that: * have the type “phonecall” * have a phone number * are overdue * are assigned to the current user * are in the current company The resulting list is intended for display in the “Next Activities” tab. """ overdue_call_activities_of_current_user = self.search( [ ("activity_type_id.category", "=", "phonecall"), ("user_id", "=", self.env.uid), ("date_deadline", "<=", fields.Date.today()), "|", ("mobile", "!=", False), ("phone", "!=", False), ] ) # ----- Tackling multi-company shenanigans 👺 ----- record_ids_by_model_name = defaultdict(set) for activity in overdue_call_activities_of_current_user: record_ids_by_model_name[activity.res_model].add(activity.res_id) allowed_record_ids_by_model_name = defaultdict(list) for model_name, ids in record_ids_by_model_name.items(): # calling search will filter out records that are irrelevant to the current company allowed_record_ids_by_model_name[model_name] = self.env[model_name].search([("id", "in", list(ids))]).ids return overdue_call_activities_of_current_user.filtered( lambda activity: activity.res_id in allowed_record_ids_by_model_name[activity.res_model] )._format_call_activities() def _action_done(self, feedback=False, attachment_ids=None): """Extends _action_done to notify the user assigned to a phonecall activity that it has been marked as done. This is useful to trigger the refresh of the “Next Activities” tab. """ to_notify = self.filtered( lambda activity: activity.activity_type_id.category == "phonecall" ).user_id.partner_id self.env["bus.bus"]._sendmany([[partner, "refresh_call_activities", {}] for partner in to_notify]) return super()._action_done(feedback=feedback, attachment_ids=attachment_ids) def _format_call_activities(self): """Serializes call activities for transmission to/use by the client side.""" formatted_activities = [] call_activities = self.filtered(lambda activity: activity.activity_type_id.category == "phonecall") for model_name, activities in call_activities.grouped("res_model").items(): model = self.env[model_name] records = model.browse(activities.mapped("res_id")) partners_by_records = records._mail_get_partners(introspect_fields=True) partners = self.env["res.partner"].browse([p[:1].id for p in partners_by_records.values() if p]) formatted_partners_by_record = partners.mail_partner_format() for activity in activities: activity_data = { **activity.read(["id", "res_name", "phone", "mobile", "res_id", "res_model", "state", "date_deadline", "mail_template_ids"])[0], "activity_category": activity.activity_type_id.category, "modelName": activity.sudo().res_model_id.display_name, "user_id": activity._read_format(["user_id"])[0]["user_id"], } partner = partners_by_records.get(activity.res_id)[:1] if partner: activity_data["partner"] = formatted_partners_by_record[partner] formatted_activities.append(activity_data) return formatted_activities def _get_phone_numbers_by_activity(self): """Batch compute the phone numbers associated with the activities. :return dict: for each activity, a sub-dict containing: * phone: phone number (obtained from the activity itself or from the related partner); * mobile: mobile number (obtained from the activity itself or from the related partner). """ phone_numbers_by_activity = {} data_by_model = self._classify_by_model() for model, data in data_by_model.items(): records = self.env[model].browse(data["record_ids"]) for record, activity in zip(records, data["activities"]): mobile = record.mobile if "mobile" in record else False phone = record.phone if "phone" in record else False if not mobile and not phone: recipient = next( iter(record._mail_get_partners(introspect_fields=True)[record.id]), self.env["res.partner"], ) mobile = recipient.mobile phone = recipient.phone phone_numbers_by_activity[activity] = {"mobile": mobile, "phone": phone} return phone_numbers_by_activity