summaryrefslogtreecommitdiff
path: root/addons/mass_mailing/models/mailing_contact.py
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/mass_mailing/models/mailing_contact.py
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/mass_mailing/models/mailing_contact.py')
-rw-r--r--addons/mass_mailing/models/mailing_contact.py170
1 files changed, 170 insertions, 0 deletions
diff --git a/addons/mass_mailing/models/mailing_contact.py b/addons/mass_mailing/models/mailing_contact.py
new file mode 100644
index 00000000..27ce87a2
--- /dev/null
+++ b/addons/mass_mailing/models/mailing_contact.py
@@ -0,0 +1,170 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models
+from odoo.osv import expression
+
+
+class MassMailingContactListRel(models.Model):
+ """ Intermediate model between mass mailing list and mass mailing contact
+ Indicates if a contact is opted out for a particular list
+ """
+ _name = 'mailing.contact.subscription'
+ _description = 'Mass Mailing Subscription Information'
+ _table = 'mailing_contact_list_rel'
+ _rec_name = 'contact_id'
+
+ contact_id = fields.Many2one('mailing.contact', string='Contact', ondelete='cascade', required=True)
+ list_id = fields.Many2one('mailing.list', string='Mailing List', ondelete='cascade', required=True)
+ opt_out = fields.Boolean(string='Opt Out',
+ help='The contact has chosen not to receive mails anymore from this list', default=False)
+ unsubscription_date = fields.Datetime(string='Unsubscription Date')
+ message_bounce = fields.Integer(related='contact_id.message_bounce', store=False, readonly=False)
+ is_blacklisted = fields.Boolean(related='contact_id.is_blacklisted', store=False, readonly=False)
+
+ _sql_constraints = [
+ ('unique_contact_list', 'unique (contact_id, list_id)',
+ 'A mailing contact cannot subscribe to the same mailing list multiple times.')
+ ]
+
+ @api.model
+ def create(self, vals):
+ if 'opt_out' in vals:
+ vals['unsubscription_date'] = vals['opt_out'] and fields.Datetime.now()
+ return super(MassMailingContactListRel, self).create(vals)
+
+ def write(self, vals):
+ if 'opt_out' in vals:
+ vals['unsubscription_date'] = vals['opt_out'] and fields.Datetime.now()
+ return super(MassMailingContactListRel, self).write(vals)
+
+
+class MassMailingContact(models.Model):
+ """Model of a contact. This model is different from the partner model
+ because it holds only some basic information: name, email. The purpose is to
+ be able to deal with large contact list to email without bloating the partner
+ base."""
+ _name = 'mailing.contact'
+ _inherit = ['mail.thread.blacklist']
+ _description = 'Mailing Contact'
+ _order = 'email'
+
+ def default_get(self, fields):
+ """ When coming from a mailing list we may have a default_list_ids context
+ key. We should use it to create subscription_list_ids default value that
+ are displayed to the user as list_ids is not displayed on form view. """
+ res = super(MassMailingContact, self).default_get(fields)
+ if 'subscription_list_ids' in fields and not res.get('subscription_list_ids'):
+ list_ids = self.env.context.get('default_list_ids')
+ if 'default_list_ids' not in res and list_ids and isinstance(list_ids, (list, tuple)):
+ res['subscription_list_ids'] = [
+ (0, 0, {'list_id': list_id}) for list_id in list_ids]
+ return res
+
+ name = fields.Char()
+ company_name = fields.Char(string='Company Name')
+ title_id = fields.Many2one('res.partner.title', string='Title')
+ email = fields.Char('Email')
+ list_ids = fields.Many2many(
+ 'mailing.list', 'mailing_contact_list_rel',
+ 'contact_id', 'list_id', string='Mailing Lists')
+ subscription_list_ids = fields.One2many('mailing.contact.subscription', 'contact_id', string='Subscription Information')
+ 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',
+ help='Opt out flag for a specific mailing list.'
+ 'This field should not be used in a view without a unique and active mailing list context.')
+
+ @api.model
+ def _search_opt_out(self, operator, value):
+ # Assumes operator is '=' or '!=' and value is True or False
+ if operator != '=':
+ if operator == '!=' and isinstance(value, bool):
+ value = not value
+ else:
+ raise NotImplementedError()
+
+ if 'default_list_ids' in self._context and isinstance(self._context['default_list_ids'], (list, tuple)) and len(self._context['default_list_ids']) == 1:
+ [active_list_id] = self._context['default_list_ids']
+ contacts = self.env['mailing.contact.subscription'].search([('list_id', '=', active_list_id)])
+ return [('id', 'in', [record.contact_id.id for record in contacts if record.opt_out == value])]
+ else:
+ return expression.FALSE_DOMAIN if value else expression.TRUE_DOMAIN
+
+ @api.depends('subscription_list_ids')
+ @api.depends_context('default_list_ids')
+ def _compute_opt_out(self):
+ if 'default_list_ids' in self._context and isinstance(self._context['default_list_ids'], (list, tuple)) and len(self._context['default_list_ids']) == 1:
+ [active_list_id] = self._context['default_list_ids']
+ for record in self:
+ active_subscription_list = record.subscription_list_ids.filtered(lambda l: l.list_id.id == active_list_id)
+ record.opt_out = active_subscription_list.opt_out
+ else:
+ for record in self:
+ record.opt_out = False
+
+ def get_name_email(self, name):
+ name, email = self.env['res.partner']._parse_partner_name(name)
+ if name and not email:
+ email = name
+ if email and not name:
+ name = email
+ return name, email
+
+ @api.model_create_multi
+ def create(self, vals_list):
+ """ Synchronize default_list_ids (currently used notably for computed
+ fields) default key with subscription_list_ids given by user when creating
+ contacts.
+
+ Those two values have the same purpose, adding a list to to the contact
+ either through a direct write on m2m, either through a write on middle
+ model subscription.
+
+ This is a bit hackish but is due to default_list_ids key being
+ used to compute oupt_out field. This should be cleaned in master but here
+ we simply try to limit issues while keeping current behavior. """
+ default_list_ids = self._context.get('default_list_ids')
+ default_list_ids = default_list_ids if isinstance(default_list_ids, (list, tuple)) else []
+
+ if default_list_ids:
+ for vals in vals_list:
+ current_list_ids = []
+ subscription_ids = vals.get('subscription_list_ids') or []
+ for subscription in subscription_ids:
+ if len(subscription) == 3:
+ current_list_ids.append(subscription[2]['list_id'])
+ for list_id in set(default_list_ids) - set(current_list_ids):
+ subscription_ids.append((0, 0, {'list_id': list_id}))
+ vals['subscription_list_ids'] = subscription_ids
+
+ return super(MassMailingContact, self.with_context(default_list_ids=False)).create(vals_list)
+
+ @api.returns('self', lambda value: value.id)
+ def copy(self, default=None):
+ """ Cleans the default_list_ids while duplicating mailing contact in context of
+ a mailing list because we already have subscription lists copied over for newly
+ created contact, no need to add the ones from default_list_ids again """
+ if self.env.context.get('default_list_ids'):
+ self = self.with_context(default_list_ids=False)
+ return super().copy(default)
+
+ @api.model
+ def name_create(self, name):
+ name, email = self.get_name_email(name)
+ contact = self.create({'name': name, 'email': email})
+ return contact.name_get()[0]
+
+ @api.model
+ def add_to_list(self, name, list_id):
+ name, email = self.get_name_email(name)
+ contact = self.create({'name': name, 'email': email, 'list_ids': [(4, list_id)]})
+ return contact.name_get()[0]
+
+ def _message_get_default_recipients(self):
+ return {r.id: {
+ 'partner_ids': [],
+ 'email_to': r.email_normalized,
+ 'email_cc': False}
+ for r in self
+ }