1. The merge of the "email_template" module into the "mail" module. 2. The send action of the mass mailing has been moved from the frontend to a cron, because it was too slow to send over 10,000 mails (the user's browser was blocked for 15 - 20 minutes). Mass mailings have now their own process in the kanban view. 3. Mails sent from the mail form are sent immediatly instead of from the mail queue (for instance, when you go to sales > customers > list view > select 2 -3 customers > More > Partner Mass Mailing). 4. Users have now the choice from which mailing list they want to unsubscribe when they click on the unsubscribe link at the bottom of the mail. 5. Mass mailings inherit from their campaign UTMs and mass mailing campaigns are linked to an UTM campaign. 6. Many little improvements
130 lines
6.3 KiB
Python
130 lines
6.3 KiB
Python
# -*- 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 openerp
|
|
from openerp.osv import fields, osv
|
|
from openerp import models, api, _
|
|
|
|
|
|
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'
|
|
|
|
def _compute_state(self, cr, uid, stat_ids, field_names, arg, context=None):
|
|
stats = self.browse(cr, uid, stat_ids, context=context)
|
|
res = dict([(stat, False) for stat in stat_ids])
|
|
|
|
self.write(cr, uid, stat_ids, {'state_update': fields.datetime.now()}, context=context)
|
|
|
|
for stat in stats:
|
|
if stat.exception:
|
|
res[stat.id] = 'exception'
|
|
if stat.sent:
|
|
res[stat.id] = 'sent'
|
|
if stat.opened:
|
|
res[stat.id] = 'opened'
|
|
if stat.replied:
|
|
res[stat.id] = 'replied'
|
|
if stat.bounced:
|
|
res[stat.id] = 'bounced'
|
|
|
|
return res
|
|
|
|
_columns = {
|
|
'mail_mail_id': fields.many2one('mail.mail', 'Mail', ondelete='set null'),
|
|
'mail_mail_id_int': fields.integer(
|
|
'Mail ID (tech)',
|
|
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.'
|
|
'However the ID is needed for several action and controllers.'
|
|
),
|
|
'message_id': fields.char('Message-ID'),
|
|
'model': fields.char('Document model'),
|
|
'res_id': fields.integer('Document ID'),
|
|
# 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,
|
|
),
|
|
# Bounce and tracking
|
|
'scheduled': fields.datetime('Scheduled', help='Date when the email has been created'),
|
|
'sent': fields.datetime('Sent', help='Date when the email has been sent'),
|
|
'exception': fields.datetime('Exception', help='Date of technical error leading to the email not being sent'),
|
|
'opened': fields.datetime('Opened', help='Date when the email has been opened 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.'),
|
|
'links_click_ids': fields.one2many('website.links.click', 'mail_stat_id', 'Links click'),
|
|
'state': fields.function(_compute_state, string='State', type="selection",
|
|
selection=[('outgoing', 'Outgoing'), ('exception', 'Exception'), ('sent', 'Sent'), ('opened', 'Opened'), ('replied', 'Replied'), ('bounced', 'Bounced')],
|
|
store={'mail.mail.statistics': (lambda self, cr, uid, ids, context=None: ids, ['exception', 'sent', 'opened', 'replied', 'bounced'], 10)}),
|
|
'state_update': fields.datetime('State Update', help='Last state update of the mail'),
|
|
}
|
|
|
|
_defaults = {
|
|
'scheduled': fields.datetime.now,
|
|
}
|
|
|
|
def create(self, cr, uid, values, context=None):
|
|
if 'mail_mail_id' in values:
|
|
values['mail_mail_id_int'] = values['mail_mail_id']
|
|
res = super(MailMailStats, self).create(cr, uid, values, context=context)
|
|
return res
|
|
|
|
def _get_ids(self, cr, uid, ids=None, mail_mail_ids=None, mail_message_ids=None, domain=None, context=None):
|
|
if not ids and mail_mail_ids:
|
|
base_domain = [('mail_mail_id_int', 'in', mail_mail_ids)]
|
|
elif not ids and mail_message_ids:
|
|
base_domain = [('message_id', 'in', mail_message_ids)]
|
|
else:
|
|
base_domain = [('id', 'in', ids or [])]
|
|
if domain:
|
|
base_domain = ['&'] + domain + base_domain
|
|
return self.search(cr, uid, base_domain, context=context)
|
|
|
|
def set_opened(self, cr, uid, ids=None, mail_mail_ids=None, mail_message_ids=None, context=None):
|
|
stat_ids = self._get_ids(cr, uid, ids, mail_mail_ids, mail_message_ids, [('opened', '=', False)], context)
|
|
self.write(cr, uid, stat_ids, {'opened': fields.datetime.now()}, context=context)
|
|
return stat_ids
|
|
|
|
def set_replied(self, cr, uid, ids=None, mail_mail_ids=None, mail_message_ids=None, context=None):
|
|
stat_ids = self._get_ids(cr, uid, ids, mail_mail_ids, mail_message_ids, [('replied', '=', False)], context)
|
|
self.write(cr, uid, stat_ids, {'replied': fields.datetime.now()}, context=context)
|
|
return stat_ids
|
|
|
|
def set_bounced(self, cr, uid, ids=None, mail_mail_ids=None, mail_message_ids=None, context=None):
|
|
stat_ids = self._get_ids(cr, uid, ids, mail_mail_ids, mail_message_ids, [('bounced', '=', False)], context)
|
|
self.write(cr, uid, stat_ids, {'bounced': fields.datetime.now()}, context=context)
|
|
return stat_ids
|