API : permettre aux intégrations externes de déclarer leurs propres types de contrat énergie

Bonjour à tous,

Suite à la discussion sur les tarifs week-end et à la réponse de @pierre-gilles sur Gladys#2999, voici la demande de fonctionnalité correspondante.

Le principe, d’abord. Pierre-Gilles proposait de garder le cœur générique et de permettre aux intégrations externes de déclarer de nouveaux types de contrat, plutôt que d’empiler dans le cœur des cas propres à chaque pays. Je suis complètement d’accord, et en regardant le code je trouve que c’est encore plus pertinent que je ne le pensais : l’architecture va déjà dans ce sens, il manque surtout d’ouvrir la porte.

À noter que @mutmut a déjà ouvert une demande sur la gestion des tarifs selon le jour, la saison et les vacances. Celle-ci ne la remplace pas : la sienne décrit le besoin utilisateur, celle-ci décrit le mécanisme technique qui permettrait d’y répondre sans alourdir le cœur. Les deux sont complémentaires.

Ce qui existe déjà et qui va dans le bon sens

server/services/energy-monitoring/contracts/contracts.calculateCost.js est déjà un dictionnaire indexé par type de contrat, où chaque entrée est une fonction de calcul avec une signature uniforme :

(energyPricesAtConsumptionDate, consumptionDate, consumptionValue, systemTimezone, context) => cost

C’est exactement un point d’extension. Mieux : le paramètre context sert déjà à injecter des données externes — Tempo y reçoit edfTempoHistoricalMap, c’est-à-dire un calendrier de couleurs de jours récupéré ailleurs. Le précédent d’un type de contrat qui dépend d’une source de données extérieure est donc déjà établi.

Côté stockage, hour_slots est une simple chaîne libre et subscribed_power aussi. Une intégration externe pourrait y encoder ce qu’elle veut sans toucher au schéma.

Ce qui bloque

Quatre verrous, du plus dur au plus simple :

  1. Le modèle SQL. Dans server/models/energy_price.js, la colonne contract est un DataTypes.ENUM(...ENERGY_CONTRACT_TYPES_LIST), et day_type un ENUM(...ENERGY_PRICE_DAY_TYPES_LIST). C’est le vrai blocage : ajouter un type demande une migration de base, ce qu’une intégration externe ne peut pas faire.
  2. La liste en dur ENERGY_CONTRACT_TYPES dans server/utils/constants.js, qui alimente cet ENUM et la validation serveur.
  3. Le dictionnaire de calcul n’est pas enregistrable : les trois handlers sont écrits en dur dans le module.
  4. Le front, avec KNOWN_CONTRACT_TYPES en dur dans ImportPrices.jsx et les libellés dans contractTypes des fichiers i18n.

Ce que je propose

a) Passer contract et day_type de ENUM à STRING, en déplaçant la validation dans la couche applicative (contre la liste des types enregistrés, cœur + intégrations). Une convention de nommage par intégration éviterait les collisions, par exemple <service>:<type>. C’est le changement structurant : sans lui, aucune extension externe n’est possible.

b) Exposer une API d’enregistrement sur le service, dans l’esprit de :

gladys.energyMonitoring.registerContractType({
  id: 'edf-zen-week-end',
  calculateCost: async (prices, date, value, timezone, context) => { ... },
});

La signature étant déjà celle des handlers existants, les trois types actuels pourraient être migrés vers ce mécanisme sans rien changer à leur logique — bonne façon de valider l’API au passage.

c) Rendre l’écran d’import pilotable par les données : la liste des types et le libellé viennent des types enregistrés, avec un repli propre quand aucune traduction n’existe. Le sélecteur de créneaux horaires actuel resterait le comportement par défaut.

d) Optionnel, pour plus tard : permettre à l’intégration de déclarer le type d’éditeur de prix dont elle a besoin, voire de fournir son propre écran.

Avec ça, une intégration « énergie France » pourrait porter Zen Week-End, Tempo, les offres Engie / OHM / Enercoop et les cas saisonniers de @mutmut, sans que le cœur ait à connaître le calendrier des jours fériés français.

Une question ouverte

Aujourd’hui, les grilles tarifaires vivent dans le dépôt communautaire energy-contracts, et c’est précieux : quand quelqu’un met à jour les prix EDF, tout le monde en profite. Si les types spécifiques à un pays partent dans une intégration externe, où vont les données de prix ? Elles restent dans energy-contracts avec l’intégration qui vient juste ajouter la logique de calcul, ou elles déménagent aussi ?

J’ai une préférence pour la première option — garder un dépôt unique de grilles, mutualisé, et ne sortir que le code métier — mais c’est vraiment une question, pas une position arrêtée.

Merci !