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