diff --git a/eme-backend/prisma/fixtures.ts b/eme-backend/prisma/fixtures.ts index 6b9c55a..abb98f9 100644 --- a/eme-backend/prisma/fixtures.ts +++ b/eme-backend/prisma/fixtures.ts @@ -51,14 +51,175 @@ function daysFromNow(n: number): Date { return d; } -async function main(): Promise { - // Idempotence : on ne recharge pas si déjà présent. - const witness = await prisma.utilisateur.findUnique({ where: { email: WITNESS_EMAIL } }); - if (witness) { - console.log('[fixtures] deja chargees — rien a faire'); - return; +interface DemoCategories { + ordinateur: number; + video: number; + tablette: number; + camera: number; + audio: number; + cable: number; +} + +interface DemoMateriel { + nom: string; + marque: string; + modele: string; + reference: string; + numeroInventaire: string; + numeroSerie: string; + etatGeneral: string; + categorieId: number; + accessoires?: string[]; +} + +async function assurerAccessoire(nom: string, description: string): Promise { + const existant = await prisma.accessoire.findFirst({ where: { nom } }); + if (existant) { + return existant.id; } + const accessoire = await prisma.accessoire.create({ data: { nom, description } }); + return accessoire.id; +} + +async function assurerMaterielDemo(data: DemoMateriel, campusId: number): Promise { + let materiel = await prisma.materiel.findFirst({ where: { reference: data.reference } }); + + if (!materiel) { + materiel = await prisma.materiel.create({ + data: { + nom: data.nom, + marque: data.marque, + modele: data.modele, + reference: data.reference, + numeroInventaire: data.numeroInventaire, + numeroSerie: data.numeroSerie, + statut: 'DISPONIBLE', + etatGeneral: data.etatGeneral, + categorieId: data.categorieId, + campusId, + }, + }); + } + + for (const nomAccessoire of data.accessoires ?? []) { + const accessoireId = await assurerAccessoire(nomAccessoire, `Accessoire ${nomAccessoire}.`); + const liaison = await prisma.materielAccessoire.findFirst({ + where: { materielId: materiel.id, accessoireId }, + }); + + if (!liaison) { + await prisma.materielAccessoire.create({ + data: { + materielId: materiel.id, + accessoireId, + quantiteAttendue: 1, + obligatoire: true, + }, + }); + } + } +} + +async function assurerCatalogueDemo(campusId: number, categories: DemoCategories): Promise { + const materiels: DemoMateriel[] = [ + { + nom: 'PC Lenovo ThinkPad E14', + marque: 'Lenovo', + modele: 'ThinkPad E14', + reference: 'REF-PC-003', + numeroInventaire: 'INV-PC-003', + numeroSerie: 'SN-LENOVO-003', + etatGeneral: 'Bon etat', + categorieId: categories.ordinateur, + accessoires: ['Chargeur', 'Souris', 'Housse'], + }, + { + nom: 'MacBook Air M2', + marque: 'Apple', + modele: 'MacBook Air 13', + reference: 'REF-PC-004', + numeroInventaire: 'INV-PC-004', + numeroSerie: 'SN-MBA-004', + etatGeneral: 'Tres bon etat', + categorieId: categories.ordinateur, + accessoires: ['Chargeur USB-C', 'Housse'], + }, + { + nom: 'Microsoft Surface Pro', + marque: 'Microsoft', + modele: 'Surface Pro 9', + reference: 'REF-TAB-002', + numeroInventaire: 'INV-TAB-002', + numeroSerie: 'SN-SURFACE-002', + etatGeneral: 'Bon etat', + categorieId: categories.tablette, + accessoires: ['Clavier Surface', 'Stylet'], + }, + { + nom: 'Vidéoprojecteur Epson EB-X49', + marque: 'Epson', + modele: 'EB-X49', + reference: 'REF-VP-002', + numeroInventaire: 'INV-VP-002', + numeroSerie: 'SN-EPSON-002', + etatGeneral: 'Bon etat', + categorieId: categories.video, + accessoires: ['Telecommande', 'Cable HDMI'], + }, + { + nom: 'Caméra Canon EOS M50', + marque: 'Canon', + modele: 'EOS M50', + reference: 'REF-CAM-002', + numeroInventaire: 'INV-CAM-002', + numeroSerie: 'SN-CANON-002', + etatGeneral: 'Bon etat', + categorieId: categories.camera, + accessoires: ['Batterie', 'Carte SD', 'Housse'], + }, + { + nom: 'Casque Jabra Evolve', + marque: 'Jabra', + modele: 'Evolve 40', + reference: 'REF-AUD-002', + numeroInventaire: 'INV-AUD-002', + numeroSerie: 'SN-JABRA-002', + etatGeneral: 'Bon etat', + categorieId: categories.audio, + }, + { + nom: 'Kit câbles HDMI / USB-C', + marque: 'ENSUP', + modele: 'Kit connectique', + reference: 'REF-CAB-001', + numeroInventaire: 'INV-CAB-001', + numeroSerie: 'SN-CABLE-001', + etatGeneral: 'Bon etat', + categorieId: categories.cable, + accessoires: ['Cable HDMI', 'Adaptateur USB-C'], + }, + { + nom: 'Enceinte Bluetooth JBL', + marque: 'JBL', + modele: 'Flip 6', + reference: 'REF-AUD-003', + numeroInventaire: 'INV-AUD-003', + numeroSerie: 'SN-JBL-003', + etatGeneral: 'Bon etat', + categorieId: categories.audio, + accessoires: ['Cable USB-C'], + }, + ]; + + for (const materiel of materiels) { + await assurerMaterielDemo(materiel, campusId); + } + + console.log(`[fixtures] ${materiels.length} materiels de catalogue assures`); +} + +async function main(): Promise { // Références (doivent exister via le seed). const roleEtudiant = required( await prisma.role.findUnique({ where: { code: 'ETUDIANT' } }), @@ -100,6 +261,28 @@ async function main(): Promise { await prisma.categorieMateriel.findFirst({ where: { nom: 'Périphérique audio' } }), 'Categorie Périphérique audio manquante', ); + const catCable = required( + await prisma.categorieMateriel.findFirst({ where: { nom: 'Câble / Adaptateur' } }), + 'Categorie Câble / Adaptateur manquante', + ); + + const demoCategories: DemoCategories = { + ordinateur: catOrdi.id, + video: catVideo.id, + tablette: catTablette.id, + camera: catCamera.id, + audio: catAudio.id, + cable: catCable.id, + }; + + // Idempotence : si les fixtures de base existent déjà, on assure seulement + // les matériels de catalogue ajoutés après coup. + const witness = await prisma.utilisateur.findUnique({ where: { email: WITNESS_EMAIL } }); + if (witness) { + await assurerCatalogueDemo(campus.id, demoCategories); + console.log('[fixtures] deja chargees — catalogue demo assure'); + return; + } // Utilisateurs : 3 étudiants (2 info @ensitech.eu, 1 autre @ensup.eu) + 1 responsable. const marie = await prisma.utilisateur.create({ @@ -280,6 +463,7 @@ async function main(): Promise { { materielId: camera.id, accessoireId: chargeur.id, quantiteAttendue: 1, obligatoire: true }, ], }); + await assurerCatalogueDemo(campus.id, demoCategories); // Emprunt 1 — EN_COURS (Marie / PC Dell) + checklist de départ. const empruntEnCours = await prisma.emprunt.create({ diff --git a/review.md b/review.md index e16e1e7..22d718e 100644 --- a/review.md +++ b/review.md @@ -229,6 +229,13 @@ En terminal interactif (VS Code), `migrate dev` fonctionne normalement (taper `y - Confirmation alimentée par la réponse API (`id`, `dateEmprunt`, matériel renvoyé). - Testé sans mutation de données : `POST /api/emprunts` avec checklist vide renvoie bien 400 `La checklist de depart est obligatoire (RG11)`. +### Étape 29 — Données de démonstration catalogue +- Branche dédiée `feat/demo-catalogue-fixtures` créée après merge de `feat/frontend-emprunt-api` dans `develop`. +- `prisma/fixtures.ts` enrichi avec un catalogue de démonstration idempotent : si les fixtures de base existent déjà, le script assure seulement les matériels de catalogue manquants. +- 8 matériels disponibles ajoutés : Lenovo ThinkPad, MacBook Air, Surface Pro, vidéoprojecteur Epson, caméra Canon, casque Jabra, kit câbles HDMI/USB-C, enceinte JBL. +- Accessoires associés assurés sans doublon logique par recherche de nom/référence avant création. +- Vérifié en réel : `npm run fixtures` OK ; `GET /api/materiels` pour Lucas renvoie 10 matériels disponibles. + --- -*Dernière mise à jour : 2026-07-07 — Catalogue, détail matériel et création d'emprunt frontend branchés sur l'API backend.* +*Dernière mise à jour : 2026-07-07 — Catalogue enrichi, détail matériel et création d'emprunt frontend branchés sur l'API backend.*