feat: auto populate machine structures and seed sample data

This commit is contained in:
Matthieu
2025-10-13 09:01:33 +02:00
parent b7682ac312
commit dc4a12440b
21 changed files with 2218 additions and 7267 deletions

View File

@@ -1,42 +0,0 @@
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
async function main () {
try {
console.log('Starting custom fields cleanup...')
const deletedValues = await prisma.customFieldValue.deleteMany({
where: {
customField: {
OR: [
{ typeComposantId: { not: null } },
{ typePieceId: { not: null } },
{ typeMachineId: { not: null } }
]
}
}
})
console.log(`Deleted ${deletedValues.count} custom field values linked to type-level definitions.`)
const deletedFields = await prisma.customField.deleteMany({
where: {
OR: [
{ typeComposantId: { not: null } },
{ typePieceId: { not: null } },
{ typeMachineId: { not: null } }
]
}
})
console.log(`Deleted ${deletedFields.count} custom field definitions linked to model types.`)
console.log('Cleanup complete.')
} catch (error) {
console.error('Cleanup failed:', error)
process.exitCode = 1
} finally {
await prisma.$disconnect()
}
}
main()

View File

@@ -1,593 +0,0 @@
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();
});

File diff suppressed because it is too large Load Diff

550
scripts/seed-sample-data.ts Normal file
View File

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