import { PrismaClient, Prisma, ModelCategory } from '@prisma/client'; const prisma = new PrismaClient(); type CustomFieldSpec = { name: string; type: 'string' | 'number' | 'boolean' | 'date'; required?: boolean; defaultValue?: string; options?: string[]; }; type ComponentTypeDefinition = { code: string; name: string; description: string; customFields: CustomFieldSpec[]; }; type PieceTypeDefinition = { code: string; name: string; description: string; customFields: CustomFieldSpec[]; }; type PieceModelDefinition = { code: string; name: string; description: string; typeCode: string; structure?: Prisma.InputJsonValue; }; type ComponentModelDefinition = { code: string; name: string; description: string; typeCode: string; structure?: Prisma.InputJsonValue; }; type ConstructeurDefinition = { key: string; name: string; email?: string; phone?: string; }; type ComponentPieceInstance = { name: string; reference?: string; prix?: string; typeCode: string; modelCode: string; constructeur?: string; customValues?: Record; }; type ComponentInstance = { name: string; reference?: string; prix?: string; typeCode: string; requirementLabel?: string; modelCode: string; constructeur?: string; customValues?: Record; pieces?: ComponentPieceInstance[]; children?: ComponentInstance[]; }; const componentTypeDefinitions: ComponentTypeDefinition[] = [ { code: 'bucket-elevator', name: 'Élévateur à godets', description: "Élévateur vertical utilisé pour monter les grains vers les étages supérieurs de l'usine.", customFields: [ { name: 'Débit nominal (t/h)', type: 'number', required: true }, { name: 'Hauteur de levage (m)', type: 'number' }, { name: 'Matériau des godets', type: 'string', defaultValue: 'Acier galvanisé' }, ], }, { code: 'belt-conveyor', name: 'Convoyeur à bande', description: 'Convoyeur horizontal ou incliné assurant le transfert des grains entre les postes.', customFields: [ { name: 'Largeur de bande (mm)', type: 'number', required: true }, { name: 'Longueur (m)', type: 'number' }, { name: 'Type de bande', type: 'string', defaultValue: 'Caoutchouc anti-statique' }, ], }, { code: 'gravity-separator', name: 'Table densimétrique', description: 'Séparateur gravimétrique permettant de retirer les impuretés lourdes ou légères.', customFields: [ { name: 'Capacité de tri (t/h)', type: 'number', required: true }, { name: 'Fréquence de vibration (Hz)', type: 'number' }, { name: 'Type de plateau', type: 'string', defaultValue: 'Acier perforé' }, ], }, { code: 'grain-dryer', name: 'Séchoir à grains', description: 'Séchoir continu assurant la réduction du taux d\'humidité des céréales.', customFields: [ { name: 'Capacité sèche (t/h)', type: 'number', required: true }, { name: "Nombre d'étages", type: 'number' }, { name: "Type d'énergie", type: 'string', defaultValue: 'Gaz naturel' }, ], }, { code: 'screw-conveyor', name: 'Vis sans fin', description: 'Vis de transfert pour la reprise des grains et le rechargement des silos.', customFields: [ { name: 'Diamètre vis (mm)', type: 'number', required: true }, { name: 'Inclinaison (°)', type: 'number' }, { name: 'Vitesse (rpm)', type: 'number' }, ], }, { code: 'weigh-hopper', name: 'Benna peseuse', description: 'Benna équipée de capteurs de pesage pour le chargement précis des camions.', customFields: [ { name: 'Capacité de pesée (kg)', type: 'number', required: true }, { name: 'Précision (%)', type: 'number' }, { name: 'Nombre de capteurs', type: 'number', defaultValue: '4' }, ], }, { code: 'control-panel', name: 'Armoire de contrôle', description: 'Armoire électrique pilotant l\'ensemble de la ligne de triage.', customFields: [ { name: 'Automate principal', type: 'string', required: true }, { name: 'Année de mise à jour', type: 'number' }, { name: "Indice de protection", type: 'string', defaultValue: 'IP55' }, ], }, { code: 'telehandler', name: 'Chariot télescopique', description: 'Manitou utilisé pour les manutentions ponctuelles et la gestion des sacs big-bags.', customFields: [ { name: 'Capacité de levage (t)', type: 'number', required: true }, { name: 'Hauteur de levage (m)', type: 'number' }, { name: "Type d'attache", type: 'string', defaultValue: 'Fourches FEM' }, ], }, { code: 'burner-module', name: 'Module brûleur', description: 'Module de combustion alimentant le séchoir en air chaud.', customFields: [ { name: 'Puissance thermique (kW)', type: 'number', required: true }, { name: 'Type de carburant', type: 'string', defaultValue: 'Gaz naturel' }, { name: "Système d'allumage", type: 'string', defaultValue: 'Double électrode' }, ], }, { code: 'dust-filter', name: 'Filtre à poussière', description: 'Filtre à cyclone captant les poussières en sortie de séchoir.', customFields: [ { name: 'Efficacité de filtration (%)', type: 'number', required: true }, { name: 'Type de média filtrant', type: 'string' }, { name: 'Nombre de cartouches', type: 'number' }, ], }, ]; const pieceTypeDefinitions: PieceTypeDefinition[] = [ { code: 'electric-motor', name: 'Moteur électrique', description: 'Motorisation asynchrone IE3 pour entraînements industriels.', customFields: [ { name: 'Puissance nominale (kW)', type: 'number', required: true }, { name: 'Tension (V)', type: 'number' }, { name: "Indice de protection", type: 'string', defaultValue: 'IP55' }, ], }, { code: 'speed-sensor', name: 'Capteur de vitesse', description: 'Capteur inductif contrôlant la vitesse des organes rotatifs.', customFields: [ { name: 'Type de sortie', type: 'string', required: true }, { name: 'Plage de mesure (rpm)', type: 'number' }, ], }, { code: 'temperature-probe', name: 'Sonde de température', description: 'Sonde PT100 pour la surveillance thermique du séchoir.', customFields: [ { name: 'Plage de mesure (°C)', type: 'string', required: true }, { name: 'Type de sonde', type: 'string', defaultValue: 'PT100' }, ], }, { code: 'belt', name: 'Bande transporteuse', description: 'Bande caoutchouc renforcée pour convoyeurs et élévateurs.', customFields: [ { name: 'Largeur (mm)', type: 'number', required: true }, { name: 'Matériau', type: 'string' }, ], }, { code: 'bearing', name: 'Roulement', description: 'Roulement à semelle pour transmissions tournantes.', customFields: [ { name: 'Référence fournisseur', type: 'string', required: true }, { name: "Type d'étanchéité", type: 'string' }, ], }, { code: 'gearbox', name: 'Réducteur', description: 'Réducteur de vitesse pour entraînement lourd.', customFields: [ { name: 'Rapport de réduction', type: 'string', required: true }, { name: 'Couple nominal (Nm)', type: 'number' }, ], }, { code: 'air-filter', name: 'Filtre à air', description: 'Cartouche filtrante pour dépoussiérage.', customFields: [ { name: 'Classe de filtration', type: 'string', required: true }, { name: 'Dimensions (mm)', type: 'string' }, ], }, { code: 'hydraulic-pump', name: 'Pompe hydraulique', description: 'Pompe hydraulique pour chariot télescopique.', customFields: [ { name: 'Débit nominal (l/min)', type: 'number', required: true }, { name: 'Pression max (bar)', type: 'number' }, ], }, { code: 'control-module', name: 'Module automate', description: 'Module PLC assurant le contrôle de ligne.', customFields: [ { name: "Nombre d'E/S", type: 'number', required: true }, { name: 'Version firmware', type: 'string' }, ], }, { code: 'load-cell', name: 'Capteur de pesage', description: 'Capteur de pesage pour benne peseuse.', customFields: [ { name: 'Capacité (kg)', type: 'number', required: true }, { name: 'Sensibilité (mV/V)', type: 'string' }, ], }, ]; const pieceModelDefinitions: PieceModelDefinition[] = [ { code: 'motor-75kw', name: 'Moteur IE3 75 kW', description: 'Moteur principal pour élévateur haute capacité.', typeCode: 'electric-motor', structure: { defaultCustomFieldValues: { 'Puissance nominale (kW)': '75', 'Tension (V)': '400', "Indice de protection": 'IP55', }, }, }, { code: 'motor-45kw', name: 'Moteur IE3 45 kW', description: 'Motorisation pour convoyeur ou ventilateur.', typeCode: 'electric-motor', structure: { defaultCustomFieldValues: { 'Puissance nominale (kW)': '45', 'Tension (V)': '400', "Indice de protection": 'IP55', }, }, }, { code: 'motor-18kw', name: 'Moteur IE3 18,5 kW', description: 'Motorisation pour vis sans fin ou table densimétrique.', typeCode: 'electric-motor', structure: { defaultCustomFieldValues: { 'Puissance nominale (kW)': '18.5', 'Tension (V)': '400', "Indice de protection": 'IP55', }, }, }, { code: 'sensor-speed-m12', name: 'Capteur inductif M12', description: 'Capteur de vitesse inductif 4-20 mA.', typeCode: 'speed-sensor', structure: { defaultCustomFieldValues: { 'Type de sortie': 'PNP 4-20 mA', 'Plage de mesure (rpm)': '0-1200', }, }, }, { code: 'temp-probe-pt100', name: 'Sonde PT100 classe A', description: 'Sonde PT100 pour mesure d\'air chaud.', typeCode: 'temperature-probe', structure: { defaultCustomFieldValues: { 'Plage de mesure (°C)': '0-250', 'Type de sonde': 'PT100 classe A', }, }, }, { code: 'belt-hd-800', name: 'Bande renforcée 800 mm', description: 'Bande HD 800 mm anti-statique.', typeCode: 'belt', structure: { defaultCustomFieldValues: { 'Largeur (mm)': '800', 'Matériau': 'Caoutchouc nitrile', }, }, }, { code: 'belt-hd-650', name: 'Bande renforcée 650 mm', description: 'Bande HD 650 mm résistante à l\'abrasion.', typeCode: 'belt', structure: { defaultCustomFieldValues: { 'Largeur (mm)': '650', 'Matériau': 'Caoutchouc anti-abrasion', }, }, }, { code: 'bearing-ucp210', name: 'Roulement UCP210', description: 'Roulement à semelle graissable.', typeCode: 'bearing', structure: { defaultCustomFieldValues: { 'Référence fournisseur': 'SKF UCP210', "Type d'étanchéité": '2RS', }, }, }, { code: 'gearbox-flender', name: 'Réducteur Flender FZ', description: 'Réducteur coaxial pour charge lourde.', typeCode: 'gearbox', structure: { defaultCustomFieldValues: { 'Rapport de réduction': '1:28', 'Couple nominal (Nm)': '3200', }, }, }, { code: 'filter-g4-500', name: 'Filtre G4 500x500', description: 'Cartouche filtrante G4 pour dépoussiérage.', typeCode: 'air-filter', structure: { defaultCustomFieldValues: { 'Classe de filtration': 'G4', 'Dimensions (mm)': '500x500x50', }, }, }, { code: 'hydraulic-pump-pvh98', name: 'Pompe hydraulique PVH98', description: 'Pompe haute pression pour Manitou.', typeCode: 'hydraulic-pump', structure: { defaultCustomFieldValues: { 'Débit nominal (l/min)': '160', 'Pression max (bar)': '320', }, }, }, { code: 'plc-module-siemens-1512', name: 'Module PLC Siemens 1512', description: 'Automate Siemens S7-1500 1512SP.', typeCode: 'control-module', structure: { defaultCustomFieldValues: { "Nombre d'E/S": '32', 'Version firmware': 'V2.9', }, }, }, { code: 'load-cell-5t', name: 'Capteur de pesage 5 t', description: 'Capteur de pesage IP68 pour benne peseuse.', typeCode: 'load-cell', structure: { defaultCustomFieldValues: { 'Capacité (kg)': '5000', 'Sensibilité (mV/V)': '2.0', }, }, }, ]; const componentModelDefinitions: ComponentModelDefinition[] = [ { code: 'elevator-z400', name: 'Élévateur Z400', description: 'Élévateur à godets 120 t/h, 38 m.', typeCode: 'bucket-elevator', structure: { defaultPieces: [ { modelCode: 'motor-75kw', role: 'Motorisation principale' }, { modelCode: 'gearbox-flender', role: 'Réducteur' }, { modelCode: 'belt-hd-800', role: 'Courroie élévateur' }, { modelCode: 'sensor-speed-m12', role: 'Capteur vitesse tête' }, { modelCode: 'bearing-ucp210', role: 'Paliers de tête' }, ], recommendedCustomFields: { 'Débit nominal (t/h)': '120', 'Hauteur de levage (m)': '38', 'Matériau des godets': 'Acier galvanisé', }, }, }, { code: 'conveyor-18m', name: 'Convoyeur bande 18 m', description: 'Convoyeur à bande 800 mm, 18 mètres.', typeCode: 'belt-conveyor', structure: { defaultPieces: [ { modelCode: 'motor-45kw', role: 'Motorisation' }, { modelCode: 'belt-hd-800', role: 'Bande transporteuse' }, { modelCode: 'sensor-speed-m12', role: 'Capteur rotation' }, ], recommendedCustomFields: { 'Largeur de bande (mm)': '800', 'Longueur (m)': '18', 'Type de bande': 'Caoutchouc anti-statique', }, }, }, { code: 'gravity-table-tqx', name: 'Table densimétrique TQX-120', description: 'Table densimétrique haute précision 120 t/h.', typeCode: 'gravity-separator', structure: { defaultPieces: [ { modelCode: 'motor-18kw', role: 'Motorisation vibration' }, { modelCode: 'sensor-speed-m12', role: 'Capteur vibration' }, ], recommendedCustomFields: { 'Capacité de tri (t/h)': '120', 'Fréquence de vibration (Hz)': '45', 'Type de plateau': 'Acier perforé', }, }, }, { code: 'grain-dryer-cd6', name: 'Séchoir continu CD-6', description: 'Séchoir continu 6 étages avec récupération de chaleur.', typeCode: 'grain-dryer', structure: { defaultPieces: [ { modelCode: 'motor-45kw', role: 'Ventilateur principal' }, { modelCode: 'temp-probe-pt100', role: 'Sonde air chaud' }, ], subComponents: [ { modelCode: 'burner-module-3mw', role: 'Brûleur gaz principal' }, { modelCode: 'dust-filter-fc12', role: 'Filtre cyclone' }, ], recommendedCustomFields: { 'Capacité sèche (t/h)': '60', "Nombre d'étages": '6', "Type d'énergie": 'Gaz naturel', }, }, }, { code: 'screw-conveyor-v200', name: 'Vis sans fin V200', description: 'Vis de reprise diamètre 200 mm.', typeCode: 'screw-conveyor', structure: { defaultPieces: [ { modelCode: 'motor-18kw', role: 'Motorisation vis' }, { modelCode: 'bearing-ucp210', role: 'Paliers supports' }, ], recommendedCustomFields: { 'Diamètre vis (mm)': '200', 'Inclinaison (°)': '12', 'Vitesse (rpm)': '140', }, }, }, { code: 'weigh-hopper-bp5', name: 'Benna peseuse BP-5', description: 'Benna peseuse 5 tonnes pour chargement camion.', typeCode: 'weigh-hopper', structure: { defaultPieces: [ { modelCode: 'load-cell-5t', role: 'Capteurs de pesage' }, { modelCode: 'sensor-speed-m12', role: 'Capteur rotation trappe' }, ], recommendedCustomFields: { 'Capacité de pesée (kg)': '5000', 'Précision (%)': '0.5', 'Nombre de capteurs': '4', }, }, }, { code: 'control-panel-m340', name: 'Armoire Schneider M340', description: 'Armoire Schneider Electric avec automate M340.', typeCode: 'control-panel', structure: { defaultPieces: [ { modelCode: 'plc-module-siemens-1512', role: 'Automate principal' }, { modelCode: 'sensor-speed-m12', role: 'Entrée vitesse générale' }, ], recommendedCustomFields: { 'Automate principal': 'Schneider Modicon M340', 'Année de mise à jour': '2023', "Indice de protection": 'IP55', }, }, }, { code: 'burner-module-3mw', name: 'Brûleur gaz 3 MW', description: 'Brûleur gaz modulant 3 MW pour séchoir.', typeCode: 'burner-module', structure: { defaultPieces: [ { modelCode: 'temp-probe-pt100', role: 'Sonde sécurité flamme' }, ], recommendedCustomFields: { 'Puissance thermique (kW)': '3000', 'Type de carburant': 'Gaz naturel', "Système d'allumage": 'Double électrode', }, }, }, { code: 'dust-filter-fc12', name: 'Filtre cyclone FC-12', description: 'Filtre cyclone avec cartouches G4.', typeCode: 'dust-filter', structure: { defaultPieces: [ { modelCode: 'filter-g4-500', role: 'Cartouche filtrante' }, ], recommendedCustomFields: { 'Efficacité de filtration (%)': '95', 'Type de média filtrant': 'Polyester', 'Nombre de cartouches': '6', }, }, }, { code: 'telehandler-mlt1040', name: 'Manitou MLT 1040', description: 'Chariot télescopique Manitou 4 t.', typeCode: 'telehandler', structure: { defaultPieces: [ { modelCode: 'hydraulic-pump-pvh98', role: 'Pompe hydraulique principale' }, { modelCode: 'bearing-ucp210', role: 'Articulation de flèche' }, ], recommendedCustomFields: { 'Capacité de levage (t)': '4', 'Hauteur de levage (m)': '9.6', "Type d'attache": 'Fourches FEM', }, }, }, ]; const constructeurDefinitions: ConstructeurDefinition[] = [ { key: 'agritech', name: 'AgriTech Elevators', email: 'support@agritech-elevators.fr', phone: '+33 3 74 01 20 10', }, { key: 'valmont', name: 'Valmont Conveyors', email: 'info@valmont-conveyors.com', phone: '+33 3 80 45 77 20', }, { key: 'buhler', name: 'Bühler Sortex', email: 'service@buhlergroup.com', phone: '+41 71 955 11 11', }, { key: 'agridry', name: 'AgriDry Solutions', email: 'contact@agridry.eu', phone: '+33 4 74 22 55 90', }, { key: 'sew', name: 'SEW-Eurodrive', email: 'contact@sew-eurodrive.fr', phone: '+33 3 88 73 67 00', }, { key: 'skf', name: 'SKF France', email: 'support@skf.com', phone: '+33 1 30 57 67 00', }, { key: 'schneider', name: 'Schneider Electric', email: 'contact@se.com', phone: '+33 1 47 29 70 00', }, { key: 'manitou', name: 'Manitou BF', email: 'support@manitou-group.com', phone: '+33 2 40 09 10 11', }, { key: 'flender', name: 'Flender GmbH', email: 'service@flender.com', phone: '+49 521 525 0', }, { key: 'ifm', name: 'ifm electronic', email: 'info.fr@ifm.com', phone: '+33 1 45 12 24 00', }, { key: 'siemens', name: 'Siemens Industry', email: 'industry.fr@siemens.com', phone: '+33 1 85 57 00 00', }, ]; async function clearDatabaseExceptSitesAndProfiles() { console.log('🧹 Nettoyage des tables (hors sites et profils)...'); const deleteOrder = [ prisma.customFieldValue.deleteMany(), prisma.document.deleteMany(), prisma.piece.deleteMany(), prisma.composant.deleteMany(), prisma.machine.deleteMany(), prisma.typeMachineComponentRequirement.deleteMany(), prisma.typeMachinePieceRequirement.deleteMany(), prisma.customField.deleteMany(), prisma.pieceModel.deleteMany(), prisma.composantModel.deleteMany(), prisma.typeMachine.deleteMany(), prisma.modelType.deleteMany(), prisma.constructeur.deleteMany(), ]; for (const promise of deleteOrder) { await promise; } console.log('✅ Tables nettoyées.'); } async function ensureDemoSite() { const existingSite = await prisma.site.findFirst(); if (existingSite) { return existingSite; } console.log('🏗️ Création du site de démonstration...'); return prisma.site.create({ data: { name: 'Usine de triage Valgrain', contactName: 'Lucie Bernard', contactPhone: '+33 3 80 12 45 78', contactAddress: 'Zone industrielle des Platanes', contactPostalCode: '21000', contactCity: 'Dijon', }, }); } async function createConstructeurs() { console.log('🏭 Création des constructeurs...'); const entries = await Promise.all( constructeurDefinitions.map((definition) => prisma.constructeur .create({ data: { name: definition.name, email: definition.email, phone: definition.phone, }, }) .then((constructeur) => [definition.key, constructeur] as const), ), ); return Object.fromEntries(entries) as Record; } function mapCustomFields( fields: { id: string; name: string }[], ): Record { return fields.reduce>((acc, field) => { acc[field.name] = field.id; return acc; }, {}); } async function createModelTypes() { console.log('🗂️ Création des catégories de composants et pièces...'); const componentTypeEntries: [ string, { id: string; customFields: Record; }, ][] = []; for (const definition of componentTypeDefinitions) { const record = await prisma.modelType.create({ data: { name: definition.name, code: definition.code, category: ModelCategory.COMPONENT, description: definition.description, customFields: { create: definition.customFields.map((field) => ({ name: field.name, type: field.type, required: field.required ?? false, defaultValue: field.defaultValue, options: field.options ?? [], })), }, }, include: { customFields: true, }, }); componentTypeEntries.push([ definition.code, { id: record.id, customFields: mapCustomFields(record.customFields), }, ]); } const pieceTypeEntries: [ string, { id: string; customFields: Record; }, ][] = []; for (const definition of pieceTypeDefinitions) { const record = await prisma.modelType.create({ data: { name: definition.name, code: definition.code, category: ModelCategory.PIECE, description: definition.description, pieceCustomFields: { create: definition.customFields.map((field) => ({ name: field.name, type: field.type, required: field.required ?? false, defaultValue: field.defaultValue, options: field.options ?? [], })), }, }, include: { pieceCustomFields: true, }, }); pieceTypeEntries.push([ definition.code, { id: record.id, customFields: mapCustomFields(record.pieceCustomFields), }, ]); } return { componentTypes: Object.fromEntries(componentTypeEntries) as Record< string, { id: string; customFields: Record } >, pieceTypes: Object.fromEntries(pieceTypeEntries) as Record< string, { id: string; customFields: Record } >, }; } async function createPieceModels(pieceTypes: Record) { console.log('🔧 Création des modèles de pièces...'); const entries = await Promise.all( pieceModelDefinitions.map(async (definition) => { const record = await prisma.pieceModel.create({ data: { name: definition.name, description: definition.description, typePiece: { connect: { id: pieceTypes[definition.typeCode].id } }, structure: definition.structure ?? Prisma.JsonNull, }, }); return [definition.code, record] as const; }), ); return Object.fromEntries(entries) as Record; } async function createComponentModels( componentTypes: Record, ) { console.log('🧱 Création des modèles de composants...'); const entries = await Promise.all( componentModelDefinitions.map(async (definition) => { const record = await prisma.composantModel.create({ data: { name: definition.name, description: definition.description, typeComposant: { connect: { id: componentTypes[definition.typeCode].id } }, structure: definition.structure ?? Prisma.JsonNull, }, }); return [definition.code, record] as const; }), ); return Object.fromEntries(entries) as Record; } async function createTypeMachine( componentTypes: Record, pieceTypes: Record, ) { console.log('🧬 Création du squelette de machine...'); const typeMachine = await prisma.typeMachine.create({ data: { name: 'Ligne de triage et séchage céréales 120 t/h', description: 'Chaîne automatisée de réception, triage, séchage et expédition des céréales.', category: 'Triage et stockage', maintenanceFrequency: 'Inspection quotidienne, graissage hebdomadaire, révision trimestrielle.', specifications: { nominalThroughput: 120, moistureTarget: 14.5, building: 'Hall principal', shiftsPerDay: 2, }, components: { layout: [ { order: 1, zone: 'Réception', type: 'bucket-elevator', model: 'elevator-z400' }, { order: 2, zone: 'Pré-nettoyage', type: 'belt-conveyor', model: 'conveyor-18m' }, { order: 3, zone: 'Séparation', type: 'gravity-separator', model: 'gravity-table-tqx' }, { order: 4, zone: 'Séchage', type: 'grain-dryer', model: 'grain-dryer-cd6' }, { order: 5, zone: 'Reprise', type: 'screw-conveyor', model: 'screw-conveyor-v200' }, { order: 6, zone: 'Expédition', type: 'weigh-hopper', model: 'weigh-hopper-bp5' }, ], }, criticalParts: { motors: ['motor-75kw', 'motor-45kw'], sensors: ['sensor-speed-m12', 'temp-probe-pt100'], wearItems: ['belt-hd-800', 'filter-g4-500'], }, machinePieces: { recommendedStock: [ { model: 'motor-75kw', quantity: 1 }, { model: 'sensor-speed-m12', quantity: 3 }, { model: 'belt-hd-800', quantity: 1 }, ], }, customFields: { create: [ { name: 'Capacité nominale (t/h)', type: 'number', required: true, options: [], }, { name: 'Produit traité', type: 'string', required: true, defaultValue: 'Blé tendre', options: [], }, { name: 'Date de mise en service', type: 'date', required: true, options: [], }, { name: 'Responsable de ligne', type: 'string', required: false, defaultValue: 'Lucie Bernard', options: [], }, ], }, componentRequirements: { create: [ { label: 'Élévateurs principaux', minCount: 2, maxCount: 2, required: true, typeComposant: { connect: { id: componentTypes['bucket-elevator'].id } }, }, { label: "Convoyeurs d'alimentation", minCount: 2, required: true, typeComposant: { connect: { id: componentTypes['belt-conveyor'].id } }, }, { label: 'Table densimétrique', minCount: 1, maxCount: 1, required: true, typeComposant: { connect: { id: componentTypes['gravity-separator'].id } }, }, { label: 'Séchoir continu', minCount: 1, maxCount: 1, required: true, typeComposant: { connect: { id: componentTypes['grain-dryer'].id } }, }, { label: 'Vis de reprise', minCount: 2, required: true, typeComposant: { connect: { id: componentTypes['screw-conveyor'].id } }, }, { label: 'Benna peseuse', minCount: 1, required: true, typeComposant: { connect: { id: componentTypes['weigh-hopper'].id } }, }, { label: 'Armoire de contrôle', minCount: 1, maxCount: 1, required: true, typeComposant: { connect: { id: componentTypes['control-panel'].id } }, }, { label: 'Chariot télescopique', minCount: 1, required: false, typeComposant: { connect: { id: componentTypes['telehandler'].id } }, }, ], }, pieceRequirements: { create: [ { label: 'Moteurs de rechange', minCount: 1, required: false, typePiece: { connect: { id: pieceTypes['electric-motor'].id } }, }, { label: 'Capteurs de vitesse de secours', minCount: 2, required: false, typePiece: { connect: { id: pieceTypes['speed-sensor'].id } }, }, ], }, }, include: { customFields: true, componentRequirements: true, pieceRequirements: true, }, }); return typeMachine; } function buildCustomFieldValues( fieldMap: Record, values?: Record, ) { if (!values) { return undefined; } return { create: Object.entries(values).map(([name, value]) => { const fieldId = fieldMap[name]; if (!fieldId) { throw new Error(`Champ personnalisé inconnu: ${name}`); } return { value: value instanceof Date ? value.toISOString() : String(value), customField: { connect: { id: fieldId }, }, }; }), }; } async function createComponentHierarchy( machineId: string, component: ComponentInstance, context: { componentTypes: Record }>; componentModels: Record; pieceTypes: Record }>; pieceModels: Record; constructeurs: Record; requirementMap: Map; }, parentId?: string, ) { const requirementId = component.requirementLabel ? context.requirementMap.get(component.requirementLabel) : undefined; const record = await prisma.composant.create({ data: { name: component.name, reference: component.reference, prix: component.prix ? new Prisma.Decimal(component.prix) : undefined, machine: { connect: { id: machineId } }, parentComposant: parentId ? { connect: { id: parentId } } : undefined, typeComposant: { connect: { id: context.componentTypes[component.typeCode].id }, }, composantModel: { connect: { id: context.componentModels[component.modelCode].id }, }, typeMachineComponentRequirement: requirementId ? { connect: { id: requirementId } } : undefined, constructeur: component.constructeur ? { connect: { id: context.constructeurs[component.constructeur].id } } : undefined, customFieldValues: buildCustomFieldValues( context.componentTypes[component.typeCode].customFields, component.customValues, ), pieces: component.pieces ? { create: component.pieces.map((piece) => { const type = context.pieceTypes[piece.typeCode]; if (!type) { throw new Error(`Type de pièce introuvable: ${piece.typeCode}`); } return { name: piece.name, reference: piece.reference, prix: piece.prix ? new Prisma.Decimal(piece.prix) : undefined, typePiece: { connect: { id: type.id } }, pieceModel: { connect: { id: context.pieceModels[piece.modelCode].id }, }, constructeur: piece.constructeur ? { connect: { id: context.constructeurs[piece.constructeur].id } } : undefined, customFieldValues: buildCustomFieldValues( type.customFields, piece.customValues, ), }; }), } : undefined, }, }); if (component.children && component.children.length > 0) { for (const child of component.children) { await createComponentHierarchy(machineId, child, context, record.id); } } return record; } async function createMachineWithComponents( siteId: string, typeMachine: Awaited>, context: { componentTypes: Record }>; componentModels: Record; pieceTypes: Record }>; pieceModels: Record; constructeurs: Record; }, ) { console.log('🏗️ Création de la machine de démonstration...'); const requirementMap = new Map(); typeMachine.componentRequirements.forEach((requirement) => { if (requirement.label) { requirementMap.set(requirement.label, requirement.id); } }); const pieceRequirementMap = new Map(); typeMachine.pieceRequirements.forEach((requirement) => { if (requirement.label) { pieceRequirementMap.set(requirement.label, requirement.id); } }); const machineCustomFieldMap = mapCustomFields(typeMachine.customFields); const machine = await prisma.machine.create({ data: { name: 'Ligne de triage Valgrain 2024', reference: 'VT-TRI-2024', prix: new Prisma.Decimal('725000'), site: { connect: { id: siteId } }, typeMachine: { connect: { id: typeMachine.id } }, constructeur: { connect: { id: context.constructeurs['buhler'].id } }, customFieldValues: buildCustomFieldValues(machineCustomFieldMap, { 'Capacité nominale (t/h)': 120, 'Produit traité': 'Blé tendre, orge brassicole', 'Date de mise en service': new Date('2024-02-01'), 'Responsable de ligne': 'Lucie Bernard', }), }, }); console.log('⚙️ Ajout des composants et sous-ensembles...'); const componentContext = { ...context, requirementMap, }; const components: ComponentInstance[] = [ { name: 'Élévateur amont Z400', reference: 'BE-Z400-01', prix: '58000', typeCode: 'bucket-elevator', requirementLabel: 'Élévateurs principaux', modelCode: 'elevator-z400', constructeur: 'agritech', customValues: { 'Débit nominal (t/h)': 120, 'Hauteur de levage (m)': 38, 'Matériau des godets': 'Acier galvanisé', }, pieces: [ { name: 'Moteur principal 75 kW', reference: 'MTR-75-01', prix: '8200', typeCode: 'electric-motor', modelCode: 'motor-75kw', constructeur: 'sew', customValues: { 'Puissance nominale (kW)': 75, 'Tension (V)': 400, "Indice de protection": 'IP55', }, }, { name: 'Réducteur Flender FZ', reference: 'GBX-FZ-01', prix: '5100', typeCode: 'gearbox', modelCode: 'gearbox-flender', constructeur: 'flender', customValues: { 'Rapport de réduction': '1:28', 'Couple nominal (Nm)': 3200, }, }, { name: 'Courroie élévateur 800 mm', reference: 'BLT-800-01', prix: '2300', typeCode: 'belt', modelCode: 'belt-hd-800', constructeur: 'agritech', customValues: { 'Largeur (mm)': 800, 'Matériau': 'Caoutchouc nitrile', }, }, { name: 'Capteur vitesse tête', reference: 'SNS-VIT-01', typeCode: 'speed-sensor', modelCode: 'sensor-speed-m12', constructeur: 'ifm', customValues: { 'Type de sortie': 'PNP 4-20 mA', 'Plage de mesure (rpm)': 1200, }, }, { name: 'Roulement tête élévateur', reference: 'BRG-UCP210-01', typeCode: 'bearing', modelCode: 'bearing-ucp210', constructeur: 'skf', customValues: { 'Référence fournisseur': 'SKF UCP210', "Type d'étanchéité": '2RS', }, }, ], }, { name: 'Élévateur aval Z400', reference: 'BE-Z400-02', prix: '56800', typeCode: 'bucket-elevator', requirementLabel: 'Élévateurs principaux', modelCode: 'elevator-z400', constructeur: 'agritech', customValues: { 'Débit nominal (t/h)': 115, 'Hauteur de levage (m)': 36, 'Matériau des godets': 'Acier galvanisé', }, pieces: [ { name: 'Moteur principal 75 kW (aval)', reference: 'MTR-75-02', prix: '8150', typeCode: 'electric-motor', modelCode: 'motor-75kw', constructeur: 'sew', customValues: { 'Puissance nominale (kW)': 75, 'Tension (V)': 400, "Indice de protection": 'IP55', }, }, { name: 'Courroie élévateur 800 mm (aval)', reference: 'BLT-800-02', typeCode: 'belt', modelCode: 'belt-hd-800', constructeur: 'agritech', customValues: { 'Largeur (mm)': 800, 'Matériau': 'Caoutchouc nitrile', }, }, { name: 'Capteur vitesse aval', reference: 'SNS-VIT-02', typeCode: 'speed-sensor', modelCode: 'sensor-speed-m12', constructeur: 'ifm', customValues: { 'Type de sortie': 'PNP 4-20 mA', 'Plage de mesure (rpm)': 1100, }, }, ], }, { name: "Convoyeur alimentation réception", reference: 'CV-ALIM-01', prix: '24500', typeCode: 'belt-conveyor', requirementLabel: "Convoyeurs d'alimentation", modelCode: 'conveyor-18m', constructeur: 'valmont', customValues: { 'Largeur de bande (mm)': 800, 'Longueur (m)': 18, 'Type de bande': 'Caoutchouc anti-statique', }, pieces: [ { name: 'Moteur convoyeur 45 kW', reference: 'MTR-45-01', prix: '6100', typeCode: 'electric-motor', modelCode: 'motor-45kw', constructeur: 'sew', customValues: { 'Puissance nominale (kW)': 45, 'Tension (V)': 400, "Indice de protection": 'IP55', }, }, { name: 'Bande transporteuse 800 mm', reference: 'BLT-800-03', typeCode: 'belt', modelCode: 'belt-hd-800', constructeur: 'valmont', customValues: { 'Largeur (mm)': 800, 'Matériau': 'Caoutchouc anti-abrasion', }, }, { name: 'Capteur vitesse convoyeur', reference: 'SNS-VIT-03', typeCode: 'speed-sensor', modelCode: 'sensor-speed-m12', constructeur: 'ifm', customValues: { 'Type de sortie': 'PNP 4-20 mA', 'Plage de mesure (rpm)': 900, }, }, ], }, { name: "Convoyeur alimentation séparateur", reference: 'CV-ALIM-02', prix: '23200', typeCode: 'belt-conveyor', requirementLabel: "Convoyeurs d'alimentation", modelCode: 'conveyor-18m', constructeur: 'valmont', customValues: { 'Largeur de bande (mm)': 650, 'Longueur (m)': 15, 'Type de bande': 'Caoutchouc anti-statique', }, pieces: [ { name: 'Moteur convoyeur 45 kW (séparateur)', reference: 'MTR-45-02', typeCode: 'electric-motor', modelCode: 'motor-45kw', constructeur: 'sew', customValues: { 'Puissance nominale (kW)': 45, 'Tension (V)': 400, "Indice de protection": 'IP55', }, }, { name: 'Bande transporteuse 650 mm', reference: 'BLT-650-01', typeCode: 'belt', modelCode: 'belt-hd-650', constructeur: 'valmont', customValues: { 'Largeur (mm)': 650, 'Matériau': 'Caoutchouc anti-abrasion', }, }, { name: 'Capteur vitesse convoyeur 2', reference: 'SNS-VIT-04', typeCode: 'speed-sensor', modelCode: 'sensor-speed-m12', constructeur: 'ifm', customValues: { 'Type de sortie': 'PNP 4-20 mA', 'Plage de mesure (rpm)': 850, }, }, ], }, { name: 'Table densimétrique TQX-120', reference: 'TBL-TQX-01', prix: '68500', typeCode: 'gravity-separator', requirementLabel: 'Table densimétrique', modelCode: 'gravity-table-tqx', constructeur: 'buhler', customValues: { 'Capacité de tri (t/h)': 120, 'Fréquence de vibration (Hz)': 45, 'Type de plateau': 'Acier perforé', }, pieces: [ { name: 'Moteur vibration 18,5 kW', reference: 'MTR-18-01', typeCode: 'electric-motor', modelCode: 'motor-18kw', constructeur: 'sew', customValues: { 'Puissance nominale (kW)': 18.5, 'Tension (V)': 400, "Indice de protection": 'IP55', }, }, { name: 'Capteur vibration table', reference: 'SNS-VIB-01', typeCode: 'speed-sensor', modelCode: 'sensor-speed-m12', constructeur: 'ifm', customValues: { 'Type de sortie': 'PNP 4-20 mA', 'Plage de mesure (rpm)': 600, }, }, ], }, { name: 'Séchoir continu CD-6', reference: 'DRY-CD6-01', prix: '148000', typeCode: 'grain-dryer', requirementLabel: 'Séchoir continu', modelCode: 'grain-dryer-cd6', constructeur: 'agridry', customValues: { 'Capacité sèche (t/h)': 60, "Nombre d'étages": 6, "Type d'énergie": 'Gaz naturel', }, pieces: [ { name: 'Ventilateur principal 45 kW', reference: 'MTR-45-03', typeCode: 'electric-motor', modelCode: 'motor-45kw', constructeur: 'sew', customValues: { 'Puissance nominale (kW)': 45, 'Tension (V)': 400, "Indice de protection": 'IP55', }, }, { name: "Sonde air chaud PT100", reference: 'TMP-PT100-01', typeCode: 'temperature-probe', modelCode: 'temp-probe-pt100', constructeur: 'ifm', customValues: { 'Plage de mesure (°C)': '0-250', 'Type de sonde': 'PT100 classe A', }, }, ], children: [ { name: 'Brûleur gaz modulant', reference: 'BRN-3MW-01', typeCode: 'burner-module', modelCode: 'burner-module-3mw', constructeur: 'agridry', customValues: { 'Puissance thermique (kW)': 2950, 'Type de carburant': 'Gaz naturel', "Système d'allumage": 'Double électrode', }, pieces: [ { name: 'Sonde sécurité flamme', reference: 'TMP-PT100-02', typeCode: 'temperature-probe', modelCode: 'temp-probe-pt100', constructeur: 'ifm', customValues: { 'Plage de mesure (°C)': '0-250', 'Type de sonde': 'PT100 classe A', }, }, ], }, { name: 'Filtre cyclone FC-12', reference: 'FLT-FC12-01', typeCode: 'dust-filter', modelCode: 'dust-filter-fc12', constructeur: 'agridry', customValues: { 'Efficacité de filtration (%)': 95, 'Type de média filtrant': 'Polyester', 'Nombre de cartouches': 6, }, pieces: [ { name: 'Cartouche filtrante G4', reference: 'FLT-G4-01', typeCode: 'air-filter', modelCode: 'filter-g4-500', constructeur: 'agridry', customValues: { 'Classe de filtration': 'G4', 'Dimensions (mm)': '500x500x50', }, }, ], }, ], }, { name: 'Vis de reprise nord', reference: 'SC-V200-01', prix: '18400', typeCode: 'screw-conveyor', requirementLabel: 'Vis de reprise', modelCode: 'screw-conveyor-v200', constructeur: 'valmont', customValues: { 'Diamètre vis (mm)': 200, 'Inclinaison (°)': 10, 'Vitesse (rpm)': 140, }, pieces: [ { name: 'Moteur vis 18,5 kW', reference: 'MTR-18-02', typeCode: 'electric-motor', modelCode: 'motor-18kw', constructeur: 'sew', customValues: { 'Puissance nominale (kW)': 18.5, 'Tension (V)': 400, "Indice de protection": 'IP55', }, }, { name: 'Palier vis nord', reference: 'BRG-UCP210-02', typeCode: 'bearing', modelCode: 'bearing-ucp210', constructeur: 'skf', customValues: { 'Référence fournisseur': 'SKF UCP210', "Type d'étanchéité": '2RS', }, }, ], }, { name: 'Vis de reprise sud', reference: 'SC-V200-02', prix: '18100', typeCode: 'screw-conveyor', requirementLabel: 'Vis de reprise', modelCode: 'screw-conveyor-v200', constructeur: 'valmont', customValues: { 'Diamètre vis (mm)': 200, 'Inclinaison (°)': 12, 'Vitesse (rpm)': 145, }, pieces: [ { name: 'Moteur vis 18,5 kW (sud)', reference: 'MTR-18-03', typeCode: 'electric-motor', modelCode: 'motor-18kw', constructeur: 'sew', customValues: { 'Puissance nominale (kW)': 18.5, 'Tension (V)': 400, "Indice de protection": 'IP55', }, }, { name: 'Palier vis sud', reference: 'BRG-UCP210-03', typeCode: 'bearing', modelCode: 'bearing-ucp210', constructeur: 'skf', customValues: { 'Référence fournisseur': 'SKF UCP210', "Type d'étanchéité": '2RS', }, }, ], }, { name: 'Benna peseuse BP-5', reference: 'BP-5000-01', prix: '39200', typeCode: 'weigh-hopper', requirementLabel: 'Benna peseuse', modelCode: 'weigh-hopper-bp5', constructeur: 'buhler', customValues: { 'Capacité de pesée (kg)': 5000, 'Précision (%)': 0.5, 'Nombre de capteurs': 4, }, pieces: [ { name: 'Capteur pesage 1', reference: 'LC-5000-01', typeCode: 'load-cell', modelCode: 'load-cell-5t', constructeur: 'ifm', customValues: { 'Capacité (kg)': 5000, 'Sensibilité (mV/V)': '2.0', }, }, { name: 'Capteur pesage 2', reference: 'LC-5000-02', typeCode: 'load-cell', modelCode: 'load-cell-5t', constructeur: 'ifm', customValues: { 'Capacité (kg)': 5000, 'Sensibilité (mV/V)': '2.0', }, }, { name: 'Capteur pesage 3', reference: 'LC-5000-03', typeCode: 'load-cell', modelCode: 'load-cell-5t', constructeur: 'ifm', customValues: { 'Capacité (kg)': 5000, 'Sensibilité (mV/V)': '2.0', }, }, { name: 'Capteur pesage 4', reference: 'LC-5000-04', typeCode: 'load-cell', modelCode: 'load-cell-5t', constructeur: 'ifm', customValues: { 'Capacité (kg)': 5000, 'Sensibilité (mV/V)': '2.0', }, }, ], }, { name: 'Armoire Schneider M340', reference: 'CTRL-M340-01', prix: '46500', typeCode: 'control-panel', requirementLabel: 'Armoire de contrôle', modelCode: 'control-panel-m340', constructeur: 'schneider', customValues: { 'Automate principal': 'Schneider Modicon M340', 'Année de mise à jour': 2023, "Indice de protection": 'IP55', }, pieces: [ { name: 'Module PLC Siemens 1512', reference: 'PLC-1512-01', typeCode: 'control-module', modelCode: 'plc-module-siemens-1512', constructeur: 'siemens', customValues: { "Nombre d'E/S": 32, 'Version firmware': 'V2.9', }, }, { name: 'Entrée vitesse ligne', reference: 'SNS-VIT-CTRL-01', typeCode: 'speed-sensor', modelCode: 'sensor-speed-m12', constructeur: 'ifm', customValues: { 'Type de sortie': 'PNP 4-20 mA', 'Plage de mesure (rpm)': 1000, }, }, ], }, { name: 'Chariot télescopique Manitou', reference: 'MLT-1040-01', prix: '73500', typeCode: 'telehandler', requirementLabel: 'Chariot télescopique', modelCode: 'telehandler-mlt1040', constructeur: 'manitou', customValues: { 'Capacité de levage (t)': 4, 'Hauteur de levage (m)': 9.6, "Type d'attache": 'Fourches FEM', }, pieces: [ { name: 'Pompe hydraulique principale', reference: 'HDP-PVH98-01', typeCode: 'hydraulic-pump', modelCode: 'hydraulic-pump-pvh98', constructeur: 'manitou', customValues: { 'Débit nominal (l/min)': 160, 'Pression max (bar)': 320, }, }, { name: 'Palier articulation flèche', reference: 'BRG-UCP210-04', typeCode: 'bearing', modelCode: 'bearing-ucp210', constructeur: 'skf', customValues: { 'Référence fournisseur': 'SKF UCP210', "Type d'étanchéité": '2RS', }, }, ], }, ]; for (const component of components) { await createComponentHierarchy(machine.id, component, componentContext); } console.log('📦 Ajout des pièces de réserve machine...'); const spareMotor = await prisma.piece.create({ data: { name: 'Moteur IE3 75 kW de secours', reference: 'MTR-75-SPARE', prix: new Prisma.Decimal('7900'), machine: { connect: { id: machine.id } }, typePiece: { connect: { id: context.pieceTypes['electric-motor'].id } }, pieceModel: { connect: { id: context.pieceModels['motor-75kw'].id } }, constructeur: { connect: { id: context.constructeurs['sew'].id } }, typeMachinePieceRequirement: { connect: { id: pieceRequirementMap.get('Moteurs de rechange')! }, }, customFieldValues: buildCustomFieldValues( context.pieceTypes['electric-motor'].customFields, { 'Puissance nominale (kW)': 75, 'Tension (V)': 400, "Indice de protection": 'IP55', }, ), }, }); await prisma.piece.create({ data: { name: 'Capteur vitesse M12 de secours', reference: 'SNS-VIT-SPARE-01', prix: new Prisma.Decimal('190'), machine: { connect: { id: machine.id } }, typePiece: { connect: { id: context.pieceTypes['speed-sensor'].id } }, pieceModel: { connect: { id: context.pieceModels['sensor-speed-m12'].id } }, constructeur: { connect: { id: context.constructeurs['ifm'].id } }, typeMachinePieceRequirement: { connect: { id: pieceRequirementMap.get('Capteurs de vitesse de secours')! }, }, customFieldValues: buildCustomFieldValues( context.pieceTypes['speed-sensor'].customFields, { 'Type de sortie': 'PNP 4-20 mA', 'Plage de mesure (rpm)': 1200, }, ), }, }); console.log( `✅ Machine créée avec ${components.length} composants et pièces critiques enregistrées.`, ); return { machine, spareMotor, }; } async function main() { try { await clearDatabaseExceptSitesAndProfiles(); const [site, constructeurs] = await Promise.all([ ensureDemoSite(), createConstructeurs(), ]); const { componentTypes, pieceTypes } = await createModelTypes(); const [pieceModels, componentModels, typeMachine] = await Promise.all([ createPieceModels(pieceTypes), createComponentModels(componentTypes), createTypeMachine(componentTypes, pieceTypes), ]); await createMachineWithComponents(site.id, typeMachine, { componentTypes, componentModels, pieceTypes, pieceModels, constructeurs, }); console.log('🎉 Données de démonstration générées avec succès.'); } catch (error) { console.error('❌ Erreur pendant la génération des données :', error); process.exitCode = 1; } finally { await prisma.$disconnect(); } } main();