diff --git a/addons/crm/models/crm_lead.py b/addons/crm/models/crm_lead.py index ff91389cbce..96a4048d891 100644 --- a/addons/crm/models/crm_lead.py +++ b/addons/crm/models/crm_lead.py @@ -50,7 +50,7 @@ class Lead(models.Model): _name = "crm.lead" _description = "Lead/Opportunity" _order = "priority desc,activity_date_deadline,id desc" - _inherit = ['mail.thread.cc', 'mail.activity.mixin', 'utm.mixin', 'format.address.mixin', 'mail.blacklist.mixin'] + _inherit = ['mail.thread.cc', 'mail.thread.blacklist', 'mail.activity.mixin', 'utm.mixin', 'format.address.mixin'] _primary_email = 'email_from' def _default_probability(self): @@ -96,9 +96,6 @@ class Lead(models.Model): date_last_stage_update = fields.Datetime(string='Last Stage Update', index=True, default=fields.Datetime.now) date_conversion = fields.Datetime('Conversion Date', readonly=True) - # Messaging and marketing - message_bounce = fields.Integer('Bounce', help="Counter of the number of bounced emails for this contact", default=0) - # Only used for type opportunity probability = fields.Float('Probability', group_operator="avg", default=lambda self: self._default_probability()) planned_revenue = fields.Monetary('Expected Revenue', currency_field='company_currency', tracking=True) diff --git a/addons/mail/models/ir_model.py b/addons/mail/models/ir_model.py index 4ac1e8d7fc4..b4950d73ed4 100644 --- a/addons/mail/models/ir_model.py +++ b/addons/mail/models/ir_model.py @@ -78,7 +78,7 @@ class IrModel(models.Model): vals = super(IrModel, self)._reflect_model_params(model) vals['is_mail_thread'] = issubclass(type(model), self.pool['mail.thread']) vals['is_mail_activity'] = issubclass(type(model), self.pool['mail.activity.mixin']) - vals['is_mail_blacklist'] = issubclass(type(model), self.pool['mail.blacklist.mixin']) + vals['is_mail_blacklist'] = issubclass(type(model), self.pool['mail.thread.blacklist']) return vals @api.model @@ -92,8 +92,8 @@ class IrModel(models.Model): parents = model_class._inherit or [] parents = [parents] if isinstance(parents, str) else parents model_class._inherit = parents + ['mail.activity.mixin'] - if model_data.get('is_mail_blacklist') and model_class._name != 'mail.blacklist.mixin': + if model_data.get('is_mail_blacklist') and model_class._name != 'mail.thread.blacklist': parents = model_class._inherit or [] parents = [parents] if isinstance(parents, str) else parents - model_class._inherit = parents + ['mail.blacklist.mixin'] + model_class._inherit = parents + ['mail.thread.blacklist'] return model_class diff --git a/addons/mail/models/mail_address_mixin.py b/addons/mail/models/mail_address_mixin.py index 49dff2f86c5..c2e5fdb9111 100644 --- a/addons/mail/models/mail_address_mixin.py +++ b/addons/mail/models/mail_address_mixin.py @@ -6,25 +6,25 @@ from odoo.exceptions import UserError class MailAddressMixin(models.AbstractModel): - """ Purpose of this mixing is to store a normalized email based on the primary email field. - A normalized email is considered as : - - having a left part + @ + a right part (the domain can be without '.something') - - being lower case - - having no name before the address. Typically, having no 'Name <>' - Ex: - - Formatted Email : 'Name ' - - Normalized Email : 'name@domain.com' - The primary email field can be specified on the parent model, if it differs from the default one ('email') - The email_normalized field can than be used on that model to search quickly on emails (by simple comparison - and not using time consuming regex anymore). - """ + """ Purpose of this mixin is to store a normalized email based on the primary email field. + A normalized email is considered as : + - having a left part + @ + a right part (the domain can be without '.something') + - being lower case + - having no name before the address. Typically, having no 'Name <>' + Ex: + - Formatted Email : 'Name ' + - Normalized Email : 'name@domain.com' + The primary email field can be specified on the parent model, if it differs from the default one ('email') + The email_normalized field can than be used on that model to search quickly on emails (by simple comparison + and not using time consuming regex anymore). """ _name = 'mail.address.mixin' - _description = 'Email address mixin' + _description = 'Email Address Mixin' _primary_email = 'email' - email_normalized = fields.Char(string='Normalized email address', compute="_compute_email_normalized", invisible=True, - compute_sudo=True, store=True, help="""This field is used to search on email address, - as the primary email field can contain more than strictly an email address.""") + email_normalized = fields.Char( + string='Normalized Email', compute="_compute_email_normalized", compute_sudo=True, + store=True, invisible=True, + help="This field is used to search on email address as the primary email field can contain more than strictly an email address.") @api.depends(lambda self: [self._primary_email]) def _compute_email_normalized(self): diff --git a/addons/mail/models/mail_blacklist.py b/addons/mail/models/mail_blacklist.py index 4636a19dcbe..4311aa54b6e 100644 --- a/addons/mail/models/mail_blacklist.py +++ b/addons/mail/models/mail_blacklist.py @@ -94,19 +94,22 @@ class MailBlackList(models.Model): class MailBlackListMixin(models.AbstractModel): - """ Mixin that is inherited by all model with opt out. - This mixin is inheriting of another mixin, mail.address.mixin which defines the _primary_email variable - and the email_normalized field that are mandatory to use the blacklist mixin. - """ - _name = 'mail.blacklist.mixin' + """ Mixin that is inherited by all model with opt out. This mixin inherits from + mail.address.mixin which defines the _primary_email variable and the email_normalized + field that are mandatory to use the blacklist mixin. Mail Thread capabilities + are required for this mixin. """ + _name = 'mail.thread.blacklist' _description = 'Mail Blacklist mixin' - _inherit = ['mail.address.mixin'] + _inherit = ['mail.thread', 'mail.address.mixin'] # Note : is_blacklisted sould only be used for display. As the compute is not depending on the blacklist, # once read, it won't be re-computed again if the blacklist is modified in the same request. - is_blacklisted = fields.Boolean(string='Blacklist', compute="_compute_is_blacklisted", compute_sudo=True, - store=False, search="_search_is_blacklisted", groups="base.group_user", + is_blacklisted = fields.Boolean( + string='Blacklist', compute="_compute_is_blacklisted", compute_sudo=True, store=False, + search="_search_is_blacklisted", groups="base.group_user", help="If the email address is on the blacklist, the contact won't receive mass mailing anymore, from any list") + # messaging + message_bounce = fields.Integer('Bounce', help="Counter of the number of bounced emails for this contact", default=0) @api.model def _search_is_blacklisted(self, operator, value): @@ -147,3 +150,11 @@ class MailBlackListMixin(models.AbstractModel): ('email', 'in', self.mapped('email_normalized'))]).mapped('email')) for record in self: record.is_blacklisted = record.email_normalized in blacklist + + @api.multi + def _message_receive_bounce(self, email, partner, mail_id=None): + """ Override of mail.thread generic method. Purpose is to increment the + bounce counter of the record. """ + super(MailBlackListMixin, self)._message_receive_bounce(email, partner, mail_id=mail_id) + for record in self: + record.message_bounce = record.message_bounce + 1 diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index df59d76692c..3d645bca794 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -1472,17 +1472,18 @@ class MailThread(models.AbstractModel): def _message_receive_bounce(self, email, partner, mail_id=None): """Called by ``message_process`` when a bounce email (such as Undelivered Mail Returned to Sender) is received for an existing thread. The default - behavior is to check is an integer ``message_bounce`` column exists. + behavior is to do nothing. This method is meant to be overridden in various + modules to add some specific behavior like blacklist management or mass + mailing statistics update. check is an integer ``message_bounce`` column exists. If it is the case, its content is incremented. + :param record partner: partner matching the bounced email address, if any; + :param string email: email that caused the bounce; :param mail_id: ID of the sent email that bounced. It may not exist anymore - but it could be usefull if the information was kept. This is - used notably in mass mailing. - :param RecordSet partner: partner matching the bounced email address, if any - :param string email: email that caused the bounce """ - if 'message_bounce' in self._fields: - for record in self: - record.message_bounce = record.message_bounce + 1 + but it could be useful if the information was kept. This is + used notably in mass mailing; + """ + pass def _message_extract_payload_postprocess(self, message, body, attachments): """ Perform some cleaning / postprocess in the body and attachments diff --git a/addons/mail/models/res_partner.py b/addons/mail/models/res_partner.py index e5877a2f201..966d0bb2dbd 100644 --- a/addons/mail/models/res_partner.py +++ b/addons/mail/models/res_partner.py @@ -18,10 +18,9 @@ class Partner(models.Model): """ Update partner to add a field about notification preferences. Add a generic opt-out field that can be used to restrict usage of automatic email templates. """ _name = "res.partner" - _inherit = ['res.partner', 'mail.thread', 'mail.activity.mixin', 'mail.blacklist.mixin'] + _inherit = ['res.partner', 'mail.activity.mixin', 'mail.thread.blacklist'] _mail_flat_thread = False - message_bounce = fields.Integer('Bounce', help="Counter of the number of bounced emails for this contact", default=0) channel_ids = fields.Many2many('mail.channel', 'mail_channel_partner', 'partner_id', 'channel_id', string='Channels', copy=False) # override the field to track the visibility of user user_id = fields.Many2one(tracking=True) diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 0fdc996a600..72076826085 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -305,7 +305,7 @@ class MailComposer(models.TransientModel): reply_to_value = self.env['mail.thread']._notify_get_reply_to_on_records(default=self.email_from, records=records) blacklisted_rec_ids = [] - if mass_mail_mode and issubclass(type(self.env[self.model]), self.pool['mail.blacklist.mixin']): + if mass_mail_mode and issubclass(type(self.env[self.model]), self.pool['mail.thread.blacklist']): BL_sudo = self.env['mail.blacklist'].sudo() blacklist = set(BL_sudo.search([]).mapped('email')) if blacklist: diff --git a/addons/mass_mailing/models/mass_mailing.py b/addons/mass_mailing/models/mass_mailing.py index b43b6e8a428..6d47d33e356 100644 --- a/addons/mass_mailing/models/mass_mailing.py +++ b/addons/mass_mailing/models/mass_mailing.py @@ -211,7 +211,7 @@ class MassMailingContact(models.Model): be able to deal with large contact list to email without bloating the partner base.""" _name = 'mail.mass_mailing.contact' - _inherit = ['mail.thread', 'mail.blacklist.mixin'] + _inherit = ['mail.thread.blacklist'] _description = 'Mass Mailing Contact' _order = 'email' _rec_name = 'email' @@ -226,7 +226,6 @@ class MassMailingContact(models.Model): 'contact_id', 'list_id', string='Mailing Lists') subscription_list_ids = fields.One2many('mail.mass_mailing.list_contact_rel', 'contact_id', string='Subscription Information') - message_bounce = fields.Integer(string='Bounced', help='Counter of the number of bounced emails for this contact.', default=0) country_id = fields.Many2one('res.country', string='Country') tag_ids = fields.Many2many('res.partner.category', string='Tags') opt_out = fields.Boolean('Opt Out', compute='_compute_opt_out', search='_search_opt_out', diff --git a/addons/test_mail/models/test_mail_models.py b/addons/test_mail/models/test_mail_models.py index bdf9f164da8..b9ff40ebc7a 100644 --- a/addons/test_mail/models/test_mail_models.py +++ b/addons/test_mail/models/test_mail_models.py @@ -20,7 +20,7 @@ class MailTestGateway(models.Model): mailing features and base performances. """ _description = 'Simple Chatter Model for Mail Gateway' _name = 'mail.test.gateway' - _inherit = ['mail.blacklist.mixin', 'mail.thread'] + _inherit = ['mail.thread.blacklist'] _primary_email = 'email_from' name = fields.Char() diff --git a/addons/test_mass_mailing/models/mass_mail_test.py b/addons/test_mass_mailing/models/mass_mail_test.py index 1abe766c412..3da40c424ce 100644 --- a/addons/test_mass_mailing/models/mass_mail_test.py +++ b/addons/test_mass_mailing/models/mass_mail_test.py @@ -20,7 +20,7 @@ class MassMailTestBlacklist(models.Model): """ Model using blacklist mechanism for mass mailing. """ _description = 'Mass Mailing Model w Blacklist' _name = 'mass.mail.test.bl' - _inherit = ['mail.thread', 'mail.blacklist.mixin'] + _inherit = ['mail.thread.blacklist'] _primary_email = 'email_from' # blacklist field to check