Se rendre au contenu

Développer des modules Odoo 19 avec Claude

Développer un module Odoo custom avec une IA ne s'improvise pas : sans méthode, on accumule les erreurs silencieuses, les caches non purgés et les mises à jour qui échouent sans un mot dans les logs. Cette page présente le digest d'une méthode de développement de modules Odoo 19 Community Edition sur VPS Ubuntu, pilotée par Claude (Cowork et Claude Code) : environnement type, modes de travail, protocole d'échange, commandes clés et table des pièges connus — le tout validé sur le terrain, module après module.

Environnement serveur type

La méthode s'appuie sur une installation standardisée : mêmes chemins, mêmes services, mêmes conventions sur chaque VPS. Seuls les noms de bases et de domaines changent d'un projet à l'autre.

ÉlémentValeur typeÀ personnaliser
OSUbuntu 24.04 LTSNon
Odoo19 Community EditionNon
Utilisateur OdooodooNon
Config/etc/odoo/odoo.confNon
Modules custom/opt/odoo/custom_addons/Non
Modules natifs/usr/lib/python3/dist-packages/odoo/addons/Non
Log/var/log/odoo/odoo-server.logNon
Serviceodoo (systemctl)Non
Log Nginx accès/var/log/nginx/access.logNon
Base dev[NOM_PROJET]_devÀ définir
Base prod[NOM_PROJET]_prodÀ définir
Domaine devdev.[DOMAINE]À définir
Domaine prod[DOMAINE]À définir

Structure de dossiers du projet de développement

Côté poste de travail, chaque projet suit la même arborescence : fichiers de pilotage (roadmap, suivi, bugs), boîte aux lettres pour les échanges entre agents, et un dossier dev/odoo/ qui sépare les modules en cours des modules validés.

[NOM_PROJET]/
├── CLAUDE.md
├── NOTES_CLAUDE.md
├── ROADMAP.md
├── DAR.md
├── Documentation.md
├── BUGS.md
├── CONTRATS.md
├── SUIVI.md
├── boite_aux_lettres/
├── DOCS/
│   ├── specs/
│   ├── sources/
│   └── captures/
├── dev/
│   └── odoo/
│       ├── modules/        ← modules custom en développement
│       └── _staging/       ← modules validés, prêts pour la prod
└── index.db

Règle _staging/ : quand un module est validé sur dev (tests OK, logs propres), il est copié dans _staging/ — le signal qu'il est prêt pour le déploiement en production.

Trois modes de travail : A, B, C

Selon les outils activés sur le poste de l'utilisateur, trois modes de collaboration peuvent coexister. Ils se combinent : du dev simple en mode A seul jusqu'au cycle complet dev → déploiement → test en A + B + C.

Mode A — Cowork seul (Claude Cowork + VS Code SSH)

Le mode de base, toujours disponible. L'humain reste la courroie de transmission :

  1. Cowork prépare les modifications (contenu des fichiers)
  2. L'utilisateur colle dans VS Code ouvert en SSH sur le serveur dev
  3. L'utilisateur lance la commande de mise à jour du module
  4. L'utilisateur fournit les logs en cas d'erreur
  5. L'utilisateur confirme → mise à jour des fichiers de suivi

Mode B — Cowork + Code (collaboration via boîte aux lettres)

Recommandé pour les travaux lourds. Claude Code tourne sur le PC de l'utilisateur et pilote le VPS via ssh/scp — il n'est jamais installé sur le serveur. Cowork et Code ne se parlent pas directement — ils échangent via des fichiers déposés dans boite_aux_lettres/ (protocole REQ/RES, détaillé plus bas).

[Claude Cowork]          [boite_aux_lettres/]          [Claude Code — PC]
      │  écrit REQ_*.md          │                              │
      │─────────────────────────>│   lit REQ_*.md               │
      │                          │<─────────────────────────────│
      │                          │   exécute via SSH → VPS            │
      │  lit RES_*.md            │   écrit RES_*.md             │
      │<─────────────────────────│─────────────────────────────>│
      │  archive → DOCS/         │                              │

Mode C — Claude in Chrome (test navigateur direct)

Activé si l'extension Claude in Chrome est installée et connectée. Elle permet à Claude de naviguer sur dev.[DOMAINE] et lire les pages rendues, lire les erreurs de la console JS, inspecter les requêtes réseau (webhooks, routes), tester le responsive et interagir avec plusieurs sessions simultanées (ex. propriétaire + enchérisseur).

Configuration type pour les tests multi-utilisateurs :

SessionRôle
Firefox normalAdmin back-office
Firefox privéPropriétaire (user1)
Chromium normalIntervenant (user2)
Chromium privé*2e intervenant ou 2e validation

*Autoriser l'extension en mode privé : chrome://extensions → Claude in Chrome → « Autoriser en mode navigation privée ». Limite : les pages authentifiées nécessitent que l'utilisateur soit déjà connecté — Claude ne gère pas les mots de passe.

Combinaisons recommandées

ContexteModes actifs
Dev simple (modifs mineures)A seul
Dev avec déploiement automatiséA + B
Debug visuel navigateurA + C
Cycle complet (dev → déploiement → test)A + B + C

Le protocole REQ_ / RES_ : deux IA qui s'écrivent

Cœur du mode B : un protocole d'échange par fichiers, traçable et archivable. Cowork écrit une requête (REQ_), Claude Code l'exécute sur le VPS et répond par un compte-rendu (RES_). Rien ne se perd, tout s'archive.

Convention de nommage

PréfixeSignificationAuteur
REQ_Requête en attente de traitementCowork
RES_Réponse à une REQCode
U_Document urgent à traiter
NP_Document non prioritaire
D_Document à différer

Structure d'une REQ_

---
type: REQ
de: Cowork (NOM_PROJET)
pour: Code (NOM_PROJET)
date: YYYY-MM-DD
statut: en attente
objet: [Description courte]
---

# REQ — [Titre de la demande]

## Contexte
[Fichiers modifiés + fonctionnalité concernée]

## Étapes
[Commandes exactes / code Python prêt à exécuter]

## Données source
[JSON, CSV ou références aux fichiers DOCS/sources/]

## À livrer dans RES_
[Compte-rendu attendu : logs + résultat des tests]

Cycle de vie d'un échange

  1. Cowork écrit REQ_*.md → dépose dans boite_aux_lettres/
  2. Code lit, exécute sur le VPS, écrit RES_*.md
  3. Cowork lit le RES_, résout les blocages si besoin
  4. Les deux fichiers sont archivés dans DOCS/archives/echanges/
  5. La boîte aux lettres doit être vide à la fin de chaque session

Règles d'usage

  • Cowork ne suppose jamais que Code a exécuté — il attend le RES_ avant de valider.
  • Code n'exécute jamais sans REQ_ préalable (sauf commande directe de l'utilisateur).
  • Les commandes destructives (DROP, DELETE, opérations en production) exigent une confirmation humaine explicite dans la REQ.
  • La boîte aux lettres n'est pas un journal permanent — on archive après traitement.

Automatisme : une REQ après chaque modification de code

Dès que Cowork modifie ou crée un fichier de code (.py, .xml, .csv, .js), il dépose automatiquement une REQ_ listant les fichiers modifiés, la commande de mise à jour adaptée et les points à tester. La commande dépend du type de modification :

Type de modificationAction requise
.py controller uniquementsudo systemctl restart odoo
.py model / .xml / .csv-u [module] + restart
Nouveau fichier dans data/-u [module]
__manifest__.py-u [module]

REQ de type SCAFFOLD : un module complet en une passe

Le scaffold est une REQ spéciale : Cowork écrit le code complet de tous les fichiers d'un nouveau module, et Code se contente de les créer, d'installer le module et de lancer les tests. À ne pas confondre avec le odoo-bin scaffold officiel, qui ne génère qu'un squelette vide — ici, le module est entier et prêt à l'installation.

Quand l'utiliser : création d'un nouveau module (aucun fichier existant à écraser), cahier des charges complet, modèles bien définis, pièges connus intégrés au code fourni dès la rédaction.

---
type: REQ
objet: Scaffold complet — module [NOM]
---

## PDCA à appliquer
[Tableau des pièges à respecter]

## Étapes
1. Créer les N fichiers dans dev/modules/[NOM]/
2. py_compile sur tous les .py
3. systemctl stop odoo
4. -i [NOM] -d [DB] --test-enable --stop-after-init
5. systemctl start odoo
6. Rédiger le RES

## Fichiers à générer
### [NOM]/__init__.py
[code complet]
### [NOM]/__manifest__.py
[code complet]
...

Le RES attendu détaille : nombre de tests détectés/exécutés/échoués, extrait des logs d'installation, nouveaux pièges détectés, état global du module.

Bilan terrain (mesuré) : sur 4 scaffolds consécutifs, de 0 à 6 corrections minimes par module grâce à l'intégration des pièges connus en amont — le dernier : 0 correction, réussite à la première tentative.

Commandes Odoo de référence

Les commandes essentielles du cycle de développement, telles qu'exécutées sur le VPS.

Mise à jour / installation de module

# Un module
sudo -u odoo /usr/bin/odoo -c /etc/odoo/odoo.conf -u [MODULE] -d [DB_DEV] --stop-after-init
sudo systemctl restart odoo

# Plusieurs modules
sudo -u odoo /usr/bin/odoo -c /etc/odoo/odoo.conf -u [MOD1],[MOD2] -d [DB_DEV] --stop-after-init

# Installation d'un nouveau module
sudo -u odoo /usr/bin/odoo -c /etc/odoo/odoo.conf -i [MODULE] -d [DB_DEV] --stop-after-init
sudo systemctl restart odoo

Logs

sudo tail -f /var/log/odoo/odoo-server.log                      # temps réel
sudo tail -50 /var/log/odoo/odoo-server.log                     # 50 dernières lignes
sudo tail -100 /var/log/odoo/odoo-server.log | grep -E "ERROR|WARNING"
sudo journalctl -u odoo -n 100 --no-pager                       # logs système (crash)

Service

sudo systemctl restart odoo     # après modif Python/controllers
sudo systemctl stop odoo        # avant un -u propre
sudo systemctl start odoo       # après le -u
sudo systemctl status odoo      # vérifier l'état

Cache bytecode (si un .py modifié n'est pas pris en compte)

find /opt/odoo/custom_addons/ -name "*.pyc" -delete
sudo systemctl restart odoo

PostgreSQL

sudo -u odoo psql -d [DB_DEV]
# État des modules
sudo -u odoo psql -d [DB_DEV] -c "SELECT name, state FROM ir_module_module WHERE name LIKE '[PREFIX]%' ORDER BY name;"

Mode debug interface

https://dev.[DOMAINE]/web?debug=1 — vérifier les menus, inspecter les vues, consulter ir.config_parameter.

Structure d'un module custom

Chaque module suit la structure canonique Odoo : modèles, vues, sécurité, données initiales, controllers pour les routes website.

nom_module/
├── __init__.py
├── __manifest__.py          ← version, dépendances, données
├── models/
│   ├── __init__.py
│   └── nom_model.py
├── views/
│   └── nom_model_views.xml
├── security/
│   ├── ir.model.access.csv
│   └── security.xml         (si groupes custom)
├── data/
│   └── data.xml             (données initiales optionnelles)
├── controllers/
│   ├── __init__.py
│   └── main.py              (si routes website)
└── static/
    └── src/
        └── xml/             (templates OWL si nécessaire)

Le manifeste type

{
    'name': 'Nom du module',
    'version': '19.0.1.0.0',
    'category': 'Custom',
    'depends': ['base', 'web'],   # ajouter les dépendances réelles
    'data': [
        'security/ir.model.access.csv',
        'views/nom_model_views.xml',
    ],
    'installable': True,
    'auto_install': False,
}

Valider et promouvoir : TEST et STAGING

Deux commandes de projet encadrent la validation : TEST vérifie la syntaxe et met à jour le module sur dev en lisant les logs ; STAGING promeut le module validé vers le dossier de pré-production.

Protocole TEST

# 1. Syntaxe Python
python3 -m py_compile /opt/odoo/custom_addons/[MODULE]/models/[FICHIER].py
# 2. Mise à jour sur dev
sudo -u odoo /usr/bin/odoo -c /etc/odoo/odoo.conf -u [MODULE] -d [DB_DEV] --stop-after-init
sudo systemctl restart odoo
# 3. Logs
sudo tail -50 /var/log/odoo/odoo-server.log | grep -E "ERROR|WARNING|[MODULE]"

Protocole STAGING

  1. Tests OK sur dev (logs propres, fonctionnalité validée)
  2. Copier le module dans dev/odoo/_staging/[MODULE]/
  3. ROADMAP : tâche marquée validée, prête pour le staging
  4. Mettre à jour les notes de travail

Convention de versioning

Le format Odoo standard est conservé : 19.0.[MAJEUR].[MINEUR].[PATCH]. Pour les projets custom, la convention A.B.C.D encode aussi le rythme des sessions de travail :

LettreSignification
AVersion majeure du projet
BVersion mineure (fonctionnalité majeure)
CNuméro de session (incrémenté à chaque session de travail)
DNombre de modifications cumulées dans la session

Exemple : v1.2.5.12 = majeure 1, mineure 2, 5e session, 12 modifications. La version est tracée dans le fichier de suivi à chaque mise à jour.

Pièges connus — table de triage rapide

La vraie valeur d'une méthode se mesure à sa mémoire des erreurs. Cette table de triage, alimentée en continu par le terrain, permet de diagnostiquer en quelques secondes les pannes les plus fréquentes du développement Odoo 19.

ProblèmeCauseSolution
Mise à jour silencieuse (aucun log)Erreur XML/Python bloquante silencieuseLire le log après -u
Logs vides dans le terminallogfile configuré dans odoo.confLire /var/log/odoo/odoo-server.log
.py modifié non pris en compteCache bytecode .pycfind ... -name "*.pyc" -delete + restart
Menu invisible après -uErreur XML silencieuse / action manquanteVérifier logs + mode debug
attrs deprecatedOdoo 17+ supprime attrsinvisible="condition" directe
t-set en back-officeDirective OWL interdite en vue formulaireTemplates QWeb website uniquement
res.partner.mobile → AttributeErrorChamp absent en Odoo 19 CEUtiliser phone et email
502 Bad GatewayOdoo planté au démarragesudo tail -50 /var/log/odoo/odoo-server.log
CSRF manquant dans un form POSTToken requis dans tout
Champ inconnu dans un domain filterModule dépendant non déclaréAjouter la dépendance dans __manifest__.py
/website/info expose les modulesRoute publique listant les modules — risque sécuritéSite Web → Pages système → désactiver /website/info
_sql_constraints → upgrade silencieuxOdoo 19 n'utilise plus _sql_constraintsMigrer vers models.Constraint en attribut direct
models.Constraint dans une liste → AttributeErrorSyntaxe liste interditename_unique = models.Constraint('UNIQUE(name)', 'Message')
groups_id dans un domain ORM → erreurNon searchable en Odoo 19.filtered(lambda u: u.has_group('base.group_system'))
StripeObject.get() → AttributeErrorSDK Stripe récent : .get() non supportépi[key] dans un helper _pi_field(pi, key, default)
'cancelled' dans une Selection → rollback silencieuxValeur inexistante → rollback PostgreSQL sans logUtiliser une valeur déclarée (ex. 'rejected')
numbercall dans ir.cron → erreur installChamp supprimé en Odoo 19Ne pas l'utiliser
active="True" dans ir.cron → ignoréForme texte non interprétée
en search → erreur n'accepte aucun attribut simple
type="json" dans @http.route → KODeprecatedtype="jsonrpc"
Ligne ACL sans group_id → warning bloquantObligatoire en Odoo 19Déclarer un groupe explicite

Préserver la configuration de production : le pattern noupdate

Pour un modèle singleton (configuration, clés API), il faut empêcher les mises à jour de module (-u) de réinitialiser les données saisies en production :



    Configuration

# models/config.py
@api.model
def get_config(self):
    records = self.search([], limit=1)
    if not records:
        return self.create({'name': 'Configuration'})
    return records

noupdate="1" → Odoo crée l'enregistrement à l'installation mais ne le modifie plus lors des -u suivants. Les clés sensibles (API, webhooks) saisies en production sont préservées.

Règles absolues du développement

Comme pour le pilotage, le développement assisté par IA repose sur un cadre de sécurité strict — c'est lui qui rend la vitesse tenable.

  • Jamais d'action sur la base de production sans confirmation humaine explicite.
  • Ne pas modifier /etc/odoo/odoo.conf sans discussion préalable.
  • Ne pas supprimer ou écraser un fichier sans le lire d'abord.
  • Ne pas modifier la structure de la base de données hors ORM Odoo.
  • Toujours vérifier les logs après un -u.
  • _staging/ = signal de validation — n'y copier que les modules testés.

Cette page est le pendant « développement » de la méthode. Pour le pilotage quotidien d'Odoo en langage naturel (devis, CRM, factures, contacts), voir la page Piloter Odoo en langage naturel avec Claude.

Méthode IA&TIC — © 2026 IA&TIC — Freddy Maillard & Fabrice-Loïc Barbier — CC BY-SA 4.0