PURPOSE
SMS are a powerful marketing tool. For instance it is perfect to announce a
sale or to communicate a coupon code, to welcome a new customer in a fidelity
program, ...
Purpose of this task is to integrate SMS sending in batch in mass mailing. It
will use same mailing objects but sending SMS instead of emails. Some metrics
and flows will have to be slightly updated at the same time.
SPECIFICATIONS
Prepare mass mailing module to addition of mass_mailing_sms.
Mailing model: mailing type
* add a mailing_type selection field;
* mass mailing contains only 'mail';
* synchronize medium accordingly;
* update actions of mass mailing application to add a domain on mailing
type being 'mail';
Mailing contact
* remove is_email_valid field as its purpose is achieved by email_normalized
field coming from address mixin (added by blacklist management);
Mailing contact subscription
* clean a bit fields and views as this should stay a technical model;
Trace model and report: trace type
* add a trace_type selection field;
* mass mailing contains only 'mail';
Various
* clean some bits of code in views, remove old code bits;
* add anchors to ease view inheritance to be able to customize views for
SMS mailings;
* ensure all views in mass mailing filter content on mail type (mailing
and traces being type mail only);
* rename some methods to be more updated with current guidelines, notably
main action methods;
LINKS
Task 1997464
PR #34424
Original SMS addition: Task 1922163 (4287481)
103 lines
5.4 KiB
Python
103 lines
5.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo import api, fields, models
|
|
|
|
|
|
class UtmCampaign(models.Model):
|
|
_inherit = 'utm.campaign'
|
|
|
|
mailing_ids = fields.One2many(
|
|
'mailing.mailing', 'campaign_id',
|
|
string='Mass Mailings')
|
|
mailing_clicks_ratio = fields.Integer(default=0, compute="_compute_mailing_clicks_ratio", string="Number of clicks")
|
|
mailing_items = fields.Integer(compute="_compute_mailing_items", string='Mailings')
|
|
mailing_clicked = fields.Integer(compute="_compute_mailing_items", string='Mailings Clicked')
|
|
# stat fields
|
|
total = fields.Integer(compute="_compute_statistics")
|
|
scheduled = fields.Integer(compute="_compute_statistics")
|
|
failed = fields.Integer(compute="_compute_statistics")
|
|
ignored = fields.Integer(compute="_compute_statistics")
|
|
sent = fields.Integer(compute="_compute_statistics", string="Sent Emails")
|
|
delivered = fields.Integer(compute="_compute_statistics")
|
|
opened = fields.Integer(compute="_compute_statistics")
|
|
replied = fields.Integer(compute="_compute_statistics")
|
|
bounced = fields.Integer(compute="_compute_statistics")
|
|
received_ratio = fields.Integer(compute="_compute_statistics", string='Received Ratio')
|
|
opened_ratio = fields.Integer(compute="_compute_statistics", string='Opened Ratio')
|
|
replied_ratio = fields.Integer(compute="_compute_statistics", string='Replied Ratio')
|
|
bounced_ratio = fields.Integer(compute="_compute_statistics", string='Bounced Ratio')
|
|
|
|
@api.depends('mailing_items', 'mailing_clicked')
|
|
def _compute_items_total(self):
|
|
for campaign in self:
|
|
campaign.items_total += campaign.mailing_items
|
|
campaign.clicked_total += campaign.mailing_clicked
|
|
|
|
def _compute_mailing_items(self):
|
|
super(UtmCampaign, self)._compute_clicks_ratio()
|
|
query = """SELECT trace.campaign_id AS campaign_id, COUNT(DISTINCT(trace.id)) AS items_total, COUNT(DISTINCT(click.mailing_trace_id)) AS clicked_total
|
|
FROM mailing_trace AS trace
|
|
LEFT OUTER JOIN link_tracker_click as click ON click.mailing_trace_id = trace.id
|
|
WHERE trace.campaign_id IN %s
|
|
GROUP BY trace.campaign_id """
|
|
params = [tuple(self.ids)]
|
|
self.env.cr.execute(query, params)
|
|
clicked_data = self.env.cr.dictfetchall()
|
|
mapped_data = {datum['campaign_id']: {'clicked_total': datum['clicked_total'], 'items_total': datum['items_total']} for datum in clicked_data}
|
|
|
|
for campaign in self:
|
|
campaign_items_values = mapped_data.get(campaign.id, {})
|
|
campaign.mailing_items = campaign_items_values.get('items_total', 0)
|
|
campaign.mailing_clicked = campaign_items_values.get('clicked_total', 0)
|
|
|
|
@api.depends('mailing_items', 'mailing_clicked')
|
|
def _compute_mailing_clicks_ratio(self):
|
|
for campaign in self:
|
|
campaign.mailing_clicks_ratio = campaign.mailing_clicked / campaign.mailing_items * 100 if campaign.mailing_items > 0 else 0
|
|
|
|
def _compute_statistics(self):
|
|
""" Compute statistics of the mass mailing campaign """
|
|
self.env.cr.execute("""
|
|
SELECT
|
|
c.id as campaign_id,
|
|
COUNT(s.id) AS total,
|
|
COUNT(CASE WHEN s.sent is not null THEN 1 ELSE null END) AS sent,
|
|
COUNT(CASE WHEN s.scheduled is not null AND s.sent is null AND s.exception is null AND s.ignored is null THEN 1 ELSE null END) AS scheduled,
|
|
COUNT(CASE WHEN s.scheduled is not null AND s.sent is null AND s.exception is not null THEN 1 ELSE null END) AS failed,
|
|
COUNT(CASE WHEN s.scheduled is not null AND s.sent is null AND s.exception is null AND s.ignored is not null THEN 1 ELSE null END) AS ignored,
|
|
COUNT(CASE WHEN s.id is not null AND s.bounced is null THEN 1 ELSE null END) AS delivered,
|
|
COUNT(CASE WHEN s.opened is not null THEN 1 ELSE null END) AS opened,
|
|
COUNT(CASE WHEN s.replied is not null THEN 1 ELSE null END) AS replied ,
|
|
COUNT(CASE WHEN s.bounced is not null THEN 1 ELSE null END) AS bounced
|
|
FROM
|
|
mailing_trace s
|
|
RIGHT JOIN
|
|
utm_campaign c
|
|
ON (c.id = s.campaign_id)
|
|
WHERE
|
|
c.id IN %s
|
|
GROUP BY
|
|
c.id
|
|
""", (tuple(self.ids), ))
|
|
|
|
for row in self.env.cr.dictfetchall():
|
|
total = (row['total'] - row['ignored']) or 1
|
|
row['delivered'] = row['sent'] - row['bounced']
|
|
row['received_ratio'] = 100.0 * row['delivered'] / total
|
|
row['opened_ratio'] = 100.0 * row['opened'] / total
|
|
row['replied_ratio'] = 100.0 * row['replied'] / total
|
|
row['bounced_ratio'] = 100.0 * row['bounced'] / total
|
|
self.browse(row.pop('campaign_id')).update(row)
|
|
|
|
def _get_mailing_recipients(self, model=None):
|
|
"""Return the recipients of a mailing campaign. This is based on the statistics
|
|
build for each mailing. """
|
|
res = dict.fromkeys(self.ids, {})
|
|
for campaign in self:
|
|
domain = [('campaign_id', '=', campaign.id)]
|
|
if model:
|
|
domain += [('model', '=', model)]
|
|
res[campaign.id] = set(self.env['mailing.trace'].search(domain).mapped('res_id'))
|
|
return res
|