Odoo 19 poursuit un travail de normalisation des noms de champs commencé depuis plusieurs versions. Les champs relationnels doivent se terminer par le suffixe _id, conformément à la convention de l'ORM. Le champ d'unité de mesure historique, appelé product_uom sur de nombreux modèles, devient donc product_uom_id. Le problème pour les équipes qui maintiennent des modules sur mesure ne vient pas du renommage lui-même. Il vient du fait que ce renommage n'est pas appliqué de façon uniforme, et qu'un champ qui change de nom sans alias de compatibilité casse le code qui le référence, souvent sans lever d'erreur immédiate au chargement.
Ce que dit le code source
En comparant directement les définitions de champs entre la branche 18.0 et la branche 19.0 du dépôt Odoo, le tableau se précise. Sur sale.order.line, le champ passe de product_uom en version 18 à product_uom_id en version 191. Même changement sur purchase.order.line. Sur account.move.line, le champ s'appelait déjà product_uom_id en version 18, il ne bouge donc pas. Et sur stock.move, le champ reste product_uom en version 19, exactement comme avant.
| Modèle | Odoo 18 | Odoo 19 | Statut |
|---|---|---|---|
sale.order.line | product_uom | product_uom_id | renommé |
purchase.order.line | product_uom | product_uom_id | renommé |
account.move.line | product_uom_id | product_uom_id | déjà aligné |
stock.move | product_uom | product_uom | inchangé |
La logique devient lisible une fois posée ainsi. La comptabilité servait déjà de référence avec product_uom_id. Les ventes et les achats s'alignent sur cette convention en version 19. Le module stock, lui, garde son ancien nom. Un développeur qui suppose que « tout est passé en product_uom_id » et remplace mécaniquement toutes les occurrences va casser son code sur les mouvements de stock. À l'inverse, celui qui n'adapte rien va casser ses surcharges de vente et d'achat.
Ce qui casse concrètement
Le champ renommé n'a pas d'alias de repli. Sur sale.order.line en version 19, il n'existe plus aucun attribut product_uom. Toute référence directe déclenche une erreur, mais pas toujours au même moment. Une vue XML qui cite product_uom dans un attribut <field> ou un domain échoue au chargement du module. Une expression Python comme line.product_uom.name lève une AttributeError seulement à l'exécution, c'est à dire quand un utilisateur ouvre le bon écran. Un champ related déclaré sur product_uom casse à la définition du modèle. Les appels mapped('product_uom'), filtered, ou les décorateurs @api.depends('product_uom') tombent dans la même catégorie de rupture différée.
Les filtres de recherche stockés en base et les actions serveur écrites dans l'interface sont un angle mort supplémentaire, car ils ne passent pas par la revue de code habituelle. Un domaine sauvegardé qui référence product_uom sur une ligne de vente renverra une erreur à la première utilisation après migration.
Le domaine de sélection a aussi bougé
Le renommage s'accompagne d'un second changement moins visible. En version 18, le domaine du champ sur sale.order.line limitait le choix aux unités de la même catégorie que le produit, via [('category_id', '=', product_uom_category_id)]. En version 19, ce domaine devient [('id', 'in', allowed_uom_ids)] et s'appuie sur un champ calculé allowed_uom_ids2. Si vos modules surchargeaient le domaine ou recalculaient les unités autorisées, cette logique est à revoir en parallèle du renommage.
Comment sécuriser la migration
La première étape reste une recherche exhaustive de la chaîne product_uom dans tout le code métier, les vues, et les données exportées. Un grep distingue rapidement les occurrences légitimes sur stock.move de celles à corriger sur les ventes et les achats. Un environnement de préproduction avec les vrais modules installés reste le seul moyen fiable de faire remonter les ruptures différées, celles que le simple chargement des modules ne révèle pas. Les tests unitaires qui exercent la création de commandes et de factures attrapent une bonne partie des AttributeError avant la mise en production.
Sur le plan de l'environnement, Odoo 19 demande Python 3.10 ou plus récent, et relève le minimum PostgreSQL de la version 12 à la version 133. Ces prérequis conditionnent la préparation du serveur avant même de toucher au code, et ils sont documentés officiellement.
Pour un panorama plus large des ajustements ORM de cette version, voir notre article sur les changements ORM qui touchent vos modules.
Notre lecture
Ce type de renommage partiel est typique des migrations Odoo. La convention est saine sur le long terme, mais l'absence d'alias de compatibilité et l'application inégale entre modules transforment une bonne intention en source de régressions silencieuses. Notre conseil de praticien est de ne jamais traiter product_uom comme un remplacement global. Le bon réflexe consiste à établir, modèle par modèle, la liste des champs réellement renommés dans les modules que vous utilisez, puis à corriger en s'appuyant sur des tests qui ouvrent effectivement les écrans concernés. Le coût d'une migration Odoo se joue rarement sur les grandes fonctionnalités annoncées. Il se joue sur ces détails de schéma qui ne préviennent pas.
Sources
- Odoo 19.0, sale/models/sale_order_line.py : https://github.com/odoo/odoo/blob/19.0/addons/sale/models/sale_order_line.py
- Odoo 18.0, sale/models/sale_order_line.py : https://github.com/odoo/odoo/blob/18.0/addons/sale/models/sale_order_line.py
- Odoo 19.0, stock/models/stock_move.py : https://github.com/odoo/odoo/blob/19.0/addons/stock/models/stock_move.py
- Odoo 19.0, account/models/account_move_line.py : https://github.com/odoo/odoo/blob/19.0/addons/account/models/account_move_line.py
- Odoo 19.0 documentation, Source install (prérequis Python et PostgreSQL) : https://www.odoo.com/documentation/19.0/administration/on_premise/source.html
Footnotes
-
Un champ
fields.Many2oneétablit une relation « plusieurs vers un » vers un autre modèle, iciuom.uom. La convention Odoo impose le suffixe_idsur ce type de champ relationnel. ↩ -
allowed_uom_idsest un champ calculé qui liste les unités de mesure autorisées pour une ligne donnée, en fonction du produit sélectionné. Le domaine du champ d'unité s'appuie sur cette liste en version 19. ↩ -
Prérequis relevés dans la documentation officielle d'installation depuis les sources d'Odoo 19.0. ↩