mass_mailing_event (#127)

pull/563/head
Antonio Espinosa 2016-11-09 19:23:30 +01:00 committed by Pedro M. Baeza
parent cd3f645eea
commit 3673d4c488
16 changed files with 521 additions and 0 deletions

View File

@ -0,0 +1,72 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
==================
Mass mailing event
==================
This module links ``mass_mailing`` with ``event`` in order to exclude
recipients that are already registered, confirmed, cancelled, attended, or a
combination of these states, when the mass mailing is sent.
Usage
=====
In a mass mailing, users can set an event related and exclude the recipients who
have an email address already registered in that event.
This is useful in this scenario:
1. Create a mass mailing for telling to 1000 partners that a new event is
available.
2. During a week some of them (50) have been registered in the event
3. Then, a week after the first mass mailing, duplicate it to get a second
mass mailing. Relate this one to the event and exclude the registered emails.
Change the message body to remember that early bird period is going to expire
soon.
4. Send the second mass mailing and registered emails are automatically excluded,
So it's been only sent to 950 partners the ones who are not registered in the
event yet.
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/205/8.0
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/social/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smashing it by providing a detailed and welcomed feedback.
Credits
=======
Images
------
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
Contributors
------------
* Antonio Espinosa <antonio.espinosa@tecnativa.com>
Maintainer
----------
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
To contribute to this module, please visit https://odoo-community.org.

View File

@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import models

View File

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "Mass mailing event",
"summary": "Link mass mailing with event for excluding recipients",
"version": "8.0.1.0.0",
"category": "Marketing",
"website": "https://odoo-community.org/",
"author": "Tecnativa, Odoo Community Association (OCA)",
"license": "AGPL-3",
"application": False,
"installable": True,
"depends": [
"mass_mailing",
"event",
],
"data": [
"security/ir.model.access.csv",
"data/event_state_data.xml",
"views/mass_mailing_view.xml",
],
}

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<openerp>
<data>
<record id="event_draft" model="event.registration.state">
<field name="name">Unconfirmed</field>
<field name="code">draft</field>
</record>
<record id="event_cancel" model="event.registration.state">
<field name="name">Cancelled</field>
<field name="code">cancel</field>
</record>
<record id="event_open" model="event.registration.state">
<field name="name">Confirmed</field>
<field name="code">open</field>
</record>
<record id="event_done" model="event.registration.state">
<field name="name">Attended</field>
<field name="code">done</field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,96 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mass_mailing_event
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-11-07 11:16+0000\n"
"PO-Revision-Date: 2016-11-07 11:16+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: mass_mailing_event
#: field:event.registration.state,code:0
msgid "Code"
msgstr "Código"
#. module: mass_mailing_event
#: field:event.registration.state,create_uid:0
msgid "Created by"
msgstr "Creado por"
#. module: mass_mailing_event
#: field:event.registration.state,create_date:0
msgid "Created on"
msgstr "Creado en"
#. module: mass_mailing_event
#: field:event.registration.state,display_name:0
msgid "Display Name"
msgstr "Nombre mostrado"
#. module: mass_mailing_event
#: model:ir.model,name:mass_mailing_event.model_event_registration
msgid "Event Registration"
msgstr "Registro de evento"
#. module: mass_mailing_event
#: field:mail.mass_mailing,event_id:0
msgid "Event related"
msgstr "Evento relacionado"
#. module: mass_mailing_event
#: field:mail.mass_mailing,exclude_event_state_ids:0
msgid "Exclude"
msgstr "Excluir"
#. module: mass_mailing_event
#: field:event.registration.state,id:0
msgid "ID"
msgstr "ID"
#. module: mass_mailing_event
#: field:event.registration.state,__last_update:0
msgid "Last Modified on"
msgstr "Última modificación en"
#. module: mass_mailing_event
#: field:event.registration.state,write_uid:0
msgid "Last Updated by"
msgstr "Última actualización por"
#. module: mass_mailing_event
#: field:event.registration.state,write_date:0
msgid "Last Updated on"
msgstr "Última actualización en"
#. module: mass_mailing_event
#: model:ir.model,name:mass_mailing_event.model_mail_mass_mailing
msgid "Mass Mailing"
msgstr "Envío masivo"
#. module: mass_mailing_event
#: model:ir.model,name:mass_mailing_event.model_mail_mass_mailing_contact
msgid "Mass Mailing Contact"
msgstr "Contacto"
#. module: mass_mailing_event
#: field:event.registration.state,name:0
msgid "Name"
msgstr "Nombre"
#. module: mass_mailing_event
#: model:ir.model,name:mass_mailing_event.model_res_partner
msgid "Partner"
msgstr "Empresa"
#. module: mass_mailing_event
#: view:mail.mass_mailing:mass_mailing_event.view_mail_mass_mailing_form
msgid "{'search_default_not_opt_out': 1, 'exclude_mass_mailing': active_id}"
msgstr "{'search_default_not_opt_out': 1, 'exclude_mass_mailing': active_id}"

View File

@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import event_registration_state
from . import mail_mass_mailing
from . import mail_mass_mailing_contact
from . import res_partner
from . import event_registration

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import models, api
from .mail_mass_mailing import event_filtered_ids
class EventRegistration(models.Model):
_inherit = 'event.registration'
@api.model
def search_count(self, domain):
res = super(EventRegistration, self).search_count(domain)
mass_mailing_id = self.env.context.get('exclude_mass_mailing', False)
if mass_mailing_id:
res_ids = event_filtered_ids(
self, mass_mailing_id, domain, field='email')
return len(res_ids) if res_ids else 0
return res

View File

@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import models, fields
class EventRegistrationState(models.Model):
_name = 'event.registration.state'
name = fields.Char(required=True)
code = fields.Char(required=True)

View File

@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import copy
from openerp import models, fields, api
def event_filtered_ids(model, mass_mailing_id, domain, field='email'):
field = field or 'email'
domain = domain or []
exclude_emails = []
mass_mailing = model.env['mail.mass_mailing'].browse(mass_mailing_id)
if mass_mailing.event_id:
exclude = mass_mailing.exclude_event_state_ids.mapped('code')
reg_domain = False
registrations = model.env['event.registration']
if exclude:
reg_domain = [
('event_id', '=', mass_mailing.event_id.id),
('state', 'in', exclude)
]
if reg_domain:
registrations = registrations.search(reg_domain)
if registrations:
exclude_emails = registrations.mapped('email')
apply_domain = copy.deepcopy(domain)
if exclude_emails:
apply_domain.append((field, 'not in', exclude_emails))
rows = model.search(apply_domain)
return rows.ids
class MailMassMailing(models.Model):
_inherit = 'mail.mass_mailing'
def _default_exclude_event_state_ids(self):
return self.env['event.registration.state'].search([])
event_id = fields.Many2one(
string="Event related", comodel_name='event.event')
exclude_event_state_ids = fields.Many2many(
comodel_name='event.registration.state',
string="Exclude", default=_default_exclude_event_state_ids)
@api.model
def get_recipients(self, mailing):
res_ids = super(MailMassMailing, self).get_recipients(mailing)
if res_ids:
domain = [('id', 'in', res_ids)]
return event_filtered_ids(
self.env[mailing.mailing_model], mailing.id, domain,
field='email')
return res_ids

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import models, api
from .mail_mass_mailing import event_filtered_ids
class MailMassMailingContact(models.Model):
_inherit = 'mail.mass_mailing.contact'
@api.model
def search_count(self, domain):
res = super(MailMassMailingContact, self).search_count(domain)
mass_mailing_id = self.env.context.get('exclude_mass_mailing', False)
if mass_mailing_id:
res_ids = event_filtered_ids(
self, mass_mailing_id, domain, field='email')
return len(res_ids) if res_ids else 0
return res

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import models, api
from .mail_mass_mailing import event_filtered_ids
class ResPartner(models.Model):
_inherit = 'res.partner'
@api.model
def search_count(self, domain):
res = super(ResPartner, self).search_count(domain)
mass_mailing_id = self.env.context.get('exclude_mass_mailing', False)
if mass_mailing_id:
res_ids = event_filtered_ids(
self, mass_mailing_id, domain, field='email')
return len(res_ids) if res_ids else 0
return res

View File

@ -0,0 +1,2 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_event_registration_state_group_user","event_registration_state group_user","model_event_registration_state","base.group_user",1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_event_registration_state_group_user event_registration_state group_user model_event_registration_state base.group_user 1 0 0 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import test_mass_mailing_event

View File

@ -0,0 +1,123 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from datetime import datetime, timedelta
from openerp.tests.common import TransactionCase
class TestMassMailingEvent(TransactionCase):
def setUp(self, *args, **kwargs):
super(TestMassMailingEvent, self).setUp(*args, **kwargs)
day_1 = (datetime.now() + timedelta(days=1)).strftime(
'%Y-%m-%d 8:00:00')
day_2 = (datetime.now() + timedelta(days=5)).strftime(
'%Y-%m-%d 18:00:00')
self.event = self.env['event.event'].create({
'name': 'Test event',
'date_begin': day_1,
'date_end': day_2,
})
self.registration = self.env['event.registration'].create({
'event_id': self.event.id,
'email': 'partner_a@example.org',
'nb_register': 1,
'state': 'draft',
})
self.states_all = self.env['event.registration.state'].search([])
self.state_confirmed = self.env['event.registration.state'].search([
('code', '=', 'open'),
])
def test_mailing_contact(self):
contact_list = self.env['mail.mass_mailing.list'].create({
'name': 'Test list',
})
contact_a = self.env['mail.mass_mailing.contact'].create({
'list_id': contact_list.id,
'name': 'Test contact A',
'email': 'partner_a@example.org',
})
contact_b = self.env['mail.mass_mailing.contact'].create({
'list_id': contact_list.id,
'name': 'Test contact B',
'email': 'partner_b@example.org',
})
domain = [
('list_id', 'in', [contact_list.id]),
('opt_out', '=', False),
]
mass_mailing = self.env['mail.mass_mailing'].create({
'name': 'Test subject',
'email_from': 'from@example.com',
'mailing_model': 'mail.mass_mailing.contact',
'mailing_domain': str(domain),
'contact_list_ids': [(6, False, [contact_list.id])],
'body_html': '<p>Test email body</p>',
'reply_to_mode': 'email',
})
m_contact = self.env['mail.mass_mailing.contact'].with_context(
exclude_mass_mailing=mass_mailing.id)
self.assertEqual(
[contact_a.id, contact_b.id],
mass_mailing.get_recipients(mass_mailing))
self.assertEqual(2, m_contact.search_count(domain))
mass_mailing.write({
'event_id': self.event.id,
'exclude_event_state_ids': [(6, False, self.states_all.ids)],
})
self.assertEqual(
[contact_b.id],
mass_mailing.get_recipients(mass_mailing))
self.assertEqual(1, m_contact.search_count(domain))
mass_mailing.write({
'exclude_event_state_ids': [(6, False, self.state_confirmed.ids)],
})
self.assertEqual(
[contact_a.id, contact_b.id],
mass_mailing.get_recipients(mass_mailing))
self.assertEqual(2, m_contact.search_count(domain))
def test_mailing_partner(self):
partner_a = self.env['res.partner'].create({
'name': 'Test partner A',
'email': 'partner_a@example.org',
})
partner_b = self.env['res.partner'].create({
'name': 'Test partner B',
'email': 'partner_b@example.org',
})
domain = [
('id', 'in', [partner_a.id, partner_b.id]),
('opt_out', '=', False),
]
mass_mailing = self.env['mail.mass_mailing'].create({
'name': 'Test subject',
'email_from': 'from@example.com',
'mailing_model': 'res.partner',
'mailing_domain': str(domain),
'body_html': '<p>Test email body</p>',
'reply_to_mode': 'email',
})
m_partner = self.env['res.partner'].with_context(
exclude_mass_mailing=mass_mailing.id)
self.assertEqual(
[partner_a.id, partner_b.id],
mass_mailing.get_recipients(mass_mailing))
self.assertEqual(2, m_partner.search_count(domain))
mass_mailing.write({
'event_id': self.event.id,
'exclude_event_state_ids': [(6, False, self.states_all.ids)],
})
self.assertEqual(
[partner_b.id],
mass_mailing.get_recipients(mass_mailing))
self.assertEqual(1, m_partner.search_count(domain))
mass_mailing.write({
'exclude_event_state_ids': [(6, False, self.state_confirmed.ids)],
})
self.assertEqual(
[partner_a.id, partner_b.id],
mass_mailing.get_recipients(mass_mailing))
self.assertEqual(2, m_partner.search_count(domain))

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<openerp>
<data>
<record model="ir.ui.view" id="view_mail_mass_mailing_form">
<field name="name">Add event and exclude</field>
<field name="model">mail.mass_mailing</field>
<field name="inherit_id" ref="mass_mailing.view_mail_mass_mailing_form"/>
<field name="arch" type="xml">
<field name="mailing_domain" position="attributes">
<attribute name="context">{'search_default_not_opt_out': 1, 'exclude_mass_mailing': active_id}</attribute>
</field>
<notebook position="before">
<group>
<group>
<field name="event_id"/>
</group>
<group>
<field name="exclude_event_state_ids"
widget="many2many_tags"
options="{'no_create_edit': True}"
attrs="{'invisible': [('event_id', '=', False)]}"/>
</group>
</group>
</notebook>
</field>
</record>
</data>
</openerp>