[REF] mass_mailing: first refactor

Mail statistics are now stored onto a separated object (mail.mail.statistics), allowing to
handle emails separately from statistics (among other removing mail.mail entries while keeping
statistics).

Everything linnked to opened/replied/bounce is not managed by mass_mailing, removed added code
in mail module.

bzr revid: tde@openerp.com-20130913115408-322cyjipdg680as6
This commit is contained in:
Thibault Delavallée 2013-09-13 13:54:08 +02:00
parent 23f9324b94
commit ed62d1dac7
24 changed files with 431 additions and 347 deletions

View File

@ -5,7 +5,6 @@ import openerp
from openerp import SUPERUSER_ID
import openerp.addons.web.http as http
from openerp.addons.web.controllers.main import content_disposition
from openerp.addons.web.http import request
class MailController(http.Controller):
@ -38,10 +37,3 @@ class MailController(http.Controller):
except psycopg2.Error:
pass
return True
@http.route('/mail/track/<int:mail_id>/blank.gif', type='http', auth='admin')
def track_read_email(self, mail_id):
""" Email tracking. """
mail_mail = request.registry.get('mail.mail')
mail_mail.set_opened(request.cr, request.uid, [mail_id])
return False

View File

@ -51,12 +51,6 @@
<field name="value">catchall</field>
</record>
<!-- Bounce Email Alias -->
<record id="icp_mail_bounce_alias" model="ir.config_parameter">
<field name="key">mail.bounce.alias</field>
<field name="value">bounce</field>
</record>
<!-- Discussion subtype for messaging / Chatter -->
<record id="mt_comment" model="mail.message.subtype">
<field name="name">Discussions</field>

View File

@ -62,17 +62,6 @@ class mail_mail(osv.Model):
# and during unlink() we will not cascade delete the parent and its attachments
'notification': fields.boolean('Is Notification',
help='Mail has been created to notify people of an existing mail.message'),
# Bounce and tracking
'opened': fields.datetime(
'Opened',
help='Date when this email has been opened for the first time.'),
'replied': fields.datetime(
'Replied',
help='Date when this email has been replied for the first time.'),
'bounced': fields.datetime(
'Bounced',
help='Date when this email has bounced.'
),
}
_defaults = {
@ -106,30 +95,6 @@ class mail_mail(osv.Model):
def cancel(self, cr, uid, ids, context=None):
return self.write(cr, uid, ids, {'state': 'cancel'}, context=context)
def set_opened(self, cr, uid, ids, context=None):
""" Set as opened """
existing_ids = self.exists(cr, uid, ids, context=context)
for mail in self.browse(cr, uid, existing_ids, context=context):
if not mail.opened:
self.write(cr, uid, [mail.id], {'opened': fields.datetime.now()}, context=context)
return True
def set_replied(self, cr, uid, ids, context=None):
""" Set as replied """
existing_ids = self.exists(cr, uid, ids, context=context)
for mail in self.browse(cr, uid, existing_ids, context=context):
if not mail.replied:
self.write(cr, uid, [mail.id], {'replied': fields.datetime.now()}, context=context)
return True
def set_bounced(self, cr, uid, ids, context=None):
""" Set as bounced """
existing_ids = self.exists(cr, uid, ids, context=context)
for mail in self.browse(cr, uid, existing_ids, context=context):
if not mail.bounced:
self.write(cr, uid, [mail.id], {'bounced': fields.datetime.now()}, context=context)
return True
def process_email_queue(self, cr, uid, ids=None, context=None):
"""Send immediately queued messages, committing after each
message is sent - this is not transactional and should
@ -200,15 +165,6 @@ class mail_mail(osv.Model):
else:
return None
def _get_tracking_url(self, cr, uid, mail, partner=None, context=None):
if not mail.auto_delete:
base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
track_url = urljoin(base_url, 'mail/track/%d/blank.gif' % mail.id)
print base_url, track_url
return '<img src="%s" alt=""/>' % track_url
else:
return ''
def send_get_mail_subject(self, cr, uid, mail, force=False, partner=None, context=None):
""" If subject is void and record_name defined: '<Author> posted on <Resource>'
@ -233,11 +189,8 @@ class mail_mail(osv.Model):
# generate footer
link = self._get_partner_access_link(cr, uid, mail, partner, context=context)
tracking_url = self._get_tracking_url(cr, uid, mail, partner, context=context)
if link:
body = tools.append_content_to_html(body, link, plaintext=False, container_tag='div')
if tracking_url:
body = tools.append_content_to_html(body, tracking_url, plaintext=False, container_tag='div')
return body
def send_get_email_dict(self, cr, uid, mail, partner=None, context=None):

View File

@ -41,11 +41,6 @@
<field name="model"/>
<field name="res_id"/>
</group>
<group string="Tracking">
<field name="opened"/>
<field name="replied"/>
<field name="bounced"/>
</group>
</div>
<div>
<group string="Headers">

View File

@ -778,7 +778,6 @@ class mail_thread(osv.AbstractModel):
"""
assert isinstance(message, Message), 'message must be an email.message.Message at this point'
fallback_model = model
bounce_alias = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.bounce.alias", context=context)
# Get email.message.Message variables for future processing
message_id = message.get('Message-Id')
@ -787,25 +786,6 @@ class mail_thread(osv.AbstractModel):
references = decode_header(message, 'References')
in_reply_to = decode_header(message, 'In-Reply-To')
# 0. Verify whether this is a bounced email (wrong destination,...) -> use it to collect data, such as dead leads
if bounce_alias in email_to:
bounce_match = tools.bounce_re.search(email_to)
if bounce_match:
bounced_mail_id = bounce_match.group(1)
self.pool['mail.mail'].set_bounced(cr, uid, [bounced_mail_id], context=context)
if self.pool['mail.mail'].exists(cr, uid, bounced_mail_id):
mail = self.pool['mail.mail'].browse(cr, uid, bounced_mail_id, context=context)
bounced_model = mail.model
bounced_thread_id = mail.res_id
else:
bounced_model = bounce_match.group(2)
bounced_thread_id = int(bounce_match.group(3)) if bounce_match.group(3) else 0
_logger.info('Routing mail from %s to %s with Message-Id %s: bounced mail from mail %s, model: %s, thread_id: %s',
email_from, email_to, message_id, bounced_mail_id, bounced_model, bounced_thread_id)
if bounced_model and bounced_model in self.pool and hasattr(self.pool[bounced_model], 'message_receive_bounce'):
self.pool[bounced_model].message_receive_bounce(cr, uid, [bounced_thread_id], mail_id=bounced_mail_id, context=context)
return []
# 1. Verify if this is a reply to an existing thread
thread_references = references or in_reply_to
ref_match = thread_references and tools.reference_re.search(thread_references)
@ -894,6 +874,40 @@ class mail_thread(osv.AbstractModel):
"No possible route found for incoming message from %s to %s (Message-Id %s:)." \
"Create an appropriate mail.alias or force the destination model." % (email_from, email_to, message_id)
def message_route_process(self, cr, uid, msg, routes, context=None):
# postpone setting msg.partner_ids after message_post, to avoid double notifications
partner_ids = msg.pop('partner_ids', [])
thread_id = False
for model, thread_id, custom_values, user_id, alias in routes:
if self._name == 'mail.thread':
context.update({'thread_model': model})
if model:
model_pool = self.pool[model]
assert thread_id and hasattr(model_pool, 'message_update') or hasattr(model_pool, 'message_new'), \
"Undeliverable mail with Message-Id %s, model %s does not accept incoming emails" % \
(msg['message_id'], model)
# disabled subscriptions during message_new/update to avoid having the system user running the
# email gateway become a follower of all inbound messages
nosub_ctx = dict(context, mail_create_nosubscribe=True, mail_create_nolog=True)
if thread_id and hasattr(model_pool, 'message_update'):
model_pool.message_update(cr, user_id, [thread_id], msg, context=nosub_ctx)
else:
thread_id = model_pool.message_new(cr, user_id, msg, custom_values, context=nosub_ctx)
else:
assert thread_id == 0, "Posting a message without model should be with a null res_id, to create a private message."
model_pool = self.pool.get('mail.thread')
if not hasattr(model_pool, 'message_post'):
context['thread_model'] = model
model_pool = self.pool['mail.thread']
new_msg_id = model_pool.message_post(cr, uid, [thread_id], context=context, subtype='mail.mt_comment', **msg)
if partner_ids:
# postponed after message_post, because this is an external message and we don't want to create
# duplicate emails due to notifications
self.pool.get('mail.message').write(cr, uid, [new_msg_id], {'partner_ids': partner_ids}, context=context)
return thread_id
def message_process(self, cr, uid, model, message, custom_values=None,
save_original=False, strip_attachments=False,
thread_id=None, context=None):
@ -946,8 +960,7 @@ class mail_thread(osv.AbstractModel):
msg = self.message_parse(cr, uid, msg_txt, save_original=save_original, context=context)
if strip_attachments:
msg.pop('attachments', None)
# postpone setting msg.partner_ids after message_post, to avoid double notifications
partner_ids = msg.pop('partner_ids', [])
if msg.get('message_id'): # should always be True as message_parse generate one if missing
existing_msg_ids = self.pool.get('mail.message').search(cr, SUPERUSER_ID, [
('message_id', '=', msg.get('message_id')),
@ -959,36 +972,7 @@ class mail_thread(osv.AbstractModel):
# find possible routes for the message
routes = self.message_route(cr, uid, msg_txt, msg, model, thread_id, custom_values, context=context)
thread_id = False
for model, thread_id, custom_values, user_id, alias in routes:
if self._name == 'mail.thread':
context.update({'thread_model': model})
if model:
model_pool = self.pool[model]
assert thread_id and hasattr(model_pool, 'message_update') or hasattr(model_pool, 'message_new'), \
"Undeliverable mail with Message-Id %s, model %s does not accept incoming emails" % \
(msg['message_id'], model)
# disabled subscriptions during message_new/update to avoid having the system user running the
# email gateway become a follower of all inbound messages
nosub_ctx = dict(context, mail_create_nosubscribe=True, mail_create_nolog=True)
if thread_id and hasattr(model_pool, 'message_update'):
model_pool.message_update(cr, user_id, [thread_id], msg, context=nosub_ctx)
else:
thread_id = model_pool.message_new(cr, user_id, msg, custom_values, context=nosub_ctx)
else:
assert thread_id == 0, "Posting a message without model should be with a null res_id, to create a private message."
model_pool = self.pool.get('mail.thread')
if not hasattr(model_pool, 'message_post'):
context['thread_model'] = model
model_pool = self.pool['mail.thread']
new_msg_id = model_pool.message_post(cr, uid, [thread_id], context=context, subtype='mail.mt_comment', **msg)
if partner_ids:
# postponed after message_post, because this is an external message and we don't want to create
# duplicate emails due to notifications
self.pool.get('mail.message').write(cr, uid, [new_msg_id], {'partner_ids': partner_ids}, context=context)
thread_id = self.message_route_process(cr, uid, msg, routes, context=context)
return thread_id
def message_new(self, cr, uid, msg_dict, custom_values=None, context=None):
@ -1044,15 +1028,6 @@ class mail_thread(osv.AbstractModel):
self.write(cr, uid, ids, update_vals, context=context)
return True
def message_receive_bounce(self, cr, uid, ids, mail_id=None, context=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.
If it is the case, its content is incremented. """
if self._all_columns.get('message_bounce'):
for obj in self.browse(cr, uid, ids, context=context):
self.write(cr, uid, [obj.id], {'message_bounce': obj.message_bounce + 1}, context=context)
def _message_extract_payload(self, message, save_original=False):
"""Extract body as HTML and attachments from the mail message"""
attachments = []
@ -1303,8 +1278,8 @@ class mail_thread(osv.AbstractModel):
return result
def message_post(self, cr, uid, thread_id, body='', subject=None, type='notification',
subtype=None, parent_id=False, attachments=None, context=None,
content_subtype='html', **kwargs):
subtype=None, parent_id=False, attachments=None, context=None,
content_subtype='html', **kwargs):
""" Post a new message in an existing thread, returning the new
mail.message ID.

View File

@ -32,14 +32,6 @@ class project_configuration(osv.TransientModel):
'Alias Domain',
help="If you have setup a catch-all email domain redirected to the OpenERP server, enter the domain name here."
),
'alias_bounce': fields.char(
'Return-Path for Emails',
help="Return-Path of send Emails. Used to compute bounced emails.",
),
'alias_catchall': fields.char(
'Default Alias',
help='Default email alias',
),
}
def get_default_alias_domain(self, cr, uid, ids, context=None):
@ -56,21 +48,3 @@ class project_configuration(osv.TransientModel):
config_parameters = self.pool.get("ir.config_parameter")
for record in self.browse(cr, uid, ids, context=context):
config_parameters.set_param(cr, uid, "mail.catchall.domain", record.alias_domain or '', context=context)
def get_default_alias_bounce(self, cr, uid, ids, context=None):
alias_bounce = self.pool.get("ir.config_parameter").get_param(cr, uid, "mail.bounce.alias", context=context)
return {'alias_bounce': alias_bounce}
def set_alias_bounce(self, cr, uid, ids, context=None):
config_parameters = self.pool.get("ir.config_parameter")
for record in self.browse(cr, uid, ids, context=context):
config_parameters.set_param(cr, uid, "mail.bounce.alias", record.alias_bounce or '', context=context)
def get_default_alias_catchall(self, cr, uid, ids, context=None):
alias_catchall = self.pool.get("ir.config_parameter").get_param(cr, uid, "mail.catchall.alias", context=context)
return {'alias_catchall': alias_catchall}
def set_alias_catchall(self, cr, uid, ids, context=None):
config_parameters = self.pool.get("ir.config_parameter")
for record in self.browse(cr, uid, ids, context=context):
config_parameters.set_param(cr, uid, "mail.catchall.alias", record.alias_catchall or '', context=context)

View File

@ -11,14 +11,6 @@
<label for="alias_domain" class="oe_inline"/>
<field name="alias_domain" placeholder="mycompany.my.openerp.com" class="oe_inline"/>
</div>
<div>
<label for="alias_bounce" class="oe_inline"/>
<field name="alias_bounce" placeholder="bounce" class="oe_inline"/>
</div>
<div>
<label for="alias_catchall" class="oe_inline"/>
<field name="alias_catchall" placeholder="catchall" class="oe_inline"/>
</div>
</xpath>
</field>
</record>

View File

@ -19,10 +19,9 @@
#
##############################################################################
from . import test_mail_mail, test_mail_group, test_mail_message, test_mail_features, test_mail_gateway, test_message_read, test_invite
from . import test_mail_group, test_mail_message, test_mail_features, test_mail_gateway, test_message_read, test_invite
checks = [
# test_mail_mail,
test_mail_group,
test_mail_message,
test_mail_features,

View File

@ -22,3 +22,4 @@
import mass_mailing
import mail_mail
import wizard
import controllers

View File

@ -21,6 +21,7 @@
{
'name': 'Mass Mailing Campaigns',
'description': """TODO""",
'version': '1.0',
'author': 'OpenERP',
'website': 'http://www.openerp.com',
@ -31,11 +32,10 @@
'web_kanban_gauge',
'web_kanban_sparkline',
],
'description': """TODO""",
'data': [
'mail_data.xml',
'mass_mailing_view.xml',
'mass_mailing_demo.xml',
'mail_mail_view.xml',
'wizard/mail_compose_message_view.xml',
'wizard/mail_mass_mailing_create_segment.xml',
'security/ir.model.access.csv',

View File

@ -0,0 +1,3 @@
import main
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,12 @@
import openerp.addons.web.http as http
from openerp.addons.web.http import request
class MassMailController(http.Controller):
@http.route('/mail/track/<int:mail_id>/blank.gif', type='http', auth='admin')
def track_mail_open(self, mail_id):
""" Email tracking. """
mail_mail_stats = request.registry.get('mail.mail.statistics')
mail_mail_stats.set_opened(request.cr, request.uid, mail_ids=[mail_id])
return False

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<!-- Bounce Email Alias -->
<record id="icp_mail_bounce_alias" model="ir.config_parameter">
<field name="key">mail.bounce.alias</field>
<field name="value">bounce</field>
</record>
</data>
</openerp>

View File

@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2013-today OpenERP SA (<http://www.openerp.com>)
# Copyright (C) 2013-Today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@ -19,7 +19,11 @@
#
##############################################################################
from openerp.osv import osv, fields
from urlparse import urljoin
from openerp import tools
from openerp import SUPERUSER_ID
from openerp.osv import osv
class MailMail(osv.Model):
@ -27,23 +31,29 @@ class MailMail(osv.Model):
_name = 'mail.mail'
_inherit = ['mail.mail']
_columns = {
'mass_mailing_segment_id': fields.many2one(
'mail.mass_mailing.segment', 'Mass Mailing Segment',
ondelete='set null',
),
'mass_mailing_campaign_id': fields.related(
'mass_mailing_segment_id', 'mass_mailing_campaign_id',
type='many2one', ondelete='set null',
relation='mail.mass_mailing.campaign',
string='Mass Mailing Campaign',
store=True, readonly=True,
),
'template_id': fields.related(
'mass_mailing_segment_id', 'template_id',
type='many2one', ondelete='set null',
relation='email.template',
string='Email Template',
store=True, readonly=True,
),
}
def create(self, cr, uid, values, context=None):
""" Override mail_mail creation to create an entry in mail.mail.statistics """
# TDE note: should be after 'all values computed', to have values (FIXME after merging other branch holding create refactoring)
mail_id = super(MailMail, self).create(cr, uid, values, context=context)
message_id = self.browse(cr, SUPERUSER_ID, mail_id).message_id
self.pool['mail.mail.statistics'].create(
cr, uid, {
'mail_mail_id': mail_id,
'message_id': message_id,
}, context=context)
return mail_id
def _get_tracking_url(self, cr, uid, mail, partner=None, context=None):
base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
track_url = urljoin(base_url, 'mail/track/%d/blank.gif' % mail.id)
return '<img src="%s" alt=""/>' % track_url
def send_get_mail_body(self, cr, uid, mail, partner=None, context=None):
""" Override to add the tracking URL to the body. """
body = super(MailMail, self).send_get_mail_body(cr, uid, mail, partner=partner, context=context)
# generate tracking URL
tracking_url = self._get_tracking_url(cr, uid, mail, partner, context=context)
if tracking_url:
body = tools.append_content_to_html(body, tracking_url, plaintext=False, container_tag='div')
return body

View File

@ -1,20 +0,0 @@
<?xml version="1.0"?>
<openerp>
<data>
<!-- FOLLOWERS !-->
<record model="ir.ui.view" id="mail_mail_form_mass_mailing">
<field name="name">mail.mail.form.mass_mailing</field>
<field name="model">mail.mail</field>
<field name="inherit_id" ref="mail.view_mail_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='opened']" position="before">
<field name="mass_mailing_campaign_id"/>
<field name="mass_mailing_segment_id"/>
<field name="template_id"/>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,82 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2013-Today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
#
##############################################################################
import logging
from openerp import tools
from openerp.addons.mail.mail_thread import decode_header
from openerp.osv import osv
_logger = logging.getLogger(__name__)
class MailThread(osv.Model):
""" Update MailThread to add the feature of bounced emails and replied emails
in message_process. """
_name = 'mail.thread'
_inherit = ['mail.thread']
def message_route_check_bounce(self, cr, uid, message, context=None):
bounce_alias = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.bounce.alias", context=context)
message_id = message.get('Message-Id')
email_from = decode_header(message, 'From')
email_to = decode_header(message, 'To')
# 0. Verify whether this is a bounced email (wrong destination,...) -> use it to collect data, such as dead leads
if bounce_alias in email_to:
bounce_match = tools.bounce_re.search(email_to)
if bounce_match:
bounced_mail_id = bounce_match.group(1)
self.pool['mail.mail'].set_bounced(cr, uid, [bounced_mail_id], context=context)
if self.pool['mail.mail'].exists(cr, uid, bounced_mail_id):
mail = self.pool['mail.mail'].browse(cr, uid, bounced_mail_id, context=context)
bounced_model = mail.model
bounced_thread_id = mail.res_id
else:
bounced_model = bounce_match.group(2)
bounced_thread_id = int(bounce_match.group(3)) if bounce_match.group(3) else 0
_logger.info('Routing mail from %s to %s with Message-Id %s: bounced mail from mail %s, model: %s, thread_id: %s',
email_from, email_to, message_id, bounced_mail_id, bounced_model, bounced_thread_id)
if bounced_model and bounced_model in self.pool and hasattr(self.pool[bounced_model], 'message_receive_bounce'):
self.pool[bounced_model].message_receive_bounce(cr, uid, [bounced_thread_id], mail_id=bounced_mail_id, context=context)
return False
return True
def message_route(self, cr, uid, message, message_dict, model=None, thread_id=None,
custom_values=None, context=None):
if not self.message_route_check_bounce(cr, uid, message, context=context):
return []
return super(MailThread, self).message_route(cr, uid, message, message_dict, model, thread_id, custom_values, context)
def message_receive_bounce(self, cr, uid, ids, mail_id=None, context=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.
If it is the case, its content is incremented. """
if self._all_columns.get('message_bounce'):
for obj in self.browse(cr, uid, ids, context=context):
self.write(cr, uid, [obj.id], {'message_bounce': obj.message_bounce + 1}, context=context)
def message_route_process(self, cr, uid, msg, routes, context=None):
if msg.get('message_id'):
self.pool['mail.mail.statistics'].set_replied(cr, uid, mail_message_ids=[msg.get('message_id')], context=context)
return super(MailThread, self).message_route_process(cr, uid, msg, routes, context=context)

View File

@ -38,42 +38,46 @@ class MassMailingCampaign(osv.Model):
results = dict.fromkeys(ids, False)
for campaign in self.browse(cr, uid, ids, context=context):
results[campaign.id] = {
'sent': len(campaign.mail_ids),
'opened': len([mail for mail in campaign.mail_ids if mail.opened]),
'replied': len([mail for mail in campaign.mail_ids if mail.replied]),
'bounced': len([mail for mail in campaign.mail_ids if mail.bounced]),
'sent': len(campaign.statistics_ids),
# delivered: shouldn't be: all mails - (failed + bounced) ?
'delivered': len([mail for mail in campaign.mail_ids if mail.state == 'sent' and not mail.bounced]),
'delivered': len([stat for stat in campaign.statistics_ids if not stat.bounced]), # stat.state == 'sent' and
'opened': len([stat for stat in campaign.statistics_ids if stat.opened]),
'replied': len([stat for stat in campaign.statistics_ids if stat.replied]),
'bounced': len([stat for stat in campaign.statistics_ids if stat.bounced]),
}
return results
def _get_segment_kanban_ids(self, cr, uid, ids, name, arg, context=None):
def _get_mass_mailing_kanban_ids(self, cr, uid, ids, name, arg, context=None):
results = dict.fromkeys(ids, '')
for campaign in self.browse(cr, uid, ids, context=context):
segment_results = []
for segment in campaign.segment_ids:
segment_object = {}
mass_mailing_results = []
for mass_mailing in campaign.mass_mailing_ids:
mass_mailing_object = {}
for attr in ['name', 'sent', 'delivered', 'opened', 'replied', 'bounced']:
segment_object[attr] = getattr(segment, attr)
segment_results.append(segment_object)
results[campaign.id] = segment_results
mass_mailing_object[attr] = getattr(mass_mailing, attr)
mass_mailing_results.append(mass_mailing_object)
results[campaign.id] = mass_mailing_results
return results
_columns = {
'name': fields.char(
'Campaign Name', required=True,
),
'segment_ids': fields.one2many(
'mail.mass_mailing.segment', 'mass_mailing_campaign_id',
'Segments',
'user_id': fields.many2one(
'res.users', 'Responsible',
required=True,
),
'segment_kanban_ids': fields.function(
_get_segment_kanban_ids,
type='text', string='Segments (kanban data)',
help='This field has for purpose to gather data about segment to display them in kanban view as nested kanban views is not possible currently',
'mass_mailing_ids': fields.one2many(
'mail.mass_mailing', 'mass_mailing_campaign_id',
'Mass Mailings',
),
'mail_ids': fields.one2many(
'mail.mail', 'mass_mailing_campaign_id',
'mass_mailing_kanban_ids': fields.function(
_get_mass_mailing_kanban_ids,
type='text', string='Mass Mailings (kanban data)',
help='This field has for purpose to gather data about mass mailings to display them in kanban view as nested kanban views is not possible currently',
),
'statistics_ids': fields.one2many(
'mail.mail.statistics', 'mass_mailing_campaign_id',
'Sent Emails',
),
'color': fields.integer('Color Index'),
@ -105,17 +109,21 @@ class MassMailingCampaign(osv.Model):
),
}
def launch_segment_create_wizard(self, cr, uid, ids, context=None):
# _defaults = {
# 'user_id': lambda self, cr, uid, ctx=None: uid,
# },
def launch_mass_mailing_create_wizard(self, cr, uid, ids, context=None):
ctx = dict(context)
ctx.update({
'default_mass_mailing_campaign_id': ids[0],
})
return {
'name': _('Create a Segment for the Campaign'),
'name': _('Create a Mass Mailing for the Campaign'),
'type': 'ir.actions.act_window',
'view_type': 'form',
'view_mode': 'form',
'res_model': 'mail.mass_mailing.segment.create',
'res_model': 'mail.mass_mailing.create',
'views': [(False, 'form')],
'view_id': False,
'target': 'new',
@ -123,12 +131,12 @@ class MassMailingCampaign(osv.Model):
}
class MassMailingSegment(osv.Model):
""" MassMailingSegment models a segment for a mass mailign campaign. A segment
is an occurence of sending emails. """
class MassMailing(osv.Model):
""" MassMailing models a wave of emails for a mass mailign campaign.
A mass mailing is an occurence of sending emails. """
_name = 'mail.mass_mailing.segment'
_description = 'Segment of a mass mailing campaign'
_name = 'mail.mass_mailing'
_description = 'Wave of sending emails'
# number of periods for tracking mail_mail statistics
_period_number = 6
@ -162,7 +170,7 @@ class MassMailingSegment(osv.Model):
def _get_monthly_statistics(self, cr, uid, ids, field_name, arg, context=None):
""" TODO
"""
obj = self.pool['mail.mail']
obj = self.pool['mail.mail.statistics']
res = {}
context['datetime_format'] = {
'opened': {
@ -179,22 +187,22 @@ class MassMailingSegment(osv.Model):
for id in ids:
res[id] = {}
date_begin = self.browse(cr, uid, id, context=context).date
domain = [('mass_mailing_segment_id', '=', id), ('opened', '>=', date_begin)]
domain = [('mass_mailing_id', '=', id), ('opened', '>=', date_begin)]
res[id]['opened_monthly'] = self.__get_bar_values(cr, uid, id, obj, domain, ['opened'], 'opened_count', 'opened', context=context)
domain = [('mass_mailing_segment_id', '=', id), ('replied', '>=', date_begin)]
domain = [('mass_mailing_id', '=', id), ('replied', '>=', date_begin)]
res[id]['replied_monthly'] = self.__get_bar_values(cr, uid, id, obj, domain, ['replied'], 'replied_count', 'replied', context=context)
return res
def _get_statistics(self, cr, uid, ids, name, arg, context=None):
""" Compute statistics of the mass mailing campaign """
results = dict.fromkeys(ids, False)
for segment in self.browse(cr, uid, ids, context=context):
results[segment.id] = {
'sent': len(segment.mail_ids),
'delivered': len([mail for mail in segment.mail_ids if mail.state == 'sent' and not mail.bounced]),
'opened': len([mail for mail in segment.mail_ids if mail.opened]),
'replied': len([mail for mail in segment.mail_ids if mail.replied]),
'bounced': len([mail for mail in segment.mail_ids if mail.bounced]),
for mass_mailing in self.browse(cr, uid, ids, context=context):
results[mass_mailing.id] = {
'sent': len(mass_mailing.statistics_ids),
'delivered': len([stat for stat in mass_mailing.statistics_ids if not stat.bounced]), # mail.state == 'sent' and
'opened': len([stat for stat in mass_mailing.statistics_ids if stat.opened]),
'replied': len([stat for stat in mass_mailing.statistics_ids if stat.replied]),
'bounced': len([stat for stat in mass_mailing.statistics_ids if stat.bounced]),
}
return results
@ -214,9 +222,9 @@ class MassMailingSegment(osv.Model):
'mass_mailing_campaign_id', 'color',
type='integer', string='Color Index',
),
# mail_mail data
'mail_ids': fields.one2many(
'mail.mail', 'mass_mailing_segment_id',
# statistics data
'statistics_ids': fields.one2many(
'mail.mail.statistics', 'mass_mailing_id',
'Send Emails',
),
'sent': fields.function(
@ -260,3 +268,95 @@ class MassMailingSegment(osv.Model):
_defaults = {
'date': fields.datetime.now(),
}
class MailMailStats(osv.Model):
""" MailMailStats models the statistics collected about emails. Those statistics
are stored in a separated model and table to avoid bloating the mail_mail table
with statistics values. This also allows to delete emails send with mass mailing
without loosing the statistics about them. """
_name = 'mail.mail.statistics'
_description = 'Email Statistics'
_rec_name = 'message_id'
_order = 'message_id'
_columns = {
'mail_mail_id': fields.integer(
'Mail ID',
help='ID of the related mail_mail. This field is an integer field because'
'the related mail_mail can be deleted separately from its statistics.'
),
'message_id': fields.char(
'Message-ID', required=True,
),
# campaign / wave data
'mass_mailing_id': fields.many2one(
'mail.mass_mailing', 'Mass Mailing',
ondelete='set null',
),
'mass_mailing_campaign_id': fields.related(
'mass_mailing_id', 'mass_mailing_campaign_id',
type='many2one', ondelete='set null',
relation='mail.mass_mailing.campaign',
string='Mass Mailing Campaign',
store=True, readonly=True,
),
'template_id': fields.related(
'mass_mailing_id', 'template_id',
type='many2one', ondelete='set null',
relation='email.template',
string='Email Template',
store=True, readonly=True,
),
# Bounce and tracking
'opened': fields.datetime(
'Opened',
help='Date when this email has been opened for the first time.'),
'replied': fields.datetime(
'Replied',
help='Date when this email has been replied for the first time.'),
'bounced': fields.datetime(
'Bounced',
help='Date when this email has bounced.'
),
}
def set_opened(self, cr, uid, ids=None, mail_mail_ids=None, mail_message_ids=None, context=None):
""" Set as opened """
if not ids and mail_mail_ids:
ids = self.search(cr, uid, [('mail_mail_id', 'in', mail_mail_ids)], context=context)
elif not ids and mail_message_ids:
ids = self.search(cr, uid, [('message_id', 'in', mail_message_ids)], context=context)
else:
ids = []
for stat in self.browse(cr, uid, ids, context=context):
if not stat.opened:
self.write(cr, uid, [stat.id], {'opened': fields.datetime.now()}, context=context)
return True
def set_replied(self, cr, uid, ids=None, mail_mail_ids=None, mail_message_ids=None, context=None):
""" Set as replied """
if not ids and mail_mail_ids:
ids = self.search(cr, uid, [('mail_mail_id', 'in', mail_mail_ids)], context=context)
elif not ids and mail_message_ids:
ids = self.search(cr, uid, [('message_id', 'in', mail_message_ids)], context=context)
else:
ids = []
for stat in self.browse(cr, uid, ids, context=context):
if not stat.replied:
self.write(cr, uid, [stat.id], {'replied': fields.datetime.now()}, context=context)
return True
def set_bounced(self, cr, uid, ids=None, mail_mail_ids=None, mail_message_ids=None, context=None):
""" Set as bounced """
if not ids and mail_mail_ids:
ids = self.search(cr, uid, [('mail_mail_id', 'in', mail_mail_ids)], context=context)
elif not ids and mail_message_ids:
ids = self.search(cr, uid, [('message_id', 'in', mail_message_ids)], context=context)
else:
ids = []
for stat in self.browse(cr, uid, ids, context=context):
if not stat.bounced:
self.write(cr, uid, [stat.id], {'bounced': fields.datetime.now()}, context=context)
return True

View File

@ -20,60 +20,69 @@
<record id="mass_mail_campaign_1" model="mail.mass_mailing.campaign">
<field name="name">Partners Newsletter</field>
<field name="user_id" eval="ref('base.user_root')"/>
</record>
<record id="mass_mail_segment_1" model="mail.mass_mailing.segment">
<record id="mass_mail_1" model="mail.mass_mailing">
<field name="name">First Newsletter</field>
<field name="template_id" eval="ref('mass_mail_template_1')"/>
<field name="date" eval="(DateTime.today() - relativedelta(days=3)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="mass_mailing_campaign_id" eval="ref('mass_mail_campaign_1')"/>
</record>
<record id="mass_mail_segment_2" model="mail.mass_mailing.segment">
<record id="mass_mail_2" model="mail.mass_mailing">
<field name="name">Second Newsletter</field>
<field name="template_id" eval="ref('mass_mail_template_2')"/>
<field name="date" eval="(DateTime.today() - relativedelta(days=3)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="mass_mailing_campaign_id" eval="ref('mass_mail_campaign_1')"/>
</record>
<record id="mass_mail_email_1" model="mail.mail">
<field name="mass_mailing_segment_id" eval="ref('mass_mail_segment_1')"/>
<record id="mass_mail_email_1" model="mail.mail.statistics">
<field name="mass_mailing_id" eval="ref('mass_mail_1')"/>
<field name="message_id">1111000@OpenERP.com</field>
<field name="opened" eval="(DateTime.today() - relativedelta(days=2)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="replied" eval="(DateTime.today() - relativedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="state">sent</field>
</record>
<record id="mass_mail_email_2" model="mail.mail">
<field name="mass_mailing_segment_id" eval="ref('mass_mail_segment_1')"/>
<record id="mass_mail_email_2" model="mail.mail.statistics">
<field name="mass_mailing_id" eval="ref('mass_mail_1')"/>
<field name="message_id">1111001@OpenERP.com</field>
<field name="opened" eval="(DateTime.today() - relativedelta(days=2)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="replied" eval="(DateTime.today() - relativedelta(days=0)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="state">sent</field>
</record>
<record id="mass_mail_email_3" model="mail.mail">
<field name="mass_mailing_segment_id" eval="ref('mass_mail_segment_1')"/>
<record id="mass_mail_email_3" model="mail.mail.statistics">
<field name="mass_mailing_id" eval="ref('mass_mail_1')"/>
<field name="message_id">1111002@OpenERP.com</field>
<field name="opened" eval="(DateTime.today() - relativedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="state">sent</field>
</record>
<record id="mass_mail_email_4" model="mail.mail">
<field name="mass_mailing_segment_id" eval="ref('mass_mail_segment_1')"/>
<record id="mass_mail_email_4" model="mail.mail.statistics">
<field name="mass_mailing_id" eval="ref('mass_mail_1')"/>
<field name="message_id">1111003@OpenERP.com</field>
<field name="state">sent</field>
</record>
<record id="mass_mail_email_5" model="mail.mail">
<field name="mass_mailing_segment_id" eval="ref('mass_mail_segment_1')"/>
<record id="mass_mail_email_5" model="mail.mail.statistics">
<field name="mass_mailing_id" eval="ref('mass_mail_1')"/>
<field name="message_id">1111004@OpenERP.com</field>
<field name="bounced" eval="(DateTime.today() - relativedelta(days=3)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="state">sent</field>
</record>
<record id="mass_mail_email_2_1" model="mail.mail">
<field name="mass_mailing_segment_id" eval="ref('mass_mail_segment_2')"/>
<record id="mass_mail_email_2_1" model="mail.mail.statistics">
<field name="mass_mailing_id" eval="ref('mass_mail_2')"/>
<field name="message_id">1111005@OpenERP.com</field>
<field name="opened" eval="(DateTime.today() - relativedelta(days=3)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="state">sent</field>
</record>
<record id="mass_mail_email_2_2" model="mail.mail">
<field name="mass_mailing_segment_id" eval="ref('mass_mail_segment_2')"/>
<record id="mass_mail_email_2_2" model="mail.mail.statistics">
<field name="mass_mailing_id" eval="ref('mass_mail_2')"/>
<field name="message_id">1111006@OpenERP.com</field>
<field name="opened" eval="(DateTime.today() - relativedelta(days=2)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="state">sent</field>
</record>
<record id="mass_mail_email_2_3" model="mail.mail">
<field name="mass_mailing_segment_id" eval="ref('mass_mail_segment_2')"/>
<record id="mass_mail_email_2_3" model="mail.mail.statistics">
<field name="mass_mailing_id" eval="ref('mass_mail_2')"/>
<field name="message_id">1111007@OpenERP.com</field>
<field name="state">sent</field>
</record>

View File

@ -20,8 +20,8 @@
<field name="arch" type="xml">
<form string="Mass Mailing Campaign" version="7.0">
<header>
<button name="launch_segment_create_wizard" type="object"
class="oe_highlight" string="Create a New Segment"/>
<button name="launch_mass_mailing_create_wizard" type="object"
class="oe_highlight" string="Create a New Mailing"/>
</header>
<sheet>
<group>
@ -32,8 +32,8 @@
<field name="replied"/>
<field name="bounced"/>
</group>
<label for="segment_ids"/>
<field name="segment_ids"/>
<label for="mass_mailing_ids"/>
<field name="mass_mailing_ids"/>
</sheet>
</form>
</field>
@ -46,29 +46,29 @@
<field name="model">mail.mass_mailing.campaign</field>
<field name="arch" type="xml">
<kanban>
<field name="segment_kanban_ids"/>
<field name="mass_mailing_kanban_ids"/>
<field name='sent'/>
<field name='color'/>
<templates>
<t t-name="mass_mailing.segment">
<t t-name="mass_mailing.mass_mailing">
<div>
<h4><t t-raw="segment.name"/></h4>
<h4><t t-raw="mass_mailing.name"/></h4>
</div>
<div>
<p class="oe_mail_stats">
<span class="oe_mail_result"><t t-raw="segment.sent"/></span><br />
<span class="oe_mail_result"><t t-raw="mass_mailing.sent"/></span><br />
Sent
</p>
<p class="oe_mail_stats">
<span class="oe_mail_result"><t t-raw="segment.delivered"/></span><br />
<span class="oe_mail_result"><t t-raw="mass_mailing.delivered"/></span><br />
Delivered
</p>
<p class="oe_mail_stats">
<span class="oe_mail_result"><t t-raw="segment.opened"/></span><br />
<span class="oe_mail_result"><t t-raw="mass_mailing.opened"/></span><br />
Opened
</p>
<p class="oe_mail_stats">
<span class="oe_mail_result"><t t-raw="segment.replied"/></span><br />
<span class="oe_mail_result"><t t-raw="mass_mailing.replied"/></span><br />
Replied
</p>
</div>
@ -93,8 +93,8 @@
<field name="replied" widget="gauge" style="width:160px; height: 120px;"
options="{'max_field': 'sent'}"/>
</div>
<t t-foreach='record.segment_kanban_ids.value' t-as='segment'>
<t t-call="mass_mailing.segment"/>
<t t-foreach='record.mass_mailing_kanban_ids.value' t-as='mass_mailing'>
<t t-call="mass_mailing.mass_mailing"/>
</t>
</div>
<div class="oe_clear"></div>
@ -112,13 +112,13 @@
<field name="view_mode">kanban,tree,form</field>
</record>
<!-- MASS MAILING SEGMENTS !-->
<record model="ir.ui.view" id="view_mail_mass_mailing_segment_tree">
<field name="name">mail.mass_mailing.segment.tree</field>
<field name="model">mail.mass_mailing.segment</field>
<!-- MASS MAILING !-->
<record model="ir.ui.view" id="view_mail_mass_mailing_tree">
<field name="name">mail.mass_mailing.tree</field>
<field name="model">mail.mass_mailing</field>
<field name="priority">10</field>
<field name="arch" type="xml">
<tree string="Mass Mailing Segments">
<tree string="Mass Mailings">
<field name="name"/>
<field name="sent"/>
<field name="delivered"/>
@ -128,11 +128,11 @@
</field>
</record>
<record model="ir.ui.view" id="view_mail_mass_mailing_segment_form">
<field name="name">mail.mass_mailing.segment.form</field>
<field name="model">mail.mass_mailing.segment</field>
<record model="ir.ui.view" id="view_mail_mass_mailing_form">
<field name="name">mail.mass_mailing.form</field>
<field name="model">mail.mass_mailing</field>
<field name="arch" type="xml">
<form string="Mass Mailing Segment" version="7.0">
<form string="Mass Mailing" version="7.0">
<sheet>
<group>
<group>
@ -150,12 +150,12 @@
</field>
</record>
<record model="ir.ui.view" id="view_mail_mass_mailing_segment_form_readonly">
<field name="name">mail.mass_mailing.segment.form</field>
<field name="model">mail.mass_mailing.segment</field>
<record model="ir.ui.view" id="view_mail_mass_mailing_form_readonly">
<field name="name">mail.mass_mailing.form</field>
<field name="model">mail.mass_mailing</field>
<field name="priority">18</field>
<field name="arch" type="xml">
<form string="Mass Mailing Segment" version="7.0">
<form string="Mass Mailing" version="7.0">
<sheet>
<group>
<group>
@ -173,9 +173,9 @@
</field>
</record>
<record model="ir.ui.view" id="view_mail_mass_mailing_segment_kanban">
<field name="name">mail.mass_mailing.segment.kanban</field>
<field name="model">mail.mass_mailing.segment</field>
<record model="ir.ui.view" id="view_mail_mass_mailing_kanban">
<field name="name">mail.mass_mailing.kanban</field>
<field name="model">mail.mass_mailing</field>
<field name="arch" type="xml">
<kanban>
<field name='color'/>
@ -229,9 +229,9 @@
</field>
</record>
<record id="action_view_mass_mailing_segments" model="ir.actions.act_window">
<field name="name">Mass Mailing Segments</field>
<field name="res_model">mail.mass_mailing.segment</field>
<record id="action_view_mass_mailings" model="ir.actions.act_window">
<field name="name">Mass Mailings</field>
<field name="res_model">mail.mass_mailing</field>
<field name="view_type">form</field>
<field name="view_mode">kanban,tree,form</field>
</record>
@ -245,9 +245,9 @@
<menuitem name="Campaigns" id="menu_email_campaigns"
parent="mass_mailing_campaign" sequence="1"
action="action_view_mass_mailing_campaigns"/>
<menuitem name="Segments" id="menu_email_segments"
<menuitem name="Mass Mailings" id="menu_email_mass_mailings"
parent="mass_mailing_campaign" sequence="2"
action="action_view_mass_mailing_segments"/>
action="action_view_mass_mailings"/>
</data>
</openerp>

View File

@ -1,5 +1,6 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_mass_mailing_campaign,mail.mass_mailing.campaign,model_mail_mass_mailing_campaign,,1,1,1,0
access_mass_mailing_campaign_system,mail.mass_mailing.campaign.system,model_mail_mass_mailing_campaign,base.group_system,1,1,1,1
access_mass_mailing_segment,mail.mass_mailing.segment,model_mail_mass_mailing_segment,,1,1,1,0
access_mass_mailing_segment_system,mail.mass_mailing.segment.system,model_mail_mass_mailing_segment,base.group_system,1,1,1,1
access_mass_mailing,mail.mass_mailing,model_mail_mass_mailing,,1,1,1,0
access_mass_mailing_system,mail.mass_mailing.system,model_mail_mass_mailing,base.group_system,1,1,1,1
access_mail_mail_statistics,mail.mail.statistics,model_mail_mail_statistics,,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_mass_mailing_campaign mail.mass_mailing.campaign model_mail_mass_mailing_campaign 1 1 1 0
3 access_mass_mailing_campaign_system mail.mass_mailing.campaign.system model_mail_mass_mailing_campaign base.group_system 1 1 1 1
4 access_mass_mailing_segment access_mass_mailing mail.mass_mailing.segment mail.mass_mailing model_mail_mass_mailing_segment model_mail_mass_mailing 1 1 1 0
5 access_mass_mailing_segment_system access_mass_mailing_system mail.mass_mailing.segment.system mail.mass_mailing.system model_mail_mass_mailing_segment model_mail_mass_mailing base.group_system 1 1 1 1
6 access_mail_mail_statistics mail.mail.statistics model_mail_mail_statistics 1 1 1 1

View File

@ -34,8 +34,8 @@ class MailComposeMessage(osv.TransientModel):
'mass_mailing_campaign_id': fields.many2one(
'mail.mass_mailing.campaign', 'Mass mailing campaign',
),
'mass_mailing_segment_id': fields.many2one(
'mail.mass_mailing.segment', 'Mass mailing segment',
'mass_mailing_id': fields.many2one(
'mail.mass_mailing', 'Mass mailing',
domain="[('mass_mailing_campaign_id', '=', mass_mailing_campaign_id)]",
),
}
@ -44,18 +44,18 @@ class MailComposeMessage(osv.TransientModel):
'use_mass_mailing_campaign': False,
}
def onchange_mass_mail_campaign_id(self, cr, uid, ids, mass_mailing_campaign_id, mass_mail_segment_id, context=None):
if mass_mail_segment_id:
segment = self.pool['mail.mass_mailing.segment'].browse(cr, uid, mass_mail_segment_id, context=context)
if segment.mass_mailing_campaign_id.id == mass_mailing_campaign_id:
def onchange_mass_mail_campaign_id(self, cr, uid, ids, mass_mailing_campaign_id, mass_mailing_id, context=None):
if mass_mailing_id:
mass_mailing = self.pool['mail.mass_mailing'].browse(cr, uid, mass_mailing_id, context=context)
if mass_mailing.mass_mailing_campaign_id.id == mass_mailing_campaign_id:
return {}
return {'value': {'mass_mailing_segment_id': False}}
return {'value': {'mass_mailing_id': False}}
def render_message_batch(self, cr, uid, wizard, res_ids, context=None):
""" Override method that generated the mail content by adding the mass
mailing campaign, when doing pure email mass mailing. """
res = super(MailComposeMessage, self).render_message_batch(cr, uid, wizard, res_ids, context=context)
if wizard.composition_mode == 'mass_mail' and wizard.use_mass_mailing_campaign and wizard.mass_mailing_segment_id: # TODO: which kind of mass mailing ?
if wizard.composition_mode == 'mass_mail' and wizard.use_mass_mailing_campaign and wizard.mass_mailing_id: # TODO: which kind of mass mailing ?
for res_id in res_ids:
res[res_id]['mass_mailing_segment_id'] = wizard.mass_mailing_segment_id.id
res[res_id]['mass_mailing_id'] = wizard.mass_mailing_id.id
return res

View File

@ -15,16 +15,16 @@
<div>
<group>
<field name="mass_mailing_campaign_id"
on_change="onchange_mass_mail_campaign_id(mass_mailing_campaign_id, mass_mailing_segment_id, context)"
on_change="onchange_mass_mail_campaign_id(mass_mailing_campaign_id, mass_mailing_id, context)"
attrs="{'invisible': ['|', ('composition_mode', '!=', 'mass_mail'), ('use_mass_mailing_campaign', '=', False)],
'required': [('composition_mode', '=', 'mass_mail'), ('use_mass_mailing_campaign', '=', True)]}"/>
<field name="mass_mailing_segment_id"
<field name="mass_mailing_id"
attrs="{'invisible': ['|', ('composition_mode', '!=', 'mass_mail'), ('use_mass_mailing_campaign', '=', False)],
'required': [('composition_mode', '=', 'mass_mail'), ('use_mass_mailing_campaign', '=', True)]}"
context="{'default_mass_mailing_campaign_id': mass_mailing_campaign_id,
'default_template_id': template_id,
'default_domain': active_domain,
'form_view_ref': 'mass_mailing.view_mail_mass_mailing_segment_form_readonly'}"/>
'form_view_ref': 'mass_mailing.view_mail_mass_mailing_form_readonly'}"/>
</group>
</div>
</xpath>

View File

@ -24,11 +24,11 @@ from openerp.osv import osv, fields
from openerp.tools.translate import _
class MailMassMailingSegmentCreate(osv.TransientModel):
"""Wizard to help creating mass mailing segments for a campaign. """
class MailMassMailingCreate(osv.TransientModel):
"""Wizard to help creating mass mailing waves for a campaign. """
_name = 'mail.mass_mailing.segment.create'
_description = 'Mass mailing segment creation'
_name = 'mail.mass_mailing.create'
_description = 'Mass mailing creation'
_columns = {
'mass_mailing_campaign_id': fields.many2one(
@ -55,11 +55,11 @@ class MailMassMailingSegmentCreate(osv.TransientModel):
'email.template', 'Template', required=True,
domain="[('model_id', '=', model_id)]",
),
'segment_name': fields.char(
'Segment name', required=True,
'name': fields.char(
'Name', required=True,
),
'mass_mailing_segment_id': fields.many2one(
'mail.mass_mailing.segment', 'Mass Mailing Segment',
'mass_mailing_id': fields.many2one(
'mail.mass_mailing', 'Mass Mailing',
),
}
@ -80,23 +80,23 @@ class MailMassMailingSegmentCreate(osv.TransientModel):
domain = False
return {'value': {'domain': domain}}
def create_segment(self, cr, uid, ids, context=None):
""" Create a segment based on wizard data, and update the wizard """
def create_mass_mailing(self, cr, uid, ids, context=None):
""" Create a mass mailing based on wizard data, and update the wizard """
for wizard in self.browse(cr, uid, ids, context=context):
segment_values = {
'name': wizard.segment_name,
mass_mailing_values = {
'name': wizard.name,
'mass_mailing_campaign_id': wizard.mass_mailing_campaign_id.id,
'domain': wizard.domain,
'template_id': wizard.template_id.id,
}
segment_id = self.pool['mail.mass_mailing.segment'].create(cr, uid, segment_values, context=context)
self.write(cr, uid, [wizard.id], {'mass_mailing_segment_id': segment_id}, context=context)
mass_mailing_id = self.pool['mail.mass_mailing'].create(cr, uid, mass_mailing_values, context=context)
self.write(cr, uid, [wizard.id], {'mass_mailing_id': mass_mailing_id}, context=context)
return True
def launch_composer(self, cr, uid, ids, context=None):
""" Main wizard action: create a new segment and launch the mail.compose.message
""" Main wizard action: create a new mailing and launch the mail.compose.message
email composer with wizard data. """
self.create_segment(cr, uid, ids, context=context)
self.create_mass_mailing(cr, uid, ids, context=context)
wizard = self.browse(cr, uid, ids[0], context=context)
ctx = dict(context)
@ -107,7 +107,7 @@ class MailMassMailingSegmentCreate(osv.TransientModel):
'default_use_active_domain': True,
'default_active_domain': wizard.domain,
'default_mass_mailing_campaign_id': wizard.mass_mailing_campaign_id.id,
'default_mass_mailing_segment_id': wizard.mass_mailing_segment_id.id,
'default_mass_mailing_id': wizard.mass_mailing_id.id,
})
return {
'name': _('Compose Email for Mass Mailing'),

View File

@ -3,17 +3,17 @@
<data>
<!-- Wizard form view -->
<record model="ir.ui.view" id="view_mail_mass_mailing_segment_create_form">
<field name="name">mail.mass_mailing.segment.create.form</field>
<field name="model">mail.mass_mailing.segment.create</field>
<record model="ir.ui.view" id="view_mail_mass_mailing_create_form">
<field name="name">mail.mass_mailing.create.form</field>
<field name="model">mail.mass_mailing.create</field>
<field name="arch" type="xml">
<form string="Create a Mass Mailing Segment" version="7.0">
<form string="Create a Mass Mailing" version="7.0">
<group>
<field name="model_model" invisible="1"/>
<field name="domain" invisible="1"/>
<p class="oe_grey" colspan="2"
attrs="{'invisible': [('mass_mailing_campaign_id', '!=', False)]}">
Please choose a mass mailing campaign that will hold the new segment.
Please choose a mass mailing campaign that will hold the new mailing.
</p>
<field name="mass_mailing_campaign_id"/>
@ -43,23 +43,23 @@
attrs="{'invisible': [('filter_id', '=', False)]}"/>
<p class="oe_grey" colspan="2"
attrs="{'invisible': ['|', ('segment_name', '!=', False), ('template_id', '=', False)]}">
Please choose the name of the campaign segment.
attrs="{'invisible': ['|', ('name', '!=', False), ('template_id', '=', False)]}">
Please choose the name of the mailing.
</p>
<field name="segment_name"
<field name="name"
attrs="{'invisible': [('template_id', '=', False)]}"/>
<button name="launch_composer" type="object"
string="Create segment and launch email composer"
attrs="{'invisible': [('segment_name', '=', False)]}"/>
string="Create mailing and launch email composer"
attrs="{'invisible': [('name', '=', False)]}"/>
</group>
</form>
</field>
</record>
<record model="ir.actions.act_window" id="action_mail_mass_mailing_segment_create">
<field name="name">Create Mass Mailing Segment</field>
<field name="res_model">mail.mass_mailing.segment.create</field>
<record model="ir.actions.act_window" id="action_mail_mass_mailing_create">
<field name="name">Create Mass Mailing</field>
<field name="res_model">mail.mass_mailing.create</field>
<field name="src_model">mail.mass_mailing.campaign</field>
<field name="type">ir.actions.act_window</field>
<field name="view_type">form</field>