summaryrefslogtreecommitdiff
path: root/addons/mass_mailing/models/mailing_list.py
diff options
context:
space:
mode:
authorThibault Delavallée <tde@odoo.com>2019-07-17 10:58:39 +0000
committerThibault Delavallée <tde@odoo.com>2019-07-17 15:50:33 +0000
commit772e1c0cc4e75cec971d2b4e1c820eb54872dc93 (patch)
tree30126a9fa9acabb9f75ab1d0665428180b9cc444 /addons/mass_mailing/models/mailing_list.py
parent2896aad34e797b6ca65a8efe3c5d71f31500a9a2 (diff)
[REF] mass_mailing: rename mail.mass_mailing.{.contact{_rel}, list{.merge}} models
PURPOSE Mass mailing is currently a bit messy. As SMS will be added as a way to notify people in mass through SMS let us take this opportunity to somehow clean this application: organization, light code cleaning, model renaming. SPECIFICATIONS Rename mail.mass_mailing.list and mail.mass_mailing.list to mailing.list and mailing.list.merge. Rename mail.mass_mailing.contact to mailing.contact. Rename mail.mailing_list.list_contact_rel to mailing.contact.subscription. Rationale : * those new names are easier to understand: mailing.list and mailing.contact are less mail-related, especially taking into account that SMS will allow to be less mail-oriented; * those names are easier to read / find / understand; * align wizard and sub-models naming with the main naming; * have a mailing as first part of namespacing; MIGRATION mail.mass_mailing.list model -> mailing.list mail.mass_mailing.list.merge model -> mailing.list.merge mail.mass_mailing.contact model -> mailing.contact mail.mass_mailing.list_contact_rel model -> mailing.contact.subscription mail_mass_mailing_contact_list_rel table -> mailing_contact_list_rel (specific case of a decorated m2m) fields updated (no column change) * mailing.list: subscription_contact_ids -> subscription_ids LINKS Task ID 2037906 Preparing task ID 1997464 (SMS addition in mass mailing) PR #34938
Diffstat (limited to 'addons/mass_mailing/models/mailing_list.py')
-rw-r--r--addons/mass_mailing/models/mailing_list.py111
1 files changed, 111 insertions, 0 deletions
diff --git a/addons/mass_mailing/models/mailing_list.py b/addons/mass_mailing/models/mailing_list.py
new file mode 100644
index 00000000000..ad78b7ad85f
--- /dev/null
+++ b/addons/mass_mailing/models/mailing_list.py
@@ -0,0 +1,111 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models
+
+
+class MassMailingList(models.Model):
+ """Model of a contact list. """
+ _name = 'mailing.list'
+ _order = 'name'
+ _description = 'Mailing List'
+
+ name = fields.Char(string='Mailing List', required=True)
+ active = fields.Boolean(default=True)
+ contact_nbr = fields.Integer(compute="_compute_contact_nbr", string='Number of Contacts')
+ contact_ids = fields.Many2many(
+ 'mailing.contact', 'mailing_contact_list_rel', 'list_id', 'contact_id',
+ string='Mailing Lists')
+ subscription_ids = fields.One2many('mailing.contact.subscription', 'list_id',
+ string='Subscription Information')
+ is_public = fields.Boolean(default=True, help="The mailing list can be accessible by recipient in the unsubscription"
+ " page to allows him to update his subscription preferences.")
+
+ # Compute number of contacts non opt-out, non blacklisted and valid email recipient for a mailing list
+ def _compute_contact_nbr(self):
+ self.env.cr.execute('''
+ select
+ list_id, count(*)
+ from
+ mailing_contact_list_rel r
+ left join mailing_contact c on (r.contact_id=c.id)
+ left join mail_blacklist bl on c.email_normalized = bl.email and bl.active
+ where
+ list_id in %s
+ AND COALESCE(r.opt_out,FALSE) = FALSE
+ AND c.email_normalized IS NOT NULL
+ AND bl.id IS NULL
+ group by
+ list_id
+ ''', (tuple(self.ids), ))
+ data = dict(self.env.cr.fetchall())
+ for mailing_list in self:
+ mailing_list.contact_nbr = data.get(mailing_list.id, 0)
+
+ def name_get(self):
+ return [(list.id, "%s (%s)" % (list.name, list.contact_nbr)) for list in self]
+
+ def action_merge(self, src_lists, archive):
+ """
+ Insert all the contact from the mailing lists 'src_lists' to the
+ mailing list in 'self'. Possibility to archive the mailing lists
+ 'src_lists' after the merge except the destination mailing list 'self'.
+ """
+ # Explation of the SQL query with an example. There are the following lists
+ # A (id=4): yti@odoo.com; yti@example.com
+ # B (id=5): yti@odoo.com; yti@openerp.com
+ # C (id=6): nothing
+ # To merge the mailing lists A and B into C, we build the view st that looks
+ # like this with our example:
+ #
+ # contact_id | email | row_number | list_id |
+ # ------------+---------------------------+------------------------
+ # 4 | yti@odoo.com | 1 | 4 |
+ # 6 | yti@odoo.com | 2 | 5 |
+ # 5 | yti@example.com | 1 | 4 |
+ # 7 | yti@openerp.com | 1 | 5 |
+ #
+ # The row_column is kind of an occurence counter for the email address.
+ # Then we create the Many2many relation between the destination list and the contacts
+ # while avoiding to insert an existing email address (if the destination is in the source
+ # for example)
+ self.ensure_one()
+ # Put destination is sources lists if not already the case
+ src_lists |= self
+ self.env.cr.execute("""
+ INSERT INTO mailing_contact_list_rel (contact_id, list_id)
+ SELECT st.contact_id AS contact_id, %s AS list_id
+ FROM
+ (
+ SELECT
+ contact.id AS contact_id,
+ contact.email AS email,
+ list.id AS list_id,
+ row_number() OVER (PARTITION BY email ORDER BY email) AS rn
+ FROM
+ mailing_contact contact,
+ mailing_contact_list_rel contact_list_rel,
+ mailing_list list
+ WHERE contact.id=contact_list_rel.contact_id
+ AND COALESCE(contact_list_rel.opt_out,FALSE) = FALSE
+ AND contact.email_normalized NOT IN (select email from mail_blacklist where active = TRUE)
+ AND list.id=contact_list_rel.list_id
+ AND list.id IN %s
+ AND NOT EXISTS
+ (
+ SELECT 1
+ FROM
+ mailing_contact contact2,
+ mailing_contact_list_rel contact_list_rel2
+ WHERE contact2.email = contact.email
+ AND contact_list_rel2.contact_id = contact2.id
+ AND contact_list_rel2.list_id = %s
+ )
+ ) st
+ WHERE st.rn = 1;""", (self.id, tuple(src_lists.ids), self.id))
+ self.invalidate_cache()
+ if archive:
+ (src_lists - self).write({'active': False})
+
+ def close_dialog(self):
+ return {'type': 'ir.actions.act_window_close'}