From ed508ee1bd56847e235a4f8e487e827cc34e3a87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 5 Mar 2021 10:28:23 +0000 Subject: [PATCH] [REF] mass_mailing: move mailing capability on model PURPOSE Remove hardcoded list of models on which mailing is possible. Indeed it is not modular and not really smart with enterprise code not being reachable in community. SPECIFICATIONS Replace hardcoded list of models on which mailing (both mail or sms) is possible by a computed searchable field on ``ir.model`` based on a class attribute. It allows to cleanly define models having mass mailing capabilities and add this attribute in bridge modules (when existing) or directly on base model definition to avoid bridge modules. In this commit we introduce a basic ``_mailing_enabled`` class attribute activating mailing on model. Mailing models may also have a ``_mailing_get_default_domain`` method allowing to define a custom default domain when sending a marketing mailing on records on this class. Mailing models can now define a ``_mailing_get_opt_out_list(_sms)`` method allowing to define custom behavior to fetch opt-outed records. Instead of defining a model-based behavior on Mailing itself, it now calls the model defined one. We still have two methods, one for mailing and one for SMS opt out computation as it relies on different underlying models and fields. LINKS Task ID-2431217 COM PR odoo/odoo#67322 ENT PR odoo/enterprise#16876 UPG PR odoo/upgrade#2236 --- .../hr_recruitment/models/hr_recruitment.py | 1 + addons/mass_mailing/models/__init__.py | 2 ++ addons/mass_mailing/models/ir_model.py | 34 ++++++++++++++++++ addons/mass_mailing/models/mailing.py | 36 ++++++------------- addons/mass_mailing/models/mailing_contact.py | 1 + addons/mass_mailing/models/mailing_list.py | 19 ++++++++++ addons/mass_mailing/models/res_partner.py | 9 +++++ .../tests/test_mailing_internals.py | 2 +- addons/mass_mailing_crm/models/__init__.py | 1 + addons/mass_mailing_crm/models/crm_lead.py | 9 +++++ addons/mass_mailing_event/models/__init__.py | 3 +- .../models/{event.py => event_event.py} | 2 +- .../models/event_registration.py | 9 +++++ .../models/__init__.py | 3 +- .../models/{event.py => event_event.py} | 0 .../models/event_track.py | 9 +++++ addons/mass_mailing_sale/models/__init__.py | 1 + addons/mass_mailing_sale/models/sale_order.py | 9 +++++ .../mass_mailing_sms/models/mailing_list.py | 12 +++++++ .../models/mailing_mailing.py | 18 ++++------ .../tests/test_mailing_internals.py | 2 +- addons/test_mail_full/models/__init__.py | 1 - .../test_mail_full/models/test_mail_models.py | 26 ++++++++++++++ addons/test_mass_mailing/models/__init__.py | 1 - .../models/mailing_mailing.py | 27 -------------- .../models/mailing_models.py | 8 +++++ .../test_mass_mailing/tests/test_mailing.py | 11 +++--- 27 files changed, 181 insertions(+), 75 deletions(-) create mode 100644 addons/mass_mailing/models/ir_model.py create mode 100644 addons/mass_mailing/models/res_partner.py create mode 100644 addons/mass_mailing_crm/models/crm_lead.py rename addons/mass_mailing_event/models/{event.py => event_event.py} (97%) create mode 100644 addons/mass_mailing_event/models/event_registration.py rename addons/mass_mailing_event_track/models/{event.py => event_event.py} (100%) create mode 100644 addons/mass_mailing_event_track/models/event_track.py create mode 100644 addons/mass_mailing_sale/models/sale_order.py delete mode 100644 addons/test_mass_mailing/models/mailing_mailing.py diff --git a/addons/hr_recruitment/models/hr_recruitment.py b/addons/hr_recruitment/models/hr_recruitment.py index 1535dec3eef..395470d9b0d 100644 --- a/addons/hr_recruitment/models/hr_recruitment.py +++ b/addons/hr_recruitment/models/hr_recruitment.py @@ -95,6 +95,7 @@ class Applicant(models.Model): _description = "Applicant" _order = "priority desc, id desc" _inherit = ['mail.thread.cc', 'mail.activity.mixin', 'utm.mixin'] + _mailing_enabled = True name = fields.Char("Subject / Application Name", required=True, help="Email subject for applications sent via email") active = fields.Boolean("Active", default=True, help="If the active field is set to false, it will allow you to hide the case without removing it.") diff --git a/addons/mass_mailing/models/__init__.py b/addons/mass_mailing/models/__init__.py index 9d9a63bd96f..3f108206778 100644 --- a/addons/mass_mailing/models/__init__.py +++ b/addons/mass_mailing/models/__init__.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from . import ir_model from . import link_tracker from . import mailing_contact from . import mailing_list @@ -10,6 +11,7 @@ from . import mail_mail from . import mail_render_mixin from . import mail_thread from . import res_config_settings +from . import res_partner from . import res_users from . import utm from . import res_company diff --git a/addons/mass_mailing/models/ir_model.py b/addons/mass_mailing/models/ir_model.py new file mode 100644 index 00000000000..6035a8ee635 --- /dev/null +++ b/addons/mass_mailing/models/ir_model.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import fields, models, _ + + +class IrModel(models.Model): + _inherit = 'ir.model' + + is_mailing_enabled = fields.Boolean( + string="Mailing Enabled", + compute='_compute_is_mailing_enabled', search='_search_is_mailing_enabled', + help="Whether this model supports marketing mailing capabilities (notably email and SMS).", + ) + + def _compute_is_mailing_enabled(self): + for model in self: + model.is_mailing_enabled = getattr(self.env[model.model], '_mailing_enabled', False) + + def _search_is_mailing_enabled(self, operator, value): + if operator not in ('=', '!='): + raise ValueError(_("Searching Mailing Enabled models supports only direct search using '='' or '!='.")) + + valid_models = self.env['ir.model'] + for model in self.search([]): + if model.model not in self.env or model.is_transient(): + continue + if getattr(self.env[model.model], '_mailing_enabled', False): + valid_models |= model + + search_is_mailing_enabled = (operator == '=' and value) or (operator == '!=' and not value) + if search_is_mailing_enabled: + return [('id', 'in', valid_models.ids)] + return [('id', 'not in', valid_models.ids)] diff --git a/addons/mass_mailing/models/mailing.py b/addons/mass_mailing/models/mailing.py index f25f7b138b0..568a283cf1b 100644 --- a/addons/mass_mailing/models/mailing.py +++ b/addons/mass_mailing/models/mailing.py @@ -10,7 +10,6 @@ import re import threading import werkzeug.urls from ast import literal_eval -from datetime import datetime from dateutil.relativedelta import relativedelta from werkzeug.urls import url_join @@ -20,17 +19,6 @@ from odoo.osv import expression _logger = logging.getLogger(__name__) -MASS_MAILING_BUSINESS_MODELS = [ - 'crm.lead', - 'event.registration', - 'hr.applicant', - 'res.partner', - 'event.track', - 'sale.order', - 'mailing.list', - 'mailing.contact' -] - # Syntax of the data URL Scheme: https://tools.ietf.org/html/rfc2397#section-3 # Used to find inline images image_re = re.compile(r"data:(image/[A-Za-z]+);base64,(.*)") @@ -128,7 +116,7 @@ class MassMailing(models.Model): mailing_model_real = fields.Char(string='Recipients Real Model', compute='_compute_model') mailing_model_id = fields.Many2one( 'ir.model', string='Recipients Model', ondelete='cascade', required=True, - domain=[('model', 'in', MASS_MAILING_BUSINESS_MODELS)], + domain=[('is_mailing_enabled', '=', True)], default=lambda self: self.env.ref('mass_mailing.model_mailing_list').id) mailing_model_name = fields.Char( string='Recipients Model Name', related='mailing_model_id.model', @@ -485,20 +473,16 @@ class MassMailing(models.Model): # ------------------------------------------------------ def _get_opt_out_list(self): - """Returns a set of emails opted-out in target model""" + """ Give list of opt-outed emails, depending on specific model-based + computation if available. + + :return list: opt-outed emails, preferably normalized (aka not records) + """ self.ensure_one() opt_out = {} target = self.env[self.mailing_model_real] - if self.mailing_model_real == "mailing.contact": - # if user is opt_out on One list but not on another - # or if two user with same email address, one opted in and the other one opted out, send the mail anyway - # TODO DBE Fixme : Optimise the following to get real opt_out and opt_in - target_list_contacts = self.env['mailing.contact.subscription'].search( - [('list_id', 'in', self.contact_list_ids.ids)]) - opt_out_contacts = target_list_contacts.filtered(lambda rel: rel.opt_out).mapped('contact_id.email_normalized') - opt_in_contacts = target_list_contacts.filtered(lambda rel: not rel.opt_out).mapped('contact_id.email_normalized') - opt_out = set(c for c in opt_out_contacts if c not in opt_in_contacts) - + if hasattr(self.env[self.mailing_model_name], '_mailing_get_opt_out_list'): + opt_out = self.env[self.mailing_model_name]._mailing_get_opt_out_list(self) _logger.info( "Mass-mailing %s targets %s, blacklist: %s emails", self, target._name, len(opt_out)) @@ -848,8 +832,8 @@ class MassMailing(models.Model): def _get_default_mailing_domain(self): mailing_domain = [] - if self.mailing_model_name == 'mailing.list' and self.contact_list_ids: - mailing_domain = [('list_ids', 'in', self.contact_list_ids.ids)] + if hasattr(self.env[self.mailing_model_name], '_mailing_get_default_domain'): + mailing_domain = self.env[self.mailing_model_name]._mailing_get_default_domain(self) if self.mailing_type == 'mail' and 'is_blacklisted' in self.env[self.mailing_model_name]._fields: mailing_domain = expression.AND([[('is_blacklisted', '=', False)], mailing_domain]) diff --git a/addons/mass_mailing/models/mailing_contact.py b/addons/mass_mailing/models/mailing_contact.py index 579599ae4bb..75ea8d0f15c 100644 --- a/addons/mass_mailing/models/mailing_contact.py +++ b/addons/mass_mailing/models/mailing_contact.py @@ -54,6 +54,7 @@ class MassMailingContact(models.Model): _inherit = ['mail.thread.blacklist'] _description = 'Mailing Contact' _order = 'email' + _mailing_enabled = True def default_get(self, fields): """ When coming from a mailing list we may have a default_list_ids context diff --git a/addons/mass_mailing/models/mailing_list.py b/addons/mass_mailing/models/mailing_list.py index e8cbc7922b8..c3aa31baedb 100644 --- a/addons/mass_mailing/models/mailing_list.py +++ b/addons/mass_mailing/models/mailing_list.py @@ -10,6 +10,7 @@ class MassMailingList(models.Model): _name = 'mailing.list' _order = 'name' _description = 'Mailing List' + _mailing_enabled = True name = fields.Char(string='Mailing List', required=True) active = fields.Boolean(default=True) @@ -224,6 +225,24 @@ class MassMailingList(models.Model): def close_dialog(self): return {'type': 'ir.actions.act_window_close'} + # ------------------------------------------------------ + # MAILING + # ------------------------------------------------------ + + def _mailing_get_default_domain(self, mailing): + return [('list_ids', 'in', mailing.contact_list_ids.ids)] + + def _mailing_get_opt_out_list(self, mailing): + """ Check subscription on all involved mailing lists. If user is opt_out + on one list but not on another if two users with same email address, one + opted in and the other one opted out, send the mail anyway. """ + # TODO DBE Fixme : Optimize the following to get real opt_out and opt_in + subscriptions = self.subscription_ids if self else mailing.contact_list_ids.subscription_ids + opt_out_contacts = subscriptions.filtered(lambda rel: rel.opt_out).mapped('contact_id.email_normalized') + opt_in_contacts = subscriptions.filtered(lambda rel: not rel.opt_out).mapped('contact_id.email_normalized') + opt_out = set(c for c in opt_out_contacts if c not in opt_in_contacts) + return opt_out + # ------------------------------------------------------ # UTILITY # ------------------------------------------------------ diff --git a/addons/mass_mailing/models/res_partner.py b/addons/mass_mailing/models/res_partner.py new file mode 100644 index 00000000000..ecc80b745c0 --- /dev/null +++ b/addons/mass_mailing/models/res_partner.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models + + +class Partner(models.Model): + _inherit = 'res.partner' + _mailing_enabled = True diff --git a/addons/mass_mailing/tests/test_mailing_internals.py b/addons/mass_mailing/tests/test_mailing_internals.py index 2d762f5dfcd..696e187d90e 100644 --- a/addons/mass_mailing/tests/test_mailing_internals.py +++ b/addons/mass_mailing/tests/test_mailing_internals.py @@ -92,7 +92,7 @@ class TestMassMailValues(MassMailCommon): self.assertEqual(mailing.reply_to_mode, 'new') self.assertEqual(mailing.reply_to, self.email_reply_to) # default for mailing list: depends upon contact_list_ids - self.assertEqual(literal_eval(mailing.mailing_domain), []) + self.assertEqual(literal_eval(mailing.mailing_domain), [('list_ids', 'in', [])]) mailing.write({ 'contact_list_ids': [(4, self.mailing_list_1.id), (4, self.mailing_list_2.id)] }) diff --git a/addons/mass_mailing_crm/models/__init__.py b/addons/mass_mailing_crm/models/__init__.py index 9733ab5351b..c6a6f373a22 100644 --- a/addons/mass_mailing_crm/models/__init__.py +++ b/addons/mass_mailing_crm/models/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from . import crm_lead from . import mailing_mailing diff --git a/addons/mass_mailing_crm/models/crm_lead.py b/addons/mass_mailing_crm/models/crm_lead.py new file mode 100644 index 00000000000..f2a13f5356b --- /dev/null +++ b/addons/mass_mailing_crm/models/crm_lead.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models + + +class CrmLead(models.Model): + _inherit = 'crm.lead' + _mailing_enabled = True diff --git a/addons/mass_mailing_event/models/__init__.py b/addons/mass_mailing_event/models/__init__.py index 80862024115..00b4cca8520 100644 --- a/addons/mass_mailing_event/models/__init__.py +++ b/addons/mass_mailing_event/models/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from . import event +from . import event_event +from . import event_registration diff --git a/addons/mass_mailing_event/models/event.py b/addons/mass_mailing_event/models/event_event.py similarity index 97% rename from addons/mass_mailing_event/models/event.py rename to addons/mass_mailing_event/models/event_event.py index 648a900795b..0212db092f5 100644 --- a/addons/mass_mailing_event/models/event.py +++ b/addons/mass_mailing_event/models/event_event.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import models, _ +from odoo import models class Event(models.Model): diff --git a/addons/mass_mailing_event/models/event_registration.py b/addons/mass_mailing_event/models/event_registration.py new file mode 100644 index 00000000000..26b5f8f0a65 --- /dev/null +++ b/addons/mass_mailing_event/models/event_registration.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models + + +class EventRegistration(models.Model): + _inherit = 'event.registration' + _mailing_enabled = True diff --git a/addons/mass_mailing_event_track/models/__init__.py b/addons/mass_mailing_event_track/models/__init__.py index 80862024115..560fda625cd 100644 --- a/addons/mass_mailing_event_track/models/__init__.py +++ b/addons/mass_mailing_event_track/models/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from . import event +from . import event_event +from . import event_track diff --git a/addons/mass_mailing_event_track/models/event.py b/addons/mass_mailing_event_track/models/event_event.py similarity index 100% rename from addons/mass_mailing_event_track/models/event.py rename to addons/mass_mailing_event_track/models/event_event.py diff --git a/addons/mass_mailing_event_track/models/event_track.py b/addons/mass_mailing_event_track/models/event_track.py new file mode 100644 index 00000000000..6fb71e7ef09 --- /dev/null +++ b/addons/mass_mailing_event_track/models/event_track.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models + + +class EventTrack(models.Model): + _inherit = 'event.track' + _mailing_enabled = True diff --git a/addons/mass_mailing_sale/models/__init__.py b/addons/mass_mailing_sale/models/__init__.py index 9733ab5351b..68af919bac3 100644 --- a/addons/mass_mailing_sale/models/__init__.py +++ b/addons/mass_mailing_sale/models/__init__.py @@ -2,3 +2,4 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import mailing_mailing +from . import sale_order diff --git a/addons/mass_mailing_sale/models/sale_order.py b/addons/mass_mailing_sale/models/sale_order.py new file mode 100644 index 00000000000..36dccbd4ffa --- /dev/null +++ b/addons/mass_mailing_sale/models/sale_order.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models + + +class SaleOrder(models.Model): + _inherit = 'sale.order' + _mailing_enabled = True diff --git a/addons/mass_mailing_sms/models/mailing_list.py b/addons/mass_mailing_sms/models/mailing_list.py index c07258e5eb3..bc2347ecbde 100644 --- a/addons/mass_mailing_sms/models/mailing_list.py +++ b/addons/mass_mailing_sms/models/mailing_list.py @@ -51,3 +51,15 @@ class MailingList(models.Model): return super(MailingList, self)._get_contact_statistics_joins() + ''' LEFT JOIN phone_blacklist bl_sms ON c.phone_sanitized = bl_sms.number and bl_sms.active ''' + + def _mailing_get_opt_out_list_sms(self, mailing): + """ Check subscription on all involved mailing lists. If user is opt_out + on one list but not on another, one opted in and the other one opted out, + send mailing anyway. + + :return list: opt-outed record IDs + """ + subscriptions = self.subscription_ids if self else mailing.contact_list_ids.subscription_ids + opt_out_contacts = subscriptions.filtered(lambda sub: sub.opt_out).mapped('contact_id') + opt_in_contacts = subscriptions.filtered(lambda sub: not sub.opt_out).mapped('contact_id') + return list(set(c.id for c in opt_out_contacts if c not in opt_in_contacts)) diff --git a/addons/mass_mailing_sms/models/mailing_mailing.py b/addons/mass_mailing_sms/models/mailing_mailing.py index ef1f3445ea1..2a36ef8329b 100644 --- a/addons/mass_mailing_sms/models/mailing_mailing.py +++ b/addons/mass_mailing_sms/models/mailing_mailing.py @@ -145,20 +145,16 @@ class Mailing(models.Model): # -------------------------------------------------- def _get_opt_out_list_sms(self): - """Returns a set of emails opted-out in target model""" + """ Give list of opt-outed records, depending on specific model-based + computation if available. + + :return list: opt-outed record IDs + """ self.ensure_one() opt_out = [] target = self.env[self.mailing_model_real] - if self.mailing_model_real == 'mailing.contact': - # if user is opt_out on One list but not on another - # or if two user with same email address, one opted in and the other one opted out, send the mail anyway - # TODO DBE Fixme : Optimise the following to get real opt_out and opt_in - subscriptions = self.env['mailing.contact.subscription'].sudo().search( - [('list_id', 'in', self.contact_list_ids.ids)]) - opt_out_contacts = subscriptions.filtered(lambda sub: sub.opt_out).mapped('contact_id') - opt_in_contacts = subscriptions.filtered(lambda sub: not sub.opt_out).mapped('contact_id') - opt_out = list(set(c.id for c in opt_out_contacts if c not in opt_in_contacts)) - + if hasattr(self.env[self.mailing_model_name], '_mailing_get_opt_out_list_sms'): + opt_out = self.env[self.mailing_model_name]._mailing_get_opt_out_list_sms(self) _logger.info("Mass SMS %s targets %s: optout: %s contacts", self, target._name, len(opt_out)) else: _logger.info("Mass SMS %s targets %s: no opt out list available", self, target._name) diff --git a/addons/mass_mailing_sms/tests/test_mailing_internals.py b/addons/mass_mailing_sms/tests/test_mailing_internals.py index 46f1e508665..bcbb28b656e 100644 --- a/addons/mass_mailing_sms/tests/test_mailing_internals.py +++ b/addons/mass_mailing_sms/tests/test_mailing_internals.py @@ -54,7 +54,7 @@ class TestMassMailValues(MassSMSCommon): self.assertEqual(mailing.mailing_model_name, 'mailing.list') self.assertEqual(mailing.mailing_model_real, 'mailing.contact') # default for mailing list: depends upon contact_list_ids - self.assertEqual(literal_eval(mailing.mailing_domain), []) + self.assertEqual(literal_eval(mailing.mailing_domain), [('list_ids', 'in', [])]) mailing.write({ 'contact_list_ids': [(4, self.mailing_list_1.id), (4, self.mailing_list_2.id)] }) diff --git a/addons/test_mail_full/models/__init__.py b/addons/test_mail_full/models/__init__.py index 0fe839f20a6..26ed2919633 100644 --- a/addons/test_mail_full/models/__init__.py +++ b/addons/test_mail_full/models/__init__.py @@ -1,4 +1,3 @@ # -*- coding: utf-8 -*- from . import test_mail_models -from . import mailing_mailing diff --git a/addons/test_mail_full/models/test_mail_models.py b/addons/test_mail_full/models/test_mail_models.py index 0a8ad967414..39a9cd2effe 100644 --- a/addons/test_mail_full/models/test_mail_models.py +++ b/addons/test_mail_full/models/test_mail_models.py @@ -10,6 +10,7 @@ class MailTestSMS(models.Model): _description = 'Chatter Model for SMS Gateway' _name = 'mail.test.sms' _inherit = ['mail.thread'] + _mailing_enabled = True _order = 'name asc, id asc' name = fields.Char() @@ -32,6 +33,7 @@ class MailTestSMSBL(models.Model): _description = 'SMS Mailing Blacklist Enabled' _name = 'mail.test.sms.bl' _inherit = ['mail.thread.phone'] + _mailing_enabled = True _order = 'name asc, id asc' name = fields.Char() @@ -54,6 +56,7 @@ class MailTestSMSOptout(models.Model): _description = 'SMS Mailing Blacklist / Optout Enabled' _name = 'mail.test.sms.bl.optout' _inherit = ['mail.thread.phone'] + _mailing_enabled = True _order = 'name asc, id asc' name = fields.Char() @@ -64,6 +67,13 @@ class MailTestSMSOptout(models.Model): customer_id = fields.Many2one('res.partner', 'Customer') opt_out = fields.Boolean() + def _mailing_get_opt_out_list_sms(self, mailing): + res_ids = mailing._get_recipients() + return self.search([ + ('id', 'in', res_ids), + ('opt_out', '=', True) + ]).ids + def _sms_get_partner_fields(self): return ['customer_id'] @@ -78,11 +88,19 @@ class MailTestSMSPartner(models.Model): _description = 'Chatter Model for SMS Gateway (Partner only)' _name = 'mail.test.sms.partner' _inherit = ['mail.thread'] + _mailing_enabled = True name = fields.Char() customer_id = fields.Many2one('res.partner', 'Customer') opt_out = fields.Boolean() + def _mailing_get_opt_out_list_sms(self, mailing): + res_ids = mailing._get_recipients() + return self.search([ + ('id', 'in', res_ids), + ('opt_out', '=', True) + ]).ids + def _sms_get_partner_fields(self): return ['customer_id'] @@ -96,11 +114,19 @@ class MailTestSMSPartner2Many(models.Model): _description = 'Chatter Model for SMS Gateway (M2M Partners only)' _name = 'mail.test.sms.partner.2many' _inherit = ['mail.thread'] + _mailing_enabled = True name = fields.Char() customer_ids = fields.Many2many('res.partner', string='Customers') opt_out = fields.Boolean() + def _mailing_get_opt_out_list_sms(self, mailing): + res_ids = mailing._get_recipients() + return self.search([ + ('id', 'in', res_ids), + ('opt_out', '=', True) + ]).ids + def _sms_get_partner_fields(self): return ['customer_ids'] diff --git a/addons/test_mass_mailing/models/__init__.py b/addons/test_mass_mailing/models/__init__.py index ea86084ecd7..530865275cb 100644 --- a/addons/test_mass_mailing/models/__init__.py +++ b/addons/test_mass_mailing/models/__init__.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from . import mailing_mailing from . import mailing_models diff --git a/addons/test_mass_mailing/models/mailing_mailing.py b/addons/test_mass_mailing/models/mailing_mailing.py deleted file mode 100644 index 12c888dbb8a..00000000000 --- a/addons/test_mass_mailing/models/mailing_mailing.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -import logging - -from odoo import models - -_logger = logging.getLogger(__name__) - - -class Mailing(models.Model): - _inherit = 'mailing.mailing' - - def _get_opt_out_list(self): - """Returns a set of emails opted-out in target model""" - self.ensure_one() - if self.mailing_model_real == 'mailing.test.optout': - res_ids = self._get_recipients() - opt_out_contacts = set(self.env['mailing.test.optout'].search([ - ('id', 'in', res_ids), - ('opt_out', '=', True) - ]).mapped('email_normalized')) - _logger.info( - "Mass-mailing %s targets %s, optout: %s emails", - self, self.mailing_model_real, len(opt_out_contacts)) - return opt_out_contacts - return super(Mailing, self)._get_opt_out_list() diff --git a/addons/test_mass_mailing/models/mailing_models.py b/addons/test_mass_mailing/models/mailing_models.py index 54ede3f8e14..cafda8d5cab 100644 --- a/addons/test_mass_mailing/models/mailing_models.py +++ b/addons/test_mass_mailing/models/mailing_models.py @@ -51,6 +51,14 @@ class MailingOptOut(models.Model): customer_id = fields.Many2one('res.partner', 'Customer', tracking=True) user_id = fields.Many2one('res.users', 'Responsible', tracking=True) + def _mailing_get_opt_out_list(self, mailing): + res_ids = mailing._get_recipients() + opt_out_contacts = set(self.search([ + ('id', 'in', res_ids), + ('opt_out', '=', True) + ]).mapped('email_normalized')) + return opt_out_contacts + class MailingPerformance(models.Model): """ A very simple model only inheriting from mail.thread to test pure mass diff --git a/addons/test_mass_mailing/tests/test_mailing.py b/addons/test_mass_mailing/tests/test_mailing.py index 7615d858a45..7f534e20de8 100644 --- a/addons/test_mass_mailing/tests/test_mailing.py +++ b/addons/test_mass_mailing/tests/test_mailing.py @@ -303,10 +303,13 @@ class TestMassMailing(TestMassMailCommon): # contact_1 is optout but same email is not optout from the same list # contact 3 is optout in list 1 but not in list 2 # contact 5 is optout - Sub = self.env['mailing.contact.subscription'] - Sub.search([('contact_id', '=', mailing_contact_1.id), ('list_id', '=', mailing_list_1.id)]).write({'opt_out': True}) - Sub.search([('contact_id', '=', mailing_contact_3.id), ('list_id', '=', mailing_list_1.id)]).write({'opt_out': True}) - Sub.search([('contact_id', '=', mailing_contact_5.id), ('list_id', '=', mailing_list_1.id)]).write({'opt_out': True}) + subs = self.env['mailing.contact.subscription'].search([ + '|', '|', + '&', ('contact_id', '=', mailing_contact_1.id), ('list_id', '=', mailing_list_1.id), + '&', ('contact_id', '=', mailing_contact_3.id), ('list_id', '=', mailing_list_1.id), + '&', ('contact_id', '=', mailing_contact_5.id), ('list_id', '=', mailing_list_1.id) + ]) + subs.write({'opt_out': True}) # create mass mailing record mailing = self.env['mailing.mailing'].create({