Browse Source

[FIX+IMP] mass_mailing_list_dynamic: tests, icons, filters... (#211)

* [FIX+IMP] mass_mailing_list_dynamic: tests, icons, filters...

* Brand new icon
* Added feature of loading an existing filter as criteria
* Tests as SavepointCase for optimizing times
* Tests in post-install for avoiding errors on res.partner not null constraints
  when several modules added them.
* Updated documentation.
* Fix mock in test for not commiting test data.

* [FIX] mass_mailing_list_dynamic: Wasn't able to create contacts in fully synced lists

Syncing context was being set in the wrong object. Added to test too.

* [FIX] mass_mailing_list_dynamic: Allow to write back vals from res.partner

Module mass_mailing_partner writes back certain values from partner to
mass_mailing_contact. Module should allow that write operation.
pull/284/head
Pedro M. Baeza 7 years ago
parent
commit
d8fc0aff1d
  1. 24
      mass_mailing_list_dynamic/README.rst
  2. 1
      mass_mailing_list_dynamic/__init__.py
  3. 4
      mass_mailing_list_dynamic/__manifest__.py
  4. 120
      mass_mailing_list_dynamic/i18n/es.po
  5. 1
      mass_mailing_list_dynamic/models/__init__.py
  6. 6
      mass_mailing_list_dynamic/models/mail_mass_mailing_list.py
  7. 13
      mass_mailing_list_dynamic/models/res_partner.py
  8. BIN
      mass_mailing_list_dynamic/static/description/icon.png
  9. 94
      mass_mailing_list_dynamic/static/description/icon.svg
  10. 65
      mass_mailing_list_dynamic/tests/test_dynamic_lists.py
  11. 7
      mass_mailing_list_dynamic/views/mail_mass_mailing_list_view.xml
  12. 4
      mass_mailing_list_dynamic/wizards/__init__.py
  13. 23
      mass_mailing_list_dynamic/wizards/mail_mass_mailing_load_filter.py
  14. 34
      mass_mailing_list_dynamic/wizards/mail_mass_mailing_load_filter_views.xml

24
mass_mailing_list_dynamic/README.rst

@ -18,7 +18,7 @@ Configuration
To create a dynamic mailing list, you need to: To create a dynamic mailing list, you need to:
#. Go to *Mass Mailing > Mailings > Mailing Lists > Create*.
#. Go to *Mass Mailing > Mailings > Mailing Lists* and create one.
#. Check the *Dynamic* box. #. Check the *Dynamic* box.
#. Choose a *Sync method*: #. Choose a *Sync method*:
- Leave empty to use as a manual mailing list, the normal behavior. - Leave empty to use as a manual mailing list, the normal behavior.
@ -27,10 +27,26 @@ To create a dynamic mailing list, you need to:
- *Add and remove records as needed* to make the list be fully synchronized - *Add and remove records as needed* to make the list be fully synchronized
with the *Synchronization critera*, even if that means removing contacts with the *Synchronization critera*, even if that means removing contacts
from it. from it.
#. Define a *Synchronization critera* that will be used to match the partners
#. Define a *Synchronization criteria* that will be used to match the partners
that should go into the list as contacts. Only partners with emails will that should go into the list as contacts. Only partners with emails will
be selected. be selected.
You can also load an existing filter over contacts:
#. Click on "Load filter" button below criteria.
#. Select one of the existing filters.
#. Click on "Load filter" and you will have that filter as criteria.
Usage
=====
#. Go to *Mass Mailing > Mailings > Mass Mailings*, and create one.
#. Select as recipients a mailing list.
#. On "Select mailing lists:", choose one mailing list with dynamic flag
checked.
#. Before sending the mass mailing, the list will be synced for having latest
changes.
Usage Usage
===== =====
@ -65,13 +81,15 @@ Credits
Images Images
------ ------
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
* Odoo.
* FontAwesome (http://fontawesome.io).
Contributors Contributors
------------ ------------
* `Tecnativa <https://www.tecnativa.com>`_: * `Tecnativa <https://www.tecnativa.com>`_:
* Jairo Llopis <jairo.llopis@tecnativa.com> * Jairo Llopis <jairo.llopis@tecnativa.com>
* Pedro M. Baeza <pedro.baeza@tecnativa.com>
Do not contact contributors directly about support or help with technical issues. Do not contact contributors directly about support or help with technical issues.

1
mass_mailing_list_dynamic/__init__.py

@ -2,3 +2,4 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import models from . import models
from . import wizards

4
mass_mailing_list_dynamic/__manifest__.py

@ -4,7 +4,7 @@
{ {
"name": "Dynamic Mass Mailing Lists", "name": "Dynamic Mass Mailing Lists",
"summary": "Mass mailing lists that get autopopulated", "summary": "Mass mailing lists that get autopopulated",
"version": "10.0.1.0.0",
"version": "10.0.1.1.0",
"category": "Marketing", "category": "Marketing",
"website": "https://github.com/OCA/social", "website": "https://github.com/OCA/social",
"author": "Tecnativa, Odoo Community Association (OCA)", "author": "Tecnativa, Odoo Community Association (OCA)",
@ -15,6 +15,8 @@
"mass_mailing_partner", "mass_mailing_partner",
], ],
"data": [ "data": [
# This should go first
"wizards/mail_mass_mailing_load_filter_views.xml",
"views/mail_mass_mailing_list_view.xml", "views/mail_mass_mailing_list_view.xml",
], ],
} }

120
mass_mailing_list_dynamic/i18n/es.po

@ -2,20 +2,22 @@
# This file contains the translation of the following modules: # This file contains the translation of the following modules:
# * mass_mailing_list_dynamic # * mass_mailing_list_dynamic
# #
# Translators:
# OCA Transbot <transbot@odoo-community.org>, 2017
# enjolras <yo@miguelrevilla.com>, 2018
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Odoo Server 10.0\n" "Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-11-03 10:58+0000\n"
"PO-Revision-Date: 2017-11-03 12:00+0100\n"
"Last-Translator: Jairo Llopis <yajo.sk8@gmail.com>\n"
"Language-Team: \n"
"Language: es\n"
"POT-Creation-Date: 2018-02-26 01:46+0000\n"
"PO-Revision-Date: 2018-02-26 01:46+0000\n"
"Last-Translator: enjolras <yo@miguelrevilla.com>, 2018\n"
"Language-Team: Spanish (https://www.transifex.com/oca/teams/23907/es/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: \n"
"X-Generator: Poedit 2.0.3\n"
"Content-Transfer-Encoding: \n"
"Language: es\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: mass_mailing_list_dynamic #. module: mass_mailing_list_dynamic
#: model:ir.ui.view,arch_db:mass_mailing_list_dynamic.view_mail_mass_mailing_list_form #: model:ir.ui.view,arch_db:mass_mailing_list_dynamic.view_mail_mass_mailing_list_form
@ -23,8 +25,6 @@ msgid ""
"<i class=\"fa fa-info-circle\"/> If you want to remove contacts from a " "<i class=\"fa fa-info-circle\"/> If you want to remove contacts from a "
"dynamic list, just <b>set them as <i>Opt Out</i></b>." "dynamic list, just <b>set them as <i>Opt Out</i></b>."
msgstr "" msgstr ""
"<i class=\"fa fa-info-circle\"/> Si quiere eliminar contactos de una lista "
"dinámica, simplemente <b>márquelos como <i>Envío no deseado</i></b>."
#. module: mass_mailing_list_dynamic #. module: mass_mailing_list_dynamic
#: model:ir.ui.view,arch_db:mass_mailing_list_dynamic.view_mail_mass_mailing_list_form #: model:ir.ui.view,arch_db:mass_mailing_list_dynamic.view_mail_mass_mailing_list_form
@ -32,31 +32,46 @@ msgid ""
"<i class=\"fa fa-info-circle\"/> You cannot make manual editions of contacts" "<i class=\"fa fa-info-circle\"/> You cannot make manual editions of contacts"
" in fully synchronized lists." " in fully synchronized lists."
msgstr "" msgstr ""
"<i class=\"fa fa-info-circle\"/> No puede editar manualmente los contactos "
"en las listas que están completamente sincronizadas."
#. module: mass_mailing_list_dynamic #. module: mass_mailing_list_dynamic
#: selection:mail.mass_mailing.list,sync_method:0 #: selection:mail.mass_mailing.list,sync_method:0
msgid "Add and remove records as needed" msgid "Add and remove records as needed"
msgstr "Añade y elimina los registros conforme se necesite"
msgstr ""
#. module: mass_mailing_list_dynamic
#: model:ir.ui.view,arch_db:mass_mailing_list_dynamic.view_mail_mass_mailing_load_filter_form
msgid "Cancel"
msgstr "Cancelar"
#. module: mass_mailing_list_dynamic #. module: mass_mailing_list_dynamic
#: code:addons/mass_mailing_list_dynamic/models/mail_mass_mailing_contact.py:17
#: code:addons/mass_mailing_list_dynamic/models/mail_mass_mailing_contact.py:18
#, python-format #, python-format
msgid "" msgid ""
"Cannot edit manually contacts in a fully synchronized list. Change its sync " "Cannot edit manually contacts in a fully synchronized list. Change its sync "
"method or execute a manual sync instead." "method or execute a manual sync instead."
msgstr "" msgstr ""
"No se pueden editar manualmente los contactos de una lista que está "
"completamente sincronizada. En vez de eso, cambie su método de "
"sincronización o ejecute una sincronización manual."
#. module: mass_mailing_list_dynamic #. module: mass_mailing_list_dynamic
#: model:ir.model.fields,help:mass_mailing_list_dynamic.field_mail_mass_mailing_list_sync_method #: model:ir.model.fields,help:mass_mailing_list_dynamic.field_mail_mass_mailing_list_sync_method
msgid "" msgid ""
"Choose the syncronization method for this list if you want to make it dynamic"
"Choose the syncronization method for this list if you want to make it "
"dynamic"
msgstr "" msgstr ""
"Escoja el método de sincronización para esta lista si quiere hacerla dinámica"
#. module: mass_mailing_list_dynamic
#: model:ir.model.fields,field_description:mass_mailing_list_dynamic.field_mail_mass_mailing_load_filter_create_uid
msgid "Created by"
msgstr "Creado por"
#. module: mass_mailing_list_dynamic
#: model:ir.model.fields,field_description:mass_mailing_list_dynamic.field_mail_mass_mailing_load_filter_create_date
msgid "Created on"
msgstr "Creado el"
#. module: mass_mailing_list_dynamic
#: model:ir.model.fields,field_description:mass_mailing_list_dynamic.field_mail_mass_mailing_load_filter_display_name
msgid "Display Name"
msgstr "Nombre mostrado"
#. module: mass_mailing_list_dynamic #. module: mass_mailing_list_dynamic
#: model:ir.model.fields,field_description:mass_mailing_list_dynamic.field_mail_mass_mailing_list_dynamic #: model:ir.model.fields,field_description:mass_mailing_list_dynamic.field_mail_mass_mailing_list_dynamic
@ -71,7 +86,39 @@ msgstr "Lista dinámica"
#. module: mass_mailing_list_dynamic #. module: mass_mailing_list_dynamic
#: model:ir.model.fields,help:mass_mailing_list_dynamic.field_mail_mass_mailing_list_sync_domain #: model:ir.model.fields,help:mass_mailing_list_dynamic.field_mail_mass_mailing_list_sync_domain
msgid "Filter partners to sync in this list" msgid "Filter partners to sync in this list"
msgstr "Filtrar contactos a sincronizar en esta lista"
msgstr ""
#. module: mass_mailing_list_dynamic
#: model:ir.model.fields,field_description:mass_mailing_list_dynamic.field_mail_mass_mailing_load_filter_filter_id
msgid "Filter to load"
msgstr ""
#. module: mass_mailing_list_dynamic
#: model:ir.model.fields,field_description:mass_mailing_list_dynamic.field_mail_mass_mailing_load_filter_id
msgid "ID"
msgstr "ID"
#. module: mass_mailing_list_dynamic
#: model:ir.model.fields,field_description:mass_mailing_list_dynamic.field_mail_mass_mailing_load_filter___last_update
msgid "Last Modified on"
msgstr "Última modificación e"
#. module: mass_mailing_list_dynamic
#: model:ir.model.fields,field_description:mass_mailing_list_dynamic.field_mail_mass_mailing_load_filter_write_uid
msgid "Last Updated by"
msgstr "Última actualización por"
#. module: mass_mailing_list_dynamic
#: model:ir.model.fields,field_description:mass_mailing_list_dynamic.field_mail_mass_mailing_load_filter_write_date
msgid "Last Updated on"
msgstr "Última actualización el"
#. module: mass_mailing_list_dynamic
#: model:ir.actions.act_window,name:mass_mailing_list_dynamic.action_mail_mass_mailing_load_filter
#: model:ir.ui.view,arch_db:mass_mailing_list_dynamic.view_mail_mass_mailing_list_form
#: model:ir.ui.view,arch_db:mass_mailing_list_dynamic.view_mail_mass_mailing_load_filter_form
msgid "Load filter"
msgstr "Cargar filtro"
#. module: mass_mailing_list_dynamic #. module: mass_mailing_list_dynamic
#: model:ir.model,name:mass_mailing_list_dynamic.model_mail_mass_mailing_list #: model:ir.model,name:mass_mailing_list_dynamic.model_mail_mass_mailing_list
@ -81,17 +128,29 @@ msgstr "Lista de correo"
#. module: mass_mailing_list_dynamic #. module: mass_mailing_list_dynamic
#: model:ir.model,name:mass_mailing_list_dynamic.model_mail_mass_mailing #: model:ir.model,name:mass_mailing_list_dynamic.model_mail_mass_mailing
msgid "Mass Mailing" msgid "Mass Mailing"
msgstr "Correo masivo"
msgstr "Envío masivo"
#. module: mass_mailing_list_dynamic #. module: mass_mailing_list_dynamic
#: model:ir.model,name:mass_mailing_list_dynamic.model_mail_mass_mailing_contact #: model:ir.model,name:mass_mailing_list_dynamic.model_mail_mass_mailing_contact
msgid "Mass Mailing Contact" msgid "Mass Mailing Contact"
msgstr "Contacto de correo masivo"
msgstr "Contacto de envío masivo"
#. module: mass_mailing_list_dynamic #. module: mass_mailing_list_dynamic
#: selection:mail.mass_mailing.list,sync_method:0 #: selection:mail.mass_mailing.list,sync_method:0
msgid "Only add new records" msgid "Only add new records"
msgstr "Solamente añadir nuevos registros"
msgstr "Añadir solo registros nuevos"
#. module: mass_mailing_list_dynamic
#: model:ir.model,name:mass_mailing_list_dynamic.model_res_partner
msgid "Partner"
msgstr "Empresa"
#. module: mass_mailing_list_dynamic
#: model:ir.model.fields,help:mass_mailing_list_dynamic.field_mail_mass_mailing_list_dynamic
msgid ""
"Set this list as dynamic, to make it autosynchronized with partners from a "
"given criteria."
msgstr ""
#. module: mass_mailing_list_dynamic #. module: mass_mailing_list_dynamic
#: model:ir.model.fields,field_description:mass_mailing_list_dynamic.field_mail_mass_mailing_list_sync_method #: model:ir.model.fields,field_description:mass_mailing_list_dynamic.field_mail_mass_mailing_list_sync_method
@ -106,14 +165,9 @@ msgstr "Sincronizar ahora"
#. module: mass_mailing_list_dynamic #. module: mass_mailing_list_dynamic
#: model:ir.model.fields,field_description:mass_mailing_list_dynamic.field_mail_mass_mailing_list_sync_domain #: model:ir.model.fields,field_description:mass_mailing_list_dynamic.field_mail_mass_mailing_list_sync_domain
msgid "Synchronization critera" msgid "Synchronization critera"
msgstr "Criterios de sincronización"
msgstr "Criterio de sincronización"
#~ msgid ""
#~ "<i class=\"fa fa-exclamation-circle\"/> If you manually add contacts to "
#~ "the list, <b>they might get removed later automatically!</b>"
#~ msgstr ""
#~ "<i class=\"fa fa-exclamation-circle\"/> Si añade contactos a la lista "
#~ "manualmente, <b>¡podrían ser eliminados automáticamente más tarde!</b>"
#~ msgid "Sync domain"
#~ msgstr "Dominio de sincronización"
#. module: mass_mailing_list_dynamic
#: model:ir.model,name:mass_mailing_list_dynamic.model_mail_mass_mailing_load_filter
msgid "mail.mass_mailing.load.filter"
msgstr "mail.mass_mailing.load.filter"

1
mass_mailing_list_dynamic/models/__init__.py

@ -4,3 +4,4 @@
from . import mail_mass_mailing from . import mail_mass_mailing
from . import mail_mass_mailing_contact from . import mail_mass_mailing_contact
from . import mail_mass_mailing_list from . import mail_mass_mailing_list
from . import res_partner

6
mass_mailing_list_dynamic/models/mail_mass_mailing_list.py

@ -32,10 +32,12 @@ class MassMailingList(models.Model):
def action_sync(self): def action_sync(self):
"""Sync contacts in dynamic lists.""" """Sync contacts in dynamic lists."""
Contact = self.env["mail.mass_mailing.contact"]
Contact = self.env["mail.mass_mailing.contact"].with_context(
syncing=True,
)
Partner = self.env["res.partner"] Partner = self.env["res.partner"]
# Skip non-dynamic lists # Skip non-dynamic lists
dynamic = self.filtered("dynamic").with_context(syncing=True)
dynamic = self.filtered("dynamic")
for one in dynamic: for one in dynamic:
sync_domain = safe_eval(one.sync_domain) + [("email", "!=", False)] sync_domain = safe_eval(one.sync_domain) + [("email", "!=", False)]
desired_partners = Partner.search(sync_domain) desired_partners = Partner.search(sync_domain)

13
mass_mailing_list_dynamic/models/res_partner.py

@ -0,0 +1,13 @@
# -*- 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 models
class ResPartner(models.Model):
_inherit = 'res.partner'
def write(self, vals):
"""Allow to write values in mass mailing contact."""
return super(ResPartner, self.with_context(syncing=True)).write(vals)

BIN
mass_mailing_list_dynamic/static/description/icon.png

Before

Width: 128  |  Height: 128  |  Size: 9.2 KiB

After

Width: 128  |  Height: 128  |  Size: 5.3 KiB

94
mass_mailing_list_dynamic/static/description/icon.svg

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
width="80"
height="80"
viewBox="0 0 80 80"
sodipodi:docname="icon.svg"
inkscape:export-filename="icon.png"
inkscape:export-xdpi="144"
inkscape:export-ydpi="144">
<metadata
id="metadata8">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs6" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1855"
inkscape:window-height="1056"
id="namedview4"
showgrid="false"
inkscape:zoom="5.9"
inkscape:cx="26.675663"
inkscape:cy="23.292293"
inkscape:window-x="65"
inkscape:window-y="24"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<rect
style="opacity:1;fill:#8c7f6c;fill-opacity:1;stroke:none;stroke-width:4.05708647;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect3346"
width="80"
height="80"
x="0"
y="0" />
<path
style="fill:#675d4f;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="M 12.344407,43.446175 -0.11984861,56.150127 0,80 41.707315,79.760303 58.366272,62.741801 64.718248,15.042055 Z"
id="path3344"
inkscape:connector-curvature="0" />
<g
style="font-style:normal;font-weight:normal;font-size:54.74009323px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="text3338">
<path
d="m 66.470545,12.85352 c -0.610938,-0.427657 -1.435705,-0.458204 -2.077191,-0.06109 l -50.830086,29.32505 c -0.641486,0.366563 -1.038596,1.069143 -0.977502,1.802269 0.06109,0.763673 0.519298,1.405159 1.221877,1.680081 l 14.418149,5.895557 0,13.807211 c 0,0.79422 0.488751,1.527346 1.252424,1.832816 0.213829,0.09164 0.458204,0.122187 0.70258,0.122187 0.549844,0 1.069142,-0.213828 1.435705,-0.641485 l 9.102985,-9.988845 16.09823,6.567589 c 0.244376,0.09164 0.488751,0.152735 0.733126,0.152735 0.336017,0 0.672033,-0.09164 0.946955,-0.244376 0.519298,-0.305469 0.885861,-0.79422 0.977502,-1.374612 L 67.295312,14.808524 C 67.4175,14.04485 67.112031,13.311724 66.470545,12.85352 Z M 56.023496,58.643364 40.994408,52.503431 55.595839,28.157531 29.233841,47.677017 18.970074,43.492088 62.774367,18.229779 56.023496,58.643364 Z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:FontAwesome;-inkscape-font-specification:FontAwesome;fill:#ffffff"
id="path3342" />
</g>
<g
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:37.0338974px;line-height:125%;font-family:FontAwesome;-inkscape-font-specification:FontAwesome;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="text3343">
<path
d="m 33.743341,24.484866 c 0,-0.723318 -0.599321,-1.322639 -1.32264,-1.322639 l -29.0980618,0 C 2.5993209,23.162227 2,23.761548 2,24.484866 l 0,2.645278 c 0,0.723319 0.5993209,1.32264 1.3226392,1.32264 l 29.0980618,0 c 0.723319,0 1.32264,-0.599321 1.32264,-1.32264 l 0,-2.645278 z m 0,-10.581114 c 0,-0.723318 -0.599321,-1.322639 -1.32264,-1.322639 l -29.0980618,0 C 2.5993209,12.581113 2,13.180434 2,13.903752 l 0,2.645279 c 0,0.723318 0.5993209,1.322639 1.3226392,1.322639 l 29.0980618,0 c 0.723319,0 1.32264,-0.599321 1.32264,-1.322639 l 0,-2.645279 z m 0,-10.5811131 c 0,-0.7233183 -0.599321,-1.3226392 -1.32264,-1.3226392 l -29.0980618,0 C 2.5993209,1.9999997 2,2.5993206 2,3.3226389 l 0,2.6452784 c 0,0.7233183 0.5993209,1.3226392 1.3226392,1.3226392 l 29.0980618,0 c 0.723319,0 1.32264,-0.5993209 1.32264,-1.3226392 l 0,-2.6452784 z"
style=""
id="path3348" />
</g>
<g
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.42372704px;line-height:125%;font-family:FontAwesome;-inkscape-font-specification:FontAwesome;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="text3339"
transform="matrix(0.94190867,0,0,0.94190867,26.461918,-5.3866658)">
<path
d="m 26.339767,17.049939 c 0,-0.193752 -0.15956,-0.364709 -0.364709,-0.364709 l -2.188257,0 c -0.170957,0 -0.284929,0.102574 -0.341915,0.262135 -0.193752,0.455886 -0.330518,0.888979 -0.60405,1.333468 -1.059936,1.73237 -2.940469,2.78091 -4.969165,2.78091 -1.470235,0 -2.894881,-0.569859 -3.966215,-1.57281 l 1.561412,-1.561412 c 0.136766,-0.136766 0.216546,-0.319121 0.216546,-0.512872 0,-0.398901 -0.330518,-0.729419 -0.729419,-0.729419 l -5.1059315,0 c -0.3989009,0 -0.7294188,0.330518 -0.7294188,0.729419 l 0,5.105931 c 0,0.398901 0.3305179,0.729419 0.7294188,0.729419 0.1937515,0 0.3761065,-0.07978 0.5128725,-0.216546 l 1.470235,-1.470235 c 1.618398,1.538618 3.772463,2.4162 5.994911,2.4162 4.205555,0 7.533529,-2.815101 8.502288,-6.849699 0.0114,-0.02279 0.0114,-0.05699 0.0114,-0.07978 z m 0.284929,-9.1177352 c 0,-0.3989009 -0.330517,-0.7294188 -0.729418,-0.7294188 -0.193752,0 -0.376107,0.07978 -0.512873,0.2165462 L 23.900773,8.889566 C 22.282375,7.3623454 20.105516,6.4733662 17.871671,6.4733662 c -4.205556,0 -7.567721,2.8037036 -8.5478772,6.8496988 0,0.02279 0,0.05699 0,0.07978 0,0.193752 0.1595603,0.364709 0.3647094,0.364709 l 2.2680368,0 c 0.170957,0 0.284929,-0.102574 0.341915,-0.262134 0.193752,-0.455887 0.330518,-0.88898 0.60405,-1.333469 1.059937,-1.73237 2.940469,-2.7809095 4.969166,-2.7809095 1.470234,0 2.894881,0.5584613 3.977612,1.5614125 l -1.57281,1.572809 c -0.136766,0.136766 -0.216546,0.319121 -0.216546,0.512873 0,0.398901 0.330518,0.729418 0.729419,0.729418 l 5.105932,0 c 0.398901,0 0.729418,-0.330517 0.729418,-0.729418 l 0,-5.1059322 z"
id="path3351"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
</g>
</svg>

65
mass_mailing_list_dynamic/tests/test_dynamic_lists.py

@ -4,32 +4,35 @@
from mock import patch from mock import patch
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
from odoo.tests.common import TransactionCase
from odoo.tests import common
class DynamicListCase(TransactionCase):
def setUp(self):
super(DynamicListCase, self).setUp()
self.tag = self.env["res.partner.category"].create({
@common.at_install(False)
@common.post_install(True)
class DynamicListCase(common.SavepointCase):
@classmethod
def setUpClass(cls):
super(DynamicListCase, cls).setUpClass()
cls.tag = cls.env["res.partner.category"].create({
"name": "testing tag", "name": "testing tag",
}) })
self.partners = self.env["res.partner"]
cls.partners = cls.env["res.partner"]
for number in range(5): for number in range(5):
self.partners |= self.partners.create({
cls.partners |= cls.partners.create({
"name": "partner %d" % number, "name": "partner %d" % number,
"category_id": [(4, self.tag.id, False)],
"category_id": [(4, cls.tag.id, False)],
"email": "%d@example.com" % number, "email": "%d@example.com" % number,
}) })
self.list = self.env["mail.mass_mailing.list"].create({
cls.list = cls.env["mail.mass_mailing.list"].create({
"name": "test list", "name": "test list",
"dynamic": True, "dynamic": True,
"sync_domain": repr([("category_id", "in", self.tag.ids)]),
"sync_domain": repr([("category_id", "in", cls.tag.ids)]),
}) })
self.mail = self.env["mail.mass_mailing"].create({
cls.mail = cls.env["mail.mass_mailing"].create({
"name": "test mass mailing", "name": "test mass mailing",
"contact_list_ids": [(4, self.list.id, False)],
"contact_list_ids": [(4, cls.list.id, False)],
}) })
self.mail._onchange_model_and_list()
cls.mail._onchange_model_and_list()
def test_list_sync(self): def test_list_sync(self):
"""List is synced correctly.""" """List is synced correctly."""
@ -53,6 +56,10 @@ class DynamicListCase(TransactionCase):
self.assertTrue(contact0.exists()) self.assertTrue(contact0.exists())
# Set list as full-synced # Set list as full-synced
self.list.sync_method = "full" self.list.sync_method = "full"
Contact.search([
("list_id", "=", self.list.id),
("partner_id", "=", self.partners[2].id),
]).unlink()
self.list.action_sync() self.list.action_sync()
self.assertEqual(self.list.contact_nbr, 3) self.assertEqual(self.list.contact_nbr, 3)
self.assertFalse(contact0.exists()) self.assertFalse(contact0.exists())
@ -73,7 +80,7 @@ class DynamicListCase(TransactionCase):
contact1.partner_id = self.partners[0] contact1.partner_id = self.partners[0]
def test_sync_when_sending_mail(self): def test_sync_when_sending_mail(self):
"""Dynamic list is synced before mailing to it."""
"""Check that list in synced when sending a mass mailing."""
self.list.action_sync() self.list.action_sync()
self.assertEqual(self.list.contact_nbr, 5) self.assertEqual(self.list.contact_nbr, 5)
# Create a new partner # Create a new partner
@ -82,9 +89,31 @@ class DynamicListCase(TransactionCase):
"category_id": [(4, self.tag.id, False)], "category_id": [(4, self.tag.id, False)],
"email": "extra@example.com", "email": "extra@example.com",
}) })
# Before sending the mail, the list is updated
with patch("odoo.addons.base.ir.ir_mail_server"
".IrMailServer.send_email") as send_email:
# Mock sending low level method, because an auto-commit happens there
with patch("odoo.addons.mail.models.mail_mail.MailMail.send") as s:
self.mail.send_mail() self.mail.send_mail()
self.assertEqual(6, send_email.call_count)
self.assertEqual(1, s.call_count)
self.assertEqual(6, self.list.contact_nbr) self.assertEqual(6, self.list.contact_nbr)
def test_load_filter(self):
domain = "[('id', '=', 1)]"
ir_filter = self.env['ir.filters'].create({
'name': 'Test filter',
'model_id': 'res.partner',
'domain': domain,
})
wizard = self.env['mail.mass_mailing.load.filter'].with_context(
active_id=self.list.id,
).create({
'filter_id': ir_filter.id,
})
wizard.load_filter()
self.assertEqual(self.list.sync_domain, domain)
def test_change_partner(self):
self.list.sync_method = 'full'
self.list.action_sync()
# This shouldn't fail
self.partners[:1].write({
'email': 'test_mass_mailing_list_dynamic@example.org',
})

7
mass_mailing_list_dynamic/views/mail_mass_mailing_list_view.xml

@ -52,6 +52,13 @@
options='{"model": "res.partner"}' options='{"model": "res.partner"}'
/> />
</group> </group>
<button
name="%(action_mail_mass_mailing_load_filter)d"
type="action"
string="Load filter"
icon="fa-filter"
colspan="1"
/>
</group> </group>
</xpath> </xpath>
</field> </field>

4
mass_mailing_list_dynamic/wizards/__init__.py

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

23
mass_mailing_list_dynamic/wizards/mail_mass_mailing_load_filter.py

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Tecnativa - Pedro M. Baeza
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models
class MassMailingLoadFilter(models.TransientModel):
_name = "mail.mass_mailing.load.filter"
filter_id = fields.Many2one(
comodel_name='ir.filters',
string="Filter to load",
required=True,
domain=[('model_id', '=', 'res.partner')],
)
def load_filter(self):
self.ensure_one()
mass_list = self.env['mail.mass_mailing.list'].browse(
self.env.context['active_id']
)
mass_list.sync_domain = self.filter_id.domain

34
mass_mailing_list_dynamic/wizards/mail_mass_mailing_load_filter_views.xml

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2017 Tecnativa - Pedro M. Baea
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="view_mail_mass_mailing_load_filter_form" model="ir.ui.view">
<field name="model">mail.mass_mailing.load.filter</field>
<field name="arch" type="xml">
<form>
<group>
<field name="filter_id"/>
</group>
<footer>
<button name="load_filter"
string="Load filter"
type="object"
/>
<button special="cancel"
string="Cancel"
/>
</footer>
</form>
</field>
</record>
<act_window id="action_mail_mass_mailing_load_filter"
name="Load filter"
res_model="mail.mass_mailing.load.filter"
target="new"
view_mode="form"
/>
</odoo>
Loading…
Cancel
Save