Aller au contenu principal
Ben Tiga Jean Vincent
Retour au blog
  • Odoo
  • Python
  • ERP

Développer un module Odoo 19 qui survit aux mises à jour

Les règles que j'applique pour qu'un module métier sur-mesure ne casse pas à la montée de version : héritage propre, record rules et séparation des responsabilités.

Ben Tiga Jean Vincent2 min de lecture

Un module Odoo écrit vite fonctionne dès le premier jour. Le problème arrive six mois plus tard, à la mise à jour : des champs ont disparu du standard, une vue ne se charge plus, et le client découvre que son POS ne s'ouvre plus. Voici ce qui change tout.

Ne jamais écraser, toujours hériter

La tentation est de modifier directement un modèle standard. En pratique, on étend :

class PosSession(models.Model):
    _inherit = "pos.session"
 
    x_control_caisse_id = fields.Many2one(
        "christo.control.caisse",
        string="Contrôle de caisse",
        copy=False,
        index=True,
    )

Deux détails qui comptent : le préfixe sur les champs ajoutés, qui garantit qu'aucune version future d'Odoo n'introduira une collision de nom, et copy=False, pour qu'une duplication d'enregistrement ne traîne pas un lien de contrôle obsolète.

Les vues : xpath plutôt que remplacement

Remplacer une vue complète revient à geler la version d'Odoo au jour de l'écriture. L'héritage ciblé résiste bien mieux :

<record id="view_pos_session_form_inherit" model="ir.ui.view">
    <field name="inherit_id" ref="point_of_sale.view_pos_session_form"/>
    <field name="arch" type="xml">
        <xpath expr="//group[@name='session_info']" position="inside">
            <field name="x_control_caisse_id" readonly="state != 'opened'"/>
        </xpath>
    </field>
</record>

Si le groupe ciblé disparaît, l'erreur est explicite au chargement du module, pas trois semaines plus tard au détour d'un écran.

Les record rules sont la seule vraie frontière

Dans un contexte multi-boutiques, filtrer côté vue ne protège rien : l'utilisateur accède toujours aux données par l'API ou l'export. Le cloisonnement se fait au niveau de l'ORM.

<record id="rule_stock_picking_boutique" model="ir.rule">
    <field name="model_id" ref="stock.model_stock_picking"/>
    <field name="domain_force">
        [('picking_type_id.warehouse_id', 'in', user.boutique_ids.ids)]
    </field>
    <field name="groups" eval="[(4, ref('group_responsable_boutique'))]"/>
</record>

Un point souvent oublié : une règle sans groups s'applique à tout le monde, y compris aux utilisateurs techniques. Il faut toujours vérifier le comportement avec un compte de test non administrateur, jamais avec le superutilisateur.

Séparer le calcul métier des modèles

Les méthodes de calcul longues (réconciliation d'encaissements, valorisation de transferts inter-boutiques) n'ont pas leur place dans un compute. Un champ calculé est réévalué à chaque lecture du recordset ; une réconciliation sur plusieurs milliers de lignes transforme alors un simple affichage de liste en requête de plusieurs secondes.

Je préfère un champ stocké, mis à jour explicitement :

montant_reconcilie = fields.Monetary(compute="_compute_reconcilie", store=True)
 
@api.depends("line_ids.amount", "line_ids.state")
def _compute_reconcilie(self):
    for record in self:
        record.montant_reconcilie = sum(
            line.amount for line in record.line_ids if line.state == "done"
        )

Le @api.depends est ce qui rend le champ stocké fiable : sans dépendance correcte, la valeur se désynchronise silencieusement.

Tester la montée de version avant le client

La vérification minimale avant toute livraison :

  1. Restaurer une copie de la base de production sur un environnement de staging.
  2. Lancer odoo -u mon_module --stop-after-init et lire tous les avertissements, pas seulement les erreurs.
  3. Ouvrir chaque vue modifiée avec un compte utilisateur réel.

Cette séquence prend vingt minutes. Une mise à jour ratée en production, sur un système de caisse, coûte une journée de chiffre d'affaires.

Articles liés

4 min de lecture

Intégrer les API de paiement des agrégateurs sans se faire piéger

Mobile Money, cartes bancaires, agrégateurs multiples : le modèle unifié, l'idempotence et la réconciliation qui évitent les doubles débits en production.

  • Paiement
  • FinTech
  • Architecture
  • API
Lire l'article
4 min de lecture

Ma méthode pour un front-end Angular qui reste maintenable

Découpage par domaine, composants standalone, signaux contre RxJS, intercepteurs et détection de changement : les décisions que je prends au démarrage d'un projet Angular.

  • Angular
  • TypeScript
  • Architecture
  • Frontend
Lire l'article