From 627b238a5a2f1fa73c9ee1537ebeb9cb4a33eb14 Mon Sep 17 00:00:00 2001 From: Javier Iniesta Date: Fri, 28 Aug 2015 11:15:50 +0200 Subject: [PATCH] mass_mailing_partner --- mass_mailing_partner/README.rst | 88 +++++++++++ mass_mailing_partner/__init__.py | 20 +++ mass_mailing_partner/__openerp__.py | 23 +++ mass_mailing_partner/i18n/es.po | 140 ++++++++++++++++++ .../i18n/mass_mailing_partner.pot | 140 ++++++++++++++++++ mass_mailing_partner/models/__init__.py | 6 + .../models/mail_mass_mailing.py | 13 ++ .../models/mail_mass_mailing_contact.py | 78 ++++++++++ mass_mailing_partner/models/res_partner.py | 41 +++++ .../static/description/icon.png | Bin 0 -> 14912 bytes mass_mailing_partner/tests/__init__.py | 5 + mass_mailing_partner/tests/base.py | 35 +++++ .../tests/test_mail_mass_mailing_contact.py | 35 +++++ .../tests/test_partner_mail_list_wizard.py | 24 +++ .../tests/test_res_partner.py | 25 ++++ .../views/mail_mass_mailing_contact_view.xml | 39 +++++ .../views/mail_mass_mailing_view.xml | 22 +++ .../views/res_partner_view.xml | 39 +++++ mass_mailing_partner/wizard/__init__.py | 4 + .../wizard/partner_mail_list_wizard.py | 34 +++++ .../wizard/partner_mail_list_wizard.xml | 32 ++++ 21 files changed, 843 insertions(+) create mode 100644 mass_mailing_partner/README.rst create mode 100644 mass_mailing_partner/__init__.py create mode 100644 mass_mailing_partner/__openerp__.py create mode 100644 mass_mailing_partner/i18n/es.po create mode 100644 mass_mailing_partner/i18n/mass_mailing_partner.pot create mode 100644 mass_mailing_partner/models/__init__.py create mode 100644 mass_mailing_partner/models/mail_mass_mailing.py create mode 100644 mass_mailing_partner/models/mail_mass_mailing_contact.py create mode 100644 mass_mailing_partner/models/res_partner.py create mode 100644 mass_mailing_partner/static/description/icon.png create mode 100644 mass_mailing_partner/tests/__init__.py create mode 100644 mass_mailing_partner/tests/base.py create mode 100644 mass_mailing_partner/tests/test_mail_mass_mailing_contact.py create mode 100644 mass_mailing_partner/tests/test_partner_mail_list_wizard.py create mode 100644 mass_mailing_partner/tests/test_res_partner.py create mode 100644 mass_mailing_partner/views/mail_mass_mailing_contact_view.xml create mode 100644 mass_mailing_partner/views/mail_mass_mailing_view.xml create mode 100644 mass_mailing_partner/views/res_partner_view.xml create mode 100644 mass_mailing_partner/wizard/__init__.py create mode 100644 mass_mailing_partner/wizard/partner_mail_list_wizard.py create mode 100644 mass_mailing_partner/wizard/partner_mail_list_wizard.xml diff --git a/mass_mailing_partner/README.rst b/mass_mailing_partner/README.rst new file mode 100644 index 00000000..09bf2dd3 --- /dev/null +++ b/mass_mailing_partner/README.rst @@ -0,0 +1,88 @@ +.. 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 + +=============================== +Link partners with mass-mailing +=============================== + +This module links mass-mailing contacts with partners. + +Features +-------- +* When creating or saving a mass-mailing contact, partners are matched through + email, linking matched partner, or creating a new one if no match and the + maling list partner mandatory field is checked. +* Mailing contacts smart button in partner form. + + +Configuration +============= + +At first install, all existing mass mailing contacts are matched against +partners. + + +Usage +===== + +In partner view, there is a new action called "Add to mailing list". This +action open a pop-up to select a mailing list. Selected partners will be added +as mailing list contacts. + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/111/8.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 +`here `_. + + +License +======= + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + + +Credits +======= + +Contributors +------------ + +* Pedro M. Baeza +* Rafael Blasco +* Antonio Espinosa +* Javier Iniesta + +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 http://odoo-community.org. \ No newline at end of file diff --git a/mass_mailing_partner/__init__.py b/mass_mailing_partner/__init__.py new file mode 100644 index 00000000..9250d91b --- /dev/null +++ b/mass_mailing_partner/__init__.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# See README.rst file on addon root folder for license details + +from . import models +from . import wizard +from openerp import api, SUPERUSER_ID + + +def _match_existing_contacts(cr, registry): + with api.Environment.manage(): + env = api.Environment(cr, SUPERUSER_ID, {}) + contact_model = env['mail.mass_mailing.contact'] + partner_model = env['res.partner'] + contacts = contact_model.search([('email', '!=', False)]) + for contact in contacts: + if contact.email: + partners = partner_model.search([('email', '=ilike', + contact.email)]) + if partners: + contact.write({'partner_id': partners[0].id}) diff --git a/mass_mailing_partner/__openerp__.py b/mass_mailing_partner/__openerp__.py new file mode 100644 index 00000000..684b39cd --- /dev/null +++ b/mass_mailing_partner/__openerp__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# See README.rst file on addon root folder for license details + +{ + "name": "Link partners with mass-mailing", + "version": "8.0.1.0.0", + "author": "Antiun Ingeniería S.L., " + "Serv. Tecnol. Avanzados - Pedro M. Baeza, " + "Odoo Community Association (OCA)", + "license": "AGPL-3", + "category": "Marketing", + "depends": [ + 'mass_mailing', + ], + "post_init_hook": "_match_existing_contacts", + 'data': [ + 'views/mail_mass_mailing_contact_view.xml', + 'views/mail_mass_mailing_view.xml', + 'views/res_partner_view.xml', + 'wizard/partner_mail_list_wizard.xml' + ], + "installable": True, +} diff --git a/mass_mailing_partner/i18n/es.po b/mass_mailing_partner/i18n/es.po new file mode 100644 index 00000000..3da5e0ad --- /dev/null +++ b/mass_mailing_partner/i18n/es.po @@ -0,0 +1,140 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mass_mailing_partner +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-11-03 14:42+0000\n" +"PO-Revision-Date: 2015-11-03 14: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_partner +#: view:partner.mail.list.wizard:mass_mailing_partner.partner_mail_list_wizard_form +msgid "Add contacts to mailing list" +msgstr "Añadir contactos a la lista de correo" + +#. module: mass_mailing_partner +#: model:ir.actions.act_window,name:mass_mailing_partner.action_partner_mail_list +msgid "Add to mailing list" +msgstr "Añadir a lista de correo" + +#. module: mass_mailing_partner +#: view:partner.mail.list.wizard:mass_mailing_partner.partner_mail_list_wizard_form +msgid "Cancel" +msgstr "Cancelar" + +#. module: mass_mailing_partner +#: model:ir.model,name:mass_mailing_partner.model_partner_mail_list_wizard +#: view:partner.mail.list.wizard:mass_mailing_partner.partner_mail_list_wizard_form +msgid "Create contact mailing list" +msgstr "Crear lista de correo de contactos" + +#. module: mass_mailing_partner +#: field:partner.mail.list.wizard,create_uid:0 +msgid "Created by" +msgstr "Creado por" + +#. module: mass_mailing_partner +#: field:partner.mail.list.wizard,create_date:0 +msgid "Created on" +msgstr "Creado en" + +#. module: mass_mailing_partner +#: field:partner.mail.list.wizard,id:0 +msgid "ID" +msgstr "ID" + +#. module: mass_mailing_partner +#: field:partner.mail.list.wizard,write_uid:0 +msgid "Last Updated by" +msgstr "Última actualización por" + +#. module: mass_mailing_partner +#: field:partner.mail.list.wizard,write_date:0 +msgid "Last Updated on" +msgstr "Última actualización en" + +#. module: mass_mailing_partner +#: model:ir.model,name:mass_mailing_partner.model_mail_mass_mailing_list +#: field:partner.mail.list.wizard,mail_list_id:0 +#: view:res.partner:mass_mailing_partner.view_res_partner_filter +msgid "Mailing List" +msgstr "Lista de correo" + +#. module: mass_mailing_partner +#: field:res.partner,mass_mailing_contacts_count:0 +msgid "Mailing list number" +msgstr "Nº Listas de correo" + +#. module: mass_mailing_partner +#: view:res.partner:mass_mailing_partner.view_partner_form +msgid "Mailing lists" +msgstr "Listas de correo" + +#. module: mass_mailing_partner +#: field:mail.mass_mailing.list,partner_mandatory:0 +msgid "Mandatory Partner" +msgstr "Empresa obligatoria" + +#. module: mass_mailing_partner +#: model:ir.model,name:mass_mailing_partner.model_mail_mass_mailing_contact +msgid "Mass Mailing Contact" +msgstr "Contacto de lista de correo" + +#. module: mass_mailing_partner +#: field:res.partner,mass_mailing_contacts:0 +msgid "Mass mailing contacts" +msgstr "Contactos de lista de correo" + +#. module: mass_mailing_partner +#: model:ir.model,name:mass_mailing_partner.model_res_partner +#: view:mail.mass_mailing.contact:mass_mailing_partner.view_mail_mass_mailing_contact_search +#: field:mail.mass_mailing.contact,partner_id:0 +msgid "Partner" +msgstr "Empresa" + +#. module: mass_mailing_partner +#: code:addons/mass_mailing_partner/wizard/partner_mail_list_wizard.py:24 +#, python-format +msgid "Partner '%s' has no email." +msgstr "La empresa '%s' no tiene email." + +#. module: mass_mailing_partner +#: field:mail.mass_mailing.list,partner_category:0 +msgid "Partner Tag" +msgstr "Etiqueta de empresa" + +#. module: mass_mailing_partner +#: code:addons/mass_mailing_partner/models/mail_mass_mailing_contact.py:15 +#: sql_constraint:mail.mass_mailing.contact:0 +#, python-format +msgid "Partner already exists in this mailing list." +msgstr "La empresa ya existe en esta lista de correo" + +#. module: mass_mailing_partner +#: field:partner.mail.list.wizard,partner_ids:0 +msgid "Partner ids" +msgstr "IDS de empresas" + +#. module: mass_mailing_partner +#: code:addons/mass_mailing_partner/models/res_partner.py:23 +#, python-format +msgid "This partner '%s' is subscribed to one or more mailing lists. Email must be assigned." +msgstr "Esta empresa '%s' está suscrita a una o más listas de correo. El email debe estar asignado." + +#. module: mass_mailing_partner +#: view:partner.mail.list.wizard:mass_mailing_partner.partner_mail_list_wizard_form +msgid "or" +msgstr "o" + +#. module: mass_mailing_partner +#: view:mail.mass_mailing.contact:mass_mailing_partner.view_mail_mass_mailing_contact_tree +msgid "{'readonly': [('partner_id', '!=', False)]}" +msgstr "" diff --git a/mass_mailing_partner/i18n/mass_mailing_partner.pot b/mass_mailing_partner/i18n/mass_mailing_partner.pot new file mode 100644 index 00000000..e09d240b --- /dev/null +++ b/mass_mailing_partner/i18n/mass_mailing_partner.pot @@ -0,0 +1,140 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mass_mailing_partner +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-11-03 14:42+0000\n" +"PO-Revision-Date: 2015-11-03 14: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_partner +#: view:partner.mail.list.wizard:mass_mailing_partner.partner_mail_list_wizard_form +msgid "Add contacts to mailing list" +msgstr "" + +#. module: mass_mailing_partner +#: model:ir.actions.act_window,name:mass_mailing_partner.action_partner_mail_list +msgid "Add to mailing list" +msgstr "" + +#. module: mass_mailing_partner +#: view:partner.mail.list.wizard:mass_mailing_partner.partner_mail_list_wizard_form +msgid "Cancel" +msgstr "" + +#. module: mass_mailing_partner +#: model:ir.model,name:mass_mailing_partner.model_partner_mail_list_wizard +#: view:partner.mail.list.wizard:mass_mailing_partner.partner_mail_list_wizard_form +msgid "Create contact mailing list" +msgstr "" + +#. module: mass_mailing_partner +#: field:partner.mail.list.wizard,create_uid:0 +msgid "Created by" +msgstr "" + +#. module: mass_mailing_partner +#: field:partner.mail.list.wizard,create_date:0 +msgid "Created on" +msgstr "" + +#. module: mass_mailing_partner +#: field:partner.mail.list.wizard,id:0 +msgid "ID" +msgstr "" + +#. module: mass_mailing_partner +#: field:partner.mail.list.wizard,write_uid:0 +msgid "Last Updated by" +msgstr "" + +#. module: mass_mailing_partner +#: field:partner.mail.list.wizard,write_date:0 +msgid "Last Updated on" +msgstr "" + +#. module: mass_mailing_partner +#: model:ir.model,name:mass_mailing_partner.model_mail_mass_mailing_list +#: field:partner.mail.list.wizard,mail_list_id:0 +#: view:res.partner:mass_mailing_partner.view_res_partner_filter +msgid "Mailing List" +msgstr "" + +#. module: mass_mailing_partner +#: field:res.partner,mass_mailing_contacts_count:0 +msgid "Mailing list number" +msgstr "" + +#. module: mass_mailing_partner +#: view:res.partner:mass_mailing_partner.view_partner_form +msgid "Mailing lists" +msgstr "" + +#. module: mass_mailing_partner +#: field:mail.mass_mailing.list,partner_mandatory:0 +msgid "Mandatory Partner" +msgstr "" + +#. module: mass_mailing_partner +#: model:ir.model,name:mass_mailing_partner.model_mail_mass_mailing_contact +msgid "Mass Mailing Contact" +msgstr "" + +#. module: mass_mailing_partner +#: field:res.partner,mass_mailing_contacts:0 +msgid "Mass mailing contacts" +msgstr "" + +#. module: mass_mailing_partner +#: model:ir.model,name:mass_mailing_partner.model_res_partner +#: view:mail.mass_mailing.contact:mass_mailing_partner.view_mail_mass_mailing_contact_search +#: field:mail.mass_mailing.contact,partner_id:0 +msgid "Partner" +msgstr "" + +#. module: mass_mailing_partner +#: code:addons/mass_mailing_partner/wizard/partner_mail_list_wizard.py:24 +#, python-format +msgid "Partner '%s' has no email." +msgstr "" + +#. module: mass_mailing_partner +#: field:mail.mass_mailing.list,partner_category:0 +msgid "Partner Tag" +msgstr "" + +#. module: mass_mailing_partner +#: code:addons/mass_mailing_partner/models/mail_mass_mailing_contact.py:15 +#: sql_constraint:mail.mass_mailing.contact:0 +#, python-format +msgid "Partner already exists in this mailing list." +msgstr "" + +#. module: mass_mailing_partner +#: field:partner.mail.list.wizard,partner_ids:0 +msgid "Partner ids" +msgstr "" + +#. module: mass_mailing_partner +#: code:addons/mass_mailing_partner/models/res_partner.py:23 +#, python-format +msgid "This partner '%s' is subscribed to one or more mailing lists. Email must be assigned." +msgstr "" + +#. module: mass_mailing_partner +#: view:partner.mail.list.wizard:mass_mailing_partner.partner_mail_list_wizard_form +msgid "or" +msgstr "" + +#. module: mass_mailing_partner +#: view:mail.mass_mailing.contact:mass_mailing_partner.view_mail_mass_mailing_contact_tree +msgid "{'readonly': [('partner_id', '!=', False)]}" +msgstr "" diff --git a/mass_mailing_partner/models/__init__.py b/mass_mailing_partner/models/__init__.py new file mode 100644 index 00000000..744629bd --- /dev/null +++ b/mass_mailing_partner/models/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# See README.rst file on addon root folder for license details + +from . import mail_mass_mailing_contact +from . import res_partner +from . import mail_mass_mailing diff --git a/mass_mailing_partner/models/mail_mass_mailing.py b/mass_mailing_partner/models/mail_mass_mailing.py new file mode 100644 index 00000000..95d062a5 --- /dev/null +++ b/mass_mailing_partner/models/mail_mass_mailing.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# See README.rst file on addon root folder for license details + +from openerp import models, fields + + +class MailMassMailingList(models.Model): + _inherit = 'mail.mass_mailing.list' + + partner_mandatory = fields.Boolean(string="Mandatory Partner", + default=False) + partner_category = fields.Many2one(comodel_name='res.partner.category', + string="Partner Tag") diff --git a/mass_mailing_partner/models/mail_mass_mailing_contact.py b/mass_mailing_partner/models/mail_mass_mailing_contact.py new file mode 100644 index 00000000..04692763 --- /dev/null +++ b/mass_mailing_partner/models/mail_mass_mailing_contact.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +# See README.rst file on addon root folder for license details + +from openerp import models, fields, api, _ + + +class MailMassMailingContact(models.Model): + _inherit = 'mail.mass_mailing.contact' + + partner_id = fields.Many2one(comodel_name='res.partner', string="Partner", + domain=[('email', '!=', False)]) + + _sql_constraints = [ + ('partner_list_uniq', 'unique(partner_id, list_id)', + _('Partner already exists in this mailing list.')) + ] + + @api.one + @api.onchange('partner_id') + def _onchange_partner(self): + if self.partner_id: + self.name = self.partner_id.name + self.email = self.partner_id.email + + @api.model + @api.returns('self', lambda x: x.id) + def create(self, vals): + if not vals.get('partner_id'): + vals = self._set_partner(vals) + vals = self._set_name_email(vals) + return super(MailMassMailingContact, self).create(vals) + + @api.one + def write(self, vals): + if vals.get('partner_id', None) is False: + # If removing partner, search again by email + vals = self._set_partner(vals) + vals = self._set_name_email(vals) + return super(MailMassMailingContact, self).write(vals) + + def _prepare_partner(self, vals, mailing_list): + vals = { + 'name': vals.get('name') or vals.get('email'), + 'email': vals.get('email', False), + } + if mailing_list.partner_category: + vals['category_id'] = [(4, mailing_list.partner_category.id, 0)] + return vals + + def _set_partner(self, vals): + email = vals.get('email', self.email) + if not email: + return vals + m_mailing = self.env['mail.mass_mailing.list'] + m_partner = self.env['res.partner'] + list_id = vals.get('list_id', self.list_id.id) + mailing_list = m_mailing.browse(list_id) + # Look for a partner with that email + email = email.strip() + partners = m_partner.search([('email', '=ilike', email)], limit=1) + if partners: + # Partner found + vals['partner_id'] = partners[0].id + elif mailing_list.partner_mandatory: + # Create partner + partner = m_partner.sudo().create( + self._prepare_partner(vals, mailing_list)) + vals['partner_id'] = partner.id + return vals + + def _set_name_email(self, vals): + partner_id = vals.get('partner_id', self.partner_id.id) + if not partner_id: + return vals + partner = self.env['res.partner'].browse(partner_id) + vals['email'] = partner.email + vals['name'] = partner.name + return vals diff --git a/mass_mailing_partner/models/res_partner.py b/mass_mailing_partner/models/res_partner.py new file mode 100644 index 00000000..82d7b4e2 --- /dev/null +++ b/mass_mailing_partner/models/res_partner.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# See README.rst file on addon root folder for license details + +from openerp import models, fields, api, _ +from openerp.exceptions import ValidationError + + +class ResPartner(models.Model): + _inherit = 'res.partner' + + mass_mailing_contacts = fields.One2many( + comodel_name='mail.mass_mailing.contact', inverse_name='partner_id') + + mass_mailing_contacts_count = fields.Integer( + string='Mailing list number', compute='_count_mass_mailing_contacts', + store=True) + + @api.one + @api.constrains('email') + def _check_email_mass_mailing_contacts(self): + if self.mass_mailing_contacts and not self.email: + raise ValidationError( + _("This partner '%s' is subscribed to one or more " + "mailing lists. Email must be assigned." % self.name)) + + @api.one + @api.depends('mass_mailing_contacts') + def _count_mass_mailing_contacts(self): + self.mass_mailing_contacts_count = len(self.mass_mailing_contacts) + + @api.multi + def write(self, vals): + res = super(ResPartner, self).write(vals) + if vals.get('name') or vals.get('email'): + mm_vals = {} + if vals.get('name'): + mm_vals['name'] = vals['name'] + if vals.get('email'): + mm_vals['name'] = vals['email'] + self.mapped('mass_mailing_contacts').write(mm_vals) + return res diff --git a/mass_mailing_partner/static/description/icon.png b/mass_mailing_partner/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b18b21a1f7fb7547bedeeb098bc451c9681d8f7a GIT binary patch literal 14912 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 + + + + + mail.mass_mailing.contact.tree.partner + mail.mass_mailing.contact + + + + + + + {'readonly': [('partner_id', '!=', False)]} + + + {'readonly': [('partner_id', '!=', False)]} + + + + + + mail.mass_mailing.contact.search.partner + mail.mass_mailing.contact + + + + + + + + + + + + + diff --git a/mass_mailing_partner/views/mail_mass_mailing_view.xml b/mass_mailing_partner/views/mail_mass_mailing_view.xml new file mode 100644 index 00000000..fff295c4 --- /dev/null +++ b/mass_mailing_partner/views/mail_mass_mailing_view.xml @@ -0,0 +1,22 @@ + + + + + + mail.mass_mailing.list.form + mail.mass_mailing.list + + + + + + + + + + + + + + + diff --git a/mass_mailing_partner/views/res_partner_view.xml b/mass_mailing_partner/views/res_partner_view.xml new file mode 100644 index 00000000..da04ad92 --- /dev/null +++ b/mass_mailing_partner/views/res_partner_view.xml @@ -0,0 +1,39 @@ + + + + + + Partner Form with mailing contacts + res.partner + + +
+ +
+
+
+ + + Partner Search with mailing contacts + res.partner + + 20 + + + + + + + +
+
diff --git a/mass_mailing_partner/wizard/__init__.py b/mass_mailing_partner/wizard/__init__.py new file mode 100644 index 00000000..d9b71887 --- /dev/null +++ b/mass_mailing_partner/wizard/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# See README.rst file on addon root folder for license details + +from . import partner_mail_list_wizard diff --git a/mass_mailing_partner/wizard/partner_mail_list_wizard.py b/mass_mailing_partner/wizard/partner_mail_list_wizard.py new file mode 100644 index 00000000..0e6d67d0 --- /dev/null +++ b/mass_mailing_partner/wizard/partner_mail_list_wizard.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# See README.rst file on addon root folder for license details + +from openerp import models, api, fields, _ +from openerp.exceptions import Warning as UserError + + +class PartnerMailListWizard(models.TransientModel): + _name = "partner.mail.list.wizard" + _description = "Create contact mailing list" + + mail_list_id = fields.Many2one(comodel_name="mail.mass_mailing.list", + string="Mailing List") + partner_ids = fields.Many2many( + comodel_name="res.partner", relation="mail_list_wizard_partner", + default=lambda self: self.env.context.get("active_ids")) + + @api.multi + def add_to_mail_list(self): + contact_obj = self.env['mail.mass_mailing.contact'] + for partner in self.partner_ids: + if not partner.email: + raise UserError(_("Partner '%s' has no email.") % partner.name) + criteria = [('email', '=', partner.email), + ('list_id', '=', self.mail_list_id.id)] + contact_test = contact_obj.search(criteria) + if contact_test: + continue + contact_vals = { + 'email': partner.email, + 'name': partner.name, + 'list_id': self.mail_list_id.id + } + contact_obj.create(contact_vals) diff --git a/mass_mailing_partner/wizard/partner_mail_list_wizard.xml b/mass_mailing_partner/wizard/partner_mail_list_wizard.xml new file mode 100644 index 00000000..28125056 --- /dev/null +++ b/mass_mailing_partner/wizard/partner_mail_list_wizard.xml @@ -0,0 +1,32 @@ + + + + + + + + partner.mail.list.form + partner.mail.list.wizard + +
+ + + + +
+
+
+ +
+