Le développement modulaire, expliqué aux développeurs
L’arbre des dépendances est la seule structure qu’Odoo suit pour installer, mettre à jour et charger le code. Tout ce qui le contourne finit par casser, en général à la migration suivante.
Un module, c’est quoi ?
Un dossier qui ajoute quelque chose à Odoo : des modèles (les objets, comme une commande ou un contact), des vues (les écrans), des données, des droits d’accès et des tests. Son fichier __manifest__.py est sa carte d’identité : son nom, sa version, et surtout ses depends, la liste des modules dont il a besoin. Odoo charge les modules dans l’ordre de ces dépendances, et un module ne voit que ce que cette liste lui donne.
Les règles
-
Un besoin, un module
Un module fait une chose, dite dans son nom (
timesheet_billingplutôt quecustom_stuff). Il s’installe seul sur une base vide, et ses tests passent avec ses seules dépendances. On ne découpe pas pour le plaisir : on sépare quand une moitié peut servir sans l’autre, quand une licence l’impose, ou quand une application est facultative. Sinon, on garde ensemble. -
Des dépendances dans un seul sens
La liste
dependsdit tout. Un module ne nomme jamais un module qui dépend de lui, ni un module absent de sa liste : ni dans le code, ni dans les fichiers de données, ni dans les tests, ni dans la documentation. Si le module A a besoin d’un champ du module B, c’est A qui dépend de B, jamais l’inverse. -
Un module de liaison plutôt qu’une devinette
Regarder dans le code si un autre module est installé, pour changer de comportement, crée une dépendance cachée : Odoo ne la voit pas, et elle casse à la migration suivante. À la place, un troisième module, dit de liaison, qui dépend des deux et porte le code qui les relie. Marqué
auto_install, Odoo l’installe tout seul dès que les deux sont là. Un script du dépôt vérifie cette règle à chaque changement. -
Étendre, pas modifier
Pour ajouter un champ ou changer le comportement d’un module existant, on ne touche pas à ses fichiers : on hérite. En Odoo,
_inheritpermet d’ajouter à un modèle existant depuis son propre module, et une méthode peut être complétée en la redéfinissant. Le module d’origine reste intact, et il se met à jour sans conflit. -
Les données propres au client, dans leur module
La configuration d’un déploiement (catégories, numérotation, étapes de projet) vit dans un module de données qui dépend des modules génériques, jamais dans les génériques eux-mêmes. Ils restent réutilisables, et ce qui est propre au client se voit. Déplacer un champ d’un module à un autre demande un script de migration, sinon la mise à jour supprime ses données.
-
Supprimer, pas désactiver
Un comportement non voulu se retire : son code, ses tests, sa documentation, et un script de migration pour ce qui reste en base. Pas d’option pour le taire : elle garderait du code mort, qu’il faudrait encore migrer.
Un exemple : facturer le temps passé
Trois modules, et chacun s’installe seul.
-
timesheet_categoryAjoute une catégorie sur chaque ligne de temps : facturable ou non. Dépend du module de feuilles de temps d’Odoo.
-
timesheet_budgetUn budget d’heures par client, et ce qu’il en reste. Dépend du premier.
-
timesheet_billingTransforme les heures facturables en lignes de facture. Dépend des deux, et n’existe que parce qu’un client en avait besoin.
En pratique
Le manifeste d’un module de liaison : il dépend des deux modules qu’il relie, et Odoo l’installe de lui-même quand les deux sont là.
# sale_delivery_zone/__manifest__.py
{
"name": "Ventes – zones de livraison",
# Les deux modules reliés : sans l'un des deux, rien à faire.
"depends": ["sale", "delivery_zone"],
# Installé tout seul dès que les deux sont présents.
"auto_install": True,
"license": "AGPL-3",
}
Étendre sans modifier : le champ s’ajoute au modèle existant depuis le module de liaison, et le module d’origine ne change pas.
# sale_delivery_zone/models/sale_order.py
from odoo import fields, models
class SaleOrder(models.Model):
# On complète la commande de vente, on ne la réécrit pas.
_inherit = "sale.order"
delivery_zone_id = fields.Many2one("delivery.zone")
Ce que ça coûte
Plus de modules, donc plus de fichiers à nommer, documenter et lister. Une branche de travail par module. Et un refus, parfois, d’ajouter un champ « là où c’est pratique ». C’est le prix d’une mise à jour qui ne surprend pas.
Vous faites autrement ?
Un désaccord, une meilleure façon de faire, un cas qui ne rentre pas dans ces règles : écrivez-moi, je lis tout.
Les autres méthodologies
-
Les tests automatisés, expliqués aux développeurs
Les règles, un bug pris en quatre étapes et un test de dix lignes, pour qui débute avec Odoo.
-
Les standards de l’OCA, expliqués aux développeurs
Un résumé des conventions, les trois questions avant d’écrire, et les liens vers les pages de l’OCA qui font foi.