Recherche du contact Odoo à partir du numéro de téléphone (format E.164), 

voici des helpers prêts à l’emploi pour retrouver un contact Odoo (res.partner) à partir d’un numéro E.164.

Je vous donne les deux variantes : Python (XML-RPC) et Node.js (JSON-RPC), avec une logique de fallback (ex. numéros stockés au format local).

Option A — Python (XML-RPC)

# find_partner_by_phone.py import re import xmlrpc.client from typing import Optional, Tuple ODOO_URL = "https://votre-odoo.fr" ODOO_DB = "votre_db" ODOO_USER = "vous@exemple.fr" ODOO_API_KEY = "XXXXXXXX" common = xmlrpc.client.ServerProxy(f"{ODOO_URL}/xmlrpc/2/common") uid = common.authenticate(ODOO_DB, ODOO_USER, ODOO_API_KEY, {}) models = xmlrpc.client.ServerProxy(f"{ODOO_URL}/xmlrpc/2/object") def sanitize(num: str) -> str: """Garde + et chiffres uniquement (ex.: +33123456789).""" num = num.strip() return re.sub(r"[^\d+]", "", num) def build_domains(e164: str) -> list: """ Domaines de recherche hiérarchisés : 1) phone == e164 OR mobile == e164 2) phone ilike suffixe 7–10 derniers chiffres OR mobile idem (fallback si Odoo stocke '01 23 45 67 89') """ suffix = re.sub(r"^\+\d{1,3}", "", e164) # retire l'indicatif pour suffix-match suffix = suffix[-10:] if len(suffix) > 10 else suffix # limite la longueur return [ # 1) match exact E.164 ['|', ('phone', '=', e164), ('mobile', '=', e164)], # 2) fallback "ilike" sur suffixe ['|', ('phone', 'ilike', suffix), ('mobile', 'ilike', suffix)], ] def find_partner_by_phone(e164: str) -> Optional[Tuple[int, dict]]: e164 = sanitize(e164) for domain in build_domains(e164): recs = models.execute_kw( ODOO_DB, uid, ODOO_API_KEY, 'res.partner', 'search_read', [domain], {'fields': ['id', 'name', 'phone', 'mobile', 'email', 'company_type'], 'limit': 1} ) if recs: r = recs[0] return r['id'], r return None # Exemple d’usage : if __name__ == "__main__": result = find_partner_by_phone("+33611223344") if result: pid, data = result print("Trouvé:", pid, data) else: print("Aucun contact trouvé")

Intégration webhook Twilio (extrait)

partner = find_partner_by_phone(req.form.get('To') or req.form.get('From')) if partner: partner_id, _ = partner # … créer/mettre à jour votre activité d’appel

Option B — Node.js (JSON-RPC)

// findPartnerByPhone.js import fetch from "node-fetch"; const ODOO_URL = "https://votre-odoo.fr"; const ODOO_DB = "votre_db"; const ODOO_LOGIN = "vous@exemple.fr"; const ODOO_API_KEY = "XXXXXXXX"; function sanitize(num) { return (num || "").trim().replace(/[^\d+]/g, ""); } async function rpc(path, params) { const res = await fetch(`${ODOO_URL}${path}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ jsonrpc: "2.0", id: Date.now(), method: "call", params }) }); const data = await res.json(); if (data.error) throw new Error(JSON.stringify(data.error)); return data.result; } async function auth() { const r = await rpc("/web/session/authenticate", { db: ODOO_DB, login: ODOO_LOGIN, password: ODOO_API_KEY }); return r.uid; } async function callKW(model, method, args = [], kwargs = {}) { return rpc("/web/dataset/call_kw", { model, method, args, kwargs }); } function buildDomains(e164) { const suffix = e164.replace(/^\+\d{1,3}/, "").slice(-10); // supprime indicatif, garde 10 derniers return [ ["|", ["phone", "=", e164], ["mobile", "=", e164]], ["|", ["phone", "ilike", suffix], ["mobile", "ilike", suffix]], ]; } /** * Retourne { id, name, phone, mobile, email } ou null. */ export async function findPartnerByPhone(e164Input) { await auth(); const e164 = sanitize(e164Input); for (const domain of buildDomains(e164)) { const recs = await callKW("res.partner", "search_read", [domain], { fields: ["id", "name", "phone", "mobile", "email", "company_type"], limit: 1 }); if (recs && recs.length) return recs[0]; } return null; } // Exemple d’utilisation (dans votre webhook Twilio): // const partner = await findPartnerByPhone(req.body.To || req.body.From);

Conseils pratiques

  • Stockage normalisé : si possible, écrivez toujours phone/mobile en E.164 côté Odoo (ex. via un onchange ou un @api.constrains) pour fiabiliser les matches.
  • Performance : créez un index B-Tree sur phone et mobile si vous avez beaucoup de contacts.
  • Multi-pays : si, dans certains cas, vous recevez des numéros non-E.164, normalisez côté backend (librairies : phonenumbers en Python, libphonenumber-js en Node) avant d’appeler Odoo.
  • Conflits : s’il existe plusieurs contacts pour un même numéro, retournez le plus récent ou prévoyez un écran de désambigüisation côté agent.

> post automatique dans le chatter

post automatique dans le chatter à la fin de l’appel

ajout du post automatique dans le chatter à la fin de l’appel (statut Twilio = completed). Je vous donne les deux variantes (Python XML-RPC et Node.js JSON-RPC), avec : durée, statut, lien d’enregistrement (si disponible), et mapping contact/opportunité.


Découvrir plus