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é avecDB_PASSWORD). - Découverte : Prisma 7 interdit
urldansschema.prisma-> la connexion vit dansprisma.config.ts. Ligneurlretiré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é).
@uniquesurRole.code,Utilisateur.microsoftId,Utilisateur.email.Utilisateur.classe-> nullable : unRESPONSABLEn'a pas de classe (seuls les étudiants en ont).- Ajout dans l'ERD des annotations
UK(surcode,microsoft_id,email) etnullable(surclasse). - Choix laissé de côté : tailles
@db.NVarChar(n)(colonnes restées enNVARCHAR(1000)par défaut). À reconsidérer plus tard.
Étape 3 — Bloc A : Infrastructure (SallePret, PosteEmprunt) — commit d026b75
SallePretrattachée àCampus(FKcampus_id) ;PosteEmpruntrattaché àSallePret(FKsalle_pret_id).
Étape 4 — Bloc B : CarteEtudiante — commit dfa73cd
- Rattachée à
Utilisateur. - Dette notée :
qr_codedevrait ê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
MaterielAccessoireentreMaterieletAccessoire, sans colonnescreated_at/updated_at. - Dette notée :
@@unique([materielId, accessoireId])etMateriel.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.
- Pourquoi : RG12 crée l'emprunt au départ ; les infos de retour n'existent pas encore. En
ChecklistElement.accessoireIdrendu 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
nullableajoutée dans l'ERD sur les champs de retour/commentaires d'Empruntetaccessoire_id/commentairede checklist.
Étape 7 — Stratégie Git : branche develop
- Création de
developdepuismaster. Désormais tous les commits vont surdevelop;masterfigé 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
AnomalieversUtilisateurnommé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é). NotificationetHistoriquecréées sans colonneupdatedAt(seulementdate_creation/created_at).- Annotation
nullableajoutée dans l'ERD surtraitee_par_id,observation,date_resolution,anomalie_idet les 5 FK optionnelles d'Historique.
Étape 9 — Seeds de référence — commit 2c3a5db
- Fichier
prisma/seed.tsautonome (adapter MSSQL, lecture directe des variables DB), lancé vianpm run seed(script ajouté aupackage.json). - Choix
npm run seed(ts-node) plutôt queprisma 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 :
upsertsurRole.code;findFirstavantcreatepour 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 generateavant 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+ scriptnpm 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) etensup.eu(reste). La validation de domaine (RG, à venir) doit accepter les deux. - Le modèle n'a pas d'entité "École" : seule
Campusexiste. 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
AppErrorrenvoie son statut + message ; toute autre erreur devient une 500 (stack exposée uniquement endevelopment) 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).SIGTERMgéré en plus deSIGINT(arrêt propre du container Docker). - Express 5 propage nativement les erreurs des handlers async : pas de wrapper
asyncHandlernécessaire. - Vérifié au runtime :
/health200, 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-prettierdé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.tsetfixtures.tsétaient hors duincludedu tsconfig ; l'IDE ne chargeait pas les types Node (processnon reconnu, 2 erreurs). - Correctif :
tsconfig.jsonélargi àsrc+prismaennoEmit(IDE ettsc --noEmit) ; nouveautsconfig.build.jsondédié à la compilationsrc->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-userqui résout l'utilisateur via l'en-têtex-user-emailet posereq.user(typé viatypes/authenticated-user.ts+ augmentationExpress.Request). À remplacer par la validation JWT au Block 3. - Endpoint
GET /api/materiels(catalogue) : applique RG10 (matérielsDISPONIBLEdu campus de l'étudiant), filtrescategorieIdetq(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
categorieIdinvalide.
É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, sonCLOTUREexclu), Lucas 1 (EN_RETARD), Sofia 0 (sonRETOUR_NON_CONFORMEexclu), 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 :
currentUserappliqué par sous-routeur (au lieu d'un middleware global) pour laisser la route d'identification publique. - Vérifié au runtime :
/me200 (profil Marie) et 401 sans en-tête ;/carteQR 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 interfaceXxxResponse+ un mapper purtoXxxResponse(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é :
**/.envne 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
.envrestent 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/empruntsetGET /api/mes-empruntssur 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
accessoireIdsinon parnomElement. - 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érielDISPONIBLE), RG19 (non conforme ->RETOUR_NON_CONFORME+ matérielDETERIORE/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_fontsajouté (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.mdrespectée : guillemets doubles en Dart (règle lintprefer_single_quotesdé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.mdetCONTEXT.mdpour 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-loginde la maquette), pas l'accueil étudiant. Logo, "Bienvenue sur EME", deux options (scanner carte / compte ENSUP Microsoft 365), bouton accès responsable. EnsupTopBarrendue réutilisable (utilisateur optionnel, marque paramétrable) ; nouveau widgetIdentificationOption; 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) :
CarteEtudiante.qr_code->@unique(identification, RG02)MaterielAccessoire->@@unique([materielId, accessoireId])(un accessoire une seule fois par matériel)Materiel.statut->@default("DISPONIBLE")(un matériel neuf est disponible, RG07)- Tailles de colonnes
@db.NVarChar(n)(actuellementNVARCHAR(1000)partout) - Swagger/OpenAPI (Block 1, reporté après les endpoints)
- 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.