Files
EME_APP/eme-backend/prisma/fixtures.ts
T
2026-07-07 13:47:55 +02:00

701 lines
20 KiB
TypeScript

import 'dotenv/config';
import { PrismaClient } from '@prisma/client';
import { PrismaMssql } from '@prisma/adapter-mssql';
// Fixtures de DEV/TEST. À lancer après `npm run seed` (dépend des données de référence).
// Non destructif : si déjà chargées (email témoin présent), le script s'arrête.
function getEnv(name: string): string {
const value = process.env[name];
if (!value) {
throw new Error(`Variable d'environnement manquante: ${name}`);
}
return value;
}
function required<T>(value: T | null, message: string): T {
if (value === null) {
throw new Error(message);
}
return value;
}
const adapter = new PrismaMssql({
server: getEnv('DB_SERVER'),
port: Number(process.env.DB_PORT ?? 1433),
database: getEnv('DB_NAME'),
user: getEnv('DB_USER'),
password: getEnv('DB_PASSWORD'),
options: {
encrypt: true,
trustServerCertificate: true,
},
});
const prisma = new PrismaClient({ adapter });
const WITNESS_EMAIL = 'marie.dupont@ensitech.eu';
const now = new Date();
function daysAgo(n: number): Date {
const d = new Date(now);
d.setDate(d.getDate() - n);
return d;
}
function daysFromNow(n: number): Date {
const d = new Date(now);
d.setDate(d.getDate() + n);
return d;
}
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<number> {
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<void> {
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<void> {
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<void> {
// Références (doivent exister via le seed).
const roleEtudiant = required(
await prisma.role.findUnique({ where: { code: 'ETUDIANT' } }),
"Role ETUDIANT manquant — lance d'abord `npm run seed`",
);
const roleResponsable = required(
await prisma.role.findUnique({ where: { code: 'RESPONSABLE' } }),
"Role RESPONSABLE manquant — lance d'abord `npm run seed`",
);
const campus = required(
await prisma.campus.findFirst({ where: { nom: 'Campus Saint-Christophe' } }),
"Campus manquant — lance d'abord `npm run seed`",
);
const salle = required(
await prisma.sallePret.findFirst({ where: { campusId: campus.id } }),
"Salle de pret manquante — lance d'abord `npm run seed`",
);
const poste = required(
await prisma.posteEmprunt.findFirst({ where: { sallePretId: salle.id } }),
"Poste manquant — lance d'abord `npm run seed`",
);
const catOrdi = required(
await prisma.categorieMateriel.findFirst({ where: { nom: 'Ordinateur portable' } }),
'Categorie Ordinateur portable manquante',
);
const catVideo = required(
await prisma.categorieMateriel.findFirst({ where: { nom: 'Vidéoprojecteur' } }),
'Categorie Vidéoprojecteur manquante',
);
const catTablette = required(
await prisma.categorieMateriel.findFirst({ where: { nom: 'Tablette' } }),
'Categorie Tablette manquante',
);
const catCamera = required(
await prisma.categorieMateriel.findFirst({ where: { nom: 'Caméra' } }),
'Categorie Caméra manquante',
);
const catAudio = required(
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({
data: {
microsoftId: 'ms-marie-dupont',
nom: 'Dupont',
prenom: 'Marie',
email: WITNESS_EMAIL,
classe: 'B3 Informatique',
roleId: roleEtudiant.id,
campusId: campus.id,
},
});
const lucas = await prisma.utilisateur.create({
data: {
microsoftId: 'ms-lucas-martin',
nom: 'Martin',
prenom: 'Lucas',
email: 'lucas.martin@ensitech.eu',
classe: 'B2 Informatique',
roleId: roleEtudiant.id,
campusId: campus.id,
},
});
const sofia = await prisma.utilisateur.create({
data: {
microsoftId: 'ms-sofia-bernard',
nom: 'Bernard',
prenom: 'Sofia',
email: 'sofia.bernard@ensup.eu',
classe: 'B1 Marketing',
roleId: roleEtudiant.id,
campusId: campus.id,
},
});
const responsable = await prisma.utilisateur.create({
data: {
microsoftId: 'ms-karim-benali',
nom: 'Benali',
prenom: 'Karim',
email: 'karim.benali@ensup.eu',
classe: null, // un responsable n'a pas de classe
roleId: roleResponsable.id,
campusId: campus.id,
},
});
console.log('[fixtures] 4 utilisateurs crees (3 etudiants + 1 responsable)');
// Cartes étudiantes (qrCode unique par carte).
await prisma.carteEtudiante.createMany({
data: [
{
utilisateurId: marie.id,
qrCode: 'QR-ETU-0001',
dateActivation: daysAgo(300),
dateExpiration: daysFromNow(400),
},
{
utilisateurId: lucas.id,
qrCode: 'QR-ETU-0002',
dateActivation: daysAgo(300),
dateExpiration: daysFromNow(400),
},
{
utilisateurId: sofia.id,
qrCode: 'QR-ETU-0003',
dateActivation: daysAgo(300),
dateExpiration: daysFromNow(400),
},
],
});
console.log('[fixtures] 3 cartes etudiantes creees');
// Accessoires.
const chargeur = await prisma.accessoire.create({
data: { nom: 'Chargeur', description: 'Chargeur secteur.' },
});
const souris = await prisma.accessoire.create({
data: { nom: 'Souris', description: 'Souris filaire.' },
});
const housse = await prisma.accessoire.create({
data: { nom: 'Housse', description: 'Housse de protection.' },
});
// Matériels (statut explicite — pas de défaut sur ce champ).
const pcDell = await prisma.materiel.create({
data: {
nom: 'PC portable Dell Latitude',
marque: 'Dell',
modele: 'Latitude 5440',
reference: 'REF-PC-001',
numeroInventaire: 'INV-PC-001',
numeroSerie: 'SN-DELL-001',
statut: 'EMPRUNTE',
etatGeneral: 'Bon etat',
categorieId: catOrdi.id,
campusId: campus.id,
},
});
const projecteur = await prisma.materiel.create({
data: {
nom: 'Vidéoprojecteur Epson',
marque: 'Epson',
modele: 'EB-W06',
reference: 'REF-VP-001',
numeroInventaire: 'INV-VP-001',
numeroSerie: 'SN-EPSON-001',
statut: 'EMPRUNTE',
etatGeneral: 'Bon etat',
categorieId: catVideo.id,
campusId: campus.id,
},
});
const ipad = await prisma.materiel.create({
data: {
nom: 'Tablette iPad',
marque: 'Apple',
modele: 'iPad 10',
reference: 'REF-TAB-001',
numeroInventaire: 'INV-TAB-001',
numeroSerie: 'SN-IPAD-001',
statut: 'DISPONIBLE',
etatGeneral: 'Bon etat',
categorieId: catTablette.id,
campusId: campus.id,
},
});
const camera = await prisma.materiel.create({
data: {
nom: 'Caméra Sony',
marque: 'Sony',
modele: 'Alpha 6400',
reference: 'REF-CAM-001',
numeroInventaire: 'INV-CAM-001',
numeroSerie: 'SN-SONY-001',
statut: 'DETERIORE',
etatGeneral: 'Housse manquante au retour',
categorieId: catCamera.id,
campusId: campus.id,
},
});
await prisma.materiel.create({
data: {
nom: 'Micro Rode',
marque: 'Rode',
modele: 'NT-USB',
reference: 'REF-MIC-001',
numeroInventaire: 'INV-MIC-001',
numeroSerie: 'SN-RODE-001',
statut: 'DISPONIBLE',
etatGeneral: 'Bon etat',
categorieId: catAudio.id,
campusId: campus.id,
},
});
await prisma.materiel.create({
data: {
nom: 'PC portable HP',
marque: 'HP',
modele: 'ProBook 450',
reference: 'REF-PC-002',
numeroInventaire: 'INV-PC-002',
numeroSerie: 'SN-HP-002',
statut: 'MAINTENANCE',
etatGeneral: 'En revision',
categorieId: catOrdi.id,
campusId: campus.id,
},
});
console.log('[fixtures] 6 materiels crees');
// Accessoires attendus (obligatoire explicite — pas de défaut).
await prisma.materielAccessoire.createMany({
data: [
{ materielId: pcDell.id, accessoireId: chargeur.id, quantiteAttendue: 1, obligatoire: true },
{ materielId: pcDell.id, accessoireId: souris.id, quantiteAttendue: 1, obligatoire: false },
{ materielId: camera.id, accessoireId: housse.id, quantiteAttendue: 1, obligatoire: true },
{ 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({
data: {
utilisateurId: marie.id,
materielId: pcDell.id,
campusId: campus.id,
sallePretId: salle.id,
posteEmpruntId: poste.id,
dateEmprunt: daysAgo(2),
dateRetourPrevue: daysFromNow(5),
statut: 'EN_COURS',
modeIdentificationEmprunt: 'QR_CODE',
},
});
const clDepart1 = await prisma.checklist.create({
data: {
empruntId: empruntEnCours.id,
utilisateurId: marie.id,
type: 'DEPART',
dateVerification: daysAgo(2),
},
});
await prisma.checklistElement.createMany({
data: [
{
checklistId: clDepart1.id,
accessoireId: chargeur.id,
nomElement: 'Chargeur',
etat: 'PRESENT',
quantiteConstatee: 1,
},
{
checklistId: clDepart1.id,
accessoireId: souris.id,
nomElement: 'Souris',
etat: 'PRESENT',
quantiteConstatee: 1,
},
],
});
// Emprunt 2 — EN_RETARD (Lucas / Vidéoprojecteur), date de retour dépassée.
const empruntEnRetard = await prisma.emprunt.create({
data: {
utilisateurId: lucas.id,
materielId: projecteur.id,
campusId: campus.id,
sallePretId: salle.id,
posteEmpruntId: poste.id,
dateEmprunt: daysAgo(20),
dateRetourPrevue: daysAgo(6),
statut: 'EN_RETARD',
modeIdentificationEmprunt: 'MAIL_ENSUP',
},
});
await prisma.checklist.create({
data: {
empruntId: empruntEnRetard.id,
utilisateurId: lucas.id,
type: 'DEPART',
dateVerification: daysAgo(20),
},
});
// Emprunt 3 — CLOTURE (Marie / iPad), retour conforme.
const empruntCloture = await prisma.emprunt.create({
data: {
utilisateurId: marie.id,
materielId: ipad.id,
campusId: campus.id,
sallePretId: salle.id,
posteEmpruntId: poste.id,
dateEmprunt: daysAgo(10),
dateRetourPrevue: daysAgo(3),
dateRetourReelle: daysAgo(3),
statut: 'CLOTURE',
modeIdentificationEmprunt: 'QR_CODE',
modeIdentificationRetour: 'QR_CODE',
},
});
await prisma.checklist.create({
data: {
empruntId: empruntCloture.id,
utilisateurId: marie.id,
type: 'DEPART',
dateVerification: daysAgo(10),
},
});
await prisma.checklist.create({
data: {
empruntId: empruntCloture.id,
utilisateurId: marie.id,
type: 'RETOUR',
dateVerification: daysAgo(3),
},
});
// Emprunt 4 — RETOUR_NON_CONFORME (Sofia / Caméra) : housse absente au retour.
const empruntNonConforme = await prisma.emprunt.create({
data: {
utilisateurId: sofia.id,
materielId: camera.id,
campusId: campus.id,
sallePretId: salle.id,
posteEmpruntId: poste.id,
dateEmprunt: daysAgo(5),
dateRetourPrevue: daysAgo(1),
dateRetourReelle: daysAgo(1),
statut: 'RETOUR_NON_CONFORME',
modeIdentificationEmprunt: 'MAIL_ENSUP',
modeIdentificationRetour: 'MAIL_ENSUP',
commentaireRetour: 'Housse non rapportee.',
},
});
const clDepart4 = await prisma.checklist.create({
data: {
empruntId: empruntNonConforme.id,
utilisateurId: sofia.id,
type: 'DEPART',
dateVerification: daysAgo(5),
},
});
await prisma.checklistElement.createMany({
data: [
{
checklistId: clDepart4.id,
accessoireId: housse.id,
nomElement: 'Housse',
etat: 'PRESENT',
quantiteConstatee: 1,
},
{
checklistId: clDepart4.id,
accessoireId: chargeur.id,
nomElement: 'Chargeur',
etat: 'PRESENT',
quantiteConstatee: 1,
},
],
});
const clRetour4 = await prisma.checklist.create({
data: {
empruntId: empruntNonConforme.id,
utilisateurId: sofia.id,
type: 'RETOUR',
dateVerification: daysAgo(1),
},
});
await prisma.checklistElement.createMany({
data: [
{
checklistId: clRetour4.id,
accessoireId: housse.id,
nomElement: 'Housse',
etat: 'ABSENT',
quantiteConstatee: 0,
commentaire: 'Manquante au retour',
},
{
checklistId: clRetour4.id,
accessoireId: chargeur.id,
nomElement: 'Chargeur',
etat: 'PRESENT',
quantiteConstatee: 1,
},
],
});
console.log('[fixtures] 4 emprunts + checklists crees');
// Anomalie sur l'emprunt non conforme + notification au responsable.
const anomalie = await prisma.anomalie.create({
data: {
empruntId: empruntNonConforme.id,
materielId: camera.id,
utilisateurId: sofia.id,
type: 'ACCESSOIRE_MANQUANT',
description: 'Housse declaree presente au depart, absente au retour.',
statut: 'DETECTEE',
detecteeAutomatiquement: true,
dateDetection: daysAgo(1),
},
});
await prisma.notification.create({
data: {
utilisateurId: responsable.id,
anomalieId: anomalie.id,
titre: 'Nouvelle anomalie detectee',
message: 'Retour non conforme sur la Caméra Sony (housse manquante).',
type: 'ANOMALIE',
},
});
console.log('[fixtures] 1 anomalie + 1 notification creees');
// Historique : quelques traces.
await prisma.historique.createMany({
data: [
{
utilisateurId: marie.id,
empruntId: empruntEnCours.id,
materielId: pcDell.id,
campusId: campus.id,
sallePretId: salle.id,
posteEmpruntId: poste.id,
action: 'CREATION_EMPRUNT',
description: 'Emprunt du PC portable Dell.',
dateAction: daysAgo(2),
},
{
utilisateurId: sofia.id,
empruntId: empruntNonConforme.id,
materielId: camera.id,
campusId: campus.id,
sallePretId: salle.id,
posteEmpruntId: poste.id,
action: 'CREATION_ANOMALIE',
description: 'Anomalie detectee au retour de la Caméra Sony.',
dateAction: daysAgo(1),
},
],
});
console.log("[fixtures] 2 entrees d'historique creees");
}
main()
.then(async () => {
await prisma.$disconnect();
console.log('[fixtures] termine');
})
.catch(async (error: unknown) => {
console.error('[fixtures] echec', error);
await prisma.$disconnect();
process.exit(1);
});