import { Prisma, PrismaClient, ModelCategory } from '@prisma/client'; const prisma = new PrismaClient(); type CustomFieldInput = { name: string; type: 'text' | 'number' | 'select'; required?: boolean; options?: readonly string[]; }; type ModelTypeSeed = { code: string; name: string; description: string; customFields: readonly CustomFieldInput[]; }; type ComponentRequirementSeed = { typeCode: string; label: string; minCount: number; maxCount?: number | null; required?: boolean; allowNewModels?: boolean; }; type PieceRequirementSeed = { typeCode: string; label: string; minCount: number; maxCount?: number | null; required?: boolean; allowNewModels?: boolean; }; const componentTypes: readonly ModelTypeSeed[] = [ { code: 'drive-module', name: 'Module d entrainement', description: 'Sous-ensemble moteur et reducteur pour entrainements principaux.', customFields: [ { name: 'Puissance nominale (kW)', type: 'number', required: true }, { name: 'Indice de protection', type: 'select', options: ['IP55', 'IP65', 'IP66'] }, ], }, { code: 'sensor-array', name: 'Chaine de capteurs', description: 'Groupe de capteurs industriels (temperature, vibration, debit).', customFields: [ { name: 'Type principal', type: 'select', options: ['Temperature', 'Vibration', 'Debit'] }, { name: 'Plage de mesure', type: 'text' }, ], }, { code: 'control-cabinet', name: 'Armoire de controle', description: 'Armoire electrique avec automate, protection et distribution.', customFields: [ { name: 'Tension alimentation (V)', type: 'number' }, { name: 'Nombre de departs', type: 'number' }, ], }, { code: 'hydraulic-pack', name: 'Groupe hydraulique', description: 'Bloc hydraulique complet (pompes, accumulateurs, filtration).', customFields: [ { name: 'Pression nominale (bar)', type: 'number', required: true }, { name: 'Debit nominal (L/min)', type: 'number' }, ], }, { code: 'structure-frame', name: 'Chassis structurel', description: 'Structure porteuse ou chassis mecano-soude.', customFields: [ { name: 'Matiere', type: 'select', options: ['Acier', 'Inox', 'Aluminium'] }, { name: 'Charge admissible (kg)', type: 'number' }, ], }, ]; const pieceTypes: readonly ModelTypeSeed[] = [ { code: 'belt-kit', name: 'Kit courroie', description: 'Courroie et accessoires pour entrainements.', customFields: [ { name: 'Type', type: 'select', options: ['Poly-V', 'Trapezoidale', 'Synchronisee'] }, { name: 'Longueur (mm)', type: 'number' }, ], }, { code: 'bearing-set', name: 'Jeu de roulements', description: 'Paire de roulements avec bagues et graisse.', customFields: [ { name: 'Diametre interieur (mm)', type: 'number', required: true }, { name: 'Classe', type: 'select', options: ['P0', 'P6', 'P5'] }, ], }, { code: 'filter-cartridge', name: 'Cartouche filtrante', description: 'Element filtrant pour fluides ou air.', customFields: [ { name: 'Grade de filtration (um)', type: 'number' }, { name: 'Type de media', type: 'select', options: ['Cellulose', 'Synthetique', 'Inox'] }, ], }, { code: 'sensor-probe', name: 'Sonde de mesure', description: 'Sonde ou capteur unitaire avec cable.', customFields: [ { name: 'Signal de sortie', type: 'select', options: ['4-20 mA', '0-10 V', 'PT100'] }, { name: 'Indice IP', type: 'select', options: ['IP67', 'IP68'] }, ], }, { code: 'maintenance-kit', name: 'Kit maintenance', description: 'Ensemble de pieces pour maintenance planifiee.', customFields: [ { name: 'Niveau de maintenance', type: 'select', options: ['Preventif', 'Correctif', 'Lourde'] }, { name: 'Duree estimee (h)', type: 'number' }, ], }, ]; const constructors = [ { name: 'ElectroMec Industrie', email: 'contact@electromec.fr', phone: '+33 4 72 00 11 22' }, { name: 'Hydraulic Systems Europe', email: 'sales@hydraulics-eu.com', phone: '+33 5 56 12 34 56' }, { name: 'Automation Lyon', email: 'support@automation-lyon.fr', phone: '+33 4 37 50 60 70' }, { name: 'ThermoTech Solutions', email: 'info@thermotech.eu', phone: '+33 1 44 55 66 77' }, { name: 'BearingWorks', email: 'service@bearingworks.com', phone: '+33 3 88 90 12 45' }, ] as const; const machineCustomFields: readonly CustomFieldInput[] = [ { name: 'Reference installation', type: 'text' }, { name: 'Puissance installee (kW)', type: 'number' }, { name: 'Zone critique', type: 'select', options: ['Zone A', 'Zone B', 'Zone C'] }, ]; const componentRequirementSeeds: readonly ComponentRequirementSeed[] = [ { typeCode: 'structure-frame', label: 'Chassis principal', minCount: 1, maxCount: 1, required: true, allowNewModels: false, }, { typeCode: 'drive-module', label: 'Module d entrainement principal', minCount: 1, maxCount: 1, required: true, allowNewModels: false, }, { typeCode: 'control-cabinet', label: 'Armoire de controle', minCount: 1, maxCount: 1, required: true, allowNewModels: false, }, { typeCode: 'sensor-array', label: 'Capteurs de surveillance', minCount: 1, maxCount: 3, required: true, allowNewModels: true, }, { typeCode: 'hydraulic-pack', label: 'Groupe hydraulique auxiliaire', minCount: 0, maxCount: 1, required: false, allowNewModels: true, }, ]; const pieceRequirementSeeds: readonly PieceRequirementSeed[] = [ { typeCode: 'belt-kit', label: 'Kit courroie de rechange', minCount: 1, maxCount: 2, required: true, allowNewModels: true, }, { typeCode: 'bearing-set', label: 'Roulements de secours', minCount: 1, maxCount: 2, required: true, allowNewModels: true, }, { typeCode: 'filter-cartridge', label: 'Cartouches de filtration', minCount: 0, maxCount: 4, required: false, allowNewModels: true, }, { typeCode: 'maintenance-kit', label: 'Kit maintenance planifiee', minCount: 0, maxCount: 1, required: false, allowNewModels: true, }, { typeCode: 'sensor-probe', label: 'Sondes de rechange', minCount: 1, maxCount: 4, required: true, allowNewModels: true, }, ]; function mapCustomFields(fields: readonly CustomFieldInput[]) { if (!fields.length) { return undefined; } return { create: fields.map((field) => ({ name: field.name, type: field.type, required: field.required ?? false, options: field.options ? [...field.options] : [], })), } as const; } async function upsertComponentType(type: ModelTypeSeed) { const customFields = mapCustomFields(type.customFields); await prisma.modelType.upsert({ where: { code: type.code }, update: { name: type.name, description: type.description, notes: type.description, customFields: { deleteMany: {}, ...(customFields ?? {}), }, }, create: { code: type.code, name: type.name, description: type.description, notes: type.description, category: ModelCategory.COMPONENT, ...(customFields ? { customFields } : {}), }, }); } async function upsertPieceType(type: ModelTypeSeed) { const customFields = mapCustomFields(type.customFields); await prisma.modelType.upsert({ where: { code: type.code }, update: { name: type.name, description: type.description, notes: type.description, pieceCustomFields: { deleteMany: {}, ...(customFields ?? {}), }, }, create: { code: type.code, name: type.name, description: type.description, notes: type.description, category: ModelCategory.PIECE, ...(customFields ? { pieceCustomFields: customFields } : {}), }, }); } async function applyPieceSkeletons(pieceMap: Map) { type PieceSkeleton = { customFields?: Array<{ name: string; value?: unknown; type?: string; required?: boolean; options?: unknown }>; [key: string]: unknown; }; const definitions: Record = { 'belt-kit': { customFields: [ { name: 'Type', value: 'Poly-V' }, { name: 'Longueur (mm)', value: 1800 }, ], remplacementHeures: 1500, stockageRecommande: 'Local sec et tempere', }, 'bearing-set': { customFields: [ { name: 'Diametre interieur (mm)', value: 45 }, { name: 'Classe', value: 'P6' }, ], graisseRecommandee: 'Lithium NLGI2', }, 'filter-cartridge': { customFields: [ { name: 'Grade de filtration (um)', value: 10 }, { name: 'Type de media', value: 'Synthetique' }, ], remplacementMensuel: true, }, 'sensor-probe': { customFields: [ { name: 'Signal de sortie', value: '4-20 mA' }, { name: 'Indice IP', value: 'IP67' }, ], calibrationIntervalJours: 180, }, 'maintenance-kit': { customFields: [ { name: 'Niveau de maintenance', value: 'Preventif' }, { name: 'Duree estimee (h)', value: 4 }, ], contenuStandard: ['Filtres', 'Joints', 'Visserie'], }, }; for (const [code, structure] of Object.entries(definitions)) { const record = pieceMap.get(code); if (!record) { continue; } await prisma.modelType.update({ where: { id: record.id }, data: { pieceSkeleton: structure as Prisma.InputJsonValue, }, }); } } async function applyComponentSkeletons( componentMap: Map, pieceMap: Map, ) { const pieceRef = (code: string, role?: string) => { const piece = pieceMap.get(code); if (!piece) { throw new Error(`Piece type ${code} requis pour le squelette`); } return { typePieceId: piece.id, ...(role ? { role } : {}), }; }; const componentRef = (code: string, alias?: string) => { const component = componentMap.get(code); if (!component) { throw new Error(`Component type ${code} requis pour le squelette`); } return { typeComposantId: component.id, ...(alias ? { alias } : {}), }; }; type ComponentSkeleton = { pieces: Array<{ typePieceId: string; role?: string }>; customFields: Array<{ key: string; value: unknown }>; subcomponents: Array<{ typeComposantId?: string; alias?: string; familyCode?: string; modelId?: string }>; }; const definitions: Record = { 'drive-module': { pieces: [ pieceRef('belt-kit', 'Courroie principale'), pieceRef('bearing-set', 'Roulements de sortie'), ], customFields: [ { key: 'Lubrification', value: 'Graissage centralise' }, { key: 'ControleVibration', value: 'Capteurs integres' }, ], subcomponents: [componentRef('sensor-array', 'Capteurs integres')], }, 'sensor-array': { pieces: [pieceRef('sensor-probe', 'Sonde principale')], customFields: [ { key: 'Calibration', value: 'A effectuer tous les 6 mois' }, { key: 'NombreCapteursMax', value: 6 }, ], subcomponents: [], }, 'control-cabinet': { pieces: [ pieceRef('maintenance-kit', 'Kit rechange armoire'), pieceRef('sensor-probe', 'Sonde ambiance'), ], customFields: [ { key: 'ClassementLocal', value: 'Non ATEX' }, { key: 'RefAutomate', value: 'PLC-STD-200' }, ], subcomponents: [], }, 'hydraulic-pack': { pieces: [ pieceRef('filter-cartridge', 'Filtre hydraulique'), pieceRef('maintenance-kit', 'Kit joints hydrauliques'), ], customFields: [ { key: 'ReservoirLitres', value: 120 }, { key: 'TypeHuile', value: 'HLP46' }, ], subcomponents: [componentRef('sensor-array', 'Capteurs pression et debit')], }, 'structure-frame': { pieces: [], customFields: [ { key: 'RevĂȘtement', value: 'Peinture epoxy' }, { key: 'PointsLevage', value: 4 }, ], subcomponents: [componentRef('sensor-array', 'Capteurs deformation')], }, }; for (const [code, structure] of Object.entries(definitions)) { const record = componentMap.get(code); if (!record) { continue; } await prisma.modelType.update({ where: { id: record.id }, data: { componentSkeleton: structure as Prisma.InputJsonValue, }, }); } } function buildComponentRequirements( componentMap: Map, seeds: readonly ComponentRequirementSeed[], ) { return seeds.map((seed) => { const type = componentMap.get(seed.typeCode); if (!type) { throw new Error(`Type composant ${seed.typeCode} introuvable pour le requirement`); } return { label: seed.label, minCount: seed.minCount, maxCount: seed.maxCount ?? null, required: seed.required ?? true, allowNewModels: seed.allowNewModels ?? true, typeComposant: { connect: { id: type.id } }, }; }); } function buildPieceRequirements( pieceMap: Map, seeds: readonly PieceRequirementSeed[], ) { return seeds.map((seed) => { const type = pieceMap.get(seed.typeCode); if (!type) { throw new Error(`Type piece ${seed.typeCode} introuvable pour le requirement`); } return { label: seed.label, minCount: seed.minCount, maxCount: seed.maxCount ?? null, required: seed.required ?? true, allowNewModels: seed.allowNewModels ?? true, typePiece: { connect: { id: type.id } }, }; }); } async function seedMachineTemplate( componentMap: Map, pieceMap: Map, ) { const name = 'Cellule Modulaire Standard'; const description = 'Module generique compose d un chassis, d un entrainement, de capteurs et d une armoire de controle.'; const componentRequirements = buildComponentRequirements(componentMap, componentRequirementSeeds); const pieceRequirements = buildPieceRequirements(pieceMap, pieceRequirementSeeds); await prisma.typeMachine.upsert({ where: { name }, update: { description, category: 'Module', maintenanceFrequency: 'Mensuelle', customFields: { deleteMany: {}, ...(mapCustomFields(machineCustomFields) ?? {}), }, componentRequirements: { deleteMany: {}, create: componentRequirements, }, pieceRequirements: { deleteMany: {}, create: pieceRequirements, }, }, create: { name, description, category: 'Module', maintenanceFrequency: 'Mensuelle', ...(mapCustomFields(machineCustomFields) ? { customFields: mapCustomFields(machineCustomFields)! } : {}), componentRequirements: { create: componentRequirements, }, pieceRequirements: { create: pieceRequirements, }, }, }); } async function main() { console.log('Seeding component categories...'); for (const component of componentTypes) { await upsertComponentType(component); } console.log('Seeding piece categories...'); for (const piece of pieceTypes) { await upsertPieceType(piece); } const componentRecords = await prisma.modelType.findMany({ where: { code: { in: componentTypes.map((type) => type.code) } }, select: { id: true, code: true }, }); const pieceRecords = await prisma.modelType.findMany({ where: { code: { in: pieceTypes.map((type) => type.code) } }, select: { id: true, code: true }, }); const componentMap = new Map(componentRecords.map((record) => [record.code, { id: record.id }])); const pieceMap = new Map(pieceRecords.map((record) => [record.code, { id: record.id }])); console.log('Applying piece skeletons...'); await applyPieceSkeletons(pieceMap); console.log('Applying component skeletons...'); await applyComponentSkeletons(componentMap, pieceMap); console.log('Seeding constructors...'); for (const constructeur of constructors) { await prisma.constructeur.upsert({ where: { name: constructeur.name }, update: { email: constructeur.email, phone: constructeur.phone, }, create: constructeur, }); } console.log('Configuring machine template...'); await seedMachineTemplate(componentMap, pieceMap); } main() .then(() => { console.log('Seed completed.'); }) .catch((error) => { console.error('Seed failed:', error); process.exitCode = 1; }) .finally(async () => { await prisma.$disconnect(); });