Files
EME_APP/review.md
T

18 KiB

review.md — Journal des décisions EME

Ce fichier trace chaque étape réalisée et chaque choix technique, avec sa justification. Il est mis à jour au fil de l'avancement. Lecture du plus ancien (haut) au plus récent (bas).

Règles métier -> docs/regles-metier/regles-gestion.md · Contexte -> CONTEXT.md · Plan -> TODO.md


Conventions et choix transverses

Ces décisions s'appliquent à tout le projet, sauf mention contraire.

Décision Pourquoi
Statuts métier en String côté Prisma (pas d'enum natif) SQL Server ne supporte pas les enums Prisma. Les valeurs sont validées côté TypeScript via src/models/enums.ts.
Timestamps automatiques : createdAt @default(now()), updatedAt @updatedAt Évite d'avoir à fournir ces valeurs manuellement ; updatedAt est géré par Prisma au runtime.
actif Boolean @default(true) Une entité créée est active par défaut.
@unique sur les identifiants fonctionnels Intégrité : un identifiant en double casse l'authentification / l'unicité métier.
Champs nullable selon les cardinalités 0..1 du diagramme de classes Un champ non connu à la création (ex. date de retour) ou optionnel doit être NULL, sinon le workflow métier est bloqué.
onDelete: NoAction, onUpdate: NoAction sur les données de traçabilité (Emprunt, Checklist, ChecklistElement, Anomalie, Historique) Ces données ne doivent jamais être supprimées/altérées automatiquement en cascade. Toute suppression sera explicite côté service. Évite aussi l'erreur SQL Server "multiple cascade paths".
ERD tenu à jour en miroir du schéma (docs/conceptions/uml/ERD.md) La source de vérité doit refléter le code réel (nullable, UK, etc.).
Workflow Git : commits sur develop uniquement master est figé ; develop reste toujours à jour de master (aucune divergence puisque master ne bouge plus).
Process avant chaque migration : prisma format -> prisma validate -> npm run build -> migration Détecter toute erreur de schéma/typage avant de toucher la base.
DTO nommés XxxResponse (sortie) et XxxRequest (entrée) Le sens est explicite dans le nom. Sortie = mapping (toXxxResponse), entrée = validation.

Note outillage — migrations en environnement non-interactif

prisma migrate dev se bloque quand il doit demander une confirmation (ex. ajout de contrainte @unique sur table existante) car le terminal est non-interactif. Contournement utilisé : générer le SQL avec prisma migrate diff --from-config-datasource --to-schema ... --script, écrire le fichier de migration, puis appliquer avec prisma migrate deploy. Résultat strictement identique à migrate dev, sans perte de données. En terminal interactif (VS Code), migrate dev fonctionne normalement (taper y).


Journal chronologique

Étape 0 — Audit initial

  • Constat : Block 0 (bootstrap) terminé mais non coché ; schéma à 4/16 entités ; aucune migration ; aucun commit Git.
  • Décision : compléter le schéma par petits blocs (et non d'un coup) pour valider/committer au fur et à mesure.

Étape 1 — Configuration des migrations Prisma

  • Correction DATABASE_URL (mot de passe désaligné avec DB_PASSWORD).
  • Découverte : Prisma 7 interdit url dans schema.prisma -> la connexion vit dans prisma.config.ts. Ligne url retirée du datasource.

Étape 2 — Modèles de référence (Campus, Role, Utilisateur, CategorieMateriel)

  • Defaults + timestamps auto ajoutés sur Campus, Utilisateur, CategorieMateriel (Role n'a pas de timestamps -> non modifié).
  • @unique sur Role.code, Utilisateur.microsoftId, Utilisateur.email.
  • Utilisateur.classe -> nullable : un RESPONSABLE n'a pas de classe (seuls les étudiants en ont).
  • Ajout dans l'ERD des annotations UK (sur code, microsoft_id, email) et nullable (sur classe).
  • Choix laissé de côté : tailles @db.NVarChar(n) (colonnes restées en NVARCHAR(1000) par défaut). À reconsidérer plus tard.

Étape 3 — Bloc A : Infrastructure (SallePret, PosteEmprunt) — commit d026b75

  • SallePret rattachée à Campus (FK campus_id) ; PosteEmprunt rattaché à SallePret (FK salle_pret_id).

Étape 4 — Bloc B : CarteEtudiante — commit dfa73cd

  • Rattachée à Utilisateur.
  • Dette notée : qr_code devrait être @unique (sert à l'identification, RG02) — non posé pour rester fidèle à l'ERD.

Étape 5 — Bloc C : Matériel (Materiel, Accessoire, MaterielAccessoire) — commit 0774ee3

  • Table de liaison MaterielAccessoire entre Materiel et Accessoire, sans colonnes created_at/updated_at.
  • Dette notée : @@unique([materielId, accessoireId]) et Materiel.statut @default("DISPONIBLE").

Étape 6 — Bloc D : Transactions (Emprunt, Checklist, ChecklistElement) — commit 6908a2b

  • Champs de retour/optionnels nullable : dateRetourReelle, modeIdentificationRetour, commentaireDepart, commentaireRetour, Checklist.commentaire, ChecklistElement.commentaire.
    • Pourquoi : RG12 crée l'emprunt au départ ; les infos de retour n'existent pas encore. En NOT NULL, la création serait impossible.
  • ChecklistElement.accessoireId rendu nullable : un élément de checklist peut être un libellé libre, sans accessoire catalogué associé (cardinalité 0..1).
  • Les 9 FK en NoAction : traçabilité + évitement des cascade paths.
  • Annotation nullable ajoutée dans l'ERD sur les champs de retour/commentaires d'Emprunt et accessoire_id/commentaire de checklist.

Étape 7 — Stratégie Git : branche develop

  • Création de develop depuis master. Désormais tous les commits vont sur develop ; master figé au Bloc C.

Étape 8 — Bloc E : Suivi (Anomalie, Notification, Historique) — commit 0fc9f9e

  • Schéma 16/16 complet.
  • Nullable selon 0..1 : Anomalie.traiteeParId (null tant que non pris en charge), observation, dateResolution ; Notification.anomalieId ; les 5 FK optionnelles d'Historique (un événement n'a pas toujours d'emprunt/matériel/etc.).
  • Double relation Anomalie vers Utilisateur nommée : @relation("AnomalieEtudiant") (étudiant concerné) et @relation("AnomalieResponsable") (responsable qui traite).
  • Defaults : Anomalie.detecteeAutomatiquement @default(true), Notification.lu @default(false), Notification.dateCreation @default(now()).
  • Les 12 FK en NoAction (traçabilité).
  • Notification et Historique créées sans colonne updatedAt (seulement date_creation/created_at).
  • Annotation nullable ajoutée dans l'ERD sur traitee_par_id, observation, date_resolution, anomalie_id et les 5 FK optionnelles d'Historique.

Étape 9 — Seeds de référence — commit 2c3a5db

  • Fichier prisma/seed.ts autonome (adapter MSSQL, lecture directe des variables DB), lancé via npm run seed (script ajouté au package.json).
  • Choix npm run seed (ts-node) plutôt que prisma db seed : l'API de configuration du seed en Prisma 7 (prisma.config.ts) n'étant pas confirmée, un script direct est plus fiable.
  • Données : 2 rôles (ETUDIANT, RESPONSABLE), 1 campus (Campus Saint-Christophe, Cergy, 95800, adresse provisoire), 1 salle (Salle de prêt principale), 1 poste (POSTE-01), 6 catégories (Ordinateur portable, Vidéoprojecteur, Tablette, Caméra, Périphérique audio, Câble / Adaptateur).
  • Idempotence : upsert sur Role.code ; findFirst avant create pour les entités sans clé unique. Relance vérifiée (aucun doublon).
  • Incident : le client Prisma était obsolète (ne connaissait que les 4 modèles de référence). Régénéré avec npx prisma generate avant le seed. Leçon : après des migrations, régénérer/vérifier le client avant d'exécuter du code qui l'utilise.

Étape 10 — Fixtures de test

  • Fichier prisma/fixtures.ts + script npm run fixtures (séparé du seed : données de dev/test, non installées en production).
  • Non destructif : détection via un email témoin (marie.dupont@ensitech.eu) ; si présent, le script s'arrête (cohérent avec le principe "pas de suppression automatique").
  • Contenu : 4 utilisateurs (3 étudiants + 1 responsable), 3 cartes, 6 matériels (statuts variés), 3 accessoires + liaisons, 4 emprunts couvrant EN_COURS / EN_RETARD / CLOTURE / RETOUR_NON_CONFORME, 6 checklists, 1 anomalie + 1 notification, 2 entrées d'historique.
  • Idempotence vérifiée (2e exécution = skip).

Contexte métier clarifié — structure ENSUP (impacte la modélisation)

  • ENSUP est un groupe multi-campus (Saint-Christophe/Cergy, Saint-Quentin, Nantes, Marseille...).
  • Chaque campus héberge deux écoles : ENSUP (filières généralistes) et Ensitech (informatique). Bâtiments et salles partagés.
  • La distinction d'école se fait par le domaine email : ensitech.eu (info) et ensup.eu (reste). La validation de domaine (RG, à venir) doit accepter les deux.
  • Le modèle n'a pas d'entité "École" : seule Campus existe. Un campus = un lieu physique partagé par les deux écoles (et non une école).
  • MVP : on démarre avec le seul campus Saint-Christophe (Cergy) ; les autres campus viendront ensuite.

Étape 11 — Block 1 : middlewares backend

  • Structure en couches : utils/logger.ts, errors/app-error.ts, middlewares/ (request-logger, not-found, error-handler).
  • Gestionnaire d'erreurs global : une AppError renvoie son statut + message ; toute autre erreur devient une 500 (stack exposée uniquement en development) et est tracée.
  • Logger de requêtes (méthode, URL, statut, durée) + logger applicatif horodaté.
  • CORS restreint à env.frontendUrl (était ouvert à toutes les origines). SIGTERM géré en plus de SIGINT (arrêt propre du container Docker).
  • Express 5 propage nativement les erreurs des handlers async : pas de wrapper asyncHandler nécessaire.
  • Vérifié au runtime : /health 200, route inconnue 404 en JSON, requêtes journalisées.

Étape 12 — Block 1 : ESLint + Prettier

  • ESLint 10 (flat config eslint.config.mjs) + typescript-eslint 8 ; Prettier 3 (.prettierrc.json).
  • Règles de qualité strictes encodées : no-explicit-any, no-non-null-assertion, explicit-module-boundary-types, no-unused-vars (ignore le préfixe _), prefer-const. eslint-config-prettier désactive les règles en conflit avec Prettier.
  • Prettier : quotes simples, point-virgule, 100 colonnes, 2 espaces, trailing commas.
  • Scripts : lint, lint:fix, format, format:check.
  • État : lint vert sur tout le code existant ; formatage appliqué (3 fichiers) ; build OK.

Étape 13 — Correctif tooling : type-check des scripts Prisma

  • Problème : prisma/seed.ts et fixtures.ts étaient hors du include du tsconfig ; l'IDE ne chargeait pas les types Node (process non reconnu, 2 erreurs).
  • Correctif : tsconfig.json élargi à src + prisma en noEmit (IDE et tsc --noEmit) ; nouveau tsconfig.build.json dédié à la compilation src -> dist (npm run build).
  • Bénéfice : les scripts Prisma sont désormais type-checkés (angle mort comblé).

Étape 14 — Block 4 : architecture en couches + 1er endpoint (catalogue matériel)

  • Mise en place de l'architecture en couches : routes/ -> controllers/ -> services/ -> repositories/ (un dossier par couche).
  • Auth simulée temporaire : middleware current-user qui résout l'utilisateur via l'en-tête x-user-email et pose req.user (typé via types/authenticated-user.ts + augmentation Express.Request). À remplacer par la validation JWT au Block 3.
  • Endpoint GET /api/materiels (catalogue) : applique RG10 (matériels DISPONIBLE du campus de l'étudiant), filtres categorieId et q (recherche nom/marque/modèle/référence).
  • Format de réponse standardisé : { "data": ... } (cohérent avec { "error": ... }).
  • Vérifié au runtime : 401 sans en-tête / utilisateur inconnu, 200 avec les disponibles du campus, filtres OK, 400 sur categorieId invalide.

Étape 15 — Block 4 : endpoint "mes emprunts en cours"

  • Endpoint GET /api/mes-emprunts : retourne les emprunts non restitués (EN_COURS, EN_RETARD) de l'utilisateur courant, triés par date de retour prévue, avec le matériel et sa catégorie inclus.
  • RG15 : filtrage par utilisateurId (chacun ne voit que ses propres emprunts).
  • Réutilise l'architecture en couches et l'auth simulée déjà en place.
  • Vérifié au runtime : Marie 1 (EN_COURS, son CLOTURE exclu), Lucas 1 (EN_RETARD), Sofia 0 (son RETOUR_NON_CONFORME exclu), 401 sans en-tête.

Étape 16 — Block 4 : profil et identification

  • GET /api/auth/me (protégé) : renvoie le profil complet de l'utilisateur courant (role + campus).
  • GET /api/auth/carte/:qr (public) : identification par QR code (RG02/RG03) ; contrôles carte active, non expirée et compte actif ; renvoie le profil.
  • Refactor du routage : currentUser appliqué par sous-routeur (au lieu d'un middleware global) pour laisser la route d'identification publique.
  • Vérifié au runtime : /me 200 (profil Marie) et 401 sans en-tête ; /carte QR valide 200 sans en-tête (route publique), QR inconnu 404.

Étape 17 — DTO de sortie sur les endpoints existants

  • Dossier src/dtos/ : par ressource, une interface XxxResponse + un mapper pur toXxxResponse (utilisateur, matériel, emprunt).
  • Mapping effectué dans le controller (couche présentation) ; les services continuent de renvoyer les entités (réutilisables).
  • Champs internes désormais masqués : microsoftId, timestamps, FK brutes, numeroInventaire/numeroSerie.
  • Vérifié au runtime sur /me, /materiels, /mes-emprunts.

Étape 18 — Durcissement du .gitignore (sécurité)

  • Trou comblé : **/.env ne couvrait pas les variantes (.env.local, .env.production...). Ajout de **/.env.* avec exception !**/.env.example.
  • Préventif : clés/certificats (*.pem, *.key, *.crt, *.cert, *.pfx, *.p12) pour Azure AD/TLS à venir ; dumps de base (*.bak, *.dump) ; **/coverage/ ; *.tsbuildinfo.
  • Vérifié : aucun secret n'était déjà suivi ; les vrais .env restent ignorés ; .env.example (placeholders) reste versionné.

Étape 19 — Block 4 : création d'emprunt (POST /api/emprunts)

  • Écriture transactionnelle : réservation atomique du matériel (updateMany where statut=DISPONIBLE), création de l'emprunt (EN_COURS), de la checklist de départ et de ses éléments. Rollback global si une étape échoue.
  • Règles : RG07 (matériel disponible), RG10 (matériel du campus de l'étudiant), RG11 (checklist obligatoire non vide), RG12 (matériel -> EMPRUNTE, horodatage).
  • Validation d'entrée manuelle (CreerEmpruntRequest, parseCreerEmpruntRequest) : aucune dépendance ajoutée. Durée d'emprunt par défaut : +14 jours.
  • Contrôle du poste (existe + même campus, RG05). Routage : POST /api/emprunts et GET /api/mes-emprunts sur des routeurs séparés.
  • Vérifié au runtime : 401 sans auth, 400 checklist vide, 404 matériel inconnu, 201 création (matériel -> EMPRUNTE, +14j), 409 doublon (réservation atomique).

Étape 20 — Block 4 : restitution + anomalie automatique (POST /api/emprunts/:id/restitution)

  • Comparaison automatique départ/retour (RG17) : un élément présent au départ mais absent/détérioré au retour rend la restitution non conforme (RG20). Appariement par accessoireId sinon par nomElement.
  • Restitution atomique : checklist de retour + mise à jour emprunt/matériel + (si non conforme) anomalie DETECTEE + notifications.
  • RG15 (propriétaire), statut restituable, RG18 (conforme -> CLOTURE + matériel DISPONIBLE), RG19 (non conforme -> RETOUR_NON_CONFORME + matériel DETERIORE/NON_CONFORME).
  • RG24 : notification à tous les responsables (RESPONSABLE) du campus de l'emprunt.
  • Vérifié au runtime : 404/403/409 ; restitution conforme (CLOTURE + DISPONIBLE) ; non conforme (RETOUR_NON_CONFORME + NON_CONFORME + anomalie + notification à Karim).
  • Block 4 (API Étudiant) terminé : 6/6 endpoints.

Étape 21 — Frontend : setup Flutter Web + écran d'accueil ENSUP

  • Projet Flutter Web créé (eme-frontend/), google_fonts ajouté (Darker Grotesque pour les titres, Titillium Web pour le corps).
  • Thème ENSUP (theme/ensup_colors.dart, theme/ensup_theme.dart) : couleurs de la charte graphique.
  • Composants réutilisables : EnsupTopBar, ProfileCard, ActionCard.
  • Écran d'accueil (screens/home_screen.dart) fidèle à la maquette : topbar bleu foncé, carte profil, deux cartes Emprunter/Restituer. Données statiques (pas encore branché à l'API).
  • Convention CLAUDE.md respectée : guillemets doubles en Dart (règle lint prefer_single_quotes désactivée en conséquence).
  • flutter analyze : aucun problème. App lancée dans Chrome (http://localhost:5000).

Étape 22 — Suppression du PDF de charte graphique

  • PDF de charte ENSUP retiré du dépôt (redondant : la maquette validée applique la charte, et les couleurs/typos sont dans CONTEXT.md §7).
  • Références mises à jour dans CLAUDE.md et CONTEXT.md pour pointer vers la maquette.
  • Le PDF reste récupérable dans l'historique Git si besoin (logo officiel source).

Étape 23 — Frontend : écran d'identification (écran de démarrage)

  • Correction : l'écran de démarrage est l'identification (s-login de la maquette), pas l'accueil étudiant. Logo, "Bienvenue sur EME", deux options (scanner carte / compte ENSUP Microsoft 365), bouton accès responsable.
  • EnsupTopBar rendue réutilisable (utilisateur optionnel, marque paramétrable) ; nouveau widget IdentificationOption ; coins ENSUP aussi sur cet écran.
  • Navigation identification -> accueil étudiant (HomeScreen).

Dette technique en attente

À traiter ensemble plus tard (avec mise à jour ERD en miroir) :

  1. CarteEtudiante.qr_code -> @unique (identification, RG02)
  2. MaterielAccessoire -> @@unique([materielId, accessoireId]) (un accessoire une seule fois par matériel)
  3. Materiel.statut -> @default("DISPONIBLE") (un matériel neuf est disponible, RG07)
  4. Tailles de colonnes @db.NVarChar(n) (actuellement NVARCHAR(1000) partout)
  5. Swagger/OpenAPI (Block 1, reporté après les endpoints)
  6. Auth réelle Azure AD (Block 3) — remplacera l'auth simulée par en-tête

Dernière mise à jour : 2026-07-06 — Frontend : écran d'identification (démarrage) + accueil étudiant.