From e7cd604afa7f2cd4a4073d8e24ec0da9384c8d03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 9 Aug 2019 14:04:18 +0000 Subject: [PATCH] [IMP] sms: add a is_mail_thread_sms field allowing to filter models PURPOSE SMS are a powerful marketing tool. For instance it is perfect to announce a sale or to communicate a coupon code, to welcome a new customer in a fidelity program, ... Purpose of this task is to integrate SMS sending in batch in mass mailing. It will use same mailing objects but sending SMS instead of emails. Some metrics and flows will have to be slightly updated at the same time. SPECIFICATIONS Limit use of templates to models that are really capable of sending SMS. Templates are now available on models that inherit from mail.thread and effectively have fields used in SMS sending. Technically this is done through a not stored field on ir.model that is searchable. SMS sending capabilities is based on * having fields holding phone numbers, as defined on mail.thread in SMS; * having fields holding partners, as defined on mail.thread in SMS; This implied some code rewriting notably about finding default SMS recipients on a given model, in order to have fields instead of directly returning partners. LINKS Task 1997464 PR #34424 Original SMS addition: Task 1922163 (4287481) --- addons/calendar/models/calendar.py | 2 +- addons/hr/models/hr_employee.py | 4 +- addons/sms/models/__init__.py | 1 + addons/sms/models/ir_model.py | 39 +++++++++++++++++++ addons/sms/models/mail_thread.py | 18 +++++++-- addons/sms/models/sms_template.py | 2 +- .../test_mail_full/models/test_mail_models.py | 8 ++-- 7 files changed, 62 insertions(+), 12 deletions(-) create mode 100644 addons/sms/models/ir_model.py diff --git a/addons/calendar/models/calendar.py b/addons/calendar/models/calendar.py index d54244a2776..0341be802d1 100644 --- a/addons/calendar/models/calendar.py +++ b/addons/calendar/models/calendar.py @@ -545,7 +545,7 @@ class Meeting(models.Model): """ _name = 'calendar.event' - _description = "Event" + _description = "Calendar Event" _order = "id desc" _inherit = ["mail.thread"] diff --git a/addons/hr/models/hr_employee.py b/addons/hr/models/hr_employee.py index 43b04896ffd..e38d5b5a14e 100644 --- a/addons/hr/models/hr_employee.py +++ b/addons/hr/models/hr_employee.py @@ -381,8 +381,8 @@ class HrEmployeePrivate(models.Model): def message_post(self, *args, **kwargs): return super(HrEmployeePrivate, self._post_author()).message_post(*args, **kwargs) - def _sms_get_default_partners(self): - return self.mapped('user_partner_id') + def _sms_get_partner_fields(self): + return ['user_partner_id'] def _sms_get_number_fields(self): return ['mobile_phone'] diff --git a/addons/sms/models/__init__.py b/addons/sms/models/__init__.py index 8ae76d2a518..d7153098f46 100644 --- a/addons/sms/models/__init__.py +++ b/addons/sms/models/__init__.py @@ -2,6 +2,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import ir_actions +from . import ir_model from . import mail_followers from . import mail_message from . import mail_notification diff --git a/addons/sms/models/ir_model.py b/addons/sms/models/ir_model.py new file mode 100644 index 00000000000..58d24897640 --- /dev/null +++ b/addons/sms/models/ir_model.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, fields, models + + +class IrModel(models.Model): + _inherit = 'ir.model' + + is_mail_thread_sms = fields.Boolean( + string="Mail Thread SMS", default=False, + store=False, compute='_compute_is_mail_thread_sms', search='_search_is_mail_thread_sms', + help="Whether this model supports messages and notifications through SMS", + ) + + @api.depends('is_mail_thread') + def _compute_is_mail_thread_sms(self): + for model in self: + if model.is_mail_thread: + ModelObject = self.env[model.model] + potential_fields = ModelObject._sms_get_number_fields() + ModelObject._sms_get_partner_fields() + if any(fname in ModelObject._fields for fname in potential_fields): + model.is_mail_thread_sms = True + continue + model.is_mail_thread_sms = False + + def _search_is_mail_thread_sms(self, operator, value): + thread_models = self.search([('is_mail_thread', '=', True)]) + valid_models = self.env['ir.model'] + for model in thread_models: + ModelObject = self.env[model.model] + potential_fields = ModelObject._sms_get_number_fields() + ModelObject._sms_get_partner_fields() + if any(fname in ModelObject._fields for fname in potential_fields): + valid_models |= model + + search_sms = (operator == '=' and value) or (operator == '!=' and not value) + if search_sms: + return [('id', 'in', valid_models.ids)] + return [('id', 'not in', valid_models.ids)] diff --git a/addons/sms/models/mail_thread.py b/addons/sms/models/mail_thread.py index f15ed6a3cad..1bbf9f20f40 100644 --- a/addons/sms/models/mail_thread.py +++ b/addons/sms/models/mail_thread.py @@ -35,15 +35,25 @@ class MailThread(models.AbstractModel): def _search_message_has_sms_error(self, operator, operand): return ['&', ('message_ids.has_sms_error', operator, operand), ('message_ids.author_id', '=', self.env.user.partner_id.id)] + def _sms_get_partner_fields(self): + """ This method returns the fields to use to find the contact to link + whensending an SMS. Having partner is not necessary, having only phone + number fields is possible. However it gives more flexibility to + notifications management when having partners. """ + fields = [] + if hasattr(self, 'partner_id'): + fields.append('partner_id') + if hasattr(self, 'partner_ids'): + fields.append('partner_ids') + return fields + def _sms_get_default_partners(self): """ This method will likely need to be overridden by inherited models. :returns partners: recordset of res.partner """ partners = self.env['res.partner'] - if hasattr(self, 'partner_id'): - partners |= self.mapped('partner_id') - if hasattr(self, 'partner_ids'): - partners |= self.mapped('partner_ids') + for fname in self._sms_get_partner_fields(): + partners |= self.mapped(fname) return partners def _sms_get_number_fields(self): diff --git a/addons/sms/models/sms_template.py b/addons/sms/models/sms_template.py index 922a92df93c..5439c9cbb22 100644 --- a/addons/sms/models/sms_template.py +++ b/addons/sms/models/sms_template.py @@ -19,7 +19,7 @@ class SMSTemplate(models.Model): name = fields.Char('Name', translate=True) model_id = fields.Many2one( 'ir.model', string='Applies to', required=True, - domain=['&', ('is_mail_thread', '=', True), ('transient', '=', False)], + domain=['&', ('is_mail_thread_sms', '=', True), ('transient', '=', False)], help="The type of document this template can be used with") model = fields.Char('Related Document Model', related='model_id.model', index=True, store=True, readonly=True) body = fields.Char('Body', translate=True, required=True) diff --git a/addons/test_mail_full/models/test_mail_models.py b/addons/test_mail_full/models/test_mail_models.py index f00c72e71a2..02f330d8f26 100644 --- a/addons/test_mail_full/models/test_mail_models.py +++ b/addons/test_mail_full/models/test_mail_models.py @@ -19,8 +19,8 @@ class MailTestSMS(models.Model): mobile_nbr = fields.Char() customer_id = fields.Many2one('res.partner', 'Customer') - def _sms_get_default_partners(self): - return self.mapped('customer_id') + def _sms_get_partner_fields(self): + return ['customer_id'] def _sms_get_number_fields(self): return ['phone_nbr', 'mobile_nbr'] @@ -41,8 +41,8 @@ class MailTestSMSBL(models.Model): mobile_nbr = fields.Char() customer_id = fields.Many2one('res.partner', 'Customer') - def _sms_get_default_partners(self): - return self.mapped('customer_id') + def _sms_get_partner_fields(self): + return ['customer_id'] def _sms_get_number_fields(self): return ['phone_nbr', 'mobile_nbr']