[IMP] mass_mailing: better handling of contacts, mailing lists

Merge branch 'master-mail-fp'
This commit is contained in:
Fabien Pinckaers
2016-11-30 11:21:47 +01:00
5 changed files with 130 additions and 54 deletions
@@ -17,17 +17,17 @@
<record id="mass_mail_contact_1" model="mail.mass_mailing.contact">
<field name="name">Aristide Antario</field>
<field name="email">aa@example.com</field>
<field name="list_id" ref="mass_mailing.mass_mail_list_2"/>
<field name="list_ids" eval="[(6,0,[ref('mass_mailing.mass_mail_list_2')])]"/>
</record>
<record id="mass_mail_contact_2" model="mail.mass_mailing.contact">
<field name="name">Beverly Bridge</field>
<field name="email">bb@example.com</field>
<field name="list_id" ref="mass_mailing.mass_mail_list_2"/>
<field name="list_ids" eval="[(6,0,[ref('mass_mailing.mass_mail_list_2'), ref('mass_mailing.mass_mail_list_2')])]"/>
</record>
<record id="mass_mail_contact_3" model="mail.mass_mailing.contact">
<field name="name">Carol Cartridge</field>
<field name="email">cc@example.com</field>
<field name="list_id" ref="mass_mailing.mass_mail_list_2"/>
<field name="list_ids" eval="[(6,0,[ref('mass_mailing.mass_mail_list_2')])]"/>
<field name="opt_out" eval="True"/>
</record>
+43 -21
View File
@@ -37,11 +37,22 @@ class MassMailingList(models.Model):
create_date = fields.Datetime(string='Creation Date')
contact_nbr = fields.Integer(compute="_compute_contact_nbr", string='Number of Contacts')
# Compute number of contacts non opt-out for a mailing list
def _compute_contact_nbr(self):
contacts_data = self.env['mail.mass_mailing.contact'].read_group([('list_id', 'in', self.ids), ('opt_out', '!=', True)], ['list_id'], ['list_id'])
mapped_data = dict([(c['list_id'][0], c['list_id_count']) for c in contacts_data])
self.env.cr.execute('''
select
list_id, count(*)
from
mail_mass_mailing_contact_list_rel r
left join mail_mass_mailing_contact c on (r.contact_id=c.id)
where
c.opt_out <> true
group by
list_id
''')
data = dict(self.env.cr.fetchall())
for mailing_list in self:
mailing_list.contact_nbr = mapped_data.get(mailing_list.id, 0)
mailing_list.contact_nbr = data.get(mailing_list.id, 0)
class MassMailingContact(models.Model):
"""Model of a contact. This model is different from the partner model
@@ -55,14 +66,18 @@ class MassMailingContact(models.Model):
_rec_name = 'email'
name = fields.Char()
company_name = fields.Char(string='Company Name')
title_id = fields.Many2one('res.partner.title', string='Title')
email = fields.Char(required=True)
create_date = fields.Datetime(string='Create Date')
list_id = fields.Many2one(
'mail.mass_mailing.list', string='Mailing List',
ondelete='cascade', required=True, default=lambda self: self.env['mail.mass_mailing.list'].search([], limit=1, order='id desc'))
create_date = fields.Datetime(string='Creation Date')
list_ids = fields.Many2many(
'mail.mass_mailing.list', 'mail_mass_mailing_contact_list_rel',
'contact_id', 'list_id', string='Mailing Lists')
opt_out = fields.Boolean(string='Opt Out', help='The contact has chosen not to receive mails anymore from this list')
unsubscription_date = fields.Datetime(string='Unsubscription Date')
message_bounce = fields.Integer(string='Bounce', help='Counter of the number of bounced emails for this contact.')
message_bounce = fields.Integer(string='Bounced', help='Counter of the number of bounced emails for this contact.')
country_id = fields.Many2one('res.country', string='Country')
tag_ids = fields.Many2many('res.partner.category', string='Tags')
@api.model
def create(self, vals):
@@ -93,7 +108,7 @@ class MassMailingContact(models.Model):
@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_id': list_id})
contact = self.create({'name': name, 'email': email, 'list_ids': [(4, list_id)]})
return contact.name_get()[0]
@api.multi
@@ -266,8 +281,8 @@ class MassMailing(models.Model):
@api.model
def default_get(self, fields):
res = super(MassMailing, self).default_get(fields)
if 'reply_to_mode' in fields and not 'reply_to_mode' in res and res.get('mailing_model'):
if res['mailing_model'] in ['res.partner', 'mail.mass_mailing.contact']:
if 'reply_to_mode' in fields and not 'reply_to_mode' in res and res.get('mailing_model_real'):
if res['mailing_model_real'] in ['res.partner', 'mail.mass_mailing.contact']:
res['reply_to_mode'] = 'email'
else:
res['reply_to_mode'] = 'thread'
@@ -282,7 +297,8 @@ class MassMailing(models.Model):
res.append((model._name, model.message_mass_mailing_enabled()))
else:
res.append((model._name, model._mail_mass_mailing))
res.append(('mail.mass_mailing.contact', _('Mailing List')))
res.append(('mail.mass_mailing.contact', _('Mail Contacts')))
res.append(('mail.mass_mailing.list', _('Mailing List')))
return res
# indirections for inheritance
@@ -316,7 +332,8 @@ class MassMailing(models.Model):
reply_to = fields.Char(string='Reply To', help='Preferred Reply-To Address',
default=lambda self: self.env['mail.message']._get_default_from())
# recipients
mailing_model = fields.Selection(selection=_mailing_model, string='Recipients Model', required=True, default='mail.mass_mailing.contact')
mailing_model_real = fields.Char(compute='_compute_model', string='Recipients Real Model', default='mail.mass_mailing.contact', required=True)
mailing_model = fields.Selection(selection=_mailing_model, string='Recipients Model', default='mail.mass_mailing.list')
mailing_domain = fields.Char(string='Domain', oldname='domain', default=[])
contact_list_ids = fields.Many2many('mail.mass_mailing.list', 'mail_mass_mailing_list_rel',
string='Mailing Lists')
@@ -357,6 +374,11 @@ class MassMailing(models.Model):
for mass_mailing in self:
mass_mailing.clicks_ratio = mapped_data.get(mass_mailing.id, 0)
@api.depends('mailing_model')
def _compute_model(self):
for record in self:
record.mailing_model_real = (record.mailing_model != 'mail.mass_mailing.list') and record.mailing_model or 'mail.mass_mailing.contact'
def _compute_statistics(self):
""" Compute statistics of the mass mailing """
self.env.cr.execute("""
@@ -430,11 +452,11 @@ class MassMailing(models.Model):
@api.onchange('mailing_model', 'contact_list_ids')
def _onchange_model_and_list(self):
if self.mailing_model == 'mail.mass_mailing.contact':
if self.mailing_model == 'mail.mass_mailing.list':
if self.contact_list_ids:
self.mailing_domain = "[('list_id', 'in', %s), ('opt_out', '=', False)]" % self.contact_list_ids.ids
self.mailing_domain = "[('list_ids', 'in', [%s]), ('opt_out', '=', False)]" % (','.join(map(str,self.contact_list_ids.ids)),)
else:
self.mailing_domain = "[('list_id', '=', False)]"
self.mailing_domain = "[(0, '=', 1)]"
elif 'opt_out' in self.env[self.mailing_model]._fields:
self.mailing_domain = "[('opt_out', '=', False)]"
else:
@@ -485,7 +507,7 @@ class MassMailing(models.Model):
return super(MassMailing, self).read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby)
def update_opt_out(self, email, res_ids, value):
model = self.env[self.mailing_model]
model = self.env[self.mailing_model_real]
if 'opt_out' in model._fields:
email_fname = 'email_from'
if 'email' in model._fields:
@@ -548,14 +570,14 @@ class MassMailing(models.Model):
def get_recipients(self):
if self.mailing_domain:
domain = safe_eval(self.mailing_domain)
res_ids = self.env[self.mailing_model].search(domain).ids
res_ids = self.env[self.mailing_model_real].search(domain).ids
else:
res_ids = []
domain = [('id', 'in', res_ids)]
# randomly choose a fragment
if self.contact_ab_pc < 100:
contact_nbr = self.env[self.mailing_model].search_count(domain)
contact_nbr = self.env[self.mailing_model_real].search_count(domain)
topick = int(contact_nbr / 100.0 * self.contact_ab_pc)
if self.mass_mailing_campaign_id and self.mass_mailing_campaign_id.unique_ab_testing:
already_mailed = self.mass_mailing_campaign_id.get_recipients()[self.mass_mailing_campaign_id.id]
@@ -569,7 +591,7 @@ class MassMailing(models.Model):
def get_remaining_recipients(self):
res_ids = self.get_recipients()
already_mailed = self.env['mail.mail.statistics'].search_read([('model', '=', self.mailing_model),
already_mailed = self.env['mail.mail.statistics'].search_read([('model', '=', self.mailing_model_real),
('res_id', 'in', res_ids),
('mass_mailing_id', '=', self.id)], ['res_id'])
already_mailed_res_ids = [record['res_id'] for record in already_mailed]
@@ -591,7 +613,7 @@ class MassMailing(models.Model):
'attachment_ids': [(4, attachment.id) for attachment in mailing.attachment_ids],
'body': mailing.convert_links()[mailing.id],
'subject': mailing.name,
'model': mailing.mailing_model,
'model': mailing.mailing_model_real,
'email_from': mailing.email_from,
'record_name': False,
'composition_mode': 'mass_mail',
@@ -5,6 +5,8 @@
<menuitem name="Mass Mailing" id="mass_mailing_menu_root" sequence="60" web_icon="mass_mailing,static/description/icon.png"/>
<menuitem name="Mailings" id="mass_mailing_menu"
parent="mass_mailing_menu_root" sequence="1" groups="mass_mailing.group_mass_mailing_user"/>
<menuitem name="Contacts" id="mass_mailing_contact_menu"
parent="mass_mailing_menu_root" sequence="2" groups="mass_mailing.group_mass_mailing_user"/>
<!-- Marketing / Configuration -->
<menuitem name="Configuration" id="mass_mailing_configuration"
parent="mass_mailing_menu_root" sequence="100"/>
@@ -16,33 +18,32 @@
<field name="name">List Subscription</field>
<field name="model_id">mail.mass_mailing.contact</field>
<field name="user_id" eval="False"/>
<field name="context">{'group_by': ['create_date:month'], 'col_group_by': ['list_id']}</field>
<field name="context">{'group_by': ['create_date:month']}</field>
</record>
<record id="filter_contact_unsubscription" model="ir.filters">
<field name="name">List Unsubscription</field>
<field name="model_id">mail.mass_mailing.contact</field>
<field name="user_id" eval="False"/>
<field name="domain">[('opt_out','=',True)]</field>
<field name="context">{'group_by': ['unsubscription_date:month'], 'col_group_by': ['list_id']}</field>
<field name="context">{'group_by': ['unsubscription_date:month']}</field>
</record>
<record model="ir.ui.view" id="view_mail_mass_mailing_contact_search">
<field name="name">mail.mass_mailing.contact.search</field>
<field name="model">mail.mass_mailing.contact</field>
<field name="arch" type="xml">
<search string="Mailing Lists Subscribers">
<field name="name"/>
<field name="email"/>
<field name="list_id"/>
<search string="Contacts">
<field name="name" filter_domain="['|','|',('name','ilike',self),('company_name','ilike',self), ('email','ilike',self)]"
string="Name / Email"/>
<field name="tag_ids"/>
<field name="list_ids"/>
<separator/>
<filter string="Exclude Opt Out" name="not_opt_out" domain="[('opt_out', '=', False)]"/>
<separator/>
<filter string="Exclude Dead" name="not_bounced" domain="[('message_bounce', '&lt;', 5)]"/>
<filter string="Exclude Bounced" name="not_bounced" domain="[('message_bounce', '&lt;', 3)]"/>
<group expand="0" string="Group By">
<filter string="Creation Date" name="group_create_date"
context="{'group_by': 'create_date'}"/>
<filter string="Mailing Lists" name="group_list_id"
context="{'group_by': 'list_id'}"/>
</group>
</search>
</field>
@@ -53,35 +54,85 @@
<field name="model">mail.mass_mailing.contact</field>
<field name="priority">10</field>
<field name="arch" type="xml">
<tree string="Mailing Lists Subscribers" editable="top">
<field name="email"/>
<field name="name"/>
<field name="list_id"/>
<field name="message_bounce"/>
<tree string="Contacts">
<field name="create_date"/>
<field name="unsubscription_date" readonly="1"/>
<field name="name"/>
<field name="company_name"/>
<field name="email"/>
<field name="message_bounce"/>
<field name="opt_out"/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="view_mail_mass_mailing_contact_form">
<field name="name">mail.mass_mailing.contact.form</field>
<field name="model">mail.mass_mailing.contact</field>
<field name="priority">10</field>
<field name="arch" type="xml">
<form string="Contacts">
<sheet>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>
<h1>
<field name="name" placeholder="e.g. John Smith"/>
</h1>
<label for="tag_ids" class="oe_edit_only"/>
<div>
<field name="tag_ids" widget="many2many_tags" style="width: 100%%"/>
</div>
</div>
<group>
<group>
<field name="email"/>
<field name="title_id"/>
<field name="company_name"/>
<field name="country_id"/>
</group>
<group>
<field name="create_date" readonly="1"/>
<field name="unsubscription_date"/>
<label for="message_bounce"/>
<div class="o_row">
<field name="message_bounce"/>
<span> times</span>
</div>
<field name="opt_out"/>
</group>
</group>
<field name="list_ids"/>
</sheet>
</form>
</field>
</record>
<record model="ir.ui.view" id="view_mail_mass_mailing_contact_pivot">
<field name="name">mail.mass_mailing.contact.pivot</field>
<field name="model">mail.mass_mailing.contact</field>
<field name="priority">10</field>
<field name="arch" type="xml">
<pivot string="Contacts" type="pivot" stacked="True">
<field name="create_date" type="row"/>
</pivot>
</field>
</record>
<record model="ir.ui.view" id="view_mail_mass_mailing_contact_graph">
<field name="name">mail.mass_mailing.contact.graph</field>
<field name="model">mail.mass_mailing.contact</field>
<field name="priority">10</field>
<field name="arch" type="xml">
<graph string="Mailing Lists Subscriber" type="pivot" stacked="True">
<graph string="Contacts" type="pivot" stacked="True">
<field name="create_date" type="row"/>
<field name="list_id" type="col"/>
</graph>
</field>
</record>
<record model="ir.actions.act_window" id="action_view_mass_mailing_contacts">
<field name="name">Mailing List Subscribers</field>
<field name="name">Contacts</field>
<field name="res_model">mail.mass_mailing.contact</field>
<field name="view_type">form</field>
<field name="view_mode">tree,graph</field>
<field name="view_mode">tree,form,graph,pivot</field>
<field name="context">{'search_default_not_opt_out': 1}</field>
</record>
@@ -90,7 +141,8 @@
<field name="res_model">mail.mass_mailing.contact</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,graph</field>
<field name="context">{'search_default_list_id': active_id, 'search_default_not_opt_out': 1}</field>
<field name="domain">[('list_ids', 'in', [active_id])]</field>
<field name="context">{'search_default_not_opt_out': 1}</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create a recipient.
@@ -99,7 +151,7 @@
</record>
<menuitem name="Contacts" id="menu_email_mass_mailing_contacts"
parent="mass_mailing_menu" sequence="4" groups="base.group_no_one"
parent="mass_mailing_contact_menu" sequence="4"
action="action_view_mass_mailing_contacts"/>
<!-- MASS MAILING LIST -->
@@ -175,7 +227,7 @@
</record>
<menuitem name="Mailing Lists" id="menu_email_mass_mailing_lists"
parent="mass_mailing_menu" sequence="3"
parent="mass_mailing_contact_menu" sequence="3"
action="action_view_mass_mailing_lists"/>
<!-- MASS MAILING !-->
@@ -296,14 +348,14 @@
<label for="mailing_model" string="Recipients"/>
<div>
<field name="mailing_model" widget="radio" options="{'horizontal': true}"/>
<field name="mailing_model_real" invisible="1"/>
<field name="mailing_domain" widget="char_domain"
placeholder="Select recipients"
attrs="{'invisible': [('mailing_model', '=', 'mail.mass_mailing.contact')]}"
options="{'model_field': 'mailing_model', 'model_domain': 'mailing_domain'}"
attrs="{'invisible': [('mailing_model', '=', 'mail.mass_mailing.list')]}"
options="{'model_field': 'mailing_model_real', 'model_domain': 'mailing_domain'}"
context="{'search_default_not_opt_out':1}"/>
<div attrs="{'invisible': [('mailing_model', '&lt;&gt;', 'mail.mass_mailing.contact')]}">
<div attrs="{'invisible': [('mailing_model', '&lt;&gt;', 'mail.mass_mailing.list')]}">
<label for="contact_list_ids" string="Select mailing lists:" class="oe_edit_only"/>
<field name="contact_list_ids" widget="many2many_tags"
placeholder="Select mailing lists..." class="oe_inline"/>
@@ -38,7 +38,7 @@ class MassMailController(MassMailController):
is_subscriber = False
if email:
contacts_count = request.env['mail.mass_mailing.contact'].sudo().search_count([('list_id', '=', int(list_id)), ('email', '=', email), ('opt_out', '=', False)])
contacts_count = request.env['mail.mass_mailing.contact'].sudo().search_count([('list_ids', 'in', [int(list_id)]), ('email', '=', email), ('opt_out', '=', False)])
is_subscriber = contacts_count > 0
return {'is_subscriber': is_subscriber, 'email': email}
@@ -49,12 +49,12 @@ class MassMailController(MassMailController):
name, email = Contacts.get_name_email(email)
contact_ids = Contacts.search([
('list_id', '=', int(list_id)),
('list_ids', 'in', [int(list_id)]),
('email', '=', email),
], limit=1)
if not contact_ids:
# inline add_to_list as we've already called half of it
Contacts.create({'name': name, 'email': email, 'list_id': int(list_id)})
Contacts.create({'name': name, 'email': email, 'list_ids': [(6,0,[int(list_id)])]})
elif contact_ids.opt_out:
contact_ids.opt_out = False
# add email to session
@@ -22,7 +22,9 @@
<li class="list-group-item">
<input type="checkbox" class="mail_list_checkbox" name="contact_ids"
t-att-value="contact['id']" t-att-checked="'checked' if contact['opt_out'] == False else None"/>
<t t-esc="contact.list_id.name"/>
<t t-foreach="contact.list_ids" t-as="contact_list">
<t t-esc="contact_list.name"/>
</t>
</li>
</t>
</ul>