import { PrismaClient, Prisma } from '@prisma/client'; const prisma = new PrismaClient(); type CreatedFields = Record; async function deleteExistingData() { await prisma.machineComponentLink.deleteMany(); await prisma.machinePieceLink.deleteMany(); await prisma.machine.deleteMany(); await prisma.customFieldValue.deleteMany(); await prisma.composant.deleteMany(); await prisma.piece.deleteMany(); await prisma.modelType.deleteMany({ where: { code: { in: [ 'hydraulic-pump', 'hydraulic-reservoir', 'cooling-fan', 'cooling-module', 'structural-frame', 'hydraulic-power-unit', ], }, }, }); } async function createPieceType( name: string, code: string, description: string, fields: Array<{ name: string; type: 'text' | 'number' | 'select' | 'boolean' | 'date'; required?: boolean; options?: string[]; }>, skeleton?: Record, ) { const type = await prisma.modelType.create({ data: { name, code, category: 'PIECE', description, pieceSkeleton: skeleton ? (skeleton as Prisma.InputJsonValue) : Prisma.JsonNull, pieceCustomFields: { create: fields.map((field) => ({ name: field.name, type: field.type, required: field.required ?? false, options: field.options ?? [], })), }, }, }); const customFields = await prisma.customField.findMany({ where: { typePieceId: type.id }, }); const fieldMap: CreatedFields = {}; customFields.forEach((field) => { fieldMap[field.name] = field.id; }); return { type, fieldMap }; } async function createComponentType( name: string, code: string, description: string, fields: Array<{ name: string; type: 'text' | 'number' | 'select' | 'boolean' | 'date'; required?: boolean; options?: string[]; }>, skeleton?: Record, ) { const type = await prisma.modelType.create({ data: { name, code, category: 'COMPONENT', description, componentSkeleton: skeleton ? (skeleton as Prisma.InputJsonValue) : Prisma.JsonNull, customFields: { create: fields.map((field) => ({ name: field.name, type: field.type, required: field.required ?? false, options: field.options ?? [], })), }, }, }); const customFields = await prisma.customField.findMany({ where: { typeComposantId: type.id }, }); const fieldMap: CreatedFields = {}; customFields.forEach((field) => { fieldMap[field.name] = field.id; }); return { type, fieldMap }; } async function createPiece(options: { name: string; reference: string; price: number; constructeurIds?: string[] | null; typeId: string; fieldValues: Record; }) { const customFields = await prisma.customField.findMany({ where: { typePieceId: options.typeId }, }); const customFieldValues = Object.entries(options.fieldValues).map( ([fieldName, value]) => { const target = customFields.find((field) => field.name === fieldName); if (!target) { throw new Error( `Custom field "${fieldName}" introuvable pour le type de pièce ${options.typeId}`, ); } return { value, customFieldId: target.id, }; }, ); const constructeurIds = Array.isArray(options.constructeurIds) ? Array.from( new Set( options.constructeurIds .filter((value): value is string => typeof value === 'string') .map((value) => value.trim()) .filter((value) => value.length > 0), ), ) : []; const data: any = { name: options.name, reference: options.reference, prix: new Prisma.Decimal(options.price), typePieceId: options.typeId, customFieldValues: { create: customFieldValues, }, }; if (constructeurIds.length) { data.constructeurs = { connect: constructeurIds.map((id) => ({ id })), }; } return prisma.piece.create({ data, }); } async function createComponent(options: { name: string; reference: string; price: number; constructeurIds?: string[] | null; typeId: string; fieldValues: Record; structure?: Prisma.InputJsonValue; }) { const customFields = await prisma.customField.findMany({ where: { typeComposantId: options.typeId }, }); const customFieldValues = Object.entries(options.fieldValues).map( ([fieldName, value]) => { const target = customFields.find((field) => field.name === fieldName); if (!target) { throw new Error( `Custom field "${fieldName}" introuvable pour le type de composant ${options.typeId}`, ); } return { value, customFieldId: target.id, }; }, ); const constructeurIds = Array.isArray(options.constructeurIds) ? Array.from( new Set( options.constructeurIds .filter((value): value is string => typeof value === 'string') .map((value) => value.trim()) .filter((value) => value.length > 0), ), ) : []; const data: any = { name: options.name, reference: options.reference, prix: new Prisma.Decimal(options.price), typeComposantId: options.typeId, structure: options.structure === undefined ? Prisma.JsonNull : options.structure ?? Prisma.JsonNull, customFieldValues: { create: customFieldValues, }, }; if (constructeurIds.length) { data.constructeurs = { connect: constructeurIds.map((id) => ({ id })), }; } return prisma.composant.create({ data, }); } async function main() { console.log('Nettoyage des données existantes…'); await deleteExistingData(); console.log('Création des types de pièces…'); const pumpPieceFields: { name: string; type: 'text' | 'number' | 'select' | 'boolean' | 'date'; required?: boolean; options?: string[]; }[] = [ { name: 'Pression nominale (bar)', type: 'number', required: true }, { name: 'Débit nominal (L/min)', type: 'number', required: true }, { name: 'Puissance (kW)', type: 'number', required: true }, { name: 'Indice de protection', type: 'text' }, { name: 'Vitesse max (rpm)', type: 'number' }, ] as const; const pumpPieceType = await createPieceType( 'Pompe hydraulique', 'hydraulic-pump', 'Pompes à pistons pour unités hydrauliques', pumpPieceFields, { customFields: pumpPieceFields.map((field) => ({ name: field.name, key: field.name, type: field.type, required: field.required ?? false, })), }, ); const reservoirPieceFields: { name: string; type: 'text' | 'number' | 'select' | 'boolean' | 'date'; required?: boolean; options?: string[]; }[] = [ { name: 'Pression nominale (bar)', type: 'number', required: true }, { name: 'Débit nominal (L/min)', type: 'number', required: true }, { name: 'Capacité (L)', type: 'number', required: true }, { name: 'Type d’huile', type: 'text', required: true }, { name: 'Filtration', type: 'text' }, ] as const; const reservoirPieceType = await createPieceType( 'Réservoir hydraulique', 'hydraulic-reservoir', 'Réservoirs haute capacité pour fluide hydraulique', reservoirPieceFields, { customFields: reservoirPieceFields.map((field) => ({ name: field.name, key: field.name, type: field.type, required: field.required ?? false, })), }, ); const fanPieceFields: { name: string; type: 'text' | 'number' | 'select' | 'boolean' | 'date'; required?: boolean; options?: string[]; }[] = [ { name: 'Diamètre (mm)', type: 'number', required: true }, { name: 'Débit air (m³/h)', type: 'number', required: true }, { name: 'Consommation (A)', type: 'number' }, { name: 'Tension (V)', type: 'number', required: true }, { name: 'Niveau sonore (dB)', type: 'number' }, ] as const; const fanPieceType = await createPieceType( 'Ventilateur de refroidissement', 'cooling-fan', 'Ventilateurs axiaux pour modules de refroidissement', fanPieceFields, { customFields: fanPieceFields.map((field) => ({ name: field.name, key: field.name, type: field.type, required: field.required ?? false, })), }, ); console.log('Création des pièces…'); const pumpPiece = await createPiece({ name: 'Pompe à pistons PX-300', reference: 'PX-300', price: 1850, typeId: pumpPieceType.type.id, fieldValues: { 'Pression nominale (bar)': '180', 'Débit nominal (L/min)': '260', 'Puissance (kW)': '45', 'Indice de protection': 'IP55', 'Vitesse max (rpm)': '3200', }, }); const reservoirPiece = await createPiece({ name: 'Réservoir 120L Inox', reference: 'RES-120-INX', price: 720, typeId: reservoirPieceType.type.id, fieldValues: { 'Pression nominale (bar)': '12', 'Débit nominal (L/min)': '280', 'Capacité (L)': '120', 'Type d’huile': 'HLP46', Filtration: '10µ absolu', }, }); const fanPiece = await createPiece({ name: 'Ventilateur axial VE-450', reference: 'VE-450', price: 390, typeId: fanPieceType.type.id, fieldValues: { 'Diamètre (mm)': '450', 'Débit air (m³/h)': '5200', 'Consommation (A)': '3.2', 'Tension (V)': '400', 'Niveau sonore (dB)': '68', }, }); console.log('Création des types de composants…'); const coolingComponentFields: { name: string; type: 'text' | 'number' | 'select' | 'boolean' | 'date'; required?: boolean; options?: string[]; }[] = [ { name: 'Type de fluide', type: 'text', required: true }, { name: 'Puissance thermique (kW)', type: 'number', required: true }, { name: 'Température max (°C)', type: 'number' }, { name: 'Débit eau (L/min)', type: 'number' }, ]; const coolingComponentType = await createComponentType( 'Module de refroidissement', 'cooling-module', 'Modules compacts pour dissipation thermique du circuit hydraulique', coolingComponentFields, { customFields: coolingComponentFields.map((field) => ({ name: field.name, key: field.name, type: field.type, required: field.required ?? false, })), pieces: [ { typePieceId: fanPieceType.type.id, role: 'Ventilation principale', }, ], subcomponents: [], }, ); const frameComponentFields: { name: string; type: 'text' | 'number' | 'select' | 'boolean' | 'date'; required?: boolean; options?: string[]; }[] = [ { name: 'Matière', type: 'text', required: true }, { name: 'Charge admissible (kg)', type: 'number', required: true }, { name: 'Revêtement', type: 'text' }, { name: 'Points de levage', type: 'number' }, ]; const frameComponentType = await createComponentType( 'Châssis structurel', 'structural-frame', 'Châssis mécano-soudé pour unités hydrauliques', frameComponentFields, { customFields: frameComponentFields.map((field) => ({ name: field.name, key: field.name, type: field.type, required: field.required ?? false, })), pieces: [], subcomponents: [], }, ); const powerUnitComponentFields: { name: string; type: 'text' | 'number' | 'select' | 'boolean' | 'date'; required?: boolean; options?: string[]; }[] = [ { name: 'Pression service (bar)', type: 'number', required: true }, { name: 'Débit maxi (L/min)', type: 'number', required: true }, { name: 'Niveau sonore (dB)', type: 'number' }, { name: 'Indice de protection', type: 'text' }, ]; const powerUnitComponentType = await createComponentType( 'Centrale hydraulique', 'hydraulic-power-unit', 'Unités hydrauliques complètes pour machines industrielles', powerUnitComponentFields, { customFields: powerUnitComponentFields.map((field) => ({ name: field.name, key: field.name, type: field.type, required: field.required ?? false, })), pieces: [ { typePieceId: pumpPieceType.type.id, role: 'Pompe principale', }, { typePieceId: reservoirPieceType.type.id, role: 'Réservoir d’huile', }, ], subcomponents: [ { typeComposantId: coolingComponentType.type.id, alias: 'Module de refroidissement', }, { typeComposantId: frameComponentType.type.id, alias: 'Châssis structurel', }, ], }, ); console.log('Création des composants (sous-ensembles)…'); const coolingModule = await createComponent({ name: 'Module de refroidissement AquaCool 50', reference: 'AC-50', price: 2450, typeId: coolingComponentType.type.id, fieldValues: { 'Type de fluide': 'Huile minérale', 'Puissance thermique (kW)': '35', 'Température max (°C)': '85', 'Débit eau (L/min)': '65', }, structure: { path: 'root', pieces: [ { path: 'root:piece-0', definition: { typePieceId: fanPieceType.type.id, }, selectedPieceId: fanPiece.id, }, ], subcomponents: [], } as Prisma.InputJsonValue, }); const structuralFrame = await createComponent({ name: 'Châssis structurel XC-800', reference: 'FRAME-XC800', price: 1280, typeId: frameComponentType.type.id, fieldValues: { Matière: 'Acier S355', 'Charge admissible (kg)': '1800', Revêtement: 'Peinture epoxy', 'Points de levage': '4', }, }); console.log('Création du composant principal…'); await createComponent({ name: 'Centrale hydraulique HX-500', reference: 'HX-500', price: 12900, typeId: powerUnitComponentType.type.id, fieldValues: { 'Pression service (bar)': '210', 'Débit maxi (L/min)': '320', 'Niveau sonore (dB)': '72', 'Indice de protection': 'IP54', }, structure: { path: 'root', pieces: [ { path: 'root:piece-0', definition: { typePieceId: pumpPieceType.type.id, }, selectedPieceId: pumpPiece.id, }, { path: 'root:piece-1', definition: { typePieceId: reservoirPieceType.type.id, }, selectedPieceId: reservoirPiece.id, }, ], subcomponents: [ { path: 'root:sub-0', definition: { alias: 'Module de refroidissement', typeComposantId: coolingComponentType.type.id, }, selectedComponentId: coolingModule.id, }, { path: 'root:sub-1', definition: { alias: 'Châssis structurel', typeComposantId: frameComponentType.type.id, }, selectedComponentId: structuralFrame.id, }, ], } as Prisma.InputJsonValue, }); console.log('Population terminée ✅'); } main() .catch((error) => { console.error(error); process.exit(1); }) .finally(async () => { await prisma.$disconnect(); });