--- title: Odoo 18 sur Docker url: https://blog.guigpap.com/fr/infrastructure/odoo-18-setup/ url_md: https://blog.guigpap.com/fr/infrastructure/odoo-18-setup.md category: infrastructure date: '2026-01-19' maturite: production techno: - odoo - docker - postgresql application: - business - infrastructure --- # Odoo 18 sur Docker > Déploiement d'Odoo 18 Community avec PostgreSQL et modules custom (project_github_sync, website_experiments) ## 1. Quoi ? — Définition et contexte **Odoo** est un ERP (Enterprise Resource Planning) open-source qui centralise les données métier dans une base unique. Cette instance Odoo 18 Community tourne en Docker, sur sa propre PostgreSQL dédiée, avec trois modules custom qui adaptent l'ERP au workflow technique : sync GitHub, télémétrie Claude Code, et expériences web pour le site. > **Note - ERP** > > Un **ERP** remplace la multiplication des tableurs et des outils disparates par une base de données unifiée. Projets, contacts, factures, temps passé : tout est interconnecté et traçable depuis un seul endroit. ### Modules en place Modules **natifs** activés : | Module | Usage | |--------|-------| | **Project** | Projets et tâches | | **Timesheets** | `account.analytic.line` — alimenté par TimeTrackr | | **Website** | Page d'atterrissage et hub | | **CRM** | Leads et conversion (à étendre) | Modules **custom** maintenus dans `odoo-stack/addons/` : | Module | Version | Usage | |--------|---------|-------| | `project_github_sync` | v18.0.5.6.0 | Sync GitHub ↔ tâches + télémétrie Claude Code + double-triage AI + estimation | | `website_experiments` | v18.0.1.1.0 | Snippets interactifs (Flip Display, Glass Splat) pour le Website Builder | | `powered_by_odoo_remove` | tiers | Retire le branding "Powered by Odoo" sur les pages publiques | > **Caution - Nom de la base : `guig_db`** > > La base PostgreSQL s'appelle **`guig_db`** (et non `odoo` comme la valeur par défaut de l'image). Toutes les commandes `pg_dump`, `psql`, `--database`, `-d` doivent l'utiliser. Le filestore historique reste sous `/var/lib/odoo/filestore/odoo/` parce qu'il a été créé sous l'ancien nom de DB — ne pas le renommer sous peine de casser les références `ir_attachment`. ### Vision métier ```mermaid flowchart TD Source["CV / Carte visite · lien tracké"] Site["Website Odoo"] Form["Formulaire contact"] Cal["Prise RDV Cal.com"] Lead["CRM Odoo · Lead"] Deal["Projet + Devis · estimé via x_estimated_hours / x_complexity"] Code["Issue GitHub · sync via project_github_sync"] Claude["Claude Code · télémétrie x_claude_*"] Source --> Site Site --> Form Site --> Cal Form --> Lead Cal --> Lead Lead --> Deal Deal --> Code Code --> Claude ``` Pour comprendre pourquoi Odoo a été choisi parmi les alternatives, voir [Pourquoi Odoo](/fr/infrastructure/why-odoo/). --- ## 2. Pourquoi ? — Enjeux et motivations ### Problèmes résolus | Problème | Solution Odoo | |----------|---------------| | **Données éparpillées** | Base unifiée : projets, temps, contacts | | **Pas de traçabilité GitHub ↔ tâches** | Sync bidirectionnelle via `project_github_sync` | | **Estimation au doigt mouillé** | Champs `x_estimated_hours` / `x_complexity` + `actual_hours` réel via timesheets | | **Triage manuel des issues** | Double-triage AI (`x_ai_project_triaged_at` + `x_ai_personal_triaged_at`) | | **Effort Claude Code invisible** | Télémétrie par session (`project.task.claude.session`) avec coûts, tokens, LOC, TLDR, catégorie | | **Attribution marketing floue** | Tracking de sources via Website (CV, carte visite, LinkedIn) | ### Pourquoi Odoo 18 Community (vs Enterprise) ? 1. **Coût** — Enterprise est facturé par utilisateur/mois. Community est gratuit et auto-hébergé. 2. **Modules custom sans contrainte** — Aucune restriction sur l'installation de `project_github_sync` ou `website_experiments` côté Community. 3. **Suffisance fonctionnelle** — Les modules natifs Project, Timesheets, Website, CRM couvrent les besoins actuels. 4. **Migration possible** — Vers Enterprise plus tard si besoin de modules avancés (eCommerce, Manufacturing). > **Tip - Stratégie data-driven** > > La traçabilité de bout en bout (lead → projet → issue GitHub → temps passé Claude + humain → coût réel) permet de calibrer les futurs devis sur des données plutôt que sur des estimations à la louche. --- ## 3. Comment ? — Mise en œuvre technique ### Architecture réseau ```mermaid flowchart TD Caddy["Caddy · webproxy · odoo.guigpap.com:443"] subgraph Internal["odoo-internal"] direction LR Odoo["Odoo 18 · :8069 · Web UI · XML-RPC · /mnt/extra-addons"] PG["odoo-postgres · :5432 · DB guig_db"] Odoo <--> PG end Caddy -->|port 8069| Internal ``` ### Configuration Docker Compose (extrait) ```yaml services: odoo: image: odoo:18 volumes: - odoo-data:/var/lib/odoo - odoo-config:/etc/odoo - ./addons:/mnt/extra-addons environment: - HOST=odoo-postgres - USER=odoo - PASSWORD=${ODOO_DB_PASSWORD} healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8069/web/health"] interval: 30s timeout: 10s retries: 3 networks: [odoo-internal, webproxy] odoo-postgres: image: postgres:15 volumes: [odoo-db:/var/lib/postgresql/data] environment: - POSTGRES_USER=odoo - POSTGRES_PASSWORD=${ODOO_DB_PASSWORD} - POSTGRES_DB=guig_db networks: [odoo-internal] ``` > **Tip - Health check** > > L'endpoint `/web/health` retourne `{"status": "pass"}` quand Odoo fonctionne. C'est ce qu'utilise Docker pour la santé du container et la [Monitoring Stack](/fr/infrastructure/monitoring-stack/) pour les alertes. ### Module `project_github_sync` (v18.0.5.6.0) Le module ajoute un grand nombre de champs à `project.task` (ainsi qu'un modèle dédié à l'historique des sessions Claude Code). Les champs sont regroupés en quatre familles. #### Champs GitHub (8) | Champ | Type | Usage | |-------|------|-------| | `x_github_sync_enabled` | Boolean (sur `project.project`) | Active la sync sur le projet | | `x_github_repo` | Char | `owner/repo` source | | `x_github_issue_id` | Integer | Numéro de l'issue / PR | | `x_github_url` | Char | URL canonique vers GitHub | | `x_github_commit_ids` | One2many → `project.task.commit` | Commits liés (sha, message, author, url) | | `x_github_commit_count` | Integer (compute) | Nombre de commits liés | | `x_github_milestone_id` | Integer | Numéro du milestone GitHub | | `x_github_parent_issue_id` | Integer | Issue parente (sub-issues) | #### Champs télémétrie Claude Code (13) Alimentés par les hooks SessionEnd (voir [Claude Code Telemetry](/fr/workflows/claude-code-telemetry/)). | Champ | Type | Usage | |-------|------|-------| | `x_claude_time_total` | Float (h) | Temps Claude Code agrégé | | `x_claude_cost_total` | Float ($) | Coût agrégé | | `x_claude_token_total` | Integer | Total tokens (in + out + cache) | | `x_claude_tokens_input` / `_output` / `_cache` | Integer × 3 | Détail par catégorie | | `x_claude_sessions` | Integer | Nombre de sessions | | `x_claude_lines_added` / `_removed` | Integer × 2 | LOC agrégées | | `x_claude_last_session` | Datetime | Dernière session enregistrée | | `x_claude_cost_by_model` | Text (JSON) | Répartition par modèle (Sonnet/Opus/Haiku) | | `x_claude_session_ids` | One2many → `project.task.claude.session` | Historique session par session | | `x_claude_category` | Selection | Catégorie auto pour les tâches génériques (sans issue) | Chaque ligne `project.task.claude.session` capture le détail d'une session : ID, timestamps, tokens, coût, LOC, **TLDR** (résumé court généré par le hook) et catégorie. Cela permet de remonter de l'agrégat à la session source. #### Champs estimation effort (#188) | Champ | Type | Usage | |-------|------|-------| | `x_estimated_hours` | Float | Estimation a priori | | `x_complexity` | Selection | Complexité (low / medium / high) | La **vérité terrain** vient des timesheets : `actual_hours = SUM(account.analytic.line.unit_amount) WHERE task_id = X`. L'attribut `x_claude_time_total` n'est **pas** substituable — il mesure le temps machine Claude, pas l'effort humain. Voir le workflow `Effort Estimator V1` pour la logique d'estimation cohort-based. ```sql -- Échantillons appariés (estimé + réel) pour ré-calibrer SELECT t.id, t.name, t.x_estimated_hours, COALESCE(SUM(a.unit_amount), 0) AS actual_hours, t.x_claude_time_total, t.x_claude_sessions FROM project_task t LEFT JOIN account_analytic_line a ON a.task_id = t.id WHERE t.x_estimated_hours IS NOT NULL GROUP BY t.id, t.name, t.x_estimated_hours, t.x_claude_time_total, t.x_claude_sessions HAVING COALESCE(SUM(a.unit_amount), 0) > 0; ``` #### Champs double-triage AI (#296) | Champ | Type | Usage | |-------|------|-------| | `x_ai_project_triaged_at` | Datetime | Cache du dernier triage côté projet (stage) | | `x_ai_personal_triaged_at` | Datetime | Cache du dernier triage côté horizon perso (Aujourd'hui / Cette semaine / …) | Le workflow N8N `Daily GitHub Issue Triage` propose des affectations stage + horizon via `codex-yolo`, puis stocke le timestamp pour ne pas re-triager pendant **7 jours** (TTL configurable). L'utilisateur valide via Telegram (`/triage`), ce qui POST vers une API interne qui met à jour Odoo. ```mermaid flowchart TD Cron["Daily Trigger / SessionStart hook"] Fetch["Fetch issues without triage cache or expiré"] AI["AI Issue Triage SW · codex-yolo"] Cache["Update x_ai_*_triaged_at"] Pending["triage-pending.json"] TG["Interactive triage via /triage"] API["triage-interactive-api · POST update"] Odoo["Odoo · stage_id + x_personal_stage_id"] Cron --> Fetch --> AI --> Cache AI --> Pending --> TG --> API --> Odoo ``` ### Module `website_experiments` Ajoute trois snippets drag-and-drop au Website Builder : - **Flip Display** : afficheur split-flap animé (style aéroport). - **Glass Splat Desktop** : 3 panneaux side-by-side avec rendu 3D verre fragmenté. - **Glass Splat Mobile** : carrousel mobile équivalent. Côté assets : CSS + IIFE JavaScript chargés via `web.assets_frontend`. ### Filestore et `ir_attachment` Le filestore est dans le volume `odoo-data`, sous `/var/lib/odoo/filestore/odoo/` (sous-dossier nommé `odoo` historiquement, même si la DB s'appelle maintenant `guig_db`). Il stocke tous les binaires référencés par `ir_attachment.store_fname`. > **Caution - Ne pas nettoyer le filestore à l'aveugle** > > Toute purge non vérifiée casse le rendu du website. Quatre catégories sont critiques : > - `/_custom/` URLs → assets SCSS du thème (pages cassées si supprimées). > - `/web/content/` → documents embarqués dans les pages. > - `res_model = 'ir.ui.view'` → blocs de construction du website. > - `res_model = 'ir.attachment'` + `type = 'binary'` → fichiers téléchargeables. ### Sauvegarde Les backups sont gérés par `scripts/backup-databases.sh` (cron quotidien 03:00) qui produit : - `pg_dump -U odoo guig_db` → archive SQL - `tar czf - -C /var/lib/odoo filestore` → archive du filestore - Rotation locale (7 j) + push GDrive (30 j) via rclone. ```bash # Backup manuel docker exec odoo-postgres pg_dump -U odoo guig_db > odoo_backup.sql # Restauration docker exec -i odoo-postgres psql -U odoo guig_db < odoo_backup.sql # Backup filestore manuel docker exec odoo tar czf - -C /var/lib/odoo filestore > odoo-filestore.tar.gz ``` ### Intégration N8N via XML-RPC ```javascript // Création d'une tâche depuis github-project-sync { "url": "http://odoo:8069/xmlrpc/2/object", "method": "POST", "body": { "service": "object", "method": "execute_kw", "args": [ "guig_db", // database (PAS "odoo") 2, // uid après authentification "{{ $env.ODOO_PASSWORD }}", "project.task", "create", [{ "name": "Issue #123 — Fix bug", "project_id": 1, "x_github_issue_id": 123, "x_github_url": "https://github.com/owner/repo/issues/123" }] ] } } ``` > **Note - XML-RPC** > > **XML-RPC** est le protocole natif d'Odoo pour l'accès programmatique. Plus ancien que REST mais très bien supporté côté N8N (un node HTTP Request suffit). Préférer aux endpoints `/jsonrpc` pour la cohérence avec les workflows existants. ### Gestion des modules ```bash # Installer un nouveau module cp -r mon_module odoo-stack/addons/ docker restart odoo # UI : Apps → Update Apps List → Installer # Mettre à jour un module existant en place docker exec odoo odoo -u project_github_sync -d guig_db --stop-after-init ``` ### Commandes de dépannage ```bash # Health check curl http://localhost:8069/web/health # Logs Odoo docker logs odoo --tail 100 # Connexion PostgreSQL docker exec odoo-postgres pg_isready -U odoo docker exec odoo-postgres psql -U odoo -d guig_db -c "SELECT count(*) FROM project_task;" # Shell Odoo pour debug docker exec -it odoo odoo shell -d guig_db # Regarder les sessions Claude récentes docker exec odoo-postgres psql -U odoo -d guig_db \ -c "SELECT id, name, tldr, category FROM project_task_claude_session ORDER BY id DESC LIMIT 10;" ``` --- ## 4. Et si ? — Perspectives et limites ### Limites actuelles | Limite | Impact | Mitigation | |--------|--------|------------| | **Community sans support éditeur** | Pas de hotline Odoo | Documentation + communauté | | **PostgreSQL single-instance** | Pas de HA native | Backups quotidiens + monitoring | | **Modules custom à maintenir** | Compat future Odoo 19+ | Migrations versionnées dans `migrations/` | | **Single-user effectif** | Toute la télémétrie est attribuée à un seul employé | Mapping multi-user prévu côté `timetrackr_user_mapping` si besoin | ### Scénarios d'évolution **Si le pipeline d'estimation devient critique** : - Calibrer le modèle V1 → V2 sur les paired samples (`x_estimated_hours` vs `actual_hours`). - Étendre la télémétrie aux tâches sans issue (déjà adressé via `x_claude_category`). **Si le triage AI devient bruyant** : - Réduire la TTL du cache (actuellement 7 j) pour re-triager plus souvent. - Filtrer côté workflow par projet ou label. **Si besoin de facturation** : - Activer le module Invoicing. - Connecter les `account.analytic.line` aux factures via tâches/projets. **Si migration vers Enterprise** : - Audit des champs custom et des migrations existantes. - Vérifier la compatibilité de `project_github_sync` (LGPL-3, devrait passer). - Budget : tarification par utilisateur/mois. --- ## Pages liées ### Infrastructure - [Architecture VPS](/fr/infrastructure/architecture-vps/) — Vue d'ensemble - [Pourquoi Odoo](/fr/infrastructure/why-odoo/) — Comparaison ERP et choix - [TimeTrackr Stack](/fr/infrastructure/timetrackr-stack/) — Source des `account.analytic.line` ### Workflows - [GitHub-Odoo Sync](/fr/workflows/github-odoo-sync/) — Synchronisation issues / PRs / commits - [Claude Code Telemetry](/fr/workflows/claude-code-telemetry/) — Hooks SessionStart / SessionEnd - [Cal.com → Odoo RDV](/fr/workflows/cal-rdv-odoo/) — Intégration calendrier ### Référence - [Glossaire](/fr/reference/glossary/) — ERP, XML-RPC, ir_attachment ## 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