From 8bbb965649ff46811d8ba14e0305f732b898653e Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Tue, 23 Jan 2018 13:36:17 +0100 Subject: [PATCH 1/4] [ADD] mass_mailing_resend (#210) * [ADD] mass_mailing_resend Resend mass mailings ==================== A frequent need for users of mass mailings is to resend one mailing that has already been sent in the past to new recipients that haven't received yet that mail. But the problem is to know which are the applicable ones. Odoo already includes a method in its mass mailing logic that avoids to resend the same mail 2 times for one mass mailing, and for v9, there was a trick that allows to modify the state of a mass mailing from kanban view, covering the need. But now on v10 both status bar in form view and dragging between states in kanban are not allowed. This module introduces a button to restart a mass mailing to draft state, allowing you to reevaluate the sending domain or list for performing again the mailing. Usage ===== * Go to *Mass mailing > Mailings > Mass Mailings*. * Click on one record that is done or create a new one and send it. * You will see a button called "Resend". * If you click on it, mass mailing will be set to Draft again. Known issues / Roadmap ====================== * Add an indicator / filter for knowing resent mailings. * Include information on the number of new recipients to be sent on the resending (through `get_remaining_recipients` method). --- mass_mailing_resend/README.rst | 75 ++++++++++++++++ mass_mailing_resend/__init__.py | 4 + mass_mailing_resend/__manifest__.py | 21 +++++ mass_mailing_resend/i18n/es.po | 38 ++++++++ mass_mailing_resend/models/__init__.py | 4 + mass_mailing_resend/models/mass_mailing.py | 19 ++++ .../static/description/icon.png | Bin 0 -> 5936 bytes .../static/description/icon.svg | 84 ++++++++++++++++++ mass_mailing_resend/tests/__init__.py | 4 + .../tests/test_mass_mailing_resend.py | 36 ++++++++ .../views/mass_mailing_views.xml | 28 ++++++ 11 files changed, 313 insertions(+) create mode 100644 mass_mailing_resend/README.rst create mode 100644 mass_mailing_resend/__init__.py create mode 100644 mass_mailing_resend/__manifest__.py create mode 100644 mass_mailing_resend/i18n/es.po create mode 100644 mass_mailing_resend/models/__init__.py create mode 100644 mass_mailing_resend/models/mass_mailing.py create mode 100644 mass_mailing_resend/static/description/icon.png create mode 100644 mass_mailing_resend/static/description/icon.svg create mode 100644 mass_mailing_resend/tests/__init__.py create mode 100644 mass_mailing_resend/tests/test_mass_mailing_resend.py create mode 100644 mass_mailing_resend/views/mass_mailing_views.xml diff --git a/mass_mailing_resend/README.rst b/mass_mailing_resend/README.rst new file mode 100644 index 00000000..84deb127 --- /dev/null +++ b/mass_mailing_resend/README.rst @@ -0,0 +1,75 @@ +.. 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 + +==================== +Resend mass mailings +==================== + +A frequent need for users of mass mailings is to resend one mailing that has +already been sent in the past to new recipients that haven't received yet that +mail. But the problem is to know which are the applicable ones. + +Odoo already includes a method in its mass mailing logic that avoids to resend +the same mail 2 times for one mass mailing, and for v9, there was a trick that +allows to modify the state of a mass mailing from kanban view, covering the +need. + +But now on v10 both status bar in form view and dragging between states in +kanban are not allowed. + +This module introduces a button to restart a mass mailing to draft state, +allowing you to reevaluate the sending domain or list for performing again +the mailing. + +Usage +===== + +* Go to *Mass mailing > Mailings > Mass Mailings*. +* Click on one record that is done or create a new one and send it. +* You will see a button called "Resend". +* If you click on it, mass mailing will be set to Draft again. + +.. 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/10.0 + +Bug Tracker +=========== + +Bugs are tracked on `GitHub 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. + +Known issues / Roadmap +====================== + +* Add an indicator / filter for knowing resent mailings. +* Include information on the number of new recipients to be sent on the + resending (through `get_remaining_recipients` method). + + +Credits +======= + +Contributors +------------ + +* Tecnativa (https://www.tecnativa.com): + * Pedro M. Baeza + +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. diff --git a/mass_mailing_resend/__init__.py b/mass_mailing_resend/__init__.py new file mode 100644 index 00000000..a77a6fcb --- /dev/null +++ b/mass_mailing_resend/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import models diff --git a/mass_mailing_resend/__manifest__.py b/mass_mailing_resend/__manifest__.py new file mode 100644 index 00000000..9e7260b6 --- /dev/null +++ b/mass_mailing_resend/__manifest__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + "name": "Resend mass mailings", + "version": "10.0.1.0.0", + "category": "Marketing", + "website": "https://github.com/OCA/social", + "author": "Tecnativa, " + "Odoo Community Association (OCA)", + "license": "AGPL-3", + "application": False, + "installable": True, + "depends": [ + "mass_mailing", + ], + "data": [ + "views/mass_mailing_views.xml", + ], +} diff --git a/mass_mailing_resend/i18n/es.po b/mass_mailing_resend/i18n/es.po new file mode 100644 index 00000000..cdb4db74 --- /dev/null +++ b/mass_mailing_resend/i18n/es.po @@ -0,0 +1,38 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mass_mailing_resend +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-11-22 10:42+0000\n" +"PO-Revision-Date: 2017-11-22 10:42+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_resend +#: model:ir.ui.view,arch_db:mass_mailing_resend.view_mail_mass_mailing_form +msgid " New sending will be done only to not sent/new recipients. If you want to resend again the mass mailing to already sent recipients, click on Emails Sent smart-button for removing the existing record(s)." +msgstr " El nuevo envío se realizará solo a los destinatarios no enviados/nuevos. Si quiere reenviar otra vez el correo masivo a destinatarios ya enviados, pulse en el botón Correos enviados para eliminar el/los registro/s existentes." + +#. module: mass_mailing_resend +#: model:ir.model,name:mass_mailing_resend.model_mail_mass_mailing +msgid "Mass Mailing" +msgstr "Correo masivo" + +#. module: mass_mailing_resend +#: model:ir.ui.view,arch_db:mass_mailing_resend.view_mail_mass_mailing_form +msgid "Resend" +msgstr "Reenviar" + +#. module: mass_mailing_resend +#: code:addons/mass_mailing_resend/models/mass_mailing.py:16 +#, python-format +msgid "You can't resend a mass mailing that is being sent or in draft state." +msgstr "No puede reenviar un correo masivo que está siendo enviado en estado borrador." + diff --git a/mass_mailing_resend/models/__init__.py b/mass_mailing_resend/models/__init__.py new file mode 100644 index 00000000..d06de603 --- /dev/null +++ b/mass_mailing_resend/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import mass_mailing diff --git a/mass_mailing_resend/models/mass_mailing.py b/mass_mailing_resend/models/mass_mailing.py new file mode 100644 index 00000000..ade99070 --- /dev/null +++ b/mass_mailing_resend/models/mass_mailing.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + + +from odoo import _, exceptions, models + + +class MailMassMailingList(models.Model): + _inherit = "mail.mass_mailing" + + def button_draft(self): + """Return to draft state for resending the mass mailing.""" + if any(self.mapped(lambda x: x.state != 'done')): + raise exceptions.UserError( + _("You can't resend a mass mailing that is being sent or in " + "draft state.") + ) + self.write({'state': 'draft'}) diff --git a/mass_mailing_resend/static/description/icon.png b/mass_mailing_resend/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7828a2dc94f7b33fff3a60f076ac39b9f34b8684 GIT binary patch literal 5936 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 + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/mass_mailing_resend/tests/__init__.py b/mass_mailing_resend/tests/__init__.py new file mode 100644 index 00000000..efeb8426 --- /dev/null +++ b/mass_mailing_resend/tests/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import test_mass_mailing_resend diff --git a/mass_mailing_resend/tests/test_mass_mailing_resend.py b/mass_mailing_resend/tests/test_mass_mailing_resend.py new file mode 100644 index 00000000..41c25ee9 --- /dev/null +++ b/mass_mailing_resend/tests/test_mass_mailing_resend.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.tests import common +from odoo import exceptions + + +class TestMassMailingResend(common.SavepointCase): + @classmethod + def setUpClass(cls): + super(TestMassMailingResend, cls).setUpClass() + cls.list = cls.env['mail.mass_mailing.list'].create({ + 'name': 'Test list', + }) + cls.contact1 = cls.env['mail.mass_mailing.contact'].create({ + 'name': 'Contact 1', + 'email': 'email1@test.com', + }) + cls.mass_mailing = cls.env['mail.mass_mailing'].create({ + 'name': 'Test mass mailing', + 'email_from': 'test@example.org', + 'mailing_model': 'mail.mass_mailing.contact', + 'contact_list_ids': [(6, 0, cls.list.ids)], + 'reply_to_mode': 'thread', + }) + + def test_resend_error(self): + with self.assertRaises(exceptions.UserError): + self.mass_mailing.button_draft() + + def test_resend(self): + self.mass_mailing.state = 'done' # Force state + self.assertEqual(self.mass_mailing.state, 'done') + self.mass_mailing.button_draft() + self.assertEqual(self.mass_mailing.state, 'draft') diff --git a/mass_mailing_resend/views/mass_mailing_views.xml b/mass_mailing_resend/views/mass_mailing_views.xml new file mode 100644 index 00000000..e25d1997 --- /dev/null +++ b/mass_mailing_resend/views/mass_mailing_views.xml @@ -0,0 +1,28 @@ + + + + + mail.mass_mailing + + + +