gmao/docs/development/DECISIONS_020_DEV3.md
root 254018d132
Some checks are pending
CI - Tests et Syntax / lint-and-test (push) Waiting to run
test(c4): complete contract and consumable coverage
2026-08-24 18:17:27 +00:00

18 KiB
Raw Blame History

Décisions architecturales 0.2.0-dev.3

Les décisions sont ajoutées au fil des checkpoints. Les modèles existants sont privilégiés avant toute nouvelle table.

D-005 — Centre de configuration permanent et setup progressif

CONTEXTE : le setup initial ne doit pas devenir une page jetable ; une GMAO utile doit accepter une configuration partielle et expliciter les actions restantes.

OPTIONS ÉTUDIÉES : checklist statique de fin d'installation ; tableau calculé et permanent avec liens d'action.

CHOIX : créer/faire évoluer un Centre de configuration permanent, organisé par domaines (Essentiel, Maintenance, Patrimoine, Entreprises, Compteurs, Cartographie technique, Intégrations), avec états calculés et intégrations facultatives non bloquantes.

JUSTIFICATION : rendre la configuration progressive observable et exploitable sans SQL ni connaissance de l'architecture.

CONSÉQUENCES : chaque contrôle doit avoir une règle de calcul et une destination UI ; Pronote, ENT, Outlook, Yeastar, IA, compteurs, logements et cartographie restent facultatifs.

DATE/COMMIT : 2026-08-23 / préparation checkpoint B.

D-006 — RoomType structurel, RoomProfile séparé à étudier

CONTEXTE : RoomType classe actuellement les locaux et est utilisé par Pronote et les écrans de patrimoine, mais ne porte aucun catalogue d'équipements, de quantités ou d'ouvrages.

OPTIONS ÉTUDIÉES : enrichir RoomType ; créer un RoomProfile distinct relié à RoomType ; stocker des JSON dans RoomType.

CHOIX : RoomProfile séparé, avec RoomProfileItem et RoomSurface normalisés. RoomType reste structurel et n'est pas modifié.

JUSTIFICATION : éviter de casser les usages de classification/enseignement et permettre plusieurs profils de proposition pour un même type structurel.

CONSÉQUENCES : migrations k6f7a8b9c0d1 et l7a8b9c0d1e2 ajoutées ; association de profil nullable ; aucune création automatique d'équipement sans validation utilisateur.

DATE/COMMIT : 2026-08-23 / implémentation checkpoint B en cours.

D-007 — Origine dune proposition de profil

CONTEXTE : une réapplication dun RoomProfile doit ajouter uniquement le delta sans rechercher les équipements par nom.

OPTIONS ÉTUDIÉES : comparaison fragile nom/catégorie ; table de liaison ; identifiant nullable sur Equipment.

CHOIX : ajouter Equipment.room_profile_item_id, nullable, indexé et relié à RoomProfileItem. Les équipements historiques sans origine restent valides.

JUSTIFICATION : lidentifiant de proposition est stable, compatible avec les noms en doublon et permet une idempotence explicite sans mutation silencieuse.

CONSÉQUENCES : migration non destructive m8b9c0d1e2f3; le service commun renseigne lorigine lors de lapplication dun profil.

DATE/COMMIT : 2026-08-23 / checkpoint B.

D-008 — Centre de configuration sans faux état derreur

CONTEXTE : le lot dun équipement est facultatif et les modèles de contrats exposent des colonnes ORM dont certaines propriétés métier ne sont pas directement filtrables.

CHOIX : afficher un équipement sans lot comme information facultative ; filtrer les contrats via Contract.__table__.c pour éviter les erreurs de rendu du Centre.

JUSTIFICATION : une configuration progressive ne doit pas bloquer linventaire ni produire une page de synthèse cassée.

CONSÉQUENCES : les liens du nouveau menu pointent vers Centre, profils, bulk et horaires ; les intégrations avancées restent facultatives.

DATE/COMMIT : 2026-08-23 / validation B.

D-000 — Checkpoints indépendants

CONTEXTE : le chantier couvre plusieurs domaines avec migrations et risques différents.

OPTIONS ÉTUDIÉES : un gros développement continu ; checkpoints commitables.

CHOIX : checkpoints A à E, chacun documenté, testé et versionné.

JUSTIFICATION : reprise fiable, absence de régression masquée, dépôt source de vérité.

CONSÉQUENCES : aucun checkpoint suivant ne démarre avant validation du précédent.

DATE/COMMIT : 2026-08-23 / initialisation.

D-001 — Service de création Equipment

CONTEXTE : les routes fiche avancée et wizard construisaient des objets différents et le wizard retrouvait ensuite des équipements par nom.

OPTIONS ÉTUDIÉES : conserver les routes indépendantes ; centraliser la construction dans un service.

CHOIX : core.services.equipment_creation.create_equipment, lot et catégorie facultatifs, retour de l'instance persistée.

JUSTIFICATION : évite les collisions de noms et garantit la même gestion de quantité, parent, localisation et génération préventive.

CONSÉQUENCES : les anciens parcours sont progressivement migrés ; les relations techniques restent hors parent_id.

DATE/COMMIT : 2026-08-23 / checkpoint A en cours.

D-017 — Compléments C4

Le rôle contractuel d'un compteur est explicite (COPIER_BW, COPIER_COLOR ou NONE) : un compteur inconnu n'est jamais classé silencieusement N&B. Lorsqu'il manque l'index de début d'une période, la consommation manquante est estimée par moyenne journalière historique ; pour les équipements d'impression, les jours scolaires comparables sont privilégiés. Sans historique suffisant, la qualité est PARTIAL. L'agrégation globale suit PARTIAL > ESTIMATED > EXACT.

La clôture d'un contrat est centralisée dans un service métier : la période en cours est tronquée, les périodes futures jamais commencées sont neutralisées et les associations ouvertes sont fermées, sans suppression de l'historique. Une commande partiellement reçue peut être annulée pour son restant ; le stock déjà reçu est conservé, et plusieurs commandes ouvertes sont agrégées.

La couverture de stock est une estimation globale fondée sur les rythmes des équipements actifs compatibles, avec qualité ESTIMATED, PARTIAL ou indisponible. Les alertes quota et stock ouvertes remontent dans « Ma journée » comme informations sans durée ni intervention automatique, filtrées par contract.view et stock.view. La migration additive t5h6i7j8k9l0 complète s4g5h6i7j8k9.

D-017 — Contrats photocopieurs et consommables C4

Les photocopieurs et imprimantes restent des Equipment et leurs compteurs restent des Meter. Les contrats sont pluriannuels ; la date globale du contrat est distincte de la date de début des périodes de quota et de l'anniversaire configurable. Les quotas N&B/couleur appartiennent à la période contractuelle, jamais à une machine.

Le parc est historisé par une association datée contrat/machine. Un remplacement ferme l'association précédente avec un motif et conserve les compteurs/relevés de l'ancienne machine. Les projections utilisent prioritairement les jours scolaires connus ; une insuffisance de données est affichée comme telle. Les alertes de dépassement sont dédupliquées par période et couleur.

Les références Consumable existantes sont le stock global partagé. EquipmentConsumable porte la compatibilité multi-machines, ConsumableUsage porte les sorties, et les nouvelles commandes/réceptions augmentent le stock uniquement lors d'une réception réelle. La durée est calculée séparément pour chaque couple équipement/référence ; l'installation courante n'entre pas dans la moyenne terminée.

MIGRATION : s4g5h6i7j8k9 est additive depuis r3f4g5h6i7j8 et conserve les consommables historiques. La validation base vide et la validation visuelle restent ouvertes.

PERFORMANCE : le calcul contractuel charge les compteurs et relevés en lot ; le détail contrat précharge fournisseur, périodes et parc. Sur le jeu TEST_UI_C4_PERF_*, le calcul est passé de 75 à 11 requêtes et le détail de 234 à 98 requêtes. Le détail consommable mesuré à 4 requêtes reste séparé.

D-011 — Échéances et tournées de relevés C2

CONTEXTE : un relevé est un travail opérationnel planifiable, mais ne doit pas être modélisé comme une intervention. Les fréquences simples ne suffisent pas pour les dates contractuelles et les logements.

CHOIX : créer MeterReadingSchedule pour la règle, MeterReadingOccurrence pour chaque échéance logique, MeterReadingRound/MeterReadingRoundMember pour la définition d'une tournée et MeterReadingRoundOccurrence pour sa réalisation datée. L'unicité (schedule_id, target_date) et l'unicité d'une occurrence de tournée par date rendent la génération idempotente.

La date cible reste distincte de la date opérationnelle. L'anticipation réutilise le moteur horaire/calendrier existant pour les règles scolaires ; les logements sont hors calendrier scolaire par défaut. Sans horaires explicites, le système conserve la date cible plutôt que d'inventer un jour travaillé.

TRAITEMENT : une occurrence avec relevé appelle le service C1 record_meter_reading. Une occurrence sans relevé conserve un motif distinguant notamment inaccessible, absent, refus et relevé non connu, sans créer de mesure artificielle. Une occurrence ouverte d'un compteur remplacé est annulée immédiatement dans la transaction de remplacement avec une raison système et reste historisée.

INTÉGRATION : les occurrences sont une source du DayPlanner, avec responsable, durée et retard calculé ; elles ne créent pas d'intervention et ne simulent pas de créneau si le planificateur ne sait pas en attribuer un. Les routes de configuration sont administrateur ; le traitement d'une occurrence est limité à l'utilisateur assigné ou à un administrateur.

LIMITES C2 : génération à horizon borné autour de « Ma journée » (365 jours de regard arrière et 31 jours d'avance) ; aucune analyse de consommation, conversion, alerte, coût, contrat ou quota n'est implémentée.

DATE/COMMIT : 2026-08-24 / checkpoint C2.

D-012 — Intervalles et contexte analytique C3

CHOIX : calculer les intervalles à la demande à partir de deux relevés réels successifs, conserver les mesures brutes et annoter chaque intervalle par sa composition calendaire et ses régimes de chauffage. Les logements ne sont pas reclassés selon les vacances scolaires. Les régimes sont datés par compteur et réseau pour éviter l'hypothèse d'un chauffage global unique.

JUSTIFICATION : éviter une table de cache difficile à invalider après correction de relevé, tout en gardant des requêtes explicites et des données historiques intactes. Une rupture de compteur ou un reset ne produit jamais de delta analytique trompeur.

D-013 — Alertes, gaz et coûts C3

CHOIX : persister les règles et alertes C3 séparément des seuils historiques ; conserver une évidence par intervalle, dédupliquer tant qu'une alerte équivalente est ouverte, utiliser une médiane avec minimum configurable (défaut 3), puis permettre un lien relationnel vers une intervention. Les coefficients gaz sont datés et manuels prioritaires sur ceux issus de facture ; le tarif applicable est celui de la date du relevé final et le coût reste informatif.

HORS PÉRIMÈTRE : aucune consommation avancée, contrat/quota photocopieur, moteur comptable ou C4.

D-014 — Compléments C3

Les périodicités DAY/WEEK/MONTH réutilisent le même agrégateur. Les métriques contextuelles répartissent proportionnellement la quantité selon le nombre de jours du contexte et sont présentées comme analytiques, jamais comme une mesure physique directe. La médiane et le seuil décart configurable (défaut 30 %) sont réservés à lanomalie statistique ; lestimation dun sous-compteur utilise la moyenne journalière des derniers intervalles fiables. Leau en m³ ne déclenche jamais la conversion gaz : seul meter_type == "gaz" peut utiliser un coefficient.

Les preuves dalerte sont uniques par alerte et intervalle. Les conclusions CONFIRMED_LEAK et READING_ERROR excluent leurs preuves de la baseline ; BAD_THRESHOLD et NORMAL_EXPLAINED ne les excluent pas automatiquement. Les régimes chauffage restent liés au compteur avec network_name : cette solution permet plusieurs réseaux sans duplication obligatoire dune nouvelle entité, et reste remplaçable par une alimentation GTB future.

D-016 — Type de règle statistique C3

MeterAlertRule.rule_type distingue explicitement MANUAL_THRESHOLD et STATISTICAL_ANOMALY. Les règles historiques et les nouvelles règles sans type explicite restent manuelles par défaut. Le service de relevé appelle l'évaluation manuelle ou la médiane statistique selon ce type, en réutilisant min_comparable_intervals et min_deviation_percent. Une correction réévalue uniquement les intervalles voisins et conserve l'idempotence des preuves. La colonne est ajoutée par la migration additive r3f4g5h6i7j8, sans modifier les migrations C3 déjà appliquées.

D-015 — Clôture C3 : recalcul ciblé et exposition opérationnelle

Les nouveaux relevés déclenchent un recalcul limité aux intervalles voisins ; une correction ne réévalue que les segments précédent et suivant. Les preuves ouvertes dépendant d'une valeur corrigée sont neutralisées et l'alerte est clôturée si elle ne possède plus de preuve valide ; les alertes déjà clôturées restent historiques. Le recalcul est appelé dans la transaction du service de relevé, sans worker externe ni rescannage complet à chaque GET.

La route de clôture d'alerte réutilise le RBAC planning.manage. « Ma journée » ne montre que les alertes ouvertes liées à un compteur attribué au compte ou à une occurrence qui lui est attribuée ; l'administrateur conserve la vision globale. Le dashboard utilise une fenêtre explicite de 30 jours et agrège les unités séparément. Les mesures C3-CLOSE instrumentées sur le jeu de performance donnent notamment 83 requêtes dashboard, 24 fiche analytique, 4 évaluation d'alerte et 86 Ma journée ; les temps restent dépendants de l'environnement MariaDB.

D-009 — Rattachement explicite des compteurs

CONTEXTE : les compteurs historiques étaient obligatoirement rattachés à Equipment, alors que l'exploitation nécessite aussi des compteurs de bâtiment, zone, local et logement.

OPTIONS ÉTUDIÉES : cible polymorphe scope_type/scope_id ; FK nullable distinctes ; création d'un faux équipement pour chaque compteur patrimoine.

CHOIX : conserver equipment_id nullable et ajouter building_id, zone_id, room_id et housing_unit_id, avec une contrainte SQL imposant exactement un rattachement principal. Room est supporté directement.

JUSTIFICATION : les FK garantissent l'intégrité référentielle MariaDB, rendent les requêtes explicites et évitent le faux équipement. Le polymorphisme aurait supprimé les FK natives et complexifié les jointures.

CONSÉQUENCES : l'index historique des compteurs Equipment reste valide ; les listes ne font plus de join(Equipment) obligatoire ; migration non destructive n9c0d1e2f3g4.

D-010 — Hiérarchie, relevés audités et remplacement

CHOIX : Meter.parent_id porte une hiérarchie indépendante d'Equipment.parent_id, avec plusieurs racines possibles et validation des cycles. MeterReading reste une mesure physique dans l'unité native. record_meter_reading est le service unique ; une baisse exige un reset explicite et justifié. Une correction met à jour la mesure tout en conservant ancienne valeur, nouvelle valeur, auteur, date et justification dans MeterReadingCorrection. Un remplacement crée une nouvelle identité reliée par replaces_meter_id et clôt l'ancien compteur.

PHOTOS : une photo facultative est stockée sur le relevé (photo_filename, photo_path) en réutilisant les extensions et la racine d'upload existantes ; aucune association artificielle à EquipmentDocument.

HORS C1 : échéances, consommations, coefficients gaz, alertes, coûts, contrats et quotas restent respectivement C2, C3 et C4.

DATE/COMMIT : 2026-08-23 / checkpoint C1.

D-003 — Catégorie et lot facultatifs

CONTEXTE : un inventaire peut être saisi avant que le référentiel de maintenance soit complet.

OPTIONS ÉTUDIÉES : bloquer toute création sans lot ; accepter un équipement incomplet et le compléter plus tard.

CHOIX : la création reste valide sans lot (et sans catégorie dans les parcours wizard) ; un lot fourni est validé et peut déclencher la génération préventive.

JUSTIFICATION : respecter le principe de configuration progressive sans fabriquer de maintenance implicite.

CONSÉQUENCES : l'interface doit pouvoir signaler ultérieurement « Lot non renseigné » ; les relations techniques restent indépendantes.

DATE/COMMIT : 2026-08-23 / checkpoint A.

D-004 — Versionnement du checkpoint A

CONTEXTE : le chantier interdit 0.1.0-dev.13 et demande des versions correspondant à des checkpoints réellement validés.

CHOIX : le checkpoint A validé passe de 0.1.0-dev.12 à 0.2.0-dev.1 ; B, C, D et E restent séparés.

JUSTIFICATION : la création Equipment est consolidée et testée sans nouvelle régression ; les erreurs restantes sont exactement la baseline historique.

CONSÉQUENCES : Docker doit être reconstruit avec la nouvelle version ; aucune migration n'est ajoutée au checkpoint A.

DATE/COMMIT : 2026-08-23 / version commit.

D-002 — Durée d'intervention inconnue

CONTEXTE : Intervention.effective_estimated_duration retournait silencieusement 60 minutes.

CHOIX : retourner 0 si aucune estimation ou moyenne historique n'existe ; DayPlanner affiche alors une tâche sans durée à renseigner.

JUSTIFICATION : ne pas présenter une estimation arbitraire comme une contrainte métier.

CONSÉQUENCES : les statistiques historiques restent inchangées ; les workflows doivent renseigner les interventions planifiables.

DATE/COMMIT : 2026-08-23 / checkpoint A en cours.