From e130289ebeda550a8f786ead6e8b499f1101dde1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 2 Aug 2023 07:06:15 +0000 Subject: [PATCH] [IMP] mail: add a default heuristic to find a partner on a document RATIONALE Simplify field management for mail / phone / sms flows. Make it working out of the box, easier to use and tweak. SPECIFICATIONS In addition to finding the customer, sometimes we just want any partner on a record, notably for VOIP. For that purpose we improve heuristic for finding partner (fields or records). It introspects the model to find any relational field towards res.partner. Note that as it is generic it does not ensure the partner is a customer, just some partner. Task-3422449 (Mail, Phone: Move and improve field helpers) Prepares Task-36879 (Mail: Support MultiCompany Aliases) Part-of: odoo/odoo#130468 --- addons/mail/models/models.py | 30 ++++++++++++++----- addons/mail/models/res_partner.py | 2 +- addons/mail/tests/common.py | 13 ++------ addons/portal/models/mail_thread.py | 2 +- .../models/test_mail_corner_case_models.py | 4 +-- addons/test_mail/models/test_mail_models.py | 11 ++++--- .../models/test_mail_models_mail.py | 4 +-- .../tests/test_mail_thread_internals.py | 2 +- .../models/test_mail_sms_models.py | 12 ++++---- 9 files changed, 45 insertions(+), 35 deletions(-) diff --git a/addons/mail/models/models.py b/addons/mail/models/models.py index a757ab10b4d..02ea0a9016a 100644 --- a/addons/mail/models/models.py +++ b/addons/mail/models/models.py @@ -23,20 +23,36 @@ class BaseModel(models.AbstractModel): # ------------------------------------------------------------ @api.model - def _mail_get_partner_fields(self): + def _mail_get_partner_fields(self, introspect_fields=False): """ This method returns the fields to use to find the contact to link when sending emails or notifications. Having partner is not always - necessary but gives more flexibility to notifications management. """ - return [fname for fname in ('partner_id', 'partner_ids') - if fname in self] + necessary but gives more flexibility to notifications management. - def _mail_get_partners(self): - """ Give the default partners associated to customers. + :param bool introspect_fields: if no field is found by default + heuristics, introspect model to find relational fields towards + res.partner model. This is used notably when partners are + mandatory like in voip; + + :return list: list of valid field names that can be used to retrieve + a partner (customer) on the record; + """ + partner_fnames = [fname for fname in ('partner_id', 'partner_ids') if fname in self] + if not partner_fnames and introspect_fields: + partner_fnames = [ + fname for fname, fvalue in self._fields.items() + if fvalue.type == 'many2one' and fvalue.comodel_name == 'res.partner' + ] + return partner_fnames + + def _mail_get_partners(self, introspect_fields=False): + """ Give the default partners (customers) associated to customers. + + :param bool introspect_fields: see '_mail_get_partner_fields'; :return dict: for each record ID, a res.partner recordsets being default customers to contact; """ - partner_fields = self._mail_get_partner_fields() + partner_fields = self._mail_get_partner_fields(introspect_fields=introspect_fields) return dict( (record.id, self.env['res.partner'].union(*[record[fname] for fname in partner_fields])) for record in self diff --git a/addons/mail/models/res_partner.py b/addons/mail/models/res_partner.py index 418731f5c5f..6d5f2057bc2 100644 --- a/addons/mail/models/res_partner.py +++ b/addons/mail/models/res_partner.py @@ -64,7 +64,7 @@ class Partner(models.Model): # MESSAGING # ------------------------------------------------------------ - def _mail_get_partners(self): + def _mail_get_partners(self, introspect_fields=False): return dict((partner.id, partner) for partner in self) def _message_get_suggested_recipients(self): diff --git a/addons/mail/tests/common.py b/addons/mail/tests/common.py index 41a53e887db..2757ef48099 100644 --- a/addons/mail/tests/common.py +++ b/addons/mail/tests/common.py @@ -1142,17 +1142,8 @@ class MailCommon(common.TransactionCase, MailCase): } for idx in range(count) ] - partner_fname = False - if 'partner_id' in cls.env[model]: - partner_fname = 'partner_id' - elif hasattr(cls.env[model], '_mail_get_partner_fields'): - partner_fnames = cls.env[model]._mail_get_partner_fields() - if partner_fnames: - partner_fname = partner_fnames[0] - elif 'customer_id' in cls.env[model]: - partner_fname = 'customer_id' - - if partner_fname: + partner_fnames = cls.env[model]._mail_get_partner_fields(introspect_fields=True) + if partner_fname := partner_fnames[0] if partner_fnames else False: partners = cls.env['res.partner'].with_context(**cls._test_context).create([{ 'name': f'Partner_{idx}', 'email': f'{prefix}test_partner_{idx}@example.com', diff --git a/addons/portal/models/mail_thread.py b/addons/portal/models/mail_thread.py index 6f020cb6bd7..7edb329b0f7 100644 --- a/addons/portal/models/mail_thread.py +++ b/addons/portal/models/mail_thread.py @@ -27,7 +27,7 @@ class MailThread(models.AbstractModel): if not portal_enabled: return groups - customer = self._mail_get_partners()[self.id] + customer = self._mail_get_partners(introspect_fields=False)[self.id] if customer: access_token = self._portal_ensure_token() local_msg_vals = dict(msg_vals or {}) 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 1159cc3c19a..eaee9705070 100644 --- a/addons/test_mail/models/test_mail_corner_case_models.py +++ b/addons/test_mail/models/test_mail_corner_case_models.py @@ -53,7 +53,7 @@ class MailTestFieldType(models.Model): self = self.with_context(default_type='first') return super(MailTestFieldType, self).create(vals_list) - def _mail_get_partner_fields(self): + def _mail_get_partner_fields(self, introspect_fields=False): return ['customer_id'] @@ -69,7 +69,7 @@ class MailTestLang(models.Model): customer_id = fields.Many2one('res.partner') lang = fields.Char('Lang') - def _mail_get_partner_fields(self): + def _mail_get_partner_fields(self, introspect_fields=False): return ['customer_id'] def _notify_get_recipients_groups(self, message, model_description, msg_vals=None): diff --git a/addons/test_mail/models/test_mail_models.py b/addons/test_mail/models/test_mail_models.py index 4bcfc74d502..f0227892689 100644 --- a/addons/test_mail/models/test_mail_models.py +++ b/addons/test_mail/models/test_mail_models.py @@ -86,7 +86,7 @@ class MailTestGatewayGroups(models.Model): values['alias_parent_thread_id'] = self.id return values - def _mail_get_partner_fields(self): + def _mail_get_partner_fields(self, introspect_fields=False): return ['customer_id'] def _message_get_default_recipients(self): @@ -157,7 +157,7 @@ class MailTestTicket(models.Model): user_id = fields.Many2one('res.users', 'Responsible', tracking=1) container_id = fields.Many2one('mail.test.container', tracking=True) - def _mail_get_partner_fields(self): + def _mail_get_partner_fields(self, introspect_fields=False): return ['customer_id'] def _message_compute_subject(self): @@ -314,7 +314,7 @@ class MailTestContainer(models.Model): 'mail.alias', 'Alias', delegate=True) - def _mail_get_partner_fields(self): + def _mail_get_partner_fields(self, introspect_fields=False): return ['customer_id'] def _message_get_default_recipients(self): @@ -395,7 +395,7 @@ class MailTestComposerSource(models.Model): for source in self.filtered(lambda r: r.customer_id and not r.email_from): source.email_from = source.customer_id.email_formatted - def _mail_get_partner_fields(self): + def _mail_get_partner_fields(self, introspect_fields=False): return ['customer_id'] @@ -407,3 +407,6 @@ class MailTestMailTrackingDuration(models.Model): name = fields.Char() customer_id = fields.Many2one('res.partner', 'Customer', tracking=True) + + def _mail_get_partner_fields(self, introspect_fields=False): + return ['customer_id'] diff --git a/addons/test_mail_full/models/test_mail_models_mail.py b/addons/test_mail_full/models/test_mail_models_mail.py index ea3e3bf8e5c..f7ed26bb362 100644 --- a/addons/test_mail_full/models/test_mail_models_mail.py +++ b/addons/test_mail_full/models/test_mail_models_mail.py @@ -87,7 +87,7 @@ class MailTestRating(models.Model): elif not rating.phone_nbr: rating.phone_nbr = False - def _mail_get_partner_fields(self): + def _mail_get_partner_fields(self, introspect_fields=False): return ['customer_id'] def _phone_get_number_fields(self): @@ -116,7 +116,7 @@ class MailTestRatingThread(models.Model): customer_id = fields.Many2one('res.partner', 'Customer') user_id = fields.Many2one('res.users', 'Responsible', tracking=1) - def _mail_get_partner_fields(self): + def _mail_get_partner_fields(self, introspect_fields=False): return ['customer_id'] def _rating_get_partner(self): diff --git a/addons/test_mail_full/tests/test_mail_thread_internals.py b/addons/test_mail_full/tests/test_mail_thread_internals.py index d7423e61916..faa2f277af8 100644 --- a/addons/test_mail_full/tests/test_mail_thread_internals.py +++ b/addons/test_mail_full/tests/test_mail_thread_internals.py @@ -48,7 +48,7 @@ class TestMailThreadInternals(TestMailThreadInternalsCommon): with self.subTest(test_record=test_record): is_portal = test_record._name != 'mail.test.simple' has_customer = test_record._name != 'mail.test.portal.no.partner' - partner_fnames = test_record._mail_get_partner_fields() + partner_fnames = test_record._mail_get_partner_fields(introspect_fields=False) if is_portal: self.assertFalse( diff --git a/addons/test_mail_sms/models/test_mail_sms_models.py b/addons/test_mail_sms/models/test_mail_sms_models.py index fe47cfcb8a2..c01630b42c8 100644 --- a/addons/test_mail_sms/models/test_mail_sms_models.py +++ b/addons/test_mail_sms/models/test_mail_sms_models.py @@ -20,7 +20,7 @@ class MailTestSMS(models.Model): mobile_nbr = fields.Char() customer_id = fields.Many2one('res.partner', 'Customer') - def _mail_get_partner_fields(self): + def _mail_get_partner_fields(self, introspect_fields=False): return ['customer_id'] def _sms_get_partner_fields(self): @@ -56,7 +56,7 @@ class MailTestSMSBL(models.Model): for phone_record in self.filtered(lambda rec: not rec.phone_nbr and rec.customer_id): phone_record.phone_nbr = phone_record.customer_id.phone - def _mail_get_partner_fields(self): + def _mail_get_partner_fields(self, introspect_fields=False): return ['customer_id'] def _sms_get_partner_fields(self): @@ -96,7 +96,7 @@ class MailTestSMSOptout(models.Model): customer_id = fields.Many2one('res.partner', 'Customer') opt_out = fields.Boolean() - def _mail_get_partner_fields(self): + def _mail_get_partner_fields(self, introspect_fields=False): return ['customer_id'] def _mailing_get_opt_out_list_sms(self, mailing): @@ -126,7 +126,7 @@ class MailTestSMSPartner(models.Model): customer_id = fields.Many2one('res.partner', 'Customer') opt_out = fields.Boolean() - def _mail_get_partner_fields(self): + def _mail_get_partner_fields(self, introspect_fields=False): return ['customer_id'] def _mailing_get_opt_out_list_sms(self, mailing): @@ -155,8 +155,8 @@ class MailTestSMSPartner2Many(models.Model): customer_ids = fields.Many2many('res.partner', string='Customers') opt_out = fields.Boolean() - def _mail_get_partner_fields(self): - return ['customer_ids'] + def _mail_get_partner_fields(self, introspect_fields=False): + return ['customer_id'] def _mailing_get_opt_out_list_sms(self, mailing): res_ids = mailing._get_recipients()