Comprendre les constats
Un constat n'est pas un avis. C'est le moteur qui reconnaît une panne qu'il a déjà vue, et qui dit ce qu'elle coûte. Trois niveaux, trois conduites à tenir.
Les trois niveaux
| Niveau | Ce que fait le moteur | Ce que vous faites |
|---|---|---|
| BLOQUANT | L'écriture est refusée. Le défaut casserait la base, ou une autre page. | Corriger la cause, puis relancer la comparaison. Il n'y a rien à valider ici. |
| AVERTIR | L'écriture peut passer, mais elle demande une décision explicite, et cette décision est consignée. | Trancher, et écrire pourquoi dans le fil de la page — le bouton « Publier comme décision » est là pour ça. |
| VÉRIFIER | L'écriture passe. Un contrôle reste à faire après le déploiement. | Le noter, et regarder la page une fois le staging en ligne. |
Où ils apparaissent
- À l'audit — ce que le moteur trouve dans votre instance telle qu'elle est, avant toute maquette : copies COW, pages en doublon, données figées.
- À l'étape 3, page par page — ce que la comparaison entre la maquette et l'arbre combiné a produit, et ce qu'elle a refusé de produire.
Un AVERTIR n'est pas toujours une écriture qui passe sous condition : c'est parfois le moteur qui renonce à une écriture et vous dit pourquoi, en proposant une autre façon de l'obtenir.
Lire un constat
Quatre lignes, toujours dans le même ordre : le niveau et le numéro de règle, ce qui est constaté, pourquoi ça compte, et la sortie.
Le numéro de règle renvoie à la spécification du moteur. Il est stable : citez-le quand vous nous écrivez.
Trois pannes, trois règles
Aucune de ces règles n'a été écrite par précaution. Chacune est l'autopsie d'un incident.
A3 — la classe qui ne s'affiche jamais
Odoo pose souvent ses classes par t-attf-class, une expression évaluée au moment du rendu. Une vue d'héritage qui ajoute une classe dans l'attribut class statique voisin ne sert à rien : l'expression dynamique remplace l'attribut entier au rendu.
Ce qui rend le cas coûteux, c'est que tout a l'air juste. L'xpath résout. La classe est bien dans l'arbre combiné. Le fichier se charge sans erreur. Et à l'écran, rien n'a bougé. Une modification d'attribut qui ne modifie rien ne se signale nulle part : elle se découvre en regardant la page, ou pas du tout.
La même mécanique a un cousin, A2 : un xpath en hasclass('x') interroge la source, pas le rendu. Une classe posée dynamiquement n'y est pas — le xpath ne trouve rien. Le cas le plus retors est celui où la classe existe des deux façons dans le même module : o_wblog_post_title est dynamique partout sauf dans le bloc « à lire ensuite ». Le xpath trouve donc l'unique occurrence statique — la mauvaise — et ne signale rien.
Ce qu'il faut faire : viser une cible statique — un id, un name, un data-* — plutôt qu'une classe.
A4 — l'ancre qui désigne deux nœuds
Une expression xpath qui correspond à plusieurs nœuds ne provoque pas d'erreur. Odoo applique la modification au premier qu'il rencontre, et continue. Vous obtenez une page où le changement est arrivé, mais pas à l'endroit prévu — et rien dans les journaux ne le dit.
Le moteur vérifie donc chaque ancre contre l'arbre combiné et refuse celles qui ne désignent pas exactement un nœud. C'est aussi pourquoi il refuse qu'un bloc inséré emporte un identifiant déjà présent dans l'arbre : après combinaison, l'ancre en désignerait deux.
La même règle couvre l'erreur inverse, plus brutale : un xpath qui ne résout pas. La combinaison de la vue entière échoue, et la page répond 500. Trois pannes du chantier d'origine ont eu la même cause — l'identifiant lu dans le HTML rendu, la balise supposée : #products_grid_before est un <aside>, #o_wale_products_header un <header>, et un bouton visé en type=submit était en type=button. Le moteur prend donc la balise dans l'arbre, jamais dans une supposition.
Ce qu'il faut faire : resserrer sur un identifiant unique. Côté maquette, poser des identifiants distincts sur les blocs ajoutés.
B2 — la copie que l'éditeur a faite dans votre dos
Quand quelqu'un modifie une page depuis l'éditeur de site, Odoo ne modifie pas la vue générique : il en fait une copie attachée à ce site précis. La copie masque l'original. Une vue de thème qui hérite du parent générique n'atteint plus la page — sans la moindre erreur.
C'est la panne qui fait perdre le plus de temps, parce qu'elle ressemble à un cache : on corrige, on recharge, rien ne bouge, on recorrige. Un build de production réel en portait 24, dont website.homepage et six en-têtes de website_sale.
Ce qu'il faut faire : l'audit les liste. Avant d'intégrer, décidez pour chacune — la remettre à plat, ou hériter de la copie plutôt que du générique.
B4 — deux templates du même nom, un seul se rend
Un cas voisin, qui décide du data-odoo de votre maquette. appointment.appointments_list_layout et website_appointment.appointments_cards_layout ont le même titre et partagent une classe ; seul le second est réellement rendu. Hériter du mauvais ne produit aucune erreur : la vue est créée, elle reste active, elle se combine proprement, et elle n'atteint jamais de page.
Ce qu'il faut faire : identifier le template servi en cherchant dans la page rendue une classe unique à ce template, pas un titre.
La solidité de l'ancre
À côté de chaque écriture proposée, une colonne dit sur quoi l'ancre tient. Ce n'est pas une décoration : c'est la probabilité que l'écriture survive au prochain changement.
| Tient sur | Ce que ça veut dire |
|---|---|
| identité | Un id, un name ou un data-*. Le cas sûr. |
| classe distinctive | Une classe qui nomme le bloc. Tient tant que la classe reste. |
| chemin depuis une ancre | Un chemin relatif à un ancêtre solide. Un frère inséré au bon endroit le rend ambigu. |
| rang entre frères | Le nœud est désigné par sa position. Le plus fragile : la fratrie bouge. |
Les deux dernières valent un VÉRIFIER, jamais un refus. L'ancre est vérifiée unique au moment de la proposition ; elle ne le restera pas forcément au moment où Odoo l'appliquera, et Odoo applique alors au premier nœud, sans erreur. Les taire laisserait croire l'ancre figée.
Ce que le moteur ne fera jamais
- Supprimer. Un élément présent dans Odoo et absent de la maquette produit un constat, jamais une suppression.
- Inventer une valeur. Chaque écriture porte le nœud de maquette d'où elle sort. Une opération sans provenance est refusée. Un prix, une durée, une certification qu'il n'a lus nulle part sont affichés « donnée manquante », jamais devinés.
- Surcharger un template partagé. Un formulaire surchargé pour une seule page a levé une erreur à chaque ouverture de l'éditeur, sur trois sites qui n'avaient rien à voir avec le chantier.
- Écrire sans avoir sondé la cible. Chaque xpath est évalué contre l'arbre combiné avant de sortir.
- Rendre un XML qu'il n'a pas su relire. Une maquette contient des <img> non fermés, des commentaires, des --, des caractères venus d'un traitement de texte. Recopiés tels quels, ils font rejeter le fichier entier par l'analyseur d'Odoo : le module ne charge plus, pour une maquette mal formée.
Que faire, dans l'ordre
- Traiter les BLOQUANT. Ils désignent presque toujours soit la maquette — un identifiant à poser, un texte à déplacer sur la feuille qui le porte — soit le template visé.
- Relancer la comparaison. Elle est refaite sur la maquette courante ; si vous avez réimporté un fichier, l'écran vous dira qu'une proposition antérieure est périmée plutôt que de la présenter comme à jour.
- Trancher les AVERTIR et écrire la décision dans le fil de la page. Six mois plus tard, c'est la seule trace de pourquoi l'écriture est là.
- Noter les VÉRIFIER pour la relecture du staging.
- Marquer la page intégrée. L'empreinte de la maquette est alors figée : c'est elle qui, comparée à la suivante, signalera un changement.