install MailChimp Interface
|  | @ -0,0 +1,3 @@ | |||
| from . import models | ||||
| from . import wizard | ||||
| from . import controllers | ||||
|  | @ -0,0 +1,60 @@ | |||
| { | ||||
|     "name": "Odoo MailChimp Integration", | ||||
|     "version": "11.0.3", | ||||
|     "category": "Marketing", | ||||
|     'summary': 'Integrate & Manage MailChimp Operations from Odoo', | ||||
| 
 | ||||
|     "depends": ["mass_mailing"], | ||||
| 
 | ||||
|     'data': [ | ||||
|         'data/ir_cron.xml', | ||||
|         'views/assets.xml', | ||||
|         'views/mailchimp_accounts_view.xml', | ||||
|         'views/mailchimp_lists_view.xml', | ||||
|         'wizard/import_export_operation_view.xml', | ||||
|         'views/mass_mailing_contact_view.xml', | ||||
|         'views/mass_mailing_list_view.xml', | ||||
|         'views/mailchimp_template_view.xml', | ||||
|         'views/mass_mailing_view.xml', | ||||
|         'wizard/mass_mailing_schedule_date_view.xml', | ||||
|         'views/res_partner_views.xml', | ||||
|         'wizard/partner_export_update_wizard.xml', | ||||
|         'security/ir.model.access.csv' | ||||
|     ], | ||||
| 
 | ||||
|     'images': ['static/description/mailchimp_odoo.png'], | ||||
| 
 | ||||
|     "author": "Teqstars", | ||||
|     "website": "https://teqstars.com", | ||||
|     'support': 'info@teqstars.com', | ||||
|     'maintainer': 'Teqstars', | ||||
|     "description": """ | ||||
|         - Manage your MailChimp operation from Odoo | ||||
|         - Integration mailchimp | ||||
|         - Connector mailchimp | ||||
|         - mailchimp Connector | ||||
|         - Odoo mailchimp Connector | ||||
|         - mailchimp integration | ||||
|         - mailchimp odoo connector | ||||
|         - mailchimp odoo integration | ||||
|         - odoo mailchimp integration | ||||
|         - odoo integration with mailchimp | ||||
|         - odoo teqstars apps | ||||
|         - teqstars odoo apps | ||||
|         - manage audience | ||||
|         - manage champaign | ||||
|         - email Marketing | ||||
|         - mailchimp marketing | ||||
|         - odoo and mailchimp | ||||
|         """, | ||||
| 
 | ||||
|     'demo': [], | ||||
|     'license': 'OPL-1', | ||||
|     'live_test_url':'http://bit.ly/2n7ExKX', | ||||
|     'auto_install': False, | ||||
|     "installable": True, | ||||
|     'application': True, | ||||
|     'qweb': ['static/src/xml/shipstation_dashboard_template.xml'], | ||||
|     "price": "149.00", | ||||
|     "currency": "EUR", | ||||
| } | ||||
|  | @ -0,0 +1 @@ | |||
| from . import mailchimp | ||||
|  | @ -0,0 +1,75 @@ | |||
| from odoo.http import request | ||||
| from odoo import http | ||||
| import logging | ||||
| import hashlib | ||||
| 
 | ||||
| _logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| 
 | ||||
| class MailChimp(http.Controller): | ||||
| 
 | ||||
|     @http.route('/mailchimp/webhook/notification', type='http', auth="public", csrf=False) | ||||
|     def mailchimp_api(self, **kwargs): | ||||
|         # TODO : Work with multi databases. | ||||
|         contact_obj = request.env['mail.mass_mailing.contact'].sudo() | ||||
|         mass_mailling_obj = request.env['mail.mass_mailing'].sudo() | ||||
|         mailchimp_list_obj = request.env['mailchimp.lists'].sudo() | ||||
|         if kwargs.get('data[merges][EMAIL]', False): | ||||
|             email_address = kwargs['data[merges][EMAIL]'] | ||||
|             mailchimp_id = kwargs['data[web_id]'] | ||||
|             event = kwargs.get('type', False) | ||||
|             contact_id = contact_obj.search([('email', '=', email_address)]) | ||||
|             mailchimp_list_id = mailchimp_list_obj.search([('list_id', '=', kwargs['data[list_id]'])]) | ||||
|             name = "%s %s" % (kwargs.get('data[merges][FNAME]'), kwargs.get('data[merges][LNAME]')) | ||||
|             md5_email = hashlib.md5(email_address.encode('utf-8')).hexdigest() | ||||
|             merge_field_dict = {} | ||||
|             update_partner_required = True | ||||
|             for custom_field in mailchimp_list_id.merge_field_ids: | ||||
|                 tag = custom_field.tag | ||||
|                 if custom_field.type == 'address': | ||||
|                     address_dict = { | ||||
|                         'addr1': kwargs.get('data[merges][{}][addr1]'.format(tag), ''), | ||||
|                         'addr2': kwargs.get('data[merges][{}][addr2]'.format(tag), ''), | ||||
|                         'city': kwargs.get('data[merges][{}][city]'.format(tag), ''), | ||||
|                         'state': kwargs.get('data[merges][{}][state]'.format(tag), ''), | ||||
|                         'zip': kwargs.get('data[merges][{}][zip]'.format(tag), ''), | ||||
|                         'country': kwargs.get('data[merges][{}][country]'.format(tag), ''), | ||||
|                     } | ||||
|                     merge_field_dict.update({tag: address_dict}) | ||||
|                 else: | ||||
|                     merge_field_dict.update({tag: kwargs.get('data[merges][{}]'.format(tag), '')}) | ||||
|             tag_ids = contact_id.fetch_specific_member_data(mailchimp_list_id, md5_email) | ||||
|             prepared_vals_for_create_partner = mailchimp_list_id._prepare_vals_for_to_create_partner(merge_field_dict) | ||||
|             prepared_vals_for_create_partner.update({'category_id': [(6, 0, tag_ids.ids)]}) | ||||
|             if not contact_id: | ||||
|                 if prepared_vals_for_create_partner: | ||||
|                     mailchimp_list_id.update_partner_detail(name, email_address, prepared_vals_for_create_partner) | ||||
|                 update_partner_required = False | ||||
|                 contact_id = contact_id.create({'name': name, 'email': email_address, 'country_id': prepared_vals_for_create_partner.get('country_id', False) or False}) | ||||
|             if contact_id and kwargs.get('data[action]', '') != 'delete': | ||||
|                 if tag_ids or not tag_ids and contact_id.tag_ids: | ||||
|                     contact_id.write({'tag_ids': [(6, 0, tag_ids.ids)]}) | ||||
|                 if update_partner_required: | ||||
|                     mailchimp_list_id.update_partner_detail(name, email_address, prepared_vals_for_create_partner) | ||||
|                 vals = {'list_id': mailchimp_list_id.odoo_list_id.id, 'contact_id': contact_id.id, 'mailchimp_id': mailchimp_id, 'md5_email': md5_email} | ||||
|                 existing_define_list = contact_id.subscription_list_ids.filtered( | ||||
|                     lambda x: x.list_id.id == mailchimp_list_id.odoo_list_id.id) | ||||
|                 if existing_define_list: | ||||
|                     existing_define_list.write(vals) | ||||
|                 else: | ||||
|                     contact_id.subscription_list_ids.create(vals) | ||||
|             if event == 'unsubscribe': | ||||
|                 mass_mailling_obj.update_opt_out_ts(contact_id.email, mailchimp_list_id.odoo_list_id.ids, True) | ||||
|             elif event == 'subscribe': | ||||
|                 mass_mailling_obj.update_opt_out_ts(contact_id.email, mailchimp_list_id.odoo_list_id.ids, False) | ||||
|             elif event == 'profile': | ||||
|                 name = "%s %s" % (kwargs.get('data[merges][FNAME]'), kwargs.get('data[merges][LNAME]')) | ||||
|                 contact_id.write({'name': name, 'email': kwargs.get('data[merges][EMAIL]')}) | ||||
|             if kwargs.get('data[action]', '') == 'delete': | ||||
|                 if len(contact_id.list_ids) > 1: | ||||
|                     contact_id.write({'list_ids': [(3, mailchimp_list_id.odoo_list_id.id)]}) | ||||
|                 else: | ||||
|                     contact_id.unlink() | ||||
|             request._cr.commit() | ||||
|             return 'SUCCESS' | ||||
|         return 'FAILURE' | ||||
|  | @ -0,0 +1,26 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <odoo> | ||||
|     <data noupdate="1"> | ||||
|         <record id="fetch_email_activity" model="ir.cron"> | ||||
|             <field name="name">Do Not Delete : Fetch Campaigns Reports From MailChimp</field> | ||||
|             <field name="model_id" ref="mass_mailing.model_mail_mass_mailing"/> | ||||
|             <field name="state">code</field> | ||||
|             <field name="code">model.fetch_email_activity()</field> | ||||
|             <field name='interval_number'>1</field> | ||||
|             <field name='interval_type'>hours</field> | ||||
|             <field name="numbercall">-1</field> | ||||
|             <field name="active">1</field> | ||||
|         </record> | ||||
| 
 | ||||
|         <record id="fetch_member" model="ir.cron"> | ||||
|             <field name="name">Do Not Delete : Fetch MailChimp Audience</field> | ||||
|             <field name="model_id" ref="mailchimp.model_mailchimp_lists"/> | ||||
|             <field name="state">code</field> | ||||
|             <field name="code">model.fetch_member_cron()</field> | ||||
|             <field name='interval_number'>1</field> | ||||
|             <field name='interval_type'>hours</field> | ||||
|             <field name="numbercall">-1</field> | ||||
|             <field name="active">1</field> | ||||
|         </record> | ||||
|     </data> | ||||
| </odoo> | ||||
|  | @ -0,0 +1,13 @@ | |||
| from . import mailchimp_lists | ||||
| from . import mailchimp_accounts | ||||
| from . import mass_mailing_contact | ||||
| from . import mass_mailing_list | ||||
| from . import mailchimp_template | ||||
| from . import mass_mailing | ||||
| from . import mail_mail_statistics | ||||
| from . import mass_mailing_list_contact_rel | ||||
| from . import res_partner | ||||
| from . import ir_model | ||||
| from . import res_partner_category | ||||
| from . import mailchimp_segments | ||||
| from . import mailchimp_merge_fields | ||||
|  | @ -0,0 +1,96 @@ | |||
| import logging | ||||
| 
 | ||||
| from odoo import api, fields, models, SUPERUSER_ID, tools, _ | ||||
| from odoo.exceptions import AccessError, UserError, ValidationError | ||||
| 
 | ||||
| _logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| MODULE_UNINSTALL_FLAG = '_force_unlink' | ||||
| 
 | ||||
| 
 | ||||
| class IrModelData(models.Model): | ||||
|     _inherit = 'ir.model.data' | ||||
| 
 | ||||
|     @api.model | ||||
|     def _module_data_uninstall(self, modules_to_remove): | ||||
|         """Deletes all the records referenced by the ir.model.data entries | ||||
|         ``ids`` along with their corresponding database backed (including | ||||
|         dropping tables, columns, FKs, etc, as long as there is no other | ||||
|         ir.model.data entry holding a reference to them (which indicates that | ||||
|         they are still owned by another module). | ||||
|         Attempts to perform the deletion in an appropriate order to maximize | ||||
|         the chance of gracefully deleting all records. | ||||
|         This step is performed as part of the full uninstallation of a module. | ||||
|         """ | ||||
|         if not (self._uid == SUPERUSER_ID or self.env.user.has_group('base.group_system')): | ||||
|             raise AccessError(_('Administrator access is required to uninstall a module')) | ||||
| 
 | ||||
|         # enable model/field deletion | ||||
|         self = self.with_context(**{MODULE_UNINSTALL_FLAG: True}) | ||||
| 
 | ||||
|         datas = self.search([('module', 'in', modules_to_remove)]) | ||||
|         to_unlink = tools.OrderedSet() | ||||
|         undeletable = self.browse([]) | ||||
| 
 | ||||
|         for data in datas.sorted(key='id', reverse=True): | ||||
|             model = data.model | ||||
|             res_id = data.res_id | ||||
|             if data.model == 'ir.model' and 'mailchimp' in modules_to_remove: | ||||
|                 model_id = self.env[model].browse(res_id).with_context(prefetch_fields=False) | ||||
|                 if model_id.model == 'mail.mass_mailing.list_contact_rel': | ||||
|                     continue | ||||
|             if data.model == 'ir.model.fields' and 'mailchimp' in modules_to_remove: | ||||
|                 field_id = self.env[model].browse(res_id).with_context(prefetch_fields=False) | ||||
|                 if field_id.name in ['contact_id', 'list_id']: | ||||
|                     continue | ||||
|             to_unlink.add((model, res_id)) | ||||
| 
 | ||||
|         def unlink_if_refcount(to_unlink): | ||||
|             undeletable = self.browse() | ||||
|             for model, res_id in to_unlink: | ||||
|                 external_ids = self.search([('model', '=', model), ('res_id', '=', res_id)]) | ||||
|                 if external_ids - datas: | ||||
|                     # if other modules have defined this record, we must not delete it | ||||
|                     continue | ||||
|                 if model == 'ir.model.fields': | ||||
|                     # Don't remove the LOG_ACCESS_COLUMNS unless _log_access | ||||
|                     # has been turned off on the model. | ||||
|                     field = self.env[model].browse(res_id).with_context( | ||||
|                         prefetch_fields=False, | ||||
|                     ) | ||||
|                     if not field.exists(): | ||||
|                         _logger.info('Deleting orphan external_ids %s', external_ids) | ||||
|                         external_ids.unlink() | ||||
|                         continue | ||||
|                     if field.name in models.LOG_ACCESS_COLUMNS and field.model in self.env and self.env[field.model]._log_access: | ||||
|                         continue | ||||
|                     if field.name == 'id': | ||||
|                         continue | ||||
|                 _logger.info('Deleting %s@%s', res_id, model) | ||||
|                 try: | ||||
|                     self._cr.execute('SAVEPOINT record_unlink_save') | ||||
|                     self.env[model].browse(res_id).unlink() | ||||
|                 except Exception: | ||||
|                     _logger.info('Unable to delete %s@%s', res_id, model, exc_info=True) | ||||
|                     undeletable += external_ids | ||||
|                     self._cr.execute('ROLLBACK TO SAVEPOINT record_unlink_save') | ||||
|                 else: | ||||
|                     self._cr.execute('RELEASE SAVEPOINT record_unlink_save') | ||||
|             return undeletable | ||||
| 
 | ||||
|         # Remove non-model records first, then model fields, and finish with models | ||||
|         undeletable += unlink_if_refcount(item for item in to_unlink if item[0] not in ('ir.model', 'ir.model.fields', 'ir.model.constraint')) | ||||
|         undeletable += unlink_if_refcount(item for item in to_unlink if item[0] == 'ir.model.constraint') | ||||
| 
 | ||||
|         modules = self.env['ir.module.module'].search([('name', 'in', modules_to_remove)]) | ||||
|         constraints = self.env['ir.model.constraint'].search([('module', 'in', modules.ids)]) | ||||
|         constraints._module_data_uninstall() | ||||
| 
 | ||||
|         undeletable += unlink_if_refcount(item for item in to_unlink if item[0] == 'ir.model.fields') | ||||
| 
 | ||||
|         relations = self.env['ir.model.relation'].search([('module', 'in', modules.ids)]) | ||||
|         relations._module_data_uninstall() | ||||
| 
 | ||||
|         undeletable += unlink_if_refcount(item for item in to_unlink if item[0] == 'ir.model') | ||||
| 
 | ||||
|         (datas - undeletable).unlink() | ||||
|  | @ -0,0 +1,8 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| from odoo import api, fields, models | ||||
| 
 | ||||
| 
 | ||||
| class MailMailStats(models.Model): | ||||
|     _inherit = 'mail.mail.statistics' | ||||
| 
 | ||||
|     email = fields.Char(string="Recipient email address") | ||||
|  | @ -0,0 +1,93 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| import json | ||||
| import time | ||||
| import requests | ||||
| from email.utils import formataddr | ||||
| from odoo import api, fields, models, _ | ||||
| from odoo.tools.safe_eval import safe_eval | ||||
| from odoo.exceptions import ValidationError, Warning | ||||
| 
 | ||||
| 
 | ||||
| class MailChimpAccounts(models.Model): | ||||
|     _name = "mailchimp.accounts" | ||||
| 
 | ||||
|     name = fields.Char("Name", required=True, copy=False, help="Name of your MailChimp account") | ||||
| 
 | ||||
|     # Authentication | ||||
|     api_key = fields.Char('API Key', required=True, copy=False) | ||||
|     auto_refresh_member = fields.Boolean("Auto Sync Member?", copy=False, default=True) | ||||
|     auto_create_member = fields.Boolean("Auto Create Member?", copy=False, default=True) | ||||
|     auto_create_partner = fields.Boolean("Auto Create Customer?", copy=False, default=False) | ||||
|     list_ids = fields.One2many('mailchimp.lists', 'account_id', string="Lists/Audience", copy=False) | ||||
|     campaign_ids = fields.One2many('mail.mass_mailing', 'mailchimp_account_id', string="Campaigns", copy=False) | ||||
| 
 | ||||
|     _sql_constraints = [ | ||||
|         ('api_keys_uniq', 'unique(api_key)', 'API keys must be unique per MailChimp Account!'), | ||||
|     ] | ||||
| 
 | ||||
|     @api.model | ||||
|     def _send_request(self, request_url, request_data, params=False, method='GET'): | ||||
|         if '-' not in self.api_key: | ||||
|             raise ValidationError(_("MailChimp API key is invalid!")) | ||||
|         if len(self.api_key.split('-')) > 2: | ||||
|             raise ValidationError(_("MailChimp API key is invalid!")) | ||||
| 
 | ||||
|         api_key, dc = self.api_key.split('-') | ||||
|         headers = { | ||||
|             'Content-Type': 'application/json' | ||||
|         } | ||||
|         data = json.dumps(request_data) | ||||
|         api_url = "https://{dc}.api.mailchimp.com/3.0/{url}".format(dc=dc, url=request_url) | ||||
|         try: | ||||
|             req = requests.request(method, api_url, auth=('apikey', api_key), headers=headers, params=params, data=data) | ||||
|             req.raise_for_status() | ||||
|             response_text = req.text | ||||
|         except requests.HTTPError as e: | ||||
|             raise Warning("%s" % req.text) | ||||
|         response = json.loads(response_text) if response_text else {} | ||||
|         return response | ||||
| 
 | ||||
|     @api.multi | ||||
|     def get_refresh_member_action(self): | ||||
|         action = self.env.ref('base.ir_cron_act').read()[0] | ||||
|         refresh_member_cron = self.env.ref('mailchimp.fetch_member') | ||||
|         if refresh_member_cron: | ||||
|             action['views'] = [(False, 'form')] | ||||
|             action['res_id'] = refresh_member_cron.id | ||||
|         else: | ||||
|             raise ValidationError(_("Scheduled action isn't found! Please upgrade app to get it back!")) | ||||
|         return action | ||||
| 
 | ||||
|     def covert_date(self, value): | ||||
|         before_date = value[:19] | ||||
|         coverted_date = time.strptime(before_date, "%Y-%m-%dT%H:%M:%S") | ||||
|         final_date = time.strftime("%Y-%m-%d %H:%M:%S", coverted_date) | ||||
|         return final_date | ||||
| 
 | ||||
|     @api.multi | ||||
|     def import_lists(self): | ||||
|         mailchimp_lists = self.env['mailchimp.lists'] | ||||
|         for account in self: | ||||
|             mailchimp_lists.import_lists(account) | ||||
|         return True | ||||
| 
 | ||||
|     @api.multi | ||||
|     def import_templates(self): | ||||
|         mailchimp_templates = self.env['mailchimp.templates'] | ||||
|         for account in self: | ||||
|             mailchimp_templates.import_templates(account) | ||||
|         return True | ||||
| 
 | ||||
|     @api.multi | ||||
|     def import_campaigns(self): | ||||
|         mass_mailing_obj = self.env['mail.mass_mailing'] | ||||
|         for account in self: | ||||
|             mass_mailing_obj.import_campaigns(account) | ||||
|         return True | ||||
| 
 | ||||
|     @api.one | ||||
|     def test_connection(self): | ||||
|         response = self._send_request('lists', {}) | ||||
|         if response: | ||||
|             raise Warning("Test Connection Succeeded") | ||||
|         return True | ||||
|  | @ -0,0 +1,541 @@ | |||
| import logging | ||||
| import hashlib | ||||
| from datetime import datetime | ||||
| 
 | ||||
| _logger = logging.getLogger(__name__) | ||||
| from odoo import api, fields, models, _ | ||||
| from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT | ||||
| 
 | ||||
| EMAIL_PATTERN = '([^ ,;<@]+@[^> ,;]+)' | ||||
| unwanted_data = ['_links', 'modules'] | ||||
| replacement_of_key = [('id', 'list_id')] | ||||
| DATE_CONVERSION = ['date_created', 'last_sub_date', 'last_unsub_date', 'campaign_last_sent'] | ||||
| 
 | ||||
| NOT_REQUIRED_ON_UPDATE = ['color', 'list_id', 'web_id', 'from_name', 'from_email', 'subject', 'partner_id', | ||||
|                           'account_id', | ||||
|                           'date_created', 'list_rating', | ||||
|                           'subscribe_url_short', 'subscribe_url_long', 'beamer_address', 'id', 'display_name', | ||||
|                           'create_uid', 'create_date', 'write_uid', 'write_date', '__last_update', '__last_update', | ||||
|                           'statistics_ids', 'stats_overview_ids', 'stats_audience_perf_ids', 'stats_campaign_perf_ids', | ||||
|                           'stats_since_last_campaign_ids', 'lang_id', 'odoo_list_id', 'last_create_update_date', | ||||
|                           'is_update_required', 'contact_ids', 'subscription_contact_ids', 'mailchimp_list_id'] | ||||
| 
 | ||||
| 
 | ||||
| class MassMailingList(models.Model): | ||||
|     _inherit = "mail.mass_mailing.list" | ||||
| 
 | ||||
|     def _compute_contact_nbr(self): | ||||
|         self.env.cr.execute(''' | ||||
|             select | ||||
|                 list_id, count(*) | ||||
|             from | ||||
|                 mail_mass_mailing_contact_list_rel r | ||||
|                 left join mail_mass_mailing_contact c on (r.contact_id=c.id) | ||||
|             where | ||||
|                 c.opt_out <> true | ||||
|                 AND c.is_email_valid = TRUE   | ||||
|                 AND c.email IS NOT NULL               | ||||
|             group by | ||||
|                 list_id | ||||
|         ''') | ||||
|         data = dict(self.env.cr.fetchall()) | ||||
|         for mailing_list in self: | ||||
|             mailing_list.contact_nbr = data.get(mailing_list.id, 0) | ||||
| 
 | ||||
|     contact_nbr = fields.Integer(compute="_compute_contact_nbr", string='Number of Contacts') | ||||
| 
 | ||||
| 
 | ||||
| class MailChimpLists(models.Model): | ||||
|     _name = "mailchimp.lists" | ||||
|     _inherits = {'mail.mass_mailing.list': 'odoo_list_id'} | ||||
|     _description = "MailChimp Audience" | ||||
| 
 | ||||
|     def _compute_contact_unsub_nbr(self): | ||||
|         self.env.cr.execute(''' | ||||
|             select | ||||
|                 list_id, count(*) | ||||
|             from | ||||
|                 mail_mass_mailing_contact_list_rel r | ||||
|                 left join mail_mass_mailing_contact c on (r.contact_id=c.id) | ||||
|             where | ||||
|                 COALESCE(c.opt_out,TRUE) = TRUE AND | ||||
|                 COALESCE(c.is_email_valid,TRUE) = TRUE | ||||
|                 AND c.email IS NOT NULL | ||||
|             group by | ||||
|                 list_id | ||||
|         ''') | ||||
|         data = dict(self.env.cr.fetchall()) | ||||
|         for mailing_list in self: | ||||
|             mailing_list.contact_unsub_nbr = data.get(mailing_list.odoo_list_id.id, 0) | ||||
| 
 | ||||
|     def _compute_contact_cleaned_nbr(self): | ||||
|         self.env.cr.execute(''' | ||||
|             select | ||||
|                 list_id, count(*) | ||||
|             from | ||||
|                 mail_mass_mailing_contact_list_rel r | ||||
|                 left join mail_mass_mailing_contact c on (r.contact_id=c.id) | ||||
|             where | ||||
|                 COALESCE(c.is_email_valid,FALSE) = FALSE | ||||
|                 AND c.email IS NOT NULL | ||||
|             group by | ||||
|                 list_id | ||||
|         ''') | ||||
|         data = dict(self.env.cr.fetchall()) | ||||
|         for mailing_list in self: | ||||
|             mailing_list.contact_cleaned_nbr = data.get(mailing_list.odoo_list_id.id, 0) | ||||
| 
 | ||||
|     def _compute_contact_total_nbr(self): | ||||
|         self.env.cr.execute(''' | ||||
|             select | ||||
|                 list_id, count(*) | ||||
|             from | ||||
|                 mail_mass_mailing_contact_list_rel r | ||||
|                 left join mail_mass_mailing_contact c on (r.contact_id=c.id) | ||||
|             where | ||||
|                 c.email IS NOT NULL | ||||
|             group by | ||||
|                 list_id | ||||
|         ''') | ||||
|         data = dict(self.env.cr.fetchall()) | ||||
|         for mailing_list in self: | ||||
|             mailing_list.contact_total_nbr = data.get(mailing_list.odoo_list_id.id, 0) | ||||
| 
 | ||||
|     def _is_update_required(self): | ||||
|         for record in self: | ||||
|             if record.write_date and record.last_create_update_date and record.write_date > record.last_create_update_date: | ||||
|                 record.is_update_required = True | ||||
|             else: | ||||
|                 record.is_update_required = False | ||||
| 
 | ||||
|     # name = fields.Char("Name", required=True, help="This is how the list will be named in MailChimp.") | ||||
|     color = fields.Integer('Color Index', default=0) | ||||
|     list_id = fields.Char("Audience ID", copy=False, readonly=True) | ||||
|     web_id = fields.Char("Website Identification", readonly=True) | ||||
|     partner_id = fields.Many2one('res.partner', string="Contact", ondelete='restrict') | ||||
|     permission_reminder = fields.Text("Permission Reminder", help="Remind recipients how they signed up to your list.") | ||||
|     use_archive_bar = fields.Boolean("Use Archive Bar", | ||||
|                                      help="Whether campaigns for this list use the Archive Bar in archives by default.") | ||||
| 
 | ||||
|     notify_on_subscribe = fields.Char("Email Subscribe Notifications To", | ||||
|                                       help="The email address to send subscribe notifications to. \n Additional email addresses must be separated by a comma.") | ||||
|     notify_on_unsubscribe = fields.Char("Email Unsubscribe Notifications To", | ||||
|                                         help="The email address to send unsubscribe notifications to. \n Additional email addresses must be separated by a comma.") | ||||
|     date_created = fields.Datetime("Creation Date", readonly=True) | ||||
|     list_rating = fields.Selection([('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5')], | ||||
|                                    "List Rating") | ||||
|     email_type_option = fields.Boolean("Let users pick plain-text or HTML emails.", | ||||
|                                        help="Whether the list suports multiple formats for emails. When set to true, subscribers can choose whether they want to receive HTML or plain-text emails. When set to false, subscribers will receive HTML emails, with a plain-text alternative backup.") | ||||
|     subscribe_url_short = fields.Char("Subscribe URL Short", readonly=True) | ||||
|     subscribe_url_long = fields.Char("Subscribe URL Long", readonly=True, | ||||
|                                      help="The full version of this list’s subscribe form (host will vary).") | ||||
|     beamer_address = fields.Char("Beamer Address", readonly=True) | ||||
|     visibility = fields.Selection([('pub', 'Yes. My campaigns are public, and I want them to be discovered.'), | ||||
|                                    ('prv', 'No, my campaigns for this list are not public.')], | ||||
|                                   help="Whether this list is public or private.") | ||||
|     double_optin = fields.Boolean("Enable double opt-in", | ||||
|                                   help="Whether or not to require the subscriber to confirm subscription via email.") | ||||
|     has_welcome = fields.Boolean("Send a Final Welcome Email", | ||||
|                                  help="Whether or not this list has a welcome automation connected. Welcome Automations: welcomeSeries, singleWelcome, emailFollowup.") | ||||
|     marketing_permissions = fields.Boolean("Enable GDPR fields", | ||||
|                                            help="Whether or not the list has marketing permissions (eg. GDPR) enabled.") | ||||
|     from_name = fields.Char("Default From Name", required=True) | ||||
|     from_email = fields.Char("Default From Email Address", required=True) | ||||
|     subject = fields.Char("Default Email Subject") | ||||
|     lang_id = fields.Many2one('res.lang', string="Language") | ||||
| 
 | ||||
|     odoo_list_id = fields.Many2one('mail.mass_mailing.list', string='Odoo Mailing List', required=True, | ||||
|                                    ondelete="cascade") | ||||
|     contact_unsub_nbr = fields.Integer(compute="_compute_contact_unsub_nbr", string='Number of Unsubscribed Contacts') | ||||
|     contact_cleaned_nbr = fields.Integer(compute="_compute_contact_cleaned_nbr", string='Number of Cleaned Contacts') | ||||
|     contact_total_nbr = fields.Integer(compute="_compute_contact_total_nbr", string='Number of Total Contacts') | ||||
|     statistics_ids = fields.One2many('mailchimp.lists.stats', 'list_id', string="Statistics", | ||||
|                                      help="Stats for the list. Many of these are cached for at least five minutes.") | ||||
|     stats_overview_ids = fields.One2many('mailchimp.lists.stats', 'list_id', string="Statistics", | ||||
|                                          help="Stats for the list. Many of these are cached for at least five minutes.") | ||||
|     stats_audience_perf_ids = fields.One2many('mailchimp.lists.stats', 'list_id', string="Statistics", | ||||
|                                               help="Stats for the list. Many of these are cached for at least five minutes.") | ||||
|     stats_campaign_perf_ids = fields.One2many('mailchimp.lists.stats', 'list_id', string="Statistics", | ||||
|                                               help="Stats for the list. Many of these are cached for at least five minutes.") | ||||
|     stats_since_last_campaign_ids = fields.One2many('mailchimp.lists.stats', 'list_id', string="Statistics", | ||||
|                                                     help="Stats for the list. Many of these are cached for at least five minutes.") | ||||
|     account_id = fields.Many2one("mailchimp.accounts", string="Account", required=True) | ||||
|     last_create_update_date = fields.Datetime("Last Create Update") | ||||
|     write_date = fields.Datetime('Update on', index=True, readonly=True) | ||||
|     is_update_required = fields.Boolean("Update Required?", compute="_is_update_required") | ||||
|     member_since_last_changed = fields.Datetime("Fetch Member Since Last Change", copy=False) | ||||
|     segment_ids = fields.One2many("mailchimp.segments", 'list_id', string="Segments", copy=False) | ||||
|     merge_field_ids = fields.One2many("mailchimp.merge.fields", 'list_id', string="Merge Fields", copy=False) | ||||
| 
 | ||||
|     @api.multi | ||||
|     def unlink(self): | ||||
|         odoo_lists = self.mapped('odoo_list_id') | ||||
|         super(MailChimpLists, self).unlink() | ||||
|         return odoo_lists.unlink() | ||||
| 
 | ||||
|     def action_view_recipients(self): | ||||
|         action = self.env.ref('mass_mailing.action_view_mass_mailing_contacts_from_list').read()[0] | ||||
|         action['domain'] = [('list_ids', 'in', self.odoo_list_id.ids)] | ||||
|         ctx = {'default_list_ids': [self.odoo_list_id.id]} | ||||
|         if self.env.context.get('show_total', False): | ||||
|             action['context'] = ctx | ||||
|         if self.env.context.get('show_sub', False): | ||||
|             ctx.update({'search_default_not_opt_out': 1}) | ||||
|             action['context'] = ctx | ||||
|         if self.env.context.get('show_unsub', False): | ||||
|             ctx.update({'search_default_unsub_contact': 1}) | ||||
|             action['context'] = ctx | ||||
|         if self.env.context.get('show_cleaned', False): | ||||
|             ctx.update({'search_default_cleaned_contact': 1}) | ||||
|             action['context'] = ctx | ||||
|         return action | ||||
| 
 | ||||
|     @api.model | ||||
|     def _prepare_vals_for_update(self): | ||||
|         self.ensure_one() | ||||
|         prepared_vals = {} | ||||
|         for field_name in self.fields_get_keys(): | ||||
|             if hasattr(self, field_name) and field_name not in NOT_REQUIRED_ON_UPDATE: | ||||
|                 prepared_vals.update({field_name: getattr(self, field_name)}) | ||||
|         partner_id = self.partner_id | ||||
|         prepared_vals['contact'] = {'company': partner_id.name, 'address1': partner_id.street, | ||||
|                                     'address2': partner_id.street2, 'city': partner_id.city, | ||||
|                                     'state': partner_id.state_id.name or '', 'zip': partner_id.zip, | ||||
|                                     'country': partner_id.country_id.code, 'phone': partner_id.phone} | ||||
|         prepared_vals['campaign_defaults'] = {'from_name': self.from_name, 'from_email': self.from_email, | ||||
|                                               'subject': self.subject or '', 'language': self.lang_id.iso_code or ''} | ||||
|         return prepared_vals | ||||
| 
 | ||||
|     @api.multi | ||||
|     def export_in_mailchimp(self): | ||||
|         for list in self: | ||||
|             prepared_vals = list._prepare_vals_for_update() | ||||
|             response = list.account_id._send_request('lists', prepared_vals, method='POST') | ||||
|         return True | ||||
| 
 | ||||
|     @api.multi | ||||
|     def update_in_mailchimp(self): | ||||
|         for list in self: | ||||
|             prepared_vals = list._prepare_vals_for_update() | ||||
|             response = list.account_id._send_request('lists/%s' % list.list_id, prepared_vals, method='PATCH') | ||||
|             list.write({'last_create_update_date': fields.Datetime.now()}) | ||||
|         return True | ||||
| 
 | ||||
|     @api.model | ||||
|     def _find_partner(self, location): | ||||
|         partners = self.env['res.partner'] | ||||
|         state = self.env['res.country.state'] | ||||
|         domain = [] | ||||
|         if 'address1' in location and 'city' in location and 'company' in location: | ||||
|             domain.append(('name', '=', location['company'])) | ||||
|             domain.append(('street', '=', location['address1'])) | ||||
|             domain.append(('city', '=', location['city'])) | ||||
|             if location.get('state'): | ||||
|                 domain.append(('state_id.name', '=', location['state'])) | ||||
|             if location.get('zip'): | ||||
|                 domain.append(('zip', '=', location['zip'])) | ||||
|             partners = partners.search(domain, limit=1) | ||||
|         if not partners: | ||||
|             country_id = self.env['res.country'].search([('code', '=', location['country'])], limit=1) | ||||
|             if country_id and location['state']: | ||||
|                 state = self.env['res.country.state'].search( | ||||
|                     ['|', ('name', '=', location['state']), ('code', '=', location['state']), | ||||
|                      ('country_id', '=', country_id.id)], limit=1) | ||||
|             elif location['state']: | ||||
|                 state = self.env['res.country.state'].search( | ||||
|                     ['|', ('name', '=', location['state']), ('code', '=', location['state'])], | ||||
|                     limit=1) | ||||
|             location.update({'name': location.pop('company'), 'street': location.pop('address1'), | ||||
|                              'street2': location.pop('address2'), 'state_id': state.id, 'country_id': country_id.id}) | ||||
|             partners = partners.create(location) | ||||
|         return partners | ||||
| 
 | ||||
|     @api.multi | ||||
|     def create_or_update_list(self, values_dict, account=False): | ||||
|         list_id = values_dict.get('id') | ||||
|         existing_list = self.search([('list_id', '=', list_id)]) | ||||
|         stats = values_dict.pop('stats', {}) | ||||
|         values_dict.update(values_dict.pop('campaign_defaults')) | ||||
|         lang_id = self.env['res.lang'].search([('iso_code', '=', values_dict.get('language', 'en'))]) | ||||
|         for item in unwanted_data: | ||||
|             values_dict.pop(item) | ||||
|         for old_key, new_key in replacement_of_key: | ||||
|             values_dict[new_key] = values_dict.pop(old_key) | ||||
|         for item in DATE_CONVERSION: | ||||
|             if values_dict.get(item, False) == '': | ||||
|                 values_dict[item] = False | ||||
|             if values_dict.get(item, False): | ||||
|                 values_dict[item] = account.covert_date(values_dict.get(item)) | ||||
|         values_dict.update({'account_id': account.id}) | ||||
|         partner = self._find_partner(values_dict.pop('contact')) | ||||
|         values_dict.update( | ||||
|             {'partner_id': partner.id, 'lang_id': lang_id.id, 'list_rating': str(values_dict.pop('list_rating', '0'))}) | ||||
|         if not existing_list: | ||||
|             existing_list = self.create(values_dict) | ||||
|         else: | ||||
|             existing_list.write(values_dict) | ||||
|         existing_list.create_or_update_statistics(stats) | ||||
|         # existing_list.fetch_members() | ||||
|         existing_list.fetch_segments() | ||||
|         existing_list.fetch_merge_fields() | ||||
|         existing_list.write({'last_create_update_date': fields.Datetime.now()}) | ||||
|         return True | ||||
| 
 | ||||
|     @api.multi | ||||
|     def import_lists(self, account=False): | ||||
|         if not account: | ||||
|             raise Warning("MailChimp Account not defined to import lists") | ||||
|         response = account._send_request('lists', {}, method='GET') | ||||
|         for list in response.get('lists'): | ||||
|             self.create_or_update_list(list, account=account) | ||||
|         return True | ||||
| 
 | ||||
|     @api.one | ||||
|     def refresh_list(self): | ||||
|         if not self.account_id: | ||||
|             raise Warning("MailChimp Account not defined to Refresh list") | ||||
|         response = self.account_id._send_request('lists/%s' % self.list_id, {}) | ||||
|         self.create_or_update_list(response, account=self.account_id) | ||||
|         return True | ||||
| 
 | ||||
|     @api.multi | ||||
|     def create_or_update_statistics(self, stats): | ||||
|         self.ensure_one() | ||||
|         self.statistics_ids.unlink() | ||||
|         for item in DATE_CONVERSION: | ||||
|             if stats.get(item, False): | ||||
|                 stats[item] = self.account_id.covert_date(stats.get(item)) | ||||
|             else: | ||||
|                 stats[item] = False | ||||
|         self.write({'statistics_ids': [(0, 0, stats)]}) | ||||
|         return True | ||||
| 
 | ||||
|     @api.one | ||||
|     def fetch_merge_fields(self): | ||||
|         mailchimp_merge_field_obj = self.env['mailchimp.merge.fields'] | ||||
|         if not self.account_id: | ||||
|             raise Warning("MailChimp Account not defined to Fetch Merge Field list") | ||||
|         count = 1000 | ||||
|         offset = 0 | ||||
|         merge_field_list = [] | ||||
|         prepared_vals = {} | ||||
|         while True: | ||||
|             prepared_vals.update({'count': count, 'offset': offset, | ||||
|                                   'fields': 'merge_fields.merge_id,merge_fields.tag,merge_fields.name,merge_fields.type,merge_fields.required,merge_fields.default_value,merge_fields.public,merge_fields.display_order,merge_fields.list_id,merge_fields.options'}) | ||||
|             response = self.account_id._send_request('lists/%s/merge-fields' % self.list_id, {}, params=prepared_vals) | ||||
|             if len(response.get('merge_fields')) == 0: | ||||
|                 break | ||||
|             if isinstance(response.get('merge_fields'), dict): | ||||
|                 merge_field_list = [response.get('merge_fields')] | ||||
|             else: | ||||
|                 merge_field_list += response.get('merge_fields') | ||||
|             offset = offset + 1000 | ||||
|         for merge_field in merge_field_list: | ||||
|             if not merge_field.get('merge_id', False): | ||||
|                 continue | ||||
|             merge_field_id = mailchimp_merge_field_obj.search([('merge_id', '=', merge_field.get('merge_id')), ('list_id', '=', self.id)]) | ||||
|             merge_field.update({'list_id': self.id}) | ||||
|             if merge_field.get('options', {}).get('date_format'): | ||||
|                 date_format = merge_field.pop('options', {}).get('date_format') | ||||
|                 date_format = date_format.replace("MM", '%m') | ||||
|                 date_format = date_format.replace("DD", '%d') | ||||
|                 date_format = date_format.replace("YYYY", '%Y') | ||||
|                 merge_field.update({'date_format': date_format}) | ||||
|             if not merge_field_id: | ||||
|                 mailchimp_merge_field_obj.create(merge_field) | ||||
|             if merge_field_id: | ||||
|                 merge_field_id.write(merge_field) | ||||
|         return merge_field_list | ||||
| 
 | ||||
|     @api.one | ||||
|     def fetch_segments(self): | ||||
|         mailchimp_segments_obj = self.env['mailchimp.segments'] | ||||
|         if not self.account_id: | ||||
|             raise Warning("MailChimp Account not defined to Fetch Segments list") | ||||
|         count = 1000 | ||||
|         offset = 0 | ||||
|         segments_list = [] | ||||
|         prepared_vals = {} | ||||
|         while True: | ||||
|             prepared_vals.update({'count': count, 'offset': offset}) | ||||
|             response = self.account_id._send_request('lists/%s/segments' % self.list_id, {}, params=prepared_vals) | ||||
|             if len(response.get('segments')) == 0: | ||||
|                 break | ||||
|             if isinstance(response.get('segments'), dict): | ||||
|                 segments_list = [response.get('segments')] | ||||
|             else: | ||||
|                 segments_list += response.get('segments') | ||||
|             offset = offset + 1000 | ||||
|         for segment in segments_list: | ||||
|             if not segment.get('id', False): | ||||
|                 continue | ||||
|             segment_id = mailchimp_segments_obj.search([('mailchimp_id', '=', segment.get('id'))]) | ||||
|             name = segment.get('name') | ||||
|             if segment.get('type') == 'static': | ||||
|                 name = "Tags : %s" % name | ||||
|             vals = {'mailchimp_id': segment.get('id'), 'name': name, 'list_id': self.id} | ||||
|             if not segment_id: | ||||
|                 mailchimp_segments_obj.create(vals) | ||||
|             if segment_id: | ||||
|                 segment_id.write(vals) | ||||
|         return segments_list | ||||
| 
 | ||||
|     def _prepare_vals_for_to_create_partner(self, merge_field_vals): | ||||
|         prepared_vals = {} | ||||
|         for custom_field in self.merge_field_ids: | ||||
|             if custom_field.type == 'address': | ||||
|                 address_dict = merge_field_vals.get(custom_field.tag) | ||||
|                 if not address_dict: | ||||
|                     continue | ||||
|                 state_id = False | ||||
|                 country = self.env['res.country'].search([('code', '=', address_dict.get('country', ''))], limit=1) | ||||
|                 if country: | ||||
|                     state_id = self.env['res.country.state'].search( | ||||
|                         ['|', ('name', '=', address_dict['state']), | ||||
|                          ('code', '=', address_dict['state']), | ||||
|                          ('country_id', '=', country.id)], limit=1) | ||||
|                 prepared_vals.update({ | ||||
|                     'street': address_dict.get('addr1', ''), | ||||
|                     'street2': address_dict.get('addr2', ''), | ||||
|                     'city': address_dict.get('city', ''), | ||||
|                     'zip': address_dict.get('zip', ''), | ||||
|                     'state_id': state_id.id if state_id else False, | ||||
|                     'country_id': country.id, | ||||
|                 }) | ||||
|             elif custom_field.tag in ['FNAME', 'LNAME'] and not prepared_vals.get('name', False): | ||||
|                 prepared_vals.update({'name': "%s %s" % (merge_field_vals.get('FNAME'), merge_field_vals.get('LNAME'))}) | ||||
|             elif custom_field.field_id and custom_field.type in ['date', 'birthday']: | ||||
|                 value = merge_field_vals.get(custom_field.tag) | ||||
|                 if value and custom_field.field_id.ttype in ['date']: | ||||
|                     if len(value.split('/')) > 1: | ||||
|                         value = datetime.strptime(value, custom_field.date_format).strftime(DEFAULT_SERVER_DATE_FORMAT) | ||||
|                 prepared_vals.update({custom_field.field_id.name: value or False}) | ||||
|             elif custom_field.field_id: | ||||
|                 prepared_vals.update({custom_field.field_id.name: merge_field_vals.get(custom_field.tag)}) | ||||
|         return prepared_vals | ||||
| 
 | ||||
|     @api.one | ||||
|     def fetch_members(self): | ||||
|         mailing_contact_obj = self.env['mail.mass_mailing.contact'] | ||||
|         state = self.env['res.country.state'] | ||||
|         country = self.env['res.country'] | ||||
|         if not self.account_id: | ||||
|             raise Warning("MailChimp Account not defined to Fetch Member list") | ||||
|         if not self.merge_field_ids: | ||||
|             return True | ||||
|         count = 1000 | ||||
|         offset = 0 | ||||
|         members_list = [] | ||||
|         members_list_to_create = [] | ||||
|         prepared_vals = {} | ||||
|         if self.member_since_last_changed: | ||||
|             prepared_vals.update({'since_last_changed': datetime.strptime(self.member_since_last_changed, DEFAULT_SERVER_DATETIME_FORMAT).strftime("%Y-%m-%dT%H:%M:%S+00:00")}) | ||||
|         while True: | ||||
|             prepared_vals.update({'count': count, 'offset': offset, 'fields': 'members.email_address,members.merge_fields,members.tags,members.web_id,members.status'}) | ||||
|             response = self.account_id._send_request('lists/%s/members' % self.list_id, {}, params=prepared_vals) | ||||
|             if len(response.get('members')) == 0: | ||||
|                 break | ||||
|             # if isinstance(response.get('members'), dict): | ||||
|             #     members_list += [response.get('members')] | ||||
|             if isinstance(response.get('members'), dict): | ||||
|                 members_data = [response.get('members')] | ||||
|             else: | ||||
|                 members_data = response.get('members') | ||||
|             offset = offset + 1000 | ||||
|             for member in members_data: | ||||
|                 if not member.get('email_address', False): | ||||
|                     continue | ||||
|                 update_partner_required = True | ||||
|                 contact_id = mailing_contact_obj.search([('email', '=', member.get('email_address'))]) | ||||
|                 create_vals = member.get('merge_fields') | ||||
|                 prepared_vals_for_create_partner = self._prepare_vals_for_to_create_partner(create_vals) | ||||
|                 name = "%s %s" % (create_vals.pop('FNAME'), create_vals.pop('LNAME')) | ||||
|                 tag_ids = self.env['res.partner.category'] | ||||
|                 tag_list = member.get('tags') | ||||
|                 if tag_list: | ||||
|                     tag_ids = self.env['res.partner.category'].create_or_update_tags(tag_list) | ||||
|                 prepared_vals_for_create_partner.update({'category_id': [(6, 0, tag_ids.ids)]}) | ||||
|                 if not contact_id: | ||||
|                     if not self.account_id.auto_create_member: | ||||
|                         continue | ||||
|                     self.update_partner_detail(name, member.get('email_address'), prepared_vals_for_create_partner) | ||||
|                     update_partner_required = False | ||||
|                     contact_id = mailing_contact_obj.create( | ||||
|                         {'name': name, 'email': member.get('email_address'), | ||||
|                          'country_id': prepared_vals_for_create_partner.get('country_id', False) or False}) | ||||
|                 if contact_id: | ||||
|                     md5_email = hashlib.md5(member.get('email_address').encode('utf-8')).hexdigest() | ||||
|                     vals = {'list_id': self.odoo_list_id.id, 'contact_id': contact_id.id, | ||||
|                             'mailchimp_id': member.get('web_id'), 'md5_email': md5_email} | ||||
|                     status = member.get('status', '') | ||||
|                     if update_partner_required: | ||||
|                         self.update_partner_detail(name, member.get('email_address'), prepared_vals_for_create_partner) | ||||
|                     contact_vals = {'opt_out': True} if status == 'unsubscribed' else {'opt_out': False} | ||||
|                     if status == 'cleaned': | ||||
|                         contact_vals.update({'is_email_valid': False, 'opt_out': True}) | ||||
|                     contact_vals.update({'tag_ids': [(6, 0, tag_ids.ids)]}) | ||||
|                     contact_id.write(contact_vals) | ||||
|                     existing_define_list = contact_id.subscription_list_ids.filtered( | ||||
|                         lambda x: x.list_id.id == self.odoo_list_id.id) | ||||
|                     if existing_define_list: | ||||
|                         existing_define_list.write(vals) | ||||
|                     else: | ||||
|                         contact_id.subscription_list_ids.create(vals) | ||||
|             self._cr.commit() | ||||
|         self.write({'member_since_last_changed': fields.Datetime.now()}) | ||||
|         return members_list | ||||
| 
 | ||||
|     @api.multi | ||||
|     def update_partner_detail(self, name, email, partner_detail): | ||||
|         query = """ | ||||
|                 SELECT id  | ||||
|                   FROM res_partner | ||||
|                 WHERE LOWER(substring(email, '([^ ,;<@]+@[^> ,;]+)')) = LOWER(substring('{}', '([^ ,;<@]+@[^> ,;]+)'))""".format(email) | ||||
|         self._cr.execute(query) | ||||
|         partner_id = self._cr.fetchone() | ||||
|         partner_id = partner_id[0] if partner_id else False | ||||
|         if partner_id: | ||||
|             partner_id = self.env['res.partner'].browse(partner_id) | ||||
|             if partner_detail: | ||||
|                 partner_id.write(partner_detail) | ||||
|         else: | ||||
|             if self.account_id.auto_create_member and self.account_id.auto_create_partner: | ||||
|                 partner_detail.update({ | ||||
|                     'email': email, | ||||
|                     'is_company': False, | ||||
|                     'type': 'contact', | ||||
|                 }) | ||||
|                 self.env['res.partner'].create(partner_detail) | ||||
|         return True | ||||
| 
 | ||||
|     @api.model | ||||
|     def fetch_member_cron(self): | ||||
|         account_obj = self.env['mailchimp.accounts'] | ||||
|         for record in account_obj.search([('auto_refresh_member', '=', True)]): | ||||
|             list_ids = self.search([('account_id', '=', record.id)]) | ||||
|             for list in list_ids: | ||||
|                 list.fetch_members() | ||||
|         return True | ||||
| 
 | ||||
| 
 | ||||
| class MailChimpListsStats(models.Model): | ||||
|     _name = "mailchimp.lists.stats" | ||||
|     _description = "MailChimp Statistics" | ||||
| 
 | ||||
|     list_id = fields.Many2one("mailchimp.lists", string="MailChimp List", required=True, ondelete='cascade') | ||||
|     member_count = fields.Integer("Subscribed Count") | ||||
|     unsubscribe_count = fields.Integer("Unsubscribe Count") | ||||
|     cleaned_count = fields.Integer("Cleaned Count") | ||||
|     member_count_since_send = fields.Integer("Subscribed Count") | ||||
|     unsubscribe_count_since_send = fields.Integer("Unsubscribe Count") | ||||
|     cleaned_count_since_send = fields.Integer("Cleaned Count") | ||||
|     campaign_count = fields.Integer("Campaign Count") | ||||
|     campaign_last_sent = fields.Datetime("Campaign Last Sent") | ||||
|     merge_field_count = fields.Integer("Merge Count") | ||||
|     avg_sub_rate = fields.Float("Average Subscription Rate") | ||||
|     avg_unsub_rate = fields.Float("Average Unsubscription Rate") | ||||
|     target_sub_rate = fields.Float("Average Subscription Rate") | ||||
|     open_rate = fields.Float("Open Rate") | ||||
|     click_rate = fields.Float("Click Rate") | ||||
|     last_sub_date = fields.Datetime("Date of Last Subscribe") | ||||
|     last_unsub_date = fields.Datetime("Date of Last Unsubscribe") | ||||
|  | @ -0,0 +1,23 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| from odoo import api, fields, models, _ | ||||
| 
 | ||||
| 
 | ||||
| class MailChimpMergeFields(models.Model): | ||||
|     _name = "mailchimp.merge.fields" | ||||
| 
 | ||||
|     name = fields.Char("Name", required=True, copy=False, help="Name of your MailChimp Merge Fields") | ||||
|     merge_id = fields.Char("Merge ID", readonly=True, copy=False) | ||||
|     tag = fields.Char("Merge Field Tag", help="The tag used in Mailchimp campaigns and for the /members endpoint.") | ||||
|     type = fields.Selection( | ||||
|         [('text', 'Text'), ('number', 'Number'), ('address', 'Address'), ('phone', 'Phone'), ('date', 'Date'), ('radio', 'Radio'), ('dropdown', 'Dropdown'), ('birthday', 'Birthday'), ('zip', 'Zip'), ('imageurl', 'ImageURL'), ('url', 'URL')]) | ||||
|     date_format = fields.Char('Date Format', copy=False) | ||||
|     required = fields.Boolean("Required?", copy=False, help="Merge field is required or not.") | ||||
|     public = fields.Boolean("Visible?", copy=False, help="Whether the merge field is displayed on the signup form.") | ||||
|     default_value = fields.Char("Default Value", help="The default value for the merge field if null.") | ||||
|     display_order = fields.Char("Display Order", help="The order that the merge field displays on the list signup form.") | ||||
|     list_id = fields.Many2one("mailchimp.lists", string="Associated MailChimp List", ondelete='cascade', required=True, copy=False) | ||||
|     field_id = fields.Many2one('ir.model.fields', string='Odoo Field', help="""Odoo will fill value of selected field while contact is going to export or update""", domain="[('model_id.model', '=', 'res.partner')]") | ||||
| 
 | ||||
|     _sql_constraints = [ | ||||
|         ('merge_id_list_id_uniq', 'unique(merge_id, list_id)', 'Merge ID must be unique per MailChimp Lists!'), | ||||
|     ] | ||||
|  | @ -0,0 +1,14 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| from odoo import api, fields, models, _ | ||||
| 
 | ||||
| 
 | ||||
| class MailChimpSegments(models.Model): | ||||
|     _name = "mailchimp.segments" | ||||
| 
 | ||||
|     name = fields.Char("Name", required=True, copy=False, help="Name of your MailChimp Segments") | ||||
|     mailchimp_id = fields.Char("MailChimp ID", readonly=True, copy=False) | ||||
|     list_id = fields.Many2one("mailchimp.lists", string="Associated MailChimp List", ondelete='cascade', required=True, copy=False) | ||||
| 
 | ||||
|     _sql_constraints = [ | ||||
|         ('mailchimp_id_uniq', 'unique(mailchimp_id)', 'MailChimp ID must be unique per MailChimp Segments!'), | ||||
|     ] | ||||
|  | @ -0,0 +1,64 @@ | |||
| from odoo import api, fields, models, _ | ||||
| 
 | ||||
| REPLACEMENT_OF_KEY = [('id', 'template_id')] | ||||
| DATE_CONVERSION = ['date_created', 'date_edited'] | ||||
| UNWANTED_DATA = ['_links', 'created_by', 'edited_by', 'thumbnail'] | ||||
| 
 | ||||
| 
 | ||||
| class MailChimpTemplates(models.Model): | ||||
|     _name = "mailchimp.templates" | ||||
|     _description = "Templates" | ||||
| 
 | ||||
|     name = fields.Char("Name", required=True, help="The name of the template.") | ||||
|     template_id = fields.Char("Template ID", copy=False) | ||||
|     type = fields.Selection([('user', 'User'), ('gallery', 'Gallery'), ('base', 'Base')], default='user', copy=False, | ||||
|                             help="The type of template (user, base, or gallery).") | ||||
|     drag_and_drop = fields.Boolean("Drag and Drop", help="Whether the template uses the drag and drop editor.") | ||||
|     responsive = fields.Boolean("Responsive", help="Whether the template contains media queries to make it responsive.") | ||||
|     category = fields.Char("Template Category", help="If available, the category the template is listed in.") | ||||
|     date_created = fields.Datetime("Created On") | ||||
|     date_edited = fields.Datetime("Edited On") | ||||
|     active = fields.Boolean("Active", default=True) | ||||
|     folder_id = fields.Char("Folder ID", help="The id of the folder the template is currently in.") | ||||
|     share_url = fields.Char("Share URL", help="The URL used for template sharing") | ||||
|     account_id = fields.Many2one("mailchimp.accounts", string="Account", required=True, ondelete='cascade') | ||||
| 
 | ||||
|     @api.multi | ||||
|     def create_or_update_template(self, values_dict, account=False): | ||||
|         template_id = values_dict.get('id') | ||||
|         existing_list = self.search([('template_id', '=', template_id)]) | ||||
|         for item in UNWANTED_DATA: | ||||
|             values_dict.pop(item) | ||||
|         for old_key, new_key in REPLACEMENT_OF_KEY: | ||||
|             values_dict[new_key] = values_dict.pop(old_key) | ||||
|         for item in DATE_CONVERSION: | ||||
|             if values_dict.get(item, False) == '': | ||||
|                 values_dict[item] = False | ||||
|             if values_dict.get(item, False): | ||||
|                 values_dict[item] = account.covert_date(values_dict.get(item)) | ||||
|         values_dict.update({'account_id': account.id}) | ||||
|         if not existing_list: | ||||
|             existing_list = self.create(values_dict) | ||||
|         else: | ||||
|             existing_list.write(values_dict) | ||||
|         return True | ||||
| 
 | ||||
|     @api.multi | ||||
|     def import_templates(self, account=False): | ||||
|         if not account: | ||||
|             raise Warning("MailChimp Account not defined to import templates") | ||||
|         count = 1000 | ||||
|         offset = 0 | ||||
|         template_list = [] | ||||
|         while True: | ||||
|             prepared_vals = {'count': count, 'offset': offset} | ||||
|             response = account._send_request('templates', {}, params=prepared_vals) | ||||
|             if len(response.get('templates')) == 0: | ||||
|                 break | ||||
|             if isinstance(response.get('templates'), dict): | ||||
|                 template_list += [response.get('templates')] | ||||
|             template_list += response.get('templates') | ||||
|             offset = offset + 1000 | ||||
|         for template_dict in template_list: | ||||
|             self.create_or_update_template(template_dict, account=account) | ||||
|         return True | ||||
|  | @ -0,0 +1,348 @@ | |||
| from datetime import datetime, timedelta | ||||
| from odoo.tools.safe_eval import safe_eval | ||||
| from odoo import api, fields, models, tools, _ | ||||
| from odoo.exceptions import Warning, ValidationError | ||||
| from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT | ||||
| 
 | ||||
| from email.utils import formataddr, parseaddr | ||||
| 
 | ||||
| REPLACEMENT_OF_KEY = [('id', 'mailchimp_id'), ('create_time', 'create_date'), ('send_time', 'sent_date'), | ||||
|                       ('type', 'mailchimp_champ_type')] | ||||
| DATE_CONVERSION = ['create_date', 'sent_date'] | ||||
| UNWANTED_DATA = ['_links', 'created_by', 'edited_by', 'thumbnail'] | ||||
| 
 | ||||
| 
 | ||||
| class MassMailing(models.Model): | ||||
|     _inherit = "mail.mass_mailing" | ||||
| 
 | ||||
|     create_date = fields.Datetime("Created on", readonly=True, index=True) | ||||
|     mailchimp_template_id = fields.Many2one('mailchimp.templates', "MailChimp Template", copy=False) | ||||
|     mailchimp_account_id = fields.Many2one('mailchimp.accounts', string="MailChimp Account", | ||||
|                                            related="mailchimp_template_id.account_id", store=True) | ||||
|     mailchimp_list_id = fields.Many2one("mailchimp.lists", string="MailChimp List") | ||||
|     mailchimp_id = fields.Char("MailChimp ID", copy=False) | ||||
|     mailchimp_segment_id = fields.Many2one('mailchimp.segments', string="MailChimp Segments", copy=False) | ||||
|     mailchimp_champ_type = fields.Selection( | ||||
|         [('regular', 'Regular'), ('plaintext', 'Plain Text'), ('absplit', 'AB Split'), ('rss', 'RSS'), | ||||
|          ('variate', 'Variate')], | ||||
|         default='regular', string="Type") | ||||
| 
 | ||||
|     def update_opt_out_ts(self, email, list_ids, value): | ||||
|         if len(list_ids) > 0: | ||||
|             model = self.env['mail.mass_mailing.contact'].with_context(active_test=False) | ||||
|             records = model.search([('email', '=ilike', email), ('list_ids', 'in', list_ids)]) | ||||
|             records.write({'opt_out': value}) | ||||
| 
 | ||||
|     @api.multi | ||||
|     def action_schedule_date(self): | ||||
|         self.ensure_one() | ||||
|         action = self.env.ref('mailchimp.mass_mailing_schedule_date_action').read()[0] | ||||
|         action['context'] = dict(self.env.context, default_mass_mailing_id=self.id) | ||||
|         return action | ||||
| 
 | ||||
|     @api.model | ||||
|     def fetch_send_to_activity(self): | ||||
|         self.ensure_one() | ||||
|         account = self.mailchimp_template_id.account_id | ||||
|         if not account: | ||||
|             return True | ||||
|         count = 1000 | ||||
|         offset = 0 | ||||
|         sent_to_lists = [] | ||||
|         while True: | ||||
|             prepared_vals = {'count': count, 'offset': offset, | ||||
|                              'fields': 'sent_to.status,sent_to.email_address'} | ||||
|             response = account._send_request( | ||||
|                 'reports/%s/sent-to' % self.mailchimp_id, {}, params=prepared_vals) | ||||
|             if len(response.get('sent_to')) == 0: | ||||
|                 break | ||||
|             if isinstance(response.get('sent_to'), dict): | ||||
|                 sent_to_lists += [response.get('sent_to')] | ||||
|             sent_to_lists += response.get('sent_to') | ||||
|             offset = offset + 1000 | ||||
|         return sent_to_lists | ||||
| 
 | ||||
|     @api.multi | ||||
|     def process_send_to_activity_report(self): | ||||
|         self.ensure_one() | ||||
|         stat_obj = self.env['mail.mail.statistics'] | ||||
|         sent_to_lists = self.fetch_send_to_activity() | ||||
|         if not sent_to_lists: | ||||
|             return True | ||||
|         domain = safe_eval(self.mailing_domain) | ||||
|         contact_ids = self.env[self.mailing_model_real].search(domain) | ||||
|         for record_dict in sent_to_lists: | ||||
|             prepared_vals = {} | ||||
|             email_address = record_dict.get('email_address') | ||||
|             status = record_dict.get('status') | ||||
|             if status == 'sent': | ||||
|                 prepared_vals.update({'sent': self.sent_date, 'scheduled': self.sent_date, 'bounced': False}) | ||||
|             elif status in ['hard', 'soft']: | ||||
|                 prepared_vals.update({'bounced': self.sent_date, 'sent': self.sent_date, 'scheduled': self.sent_date}) | ||||
|             existing = self.statistics_ids.filtered(lambda x: x.email == email_address) | ||||
|             if existing: | ||||
|                 existing.write(prepared_vals) | ||||
|             else: | ||||
|                 res_id = contact_ids.filtered(lambda x: x.email == email_address) | ||||
|                 prepared_vals.update({ | ||||
|                     'model': self.mailing_model_real, | ||||
|                     'res_id': res_id.id, | ||||
|                     'mass_mailing_id': self.id, | ||||
|                     'email': email_address, | ||||
|                 }) | ||||
|                 self.statistics_ids.create(prepared_vals) | ||||
|         return sent_to_lists | ||||
| 
 | ||||
|     @api.multi | ||||
|     def process_email_activity_report(self): | ||||
|         self.ensure_one() | ||||
|         self.process_send_to_activity_report() | ||||
|         account = self.mailchimp_template_id.account_id | ||||
|         count = 1000 | ||||
|         offset = 0 | ||||
|         email_lists = [] | ||||
|         while True: | ||||
|             prepared_vals = {'count': count, 'offset': offset, 'fields': 'emails.email_address,emails.activity'} | ||||
|             response = account._send_request( | ||||
|                 'reports/%s/email-activity' % self.mailchimp_id, {}, params=prepared_vals) | ||||
|             if len(response.get('emails')) == 0: | ||||
|                 break | ||||
|             if isinstance(response.get('emails'), dict): | ||||
|                 email_lists += [response.get('emails')] | ||||
|             email_lists += response.get('emails') | ||||
|             offset = offset + 1000 | ||||
| 
 | ||||
|         for email in email_lists: | ||||
|             email_address = email.get('email_address') | ||||
|             activities = email.get('activity') | ||||
|             if not activities: | ||||
|                 continue | ||||
|             prepared_vals = {} | ||||
|             for acitvity in activities: | ||||
|                 action = acitvity.get('action') | ||||
|                 if action == 'open': | ||||
|                     prepared_vals.update({'opened': account.covert_date(acitvity.get('timestamp'))}) | ||||
|                 elif action == 'click': | ||||
|                     prepared_vals.update({'clicked': account.covert_date(acitvity.get('timestamp'))}) | ||||
|                 elif action == 'bounce': | ||||
|                     prepared_vals.update({'bounced': account.covert_date(acitvity.get('timestamp'))}) | ||||
|             existing = self.statistics_ids.filtered(lambda x: x.email == email_address) | ||||
|             if existing: | ||||
|                 existing.write(prepared_vals) | ||||
| 
 | ||||
|     @api.model | ||||
|     def fetch_email_activity(self): | ||||
|         stat_obj = self.env['mail.mail.statistics'] | ||||
|         sent_date = datetime.today() - timedelta(days=30) | ||||
|         sent_date = sent_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT) | ||||
|         for record in self.search([('state', 'not in', ['draft', 'in_queue']), ('mailchimp_id', '!=', False), ('sent_date', '>=', sent_date)]): | ||||
|             record.fetch_campaign() | ||||
|             # record.process_email_activity_report() | ||||
|         return True | ||||
| 
 | ||||
|     @api.multi | ||||
|     def create_or_update_campaigns(self, values_dict, account=False): | ||||
|         fetch_needed = False | ||||
|         list_obj = self.env['mailchimp.lists'] | ||||
|         template_obj = self.env['mailchimp.templates'] | ||||
|         mailchimp_id = values_dict.get('id') | ||||
|         settings_dict = values_dict.get('settings') | ||||
|         recipients_dict = values_dict.get('recipients') | ||||
|         list_id = recipients_dict.get('list_id') | ||||
|         template_id = settings_dict.get('template_id') | ||||
|         if list_id: | ||||
|             list_obj = list_obj.search([('list_id', '=', list_id)]).odoo_list_id | ||||
|         if template_id: | ||||
|             template_obj = template_obj.search([('template_id', '=', template_id), ('account_id', '=', account.id)]) | ||||
|         status = values_dict.get('status') | ||||
|         subject_line = settings_dict.get('subject_line') or settings_dict.get('title') | ||||
|         try: | ||||
|             email_from = formataddr((settings_dict.get('from_name'), settings_dict.get('reply_to'))) | ||||
|         except Exception as e: | ||||
|             email_from = self.env['mail.message']._get_default_from() | ||||
|         prepared_vals = { | ||||
|             'create_date': values_dict.get('create_time'), | ||||
|             'sent_date': values_dict.get('send_time'), | ||||
|             'name': subject_line, | ||||
|             'mailchimp_id': mailchimp_id, | ||||
|             'mailing_model_id': self.env.ref('mass_mailing.model_mail_mass_mailing_list').id, | ||||
|             'contact_list_ids': [(6, 0, list_obj.ids)], | ||||
|             'mailchimp_template_id': template_obj.id, | ||||
|             'mailchimp_champ_type': values_dict.get('type'), | ||||
|             'email_from': email_from, | ||||
|             'reply_to': email_from, | ||||
|         } | ||||
|         if status in ['save', 'paused']: | ||||
|             prepared_vals.update({'state': 'draft'}) | ||||
|         elif status == 'schedule': | ||||
|             prepared_vals.update({'state': 'in_queue'}) | ||||
|         elif status == 'sending': | ||||
|             prepared_vals.update({'state': 'sending'}) | ||||
|         elif status == 'sent': | ||||
|             fetch_needed = True | ||||
|             prepared_vals.update({'state': 'done'}) | ||||
|         for item in DATE_CONVERSION: | ||||
|             if prepared_vals.get(item, False) == '': | ||||
|                 prepared_vals[item] = False | ||||
|             if prepared_vals.get(item, False): | ||||
|                 prepared_vals[item] = account.covert_date(prepared_vals.get(item)) | ||||
|         existing_list = self.search([('mailchimp_id', '=', mailchimp_id)]) | ||||
|         if not existing_list: | ||||
|             existing_list = self.create(prepared_vals) | ||||
|             self.env.cr.execute(""" | ||||
|                            UPDATE | ||||
|                            mail_mass_mailing | ||||
|                            SET create_date = '%s' | ||||
|                            WHERE id = %s | ||||
|                            """ % (prepared_vals.get('create_date'), existing_list.id)) | ||||
|         else: | ||||
|             existing_list.write(prepared_vals) | ||||
|         existing_list._onchange_model_and_list() | ||||
|         existing_list.body_html = False | ||||
|         if fetch_needed: | ||||
|             existing_list.process_email_activity_report() | ||||
|         return True | ||||
| 
 | ||||
|     @api.multi | ||||
|     def fetch_campaign(self): | ||||
|         self.ensure_one() | ||||
|         if not self.mailchimp_id: | ||||
|             return True | ||||
|         account = self.mailchimp_template_id.account_id | ||||
|         params_vals = { | ||||
|             'fields': 'id,type,status,create_time,send_time,settings.template_id,settings.subject_line,settings.title,settings.from_name,settings.reply_to,recipients.list_id'} | ||||
|         response = account._send_request('campaigns/%s' % self.mailchimp_id, {}, params=params_vals) | ||||
|         self.create_or_update_campaigns(response, account=account) | ||||
|         return True | ||||
| 
 | ||||
|     @api.multi | ||||
|     def import_campaigns(self, account=False): | ||||
|         if not account: | ||||
|             raise Warning("MailChimp Account not defined to import Campaigns") | ||||
|         count = 1000 | ||||
|         offset = 0 | ||||
|         campaigns_list = [] | ||||
|         while True: | ||||
|             prepared_vals = {'count': count, 'offset': offset} | ||||
|             response = account._send_request('campaigns', {}, params=prepared_vals) | ||||
|             if len(response.get('campaigns')) == 0: | ||||
|                 break | ||||
|             if isinstance(response.get('campaigns'), dict): | ||||
|                 campaigns_list += [response.get('campaigns')] | ||||
|             campaigns_list += response.get('campaigns') | ||||
|             offset = offset + 1000 | ||||
|         for campaigns_dict in campaigns_list: | ||||
|             self.create_or_update_campaigns(campaigns_dict, account=account) | ||||
|         return True | ||||
| 
 | ||||
|     @api.model | ||||
|     def _prepare_vals_for_export(self): | ||||
|         self.ensure_one() | ||||
|         from_name, from_email = parseaddr(self.email_from) | ||||
|         reply_to_name, reply_to_email = parseaddr(self.reply_to) | ||||
|         settings_dict = {'subject_line': self.name, 'title': self.name, 'from_name': from_name, | ||||
|                          'reply_to': reply_to_email, 'template_id': int(self.mailchimp_template_id.template_id)} | ||||
|         prepared_vals = {'type': 'regular', | ||||
|                          'recipients': {'list_id': self.contact_list_ids.mailchimp_list_id.list_id, }, | ||||
|                          'settings': settings_dict} | ||||
|         if self.mailchimp_segment_id.mailchimp_id: | ||||
|             prepared_vals['recipients'].update({'segment_opts': {'saved_segment_id': int(self.mailchimp_segment_id.mailchimp_id)}}) | ||||
|         return prepared_vals | ||||
| 
 | ||||
|     @api.one | ||||
|     def export_to_mailchimp(self, account=False): | ||||
|         if self.mailchimp_id: | ||||
|             return True | ||||
|         if not account: | ||||
|             raise Warning("MailChimp Account not defined in selected Template.") | ||||
|         prepared_vals = self._prepare_vals_for_export() | ||||
|         response = account._send_request('campaigns', prepared_vals, method='POST') | ||||
|         if response.get('id', False): | ||||
|             self.write({'mailchimp_id': response['id']}) | ||||
|         else: | ||||
|             ValidationError(_("MailChimp Identification wasn't received. Please try again!")) | ||||
|         self._cr.commit() | ||||
|         return True | ||||
| 
 | ||||
|     @api.one | ||||
|     def send_now_mailchimp(self, account=False): | ||||
|         if not account: | ||||
|             raise Warning("MailChimp Account not defined in selected Template.") | ||||
|         response = account._send_request('campaigns/%s/actions/send' % self.mailchimp_id, {}, method='POST') | ||||
|         return True | ||||
| 
 | ||||
|     @api.multi | ||||
|     def send_test_mail_mailchimp(self, test_emails): | ||||
|         self.ensure_one() | ||||
|         self.export_to_mailchimp(self.mailchimp_template_id.account_id) | ||||
|         prepared_vals = {'test_emails': test_emails, 'send_type': 'html'} | ||||
|         response = self.mailchimp_template_id.account_id._send_request('campaigns/%s/actions/test' % self.mailchimp_id, | ||||
|                                                                        prepared_vals, method='POST') | ||||
|         return True | ||||
| 
 | ||||
|     @api.multi | ||||
|     def schedule_mailchimp_champaign(self, schedule_date): | ||||
|         self.ensure_one() | ||||
|         self.export_to_mailchimp(self.mailchimp_template_id.account_id) | ||||
|         prepared_vals = {'schedule_time': schedule_date.isoformat()} | ||||
|         response = self.mailchimp_template_id.account_id._send_request( | ||||
|             'campaigns/%s/actions/schedule' % self.mailchimp_id, | ||||
|             prepared_vals, method='POST') | ||||
|         return True | ||||
| 
 | ||||
|     @api.multi | ||||
|     def cancel_mass_mailing(self): | ||||
|         res = super(MassMailing, self).cancel_mass_mailing() | ||||
|         if self.mailchimp_id and self.mailchimp_template_id: | ||||
|             self.mailchimp_template_id.account_id._send_request('campaigns/%s/actions/cancel-send' % self.mailchimp_id, | ||||
|                                                                 {}, method='POST') | ||||
|             if self.schedule_date: | ||||
|                 self.mailchimp_template_id.account_id._send_request( | ||||
|                     'campaigns/%s/actions/unschedule' % self.mailchimp_id, | ||||
|                     {}, method='POST') | ||||
|         return res | ||||
| 
 | ||||
|     @api.multi | ||||
|     def put_in_queue(self): | ||||
|         res = super(MassMailing, self).put_in_queue() | ||||
|         for record in self.filtered(lambda x: x.mailchimp_template_id): | ||||
|             if len(record.contact_list_ids) > 1: | ||||
|                 raise ValidationError(_("Multiple list is not allowed while going with MailChimp!")) | ||||
|             if record.contact_list_ids.filtered(lambda x: not x.mailchimp_list_id): | ||||
|                 raise ValidationError(_("Please provide MailChimp list as you selected MailChimp Template!")) | ||||
|             record.export_to_mailchimp(record.mailchimp_template_id.account_id) | ||||
|             if record.mailchimp_id: | ||||
|                 record.send_now_mailchimp(record.mailchimp_template_id.account_id) | ||||
|                 record.process_send_to_activity_report() | ||||
|                 record.fetch_campaign() | ||||
|         return res | ||||
| 
 | ||||
|     @api.model | ||||
|     def _process_mass_mailing_queue(self): | ||||
|         mass_mailings = self.search( | ||||
|             [('state', 'in', ('in_queue', 'sending')), '|', ('schedule_date', '<', fields.Datetime.now()), | ||||
|              ('schedule_date', '=', False)]) | ||||
|         for mass_mailing in mass_mailings: | ||||
|             user = mass_mailing.write_uid or self.env.user | ||||
|             mass_mailing = mass_mailing.with_context(**user.sudo(user=user).context_get()) | ||||
|             if mass_mailing.mailchimp_id: | ||||
|                 mass_mailing.fetch_campaign() | ||||
|                 continue | ||||
|             if len(mass_mailing.get_remaining_recipients()) > 0: | ||||
|                 mass_mailing.state = 'sending' | ||||
|                 mass_mailing.send_mail() | ||||
|             else: | ||||
|                 mass_mailing.write({'state': 'done', 'sent_date': fields.Datetime.now()}) | ||||
| 
 | ||||
|     @api.onchange('mailing_model_id', 'contact_list_ids') | ||||
|     def _onchange_model_and_list(self): | ||||
|         res = super(MassMailing, self)._onchange_model_and_list() | ||||
|         mailing_domain = [] | ||||
|         list_obj = self.env['mailchimp.lists'] | ||||
|         list_ids = list_obj.search([('odoo_list_id', 'in', self.contact_list_ids.ids)]) | ||||
| 
 | ||||
|         self.mailchimp_list_id = list_ids and list_ids[0] or False | ||||
|         if self.mailchimp_list_id: | ||||
|             self.email_from = formataddr((self.mailchimp_list_id.from_name, self.mailchimp_list_id.from_email)) | ||||
|             self.reply_to = formataddr((self.mailchimp_list_id.from_name, self.mailchimp_list_id.from_email)) | ||||
|         return res | ||||
|  | @ -0,0 +1,192 @@ | |||
| import re | ||||
| import hashlib | ||||
| from datetime import datetime | ||||
| from odoo import api, fields, models, _ | ||||
| from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT | ||||
| 
 | ||||
| EMAIL_PATTERN = '([^ ,;<@]+@[^> ,;]+)' | ||||
| 
 | ||||
| 
 | ||||
| def _partner_split_name(partner_name): | ||||
|     return [' '.join(partner_name.split()[:-1]), ' '.join(partner_name.split()[-1:])] | ||||
| 
 | ||||
| 
 | ||||
| class massMailingContact(models.Model): | ||||
|     _inherit = "mail.mass_mailing.contact" | ||||
| 
 | ||||
|     @api.multi | ||||
|     def get_partner(self, email): | ||||
|         query = """ | ||||
|                 SELECT id  | ||||
|                   FROM res_partner | ||||
|                 WHERE LOWER(substring(email, '([^ ,;<@]+@[^> ,;]+)')) = LOWER(substring('{}', '([^ ,;<@]+@[^> ,;]+)'))""".format(email) | ||||
|         self._cr.execute(query) | ||||
|         return self._cr.fetchone() or False | ||||
| 
 | ||||
|     @api.model | ||||
|     def create(self, vals): | ||||
|         res = super(massMailingContact, self).create(vals) | ||||
|         if vals.get('email', False): | ||||
|             partner_record = self.get_partner(vals.get('email')) | ||||
|             res.related_partner_id = partner_record | ||||
|         return res | ||||
| 
 | ||||
|     @api.multi | ||||
|     def write(self, vals): | ||||
|         if vals.get('email', False) and not self._context.get('do_not_update', False): | ||||
|             partner_record = self.get_partner(vals.get('email')) | ||||
|             vals.update({'related_partner_id': partner_record}) | ||||
|         res = super(massMailingContact, self).write(vals) | ||||
|         return res | ||||
| 
 | ||||
|     @api.depends('subscription_list_ids', 'subscription_list_ids.mailchimp_id', 'subscription_list_ids.list_id') | ||||
|     def _get_pending_for_export(self): | ||||
|         available_mailchimp_lists = self.env['mailchimp.lists'].search([]) | ||||
|         lists = available_mailchimp_lists.mapped('odoo_list_id').ids | ||||
|         for record in self: | ||||
|             if record.subscription_list_ids.filtered(lambda x: x.list_id.id in lists and not x.mailchimp_id): | ||||
|                 record.pending_for_export = True | ||||
|             else: | ||||
|                 record.pending_for_export = False | ||||
| 
 | ||||
|     @api.depends('email') | ||||
|     def _compute_is_email_valid(self): | ||||
|         for record in self: | ||||
|             record.is_email_valid = re.match(EMAIL_PATTERN, record.email) | ||||
| 
 | ||||
|     @api.depends('email') | ||||
|     def _compute_related_partner_id(self): | ||||
|         for record in self: | ||||
|             query = """ | ||||
|             SELECT id  | ||||
|               FROM res_partner | ||||
|             WHERE LOWER(substring(email, '([^ ,;<@]+@[^> ,;]+)')) = LOWER(substring('{}', '([^ ,;<@]+@[^> ,;]+)'))""".format(record.email) | ||||
|             self._cr.execute(query) | ||||
|             partner_record = self._cr.fetchone() | ||||
|             if partner_record: | ||||
|                 record.related_partner_id = partner_record[0] | ||||
|             else: | ||||
|                 record.related_partner_id = False | ||||
| 
 | ||||
|     is_email_valid = fields.Boolean(compute='_compute_is_email_valid', store=True) | ||||
|     pending_for_export = fields.Boolean(compute="_get_pending_for_export", string="Pending For Export", store=True) | ||||
|     related_partner_id = fields.Many2one('res.partner', 'Related Customer', compute="_compute_related_partner_id", help='Display related customer by matching Email address.', store=True) | ||||
|     subscription_list_ids = fields.One2many('mail.mass_mailing.list_contact_rel', 'contact_id', string='Subscription Information') | ||||
| 
 | ||||
|     def _prepare_vals_for_merge_fields(self, mailchimp_list_id): | ||||
|         self.ensure_one() | ||||
|         merge_fields_vals = {} | ||||
|         partner_id = self.related_partner_id | ||||
|         for custom_field in mailchimp_list_id.merge_field_ids: | ||||
|             if custom_field.type == 'address' and partner_id: | ||||
|                 address = {'addr1': partner_id.street or '', | ||||
|                            'addr2': partner_id.street2 or '', | ||||
|                            'city': partner_id.city or '', | ||||
|                            'state': partner_id.state_id.name if partner_id.state_id else '', | ||||
|                            'zip': partner_id.zip, | ||||
|                            'country': partner_id.country_id.code if partner_id.country_id else ''} | ||||
|                 merge_fields_vals.update({custom_field.tag: address}) | ||||
|             elif custom_field.tag == 'FNAME': | ||||
|                 merge_fields_vals.update({custom_field.tag: _partner_split_name(self.name)[0] if _partner_split_name(self.name)[0] else _partner_split_name(self.name)[1]}) | ||||
|             elif custom_field.tag == 'LNAME': | ||||
|                 merge_fields_vals.update({custom_field.tag: _partner_split_name(self.name)[1] if _partner_split_name(self.name)[0] else _partner_split_name(self.name)[0]}) | ||||
|             elif custom_field.type in ['date', 'birthday']: | ||||
|                 value = getattr(partner_id or self, custom_field.field_id.name) if custom_field.field_id and hasattr(partner_id or self, custom_field.field_id.name) else '' | ||||
|                 if value: | ||||
|                     value = datetime.strptime(value, DEFAULT_SERVER_DATETIME_FORMAT).strftime(custom_field.date_format) | ||||
|                     # value = value.strftime(custom_field.date_format) | ||||
|                 merge_fields_vals.update({custom_field.tag: value or ''}) | ||||
|             else: | ||||
|                 value = getattr(partner_id or self, custom_field.field_id.name) if custom_field.field_id and hasattr(partner_id or self, custom_field.field_id.name) else '' | ||||
|                 merge_fields_vals.update({custom_field.tag: value or ''}) | ||||
|         return merge_fields_vals | ||||
| 
 | ||||
|     @api.multi | ||||
|     def action_export_to_mailchimp(self): | ||||
|         available_mailchimp_lists = self.env['mailchimp.lists'].search([]) | ||||
|         lists = available_mailchimp_lists.mapped('odoo_list_id').ids | ||||
|         for record in self: | ||||
|             lists_to_export = record.subscription_list_ids.filtered( | ||||
|                 lambda x: x.list_id.id in lists and not x.mailchimp_id) | ||||
|             for list in lists_to_export: | ||||
|                 mailchimp_list_id = list.list_id.mailchimp_list_id | ||||
|                 merge_fields_vals = record._prepare_vals_for_merge_fields(mailchimp_list_id) | ||||
|                 # address = '' | ||||
|                 # phone = '' | ||||
|                 # partner_id = record.related_partner_id | ||||
|                 # if partner_id: | ||||
|                 #     address = {'addr1': partner_id.street or '', | ||||
|                 #                'addr2': partner_id.street2 or '', | ||||
|                 #                'city': partner_id.city or '', | ||||
|                 #                'state': partner_id.state_id.name if partner_id.state_id else '', | ||||
|                 #                'zip': partner_id.zip, | ||||
|                 #                'country': partner_id.country_id.code if partner_id.country_id else ''} | ||||
|                 #     phone = partner_id.phone or partner_id.mobile | ||||
|                 prepared_vals = {"email_address": record.email.lower(), | ||||
|                                  "status": "unsubscribed" if record.opt_out else "subscribed", | ||||
|                                  "merge_fields": merge_fields_vals, | ||||
|                                  "tags": [tag.name for tag in record.tag_ids]} | ||||
|                 response = mailchimp_list_id.account_id._send_request('lists/%s/members' % mailchimp_list_id.list_id, | ||||
|                                                                       prepared_vals, method='POST') | ||||
|                 if response.get('web_id', False): | ||||
|                     email_address = response.get('email_address') | ||||
|                     md5_email = hashlib.md5(email_address.encode('utf-8')).hexdigest() | ||||
|                     list.write({'mailchimp_id': response.get('web_id', False), 'md5_email': md5_email}) | ||||
|         return True | ||||
| 
 | ||||
|     @api.multi | ||||
|     def action_update_to_mailchimp(self): | ||||
|         available_mailchimp_lists = self.env['mailchimp.lists'].search([]) | ||||
|         lists = available_mailchimp_lists.mapped('odoo_list_id').ids | ||||
|         for record in self: | ||||
|             lists_to_export = record.subscription_list_ids.filtered( | ||||
|                 lambda x: x.list_id.id in lists and x.mailchimp_id) | ||||
|             for list in lists_to_export: | ||||
|                 mailchimp_list_id = list.list_id.mailchimp_list_id | ||||
|                 merge_fields_vals = record._prepare_vals_for_merge_fields(mailchimp_list_id) | ||||
|                 # partner_id = record.related_partner_id | ||||
|                 # address = '' | ||||
|                 # phone = '' | ||||
|                 # if partner_id: | ||||
|                 #     address = {'addr1': partner_id.street or '', | ||||
|                 #                'addr2': partner_id.street2 or '', | ||||
|                 #                'city': partner_id.city or '', | ||||
|                 #                'state': partner_id.state_id.name if partner_id.state_id else '', | ||||
|                 #                'zip': partner_id.zip, | ||||
|                 #                'country': partner_id.country_id.code if partner_id.country_id else ''} | ||||
|                 #     phone = partner_id.phone or partner_id.mobile | ||||
|                 prepared_vals = {"email_address": record.email.lower(), | ||||
|                                  "status": "unsubscribed" if record.opt_out else "subscribed", | ||||
|                                  'merge_fields': merge_fields_vals, } | ||||
|                 response = mailchimp_list_id.account_id._send_request( | ||||
|                     'lists/%s/members/%s' % (mailchimp_list_id.list_id, list.md5_email), | ||||
|                     prepared_vals, method='PATCH') | ||||
|                 tag_res = record.update_tag_on_mailchimp(response, mailchimp_list_id, list.md5_email) | ||||
|                 if response.get('web_id', False): | ||||
|                     email_address = response.get('email_address') | ||||
|                     md5_email = hashlib.md5(email_address.encode('utf-8')).hexdigest() | ||||
|                     list.write({'mailchimp_id': response.get('web_id', False), 'md5_email': md5_email}) | ||||
|         return True | ||||
| 
 | ||||
|     def update_tag_on_mailchimp(self, response, mailchimp_list_id, md5_email): | ||||
|         tag_list = [] | ||||
|         tags = response.get('tags', []) and [tag['name'] for tag in response.get('tags', [])] or [] | ||||
|         tag_name_list = self.tag_ids.mapped('name') | ||||
|         unique_tags = list(set(tags + tag_name_list)) | ||||
|         for tag in unique_tags: | ||||
|             if tag in tag_name_list: | ||||
|                 tag_dict = {'name': tag, 'status': 'active'} | ||||
|             else: | ||||
|                 tag_dict = {'name': tag, 'status': 'inactive'} | ||||
|             tag_list.append(tag_dict) | ||||
|         tag_vals = {'tags': tag_list} | ||||
|         tag_res = mailchimp_list_id.account_id._send_request('lists/%s/members/%s/tags' % (mailchimp_list_id.list_id, md5_email), tag_vals, method='POST') | ||||
|         return tag_res | ||||
| 
 | ||||
|     def fetch_specific_member_data(self, mailchimp_list_id, md5_email): | ||||
|         member_response = mailchimp_list_id.account_id._send_request('lists/%s/members/%s' % (mailchimp_list_id.list_id, md5_email), {}, method='GET') | ||||
|         tag_list = member_response.get('tags', []) | ||||
|         tag_ids = self.env['res.partner.category'] | ||||
|         if tag_list: | ||||
|             tag_ids = self.env['res.partner.category'].create_or_update_tags(tag_list) | ||||
|         return tag_ids | ||||
|  | @ -0,0 +1,17 @@ | |||
| from odoo import api, fields, models, _ | ||||
| 
 | ||||
| 
 | ||||
| class MassMailingList(models.Model): | ||||
|     _inherit = "mail.mass_mailing.list" | ||||
| 
 | ||||
|     @api.one | ||||
|     def _compute_mailchimp_list_id(self): | ||||
|         mailchimp_list_obj = self.env['mailchimp.lists'] | ||||
|         list_id = mailchimp_list_obj.search([('odoo_list_id', '=', self.id)]) | ||||
|         if list_id: | ||||
|             self.mailchimp_list_id = list_id.id | ||||
|         else: | ||||
|             self.mailchimp_list_id = False | ||||
| 
 | ||||
|     mailchimp_list_id = fields.Many2one('mailchimp.lists', compute='_compute_mailchimp_list_id', | ||||
|                                         string="Associated MailChimp List") | ||||
|  | @ -0,0 +1,37 @@ | |||
| from odoo import api, fields, models, _ | ||||
| 
 | ||||
| 
 | ||||
| class MassMailingContactListRel(models.Model): | ||||
|     """ Intermediate model between mass mailing list and mass mailing contact | ||||
|         Indicates if a contact is opted out for a particular list | ||||
|     """ | ||||
|     _name = 'mail.mass_mailing.list_contact_rel' | ||||
|     _description = 'Mass Mailing Subscription Information' | ||||
|     _table = 'mail_mass_mailing_contact_list_rel' | ||||
|     _rec_name = 'contact_id' | ||||
| 
 | ||||
|     @api.depends('list_id') | ||||
|     def _compute_mailchimp_list_id(self): | ||||
|         mailchimp_list_obj = self.env['mailchimp.lists'] | ||||
|         for record in self: | ||||
|             list_id = mailchimp_list_obj.search([('odoo_list_id', '=', record.list_id.id)], limit=1) | ||||
|             record.mailchimp_list_id = list_id.id | ||||
| 
 | ||||
|     contact_id = fields.Many2one('mail.mass_mailing.contact', string='Contact', ondelete='cascade', required=True) | ||||
|     list_id = fields.Many2one('mail.mass_mailing.list', string='Mailing List', ondelete='cascade', required=True) | ||||
|     mailchimp_id = fields.Char("MailChimp ID", readonly=1, copy=False) | ||||
|     mailchimp_list_id = fields.Many2one(compute="_compute_mailchimp_list_id", string="MailChimp List", store=True) | ||||
|     md5_email = fields.Char("MD5 Email", readonly=1, copy=False) | ||||
|     related_partner_id = fields.Many2one('res.partner', related='contact_id.related_partner_id', string='Related Customer', readonly=True, store=True) | ||||
|     opt_out = fields.Boolean(related="contact_id.opt_out", string='Opt Out', help='The contact has chosen not to receive mails anymore from this list') | ||||
| 
 | ||||
|     _sql_constraints = [ | ||||
|         ('unique_contact_list', 'unique (contact_id, list_id)', | ||||
|          'A contact cannot be subscribed multiple times to the same list!') | ||||
|     ] | ||||
| 
 | ||||
|     @api.model_cr | ||||
|     def init(self): | ||||
|         self._cr.execute("""select * from information_schema.columns where column_name = 'id' and table_name = 'mail_mass_mailing_contact_list_rel'""") | ||||
|         if not self._cr.fetchone(): | ||||
|             self._cr.execute("""ALTER TABLE mail_mass_mailing_contact_list_rel ADD COLUMN id SERIAL PRIMARY KEY""") | ||||
|  | @ -0,0 +1,40 @@ | |||
| from odoo import fields, models, api | ||||
| 
 | ||||
| class ResPartner(models.Model): | ||||
|     _inherit = 'res.partner' | ||||
| 
 | ||||
|     subscription_list_ids = fields.One2many('mail.mass_mailing.list_contact_rel', | ||||
|         'related_partner_id', string='Subscription Information', domain=[('mailchimp_list_id','!=',False)]) | ||||
| 
 | ||||
|     @api.multi | ||||
|     def get_mailing_partner(self, email): | ||||
|         query = """ | ||||
|                         SELECT id  | ||||
|                           FROM mail_mass_mailing_contact | ||||
|                         WHERE LOWER(substring(email, '([^ ,;<@]+@[^> ,;]+)')) = LOWER(substring('{}', '([^ ,;<@]+@[^> ,;]+)'))""".format(email) | ||||
|         self._cr.execute(query) | ||||
|         return self._cr.fetchone() | ||||
| 
 | ||||
|     @api.model | ||||
|     def create(self, vals): | ||||
|         res = super(ResPartner, self).create(vals) | ||||
|         if vals.get('email', False): | ||||
|             mailing_contact = self.env['mail.mass_mailing.contact'] | ||||
|             partner_record = self.get_mailing_partner(vals.get('email')) | ||||
|             if partner_record: | ||||
|                 mailing_contact.browse(partner_record[0]).related_partner_id = res.id | ||||
|         return res | ||||
| 
 | ||||
|     @api.multi | ||||
|     def write(self, vals): | ||||
|         if vals.get('email', False): | ||||
|             mailing_contact = self.env['mail.mass_mailing.contact'] | ||||
|             partner_record = self.get_mailing_partner(vals.get('email')) | ||||
|             if partner_record: | ||||
|                 mailing_contact.browse(partner_record[0]).related_partner_id = self.id | ||||
|             else: | ||||
|                 partner_record = self.get_mailing_partner(self.email) | ||||
|                 if partner_record: | ||||
|                     mailing_contact.browse(partner_record[0]).write({'related_partner_id':False}) | ||||
|         res = super(ResPartner, self).write(vals) | ||||
|         return res | ||||
|  | @ -0,0 +1,24 @@ | |||
| from odoo import fields,api,models | ||||
| 
 | ||||
| class ResPartnerCategory(models.Model): | ||||
|     _inherit = 'res.partner.category' | ||||
| 
 | ||||
|     mailchimp_id = fields.Char('Mailchimp Id') | ||||
| 
 | ||||
|     @api.multi | ||||
|     def create_or_update_tags(self, values_dict, account=False): | ||||
|         tag_ids = self | ||||
|         for val in values_dict: | ||||
|             tag_id = val.get('id') | ||||
|             existing_list = self.search([('mailchimp_id', '=', tag_id)]) | ||||
|             val.update({'mailchimp_id':val.pop('id')}) | ||||
|             if not existing_list: | ||||
|                 existing_list =self.search([('name', '=', val.get('name'))]) | ||||
|                 if not existing_list: | ||||
|                     existing_list = self.create(val) | ||||
|                 else: | ||||
|                     existing_list.write(val) | ||||
|             else: | ||||
|                 existing_list.write(val) | ||||
|             tag_ids += existing_list | ||||
|         return tag_ids | ||||
|  | @ -0,0 +1,13 @@ | |||
| id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink | ||||
| access_mailchimp_accounts_public,mailchimp.accounts,mailchimp.model_mailchimp_accounts,,1,0,0,0 | ||||
| access_mailchimp_lists_public,mailchimp.lists,mailchimp.model_mailchimp_lists,,1,0,0,0 | ||||
| access_mailchimp_lists_stats_public,mailchimp.lists.stats,mailchimp.model_mailchimp_lists_stats,,1,0,0,0 | ||||
| access_mailchimp_templates_public,mailchimp.templates,mailchimp.model_mailchimp_templates,,1,0,0,0 | ||||
| access_mailchimp_accounts_user,mailchimp.accounts.user,mailchimp.model_mailchimp_accounts,mass_mailing.group_mass_mailing_user,1,1,1,1 | ||||
| access_mailchimp_lists_user,mailchimp.lists.user,mailchimp.model_mailchimp_lists,mass_mailing.group_mass_mailing_user,1,1,1,1 | ||||
| access_mailchimp_lists_stats_user,mailchimp.lists.stats.user,mailchimp.model_mailchimp_lists_stats,mass_mailing.group_mass_mailing_user,1,1,1,1 | ||||
| access_mailchimp_templates_user,mailchimp.templates.user,mailchimp.model_mailchimp_templates,mass_mailing.group_mass_mailing_user,1,1,1,1 | ||||
| access_mailchimp_segments_public,mailchimp.segments,mailchimp.model_mailchimp_segments,,1,0,0,0 | ||||
| access_mailchimp_segments_user,mailchimp.segments.user,mailchimp.model_mailchimp_segments,mass_mailing.group_mass_mailing_user,1,1,1,1 | ||||
| access_mailchimp_merge_fields_public,mailchimp.merge.fields,mailchimp.model_mailchimp_merge_fields,,1,0,0,0 | ||||
| access_mailchimp_merge_fields_user,mailchimp.merge.fields.user,mailchimp.model_mailchimp_merge_fields,mass_mailing.group_mass_mailing_user,1,1,1,1 | ||||
| 
 | 
| After Width: | Height: | Size: 37 KiB | 
| After Width: | Height: | Size: 40 KiB | 
| After Width: | Height: | Size: 51 KiB | 
| After Width: | Height: | Size: 61 KiB | 
| After Width: | Height: | Size: 42 KiB | 
| After Width: | Height: | Size: 92 KiB | 
| After Width: | Height: | Size: 58 KiB | 
| After Width: | Height: | Size: 39 KiB | 
| After Width: | Height: | Size: 76 KiB | 
| After Width: | Height: | Size: 80 KiB | 
| After Width: | Height: | Size: 78 KiB | 
| After Width: | Height: | Size: 129 KiB | 
| After Width: | Height: | Size: 40 MiB | 
| After Width: | Height: | Size: 96 KiB | 
| After Width: | Height: | Size: 118 KiB | 
| After Width: | Height: | Size: 30 MiB | 
| After Width: | Height: | Size: 164 KiB | 
| After Width: | Height: | Size: 8.3 MiB | 
| After Width: | Height: | Size: 49 KiB | 
| After Width: | Height: | Size: 18 KiB | 
| After Width: | Height: | Size: 63 KiB | 
| After Width: | Height: | Size: 30 KiB | 
| After Width: | Height: | Size: 54 KiB | 
| After Width: | Height: | Size: 30 KiB | 
|  | @ -0,0 +1,451 @@ | |||
| <section class="oe_container"> | ||||
|     <div class="mt64 mb64 pt32"> | ||||
|         <h3 class="text-center alert" | ||||
|             style="font-family: 'Lato'0, 'Open Sans', 'Helvetica', Sans;font-size: 40px;font-weight: 300;color:#875A7B;"> | ||||
|             MailChimp Odoo Integration</h3> | ||||
|         <p class="oe_mt32" | ||||
|            style="font-size: 20px; font-weight: 300;line-height: 150% !important; font-family: 'Lato', 'Open Sans', 'Helvetica', Sans !important; text-align:center;"> | ||||
|             Seamless one click integration to synchronize your contact list, campaign, templates between Odoo and | ||||
|             MailChimp. Level up your email marketing, get more sales and happy customers. | ||||
|         </p> | ||||
|     </div> | ||||
| </section> | ||||
| 
 | ||||
| <section> | ||||
|     <div class="text-center"> | ||||
|         <div class="mt-5 mb-4"> | ||||
|             <a class="btn btn-primary" | ||||
|                style="width: auto;font-weight: bold;font-size: 21px;background: linear-gradient(to right, #61455a, #bc088b);box-shadow: 0px 0px 6px 3px #d7e5f1;padding-top: 12px;padding-bottom: 12px;padding-left: 40px;padding-right: 40px;border-radius: 10px; color: white; border: none;" | ||||
|                href="mailto:info@teqstars.com" target="_blank">Request A Demo | ||||
|             </a> | ||||
|         </div> | ||||
|     </div> | ||||
| </section> | ||||
| 
 | ||||
| <section> | ||||
|     <div class="text-center"> | ||||
|         <div class="s_panel_video" data-video-id="DMnydaAzDGc?rel=0" style="cursor:pointer;"> | ||||
|             <img class="s_tooltip_tabs_tooltip_image s_figure_link img-fluid pb0" | ||||
|                  src="mailchimp_video.jpg" alt="MailChimp Tutorial" style="max-width:60%;"> | ||||
|         </div> | ||||
|     </div> | ||||
| </section> | ||||
| 
 | ||||
| <section class="oe_container"> | ||||
|     <div id="loempia_tabs" class="" | ||||
|          style="padding:64px 0px;margin: 100px 0px;background-color:#f6f7f9;border-radius: 10px;"> | ||||
|         <ul role="tablist" class="nav nav-tabs justify-content-center" data-tabs="tabs" style=" | ||||
|          border: none; | ||||
|          background: unset; | ||||
|          "> | ||||
|             <li class="nav-item" | ||||
|                 style="border-top-right-radius: 6px;border-top-left-radius: 6px;background: linear-gradient(to top, #FFD14F, #ffdc7a);margin-right: 1px;padding-bottom: 2px;"> | ||||
|                 <a href="#features" data-toggle="tab" aria-expanded="true" class="active" | ||||
|                    style="font-family: Roboto;text-transform: uppercase;font-weight: 600;font-size: 15px;letter-spacing: 1px;padding: 10px 20px;border-top-left-radius: 5px;border-top-right-radius: 5px;color:#000000;">Features | ||||
|                 </a> | ||||
|             </li> | ||||
|             <li class="nav-item" | ||||
|                 style="border-top-right-radius: 6px;border-top-left-radius: 6px;background: linear-gradient(to top, #FFD14F, #ffdc7a);margin-right: 1px;padding-bottom: 2px;"> | ||||
|                 <a href="#screenshots" data-toggle="tab" aria-expanded="true" class="" | ||||
|                    style="font-family: Roboto;text-transform: uppercase;font-weight: 600;font-size: 15px;letter-spacing: 1px;padding: 10px 20px;border-top-left-radius: 5px;border-top-right-radius: 5px;color:#000000;">Screenshots | ||||
|                 </a> | ||||
|             </li> | ||||
|             <li class="nav-item" | ||||
|                 style="border-top-right-radius: 6px;border-top-left-radius: 6px;background: linear-gradient(to top, #FFD14F, #ffdc7a);margin-right: 1px;padding-bottom: 2px;"> | ||||
|                 <a href="#changelogs" data-toggle="tab" aria-expanded="true" class="" | ||||
|                    style="font-family: Roboto;text-transform: uppercase;font-weight: 600;font-size: 15px;letter-spacing: 1px;padding: 10px 20px;border-top-left-radius: 5px;border-top-right-radius: 5px;color:#000000;">Changelogs | ||||
|                 </a> | ||||
|             </li> | ||||
|             <li class="nav-item" | ||||
|                 style="border-top-right-radius: 6px;border-top-left-radius: 6px;background: linear-gradient(to top, #FFD14F, #ffdc7a);margin-right: 1px;padding-bottom: 2px;"> | ||||
|                 <a href="#faqs" data-toggle="tab" aria-expanded="true" class="" | ||||
|                    style="font-family: Roboto;text-transform: uppercase;font-weight: 600;font-size: 15px;letter-spacing: 1px;padding: 10px 20px;border-top-left-radius: 5px;border-top-right-radius: 5px;color:#000000;">FAQs | ||||
|                 </a> | ||||
|             </li> | ||||
|         </ul> | ||||
|         <div id="tabs_content" class="tab-content" | ||||
|              style="background: #fff;border-radius: 10px;padding: 2%;margin: 0% 2%;padding-top:4%;"> | ||||
|             <!-- Features Tabs --> | ||||
|             <div class="tab-pane active show" id="features"> | ||||
|                 <div class="container"> | ||||
|                     <section id="other-legal-references"> | ||||
|                         <ul style="margin-bottom:8px !important;font-size: 20px;line-height: 200% !important; font-weight: 300; font-family: 'Lato', 'Open Sans', 'Helvetica', Sans;"> | ||||
|                             <li> | ||||
|                                 Import <b>Lists/Audiences</b> from MailChimp to Odoo. | ||||
|                             </li> | ||||
|                             <li> | ||||
|                                 Import <b>Member/contacts</b> from MailChimp to Odoo. | ||||
|                             </li> | ||||
|                             <li> | ||||
|                                 Import <b>Templates</b> from MailChimp to Odoo. | ||||
|                             </li> | ||||
|                             <li> | ||||
|                                 Import <b>Campaigns</b> from MailChimp to Odoo. | ||||
|                             </li> | ||||
|                             <li> | ||||
|                                 Import <b>Campaign Reports</b> from MailChimp to Odoo. | ||||
|                             </li> | ||||
|                             <li> | ||||
|                                 Import <b>Merge Fields</b> from MailChimp to Odoo and map it with Odoo fields. | ||||
|                             </li> | ||||
|                             <li> | ||||
|                                 Import <b>Segments</b> from MailChimp to Odoo and that will use to filter recipients while sending Campaign | ||||
|                             </li> | ||||
|                             <li> | ||||
|                                 Export/Update <b>Lists/Audiences</b> from Odoo to MailChimp | ||||
|                             </li> | ||||
|                             <li> | ||||
|                                 Export/Update <b>Member/contacts</b> from Odoo to MailChimp | ||||
|                             </li> | ||||
|                             <li> | ||||
|                                 <b>Create Campaign</b> by using MailChimp Template applying recipients filter using Segments and send it to MailChimp | ||||
|                             </li> | ||||
|                             <li> | ||||
|                                 Update record on the fly by setting up <b>Webhook</b> | ||||
|                             </li> | ||||
|                             <li> | ||||
|                                 Easy to get <b>Statistics</b> of Campaigns and Audiences. | ||||
|                             </li> | ||||
|                             <li> | ||||
|                                 Use <b>Multiple Accounts</b> as well. | ||||
|                             </li> | ||||
|                         </ul> | ||||
|                     </section> | ||||
|                 </div> | ||||
|             </div> | ||||
| 
 | ||||
|             <!-- ScreenShots Tabs --> | ||||
|             <div class="tab-pane" id="screenshots"> | ||||
|                 <section class="oe_container"> | ||||
|                     <div class="tab__content"> | ||||
|                         <h2 class="oe_slogan" style="color:#875A7B;">Setup MailChimp account</h2> | ||||
|                         <section class="section"> | ||||
|                             <div class="oe_mt32"> | ||||
|                                 <img class="img img-responsive" src="img/1.png"> | ||||
|                             </div> | ||||
|                         </section> | ||||
| 
 | ||||
|                         <h2 class="oe_slogan" style="color:#875A7B;">Configure auto member synchronization</h2> | ||||
|                         <section class="section"> | ||||
|                             <div class="oe_mt32"> | ||||
|                                 <img class="img img-responsive" src="img/3.png"> | ||||
|                             </div> | ||||
|                         </section> | ||||
| 
 | ||||
|                         <h2 class="oe_slogan" style="color:#875A7B;">View available Lists and Campaigns from | ||||
|                             Account</h2> | ||||
|                         <section class="section"> | ||||
|                             <div class="oe_mt32"> | ||||
|                                 <img class="img img-responsive" src="img/2.png"> | ||||
|                             </div> | ||||
|                         </section> | ||||
| 
 | ||||
|                         <h2 class="oe_slogan" style="color:#875A7B;">MailChimp Lists/Audiences</h2> | ||||
|                         <section class="section"> | ||||
|                             <div class="oe_mt32"> | ||||
|                                 <img class="img img-responsive" src="img/4.gif"> | ||||
|                             </div> | ||||
|                         </section> | ||||
| 
 | ||||
|                         <h2 class="oe_slogan" style="color:#875A7B;">Setting up or change any setting directly in Odoo | ||||
|                             and Update it to MailChimp.</h2> | ||||
|                         <section class="section"> | ||||
|                             <div class="oe_mt32"> | ||||
|                                 <img class="img img-responsive" src="img/5.png"> | ||||
|                             </div> | ||||
|                             <div class="oe_mt32"> | ||||
|                                 <img class="img img-responsive" src="img/6.png"> | ||||
|                             </div> | ||||
|                             <div class="oe_mt32"> | ||||
|                                 <img class="img img-responsive" src="img/7.png"> | ||||
|                             </div> | ||||
|                             <div class="oe_mt32"> | ||||
|                                 <img class="img img-responsive" src="img/8.png"> | ||||
|                             </div> | ||||
|                             <div class="oe_mt32"> | ||||
|                                 <img class="img img-responsive" src="img/9.png"> | ||||
|                             </div> | ||||
|                         </section> | ||||
| 
 | ||||
|                         <h2 class="oe_slogan" style="color:#875A7B;">MailChimp Template</h2> | ||||
|                         <section class="section"> | ||||
|                             <div class="oe_mt32"> | ||||
|                                 <img class="img img-responsive" src="img/11.png"> | ||||
|                             </div> | ||||
|                         </section> | ||||
| 
 | ||||
|                         <h2 class="oe_slogan" style="color:#875A7B;">Do multiple operation on one click</h2> | ||||
|                         <section class="section"> | ||||
|                             <div class="oe_mt32"> | ||||
|                                 <img class="img img-responsive" src="img/12.png"> | ||||
|                             </div> | ||||
|                         </section> | ||||
| 
 | ||||
|                         <h2 class="oe_slogan" style="color:#875A7B;">Easy to identify MailChimp list from other | ||||
|                             lists</h2> | ||||
|                         <section class="section"> | ||||
|                             <div class="oe_mt32"> | ||||
|                                 <img class="img img-responsive" src="img/13.png"> | ||||
|                             </div> | ||||
|                         </section> | ||||
| 
 | ||||
|                         <h2 class="oe_slogan" style="color:#875A7B;">Send Campaign with filtering recipients by selecting segments.</h2> | ||||
|                         <section class="section"> | ||||
|                             <div class="oe_mt32"> | ||||
|                                 <img class="img img-responsive" src="img/5.gif"> | ||||
|                             </div> | ||||
|                         </section> | ||||
| 
 | ||||
|                         <h2 class="oe_slogan" style="color:#875A7B;">Send Campaign by creating mass mailing record and | ||||
|                             get statistics back to Odoo</h2> | ||||
|                         <section class="section"> | ||||
|                             <div class="oe_mt32"> | ||||
|                                 <img class="img img-responsive" src="img/14.png"> | ||||
|                             </div> | ||||
|                             <div class="oe_mt32"> | ||||
|                                 <img class="img img-responsive" src="img/15.1.png"> | ||||
|                             </div> | ||||
|                         </section> | ||||
| 
 | ||||
|                         <h2 class="oe_slogan" style="color:#875A7B;">Quick shoot of export and update process</h2> | ||||
|                         <section class="section"> | ||||
|                             <div class="oe_mt32"> | ||||
|                                 <img class="img img-responsive" src="img/17.gif"> | ||||
|                             </div> | ||||
|                         </section> | ||||
| 
 | ||||
|                         <h2 class="oe_slogan" style="color:#875A7B;">Setup Webhook to get real-time information</h2> | ||||
|                         <section class="section"> | ||||
|                             <div class="oe_mt32"> | ||||
|                                 <img class="img img-responsive" src="img/16.png"> | ||||
|                             </div> | ||||
|                         </section> | ||||
|                     </div> | ||||
|                 </section> | ||||
|             </div> | ||||
| 
 | ||||
|             <div class="tab-pane" id="changelogs" style="margin-bottom:8px !important;font-size: 20px;line-height: 150% !important; font-weight: 300; font-family: 'Lato', 'Open Sans', 'Helvetica', Sans;"> | ||||
|                 <section class="section" style="padding: 3rem 1.5rem;"> | ||||
|                     <div class="container is-narrow" style="margin: 0 auto;position: relative;max-width: 900px;"> | ||||
|                         <div class="content" | ||||
|                              style="font-size: medium;font-weight: 300;font-family: 'Lato', 'Open Sans', 'Helvetica', Sans;"> | ||||
|                             <p> | ||||
|                             <h2 style="margin-top: 1.1428em;text-align: left;display: inline;color: #363636;font-weight: 400;line-height: 1.125;font-size: 1.75em;margin-bottom: .5714em;color: #4608ad;font-weight: 300;"> | ||||
|                                 v11.0.3.0 </h2> Released on 17 October 2019 (UTC). | ||||
| 
 | ||||
|                             <div class="changelog"> | ||||
|                                 <div class="level" style="align-items: center;justify-content: space-between;"> | ||||
|                                     <div class="level-left" | ||||
|                                          style="flex-basis: auto; flex-grow: 0; flex-shrink: 0; align-items: center; justify-content: flex-start;"> | ||||
|                                         <span class="tag" | ||||
|                                               style="font-family: monospace; margin: 0 .5rem; font-size: medium; font-weight: 300;align-items: center; border-radius: 3px;display: inline-flex; font-size: .75rem; height: 2em; justify-content: center; line-height: 1.5; padding-left: .75em; padding-right: .75em; white-space: nowrap; background-color: #6BBE45;color: #fff;">ADD</span> | ||||
|                                         Allow to Import <b> Merge field (Custom fields)</b> and map it with Odoo field. So, user can configure n no of custom fields to export or update to MailChimp. | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                             <br> | ||||
| 
 | ||||
|                             <h2 style="margin-top: 1.1428em;text-align: left;display: inline;color: #363636;font-weight: 400;line-height: 1.125;font-size: 1.75em;margin-bottom: .5714em;color: #4608ad;font-weight: 300;"> | ||||
|                                 v11.0.2.0 </h2> Released on 16 October 2019 (UTC). | ||||
| 
 | ||||
|                             <div class="changelog"> | ||||
|                                 <div class="level" style="align-items: center;justify-content: space-between;"> | ||||
|                                     <div class="level-left" | ||||
|                                          style="flex-basis: auto; flex-grow: 0; flex-shrink: 0; align-items: center; justify-content: flex-start;"> | ||||
|                                         <span class="tag update_fix" | ||||
|                                               style="font-family: monospace; margin: 0 .5rem; font-size: medium; font-weight: 300;align-items: center; border-radius: 3px;display: inline-flex; font-size: .75rem; height: 2em; justify-content: center; line-height: 1.5; padding-left: .75em; padding-right: .75em; white-space: nowrap; background-color: #E31329;color: #fff;">FIX</span> | ||||
|                                         Minor bugs and increase speed of import/fetch member process. | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                                 <div class="level" style="align-items: center;justify-content: space-between;"> | ||||
|                                     <div class="level-left" | ||||
|                                          style="flex-basis: auto; flex-grow: 0; flex-shrink: 0; align-items: center; justify-content: flex-start;"> | ||||
|                                         <span class="tag" | ||||
|                                               style="font-family: monospace; margin: 0 .5rem; font-size: medium; font-weight: 300;align-items: center; border-radius: 3px;display: inline-flex; font-size: .75rem; height: 2em; justify-content: center; line-height: 1.5; padding-left: .75em; padding-right: .75em; white-space: nowrap; background-color: #6BBE45;color: #fff;">ADD</span> | ||||
|                                         Introduced <b>Segments</b> to filter recipients while sending campaign. | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                             <br> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </section> | ||||
|             </div> | ||||
| 
 | ||||
|             <!-- FAQs Section --> | ||||
|             <div class="tab-pane" id="faqs"> | ||||
|                 <section class="oe_container"> | ||||
|                     <div class="s_faq mt32 mb32" style="background-color: transparent !important;"> | ||||
|                         <div class="tab__content"> | ||||
|                             <div class="panel-group" id="accordion"> | ||||
|                                 <div class="panel panel-default"> | ||||
|                                     <div class="panel-heading"> | ||||
|                                         <h4 class="panel-title"> | ||||
|                                             <a data-toggle="collapse" style="color:#A5187E" data-parent="#accordion" | ||||
|                                                href="#collapse2"> | ||||
|                                                 Whom to contact for Customizations?</a> | ||||
|                                         </h4> | ||||
|                                     </div> | ||||
|                                     <div id="collapse2" class="panel-collapse collapse"> | ||||
|                                         <div class="panel-body">You can drop mail at <a href="mailto:info@teqstars.com">info@teqstars.com</a> | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                                 <div class="panel panel-default"> | ||||
|                                     <div class="panel-heading"> | ||||
|                                         <h4 class="panel-title"> | ||||
|                                             <a data-toggle="collapse" style="color:#A5187E" data-parent="#accordion" | ||||
|                                                href="#collapse3"> | ||||
|                                                 Do you provide any support?</a> | ||||
|                                         </h4> | ||||
|                                     </div> | ||||
|                                     <div id="collapse3" class="panel-collapse collapse"> | ||||
|                                         <div class="panel-body">Yes, We provide 90 days free support for bugs. | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                                 <div class="panel panel-default"> | ||||
|                                     <div class="panel-heading"> | ||||
|                                         <h4 class="panel-title"> | ||||
|                                             <a data-toggle="collapse" style="color:#A5187E" data-parent="#accordion" | ||||
|                                                href="#collapse4"> | ||||
|                                                 Does your app support Enterprise Version?</a> | ||||
|                                         </h4> | ||||
|                                     </div> | ||||
|                                     <div id="collapse4" class="panel-collapse collapse"> | ||||
|                                         <div class="panel-body">Yes. | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </section> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </section> | ||||
| 
 | ||||
| <!-- The slideshow --> | ||||
| <div id="demo" class="row carousel slide mt64 mb32" data-ride="carousel"> | ||||
|     <div class="carousel-inner"> | ||||
|         <div class="carousel-item active" style="min-height: 0px;"> | ||||
|             <div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float: left;"> | ||||
|                 <a href="https://www.odoo.com/apps/modules/12.0/fedex_delivery/" target="_blank"> | ||||
|                     <div style="box-shadow: 0 15px 35px rgba(50, 50, 93, 0.1), 0 5px 15px rgba(0, 0, 0, 0.07);border-radius: 10px;"> | ||||
|                         <img class="img img-responsive center-block" | ||||
|                              style="border-radius:10px;" | ||||
|                              src="fedex_odoo.png"> | ||||
|                     </div> | ||||
|                 </a> | ||||
|             </div> | ||||
|             <div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float: left;"> | ||||
|                 <a href="https://apps.odoo.com/apps/modules/12.0/nearby_warehouse/" target="_blank"> | ||||
|                     <div style="box-shadow: 0 15px 35px rgba(50, 50, 93, 0.1), 0 5px 15px rgba(0, 0, 0, 0.07);border-radius: 10px;"> | ||||
|                         <img class="img img-responsive center-block" | ||||
|                              style="border-radius:10px;" | ||||
|                              src="nearby_warehouse.png"> | ||||
|                     </div> | ||||
|                 </a> | ||||
|             </div> | ||||
|             <div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float: left;"> | ||||
|                 <a href="https://apps.odoo.com/apps/modules/12.0/shipstation_delivery/" target="_blank"> | ||||
|                     <div style="box-shadow: 0 15px 35px rgba(50, 50, 93, 0.1), 0 5px 15px rgba(0, 0, 0, 0.07);border-radius: 10px;"> | ||||
|                         <img class="img img-responsive center-block" | ||||
|                              style="border-radius:10px;" | ||||
|                              src="shipstation_odoo.png"> | ||||
|                     </div> | ||||
|                 </a> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="carousel-item" style="min-height: 0px;"> | ||||
|             <div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float: left;"> | ||||
|                 <a href="https://www.odoo.com/apps/modules/12.0/web_sticky_list_header/" target="_blank"> | ||||
|                     <div style="box-shadow: 0 15px 35px rgba(50, 50, 93, 0.1), 0 5px 15px rgba(0, 0, 0, 0.07);border-radius: 10px;"> | ||||
|                         <img class="img img-responsive center-block" | ||||
|                              style="border-radius:10px;" | ||||
|                              src="sticky_header.jpg"> | ||||
|                     </div> | ||||
|                 </a> | ||||
|             </div> | ||||
|             <div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float: left;"> | ||||
|                 <a href="https://apps.odoo.com/apps/modules/12.0/ts_dashboard/" target="_blank"> | ||||
|                     <div style="box-shadow: 0 15px 35px rgba(50, 50, 93, 0.1), 0 5px 15px rgba(0, 0, 0, 0.07);border-radius: 10px;"> | ||||
|                         <img class="img img-responsive center-block" | ||||
|                              style="border-radius:10px;" | ||||
|                              src="all_in_one_dashboard.png"> | ||||
|                     </div> | ||||
|                 </a> | ||||
|             </div> | ||||
|             <div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float: left;"> | ||||
|                 <a href="https://www.odoo.com/apps/modules/11.0/product_alias/" target="_blank"> | ||||
|                     <div style="box-shadow: 0 15px 35px rgba(50, 50, 93, 0.1), 0 5px 15px rgba(0, 0, 0, 0.07);border-radius: 10px;"> | ||||
|                         <img class="img img-responsive center-block" | ||||
|                              style="border-radius:10px;" | ||||
|                              src="product_alias.png"> | ||||
|                     </div> | ||||
|                 </a> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <!-- Left and right controls --> | ||||
|     <a class="carousel-control-prev" href="#demo" data-slide="prev" | ||||
|        style="left:-25px;width: 35px;color: #000;"> | ||||
|                     <span class="carousel-control-prev-icon"><i class="fa fa-chevron-left" | ||||
|                                                                 style="font-size:24px"></i></span> | ||||
|     </a> | ||||
|     <a class="carousel-control-next" href="#demo" data-slide="next" | ||||
|        style="right:-25px;width: 35px;color: #000;"> | ||||
|                 <span class="carousel-control-next-icon"><i class="fa fa-chevron-right" | ||||
|                                                             style="font-size:24px"></i></span> | ||||
|     </a> | ||||
| </div> | ||||
| 
 | ||||
| <div style=" | ||||
|     border: 0; | ||||
|     height: 4px; | ||||
|     border-top: #ddd 1px solid; | ||||
|     border-bottom: #ddd 1px solid; | ||||
|     text-align: center; | ||||
|     position: relative; | ||||
|     clear: both;"><i class="fa fa-star fa-2x" style=" | ||||
|     color: #bbb; | ||||
|     text-align: center; | ||||
|     display: inline-block; | ||||
|     height: 50px; | ||||
|     line-height: 50px; | ||||
|     text-align: center; | ||||
|     width: 50px; | ||||
|     font-size: 20px; | ||||
|     position: absolute; | ||||
|     top: -25px; | ||||
|     left: 50%; | ||||
|     margin: 0 auto 0 -25px; | ||||
|     z-index:999"></i></div> | ||||
| 
 | ||||
| <section class="oe_container oe_dark"> | ||||
|     <div class="oe_row"> | ||||
|         <div class="oe_span12"> | ||||
|             <center> | ||||
|                 <a href="http://teqstars.com" target="_blank"> | ||||
|                     <img class="oe_picture" src="img/TeqStars.png" style="height:128px;weight:128px;"/> | ||||
|                 </a> | ||||
|             </center> | ||||
|         </div> | ||||
|     </div> | ||||
| </section> | ||||
| 
 | ||||
| <section class="oe_container oe_dark"> | ||||
|     <div class="oe_mt16 oe_mb16"> | ||||
|         <h2 class="oe_slogan" style="color:#875A7B;margin-top: 15px;">Need Any Help?</h2> | ||||
|         <h3 class="oe_slogan" style="font-variant: initial;">Contact us for any problem or request!</h3> | ||||
|         <div class="separator" | ||||
|              style="display: block;width: 60px;height: 5px;margin: 15px auto 15px;background-color: #d3b61c;position: relative; border: 1px solid #d3b61c;"></div> | ||||
|     </div> | ||||
|     <div class="oe_slogan"> | ||||
|         <a class="btn btn-primary btn-lg mt8" style="color: #FFFFFF !important;" href="mailto:info@teqstars.com"><i | ||||
|                 class="fa fa-envelope"></i> Want Demo or Need Support? </a> | ||||
|     </div> | ||||
| 
 | ||||
| </section> | ||||
| After Width: | Height: | Size: 55 KiB | 
| After Width: | Height: | Size: 102 KiB | 
| After Width: | Height: | Size: 55 KiB | 
| After Width: | Height: | Size: 38 KiB | 
| After Width: | Height: | Size: 39 KiB | 
| After Width: | Height: | Size: 36 KiB | 
| After Width: | Height: | Size: 71 KiB | 
| After Width: | Height: | Size: 78 KiB | 
|  | @ -0,0 +1,20 @@ | |||
| .o_kanban_view { | ||||
|   .oe_kanban_mailchimp { | ||||
|     .o_title { | ||||
|       margin-bottom: 16px; | ||||
|     } | ||||
|     .o_kanban_primary_bottom { | ||||
|       margin-top: 16px; | ||||
|     } | ||||
|     .oe_margin_top_8 { | ||||
|       margin-top: 8px; | ||||
|     } | ||||
|     .oe_margin_bottom_8 { | ||||
|       margin-bottom: 8px; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .o_white_body { | ||||
|   background-color: white; | ||||
| } | ||||
|  | @ -0,0 +1,8 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <odoo> | ||||
|     <template id="assets_backend" name="mailchimp assets" inherit_id="web.assets_backend"> | ||||
|         <xpath expr="." position="inside"> | ||||
|             <link rel="stylesheet" type="text/scss" href="/mailchimp/static/src/scss/mailchimp.scss"/> | ||||
|         </xpath> | ||||
|     </template> | ||||
| </odoo> | ||||
|  | @ -0,0 +1,145 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <odoo> | ||||
|     <data> | ||||
|         <record id="view_mailchimp_accounts_form" model="ir.ui.view"> | ||||
|             <field name="name">mailchimp.accounts.form</field> | ||||
|             <field name="model">mailchimp.accounts</field> | ||||
|             <field name="arch" type="xml"> | ||||
|                 <form string="MailChimp Accounts"> | ||||
|                     <header> | ||||
|                         <button name="test_connection" string="Test Connection" | ||||
|                                 type="object" class="btn-success"/> | ||||
|                     </header> | ||||
|                     <sheet> | ||||
|                         <div class="oe_title"> | ||||
|                             <label class="oe_edit_only" for="name" string="Account Name"/> | ||||
|                             <h1> | ||||
|                                 <field name="name" placeholder="Account Name"/> | ||||
|                             </h1> | ||||
|                         </div> | ||||
|                         <group> | ||||
|                             <group string="Authentication"> | ||||
|                                 <field name="api_key"/> | ||||
|                             </group> | ||||
|                         </group> | ||||
|                         <group string='MailChimp Tutorial' | ||||
|                                attrs="{'invisible': [('api_key', '!=', False)]}"> | ||||
|                             <ul> | ||||
|                                 <li> | ||||
|                                     <b> | ||||
|                                         Go to | ||||
|                                         <a href='https://mailchimp.com/help/about-api-keys/' target='_blank'>MailChimp | ||||
|                                             Website | ||||
|                                         </a> | ||||
|                                         to create or retrive API Key | ||||
|                                     </b> | ||||
|                                 </li> | ||||
|                             </ul> | ||||
|                         </group> | ||||
|                         <notebook> | ||||
|                             <page name="lists" string="Lists"> | ||||
|                                 <field name="list_ids" nolabel="1"> | ||||
|                                     <tree string="MailChimp Lists/Audiences" create="false"> | ||||
|                                         <field name="name"/> | ||||
|                                         <field name="date_created"/> | ||||
|                                         <field name="list_id"/> | ||||
|                                         <field name="partner_id"/> | ||||
|                                         <field name="list_rating"/> | ||||
|                                         <!--<button name="export_in_mailchimp" attrs="{'invisible': [('list_id','!=', False)]}" string="Export In MailChimp" type="object" icon="fa-external-link"/>--> | ||||
|                                         <!--<button name="update_in_mailchimp" attrs="{'invisible': [('list_id','=', False)]}" string="Update In MailChimp" type="object" icon="fa-share-square-o"/>--> | ||||
|                                         <button name="refresh_list" attrs="{'invisible': [('list_id','=', False)]}" | ||||
|                                                 string="Refresh" type="object" icon="fa-refresh"/> | ||||
|                                     </tree> | ||||
|                                 </field> | ||||
|                             </page> | ||||
|                             <page name="campaign" string="Campaigns"> | ||||
|                                 <field name="campaign_ids" nolabel="1" readonly="1"/> | ||||
|                             </page> | ||||
|                             <page name="settings" string="Settings"> | ||||
|                                 <h2>Member Options</h2> | ||||
|                                 <div class="row mt16 o_settings_container" id="member_option"> | ||||
|                                     <div class="col-lg-6 o_setting_box"> | ||||
|                                         <div class="o_setting_left_pane"> | ||||
|                                             <field name="auto_create_member"/> | ||||
|                                         </div> | ||||
|                                         <div class="o_setting_right_pane"> | ||||
|                                             <label for="auto_create_member" string="Auto Create Member In Odoo?"/> | ||||
|                                             <div class="text-muted"> | ||||
|                                                 While syncing member lists would you like to create new mailling contact | ||||
|                                                 in Odoo if not found in Odoo? | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                     <div class="col-lg-6 o_setting_box"> | ||||
|                                         <div class="o_setting_left_pane"> | ||||
|                                             <field name="auto_refresh_member"/> | ||||
|                                         </div> | ||||
|                                         <div class="o_setting_right_pane"> | ||||
|                                             <label for="auto_refresh_member"/> | ||||
|                                             <div class="text-muted"> | ||||
|                                                 Auto Import/Update member in Odoo at defined in scheduled action. | ||||
|                                             </div> | ||||
|                                             <div attrs="{'invisible': [('auto_refresh_member','=',False)]}" | ||||
|                                                  class="mt16"> | ||||
|                                                 <p attrs="{'invisible': [('auto_refresh_member', '=', False)]}"> | ||||
|                                                     <button name="get_refresh_member_action" icon="fa-arrow-right" type="object" string="Scheduled Actions" class="btn-link"/> | ||||
|                                                 </p> | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                     <div class="col-lg-6 o_setting_box" attrs="{'invisible': [('auto_create_member', '=', False)]}"> | ||||
|                                         <div class="o_setting_left_pane"> | ||||
|                                             <field name="auto_create_partner"/> | ||||
|                                         </div> | ||||
|                                         <div class="o_setting_right_pane"> | ||||
|                                             <label for="auto_create_partner" string="Auto Create Customers In Odoo?"/> | ||||
|                                             <div class="text-muted"> | ||||
|                                                 While syncing member lists would you like to create new Customers(Odoo Contacts) | ||||
|                                                 in Odoo if not found in Odoo? | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                             </page> | ||||
|                         </notebook> | ||||
|                     </sheet> | ||||
|                 </form> | ||||
|             </field> | ||||
|         </record> | ||||
| 
 | ||||
|         <record id="view_mailchimp_accounts_tree" model="ir.ui.view"> | ||||
|             <field name="name">mailchimp.accounts.tree</field> | ||||
|             <field name="model">mailchimp.accounts</field> | ||||
|             <field name="arch" type="xml"> | ||||
|                 <tree string="MailChimp Account"> | ||||
|                     <field name="name"/> | ||||
|                     <field name="api_key"/> | ||||
|                 </tree> | ||||
|             </field> | ||||
|         </record> | ||||
| 
 | ||||
|         <record id="action_mailchimp_accounts" model="ir.actions.act_window"> | ||||
|             <field name="name">MailChimp Accounts</field> | ||||
|             <field name="type">ir.actions.act_window</field> | ||||
|             <field name="res_model">mailchimp.accounts</field> | ||||
|             <field name="view_type">form</field> | ||||
|             <field name="view_mode">tree,form</field> | ||||
|             <field name="context"></field> | ||||
|             <field name="help" type="html"> | ||||
|                 <p class="oe_view_nocontent_create"> | ||||
|                     Click to create a new MailChimp Account. | ||||
|                 </p> | ||||
|                 <p> | ||||
|                     Accounts allows you to manage the all MailChimp operation from here. | ||||
|                 </p> | ||||
|             </field> | ||||
|         </record> | ||||
| 
 | ||||
|         <menuitem id="menu_mailchimp_root" name="MailChimp" parent="mass_mailing.mass_mailing_menu_root" | ||||
|                   sequence="3" web_icon="mailchimp,static/description/icon.png" groups="mass_mailing.group_mass_mailing_user"/> | ||||
| 
 | ||||
|         <menuitem name="Accounts" parent="menu_mailchimp_root" | ||||
|                   action="action_mailchimp_accounts" id="menu_action_mailchimp_accounts" | ||||
|                   sequence="10"/> | ||||
|     </data> | ||||
| </odoo> | ||||
|  | @ -0,0 +1,429 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <odoo> | ||||
|     <data> | ||||
|         <record id="view_mailchimp_lists_form" model="ir.ui.view"> | ||||
|             <field name="name">mailchimp.lists.form</field> | ||||
|             <field name="model">mailchimp.lists</field> | ||||
|             <field name="arch" type="xml"> | ||||
|                 <form string="MailChimp Lists/Audiences"> | ||||
|                     <header> | ||||
|                         <button name="export_in_mailchimp" string="Export In MailChimp" type="object" | ||||
|                                 class="btn-success" attrs="{'invisible' :[('list_id','!=', False)]}"/> | ||||
|                         <button name="update_in_mailchimp" string="Update In MailChimp" type="object" | ||||
|                                 class="btn-success" attrs="{'invisible' :[('list_id','=', False)]}"/> | ||||
|                         <button name="refresh_list" string="Refresh" type="object" class="btn-primary" | ||||
|                                 attrs="{'invisible' :[('list_id','=', False)]}"/> | ||||
|                         <button name="fetch_merge_fields" string="Fetch Merge Fields" type="object" class="btn-primary" | ||||
|                                 attrs="{'invisible' :[('list_id','=', False)]}"/> | ||||
|                         <button name="fetch_segments" string="Fetch Segments" type="object" class="btn-primary" | ||||
|                                 attrs="{'invisible' :[('list_id','=', False)]}"/> | ||||
|                         <button name="fetch_members" string="Fetch Members" type="object" class="btn-primary" | ||||
|                                 attrs="{'invisible' :[('list_id','=', False)]}"/> | ||||
|                     </header> | ||||
|                     <!-- | ||||
|                     <div class="alert alert-warning" role="alert" | ||||
|                          style="margin-bottom:0px;" | ||||
|                          attrs="{'invisible': [('is_update_required','=',False)]}"> | ||||
|                         <i class="fa fa-exclamation-triangle text-warning"></i> | ||||
|                         Some of the data is changed since last sync. Update to <bold>MailChimp</bold> as well or refresh | ||||
|                         list if you want to discard changes. | ||||
|                     </div> --> | ||||
|                     <sheet> | ||||
|                         <div class="oe_button_box"> | ||||
|                             <button name="action_view_recipients" context="{'show_total': True}" | ||||
|                                     type="object" icon="fa-user" class="oe_stat_button"> | ||||
|                                 <field name="contact_total_nbr" string="Total Contacts" widget="statinfo"/> | ||||
|                             </button> | ||||
|                             <button name="action_view_recipients" context="{'show_sub': True}" | ||||
|                                     type="object" icon="fa-user text-success" class="oe_stat_button"> | ||||
|                                 <field name="contact_nbr" string="Subscribers" widget="statinfo"/> | ||||
|                             </button> | ||||
|                             <button name="action_view_recipients" context="{'show_unsub': True}" | ||||
|                                     type="object" icon="fa-user text-info" class="oe_stat_button"> | ||||
|                                 <field name="contact_unsub_nbr" string="Unsubscribed Contacts" widget="statinfo"/> | ||||
|                             </button> | ||||
|                             <button name="action_view_recipients" context="{'show_cleaned': True}" | ||||
|                                     type="object" icon="fa-user text-danger" class="oe_stat_button"> | ||||
|                                 <field name="contact_cleaned_nbr" string="Cleaned Contacts" widget="statinfo"/> | ||||
|                             </button> | ||||
|                         </div> | ||||
|                         <div class="oe_title"> | ||||
|                             <label for="name" string="Audience Name"/> | ||||
|                             <h1> | ||||
|                                 <field name="name" placeholder="Audience Name"/> | ||||
|                             </h1> | ||||
|                             <div name="options"> | ||||
|                                 <div> | ||||
|                                     <label for="account_id" string="Associated Account"/> | ||||
|                                     <field name="account_id" required="1"/> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                         <group> | ||||
|                             <group> | ||||
|                                 <field name="date_created"/> | ||||
|                                 <field name="subscribe_url_short"/> | ||||
|                                 <field name="subscribe_url_long"/> | ||||
|                             </group> | ||||
|                             <group> | ||||
|                                 <field name="list_rating" widget="priority" readonly="1"/> | ||||
|                                 <field name="member_since_last_changed"/> | ||||
|                                 <field name="list_id" invisible="1"/> | ||||
|                                 <field name="is_update_required" invisible="1"/> | ||||
|                             </group> | ||||
|                         </group> | ||||
|                         <notebook> | ||||
|                             <page string="Settings" name="settings"> | ||||
|                                 <h2>Publicity settings</h2> | ||||
|                                 <div class="row mt16 o_settings_container" id="new_sub"> | ||||
|                                     <div class="col-lg-6 o_setting_box"> | ||||
|                                         <div class="o_setting_right_pane"> | ||||
|                                             <label for="visibility" string="Promote my campaigns"/> | ||||
|                                             <div class="text-muted"> | ||||
|                                                 Mailchimp builds tools and services that help people discover | ||||
|                                                 newsletters and campaigns. | ||||
|                                                 When we build these tools, would you like to be discovered? | ||||
|                                             </div> | ||||
|                                             <div> | ||||
|                                                 <field name="visibility" widget="radio" class="mt16"/> | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                     <div class="col-lg-6 o_setting_box"> | ||||
|                                         <div class="o_setting_left_pane"> | ||||
|                                             <field name="use_archive_bar"/> | ||||
|                                         </div> | ||||
|                                         <div class="o_setting_right_pane"> | ||||
|                                             <label for="use_archive_bar" string="Archive bar"/> | ||||
|                                             <div class="text-muted"> | ||||
|                                                 When subscribers click the “view in browser” archive link in your email, | ||||
|                                                 we'll display the archived version of your campaign in their browser, | ||||
|                                                 along with a toolbar that lets them view past campaigns and share your | ||||
|                                                 emails on social networks.. | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                                 <br/> | ||||
|                                 <h2>Form settings</h2> | ||||
|                                 <div class="row mt16 o_settings_container" id="form_settings"> | ||||
|                                     <div class="col-lg-6 o_setting_box"> | ||||
|                                         <div class="o_setting_left_pane"> | ||||
|                                             <field name="double_optin"/> | ||||
|                                         </div> | ||||
|                                         <div class="o_setting_right_pane"> | ||||
|                                             <label for="double_optin"/> | ||||
|                                             <div class="text-muted"> | ||||
|                                                 Send contacts an opt-in confirmation email when they subscribe to your | ||||
|                                                 audience. | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                     <div class="col-lg-6 o_setting_box"> | ||||
|                                         <div class="o_setting_left_pane"> | ||||
|                                             <field name="marketing_permissions"/> | ||||
|                                         </div> | ||||
|                                         <div class="o_setting_right_pane"> | ||||
|                                             <label for="marketing_permissions"/> | ||||
|                                             <div class="text-muted"> | ||||
|                                                 Customize your forms to include GDPR fields. | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                                 <br/> | ||||
|                                 <h2>Campaign defaults</h2> | ||||
|                                 <div class="row mt16 o_settings_container" id="form_settings"> | ||||
|                                     <div class="col-lg-6 o_setting_box"> | ||||
|                                         <div class="o_setting_right_pane"> | ||||
|                                             <label for="from_name"/> | ||||
|                                             <div class="text-muted"> | ||||
|                                                 This is the name your emails will come from. Use something your | ||||
|                                                 subscribers will instantly recognize, like your company name. | ||||
|                                             </div> | ||||
|                                             <div> | ||||
|                                                 <field name="from_name" class="mt16"/> | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                     <div class="col-lg-6 o_setting_box"> | ||||
|                                         <div class="o_setting_left_pane"> | ||||
|                                             <field name="email_type_option"/> | ||||
|                                         </div> | ||||
|                                         <div class="o_setting_right_pane"> | ||||
|                                             <label for="email_type_option"/> | ||||
|                                             <div class="text-muted"> | ||||
|                                                 When people sign up for your audience, you can let them specify which | ||||
|                                                 email format they prefer to receive. If they choose “Plain-text”, then | ||||
|                                                 they won't receive your fancy HTML version. | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                     <!-- commented because we aren't getting this field value from API | ||||
|                                     <div class="col-lg-6 o_setting_box"> | ||||
|                                         <div class="o_setting_left_pane"> | ||||
|                                             <field name="has_welcome"/> | ||||
|                                         </div> | ||||
|                                         <div class="o_setting_right_pane"> | ||||
|                                             <label for="has_welcome"/> | ||||
|                                             <div class="text-muted"> | ||||
|                                                 When people opt-in to your audience, send them an email welcoming them | ||||
|                                                 to your audience. The final welcome email can be edited in the audience | ||||
|                                                 forms designer. | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                     </div> --> | ||||
|                                 </div> | ||||
|                                 <div class="row mt16 o_settings_container" id="form_settings"> | ||||
|                                     <div class="col-lg-6 o_setting_box"> | ||||
|                                         <div class="o_setting_right_pane"> | ||||
|                                             <label for="from_email"/> | ||||
|                                             <div class="text-muted"> | ||||
|                                                 The entered address receives reply emails. Check it regularly to stay in | ||||
|                                                 touch with your audience. | ||||
|                                             </div> | ||||
|                                             <div> | ||||
|                                                 <field name="from_email" class="mt16"/> | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                     <div class="col-lg-6 o_setting_box"> | ||||
|                                         <div class="o_setting_right_pane"> | ||||
|                                             <label for="lang_id"/> | ||||
|                                             <div class="text-muted"> | ||||
|                                                 Default Language for Campaign | ||||
|                                             </div> | ||||
|                                             <div> | ||||
|                                                 <field name="lang_id" required="1" class="mt16"/> | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                                 <div class="row mt16 o_settings_container" id="form_settings"> | ||||
|                                     <div class="col-lg-6 o_setting_box"> | ||||
|                                         <div class="o_setting_right_pane"> | ||||
|                                             <label for="subject"/> | ||||
|                                             <div class="text-muted"> | ||||
|                                                 Keep it relevant and non-spammy. | ||||
|                                             </div> | ||||
|                                             <div> | ||||
|                                                 <field name="subject" class="mt16"/> | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                                 <br/> | ||||
|                                 <h2>New subscriber notifications</h2> | ||||
|                                 <h3>One by one</h3> | ||||
|                                 <div class="text-muted"> | ||||
|                                     Get quick email alerts when subscribers join or leave this audience. | ||||
|                                 </div> | ||||
|                                 <div class="row mt16 o_settings_container" id="new_sub"> | ||||
|                                     <div class="col-lg-6 o_setting_box"> | ||||
|                                         <div class="o_setting_right_pane"> | ||||
|                                             <label for="notify_on_subscribe"/> | ||||
|                                             <div class="text-muted"> | ||||
|                                                 Additional email addresses must be separated by a comma. | ||||
|                                             </div> | ||||
|                                             <div> | ||||
|                                                 <field name="notify_on_subscribe" class="mt16"/> | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                     <div class="col-lg-6 o_setting_box"> | ||||
|                                         <div class="o_setting_right_pane"> | ||||
|                                             <label for="notify_on_unsubscribe"/> | ||||
|                                             <div class="text-muted"> | ||||
|                                                 Additional email addresses must be separated by a comma. | ||||
|                                             </div> | ||||
|                                             <div> | ||||
|                                                 <field name="notify_on_unsubscribe" class="mt16"/> | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                                 <br/> | ||||
|                                 <h2>Required Email Footer Content</h2> | ||||
|                                 <div class="row mt16 o_settings_container" id="new_sub"> | ||||
|                                     <div class="col-lg-6 o_setting_box"> | ||||
|                                         <div class="o_setting_right_pane"> | ||||
|                                             <label for="permission_reminder" string="Permission reminder"/> | ||||
|                                             <div class="text-muted"> | ||||
|                                                 Sometimes people forget they signed up for an email newsletter. To | ||||
|                                                 prevent false spam reports, it’s best to briefly remind your recipients | ||||
|                                                 how they got on your list. | ||||
|                                                 MailChimp’ll automatically place this in your Mailchimp templates | ||||
|                                                 wherever you see *|LIST:DESCRIPTION|*. About permission reminders. | ||||
|                                             </div> | ||||
|                                             <div> | ||||
|                                                 <field name="permission_reminder" class="mt16"/> | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                     <div class="col-lg-6 o_setting_box"> | ||||
|                                         <div class="o_setting_right_pane"> | ||||
|                                             <label for="notify_on_unsubscribe" | ||||
|                                                    string="How can recipients contact you?"/> | ||||
|                                             <div class="text-muted"> | ||||
|                                                 Enter the contact information and physical mailing address for the owner | ||||
|                                                 of this list. This is required by law. If you’re an agency sending on | ||||
|                                                 behalf of a client, enter your client’s information. | ||||
|                                             </div> | ||||
|                                             <div> | ||||
|                                                 <field name="partner_id" required="1" class="mt16"/> | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                                 <br/> | ||||
|                                 <h2>Email Beamer</h2> | ||||
|                                 <div class="row mt16 o_settings_container" id="new_sub"> | ||||
|                                     <div class="col-lg-6 o_setting_box"> | ||||
|                                         <div class="o_setting_right_pane"> | ||||
|                                             <label for="beamer_address" string="Permission reminder"/> | ||||
|                                             <div class="text-muted"> | ||||
|                                                 Every list you create in Mailchimp gets its own special email address. | ||||
|                                                 Send to this private email address from your favorite email client, and | ||||
|                                                 we’ll save those emails as draft campaigns in your account. From there, | ||||
|                                                 you can send the campaign to your Mailchimp list. | ||||
|                                                 Send an email to this private address, and MailChimp’ll reply with a | ||||
|                                                 confirmation link: | ||||
|                                             </div> | ||||
|                                             <div> | ||||
|                                                 <field name="beamer_address" class="mt16"/> | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                             </page> | ||||
|                             <page string="Segments" name="segments"> | ||||
|                                 <field name="segment_ids" nolabel='1'> | ||||
|                                     <tree editable="top" delete="false" create="false"> | ||||
|                                         <field name="name"/> | ||||
|                                     </tree> | ||||
|                                 </field> | ||||
|                             </page> | ||||
|                             <page string="Merge Fields" name="merge_fields"> | ||||
|                                 <div class="alert alert-info info_icon" role="alert"> | ||||
|                                     <span class="fa fa-lightbulb-o fa-lg"><strong> Notes</strong></span> | ||||
|                                     <p> | ||||
|                                         <br/> 1. Make sure you have "FNAME" for First Name and "LNAME" for Last Name in merge field tag. | ||||
|                                         <br/> 2. You don't need to select Odoo Field for Address Type of field and for FNAME and LNAME tag because it will get selected automatically at the time of exporting contact or updating contact. | ||||
|                                     </p> | ||||
|                                 </div> | ||||
|                                 <field name="merge_field_ids" nolabel='1'> | ||||
|                                     <tree editable="top" delete="false" create="false"> | ||||
|                                         <field name="name" readonly="1"/> | ||||
|                                         <field name="type" readonly="1"/> | ||||
|                                         <field name="required" readonly="1"/> | ||||
|                                         <field name="public" readonly="1"/> | ||||
|                                         <field name="tag" readonly="1"/> | ||||
|                                         <field name="default_value" readonly="1"/> | ||||
|                                         <field name="field_id" attrs="{'readonly': ['|', ('type','=', 'address'), ('tag','in', ['FNAME','LNAME'])]}"/> | ||||
|                                         <field name="list_id" invisible="1"/> | ||||
|                                         <field name="merge_id" invisible="1"/> | ||||
|                                     </tree> | ||||
|                                 </field> | ||||
|                             </page> | ||||
|                             <page string="Statistics" name="settings"> | ||||
|                                 <group string="Overview"> | ||||
|                                     <field name="stats_overview_ids" readonly="1" nolabel='1'> | ||||
|                                         <tree editable="bottom" delete="false" create="false" edit="false"> | ||||
|                                             <field name="member_count"/> | ||||
|                                             <field name="unsubscribe_count"/> | ||||
|                                             <field name="cleaned_count"/> | ||||
|                                             <field name="last_sub_date"/> | ||||
|                                             <field name="last_unsub_date"/> | ||||
|                                             <field name="campaign_count"/> | ||||
|                                             <field name="campaign_last_sent"/> | ||||
|                                         </tree> | ||||
|                                     </field> | ||||
|                                 </group> | ||||
|                                 <group col="3"> | ||||
|                                     <group string="Audience performance"> | ||||
|                                         <field name="stats_audience_perf_ids" readonly="1" nolabel='1'> | ||||
|                                             <tree editable="bottom" delete="false" create="false" edit="false"> | ||||
|                                                 <field name="avg_sub_rate" string="Avg Sub %"/> | ||||
|                                                 <field name="avg_unsub_rate" string="Avg Unsub %"/> | ||||
|                                                 <field name="target_sub_rate" string="Target Sub %"/> | ||||
|                                             </tree> | ||||
|                                         </field> | ||||
|                                     </group> | ||||
|                                     <group string="Since Last Campaign"> | ||||
|                                         <field name="stats_since_last_campaign_ids" readonly="1" nolabel='1'> | ||||
|                                             <tree editable="bottom" delete="false" create="false" edit="false"> | ||||
|                                                 <field name="member_count_since_send"/> | ||||
|                                                 <field name="unsubscribe_count_since_send"/> | ||||
|                                                 <field name="cleaned_count_since_send"/> | ||||
|                                             </tree> | ||||
|                                         </field> | ||||
|                                     </group> | ||||
|                                     <group string="Campaign Performance"> | ||||
|                                         <field name="stats_campaign_perf_ids" readonly="1" nolabel='1'> | ||||
|                                             <tree editable="bottom" delete="false" create="false" edit="false"> | ||||
|                                                 <field name="open_rate" string="Open %"/> | ||||
|                                                 <field name="click_rate" string="Click %"/> | ||||
|                                             </tree> | ||||
|                                         </field> | ||||
|                                     </group> | ||||
|                                 </group> | ||||
|                             </page> | ||||
|                         </notebook> | ||||
|                     </sheet> | ||||
|                 </form> | ||||
|             </field> | ||||
|         </record> | ||||
| 
 | ||||
|         <record id="view_mailchimp_lists_tree" model="ir.ui.view"> | ||||
|             <field name="name">mailchimp.lists.tree</field> | ||||
|             <field name="model">mailchimp.lists</field> | ||||
|             <field name="arch" type="xml"> | ||||
|                 <tree string="MailChimp Lists/Audiences"> | ||||
|                     <field name="name"/> | ||||
|                     <field name="date_created"/> | ||||
|                     <field name="list_id"/> | ||||
|                     <field name="partner_id"/> | ||||
|                     <field name="list_rating"/> | ||||
|                     <!--<button name="export_in_mailchimp" attrs="{'invisible': [('list_id','!=', False)]}" string="Export In MailChimp" type="object" icon="fa-external-link"/>--> | ||||
|                     <!--<button name="update_in_mailchimp" attrs="{'invisible': [('list_id','=', False)]}" string="Update In MailChimp" type="object" icon="fa-share-square-o"/>--> | ||||
|                     <button name="refresh_list" attrs="{'invisible': [('list_id','=', False)]}" string="Refresh" | ||||
|                             type="object" icon="fa-refresh"/> | ||||
|                 </tree> | ||||
|             </field> | ||||
|         </record> | ||||
| 
 | ||||
|         <record id="view_mailchimp_lists_search" model="ir.ui.view"> | ||||
|             <field name="name">mailchimp.lists.search</field> | ||||
|             <field name="model">mailchimp.lists</field> | ||||
|             <field name="arch" type="xml"> | ||||
|                 <search string="MailChimp Lists/Audiences"> | ||||
|                     <field name="name"/> | ||||
|                     <field name="list_id"/> | ||||
|                     <field name="account_id"/> | ||||
|                     <group expand="0" string="Group By..."> | ||||
|                         <filter string="Account" name="account" domain="[]" context="{'group_by':'account_id'}"/> | ||||
|                     </group> | ||||
|                 </search> | ||||
|             </field> | ||||
|         </record> | ||||
| 
 | ||||
|         <record id="action_mailchimp_lists" model="ir.actions.act_window"> | ||||
|             <field name="name">MailChimp Lists/Audiences</field> | ||||
|             <field name="type">ir.actions.act_window</field> | ||||
|             <field name="res_model">mailchimp.lists</field> | ||||
|             <field name="view_type">form</field> | ||||
|             <field name="view_mode">tree,form</field> | ||||
|             <field name="context">{}</field> | ||||
|             <field name="help" type="html"> | ||||
|                 <p> | ||||
|                     Retrieve the Lists/Audiences of configured lists on the account using Import | ||||
|                     opeations. | ||||
|                 </p> | ||||
|             </field> | ||||
|         </record> | ||||
| 
 | ||||
|         <menuitem name="Lists/Audiences" parent="menu_mailchimp_root" | ||||
|                   action="action_mailchimp_lists" id="menu_action_mailchimp_listss" | ||||
|                   sequence="20"/> | ||||
|     </data> | ||||
| </odoo> | ||||
|  | @ -0,0 +1,84 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <odoo> | ||||
|     <data> | ||||
|         <record id="view_mailchimp_templates_form" model="ir.ui.view"> | ||||
|             <field name="name">mailchimp.templates.form</field> | ||||
|             <field name="model">mailchimp.templates</field> | ||||
|             <field name="arch" type="xml"> | ||||
|                 <form string="MailChimp Templates" create="0"> | ||||
|                     <header> | ||||
|                     </header> | ||||
|                     <sheet> | ||||
|                         <div class="oe_title"> | ||||
|                             <label class="oe_edit_only" for="name" string="Template Name"/> | ||||
|                             <h1> | ||||
|                                 <field name="name" placeholder="Template Name"/> | ||||
|                             </h1> | ||||
|                             <div name="options"> | ||||
|                                 <div> | ||||
|                                     <field name="drag_and_drop"/> | ||||
|                                     <label for="drag_and_drop"/> | ||||
|                                 </div> | ||||
|                                 <div> | ||||
|                                     <field name="responsive"/> | ||||
|                                     <label for="responsive"/> | ||||
|                                 </div> | ||||
|                                 <div> | ||||
|                                     <label for="account_id" string="Associated Account"/> | ||||
|                                     <field name="account_id" required="1"/> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                         <group> | ||||
|                             <group> | ||||
|                                 <field name="type"/> | ||||
|                                 <field name="category"/> | ||||
|                                 <field name="share_url"/> | ||||
|                             </group> | ||||
|                             <group> | ||||
|                                 <field name="date_created" readonly="1"/> | ||||
|                                 <field name="date_edited" readonly="1"/> | ||||
|                                 <field name="folder_id" readonly="1"/> | ||||
|                             </group> | ||||
|                         </group> | ||||
|                         <notebook> | ||||
|                         </notebook> | ||||
|                     </sheet> | ||||
|                 </form> | ||||
|             </field> | ||||
|         </record> | ||||
| 
 | ||||
|         <record id="view_mailchimp_templates_tree" model="ir.ui.view"> | ||||
|             <field name="name">mailchimp.templates.tree</field> | ||||
|             <field name="model">mailchimp.templates</field> | ||||
|             <field name="arch" type="xml"> | ||||
|                 <tree string="MailChimp Template" create="0"> | ||||
|                     <field name="name"/> | ||||
|                     <field name="date_created"/> | ||||
|                     <field name="type"/> | ||||
|                     <field name="drag_and_drop"/> | ||||
|                     <field name="responsive"/> | ||||
|                     <field name="category"/> | ||||
|                 </tree> | ||||
|             </field> | ||||
|         </record> | ||||
| 
 | ||||
|         <record id="action_mailchimp_templates" model="ir.actions.act_window"> | ||||
|             <field name="name">MailChimp Templates</field> | ||||
|             <field name="type">ir.actions.act_window</field> | ||||
|             <field name="res_model">mailchimp.templates</field> | ||||
|             <field name="view_type">form</field> | ||||
|             <field name="view_mode">tree,form</field> | ||||
|             <field name="context"></field> | ||||
|             <field name="help" type="html"> | ||||
|                 <p class="oe_view_nocontent_create"> | ||||
|                     Click to create a new MailChimp Template. | ||||
|                 </p> | ||||
|             </field> | ||||
|         </record> | ||||
| 
 | ||||
|         <menuitem name="Templates" parent="menu_mailchimp_root" | ||||
|                   action="action_mailchimp_templates" id="menu_action_mailchimp_templates" | ||||
|                   sequence="30"/> | ||||
|     </data> | ||||
| </odoo> | ||||
|  | @ -0,0 +1,68 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <odoo> | ||||
|     <data> | ||||
|         <record id="view_mail_mass_mailing_contact_mail_form" model="ir.ui.view"> | ||||
|             <field name="name">mail.mass_mailing.contact.form</field> | ||||
|             <field name="model">mail.mass_mailing.contact</field> | ||||
|             <field name="inherit_id" ref="mass_mailing.view_mail_mass_mailing_contact_form"/> | ||||
|             <field name="arch" type="xml"> | ||||
|                 <field name="title_id" position="before"> | ||||
|                     <field name="related_partner_id" /> | ||||
|                 </field> | ||||
|             </field> | ||||
|         </record> | ||||
| 
 | ||||
|         <record id="view_mail_mass_mailing_contact_tree" model="ir.ui.view"> | ||||
|             <field name="name">mail.mass_mailing.contact.tree</field> | ||||
|             <field name="model">mail.mass_mailing.contact</field> | ||||
|             <field name="inherit_id" ref="mass_mailing.view_mail_mass_mailing_contact_tree"/> | ||||
|             <field name="arch" type="xml"> | ||||
|                 <field name="opt_out" position="after"> | ||||
|                     <field name="is_email_valid"/> | ||||
|                     <field name="pending_for_export" invisible="1" /> | ||||
|                 </field> | ||||
|             </field> | ||||
|         </record> | ||||
| 
 | ||||
|         <record id="view_mail_mass_mailing_contact_search" model="ir.ui.view"> | ||||
|             <field name="name">mail.mass_mailing.contact.search</field> | ||||
|             <field name="model">mail.mass_mailing.contact</field> | ||||
|             <field name="inherit_id" ref="mass_mailing.view_mail_mass_mailing_contact_search"/> | ||||
|             <field name="arch" type="xml"> | ||||
|                 <filter name="not_opt_out" position="after"> | ||||
|                     <separator/> | ||||
|                     <filter string="Pending for Export" name="pending_export" domain="[('pending_for_export', '=', True)]"/> | ||||
|                     <separator/> | ||||
|                     <filter string="Unsubscribers" name="unsub_contact" domain="[('opt_out', '=', True), ('is_email_valid', '=', True)]" | ||||
|                             invisible="'default_list_ids' not in context"/> | ||||
|                     <filter string="Cleaned" name="cleaned_contact" domain="[('is_email_valid', '=', False)]" | ||||
|                             invisible="'default_list_ids' not in context"/> | ||||
|                 </filter> | ||||
|             </field> | ||||
|         </record> | ||||
| 
 | ||||
|         <record id="ir_actions_mass_mailing_contact_export" model="ir.actions.server"> | ||||
|             <field name="name">Export To MailChimp</field> | ||||
|             <field name="type">ir.actions.server</field> | ||||
|             <field name="model_id" ref="mass_mailing.model_mail_mass_mailing_contact"/> | ||||
|             <field name="state">code</field> | ||||
|             <field name="code"> | ||||
|                 if records: | ||||
|                     records.action_export_to_mailchimp() | ||||
|             </field> | ||||
|             <field name="binding_model_id" ref="mass_mailing.model_mail_mass_mailing_contact"/> | ||||
|         </record> | ||||
| 
 | ||||
|         <record id="ir_actions_mass_mailing_contact_update" model="ir.actions.server"> | ||||
|             <field name="name">Update To MailChimp</field> | ||||
|             <field name="type">ir.actions.server</field> | ||||
|             <field name="model_id" ref="mass_mailing.model_mail_mass_mailing_contact"/> | ||||
|             <field name="state">code</field> | ||||
|             <field name="code"> | ||||
|                 if records: | ||||
|                     records.action_update_to_mailchimp() | ||||
|             </field> | ||||
|             <field name="binding_model_id" ref="mass_mailing.model_mail_mass_mailing_contact"/> | ||||
|         </record> | ||||
|     </data> | ||||
| </odoo> | ||||
|  | @ -0,0 +1,20 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <odoo> | ||||
|     <data> | ||||
|         <record id="view_mail_mass_mailing_list_form" model="ir.ui.view"> | ||||
|             <field name="name">mail.mass_mailing.list.form</field> | ||||
|             <field name="model">mail.mass_mailing.list</field> | ||||
|             <field name="inherit_id" ref="mass_mailing.view_mail_mass_mailing_list_form"/> | ||||
|             <field name="arch" type="xml"> | ||||
|                 <xpath expr="//div[hasclass('oe_title')]/h1" position="after"> | ||||
|                     <div name="options" groups="base.group_user"> | ||||
|                         <div> | ||||
|                             <label for="mailchimp_list_id" string="Associated MailChimp Account"/> | ||||
|                             <field name="mailchimp_list_id"/> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </xpath> | ||||
|             </field> | ||||
|         </record> | ||||
|     </data> | ||||
| </odoo> | ||||
|  | @ -0,0 +1,48 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <odoo> | ||||
|     <data> | ||||
|         <record id="view_mail_mass_mailing_form" model="ir.ui.view"> | ||||
|             <field name="name">mail.mass_mailing.form</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"> | ||||
|                 <button name="put_in_queue" position="after"> | ||||
|                     <button name="action_schedule_date" type="object" attrs="{'invisible': [('state', 'in', ('in_queue', 'done'))]}" class="btn-secondary" string="Schedule"/> | ||||
|                 </button> | ||||
|                 <xpath expr="//sheet/group[1]" position="inside"> | ||||
|                     <label for="mailchimp_template_id" string="MailChimp Template"/> | ||||
|                     <div> | ||||
|                         <field name="mailchimp_template_id" nolabel="1"/> | ||||
|                     </div> | ||||
|                     <label for="mailchimp_segment_id"/> | ||||
|                     <div> | ||||
|                         <field name="mailchimp_list_id" invisible="1"/> | ||||
|                         <field name="mailchimp_segment_id" nolabel="1" domain="[('list_id','=', mailchimp_list_id)]"/> | ||||
|                     </div> | ||||
|                     <label for="mailchimp_champ_type" string="Type" attrs="{'required':[('mailchimp_template_id','!=',False)], 'invisible' : [('mailchimp_template_id','=',False)]}"/> | ||||
|                     <div> | ||||
|                         <field name="mailchimp_champ_type" nolabel="1" attrs="{'required':[('mailchimp_template_id','!=',False)], 'invisible' : [('mailchimp_template_id','=',False)]}"/> | ||||
|                     </div> | ||||
|                 </xpath> | ||||
|             </field> | ||||
|         </record> | ||||
| 
 | ||||
|         <record id="view_mail_mass_mailing_kanban" model="ir.ui.view"> | ||||
|             <field name="name">mail.mass_mailing.kanban</field> | ||||
|             <field name="model">mail.mass_mailing</field> | ||||
|             <field name="inherit_id" ref="mass_mailing.view_mail_mass_mailing_kanban"/> | ||||
|             <field name="arch" type="xml"> | ||||
|                 <field name="color" position="after"> | ||||
|                     <field name="mailchimp_id"/> | ||||
|                 </field> | ||||
|                 <xpath expr="//div[hasclass('o_kanban_record_headings')]/h3" position="after"> | ||||
|                     <t t-if="record.mailchimp_id.raw_value"> | ||||
|                         <span class="badge badge-primary oe_inline o_enterprise_label"> | ||||
|                             MailChimp | ||||
|                         </span> | ||||
|                     </t> | ||||
|                 </xpath> | ||||
|             </field> | ||||
|         </record> | ||||
|     </data> | ||||
| </odoo> | ||||
|  | @ -0,0 +1,20 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <odoo> | ||||
|     <record id="view_partner_mailchimp_form" model="ir.ui.view"> | ||||
|         <field name="name">res.partner.mailchimp.inherit</field> | ||||
|         <field name="model">res.partner</field> | ||||
|         <field name="inherit_id" ref="base.view_partner_form"/> | ||||
|         <field name="arch" type="xml"> | ||||
|             <xpath expr="//notebook[last()]" position="inside"> | ||||
|                 <page string="MailChimp List" name="mailchimp_list"> | ||||
|                     <field name="subscription_list_ids" domain="[('mailchimp_list_id','!=',False)]" readonly="True"> | ||||
|                         <tree> | ||||
|                             <field name="list_id"/> | ||||
|                             <field name="opt_out"/> | ||||
|                         </tree> | ||||
|                     </field> | ||||
|                 </page> | ||||
|             </xpath> | ||||
|         </field> | ||||
|     </record> | ||||
| </odoo> | ||||
|  | @ -0,0 +1,4 @@ | |||
| from . import import_export_operation_wizard | ||||
| from . import test_mailing | ||||
| from . import mass_mailing_schedule_date | ||||
| from . import partner_export_update_wizard | ||||
|  | @ -0,0 +1,40 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <odoo> | ||||
|     <data> | ||||
|         <record id="import_export_operation_form" model="ir.ui.view"> | ||||
|             <field name="name">Import/Export Operation</field> | ||||
|             <field name="model">mailchimp.import.export.operation</field> | ||||
|             <field name="arch" type="xml"> | ||||
|                 <form string="Import/Export"> | ||||
|                 	<group> | ||||
| 	                	<field name='account_ids' string="Account(s)" widget="many2many_tags" options="{'no_create':True,'no_create_edit': True}"/> | ||||
|                 	</group> | ||||
|                 	<group> | ||||
|                 		<group string="Import"> | ||||
|                 			<field name="get_lists" /> | ||||
| 							<field name="get_templates" /> | ||||
| 							<field name="get_campaigns" /> | ||||
|                 		</group> | ||||
|                 	</group> | ||||
| 					<footer> | ||||
| 						<button string="Process" class="oe_highlight" type="object" name="process_operation"/> | ||||
|                         <button string="Cancel" class="oe_highlight" special="cancel" /> | ||||
|                     </footer> | ||||
|                </form> | ||||
|             </field> | ||||
|         </record> | ||||
| 
 | ||||
| 		<record id="action_import_export_operation" model="ir.actions.act_window"> | ||||
|          	<field name="name">Import/Export Operations</field> | ||||
|          	<field name="type">ir.actions.act_window</field> | ||||
|          	<field name="res_model">mailchimp.import.export.operation</field> | ||||
|          	<field name="view_type">form</field> | ||||
|          	<field name="view_mode">form</field> | ||||
|          	<field name="target">new</field> | ||||
|          </record> | ||||
| 
 | ||||
| 		<menuitem name="MailChimp Operations" parent="menu_mailchimp_root" | ||||
| 		  action="action_import_export_operation" id="menu_action_mailchimp_opearation" | ||||
| 		  sequence="20"/> | ||||
|     </data> | ||||
| </odoo> | ||||
|  | @ -0,0 +1,33 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| 
 | ||||
| from odoo import api, fields, models | ||||
| 
 | ||||
| 
 | ||||
| class ImportExportOperation(models.TransientModel): | ||||
|     _name = "mailchimp.import.export.operation" | ||||
|     _description = "Import/Export Operation" | ||||
| 
 | ||||
|     account_ids = fields.Many2many('mailchimp.accounts', required=True, | ||||
|                                    help="Select Account from which you want to perform import/export operation") | ||||
| 
 | ||||
|     get_lists = fields.Boolean("Lists/Audiences", help="Obtains available lists from MailChimp") | ||||
|     get_templates = fields.Boolean("Templates", help="Get a list of an account's available templates.") | ||||
|     get_campaigns = fields.Boolean("Campaigns", help="Get a list of campaigns.") | ||||
| 
 | ||||
|     @api.model | ||||
|     def default_get(self, fields): | ||||
|         res = super(ImportExportOperation, self).default_get(fields) | ||||
|         accounts = self.env['mailchimp.accounts'].search([]) | ||||
|         res.update({'account_ids': [(6, 0, accounts.ids)]}) | ||||
|         return res | ||||
| 
 | ||||
|     @api.multi | ||||
|     def process_operation(self): | ||||
|         for account in self.account_ids: | ||||
|             if self.get_lists: | ||||
|                 account.import_lists() | ||||
|             if self.get_templates: | ||||
|                 account.import_templates() | ||||
|             if self.get_campaigns: | ||||
|                 account.import_campaigns() | ||||
|         return True | ||||
|  | @ -0,0 +1,27 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| from datetime import datetime | ||||
| from odoo import api, fields, models, _ | ||||
| from odoo.exceptions import ValidationError | ||||
| from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT | ||||
| 
 | ||||
| 
 | ||||
| class MassMailingScheduleDate(models.TransientModel): | ||||
|     _name = 'mass.mailing.schedule.date' | ||||
|     _description = 'Mass Mailing Scheduling' | ||||
| 
 | ||||
|     schedule_date = fields.Datetime(string='Schedule in the Future') | ||||
|     mass_mailing_id = fields.Many2one('mail.mass_mailing', required=True) | ||||
| 
 | ||||
|     @api.constrains('schedule_date') | ||||
|     def _check_schedule_date(self): | ||||
|         for scheduler in self: | ||||
|             if scheduler.schedule_date < fields.Datetime.now(): | ||||
|                 raise ValidationError(_('Please select a date equal/or greater than the current date.')) | ||||
| 
 | ||||
|     def set_schedule_date(self): | ||||
|         self.ensure_one() | ||||
|         mailing = self.mass_mailing_id | ||||
|         if mailing.mailchimp_template_id: | ||||
|             schedule_date = datetime.strptime(self.schedule_date, DEFAULT_SERVER_DATETIME_FORMAT) | ||||
|             mailing.schedule_mailchimp_champaign(schedule_date) | ||||
|         self.mass_mailing_id.write({'schedule_date': self.schedule_date, 'state': 'in_queue'}) | ||||
|  | @ -0,0 +1,32 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <odoo> | ||||
|     <record id="mass_mailing_schedule_date_view_form"  model="ir.ui.view"> | ||||
|         <field name="name">mass.mailing.schedule.date.view.form</field> | ||||
|         <field name="model">mass.mailing.schedule.date</field> | ||||
|         <field name="arch" type="xml"> | ||||
|             <form string="Take Future Schedule Date"> | ||||
|                 <group> | ||||
|                     <group> | ||||
|                         <field name="schedule_date" required="1"/> | ||||
|                     </group> | ||||
|                 </group> | ||||
|                 <p class="oe_grey"> | ||||
|                     If you are using MailChimp Campaigns then Campaigns may only be scheduled to send on the | ||||
|                     quarter-hour (:00, :15, :30, :45) | ||||
|                 </p> | ||||
|                 <footer> | ||||
|                     <button string="Schedule" name="set_schedule_date" type="object" class="btn-primary"/> | ||||
|                     <button string="Discard " class="btn-secondary" special="cancel" /> | ||||
|                 </footer> | ||||
|             </form> | ||||
|         </field> | ||||
|     </record> | ||||
| 
 | ||||
|     <record id="mass_mailing_schedule_date_action" model="ir.actions.act_window"> | ||||
|         <field name="name">When do you want to send your mailing?</field> | ||||
|         <field name="res_model">mass.mailing.schedule.date</field> | ||||
|         <field name="type">ir.actions.act_window</field> | ||||
|         <field name="view_mode">form</field> | ||||
|         <field name="target">new</field> | ||||
|     </record> | ||||
| </odoo> | ||||
|  | @ -0,0 +1,55 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| from odoo import models, fields, api | ||||
| 
 | ||||
| 
 | ||||
| class ParterExportMailchimp(models.TransientModel): | ||||
|     _name = 'partner.export.mailchimp' | ||||
| 
 | ||||
|     odoo_list_ids = fields.Many2many('mailchimp.lists', string='MailChimp Lists', domain=[('odoo_list_id', '!=', False)]) | ||||
| 
 | ||||
|     @api.multi | ||||
|     def get_mailing_contact_id(self, partner_id, force_create=False): | ||||
|         mailing_contact_obj = self.env['mail.mass_mailing.contact'] | ||||
|         if not partner_id.email: | ||||
|             return False | ||||
|         query = """ | ||||
|                 SELECT id  | ||||
|                   FROM mail_mass_mailing_contact | ||||
|                 WHERE LOWER(substring(email, '([^ ,;<@]+@[^> ,;]+)')) = LOWER(substring('{}', '([^ ,;<@]+@[^> ,;]+)'))""".format( | ||||
|             partner_id.email) | ||||
|         self._cr.execute(query) | ||||
|         contact_id = self._cr.fetchone() | ||||
|         contact_id = contact_id[0] if contact_id else False | ||||
|         prepared_vals = {'name': partner_id.name, 'email': partner_id.email, 'tag_ids': [(6, 0, partner_id.category_id.ids)], 'country_id': partner_id.country_id.id} | ||||
|         if contact_id: | ||||
|             contact_id = mailing_contact_obj.browse(contact_id) | ||||
|             contact_id.write(prepared_vals) | ||||
|         if not contact_id and force_create: | ||||
|             contact_id = mailing_contact_obj.create(prepared_vals) | ||||
|         return contact_id.id | ||||
| 
 | ||||
|     @api.multi | ||||
|     def action_export_partner_mailchimp(self): | ||||
|         mailing_contact_obj = self.env['mail.mass_mailing.contact'] | ||||
|         partner_ids = self.env['res.partner'].search([('id', 'in', self._context.get('active_ids', []))]) | ||||
|         for partner_id in partner_ids: | ||||
|             for odoo_list_id in self.odoo_list_ids: | ||||
|                 contact_id = self.get_mailing_contact_id(partner_id, force_create=True) | ||||
|                 if contact_id: | ||||
|                     contact_id = mailing_contact_obj.browse(contact_id) | ||||
|                     if odoo_list_id.id not in contact_id.subscription_list_ids.mapped('list_id').mapped('mailchimp_list_id').ids: | ||||
|                         vals = {'list_id': odoo_list_id.odoo_list_id.id, 'contact_id': contact_id.id} | ||||
|                         contact_id.subscription_list_ids.create(vals) | ||||
|                         contact_id.action_export_to_mailchimp() | ||||
|         return True | ||||
| 
 | ||||
|     @api.multi | ||||
|     def action_update_partner_mailchimp(self): | ||||
|         mailing_contact_obj = self.env['mail.mass_mailing.contact'] | ||||
|         partner_ids = self.env['res.partner'].search([('id', 'in', self._context.get('active_ids', []))]) | ||||
|         for partner_id in partner_ids: | ||||
|             contact_id = self.get_mailing_contact_id(partner_id) | ||||
|             if contact_id: | ||||
|                 contact_id = mailing_contact_obj.browse(contact_id) | ||||
|                 contact_id.action_update_to_mailchimp() | ||||
|         return True | ||||
|  | @ -0,0 +1,55 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <odoo> | ||||
| 
 | ||||
|     <record id="mailchimp_export_update_view_form" model="ir.ui.view"> | ||||
|         <field name="name">partner.export.mailchimp.form</field> | ||||
|         <field name="model">partner.export.mailchimp</field> | ||||
|         <field name="arch" type="xml"> | ||||
|             <form string=""> | ||||
|                 <sheet> | ||||
|                     <group invisible="context.get('update_in_mailchimp', False)"> | ||||
|                         <field name="odoo_list_ids" required="not context.get('update_in_mailchimp', False)" widget="many2many_tags"/> | ||||
|                     </group> | ||||
|                 </sheet> | ||||
|                 <footer> | ||||
|                     <group> | ||||
|                         <span> | ||||
|                             <button string="Update" invisible="not context.get('update_in_mailchimp', False)" type="object" class="oe_highlight" name="action_update_partner_mailchimp"/> | ||||
|                             <button string="Export" invisible="context.get('update_in_mailchimp', False)" type="object" class="oe_highlight" name="action_export_partner_mailchimp"/> | ||||
|                             <button string="Cancel" class="oe_link" special="cancel" /> | ||||
|                         </span> | ||||
|                     </group> | ||||
|                 </footer> | ||||
|             </form> | ||||
|         </field> | ||||
|     </record> | ||||
| 
 | ||||
|     <record id="mailchimp_export_update_form_action" model="ir.actions.act_window"> | ||||
|         <field name="name">Export to MailChimp</field> | ||||
|         <field name="res_model">partner.export.mailchimp</field> | ||||
|         <field name="view_type">form</field> | ||||
|         <field name="view_mode">form</field> | ||||
|         <field name="target">new</field> | ||||
|     </record> | ||||
| 
 | ||||
|     <!-- Add action entry in the Action Menu for Partners --> | ||||
|     <act_window id="mailchimp_export_action" | ||||
|                 name="Export to MailChimp" | ||||
|                 src_model="res.partner" | ||||
|                 res_model="partner.export.mailchimp" | ||||
|                 view_type="form" | ||||
|                 view_mode="form" | ||||
|                 key2="client_action_multi" | ||||
|                 target="new"/> | ||||
| 
 | ||||
|         <act_window id="mailchimp_update_action" | ||||
|                 name="Update to MailChimp" | ||||
|                 src_model="res.partner" | ||||
|                 res_model="partner.export.mailchimp" | ||||
|                 view_type="form" | ||||
|                 view_mode="form" | ||||
|                 key2="client_action_multi" | ||||
|                 context="{'update_in_mailchimp': True}" | ||||
|                 target="new"/> | ||||
| 
 | ||||
| </odoo> | ||||
|  | @ -0,0 +1,36 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| 
 | ||||
| from odoo import api, fields, models, tools | ||||
| 
 | ||||
| 
 | ||||
| class TestMassMailing(models.TransientModel): | ||||
|     _inherit = 'mail.mass_mailing.test' | ||||
| 
 | ||||
|     @api.multi | ||||
|     def send_mail_test(self): | ||||
|         self.ensure_one() | ||||
|         mails = self.env['mail.mail'] | ||||
|         mailing = self.mass_mailing_id | ||||
|         test_emails = tools.email_split(self.email_to) | ||||
|         if mailing.mailchimp_template_id: | ||||
|             return mailing.send_test_mail_mailchimp(test_emails) | ||||
|         mass_mail_layout = self.env.ref('mass_mailing.mass_mailing_mail_layout') | ||||
|         for test_mail in test_emails: | ||||
|             # Convert links in absolute URLs before the application of the shortener | ||||
|             mailing.write({'body_html': self.env['mail.thread']._replace_local_links(mailing.body_html)}) | ||||
|             body = tools.html_sanitize(mailing.body_html, sanitize_attributes=True, sanitize_style=True) | ||||
|             mail_values = { | ||||
|                 'email_from': mailing.email_from, | ||||
|                 'reply_to': mailing.reply_to, | ||||
|                 'email_to': test_mail, | ||||
|                 'subject': mailing.name, | ||||
|                 'body_html': mass_mail_layout.render({'body': body}, engine='ir.qweb', minimal_qcontext=True), | ||||
|                 'notification': True, | ||||
|                 'mailing_id': mailing.id, | ||||
|                 'attachment_ids': [(4, attachment.id) for attachment in mailing.attachment_ids], | ||||
|                 'auto_delete': True, | ||||
|             } | ||||
|             mail = self.env['mail.mail'].create(mail_values) | ||||
|             mails |= mail | ||||
|         mails.send() | ||||
|         return True | ||||