Skip to content

core/ strictement ERP-agnostique

Contexte

Le sous-package electricore/core/ a été pensé dès le départ comme couche métier portant les calculs énergie purs (périmètre, abonnements, énergie, TURPE, accise, facturation Enedis). En pratique, cette intention a glissé : electricore/core/loaders/odoo/ et electricore/core/writers/odoo.py (depuis migrés vers integrations/odoo/) ont été introduits parce qu'au démarrage seule l'instance EDN existait, et Odoo était le seul ERP en jeu. Aucun ADR n'avait inscrit la règle, donc le code l'a discrètement érodée.

Le coût de cette dérive devient lisible maintenant que l'instance Enargia (cf. ADR-0015) consomme core/ depuis des notebooks hébergés hors de ce dépôt pour ses propres calculs métier — sans Odoo direct. Côté EDN, les orchestrations qui rapprochent Enedis et Odoo (cf. ADR-0014, ADR-0013) sont par nature instance-spécifiques mais elles vivent aujourd'hui à côté des pipelines purs, ce qui brouille le contrat.

Décision

core/ ne dépend que de Polars, DuckDB, Pandera, et de la stdlib Python. Aucune dépendance d'intégration ERP (Odoo, Haulogy, autre) ne traverse son interface ni ses imports.

Concrètement :

  • Nouveau sous-package electricore/integrations/ qui accueille les adaptateurs vers les ERP et systèmes externes. Premier occupant : integrations/odoo/.
  • core/loaders/odoo/integrations/odoo/loaders/ (les query builders OdooReader, OdooQuery et leurs helpers).
  • core/writers/odoo.pyintegrations/odoo/writers.py.
  • Les orchestrations qui composent du métier core et de l'ERP (par ex. facturation_du_mois, accise_du_trimestre, le rapprochement rapprocher_facturation_mensuelle) vivent dans integrations/odoo/ — elles sont EDN-shaped aujourd'hui et serviront de prototype pour un futur module Odoo libre.
  • Les modèles Pandera qui décrivent des objets Odoo (core/models/odoo/) suivent dans integrations/odoo/models/.
  • Test d'imports en CI : tests/architecture/test_core_purity.py parse tous les fichiers electricore/core/**/*.py et échoue si l'un d'eux importe electricore.integrations, electricore.api, electricore.bot, ou une lib ERP (odoorpc, etc.). C'est la garantie exécutable que le contrat ne se ré-érode pas silencieusement.

api/services/ se réduit au binding HTTP + à la sérialisation (XLSX/Arrow/ZIP, factorisée). Les services importent désormais à la fois core/ et integrations/odoo/, et c'est attendu — l'API est le hub (cf. ADR-0009).

Raison

Trois facteurs cumulés :

  1. Le seam est earning its keep aujourd'hui. Enargia est un consommateur réel de core/ qui n'a pas Odoo dans la boucle. Garder Odoo dans core/ rend le module impossible à utiliser pour ce cas — ce n'est plus une pureté esthétique, c'est un blocage.
  2. L'horizon ERP libre. Le projet vise à émerger comme support des fournisseurs alternatifs/anticapitalistes, avec un futur module Odoo couplé à ElectriCore. La règle « core/ = énergie pure, integrations/<erp>/ = colle métier » trace dès maintenant l'arborescence dans laquelle ce module pourra atterrir sans refactor.
  3. Le test d'imports rend la règle exécutable. Une convention écrite seule dans CONTEXT.md se contourne sous pression. Un test CI qui échoue est lisible par tout futur contributeur — humain ou agent.

Alternatives écartées :

  • Acter qu'Odoo est in-core, écrire l'ADR « core/ est Odoo-aware ». Rapide, mais ferme la porte à Enargia et à l'évolution multi-instance. Le coût de la migration aujourd'hui (~30 fichiers, imports à mettre à jour) est inférieur au coût futur de refactor sous contrainte de production.
  • Introduire un seam ERP générique (ERPAdapter interface, Odoo et Haulogy comme adaptateurs). Hypothétique aujourd'hui : Haulogy est lui-même basé sur Odoo et le second consommateur ERP n'est pas en prod ici. Un adapter = seam hypothétique — on attend qu'il devienne réel pour le nommer.

Conséquences

  • ADRs à mettre à jour :
  • ADR-0007 (query builders)OdooQuery n'est plus en core/ mais en integrations/odoo/. Reformuler la note d'uniformité d'API (un caller à cheval core/integrations reste légitime via l'API ou via les services).
  • ADR-0009 (API-centrique) — la phrase « DuckDB, les pipelines Polars et OdooReader/Writer sont des composants internes consommés via l'API » devient « DuckDB et les pipelines Polars (core) + les adaptateurs (integrations) sont consommés via l'API ».
  • ADR-0012 (API read-only Odoo) — chemins d'import à mettre à jour, principe inchangé.
  • ADR-0013 (renommage Périmètre → Historique) — pas d'impact structurel, mention résiduelle d'Odoo à reformuler si présente.

  • CONTEXT-MAP.md : ajouter electricore/integrations/ comme contexte au même titre que core, etl, api, bot. Glossaire dédié integrations/odoo/CONTEXT.md accueillant les termes Odoo-spécifiques (sale.order, account.move, x_invoicing_state, RSC côté écriture). Le glossaire core/CONTEXT.md garde les concepts métier neutres (Historique, Périmètre, Abonnement, RSC en tant que concept Enedis) ; les sections « Intégration Odoo » et « Rapprochement PDL ↔ RSC » migrent vers integrations/odoo/CONTEXT.md.

  • Documentation : README, CLAUDE.md, docs/odoo-query-builder.md — mise à jour des imports et de l'arborescence.

  • Notebooks : les imports from electricore.core.loaders import OdooReader, OdooQuery, ... deviennent from electricore.integrations.odoo import OdooReader, OdooQuery, .... Aucun changement de signature.

Limites à connaître avant un changement d'échelle

  • Pas de seam ERP-générique aujourd'hui. Si une seconde intégration ERP (Haulogy ou autre) entre en prod, on aura deux adaptateurs réels — c'est alors le moment de nommer le seam (interface ERPAdapter ou équivalent). Tant qu'on en a un seul, la duplication éventuelle entre integrations/odoo/ et un futur integrations/<x>/ reste préférable à une abstraction prématurée.
  • Le bot Telegram reste Odoo-couplé. Plusieurs commandes du bot (/check odoo, exports facturation/taxes) supposent un ERP Odoo configuré. Avant de poser un seam ERP, étape intermédiaire à prévoir : rendre le bot fonctionnel sur une instance sans ERP connecté, au minimum pour les commandes ETL et exports flux Enedis. Issue à ouvrir après les PR de migration.
  • La règle ne couvre pas etl/. Le sous-package ETL ingère les flux Enedis (XML/CSV via SFTP, déchiffrement AES) et reste non-Enedis-agnostique — c'est sa raison d'être. Le contrat « pas d'ERP » s'applique uniquement à core/.