From 7f42251614980377a2b25679611d4252544640e4 Mon Sep 17 00:00:00 2001 From: Olivier Sarrat Date: Mon, 20 Feb 2017 17:08:24 +0100 Subject: [PATCH] With all custom fields for individual contacts. --- groupeurd_crm/__openerp__.py | 17 +- groupeurd_crm/models.py | 55 +- groupeurd_crm/views.xml | 105 ++++ partner_social_fields/__init__.py | 9 + partner_social_fields/__openerp__.py | 51 ++ partner_social_fields/i18n/ca.po | 38 ++ partner_social_fields/i18n/en.po | 38 ++ partner_social_fields/i18n/es.po | 38 ++ partner_social_fields/i18n/fr.po | 37 ++ partner_social_fields/i18n/gl.po | 38 ++ partner_social_fields/i18n/it.po | 38 ++ .../partner_social_fields.py | 69 +++ .../partner_social_fields_view.xml | 25 + web_linkedin/__init__.py | 2 + web_linkedin/__openerp__.py | 20 + web_linkedin/i18n/af.po | 221 ++++++++ web_linkedin/i18n/ar.po | 224 ++++++++ web_linkedin/i18n/bg.po | 221 ++++++++ web_linkedin/i18n/bs.po | 221 ++++++++ web_linkedin/i18n/ca.po | 222 ++++++++ web_linkedin/i18n/cs.po | 222 ++++++++ web_linkedin/i18n/da.po | 222 ++++++++ web_linkedin/i18n/de.po | 222 ++++++++ web_linkedin/i18n/el.po | 222 ++++++++ web_linkedin/i18n/en_GB.po | 221 ++++++++ web_linkedin/i18n/es.po | 222 ++++++++ web_linkedin/i18n/es_AR.po | 223 ++++++++ web_linkedin/i18n/es_BO.po | 221 ++++++++ web_linkedin/i18n/es_CL.po | 221 ++++++++ web_linkedin/i18n/es_CO.po | 221 ++++++++ web_linkedin/i18n/es_CR.po | 221 ++++++++ web_linkedin/i18n/es_DO.po | 221 ++++++++ web_linkedin/i18n/es_EC.po | 221 ++++++++ web_linkedin/i18n/es_MX.po | 221 ++++++++ web_linkedin/i18n/es_PE.po | 221 ++++++++ web_linkedin/i18n/es_PY.po | 221 ++++++++ web_linkedin/i18n/es_VE.po | 221 ++++++++ web_linkedin/i18n/et.po | 221 ++++++++ web_linkedin/i18n/eu.po | 222 ++++++++ web_linkedin/i18n/fa.po | 221 ++++++++ web_linkedin/i18n/fi.po | 225 ++++++++ web_linkedin/i18n/fr.po | 225 ++++++++ web_linkedin/i18n/fr_CA.po | 221 ++++++++ web_linkedin/i18n/gl.po | 221 ++++++++ web_linkedin/i18n/gu.po | 221 ++++++++ web_linkedin/i18n/he.po | 221 ++++++++ web_linkedin/i18n/hi.po | 221 ++++++++ web_linkedin/i18n/hr.po | 222 ++++++++ web_linkedin/i18n/hu.po | 222 ++++++++ web_linkedin/i18n/id.po | 221 ++++++++ web_linkedin/i18n/it.po | 222 ++++++++ web_linkedin/i18n/ja.po | 221 ++++++++ web_linkedin/i18n/ka.po | 222 ++++++++ web_linkedin/i18n/kab.po | 223 ++++++++ web_linkedin/i18n/ko.po | 221 ++++++++ web_linkedin/i18n/lt.po | 221 ++++++++ web_linkedin/i18n/lv.po | 221 ++++++++ web_linkedin/i18n/mk.po | 222 ++++++++ web_linkedin/i18n/mn.po | 222 ++++++++ web_linkedin/i18n/nb.po | 221 ++++++++ web_linkedin/i18n/nl.po | 222 ++++++++ web_linkedin/i18n/nl_BE.po | 221 ++++++++ web_linkedin/i18n/pl.po | 222 ++++++++ web_linkedin/i18n/pt.po | 222 ++++++++ web_linkedin/i18n/pt_BR.po | 225 ++++++++ web_linkedin/i18n/ro.po | 222 ++++++++ web_linkedin/i18n/ru.po | 223 ++++++++ web_linkedin/i18n/sk.po | 221 ++++++++ web_linkedin/i18n/sl.po | 223 ++++++++ web_linkedin/i18n/sr.po | 221 ++++++++ web_linkedin/i18n/sr@latin.po | 221 ++++++++ web_linkedin/i18n/sv.po | 223 ++++++++ web_linkedin/i18n/th.po | 221 ++++++++ web_linkedin/i18n/tr.po | 222 ++++++++ web_linkedin/i18n/uk.po | 221 ++++++++ web_linkedin/i18n/vi.po | 221 ++++++++ web_linkedin/i18n/web_linkedin.pot | 215 +++++++ web_linkedin/i18n/zh_CN.po | 226 ++++++++ web_linkedin/i18n/zh_TW.po | 221 ++++++++ web_linkedin/static/src/css/linkedin.css | 64 +++ web_linkedin/static/src/img/Linkedin_blue.png | Bin 0 -> 5435 bytes .../static/src/img/ghost_profile_60x60_v1.png | Bin 0 -> 453 bytes .../static/src/img/linkedin-profile.jpeg | Bin 0 -> 1231 bytes web_linkedin/static/src/js/linkedin.js | 528 ++++++++++++++++++ web_linkedin/static/src/xml/linkedin.xml | 49 ++ web_linkedin/views/web_linkedin.xml | 13 + web_linkedin/web_linkedin.py | 92 +++ web_linkedin/web_linkedin_view.xml | 51 ++ 88 files changed, 15552 insertions(+), 11 deletions(-) create mode 100644 partner_social_fields/__init__.py create mode 100644 partner_social_fields/__openerp__.py create mode 100644 partner_social_fields/i18n/ca.po create mode 100644 partner_social_fields/i18n/en.po create mode 100644 partner_social_fields/i18n/es.po create mode 100644 partner_social_fields/i18n/fr.po create mode 100644 partner_social_fields/i18n/gl.po create mode 100644 partner_social_fields/i18n/it.po create mode 100644 partner_social_fields/partner_social_fields.py create mode 100644 partner_social_fields/partner_social_fields_view.xml create mode 100644 web_linkedin/__init__.py create mode 100644 web_linkedin/__openerp__.py create mode 100644 web_linkedin/i18n/af.po create mode 100644 web_linkedin/i18n/ar.po create mode 100644 web_linkedin/i18n/bg.po create mode 100644 web_linkedin/i18n/bs.po create mode 100644 web_linkedin/i18n/ca.po create mode 100644 web_linkedin/i18n/cs.po create mode 100644 web_linkedin/i18n/da.po create mode 100644 web_linkedin/i18n/de.po create mode 100644 web_linkedin/i18n/el.po create mode 100644 web_linkedin/i18n/en_GB.po create mode 100644 web_linkedin/i18n/es.po create mode 100644 web_linkedin/i18n/es_AR.po create mode 100644 web_linkedin/i18n/es_BO.po create mode 100644 web_linkedin/i18n/es_CL.po create mode 100644 web_linkedin/i18n/es_CO.po create mode 100644 web_linkedin/i18n/es_CR.po create mode 100644 web_linkedin/i18n/es_DO.po create mode 100644 web_linkedin/i18n/es_EC.po create mode 100644 web_linkedin/i18n/es_MX.po create mode 100644 web_linkedin/i18n/es_PE.po create mode 100644 web_linkedin/i18n/es_PY.po create mode 100644 web_linkedin/i18n/es_VE.po create mode 100644 web_linkedin/i18n/et.po create mode 100644 web_linkedin/i18n/eu.po create mode 100644 web_linkedin/i18n/fa.po create mode 100644 web_linkedin/i18n/fi.po create mode 100644 web_linkedin/i18n/fr.po create mode 100644 web_linkedin/i18n/fr_CA.po create mode 100644 web_linkedin/i18n/gl.po create mode 100644 web_linkedin/i18n/gu.po create mode 100644 web_linkedin/i18n/he.po create mode 100644 web_linkedin/i18n/hi.po create mode 100644 web_linkedin/i18n/hr.po create mode 100644 web_linkedin/i18n/hu.po create mode 100644 web_linkedin/i18n/id.po create mode 100644 web_linkedin/i18n/it.po create mode 100644 web_linkedin/i18n/ja.po create mode 100644 web_linkedin/i18n/ka.po create mode 100644 web_linkedin/i18n/kab.po create mode 100644 web_linkedin/i18n/ko.po create mode 100644 web_linkedin/i18n/lt.po create mode 100644 web_linkedin/i18n/lv.po create mode 100644 web_linkedin/i18n/mk.po create mode 100644 web_linkedin/i18n/mn.po create mode 100644 web_linkedin/i18n/nb.po create mode 100644 web_linkedin/i18n/nl.po create mode 100644 web_linkedin/i18n/nl_BE.po create mode 100644 web_linkedin/i18n/pl.po create mode 100644 web_linkedin/i18n/pt.po create mode 100644 web_linkedin/i18n/pt_BR.po create mode 100644 web_linkedin/i18n/ro.po create mode 100644 web_linkedin/i18n/ru.po create mode 100644 web_linkedin/i18n/sk.po create mode 100644 web_linkedin/i18n/sl.po create mode 100644 web_linkedin/i18n/sr.po create mode 100644 web_linkedin/i18n/sr@latin.po create mode 100644 web_linkedin/i18n/sv.po create mode 100644 web_linkedin/i18n/th.po create mode 100644 web_linkedin/i18n/tr.po create mode 100644 web_linkedin/i18n/uk.po create mode 100644 web_linkedin/i18n/vi.po create mode 100644 web_linkedin/i18n/web_linkedin.pot create mode 100644 web_linkedin/i18n/zh_CN.po create mode 100644 web_linkedin/i18n/zh_TW.po create mode 100644 web_linkedin/static/src/css/linkedin.css create mode 100644 web_linkedin/static/src/img/Linkedin_blue.png create mode 100644 web_linkedin/static/src/img/ghost_profile_60x60_v1.png create mode 100644 web_linkedin/static/src/img/linkedin-profile.jpeg create mode 100644 web_linkedin/static/src/js/linkedin.js create mode 100644 web_linkedin/static/src/xml/linkedin.xml create mode 100644 web_linkedin/views/web_linkedin.xml create mode 100644 web_linkedin/web_linkedin.py create mode 100644 web_linkedin/web_linkedin_view.xml diff --git a/groupeurd_crm/__openerp__.py b/groupeurd_crm/__openerp__.py index 037af37..0149c95 100644 --- a/groupeurd_crm/__openerp__.py +++ b/groupeurd_crm/__openerp__.py @@ -6,17 +6,22 @@ Paramétrage d'Odoo pour correspondre aux besoins CRM du Groupe URD.""", 'description': """ - - Ajout de champs personnalisés + - Ajout de champs personnalisés (note 20/02/2017: fait pour les contacts, pas pour les organisations) - Personnalisation des listes d’affichage (tri, colonnes, filtres) - Personnalisation des écrans d’affichage des entités : position des champs, organisation par groupe des champs, champs obligatoires/facultatifs - Masquer le sous-menu "Ventes" jusqu'à ce que le besoin de suivi de pistes et opportunités soit remonté - Organisation - Nom, adresse (prévoir 3 lignes), Code postal, Pays, Téléphone, Télécopie, site web, budget annuel, langue principale, langue(s) secondaire(s), état d’adoption de Sigmah (Prise d’information, Souhait d’adoption, Adoption démarrée, Utilisation partielle, Utilisation complète, Non), zone de commentaires. + Contacts, ajout des champs: + Compte de réseau social (Skype, Twitter, LinkedIn, Facebook), Langue principale, langue(s) secondaires(s), Publications (liste d’URL). + + Organisation, ajout des champs: + budget annuel, langue principale, langue(s) secondaire(s), état d’adoption de Sigmah (Prise d’information, Souhait d’adoption, Adoption démarrée, Utilisation partielle, Utilisation complète, Non), zone de commentaires. + + Installation + ============ + Aller dans Configuration > Configuration > Ventes pour configuer le module 'web_linkedin' - Contacts - Civilité, Nom, prénom, fonction, type de fonction (liste de valeurs paramétrables) adresse (prévoir 3 lignes), code postal, pays, téléphone bureau, téléphone mobile, adresse mail, compte de réseau social (Skype, Twitter, LinkedIn, Facebook), Langue principale, langue(s) secondaires(s), niveau de contact (cercle de proximité de la relation), niveau universitaire, Centres d’intérêt (Liste de valeur, multisélection), compétences (Liste de valeur, multisélection) et nombre d’années dans la compétence sélectionnée, Publications (liste d’URL), zone de commentaires. """, 'author': "Groupe URD", @@ -29,7 +34,7 @@ 'version': '0.1', # any module necessary for this one to work correctly - 'depends': ['base', 'calendar_ics', 'groupeurd_newsletter'], + 'depends': ['base', 'calendar_ics', 'groupeurd_newsletter', 'crm', 'partner_social_fields', 'web_linkedin'], # always loaded 'data': [ diff --git a/groupeurd_crm/models.py b/groupeurd_crm/models.py index d1b3701..e63e0af 100644 --- a/groupeurd_crm/models.py +++ b/groupeurd_crm/models.py @@ -2,6 +2,7 @@ from openerp import models, fields, api from openerp.osv import osv +from openerp import tools from openerp.tools.translate import _ @@ -12,15 +13,15 @@ class contact(models.Model): partner_id = fields.Many2one("res.partner", string="Contact") - # A FAIRE - # A la création d'un abonné, mettre à jour partner_id si un contact existe avec cette adresse email - # A FINIR ! @api.model def create(self, vals): new_id = super(contact, self).create(vals) partner = self.env['res.partner'].search([('email','=',new_id.email)]) - if ( partner ): - self.partner_id = partner.id + if partner: + new_id.partner_id = partner[0].id # "new_id.partner_id" plutôt que "self.partner_id" car le self est vide dans un create + if new_id.list_id not in new_id.partner_id.list_ids: + new_id.partner_id.list_ids |= new_id.list_id + return new_id # Surcharge l'objet "Contact" pour ajouter les liens vers les objets "Abonné" et "Liste de diffusion" @@ -30,6 +31,50 @@ class partner(models.Model): list_ids = fields.Many2many("mail.mass_mailing.list", string="Listes de diffusion") contact_ids = fields.One2many("mail.mass_mailing.contact", "partner_id", string="Abonnements") + secondary_language1 = fields.Selection(tools.scan_languages(), string="Langue secondaire 1") + secondary_language2 = fields.Selection(tools.scan_languages(), string="Langue secondaire 2") + secondary_language3 = fields.Selection(tools.scan_languages(), string="Langue secondaire 3") + + + #Si des listes de diffusion sont ajoutées/supprimées pour le "Contact", modifier les abonnements en conséquence + @api.multi + def write(self, vals): + #Ajout de listes: pour toutes les listes en valeur, si une liste n'est pas dans les listes existantes, créer l'abonnement + subscribe_contact_vals_array = [] + if vals.get('list_ids'): + for list_id in vals['list_ids'][0][2]: + list = self.env['mail.mass_mailing.list'].browse(list_id) + for partner_element in self: + if list not in partner_element.list_ids: + subscribe_contact_vals_array.append({'email': partner_element.email, 'list_id':list_id}) + + + #Suppression de listes: pour toutes les listes existantes, si l'une d'elle n'est plus dans les listes passées en valeur, supprimer l'abonnement + unsubscribe_list_ids = [] + for partner_element in self: + for list in partner_element.list_ids: + if vals.get('list_ids'): + if list.id not in vals['list_ids'][0][2]: + unsubscribe_list_ids.append(list.id) + + #On fait l'écriture avant les ajout de listes pour éviter les boucles + res = super(partner, self).write(vals) + + #Appliquer les ajout de listes + for contact_vals in subscribe_contact_vals_array: + self.env['mail.mass_mailing.contact'].create(contact_vals) + + #Appliquer les désincriptions + for unsubscribe_list_id in unsubscribe_list_ids: + contact_array = self.env['mail.mass_mailing.contact'].search([('list_id','=',unsubscribe_list_id)]) + for contact in contact_array: + contact.opt_out = True + contact.unsubscription_date = fields.Datetime.now() + contact.unsubscribed_by_odoo_user = self.env.user + return res + + + # A FAIRE # A la création de contact, mettre à jour contact_ids et list_ids en fonction des abonnements existants pour cette adresse email diff --git a/groupeurd_crm/views.xml b/groupeurd_crm/views.xml index 1229d0e..3332634 100644 --- a/groupeurd_crm/views.xml +++ b/groupeurd_crm/views.xml @@ -15,6 +15,54 @@ base.group_erp_manager + + + + + + + + + + + + + + + + Autres + + + Relation interne principale + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -24,9 +72,66 @@ + + + + + + + + + + + + + + + + res.partner.social.inherit2 + res.partner + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web_linkedin/views/web_linkedin.xml b/web_linkedin/views/web_linkedin.xml new file mode 100644 index 0000000..1c75e5d --- /dev/null +++ b/web_linkedin/views/web_linkedin.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/web_linkedin/web_linkedin.py b/web_linkedin/web_linkedin.py new file mode 100644 index 0000000..8072d75 --- /dev/null +++ b/web_linkedin/web_linkedin.py @@ -0,0 +1,92 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import base64 +import urllib2 +from urlparse import urlparse, urlunparse + +import openerp +import openerp.addons.web +from openerp.osv import fields, osv + +class Binary(openerp.http.Controller): + @openerp.http.route('/web_linkedin/binary/url2binary', type='json', auth='user') + def url2binary(self, url): + """Used exclusively to load images from LinkedIn profiles, must not be used for anything else.""" + _scheme, _netloc, path, params, query, fragment = urlparse(url) + # media.linkedin.com is the master domain for LinkedIn media (replicated to CDNs), + # so forcing it should always work and prevents abusing this method to load arbitrary URLs + url = urlunparse(('http', 'media.licdn.com', path, params, query, fragment)) + bfile = urllib2.urlopen(url) + return base64.b64encode(bfile.read()) + +class web_linkedin_settings(osv.osv_memory): + _inherit = 'sale.config.settings' + _columns = { + 'api_key': fields.char(string="API Key", size=50), + 'server_domain': fields.char(), + } + + def get_default_linkedin(self, cr, uid, fields, context=None): + key = self.pool.get("ir.config_parameter").get_param(cr, uid, "web.linkedin.apikey") or "" + dom = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url') + return {'api_key': key, 'server_domain': dom,} + + def set_linkedin(self, cr, uid, ids, context=None): + key = self.browse(cr, uid, ids[0], context)["api_key"] or "" + self.pool.get("ir.config_parameter").set_param(cr, uid, "web.linkedin.apikey", key, groups=['base.group_user']) + +class web_linkedin_fields(osv.Model): + _inherit = 'res.partner' + + def _get_url(self, cr, uid, ids, name, arg, context=None): + res = dict((id, False) for id in ids) + for partner in self.browse(cr, uid, ids, context=context): + res[partner.id] = partner.linkedin_url + return res + + def linkedin_check_similar_partner(self, cr, uid, linkedin_datas, context=None): + res = [] + res_partner = self.pool.get('res.partner') + for linkedin_data in linkedin_datas: + partner_ids = res_partner.search(cr, uid, ["|", ("linkedin_id", "=", linkedin_data['id']), + "&", ("linkedin_id", "=", False), + "|", ("name", "ilike", linkedin_data['firstName'] + "%" + linkedin_data['lastName']), ("name", "ilike", linkedin_data['lastName'] + "%" + linkedin_data['firstName'])], context=context) + if partner_ids: + partner = res_partner.read(cr, uid, partner_ids[0], ["image", "mobile", "phone", "parent_id", "name", "email", "function", "linkedin_id"], context=context) + if partner['linkedin_id'] and partner['linkedin_id'] != linkedin_data['id']: + partner.pop('id') + if partner['parent_id']: + partner['parent_id'] = partner['parent_id'][0] + for key, val in partner.items(): + if not val: + partner.pop(key) + res.append(partner) + else: + res.append({}) + return res + + _columns = { + 'linkedin_id': fields.char(string="LinkedIn ID"), + 'linkedin_url': fields.char(string="LinkedIn url", store=True), + 'linkedin_public_url': fields.function(_get_url, type='text', string="LinkedIn url", + help="This url is set automatically when you join the partner with a LinkedIn account."), + } diff --git a/web_linkedin/web_linkedin_view.xml b/web_linkedin/web_linkedin_view.xml new file mode 100644 index 0000000..f7c5dfa --- /dev/null +++ b/web_linkedin/web_linkedin_view.xml @@ -0,0 +1,51 @@ + + + + + res.partner.linkedin.inherit + res.partner + + + + + + + + + + + + + + linkedin_settings + sale.config.settings + + +
+
+

+ To use the LinkedIn module with this database, an API Key is required. Please follow this procedure: +

+
    +
  • Go to this URL: + https://www.linkedin.com/secure/developer. +
  • +
  • Log into LinkedIn.
  • +
  • Add a new application and fill the form: +
      +
    • Website URL:
    • +
    • The programming tool is Javascript
    • +
    • Website URL inside "JavaScript API Domains" field
    • +
    +
  • +
  • Copy the API key here: + +
  • +
+
+
+
+
+ +
+