import { PrismaClient, Prisma, ModelCategory } from '@prisma/client'; const prisma = new PrismaClient(); type CustomFieldSpec = { name: string; type: 'string' | 'number' | 'boolean' | 'date' | 'select'; 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[]; }; type TypeMachineDefinition = { code: string; name: string; description: string; category: string; maintenanceFrequency: string; specifications: Prisma.InputJsonValue; customFields: CustomFieldSpec[]; componentRequirements: { label: string; typeCode: string; minCount: number; maxCount?: number; required: boolean; }[]; pieceRequirements?: { label: string; typeCode: string; minCount: number; maxCount?: number; required: boolean; }[]; }; type MachineBuildSpec = { code: string; typeMachineCode: string; name: string; reference: string; prix: string; constructeurKey: string; customFieldValues: Record; components: ComponentInstance[]; sparePieces?: (ComponentPieceInstance & { requirementLabel?: string })[]; }; type TypeMachineRecord = Prisma.TypeMachineGetPayload<{ include: { customFields: true; componentRequirements: true; pieceRequirements: true; }; }>; 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: 'select', defaultValue: 'Acier galvanisé', options: ['Acier galvanisé', 'Acier inoxydable', 'Polypropylène renforcé'], }, ], }, { 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: 'select', defaultValue: 'Caoutchouc anti-statique', options: ['Caoutchouc anti-statique', 'PVC alimentaire', 'Polyuréthane grain'], }, ], }, { 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: 'select', defaultValue: 'Acier perforé', options: ['Acier perforé', 'Inox poli', 'Tôle aluminium'], }, ], }, { 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: 'select', defaultValue: 'Gaz naturel', options: ['Gaz naturel', 'Biomasse', 'Fioul léger'], }, ], }, { 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: 'Mode de vidange', type: 'select', defaultValue: 'Trappe motorisée', options: ['Trappe motorisée', 'Vis doseuse', 'Vanne guillotine'], }, ], }, { code: 'control-panel', name: 'Armoire de contrôle', description: "Armoire électrique pilotant l'ensemble de la ligne de triage.", customFields: [ { name: 'Automate principal', type: 'select', required: true, defaultValue: 'Schneider Modicon M340', options: ['Schneider Modicon M340', 'Siemens S7-1500', 'Schneider Modicon M221'], }, { name: 'Année de mise à jour', type: 'number' }, { name: "Indice de protection", type: 'select', defaultValue: 'IP55', options: ['IP55', 'IP65', 'IP54'], }, ], }, { 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: 'select', defaultValue: 'Fourches FEM', options: ['Fourches FEM', 'Godet céréales', 'Pince big-bag'], }, ], }, { 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: 'select', defaultValue: 'Gaz naturel', options: ['Gaz naturel', 'Biométhane', 'Fioul léger'], }, { name: "Système d'allumage", type: 'select', defaultValue: 'Double électrode', options: ['Double électrode', 'Brûleur pilote', 'Allumeur électronique'], }, ], }, { 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: 'select', options: ['Polyester', 'Fibre de verre', 'Polypropylène'], defaultValue: 'Polyester', }, { name: 'Nombre de cartouches', type: 'number' }, ], }, { code: 'motor-drive', name: 'Groupe moteur', description: 'Motorisation asynchrone montée sur bride pour entraînements industriels.', customFields: [ { name: 'Puissance nominale (kW)', type: 'number', required: true }, { name: 'Classe énergétique', type: 'select', defaultValue: 'IE3', options: ['IE2', 'IE3', 'IE4'], }, { name: "Indice de protection", type: 'select', defaultValue: 'IP55', options: ['IP55', 'IP65', 'IP23'], }, ], }, { code: 'gearbox-assembly', name: 'Train réducteur', description: 'Réducteur de vitesse pour entraînement lourd.', customFields: [ { name: 'Rapport de réduction', type: 'select', defaultValue: '1:28', options: ['1:28', '1:24', '1:18'], }, { name: 'Couple nominal (Nm)', type: 'number' }, { name: 'Type de montage', type: 'select', options: ['À pattes', 'À bride', 'Sur arbre'], defaultValue: 'À bride', }, ], }, { code: 'hydraulic-power-pack', name: 'Groupe hydraulique', description: "Groupe hydraulique alimentant les vérins d'un chariot télescopique.", customFields: [ { name: 'Débit nominal (l/min)', type: 'number', required: true }, { name: 'Pression max (bar)', type: 'number' }, { name: 'Type de pompe', type: 'select', options: ['Piston axial', 'Palette', 'Engrenages'], defaultValue: 'Piston axial', }, ], }, { code: 'ventilation-fan', name: 'Ventilateur process', description: "Ventilateur d'extraction pour séchage et dépoussiérage.", customFields: [ { name: 'Débit (m³/h)', type: 'number', required: true }, { name: 'Type de roue', type: 'select', options: ['Axiale', 'Centrifuge', 'Mixte'], defaultValue: 'Centrifuge', }, { name: 'Vitesse nominale (rpm)', type: 'number' }, ], }, ]; const pieceTypeDefinitions: PieceTypeDefinition[] = [ { code: 'hex-screw', name: 'Vis hexagonale', description: 'Visserie pour assemblages mécaniques.', customFields: [ { name: 'Diamètre (mm)', type: 'number', required: true }, { name: 'Longueur (mm)', type: 'number', required: true }, { name: 'Classe acier', type: 'select', options: ['8.8', '10.9', '12.9'], defaultValue: '8.8', }, ], }, { code: 'lock-washer', name: 'Rondelle Grower', description: 'Rondelle anti-desserrage.', customFields: [ { name: 'Diamètre (mm)', type: 'number', required: true }, { name: 'Finition', type: 'select', options: ['Zinguée', 'Inox', 'Noire'], defaultValue: 'Zinguée', }, ], }, { code: 'flat-gasket', name: 'Joint plat', description: 'Joint pour brides et trappes de maintenance.', customFields: [ { name: 'Matière', type: 'select', options: ['NBR', 'PTFE', 'Fibre compressée'], defaultValue: 'NBR', }, { name: 'Épaisseur (mm)', type: 'number', required: true }, ], }, { code: 'drive-belt', name: 'Courroie', description: 'Courroie transporteuse ou de transmission.', customFields: [ { name: 'Largeur (mm)', type: 'number', required: true }, { name: 'Matériau', type: 'select', options: ['Caoutchouc nitrile', 'Polyuréthane', 'PVC'], defaultValue: 'Caoutchouc nitrile', }, ], }, { code: 'roller-bearing', name: 'Roulement', description: 'Roulement à semelle ou palier.', customFields: [ { name: 'Série', type: 'select', options: ['UCP', 'UCFL', 'UCT'], defaultValue: 'UCP', }, { name: "Type d'étanchéité", type: 'select', options: ['2RS', 'ZZ', 'Ouvert'], defaultValue: '2RS', }, ], }, { code: 'filter-cartridge', name: 'Cartouche filtrante', description: 'Cartouche filtrante pour dépoussiérage.', customFields: [ { name: 'Classe de filtration', type: 'select', options: ['G4', 'F7', 'M5'], defaultValue: 'G4', }, { name: 'Dimensions (mm)', type: 'string', required: true }, ], }, { code: 'speed-sensor', name: 'Capteur de vitesse', description: 'Capteur inductif contrôlant la vitesse des organes rotatifs.', customFields: [ { name: 'Type de sortie', type: 'select', options: ['PNP 4-20 mA', 'PNP tout ou rien', 'NPN'], defaultValue: 'PNP 4-20 mA', }, { 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: 'Type de sonde', type: 'select', options: ['PT100 classe A', 'PT100 classe B', 'Thermocouple K'], defaultValue: 'PT100 classe A', }, { name: 'Plage de mesure (°C)', type: 'string', required: true }, ], }, { code: 'plc-module', name: 'Module automate', description: 'Module PLC assurant des entrées/sorties pour la ligne.', customFields: [ { name: "Nombre d'E/S", type: 'number', required: true }, { name: 'Version firmware', type: 'select', options: ['V2.8', 'V2.9', 'V3.0'], defaultValue: 'V2.9', }, ], }, { code: 'load-cell', name: 'Capteur de pesage', description: 'Capteur de pesage pour benne peseuse.', customFields: [ { name: 'Capacité (kg)', type: 'number', required: true }, { name: 'Type de connexion', type: 'select', options: ['Câble 4 fils', 'Câble 6 fils', 'Connecteur M12'], defaultValue: 'Câble 6 fils', }, ], }, { code: 'fuse-cartridge', name: 'Fusible industriel', description: 'Fusible de protection pour armoire électrique.', customFields: [ { name: 'Calibre (A)', type: 'number', required: true }, { name: 'Type de fusible', type: 'select', options: ['gG', 'aM', 'gR'], defaultValue: 'gG', }, ], }, ]; const pieceModelDefinitions: PieceModelDefinition[] = [ { code: 'screw-m10x60', name: 'Vis M10x60 10.9', description: 'Vis hexagonale classe 10.9 longueur 60 mm.', typeCode: 'hex-screw', structure: { defaultCustomFieldValues: { 'Diamètre (mm)': '10', 'Longueur (mm)': '60', 'Classe acier': '10.9', }, }, }, { code: 'screw-m12x80', name: 'Vis M12x80 8.8', description: 'Vis hexagonale galvanisée 80 mm.', typeCode: 'hex-screw', structure: { defaultCustomFieldValues: { 'Diamètre (mm)': '12', 'Longueur (mm)': '80', 'Classe acier': '8.8', }, }, }, { code: 'screw-m8x30', name: 'Vis M8x30 inox', description: 'Visserie inox pour équipements alimentaires.', typeCode: 'hex-screw', structure: { defaultCustomFieldValues: { 'Diamètre (mm)': '8', 'Longueur (mm)': '30', 'Classe acier': '8.8', }, }, }, { code: 'washer-grower-10', name: 'Rondelle Grower Ø10', description: 'Rondelle Grower zinguée diamètre 10 mm.', typeCode: 'lock-washer', structure: { defaultCustomFieldValues: { 'Diamètre (mm)': '10', 'Finition': 'Zinguée', }, }, }, { code: 'washer-grower-12', name: 'Rondelle Grower Ø12 inox', description: 'Rondelle Grower inox diamètre 12 mm.', typeCode: 'lock-washer', structure: { defaultCustomFieldValues: { 'Diamètre (mm)': '12', 'Finition': 'Inox', }, }, }, { code: 'gasket-nbr-5', name: 'Joint NBR 5 mm', description: 'Joint plat NBR épaisseur 5 mm.', typeCode: 'flat-gasket', structure: { defaultCustomFieldValues: { Matière: 'NBR', 'Épaisseur (mm)': '5', }, }, }, { code: 'gasket-ptfe-3', name: 'Joint PTFE 3 mm', description: 'Joint plat PTFE pour brides haute température.', typeCode: 'flat-gasket', structure: { defaultCustomFieldValues: { Matière: 'PTFE', 'Épaisseur (mm)': '3', }, }, }, { code: 'belt-hd-800', name: 'Courroie renforcée 800 mm', description: 'Bande transporteuse HD 800 mm.', typeCode: 'drive-belt', structure: { defaultCustomFieldValues: { 'Largeur (mm)': '800', Matériau: 'Caoutchouc nitrile', }, }, }, { code: 'belt-hd-650', name: 'Courroie renforcée 650 mm', description: 'Bande transporteuse 650 mm.', typeCode: 'drive-belt', structure: { defaultCustomFieldValues: { 'Largeur (mm)': '650', Matériau: 'Polyuréthane', }, }, }, { code: 'bearing-ucp210', name: 'Palier UCP210', description: 'Palier fonte UCP210 graissable.', typeCode: 'roller-bearing', structure: { defaultCustomFieldValues: { Série: 'UCP', "Type d'étanchéité": '2RS', }, }, }, { code: 'bearing-ucfl207', name: 'Palier UCFL207', description: 'Palier à semelle UCFL207.', typeCode: 'roller-bearing', structure: { defaultCustomFieldValues: { Série: 'UCFL', "Type d'étanchéité": 'ZZ', }, }, }, { code: 'filter-g4-500', name: 'Cartouche G4 500x500', description: 'Cartouche filtrante G4.', typeCode: 'filter-cartridge', structure: { defaultCustomFieldValues: { 'Classe de filtration': 'G4', 'Dimensions (mm)': '500x500x50', }, }, }, { code: 'filter-f7-610', name: 'Cartouche F7 610x610', description: 'Cartouche haute efficacité F7.', typeCode: 'filter-cartridge', structure: { defaultCustomFieldValues: { 'Classe de filtration': 'F7', 'Dimensions (mm)': '610x610x75', }, }, }, { 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)': '1200', }, }, }, { code: 'sensor-speed-m18', name: 'Capteur inductif M18', description: 'Capteur de vitesse M18 robuste.', typeCode: 'speed-sensor', structure: { defaultCustomFieldValues: { 'Type de sortie': 'PNP tout ou rien', 'Plage de mesure (rpm)': '900', }, }, }, { code: 'temp-probe-pt100', name: 'Sonde PT100 250 mm', description: 'Sonde PT100 pour mesure d’air chaud.', typeCode: 'temperature-probe', structure: { defaultCustomFieldValues: { 'Type de sonde': 'PT100 classe A', 'Plage de mesure (°C)': '0-250', }, }, }, { code: 'temp-probe-pt100-short', name: 'Sonde PT100 150 mm', description: 'Sonde PT100 courte pour supervision.', typeCode: 'temperature-probe', structure: { defaultCustomFieldValues: { 'Type de sonde': 'PT100 classe B', 'Plage de mesure (°C)': '0-200', }, }, }, { code: 'plc-module-s71200', name: 'Module PLC S7-1200', description: 'Module d’entrées/sorties Siemens S7-1200.', typeCode: 'plc-module', structure: { defaultCustomFieldValues: { "Nombre d'E/S": '24', 'Version firmware': 'V2.8', }, }, }, { code: 'plc-module-m340', name: 'Module PLC Modicon M340', description: 'Module entrée/sortie Modicon M340.', typeCode: 'plc-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', 'Type de connexion': 'Câble 6 fils', }, }, }, { code: 'load-cell-10t', name: 'Capteur de pesage 10 t', description: 'Capteur de pesage haute capacité.', typeCode: 'load-cell', structure: { defaultCustomFieldValues: { 'Capacité (kg)': '10000', 'Type de connexion': 'Connecteur M12', }, }, }, { code: 'fuse-gg-25a', name: 'Fusible gG 25A', description: 'Fusible gG calibre 25A.', typeCode: 'fuse-cartridge', structure: { defaultCustomFieldValues: { 'Calibre (A)': '25', 'Type de fusible': 'gG', }, }, }, { code: 'fuse-gg-40a', name: 'Fusible gG 40A', description: 'Fusible gG calibre 40A.', typeCode: 'fuse-cartridge', structure: { defaultCustomFieldValues: { 'Calibre (A)': '40', 'Type de fusible': 'gG', }, }, }, ]; const componentModelDefinitions: ComponentModelDefinition[] = [ { code: 'elevator-z400', name: 'Élévateur Z400', description: 'Élévateur à godets 120 t/h, 38 m.', typeCode: 'bucket-elevator', structure: { recommendedCustomFields: { 'Débit nominal (t/h)': '120', 'Hauteur de levage (m)': '38', 'Matériau des godets': 'Acier galvanisé', }, defaultPieces: [ { modelCode: 'belt-hd-800', role: 'Courroie élévatrice' }, { modelCode: 'screw-m12x80', role: 'Fixations tête' }, { modelCode: 'washer-grower-12', role: 'Sécurité visserie' }, ], subComponents: [ { modelCode: 'motor-drive-75', role: 'Groupe moteur' }, { modelCode: 'gearbox-flender', role: 'Train réducteur' }, ], }, }, { code: 'elevator-z320', name: 'Élévateur Z320', description: 'Élévateur à godets 95 t/h, 32 m.', typeCode: 'bucket-elevator', structure: { recommendedCustomFields: { 'Débit nominal (t/h)': '95', 'Hauteur de levage (m)': '32', 'Matériau des godets': 'Polypropylène renforcé', }, defaultPieces: [ { modelCode: 'belt-hd-650', role: 'Courroie élévatrice' }, { modelCode: 'screw-m10x60', role: 'Fixations pied' }, { modelCode: 'washer-grower-10', role: 'Sécurité pied' }, ], subComponents: [ { modelCode: 'motor-drive-55', role: 'Groupe moteur' }, { modelCode: 'gearbox-bonfiglioli', role: 'Réducteur' }, ], }, }, { code: 'conveyor-18m', name: 'Convoyeur bande 18 m', description: 'Convoyeur à bande 800 mm, 18 mètres.', typeCode: 'belt-conveyor', structure: { recommendedCustomFields: { 'Largeur de bande (mm)': '800', 'Longueur (m)': '18', 'Type de bande': 'Caoutchouc anti-statique', }, defaultPieces: [ { modelCode: 'belt-hd-800', role: 'Bande transporteuse' }, { modelCode: 'screw-m10x60', role: 'Kit boulonnerie' }, ], subComponents: [ { modelCode: 'motor-drive-45', role: 'Motorisation' }, { modelCode: 'gearbox-bonfiglioli', role: 'Réducteur' }, ], }, }, { code: 'conveyor-25m', name: 'Convoyeur bande 25 m', description: 'Convoyeur à bande 650 mm, 25 mètres.', typeCode: 'belt-conveyor', structure: { recommendedCustomFields: { 'Largeur de bande (mm)': '650', 'Longueur (m)': '25', 'Type de bande': 'PVC alimentaire', }, defaultPieces: [ { modelCode: 'belt-hd-650', role: 'Bande transporteuse' }, { modelCode: 'screw-m8x30', role: 'Kit tendeur' }, ], subComponents: [ { modelCode: 'motor-drive-37', role: 'Motorisation' }, { modelCode: 'gearbox-bonfiglioli', role: 'Réducteur' }, ], }, }, { code: 'gravity-table-tqx', name: 'Table densimétrique TQX-120', description: 'Table densimétrique haute précision 120 t/h.', typeCode: 'gravity-separator', structure: { recommendedCustomFields: { 'Capacité de tri (t/h)': '120', 'Fréquence de vibration (Hz)': '45', 'Type de plateau': 'Acier perforé', }, defaultPieces: [ { modelCode: 'sensor-speed-m12', role: 'Capteur vibration' }, { modelCode: 'screw-m8x30', role: 'Fixation plateau' }, ], subComponents: [{ modelCode: 'motor-drive-18', role: 'Moteur vibration' }], }, }, { code: 'gravity-table-compact', name: 'Table densimétrique Compact 80', description: 'Table densimétrique compacte 80 t/h.', typeCode: 'gravity-separator', structure: { recommendedCustomFields: { 'Capacité de tri (t/h)': '80', 'Fréquence de vibration (Hz)': '40', 'Type de plateau': 'Inox poli', }, defaultPieces: [ { modelCode: 'sensor-speed-m18', role: 'Capteur vibration' }, { modelCode: 'screw-m8x30', role: 'Fixation plateau' }, ], subComponents: [{ modelCode: 'motor-drive-15', role: 'Moteur vibration' }], }, }, { 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: { recommendedCustomFields: { 'Capacité sèche (t/h)': '60', "Nombre d'étages": '6', "Type d'énergie": 'Gaz naturel', }, defaultPieces: [{ modelCode: 'gasket-nbr-5', role: 'Joint trappe inspection' }], subComponents: [ { modelCode: 'burner-module-3mw', role: 'Brûleur principal' }, { modelCode: 'dust-filter-fc12', role: 'Filtre cyclone' }, { modelCode: 'ventilation-fan-axial90', role: 'Ventilateur extraction' }, ], }, }, { code: 'grain-dryer-cd4', name: 'Séchoir continu CD-4', description: 'Séchoir continu 4 étages compact.', typeCode: 'grain-dryer', structure: { recommendedCustomFields: { 'Capacité sèche (t/h)': '40', "Nombre d'étages": '4', "Type d'énergie": 'Biomasse', }, defaultPieces: [{ modelCode: 'gasket-ptfe-3', role: 'Joint trappe inspection' }], subComponents: [ { modelCode: 'burner-module-2mw', role: 'Brûleur biomasse' }, { modelCode: 'dust-filter-fc8', role: 'Filtre cyclone' }, { modelCode: 'ventilation-fan-axial60', role: 'Ventilateur extraction' }, ], }, }, { code: 'screw-conveyor-v200', name: 'Vis sans fin V200', description: 'Vis de reprise diamètre 200 mm.', typeCode: 'screw-conveyor', structure: { recommendedCustomFields: { 'Diamètre vis (mm)': '200', 'Inclinaison (°)': '12', 'Vitesse (rpm)': '140', }, defaultPieces: [ { modelCode: 'screw-m12x80', role: 'Fixations palier' }, { modelCode: 'bearing-ucp210', role: 'Paliers supports' }, ], subComponents: [{ modelCode: 'motor-drive-18', role: 'Motorisation vis' }], }, }, { code: 'screw-conveyor-v160', name: 'Vis sans fin V160', description: 'Vis de transfert diamètre 160 mm.', typeCode: 'screw-conveyor', structure: { recommendedCustomFields: { 'Diamètre vis (mm)': '160', 'Inclinaison (°)': '8', 'Vitesse (rpm)': '110', }, defaultPieces: [ { modelCode: 'screw-m10x60', role: 'Fixations palier' }, { modelCode: 'bearing-ucfl207', role: 'Paliers supports' }, ], subComponents: [{ modelCode: 'motor-drive-15', role: 'Motorisation vis' }], }, }, { code: 'weigh-hopper-bp5', name: 'Benna peseuse BP-5', description: 'Benna peseuse 5 tonnes pour chargement camion.', typeCode: 'weigh-hopper', structure: { recommendedCustomFields: { 'Capacité de pesée (kg)': '5000', 'Précision (%)': '0.5', 'Mode de vidange': 'Trappe motorisée', }, defaultPieces: [ { modelCode: 'load-cell-5t', role: 'Capteurs de pesage' }, { modelCode: 'screw-m10x60', role: 'Kit fixation capteur' }, ], }, }, { code: 'weigh-hopper-bp3', name: 'Benna peseuse BP-3', description: 'Benna peseuse 3 tonnes pour chargement big-bags.', typeCode: 'weigh-hopper', structure: { recommendedCustomFields: { 'Capacité de pesée (kg)': '3000', 'Précision (%)': '0.3', 'Mode de vidange': 'Vis doseuse', }, defaultPieces: [ { modelCode: 'load-cell-5t', role: 'Capteurs de pesage' }, { modelCode: 'screw-m8x30', role: 'Kit fixation capteur' }, ], }, }, { code: 'control-panel-m340', name: 'Armoire Schneider M340', description: 'Armoire Schneider Electric avec automate M340.', typeCode: 'control-panel', structure: { recommendedCustomFields: { 'Automate principal': 'Schneider Modicon M340', 'Année de mise à jour': '2023', "Indice de protection": 'IP55', }, defaultPieces: [ { modelCode: 'plc-module-m340', role: 'Automate principal' }, { modelCode: 'fuse-gg-25a', role: 'Protection alimentation' }, { modelCode: 'sensor-speed-m12', role: 'Entrée vitesse ligne' }, ], }, }, { code: 'control-panel-s7', name: 'Armoire Siemens S7-1500', description: 'Armoire Siemens S7-1500 pour ligne secondaire.', typeCode: 'control-panel', structure: { recommendedCustomFields: { 'Automate principal': 'Siemens S7-1500', 'Année de mise à jour': '2022', "Indice de protection": 'IP65', }, defaultPieces: [ { modelCode: 'plc-module-s71200', role: 'Module E/S' }, { modelCode: 'fuse-gg-40a', role: 'Protection puissance' }, ], }, }, { code: 'burner-module-3mw', name: 'Brûleur gaz 3 MW', description: 'Brûleur gaz modulant 3 MW pour séchoir.', typeCode: 'burner-module', structure: { recommendedCustomFields: { 'Puissance thermique (kW)': '3000', 'Type de carburant': 'Gaz naturel', "Système d'allumage": 'Double électrode', }, defaultPieces: [{ modelCode: 'temp-probe-pt100', role: 'Sonde sécurité flamme' }], }, }, { code: 'burner-module-2mw', name: 'Brûleur biomasse 2 MW', description: 'Brûleur biomasse 2 MW pour séchoir compact.', typeCode: 'burner-module', structure: { recommendedCustomFields: { 'Puissance thermique (kW)': '2000', 'Type de carburant': 'Biomasse', "Système d'allumage": 'Brûleur pilote', }, defaultPieces: [{ modelCode: 'temp-probe-pt100-short', role: 'Sonde sécurité flamme' }], }, }, { code: 'dust-filter-fc12', name: 'Filtre cyclone FC-12', description: 'Filtre cyclone avec cartouches G4.', typeCode: 'dust-filter', structure: { recommendedCustomFields: { 'Efficacité de filtration (%)': '95', 'Type de média filtrant': 'Polyester', 'Nombre de cartouches': '6', }, defaultPieces: [{ modelCode: 'filter-g4-500', role: 'Cartouche filtrante' }], }, }, { code: 'dust-filter-fc8', name: 'Filtre cyclone FC-8', description: 'Filtre cyclone compact avec cartouches F7.', typeCode: 'dust-filter', structure: { recommendedCustomFields: { 'Efficacité de filtration (%)': '90', 'Type de média filtrant': 'Fibre de verre', 'Nombre de cartouches': '4', }, defaultPieces: [{ modelCode: 'filter-f7-610', role: 'Cartouche filtrante' }], }, }, { code: 'telehandler-mlt1040', name: 'Manitou MLT 1040', description: 'Chariot télescopique Manitou 4 t.', typeCode: 'telehandler', structure: { recommendedCustomFields: { 'Capacité de levage (t)': '4', 'Hauteur de levage (m)': '9.6', "Type d'attache": 'Fourches FEM', }, subComponents: [ { modelCode: 'motor-drive-75', role: 'Moteur principal' }, { modelCode: 'hydraulic-pack-pvh98', role: 'Groupe hydraulique' }, ], defaultPieces: [ { modelCode: 'gasket-nbr-5', role: 'Joint clapet' }, { modelCode: 'fuse-gg-40a', role: 'Protection cabine' }, ], }, }, { code: 'telehandler-mlt840', name: 'Manitou MLT 840', description: 'Chariot télescopique Manitou 3,5 t.', typeCode: 'telehandler', structure: { recommendedCustomFields: { 'Capacité de levage (t)': '3.5', 'Hauteur de levage (m)': '7.5', "Type d'attache": 'Godet céréales', }, subComponents: [ { modelCode: 'motor-drive-55', role: 'Moteur principal' }, { modelCode: 'hydraulic-pack-vg80', role: 'Groupe hydraulique' }, ], defaultPieces: [{ modelCode: 'fuse-gg-25a', role: 'Protection cabine' }], }, }, { code: 'motor-drive-75', name: 'Groupe moteur IE3 75 kW', description: 'Moteur IE3 75 kW montage à bride.', typeCode: 'motor-drive', structure: { recommendedCustomFields: { 'Puissance nominale (kW)': '75', 'Classe énergétique': 'IE3', "Indice de protection": 'IP55', }, defaultPieces: [ { modelCode: 'screw-m12x80', role: 'Fixations bride' }, { modelCode: 'washer-grower-12', role: 'Sécurité bride' }, ], }, }, { code: 'motor-drive-55', name: 'Groupe moteur IE3 55 kW', description: 'Moteur IE3 55 kW.', typeCode: 'motor-drive', structure: { recommendedCustomFields: { 'Puissance nominale (kW)': '55', 'Classe énergétique': 'IE3', "Indice de protection": 'IP55', }, defaultPieces: [ { modelCode: 'screw-m12x80', role: 'Fixations bride' }, { modelCode: 'washer-grower-12', role: 'Sécurité bride' }, ], }, }, { code: 'motor-drive-45', name: 'Groupe moteur IE3 45 kW', description: 'Moteur IE3 45 kW pour convoyeurs.', typeCode: 'motor-drive', structure: { recommendedCustomFields: { 'Puissance nominale (kW)': '45', 'Classe énergétique': 'IE3', "Indice de protection": 'IP55', }, defaultPieces: [ { modelCode: 'screw-m10x60', role: 'Fixations bride' }, { modelCode: 'washer-grower-10', role: 'Sécurité bride' }, ], }, }, { code: 'motor-drive-37', name: 'Groupe moteur IE3 37 kW', description: 'Moteur IE3 37 kW pour convoyeur long.', typeCode: 'motor-drive', structure: { recommendedCustomFields: { 'Puissance nominale (kW)': '37', 'Classe énergétique': 'IE3', "Indice de protection": 'IP55', }, defaultPieces: [ { modelCode: 'screw-m10x60', role: 'Fixations bride' }, { modelCode: 'washer-grower-10', role: 'Sécurité bride' }, ], }, }, { code: 'motor-drive-18', name: 'Groupe moteur IE3 18,5 kW', description: 'Moteur IE3 pour vis sans fin.', typeCode: 'motor-drive', structure: { recommendedCustomFields: { 'Puissance nominale (kW)': '18.5', 'Classe énergétique': 'IE3', "Indice de protection": 'IP55', }, defaultPieces: [ { modelCode: 'screw-m10x60', role: 'Fixations bride' }, { modelCode: 'washer-grower-10', role: 'Sécurité bride' }, ], }, }, { code: 'motor-drive-15', name: 'Groupe moteur IE3 15 kW', description: 'Moteur IE3 pour équipements auxiliaires.', typeCode: 'motor-drive', structure: { recommendedCustomFields: { 'Puissance nominale (kW)': '15', 'Classe énergétique': 'IE3', "Indice de protection": 'IP55', }, defaultPieces: [ { modelCode: 'screw-m8x30', role: 'Fixations bride' }, { modelCode: 'washer-grower-10', role: 'Sécurité bride' }, ], }, }, { code: 'gearbox-flender', name: 'Réducteur Flender FZ', description: 'Réducteur coaxial pour charge lourde.', typeCode: 'gearbox-assembly', structure: { recommendedCustomFields: { 'Rapport de réduction': '1:28', 'Couple nominal (Nm)': '3200', 'Type de montage': 'À bride', }, defaultPieces: [{ modelCode: 'gasket-nbr-5', role: 'Joint de bride' }], }, }, { code: 'gearbox-bonfiglioli', name: 'Réducteur Bonfiglioli TA', description: 'Réducteur orthogonal Bonfiglioli.', typeCode: 'gearbox-assembly', structure: { recommendedCustomFields: { 'Rapport de réduction': '1:24', 'Couple nominal (Nm)': '2100', 'Type de montage': 'Sur arbre', }, defaultPieces: [{ modelCode: 'gasket-ptfe-3', role: 'Joint de bride' }], }, }, { code: 'hydraulic-pack-pvh98', name: 'Groupe hydraulique PVH98', description: 'Groupe hydraulique 160 l/min.', typeCode: 'hydraulic-power-pack', structure: { recommendedCustomFields: { 'Débit nominal (l/min)': '160', 'Pression max (bar)': '320', 'Type de pompe': 'Piston axial', }, defaultPieces: [{ modelCode: 'gasket-nbr-5', role: 'Joint collecteur' }], }, }, { code: 'hydraulic-pack-vg80', name: 'Groupe hydraulique VG80', description: 'Groupe hydraulique 110 l/min.', typeCode: 'hydraulic-power-pack', structure: { recommendedCustomFields: { 'Débit nominal (l/min)': '110', 'Pression max (bar)': '260', 'Type de pompe': 'Engrenages', }, defaultPieces: [{ modelCode: 'gasket-ptfe-3', role: 'Joint collecteur' }], }, }, { code: 'ventilation-fan-axial90', name: 'Ventilateur axial 90 kW', description: 'Ventilateur axial 90 kW.', typeCode: 'ventilation-fan', structure: { recommendedCustomFields: { 'Débit (m³/h)': '48000', 'Type de roue': 'Axiale', 'Vitesse nominale (rpm)': '980', }, defaultPieces: [{ modelCode: 'screw-m12x80', role: 'Fixations châssis' }], }, }, { code: 'ventilation-fan-axial60', name: 'Ventilateur axial 60 kW', description: 'Ventilateur axial 60 kW compact.', typeCode: 'ventilation-fan', structure: { recommendedCustomFields: { 'Débit (m³/h)': '32000', 'Type de roue': 'Axiale', 'Vitesse nominale (rpm)': '1450', }, defaultPieces: [{ modelCode: 'screw-m10x60', role: 'Fixations châssis' }], }, }, ]; 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', }, { key: 'bonfiglioli', name: 'Bonfiglioli Riduttori', email: 'sales@bonfiglioli.com', phone: '+39 051 647 3111', }, { key: 'agrifan', name: 'AgriFan Ventilation', email: 'contact@agrifan.fr', phone: '+33 2 44 55 12 32', }, { key: 'poclain', name: 'Poclain Hydraulics', email: 'support@poclain-hydraulics.com', phone: '+33 3 44 31 74 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 }[]) { 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; } const typeMachineDefinitions: TypeMachineDefinition[] = [ { code: 'triage-line', name: 'Ligne de triage céréales 120 t/h', description: 'Chaîne automatisée de réception, triage, ventilation et expédition des céréales.', category: 'Triage et stockage', maintenanceFrequency: 'Inspection quotidienne, graissage hebdomadaire, révision trimestrielle.', specifications: { nominalThroughput: 120, shiftsPerDay: 2, building: 'Hall principal', }, customFields: [ { name: 'Capacité nominale (t/h)', type: 'number', required: true }, { name: 'Produit traité', type: 'select', required: true, defaultValue: 'Blé tendre', options: ['Blé tendre', 'Orge brassicole', 'Maïs grain', 'Pois protéagineux'], }, { name: 'Date de mise en service', type: 'date', required: true }, { name: 'Responsable de ligne', type: 'select', defaultValue: 'Lucie Bernard', options: ['Lucie Bernard', 'Karim Dubois', 'Anaëlle Petit'], }, ], componentRequirements: [ { label: 'Élévateurs principaux', typeCode: 'bucket-elevator', minCount: 2, maxCount: 2, required: true, }, { label: "Convoyeurs d'alimentation", typeCode: 'belt-conveyor', minCount: 2, required: true, }, { label: 'Table densimétrique', typeCode: 'gravity-separator', minCount: 1, maxCount: 1, required: true, }, { label: 'Vis de reprise', typeCode: 'screw-conveyor', minCount: 2, required: true, }, { label: 'Poste de pesage', typeCode: 'weigh-hopper', minCount: 1, maxCount: 1, required: true, }, { label: 'Armoire de contrôle', typeCode: 'control-panel', minCount: 1, maxCount: 1, required: true, }, { label: 'Ventilation', typeCode: 'ventilation-fan', minCount: 1, required: true, }, ], pieceRequirements: [ { label: 'Kit visserie critique', typeCode: 'hex-screw', minCount: 1, required: false, }, { label: 'Capteurs de vitesse de secours', typeCode: 'speed-sensor', minCount: 2, required: false, }, { label: 'Cartouches de filtration', typeCode: 'filter-cartridge', minCount: 2, required: false, }, ], }, { code: 'grain-dryer-station', name: 'Station de séchage et dépoussiérage', description: 'Poste de séchage haute capacité avec filtration et reprise.', category: 'Séchage', maintenanceFrequency: 'Contrôle brûleur hebdomadaire, nettoyage cyclone mensuel.', specifications: { nominalThroughput: 60, building: 'Tour de séchage', nightOperation: true, }, customFields: [ { name: 'Produit séché', type: 'select', defaultValue: 'Maïs grain', options: ['Maïs grain', 'Blé dur', 'Tournesol'], required: true, }, { name: 'Mode d’alimentation', type: 'select', options: ['Élévateur principal', 'Vis de reprise', 'Chargement manuel'], defaultValue: 'Élévateur principal', }, { name: 'Date de mise en service', type: 'date', required: true }, ], componentRequirements: [ { label: 'Séchoir continu', typeCode: 'grain-dryer', minCount: 1, maxCount: 1, required: true, }, { label: 'Module brûleur', typeCode: 'burner-module', minCount: 1, required: true, }, { label: 'Filtration poussières', typeCode: 'dust-filter', minCount: 1, required: true, }, { label: 'Ventilation process', typeCode: 'ventilation-fan', minCount: 1, required: true, }, { label: 'Vis de reprise', typeCode: 'screw-conveyor', minCount: 1, required: true, }, { label: 'Armoire de supervision', typeCode: 'control-panel', minCount: 1, required: true, }, ], pieceRequirements: [ { label: 'Joints haute température', typeCode: 'flat-gasket', minCount: 2, required: false, }, { label: 'Sondes de température de secours', typeCode: 'temperature-probe', minCount: 2, required: false, }, ], }, { code: 'telehandler-support', name: 'Chariot télescopique logistique', description: 'Chariot télescopique dédié aux manutentions céréalières.', category: 'Logistique', maintenanceFrequency: 'Graissage hebdomadaire, contrôle hydraulique mensuel.', specifications: { yardArea: 'Cour logistique', rotation: '3 équipes', }, customFields: [ { name: 'Usage principal', type: 'select', options: ['Chargement camions', 'Gestion big-bags', 'Maintenance silo'], defaultValue: 'Chargement camions', }, { name: 'Lieu de stationnement', type: 'select', options: ['Hangar nord', 'Cour extérieure', 'Atelier maintenance'], defaultValue: 'Hangar nord', }, { name: "Année d'achat", type: 'number', required: true }, ], componentRequirements: [ { label: 'Châssis télescopique', typeCode: 'telehandler', minCount: 1, maxCount: 1, required: true, }, { label: 'Groupe moteur', typeCode: 'motor-drive', minCount: 1, required: true, }, { label: 'Groupe hydraulique', typeCode: 'hydraulic-power-pack', minCount: 1, required: true, }, { label: 'Armoire de contrôle cabine', typeCode: 'control-panel', minCount: 1, required: false, }, ], pieceRequirements: [ { label: 'Fusibles cabine', typeCode: 'fuse-cartridge', minCount: 1, required: false, }, { label: 'Joints hydrauliques de secours', typeCode: 'flat-gasket', minCount: 1, required: false, }, ], }, ]; async function createTypeMachines( componentTypes: Record, pieceTypes: Record, ) { console.log('🧬 Création des squelettes de machines...'); const entries = await Promise.all( typeMachineDefinitions.map(async (definition) => { const record = await prisma.typeMachine.create({ data: { name: definition.name, description: definition.description, category: definition.category, maintenanceFrequency: definition.maintenanceFrequency, specifications: definition.specifications, components: { layout: definition.componentRequirements.map((requirement, index) => ({ order: index + 1, zone: requirement.label, type: requirement.typeCode, })), }, machinePieces: { recommendedStock: [], }, customFields: { create: definition.customFields.map((field) => ({ name: field.name, type: field.type, required: field.required ?? false, defaultValue: field.defaultValue, options: field.options ?? [], })), }, componentRequirements: { create: definition.componentRequirements.map((requirement) => ({ label: requirement.label, minCount: requirement.minCount, maxCount: requirement.maxCount, required: requirement.required, typeComposant: { connect: { id: componentTypes[requirement.typeCode].id }, }, })), }, pieceRequirements: definition.pieceRequirements ? { create: definition.pieceRequirements.map((requirement) => ({ label: requirement.label, minCount: requirement.minCount, maxCount: requirement.maxCount, required: requirement.required, typePiece: { connect: { id: pieceTypes[requirement.typeCode].id }, }, })), } : undefined, }, include: { customFields: true, componentRequirements: true, pieceRequirements: true, }, }); return [definition.code, record] as const; }), ); return Object.fromEntries(entries) as Record; } 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; } const machineBuilds: MachineBuildSpec[] = [ { code: 'triage-line-main', typeMachineCode: 'triage-line', name: 'Ligne de triage Valgrain 2024', reference: 'VT-TRI-2024', prix: '725000', constructeurKey: 'buhler', customFieldValues: { 'Capacité nominale (t/h)': 120, 'Produit traité': 'Blé tendre', 'Date de mise en service': new Date('2024-02-01'), 'Responsable de ligne': 'Lucie Bernard', }, components: [ { 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: 'Courroie élévateur 800 mm', reference: 'BLT-800-01', prix: '2300', typeCode: 'drive-belt', modelCode: 'belt-hd-800', constructeur: 'agritech', customValues: { 'Largeur (mm)': 800, Matériau: 'Caoutchouc nitrile', }, }, { name: 'Kit visserie tête élévateur', reference: 'KIT-VIS-EL-01', typeCode: 'hex-screw', modelCode: 'screw-m12x80', customValues: { 'Diamètre (mm)': 12, 'Longueur (mm)': 80, 'Classe acier': '8.8', }, }, { name: 'Rondelles Grower Ø12', reference: 'RW-12-EL-01', typeCode: 'lock-washer', modelCode: 'washer-grower-12', customValues: { 'Diamètre (mm)': 12, Finition: 'Inox', }, }, ], children: [ { name: 'Moteur élévateur 75 kW', reference: 'MTR-75-01', typeCode: 'motor-drive', modelCode: 'motor-drive-75', constructeur: 'sew', customValues: { 'Puissance nominale (kW)': 75, 'Classe énergétique': 'IE3', "Indice de protection": 'IP55', }, }, { name: 'Réducteur tête Flender', reference: 'GBX-FZ-01', typeCode: 'gearbox-assembly', modelCode: 'gearbox-flender', constructeur: 'flender', customValues: { 'Rapport de réduction': '1:28', 'Couple nominal (Nm)': 3200, 'Type de montage': 'À bride', }, }, ], }, { name: 'Élévateur aval Z320', reference: 'BE-Z320-02', prix: '49700', typeCode: 'bucket-elevator', requirementLabel: 'Élévateurs principaux', modelCode: 'elevator-z320', constructeur: 'agritech', customValues: { 'Débit nominal (t/h)': 95, 'Hauteur de levage (m)': 32, 'Matériau des godets': 'Polypropylène renforcé', }, pieces: [ { name: 'Courroie élévateur 650 mm', reference: 'BLT-650-02', typeCode: 'drive-belt', modelCode: 'belt-hd-650', customValues: { 'Largeur (mm)': 650, Matériau: 'Polyuréthane', }, }, { name: 'Kit visserie pied élévateur', reference: 'KIT-VIS-EL-02', typeCode: 'hex-screw', modelCode: 'screw-m10x60', customValues: { 'Diamètre (mm)': 10, 'Longueur (mm)': 60, 'Classe acier': '10.9', }, }, { name: 'Rondelles Grower Ø10', reference: 'RW-10-EL-02', typeCode: 'lock-washer', modelCode: 'washer-grower-10', customValues: { 'Diamètre (mm)': 10, Finition: 'Zinguée', }, }, ], children: [ { name: 'Moteur élévateur 55 kW', reference: 'MTR-55-02', typeCode: 'motor-drive', modelCode: 'motor-drive-55', constructeur: 'sew', customValues: { 'Puissance nominale (kW)': 55, 'Classe énergétique': 'IE3', "Indice de protection": 'IP55', }, }, { name: 'Réducteur Bonfiglioli TA', reference: 'GBX-TA-02', typeCode: 'gearbox-assembly', modelCode: 'gearbox-bonfiglioli', constructeur: 'bonfiglioli', customValues: { 'Rapport de réduction': '1:24', 'Couple nominal (Nm)': 2100, 'Type de montage': 'Sur arbre', }, }, ], }, { name: "Convoyeur d'alimentation A", reference: 'CV-18-01', prix: '36200', 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: 'Bande transporteuse 800 mm', reference: 'BAND-800-A', typeCode: 'drive-belt', modelCode: 'belt-hd-800', customValues: { 'Largeur (mm)': 800, Matériau: 'Caoutchouc nitrile', }, }, { name: 'Kit tendeur M10', reference: 'KIT-TEN-01', typeCode: 'hex-screw', modelCode: 'screw-m10x60', customValues: { 'Diamètre (mm)': 10, 'Longueur (mm)': 60, 'Classe acier': '10.9', }, }, ], children: [ { name: 'Motorisation convoyeur A', reference: 'MTR-45-01', typeCode: 'motor-drive', modelCode: 'motor-drive-45', constructeur: 'sew', customValues: { 'Puissance nominale (kW)': 45, 'Classe énergétique': 'IE3', "Indice de protection": 'IP55', }, }, { name: 'Réducteur convoyeur A', reference: 'GBX-TA-03', typeCode: 'gearbox-assembly', modelCode: 'gearbox-bonfiglioli', constructeur: 'bonfiglioli', customValues: { 'Rapport de réduction': '1:24', 'Couple nominal (Nm)': 2100, 'Type de montage': 'Sur arbre', }, }, ], }, { name: "Convoyeur d'expédition B", reference: 'CV-25-02', prix: '41800', typeCode: 'belt-conveyor', requirementLabel: "Convoyeurs d'alimentation", modelCode: 'conveyor-25m', constructeur: 'valmont', customValues: { 'Largeur de bande (mm)': 650, 'Longueur (m)': 25, 'Type de bande': 'PVC alimentaire', }, pieces: [ { name: 'Bande transporteuse 650 mm', reference: 'BAND-650-B', typeCode: 'drive-belt', modelCode: 'belt-hd-650', customValues: { 'Largeur (mm)': 650, Matériau: 'Polyuréthane', }, }, { name: 'Kit visserie tendeur inox', reference: 'KIT-TEN-02', typeCode: 'hex-screw', modelCode: 'screw-m8x30', customValues: { 'Diamètre (mm)': 8, 'Longueur (mm)': 30, 'Classe acier': '8.8', }, }, ], children: [ { name: 'Motorisation convoyeur B', reference: 'MTR-37-02', typeCode: 'motor-drive', modelCode: 'motor-drive-37', constructeur: 'sew', customValues: { 'Puissance nominale (kW)': 37, 'Classe énergétique': 'IE3', "Indice de protection": 'IP55', }, }, { name: 'Réducteur convoyeur B', reference: 'GBX-TA-04', typeCode: 'gearbox-assembly', modelCode: 'gearbox-bonfiglioli', constructeur: 'bonfiglioli', customValues: { 'Rapport de réduction': '1:24', 'Couple nominal (Nm)': 2100, 'Type de montage': 'Sur arbre', }, }, ], }, { name: 'Table densimétrique TQX', 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: '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)': 1200, }, }, { name: 'Kit visserie plateau', reference: 'KIT-VIS-PL-01', typeCode: 'hex-screw', modelCode: 'screw-m8x30', customValues: { 'Diamètre (mm)': 8, 'Longueur (mm)': 30, 'Classe acier': '8.8', }, }, ], children: [ { name: 'Moteur vibration 18,5 kW', reference: 'MTR-18-01', typeCode: 'motor-drive', modelCode: 'motor-drive-18', constructeur: 'sew', customValues: { 'Puissance nominale (kW)': 18.5, 'Classe énergétique': 'IE3', "Indice de protection": 'IP55', }, }, ], }, { name: 'Vis de reprise nord', reference: 'SC-V200-N', prix: '27400', typeCode: 'screw-conveyor', requirementLabel: 'Vis de reprise', modelCode: 'screw-conveyor-v200', constructeur: 'valmont', customValues: { 'Diamètre vis (mm)': 200, 'Inclinaison (°)': 12, 'Vitesse (rpm)': 140, }, pieces: [ { name: 'Palier UCP 210', reference: 'BRG-UCP210-01', typeCode: 'roller-bearing', modelCode: 'bearing-ucp210', constructeur: 'skf', customValues: { Série: 'UCP', "Type d'étanchéité": '2RS', }, }, { name: 'Kit visserie palier', reference: 'KIT-VIS-PA-01', typeCode: 'hex-screw', modelCode: 'screw-m12x80', customValues: { 'Diamètre (mm)': 12, 'Longueur (mm)': 80, 'Classe acier': '8.8', }, }, ], children: [ { name: 'Motorisation vis nord', reference: 'MTR-18-02', typeCode: 'motor-drive', modelCode: 'motor-drive-18', constructeur: 'sew', customValues: { 'Puissance nominale (kW)': 18.5, 'Classe énergétique': 'IE3', "Indice de protection": 'IP55', }, }, ], }, { name: 'Vis de reprise sud', reference: 'SC-V160-S', prix: '23600', typeCode: 'screw-conveyor', requirementLabel: 'Vis de reprise', modelCode: 'screw-conveyor-v160', constructeur: 'valmont', customValues: { 'Diamètre vis (mm)': 160, 'Inclinaison (°)': 8, 'Vitesse (rpm)': 110, }, pieces: [ { name: 'Palier UCFL207', reference: 'BRG-UCFL207-01', typeCode: 'roller-bearing', modelCode: 'bearing-ucfl207', constructeur: 'skf', customValues: { Série: 'UCFL', "Type d'étanchéité": 'ZZ', }, }, { name: 'Kit visserie palier UCFL', reference: 'KIT-VIS-PA-02', typeCode: 'hex-screw', modelCode: 'screw-m10x60', customValues: { 'Diamètre (mm)': 10, 'Longueur (mm)': 60, 'Classe acier': '10.9', }, }, ], children: [ { name: 'Motorisation vis sud', reference: 'MTR-15-01', typeCode: 'motor-drive', modelCode: 'motor-drive-15', constructeur: 'sew', customValues: { 'Puissance nominale (kW)': 15, 'Classe énergétique': 'IE3', "Indice de protection": 'IP55', }, }, ], }, { name: 'Poste de pesage BP-5', reference: 'BP-5000-01', prix: '39200', typeCode: 'weigh-hopper', requirementLabel: 'Poste de pesage', modelCode: 'weigh-hopper-bp5', constructeur: 'buhler', customValues: { 'Capacité de pesée (kg)': 5000, 'Précision (%)': 0.5, 'Mode de vidange': 'Trappe motorisée', }, pieces: [ { name: 'Capteur pesage 1', reference: 'LC-5000-01', typeCode: 'load-cell', modelCode: 'load-cell-5t', constructeur: 'ifm', customValues: { 'Capacité (kg)': 5000, 'Type de connexion': 'Câble 6 fils', }, }, { name: 'Capteur pesage 2', reference: 'LC-5000-02', typeCode: 'load-cell', modelCode: 'load-cell-5t', constructeur: 'ifm', customValues: { 'Capacité (kg)': 5000, 'Type de connexion': 'Câble 6 fils', }, }, { name: 'Capteur pesage 3', reference: 'LC-5000-03', typeCode: 'load-cell', modelCode: 'load-cell-5t', constructeur: 'ifm', customValues: { 'Capacité (kg)': 5000, 'Type de connexion': 'Câble 6 fils', }, }, { name: 'Capteur pesage 4', reference: 'LC-5000-04', typeCode: 'load-cell', modelCode: 'load-cell-5t', constructeur: 'ifm', customValues: { 'Capacité (kg)': 5000, 'Type de connexion': 'Câble 6 fils', }, }, ], }, { 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 M340', reference: 'PLC-M340-01', typeCode: 'plc-module', modelCode: 'plc-module-m340', constructeur: 'schneider', customValues: { "Nombre d'E/S": 32, 'Version firmware': 'V2.9', }, }, { name: 'Fusible alimentation 25A', reference: 'FUS-25A-01', typeCode: 'fuse-cartridge', modelCode: 'fuse-gg-25a', constructeur: 'schneider', customValues: { 'Calibre (A)': 25, 'Type de fusible': 'gG', }, }, { 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: "Ventilateur d'extraction principal", reference: 'FAN-AX-90', prix: '32500', typeCode: 'ventilation-fan', requirementLabel: 'Ventilation', modelCode: 'ventilation-fan-axial90', constructeur: 'agrifan', customValues: { 'Débit (m³/h)': 48000, 'Type de roue': 'Axiale', 'Vitesse nominale (rpm)': 980, }, pieces: [ { name: 'Kit fixation ventilateur', reference: 'KIT-FAN-01', typeCode: 'hex-screw', modelCode: 'screw-m12x80', customValues: { 'Diamètre (mm)': 12, 'Longueur (mm)': 80, 'Classe acier': '8.8', }, }, ], }, ], sparePieces: [ { requirementLabel: 'Kit visserie critique', name: 'Kit visserie M12 galvanisé', reference: 'KIT-VIS-M12', prix: '180', typeCode: 'hex-screw', modelCode: 'screw-m12x80', constructeur: 'agritech', customValues: { 'Diamètre (mm)': 12, 'Longueur (mm)': 80, 'Classe acier': '8.8', }, }, { requirementLabel: 'Capteurs de vitesse de secours', name: 'Capteur vitesse M12 de secours', reference: 'SNS-VIT-SPARE-01', prix: '190', typeCode: 'speed-sensor', modelCode: 'sensor-speed-m12', constructeur: 'ifm', customValues: { 'Type de sortie': 'PNP 4-20 mA', 'Plage de mesure (rpm)': 1200, }, }, { requirementLabel: 'Cartouches de filtration', name: 'Cartouche G4 de rechange', reference: 'FLT-G4-SPARE', prix: '140', typeCode: 'filter-cartridge', modelCode: 'filter-g4-500', constructeur: 'agrifan', customValues: { 'Classe de filtration': 'G4', 'Dimensions (mm)': '500x500x50', }, }, ], }, { code: 'dryer-station', typeMachineCode: 'grain-dryer-station', name: 'Poste de séchage CD-6', reference: 'VT-DRY-01', prix: '512000', constructeurKey: 'agridry', customFieldValues: { 'Produit séché': 'Maïs grain', 'Mode d’alimentation': 'Élévateur principal', 'Date de mise en service': new Date('2023-09-15'), }, components: [ { name: 'Séchoir continu CD-6', reference: 'DRY-CD6-01', prix: '265000', 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: 'Joint trappe inspection', reference: 'JNT-DRY-01', typeCode: 'flat-gasket', modelCode: 'gasket-nbr-5', customValues: { Matière: 'NBR', 'Épaisseur (mm)': 5, }, }, ], children: [ { name: 'Brûleur gaz principal', reference: 'BRN-3MW-01', typeCode: 'burner-module', requirementLabel: 'Module brûleur', modelCode: 'burner-module-3mw', constructeur: 'agridry', customValues: { 'Puissance thermique (kW)': 3000, 'Type de carburant': 'Gaz naturel', "Système d'allumage": 'Double électrode', }, pieces: [ { name: 'Sonde flamme PT100', reference: 'TMP-SEC-01', typeCode: 'temperature-probe', modelCode: 'temp-probe-pt100', constructeur: 'ifm', customValues: { 'Type de sonde': 'PT100 classe A', 'Plage de mesure (°C)': '0-250', }, }, ], }, { name: 'Filtre cyclone FC-12', reference: 'FIL-FC12-01', typeCode: 'dust-filter', requirementLabel: 'Filtration poussières', modelCode: 'dust-filter-fc12', constructeur: 'agrifan', 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: 'filter-cartridge', modelCode: 'filter-g4-500', constructeur: 'agrifan', customValues: { 'Classe de filtration': 'G4', 'Dimensions (mm)': '500x500x50', }, }, ], }, ], }, { name: 'Ventilateur extraction séchoir', reference: 'FAN-DRY-01', prix: '43500', typeCode: 'ventilation-fan', requirementLabel: 'Ventilation process', modelCode: 'ventilation-fan-axial90', constructeur: 'agrifan', customValues: { 'Débit (m³/h)': 48000, 'Type de roue': 'Axiale', 'Vitesse nominale (rpm)': 980, }, pieces: [ { name: 'Kit fixation ventilateur séchoir', reference: 'KIT-FAN-DRY', typeCode: 'hex-screw', modelCode: 'screw-m12x80', customValues: { 'Diamètre (mm)': 12, 'Longueur (mm)': 80, 'Classe acier': '8.8', }, }, ], }, { name: 'Vis de reprise séchoir', reference: 'SC-DRY-01', prix: '24800', typeCode: 'screw-conveyor', requirementLabel: 'Vis de reprise', modelCode: 'screw-conveyor-v200', constructeur: 'valmont', customValues: { 'Diamètre vis (mm)': 200, 'Inclinaison (°)': 10, 'Vitesse (rpm)': 130, }, pieces: [ { name: 'Palier UCP 210', reference: 'BRG-UCP210-05', typeCode: 'roller-bearing', modelCode: 'bearing-ucp210', constructeur: 'skf', customValues: { Série: 'UCP', "Type d'étanchéité": '2RS', }, }, ], children: [ { name: 'Motorisation vis séchoir', reference: 'MTR-18-05', typeCode: 'motor-drive', modelCode: 'motor-drive-18', constructeur: 'sew', customValues: { 'Puissance nominale (kW)': 18.5, 'Classe énergétique': 'IE3', "Indice de protection": 'IP55', }, }, ], }, { name: 'Armoire supervision séchage', reference: 'CTRL-S7-DRY', prix: '38900', typeCode: 'control-panel', requirementLabel: 'Armoire de supervision', modelCode: 'control-panel-s7', constructeur: 'siemens', customValues: { 'Automate principal': 'Siemens S7-1500', 'Année de mise à jour': 2022, "Indice de protection": 'IP65', }, pieces: [ { name: 'Module S7-1200', reference: 'PLC-S7-01', typeCode: 'plc-module', modelCode: 'plc-module-s71200', constructeur: 'siemens', customValues: { "Nombre d'E/S": 24, 'Version firmware': 'V2.8', }, }, { name: 'Fusible puissance 40A', reference: 'FUS-40A-DRY', typeCode: 'fuse-cartridge', modelCode: 'fuse-gg-40a', constructeur: 'schneider', customValues: { 'Calibre (A)': 40, 'Type de fusible': 'gG', }, }, ], }, ], sparePieces: [ { requirementLabel: 'Joints haute température', name: 'Joint PTFE 3 mm de rechange', reference: 'JNT-PTFE-SPARE', typeCode: 'flat-gasket', modelCode: 'gasket-ptfe-3', constructeur: 'agridry', customValues: { Matière: 'PTFE', 'Épaisseur (mm)': 3, }, }, { requirementLabel: 'Sondes de température de secours', name: 'Sonde PT100 courte', reference: 'TMP-SPARE-02', typeCode: 'temperature-probe', modelCode: 'temp-probe-pt100-short', constructeur: 'ifm', customValues: { 'Type de sonde': 'PT100 classe B', 'Plage de mesure (°C)': '0-200', }, }, ], }, { code: 'telehandler-logistics', typeMachineCode: 'telehandler-support', name: 'Manitou MLT 1040 logistique', reference: 'VT-MAN-01', prix: '73500', constructeurKey: 'manitou', customFieldValues: { 'Usage principal': 'Gestion big-bags', 'Lieu de stationnement': 'Hangar nord', "Année d'achat": 2021, }, components: [ { name: 'Chariot télescopique MLT 1040', reference: 'MLT-1040-01', prix: '73500', typeCode: 'telehandler', requirementLabel: 'Châssis 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: 'Cartouche cabine 40A', reference: 'FUS-CAB-40A', typeCode: 'fuse-cartridge', modelCode: 'fuse-gg-40a', constructeur: 'schneider', customValues: { 'Calibre (A)': 40, 'Type de fusible': 'gG', }, }, { name: 'Joint clapet direction', reference: 'JNT-DIR-01', typeCode: 'flat-gasket', modelCode: 'gasket-nbr-5', constructeur: 'manitou', customValues: { Matière: 'NBR', 'Épaisseur (mm)': 5, }, }, ], children: [ { name: 'Moteur principal 75 kW', reference: 'MTR-MAN-01', typeCode: 'motor-drive', requirementLabel: 'Groupe moteur', modelCode: 'motor-drive-75', constructeur: 'sew', customValues: { 'Puissance nominale (kW)': 75, 'Classe énergétique': 'IE3', "Indice de protection": 'IP55', }, pieces: [ { name: 'Kit fixation moteur Manitou', reference: 'KIT-MTR-MAN', typeCode: 'hex-screw', modelCode: 'screw-m12x80', customValues: { 'Diamètre (mm)': 12, 'Longueur (mm)': 80, 'Classe acier': '8.8', }, }, ], }, { name: 'Groupe hydraulique PVH98', reference: 'HYD-PVH98-01', typeCode: 'hydraulic-power-pack', requirementLabel: 'Groupe hydraulique', modelCode: 'hydraulic-pack-pvh98', constructeur: 'poclain', customValues: { 'Débit nominal (l/min)': 160, 'Pression max (bar)': 320, 'Type de pompe': 'Piston axial', }, pieces: [ { name: 'Joint collecteur hydraulique', reference: 'JNT-HYD-01', typeCode: 'flat-gasket', modelCode: 'gasket-nbr-5', customValues: { Matière: 'NBR', 'Épaisseur (mm)': 5, }, }, ], }, { name: 'Tableau cabine M340', reference: 'CTRL-CAB-01', typeCode: 'control-panel', requirementLabel: 'Armoire de contrôle cabine', 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 cabine', reference: 'PLC-CAB-01', typeCode: 'plc-module', modelCode: 'plc-module-m340', constructeur: 'schneider', customValues: { "Nombre d'E/S": 32, 'Version firmware': 'V2.9', }, }, { name: 'Fusible cabine 25A', reference: 'FUS-CAB-25A', typeCode: 'fuse-cartridge', modelCode: 'fuse-gg-25a', constructeur: 'schneider', customValues: { 'Calibre (A)': 25, 'Type de fusible': 'gG', }, }, ], }, ], }, ], sparePieces: [ { requirementLabel: 'Fusibles cabine', name: 'Fusible cabine gG 25A', reference: 'FUS-SPARE-25A', typeCode: 'fuse-cartridge', modelCode: 'fuse-gg-25a', constructeur: 'schneider', customValues: { 'Calibre (A)': 25, 'Type de fusible': 'gG', }, }, { requirementLabel: 'Joints hydrauliques de secours', name: 'Kit joints NBR Manitou', reference: 'KIT-JNT-MAN', typeCode: 'flat-gasket', modelCode: 'gasket-nbr-5', constructeur: 'manitou', customValues: { Matière: 'NBR', 'Épaisseur (mm)': 5, }, }, ], }, ]; async function createMachines( siteId: string, typeMachines: Record, context: { componentTypes: Record }>; componentModels: Record; pieceTypes: Record }>; pieceModels: Record; constructeurs: Record; }, ) { console.log('🏗️ Création des machines de démonstration...'); for (const build of machineBuilds) { const typeMachine = typeMachines[build.typeMachineCode]; if (!typeMachine) { throw new Error(`Type de machine introuvable pour ${build.typeMachineCode}`); } 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: build.name, reference: build.reference, prix: new Prisma.Decimal(build.prix), site: { connect: { id: siteId } }, typeMachine: { connect: { id: typeMachine.id } }, constructeur: { connect: { id: context.constructeurs[build.constructeurKey].id }, }, customFieldValues: buildCustomFieldValues( machineCustomFieldMap, build.customFieldValues, ), }, }); console.log(`⚙️ ${build.name} - ajout des composants...`); const componentContext = { ...context, requirementMap, }; for (const component of build.components) { await createComponentHierarchy(machine.id, component, componentContext); } if (build.sparePieces && build.sparePieces.length > 0) { console.log(`📦 ${build.name} - enregistrement des pièces de réserve...`); for (const spare of build.sparePieces) { const pieceType = context.pieceTypes[spare.typeCode]; const requirementId = spare.requirementLabel ? pieceRequirementMap.get(spare.requirementLabel) : undefined; await prisma.piece.create({ data: { name: spare.name, reference: spare.reference, prix: spare.prix ? new Prisma.Decimal(spare.prix) : undefined, machine: { connect: { id: machine.id } }, typePiece: { connect: { id: pieceType.id } }, pieceModel: { connect: { id: context.pieceModels[spare.modelCode].id } }, constructeur: spare.constructeur ? { connect: { id: context.constructeurs[spare.constructeur].id } } : undefined, typeMachinePieceRequirement: requirementId ? { connect: { id: requirementId } } : undefined, customFieldValues: buildCustomFieldValues( pieceType.customFields, spare.customValues, ), }, }); } } } } async function main() { try { await clearDatabaseExceptSitesAndProfiles(); const [site, constructeurs] = await Promise.all([ ensureDemoSite(), createConstructeurs(), ]); const { componentTypes, pieceTypes } = await createModelTypes(); const [pieceModels, componentModels, typeMachines] = await Promise.all([ createPieceModels(pieceTypes), createComponentModels(componentTypes), createTypeMachines(componentTypes, pieceTypes), ]); await createMachines(site.id, typeMachines, { 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();