--- title: Cal.com → Odoo CRM url: https://blog.guigpap.com/fr/workflows/cal-rdv-odoo/ url_md: https://blog.guigpap.com/fr/workflows/cal-rdv-odoo.md category: automation date: '2026-01-31' maturite: production techno: - n8n - odoo - telegram application: - automation - business --- # Cal.com → Odoo CRM > Synchronisation complète des rendez-vous Cal.com avec le CRM Odoo et notifications Telegram ## 1. Quoi ? — Définition et contexte Le workflow **Cal.com → Odoo CRM** synchronise automatiquement les événements Cal.com avec Odoo CRM. Il gère le cycle de vie complet des rendez-vous : création, annulation, reprogrammation et fin de réunion. > **Note - Cal.com** > > **Cal.com** est une alternative open-source à Calendly pour la prise de rendez-vous. Ce workflow connecte chaque rendez-vous à une opportunité CRM dans Odoo, permettant de suivre les prospects depuis la prise de contact jusqu'à la conversion. ### Événements gérés | Événement Cal.com | Action Odoo | Notification | |-------------------|-------------|--------------| | `BOOKING_CREATED` | Créer opportunité | Message + fichier iCal | | `BOOKING_CANCELLED` | Marquer comme perdue | Message d'annulation | | `BOOKING_RESCHEDULED` | Mettre à jour dates | Message + nouveau iCal | | `MEETING_ENDED` | Augmenter probabilité | Message fin de réunion | --- ## 2. Pourquoi ? — Enjeux et motivations ### Problèmes résolus | Problème | Sans sync | Avec sync | |----------|-----------|-----------| | **Double saisie** | Créer manuellement l'opportunité après RDV | Automatique | | **Oubli de suivi** | Pas de rappel après la réunion | Stage CRM mis à jour | | **Calendrier dispersé** | RDV Cal.com ≠ calendrier pro | Fichier iCal envoyé | | **Annulations non tracées** | Perdre l'info de l'annulation | Lead marqué comme perdu | ### Cycle de vie du lead ```mermaid flowchart TD Created["BOOKING_CREATED · probability 10%"] Ended["MEETING_ENDED · probability 30%"] Qual["Qualification manuelle · probability 50%+"] Won["Won"] Lost["Lost"] Created --> Ended --> Qual Qual --> Won Qual --> Lost ``` > **Tip - Fichier iCal** > > Chaque notification inclut un fichier `.ics` que les admins peuvent ajouter directement à leur calendrier (Google Calendar, Outlook, etc.). --- ## 3. Comment ? — Mise en œuvre technique ### Architecture ```mermaid flowchart TD Cal["Cal.com webhook"] Switch["Switch · triggerEvent"] Create["BOOKING_CREATED → Create opportunity"] Cancel["BOOKING_CANCELLED → Mark Lost"] Reschedule["BOOKING_RESCHEDULED → Update dates"] Ended["MEETING_ENDED → Update stage"] NotifyICal["Notify + iCal"] NotifyOnly["Notify"] Cal --> Switch Switch --> Create --> NotifyICal Switch --> Cancel --> NotifyOnly Switch --> Reschedule --> NotifyICal Switch --> Ended --> NotifyOnly ``` ### Flow : Création de rendez-vous ```mermaid flowchart TD CB["BOOKING_CREATED"] CO["Create Opportunity Odoo · probability 10"] Admins["Load All Admins · Data Table"] Format["Format Booking Notification"] Loop["Loop · pour chaque admin"] Gen["Generate iCal File"] Send["Send Calendar File · Telegram"] CB --> CO --> Admins --> Format --> Loop --> Gen --> Send ``` ### Exemple de notification ``` 📅 Nouveau RDV Cal.com 👤 Client : Jean Dupont 📧 Email : jean.dupont@example.com 🗓️ Date : jeudi 20 janvier 2026 ⏰ Horaire : 14:00 - 15:00 📋 Type : Appel découverte 🔗 Opportunité : https://odoo.guigpap.com/odoo/crm/123 ``` ### Configuration N8N **Cal.com Trigger :** ```yaml Events: - BOOKING_CREATED - BOOKING_CANCELLED - BOOKING_RESCHEDULED - MEETING_ENDED ``` **Switch Node (Route by Event) :** ```yaml Type: Switch Mode: Rules Value: {{ $json.triggerEvent }} Rules: - Output 0: BOOKING_CREATED - Output 1: BOOKING_CANCELLED - Output 2: BOOKING_RESCHEDULED - Output 3: MEETING_ENDED ``` **Find Opportunity (Odoo) :** ```yaml Resource: Custom Custom Resource: crm.lead Operation: Get Many Limit: 1 Filter: - Field: email_from Operator: = Value: {{ $json.attendees[0].email }} ``` ### Génération iCal ```javascript const booking = $json; const attendee = booking.attendees[0]; const formatICalDate = (date) => { return new Date(date).toISOString() .replace(/[-:]/g, '').split('.')[0] + 'Z'; }; const icsContent = `BEGIN:VCALENDAR VERSION:2.0 PRODID:-//N8N//Booking//FR BEGIN:VEVENT UID:${booking.uid}@cal.com DTSTART:${formatICalDate(booking.startTime)} DTEND:${formatICalDate(booking.endTime)} SUMMARY:RDV Cal.com - ${attendee.name} DESCRIPTION:Rendez-vous avec ${attendee.name} LOCATION:${booking.location || 'À définir'} STATUS:CONFIRMED END:VEVENT END:VCALENDAR`; return [{ json: { fileName: `rdv-${attendee.name}.ics` }, binary: { calendar: { data: Buffer.from(icsContent).toString('base64'), mimeType: 'text/calendar' } } }]; ``` ### Note sur les types Odoo > **Caution - Types booléens Odoo** > > Les champs `active` et `probability` doivent être en **mode expression** (icône fx) dans N8N pour envoyer les bons types à Odoo. Sans cela, N8N envoie des strings et Odoo retourne l'erreur : `'>=' not supported between instances of 'str' and 'int'`. ```yaml # Configuration correcte Fields: active: {{ false }} # Mode expression ! probability: {{ 0 }} # Mode expression ! ``` --- ## 4. Et si ? — Perspectives et limites ### Limites actuelles | Limite | Impact | Mitigation | |--------|--------|------------| | **Matching par email** | Doublons si emails différents | Recherche élargie future | | **Pas de sync inverse** | Odoo → Cal.com non implémenté | Actions Telegram | | **Un seul attendee** | Multi-participants non gérés | Prévu si besoin | ### Scénarios d'évolution **Si besoin de sync inverse** : - Webhook Odoo → N8N quand opportunité modifiée - Mettre à jour le booking Cal.com - Attention aux boucles infinies **Si multi-types de RDV** : - Créer différents types d'opportunité selon le type Cal.com - Assigner automatiquement selon le type - Probabilité initiale différenciée **Si volume de RDV augmente** : - Digest quotidien au lieu de notif individuelle - Filtrage par type de RDV - Assignation round-robin ### Troubleshooting | Problème | Vérification | |----------|--------------| | Pas de déclenchement | Cal.com: webhook configuré ? N8N: workflow actif ? | | Opportunité non trouvée | Email attendee = email_from dans Odoo ? | | Update échoue | Vérifier permissions credentials Odoo | | iCal non généré | Node Generate iCal connecté ? Binary data présent ? | --- ## Pages liées ### Workflows - [Notification Hub](/fr/workflows/notification-hub/) — Routage centralisé - [Website Lead Notification](/fr/workflows/website-lead-notification/) — Leads formulaire web ### Infrastructure - [Odoo 18 sur Docker](/fr/infrastructure/odoo-18-setup/) — ERP et CRM ## Metadonnees agent - Cet article est issu du blog GuiGPaP Lab. - Contexte global du blog: https://blog.guigpap.com/llms.txt - Contact auteur: https://odoo.guigpap.com/mon-cv - Licence: CC-BY-SA 4.0