feat: Add feature in component and piece for support group

This commit is contained in:
Matthieu
2025-09-22 08:35:36 +02:00
parent b8559be031
commit b6ca9ae54b
13 changed files with 1550 additions and 206 deletions

View File

@@ -13,14 +13,44 @@ export class ComposantsService {
machine: true,
parentComposant: true,
typeComposant: true,
composantModel: true,
typeMachineComponentRequirement: {
include: {
typeComposant: true,
},
},
constructeur: true,
sousComposants: {
include: {
typeComposant: true,
pieces: true,
composantModel: true,
typeMachineComponentRequirement: {
include: {
typeComposant: true,
},
},
pieces: {
include: {
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
},
},
},
},
pieces: {
include: {
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
},
},
pieces: true,
documents: true,
},
});
@@ -32,6 +62,12 @@ export class ComposantsService {
machine: true,
parentComposant: true,
typeComposant: true,
composantModel: true,
typeMachineComponentRequirement: {
include: {
typeComposant: true,
},
},
constructeur: true,
customFieldValues: {
include: {
@@ -41,6 +77,12 @@ export class ComposantsService {
sousComposants: {
include: {
typeComposant: true,
composantModel: true,
typeMachineComponentRequirement: {
include: {
typeComposant: true,
},
},
customFieldValues: {
include: {
customField: true,
@@ -54,6 +96,12 @@ export class ComposantsService {
},
},
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
},
},
constructeur: true,
@@ -67,6 +115,12 @@ export class ComposantsService {
},
},
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
},
},
documents: true,
@@ -81,6 +135,12 @@ export class ComposantsService {
machine: true,
parentComposant: true,
typeComposant: true,
composantModel: true,
typeMachineComponentRequirement: {
include: {
typeComposant: true,
},
},
constructeur: true,
customFieldValues: {
include: {
@@ -90,6 +150,12 @@ export class ComposantsService {
sousComposants: {
include: {
typeComposant: true,
composantModel: true,
typeMachineComponentRequirement: {
include: {
typeComposant: true,
},
},
customFieldValues: {
include: {
customField: true,
@@ -103,6 +169,12 @@ export class ComposantsService {
},
},
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
},
},
constructeur: true,
@@ -116,6 +188,12 @@ export class ComposantsService {
},
},
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
},
},
documents: true,
@@ -130,10 +208,22 @@ export class ComposantsService {
machine: true,
parentComposant: true,
typeComposant: true,
composantModel: true,
typeMachineComponentRequirement: {
include: {
typeComposant: true,
},
},
constructeur: true,
sousComposants: {
include: {
typeComposant: true,
composantModel: true,
typeMachineComponentRequirement: {
include: {
typeComposant: true,
},
},
pieces: true,
constructeur: true,
},
@@ -146,6 +236,12 @@ export class ComposantsService {
},
},
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
},
},
customFieldValues: {
@@ -167,6 +263,12 @@ export class ComposantsService {
},
include: {
typeComposant: true,
composantModel: true,
typeMachineComponentRequirement: {
include: {
typeComposant: true,
},
},
constructeur: true,
customFieldValues: {
include: {
@@ -176,6 +278,12 @@ export class ComposantsService {
sousComposants: {
include: {
typeComposant: true,
composantModel: true,
typeMachineComponentRequirement: {
include: {
typeComposant: true,
},
},
constructeur: true,
customFieldValues: {
include: {
@@ -190,11 +298,23 @@ export class ComposantsService {
},
},
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
},
},
sousComposants: {
include: {
typeComposant: true,
composantModel: true,
typeMachineComponentRequirement: {
include: {
typeComposant: true,
},
},
constructeur: true,
customFieldValues: {
include: {
@@ -209,6 +329,12 @@ export class ComposantsService {
},
},
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
},
},
},
@@ -223,6 +349,12 @@ export class ComposantsService {
},
},
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
},
},
},
@@ -239,6 +371,12 @@ export class ComposantsService {
machine: true,
parentComposant: true,
typeComposant: true,
composantModel: true,
typeMachineComponentRequirement: {
include: {
typeComposant: true,
},
},
constructeur: true,
customFieldValues: {
include: {
@@ -248,6 +386,12 @@ export class ComposantsService {
sousComposants: {
include: {
typeComposant: true,
composantModel: true,
typeMachineComponentRequirement: {
include: {
typeComposant: true,
},
},
constructeur: true,
customFieldValues: {
include: {
@@ -262,6 +406,12 @@ export class ComposantsService {
},
},
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
},
},
},
@@ -274,6 +424,12 @@ export class ComposantsService {
},
},
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
},
},
documents: true,

View File

@@ -1,17 +1,41 @@
import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreateMachineDto, UpdateMachineDto } from '../shared/dto/machine.dto';
import {
CreateMachineDto,
UpdateMachineDto,
MachineComponentSelectionDto,
MachinePieceSelectionDto
} from '../shared/dto/machine.dto';
@Injectable()
export class MachinesService {
constructor(private prisma: PrismaService) {}
async create(createMachineDto: CreateMachineDto) {
// Récupérer le type de machine pour hériter de sa structure
const {
componentSelections = [],
pieceSelections = [],
...machineData
} = createMachineDto;
if (!machineData.typeMachineId) {
throw new Error('typeMachineId est requis pour créer une machine à partir d\'un squelette.');
}
const typeMachine = await this.prisma.typeMachine.findUnique({
where: { id: createMachineDto.typeMachineId },
where: { id: machineData.typeMachineId },
include: {
customFields: true,
componentRequirements: {
include: {
typeComposant: true,
},
},
pieceRequirements: {
include: {
typePiece: true,
},
},
},
});
@@ -19,11 +43,152 @@ export class MachinesService {
throw new Error('Type de machine non trouvé');
}
// Créer la machine avec la structure héritée du type
const componentRequirementMap = new Map(
typeMachine.componentRequirements.map((requirement) => [requirement.id, requirement]),
);
const pieceRequirementMap = new Map(
typeMachine.pieceRequirements.map((requirement) => [requirement.id, requirement]),
);
const componentSelectionMap = new Map<string, MachineComponentSelectionDto[]>();
for (const selection of componentSelections) {
const requirement = componentRequirementMap.get(selection.requirementId);
if (!requirement) {
throw new Error(`Sélection de composant invalide: requirementId=${selection.requirementId}`);
}
if (!componentSelectionMap.has(requirement.id)) {
componentSelectionMap.set(requirement.id, []);
}
componentSelectionMap.get(requirement.id)!.push(selection);
}
const pieceSelectionMap = new Map<string, MachinePieceSelectionDto[]>();
for (const selection of pieceSelections) {
const requirement = pieceRequirementMap.get(selection.requirementId);
if (!requirement) {
throw new Error(`Sélection de pièce invalide: requirementId=${selection.requirementId}`);
}
if (!pieceSelectionMap.has(requirement.id)) {
pieceSelectionMap.set(requirement.id, []);
}
pieceSelectionMap.get(requirement.id)!.push(selection);
}
const componentModelIds = Array.from(
new Set(componentSelections.map((selection) => selection.componentModelId).filter(Boolean)),
) as string[];
const componentModels = componentModelIds.length
? await this.prisma.composantModel.findMany({
where: { id: { in: componentModelIds } },
})
: [];
const componentModelMap = new Map(componentModels.map((model) => [model.id, model]));
const pieceModelIds = Array.from(
new Set(pieceSelections.map((selection) => selection.pieceModelId).filter(Boolean)),
) as string[];
const pieceModels = pieceModelIds.length
? await this.prisma.pieceModel.findMany({
where: { id: { in: pieceModelIds } },
})
: [];
const pieceModelMap = new Map(pieceModels.map((model) => [model.id, model]));
for (const requirement of typeMachine.componentRequirements) {
const selections = componentSelectionMap.get(requirement.id) ?? [];
const min = requirement.minCount ?? (requirement.required ? 1 : 0);
const max = requirement.maxCount ?? undefined;
if (selections.length < min) {
throw new Error(
`Le groupe de composants "${requirement.label || requirement.typeComposant?.name || requirement.id}" requiert au moins ${min} sélection(s).`,
);
}
if (max !== undefined && selections.length > max) {
throw new Error(
`Le groupe de composants "${requirement.label || requirement.typeComposant?.name || requirement.id}" ne peut pas dépasser ${max} sélection(s).`,
);
}
if (!requirement.allowNewModels) {
const missingModel = selections.find((selection) => !selection.componentModelId);
if (missingModel) {
throw new Error(
`Le groupe de composants "${requirement.label || requirement.typeComposant?.name || requirement.id}" n'autorise que la sélection de modèles existants.`,
);
}
}
}
for (const requirement of typeMachine.pieceRequirements) {
const selections = pieceSelectionMap.get(requirement.id) ?? [];
const min = requirement.minCount ?? (requirement.required ? 1 : 0);
const max = requirement.maxCount ?? undefined;
if (selections.length < min) {
throw new Error(
`Le groupe de pièces "${requirement.label || requirement.typePiece?.name || requirement.id}" requiert au moins ${min} sélection(s).`,
);
}
if (max !== undefined && selections.length > max) {
throw new Error(
`Le groupe de pièces "${requirement.label || requirement.typePiece?.name || requirement.id}" ne peut pas dépasser ${max} sélection(s).`,
);
}
if (!requirement.allowNewModels) {
const missingModel = selections.find((selection) => !selection.pieceModelId);
if (missingModel) {
throw new Error(
`Le groupe de pièces "${requirement.label || requirement.typePiece?.name || requirement.id}" n'autorise que la sélection de modèles existants.`,
);
}
}
}
for (const selection of componentSelections) {
if (!selection.componentModelId) {
continue;
}
const model = componentModelMap.get(selection.componentModelId);
if (!model) {
throw new Error(`Modèle de composant introuvable: ${selection.componentModelId}`);
}
const requirement = componentRequirementMap.get(selection.requirementId);
if (!requirement) {
throw new Error(`Requirement de composant introuvable: ${selection.requirementId}`);
}
if (model.typeComposantId !== requirement.typeComposantId) {
throw new Error(
`Le modèle de composant "${model.name}" n'appartient pas au type de composant attendu pour ce groupe.`,
);
}
}
for (const selection of pieceSelections) {
if (!selection.pieceModelId) {
continue;
}
const model = pieceModelMap.get(selection.pieceModelId);
if (!model) {
throw new Error(`Modèle de pièce introuvable: ${selection.pieceModelId}`);
}
const requirement = pieceRequirementMap.get(selection.requirementId);
if (!requirement) {
throw new Error(`Requirement de pièce introuvable: ${selection.requirementId}`);
}
if (model.typePieceId !== requirement.typePieceId) {
throw new Error(
`Le modèle de pièce "${model.name}" n'appartient pas au type de pièce attendu pour ce groupe.`,
);
}
}
return await this.prisma.$transaction(async (prisma) => {
// 1. Créer la machine
const machine = await prisma.machine.create({
data: createMachineDto,
data: machineData,
include: {
site: true,
typeMachine: true,
@@ -31,37 +196,71 @@ export class MachinesService {
},
});
// 2. Créer les composants basés sur la structure du type
const components = (typeMachine as any).components;
if (components) {
await this.createComponentsFromType(prisma, machine.id, components);
if (typeMachine.componentRequirements.length > 0) {
for (const requirement of typeMachine.componentRequirements) {
const selections = componentSelectionMap.get(requirement.id) ?? [];
for (const selection of selections) {
const model = selection.componentModelId ? componentModelMap.get(selection.componentModelId) : undefined;
const definition = this.normalizeComponentSelection(selection, requirement, model);
await this.createComponentsFromType(prisma, machine.id, [definition]);
}
}
} else {
const legacyComponents = (typeMachine as any).components;
if (legacyComponents) {
await this.createComponentsFromType(prisma, machine.id, legacyComponents);
}
}
// 3. Créer les pièces de machine basées sur le type
const machinePieces = (typeMachine as any).machinePieces;
if (machinePieces) {
await this.createMachinePiecesFromType(prisma, machine.id, machinePieces);
if (typeMachine.pieceRequirements.length > 0) {
for (const requirement of typeMachine.pieceRequirements) {
const selections = pieceSelectionMap.get(requirement.id) ?? [];
for (const selection of selections) {
const model = selection.pieceModelId ? pieceModelMap.get(selection.pieceModelId) : undefined;
const definition = this.normalizePieceSelection(selection, requirement, model);
await this.createMachinePiecesFromType(prisma, machine.id, [definition]);
}
}
} else {
const legacyPieces = (typeMachine as any).machinePieces;
if (legacyPieces) {
await this.createMachinePiecesFromType(prisma, machine.id, legacyPieces);
}
}
// 4. Créer les champs personnalisés de la machine basés sur le type
if (typeMachine.customFields && typeMachine.customFields.length > 0) {
await this.createMachineCustomFieldsFromType(prisma, machine.id, typeMachine.customFields);
}
// 5. Retourner la machine avec sa structure complète
return await prisma.machine.findUnique({
return prisma.machine.findUnique({
where: { id: machine.id },
include: {
site: true,
typeMachine: {
include: {
customFields: true,
componentRequirements: {
include: {
typeComposant: true,
},
},
pieceRequirements: {
include: {
typePiece: true,
},
},
},
},
constructeur: true,
composants: {
include: {
typeComposant: true,
composantModel: true,
typeMachineComponentRequirement: {
include: {
typeComposant: true,
},
},
sousComposants: true,
pieces: {
include: {
@@ -71,6 +270,12 @@ export class MachinesService {
},
},
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
},
},
constructeur: true,
@@ -84,6 +289,12 @@ export class MachinesService {
},
},
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
},
},
customFieldValues: {
@@ -97,19 +308,97 @@ export class MachinesService {
});
}
private cloneStructure(definition: any): any {
if (definition === undefined || definition === null) {
return {};
}
try {
return JSON.parse(JSON.stringify(definition));
} catch (error) {
if (Array.isArray(definition)) {
return definition.map((item) => this.cloneStructure(item));
}
if (typeof definition === 'object') {
return { ...definition };
}
return definition;
}
}
private normalizeComponentSelection(
selection: MachineComponentSelectionDto,
requirement: any,
model?: any,
): any {
const baseDefinition = selection.definition ?? (model?.structure ?? {});
const definition = this.cloneStructure(baseDefinition);
const prepared: any = definition && typeof definition === 'object' && !Array.isArray(definition) ? definition : {};
prepared.name = prepared.name || model?.name || requirement?.typeComposant?.name || 'Composant';
prepared.reference = prepared.reference ?? model?.structure?.reference ?? '';
prepared.emplacement = prepared.emplacement ?? model?.structure?.emplacement ?? '';
prepared.prix = prepared.prix ?? model?.structure?.prix ?? null;
prepared.customFields = Array.isArray(prepared.customFields) ? prepared.customFields : [];
prepared.pieces = Array.isArray(prepared.pieces)
? prepared.pieces
: prepared.pieces
? [prepared.pieces]
: [];
prepared.subComponents = Array.isArray(prepared.subComponents)
? prepared.subComponents
: prepared.subComponents
? [prepared.subComponents]
: [];
prepared.typeComposantId = prepared.typeComposantId || requirement?.typeComposantId || model?.typeComposantId || null;
prepared.__componentModelId = selection.componentModelId ?? null;
prepared.__requirementId = requirement?.id ?? null;
return prepared;
}
private normalizePieceSelection(
selection: MachinePieceSelectionDto,
requirement: any,
model?: any,
): any {
const baseDefinition = selection.definition ?? (model?.structure ?? {});
const definition = this.cloneStructure(baseDefinition);
const prepared: any = definition && typeof definition === 'object' && !Array.isArray(definition) ? definition : {};
prepared.name = prepared.name || model?.name || requirement?.typePiece?.name || 'Pièce';
prepared.customFields = Array.isArray(prepared.customFields) ? prepared.customFields : [];
prepared.typePieceId = prepared.typePieceId || requirement?.typePieceId || model?.typePieceId || null;
prepared.__pieceModelId = selection.pieceModelId ?? null;
prepared.__requirementId = requirement?.id ?? null;
return prepared;
}
private async createComponentsFromType(prisma: any, machineId: string, components: any[], parentComposantId?: string) {
for (const component of components) {
if (!component.name) continue;
if (!component || !component.name) continue;
// Créer d'abord le type de composant s'il n'existe pas
let typeComposant: any = null;
if (component.customFields && component.customFields.length > 0) {
// Chercher d'abord si le type de composant existe déjà
typeComposant = await prisma.typeComposant.findFirst({
where: { name: component.name }
const customFields = Array.isArray(component.customFields) ? component.customFields : [];
const componentPieces = Array.isArray(component.pieces) ? component.pieces : [];
const subComponents = Array.isArray(component.subComponents) ? component.subComponents : [];
const componentModelId = component.__componentModelId ?? null;
const requirementId = component.__requirementId ?? null;
const providedTypeComposantId = component.typeComposantId
?? (component.typeComposant && component.typeComposant.id ? component.typeComposant.id : null);
let typeComposantId: string | null = providedTypeComposantId ?? null;
if (!typeComposantId && customFields.length > 0) {
let typeComposant = await prisma.typeComposant.findFirst({
where: { name: component.name },
});
// Si le type n'existe pas, le créer
if (!typeComposant) {
typeComposant = await prisma.typeComposant.create({
data: {
@@ -118,8 +407,7 @@ export class MachinesService {
},
});
// Créer les champs personnalisés pour le type de composant
for (const customField of component.customFields) {
for (const customField of customFields) {
await prisma.customField.create({
data: {
name: customField.name,
@@ -132,6 +420,8 @@ export class MachinesService {
});
}
}
typeComposantId = typeComposant.id;
}
const createdComposant = await prisma.composant.create({
@@ -140,21 +430,22 @@ export class MachinesService {
reference: component.reference || '',
constructeurId: await this.resolveConstructeurId(prisma, component.constructeur),
emplacement: component.emplacement || '',
prix: component.prix || null,
prix: component.prix ?? null,
machineId,
parentComposantId,
typeComposantId: typeComposant?.id || null,
typeComposantId,
composantModelId: componentModelId,
typeMachineComponentRequirementId: requirementId,
},
});
// Créer les valeurs des champs personnalisés pour le composant
if (typeComposant && typeComposant.id) {
const customFields = await prisma.customField.findMany({
where: { typeComposantId: typeComposant.id },
if (typeComposantId) {
const typeCustomFields = await prisma.customField.findMany({
where: { typeComposantId },
});
for (const customField of customFields) {
const defaultValue = component.customFields?.find(cf => cf.name === customField.name)?.defaultValue || '';
for (const customField of typeCustomFields) {
const defaultValue = customFields.find((cf) => cf.name === customField.name)?.defaultValue || '';
await prisma.customFieldValue.create({
data: {
value: defaultValue,
@@ -165,79 +456,81 @@ export class MachinesService {
}
}
// Créer les pièces du composant avec leurs champs personnalisés
if (component.pieces) {
for (const piece of component.pieces) {
if (!piece || !piece.name) continue;
// Créer d'abord le type de pièce s'il n'existe pas
let typePiece: any = null;
if (piece.customFields && piece.customFields.length > 0) {
// Chercher d'abord si le type de pièce existe déjà
typePiece = await prisma.typePiece.findFirst({
where: { name: piece.name }
});
for (const piece of componentPieces) {
if (!piece || !piece.name) continue;
// Si le type n'existe pas, le créer
if (!typePiece) {
typePiece = await prisma.typePiece.create({
data: {
name: piece.name,
description: piece.description || '',
},
});
const pieceCustomFields = Array.isArray(piece.customFields) ? piece.customFields : [];
const pieceModelId = piece.__pieceModelId ?? null;
const pieceRequirementId = piece.__requirementId ?? null;
const providedTypePieceId = piece.typePieceId
?? (piece.typePiece && piece.typePiece.id ? piece.typePiece.id : null);
// Créer les champs personnalisés pour le type de pièce
for (const customField of piece.customFields) {
await prisma.customField.create({
data: {
name: customField.name,
type: customField.type,
required: customField.required || false,
defaultValue: customField.defaultValue,
options: customField.options || [],
typePieceId: typePiece.id,
},
});
}
}
}
const createdPiece = await prisma.piece.create({
data: {
name: piece.name,
reference: piece.reference || '',
constructeurId: await this.resolveConstructeurId(prisma, piece.constructeur),
emplacement: piece.emplacement || '',
prix: piece.prix || null,
composantId: createdComposant.id,
typePieceId: typePiece?.id || null,
},
let typePieceId: string | null = providedTypePieceId ?? null;
if (!typePieceId && pieceCustomFields.length > 0) {
let typePiece = await prisma.typePiece.findFirst({
where: { name: piece.name },
});
// Créer les valeurs des champs personnalisés pour la pièce
if (typePiece && typePiece.id) {
const customFields = await prisma.customField.findMany({
where: { typePieceId: typePiece.id },
if (!typePiece) {
typePiece = await prisma.typePiece.create({
data: {
name: piece.name,
description: piece.description || '',
},
});
for (const customField of customFields) {
const defaultValue = piece.customFields?.find(cf => cf.name === customField.name)?.defaultValue || '';
await prisma.customFieldValue.create({
for (const customField of pieceCustomFields) {
await prisma.customField.create({
data: {
value: defaultValue,
customFieldId: customField.id,
pieceId: createdPiece.id,
name: customField.name,
type: customField.type,
required: customField.required || false,
defaultValue: customField.defaultValue,
options: customField.options || [],
typePieceId: typePiece.id,
},
});
}
}
typePieceId = typePiece.id;
}
const createdPiece = await prisma.piece.create({
data: {
name: piece.name,
reference: piece.reference || '',
constructeurId: await this.resolveConstructeurId(prisma, piece.constructeur),
emplacement: piece.emplacement || '',
prix: piece.prix ?? null,
composantId: createdComposant.id,
typePieceId,
pieceModelId,
typeMachinePieceRequirementId: pieceRequirementId,
},
});
if (typePieceId) {
const typePieceCustomFields = await prisma.customField.findMany({
where: { typePieceId },
});
for (const customField of typePieceCustomFields) {
const defaultValue = pieceCustomFields.find((cf) => cf.name === customField.name)?.defaultValue || '';
await prisma.customFieldValue.create({
data: {
value: defaultValue,
customFieldId: customField.id,
pieceId: createdPiece.id,
},
});
}
}
}
// Créer les sous-composants récursivement
if (component.subComponents) {
await this.createComponentsFromType(prisma, machineId, component.subComponents, createdComposant.id);
if (subComponents.length > 0) {
await this.createComponentsFromType(prisma, machineId, subComponents, createdComposant.id);
}
}
}
@@ -245,19 +538,76 @@ export class MachinesService {
private async createMachinePiecesFromType(prisma: any, machineId: string, machinePieces: any[]) {
for (const piece of machinePieces) {
if (!piece || !piece.name) continue;
const customFields = Array.isArray(piece.customFields) ? piece.customFields : [];
const pieceModelId = piece.__pieceModelId ?? null;
const requirementId = piece.__requirementId ?? null;
const providedTypePieceId = piece.typePieceId
?? (piece.typePiece && piece.typePiece.id ? piece.typePiece.id : null);
let typePieceId: string | null = providedTypePieceId ?? null;
if (!typePieceId && customFields.length > 0) {
let typePiece = await prisma.typePiece.findFirst({
where: { name: piece.name },
});
if (!typePiece) {
typePiece = await prisma.typePiece.create({
data: {
name: piece.name,
description: piece.description || '',
},
});
for (const customField of customFields) {
await prisma.customField.create({
data: {
name: customField.name,
type: customField.type,
required: customField.required || false,
defaultValue: customField.defaultValue,
options: customField.options || [],
typePieceId: typePiece.id,
},
});
}
}
typePieceId = typePiece.id;
}
const createdPiece = await prisma.piece.create({
data: {
name: piece.name,
machineId,
reference: piece.reference || '',
constructeurId: await this.resolveConstructeurId(prisma, piece.constructeur),
emplacement: piece.emplacement || '',
prix: piece.prix ?? null,
machineId,
typePieceId,
pieceModelId,
typeMachinePieceRequirementId: requirementId,
},
});
// Copier les champs personnalisés du type vers la pièce
if (piece.customFields && piece.customFields.length > 0) {
for (const customField of piece.customFields) {
// Créer le champ personnalisé
if (typePieceId) {
const typePieceCustomFields = await prisma.customField.findMany({
where: { typePieceId },
});
for (const customField of typePieceCustomFields) {
const defaultValue = customFields.find((cf) => cf.name === customField.name)?.defaultValue || '';
await prisma.customFieldValue.create({
data: {
value: defaultValue,
customFieldId: customField.id,
pieceId: createdPiece.id,
},
});
}
} else if (customFields.length > 0) {
for (const customField of customFields) {
const createdCustomField = await prisma.customField.create({
data: {
name: customField.name,
@@ -265,11 +615,10 @@ export class MachinesService {
required: customField.required || false,
defaultValue: customField.defaultValue,
options: customField.options || [],
typePieceId: null, // Ce champ sera lié à la pièce individuelle
typePieceId: null,
},
});
// Créer la valeur par défaut
await prisma.customFieldValue.create({
data: {
value: customField.defaultValue || '',
@@ -316,12 +665,28 @@ export class MachinesService {
typeMachine: {
include: {
customFields: true,
componentRequirements: {
include: {
typeComposant: true,
},
},
pieceRequirements: {
include: {
typePiece: true,
},
},
},
},
constructeur: true,
composants: {
include: {
typeComposant: true,
composantModel: true,
typeMachineComponentRequirement: {
include: {
typeComposant: true,
},
},
sousComposants: true,
customFieldValues: {
include: {
@@ -337,6 +702,12 @@ export class MachinesService {
},
},
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
},
},
},
@@ -349,6 +720,12 @@ export class MachinesService {
},
},
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
},
},
customFieldValues: {
@@ -369,12 +746,28 @@ export class MachinesService {
typeMachine: {
include: {
customFields: true,
componentRequirements: {
include: {
typeComposant: true,
},
},
pieceRequirements: {
include: {
typePiece: true,
},
},
},
},
constructeur: true,
composants: {
include: {
typeComposant: true,
composantModel: true,
typeMachineComponentRequirement: {
include: {
typeComposant: true,
},
},
sousComposants: true,
customFieldValues: {
include: {
@@ -390,6 +783,12 @@ export class MachinesService {
},
},
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
},
},
},
@@ -402,6 +801,12 @@ export class MachinesService {
},
},
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
},
},
customFieldValues: {
@@ -423,17 +828,39 @@ export class MachinesService {
typeMachine: {
include: {
customFields: true,
componentRequirements: {
include: {
typeComposant: true,
},
},
pieceRequirements: {
include: {
typePiece: true,
},
},
},
},
constructeur: true,
composants: {
include: {
typeComposant: true,
composantModel: true,
typeMachineComponentRequirement: {
include: {
typeComposant: true,
},
},
sousComposants: true,
constructeur: true,
pieces: {
include: {
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
customFieldValues: {
include: {
customField: true,
@@ -446,6 +873,12 @@ export class MachinesService {
pieces: {
include: {
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
customFieldValues: {
include: {
customField: true,

View File

@@ -15,6 +15,17 @@ export class PiecesService {
typePiece: true,
documents: true,
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
customFieldValues: {
include: {
customField: true,
},
},
},
});
}
@@ -27,6 +38,17 @@ export class PiecesService {
typePiece: true,
documents: true,
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
customFieldValues: {
include: {
customField: true,
},
},
},
});
}
@@ -40,6 +62,17 @@ export class PiecesService {
typePiece: true,
documents: true,
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
customFieldValues: {
include: {
customField: true,
},
},
},
});
}
@@ -53,6 +86,17 @@ export class PiecesService {
typePiece: true,
documents: true,
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
customFieldValues: {
include: {
customField: true,
},
},
},
});
}
@@ -66,6 +110,17 @@ export class PiecesService {
typePiece: true,
documents: true,
constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
customFieldValues: {
include: {
customField: true,
},
},
},
});
}

View File

@@ -33,6 +33,10 @@ export class CreateComposantDto {
@IsOptional()
@IsString()
typeComposantId?: string;
@IsOptional()
@IsString()
composantModelId?: string;
}
export class UpdateComposantDto {
@@ -60,4 +64,8 @@ export class UpdateComposantDto {
@IsOptional()
@IsString()
typeComposantId?: string;
}
@IsOptional()
@IsString()
composantModelId?: string;
}

View File

@@ -1,4 +1,30 @@
import { IsString, IsOptional, IsDecimal } from 'class-validator';
import { IsString, IsOptional, IsDecimal, IsArray } from 'class-validator';
import { Type } from 'class-transformer';
import { ValidateNested } from 'class-validator';
export class MachineComponentSelectionDto {
@IsString()
requirementId: string;
@IsOptional()
@IsString()
componentModelId?: string;
@IsOptional()
definition?: any;
}
export class MachinePieceSelectionDto {
@IsString()
requirementId: string;
@IsOptional()
@IsString()
pieceModelId?: string;
@IsOptional()
definition?: any;
}
export class CreateMachineDto {
@IsString()
@@ -26,6 +52,18 @@ export class CreateMachineDto {
@IsOptional()
@IsString()
typeMachineId?: string;
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => MachineComponentSelectionDto)
componentSelections?: MachineComponentSelectionDto[];
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => MachinePieceSelectionDto)
pieceSelections?: MachinePieceSelectionDto[];
}
export class UpdateMachineDto {

View File

@@ -33,6 +33,10 @@ export class CreatePieceDto {
@IsOptional()
@IsString()
typePieceId?: string;
@IsOptional()
@IsString()
pieceModelId?: string;
}
export class UpdatePieceDto {
@@ -60,4 +64,8 @@ export class UpdatePieceDto {
@IsOptional()
@IsString()
typePieceId?: string;
}
@IsOptional()
@IsString()
pieceModelId?: string;
}

View File

@@ -1,4 +1,6 @@
import { IsString, IsOptional, IsArray, IsObject, IsBoolean, IsEnum } from 'class-validator';
import { IsString, IsOptional, IsArray, IsObject, IsBoolean, IsEnum, IsInt } from 'class-validator';
import { Type } from 'class-transformer';
import { ValidateNested } from 'class-validator';
export enum CustomFieldType {
TEXT = 'text',
@@ -50,6 +52,56 @@ export class UpdateCustomFieldDto {
options?: string[];
}
export class TypeMachineComponentRequirementDto {
@IsString()
typeComposantId: string;
@IsOptional()
@IsString()
label?: string;
@IsOptional()
@IsInt()
minCount?: number;
@IsOptional()
@IsInt()
maxCount?: number | null;
@IsOptional()
@IsBoolean()
required?: boolean;
@IsOptional()
@IsBoolean()
allowNewModels?: boolean;
}
export class TypeMachinePieceRequirementDto {
@IsString()
typePieceId: string;
@IsOptional()
@IsString()
label?: string;
@IsOptional()
@IsInt()
minCount?: number;
@IsOptional()
@IsInt()
maxCount?: number | null;
@IsOptional()
@IsBoolean()
required?: boolean;
@IsOptional()
@IsBoolean()
allowNewModels?: boolean;
}
export class CreateTypeMachineDto {
@IsString()
name: string;
@@ -77,6 +129,18 @@ export class CreateTypeMachineDto {
@IsOptional()
@IsArray()
customFields?: CreateCustomFieldDto[];
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => TypeMachineComponentRequirementDto)
componentRequirements?: TypeMachineComponentRequirementDto[];
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => TypeMachinePieceRequirementDto)
pieceRequirements?: TypeMachinePieceRequirementDto[];
}
export class UpdateTypeMachineDto {
@@ -107,6 +171,18 @@ export class UpdateTypeMachineDto {
@IsOptional()
@IsArray()
customFields?: CreateCustomFieldDto[];
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => TypeMachineComponentRequirementDto)
componentRequirements?: TypeMachineComponentRequirementDto[];
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => TypeMachinePieceRequirementDto)
pieceRequirements?: TypeMachinePieceRequirementDto[];
}
export class CreateTypeComposantDto {
@@ -161,4 +237,68 @@ export class UpdateTypePieceDto {
@IsOptional()
@IsArray()
customFields?: CreateCustomFieldDto[];
}
}
export class CreateComposantModelDto {
@IsString()
name: string;
@IsOptional()
@IsString()
description?: string;
@IsString()
typeComposantId: string;
@IsOptional()
structure?: any;
}
export class UpdateComposantModelDto {
@IsOptional()
@IsString()
name?: string;
@IsOptional()
@IsString()
description?: string;
@IsOptional()
@IsString()
typeComposantId?: string;
@IsOptional()
structure?: any;
}
export class CreatePieceModelDto {
@IsString()
name: string;
@IsOptional()
@IsString()
description?: string;
@IsString()
typePieceId: string;
@IsOptional()
structure?: any;
}
export class UpdatePieceModelDto {
@IsOptional()
@IsString()
name?: string;
@IsOptional()
@IsString()
description?: string;
@IsOptional()
@IsString()
typePieceId?: string;
@IsOptional()
structure?: any;
}

View File

@@ -1,4 +1,4 @@
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { Controller, Get, Post, Body, Patch, Param, Delete, Query } from '@nestjs/common';
import { TypesService } from './types.service';
import {
CreateTypeMachineDto,
@@ -6,7 +6,11 @@ import {
CreateTypeComposantDto,
UpdateTypeComposantDto,
CreateTypePieceDto,
UpdateTypePieceDto
UpdateTypePieceDto,
CreateComposantModelDto,
UpdateComposantModelDto,
CreatePieceModelDto,
UpdatePieceModelDto
} from '../shared/dto/type.dto';
@Controller('types')
@@ -50,6 +54,32 @@ export class TypesController {
return this.typesService.findAllTypeComposants();
}
// ComposantModel routes
@Post('composants/models')
createComposantModel(@Body() createComposantModelDto: CreateComposantModelDto) {
return this.typesService.createComposantModel(createComposantModelDto);
}
@Get('composants/models')
findAllComposantModels(@Query('typeComposantId') typeComposantId?: string) {
return this.typesService.findAllComposantModels(typeComposantId);
}
@Get('composants/models/:id')
findOneComposantModel(@Param('id') id: string) {
return this.typesService.findOneComposantModel(id);
}
@Patch('composants/models/:id')
updateComposantModel(@Param('id') id: string, @Body() updateComposantModelDto: UpdateComposantModelDto) {
return this.typesService.updateComposantModel(id, updateComposantModelDto);
}
@Delete('composants/models/:id')
removeComposantModel(@Param('id') id: string) {
return this.typesService.removeComposantModel(id);
}
@Get('composants/:id')
findOneTypeComposant(@Param('id') id: string) {
return this.typesService.findOneTypeComposant(id);
@@ -76,6 +106,32 @@ export class TypesController {
return this.typesService.findAllTypePieces();
}
// PieceModel routes
@Post('pieces/models')
createPieceModel(@Body() createPieceModelDto: CreatePieceModelDto) {
return this.typesService.createPieceModel(createPieceModelDto);
}
@Get('pieces/models')
findAllPieceModels(@Query('typePieceId') typePieceId?: string) {
return this.typesService.findAllPieceModels(typePieceId);
}
@Get('pieces/models/:id')
findOnePieceModel(@Param('id') id: string) {
return this.typesService.findOnePieceModel(id);
}
@Patch('pieces/models/:id')
updatePieceModel(@Param('id') id: string, @Body() updatePieceModelDto: UpdatePieceModelDto) {
return this.typesService.updatePieceModel(id, updatePieceModelDto);
}
@Delete('pieces/models/:id')
removePieceModel(@Param('id') id: string) {
return this.typesService.removePieceModel(id);
}
@Get('pieces/:id')
findOneTypePiece(@Param('id') id: string) {
return this.typesService.findOneTypePiece(id);

View File

@@ -6,7 +6,11 @@ import {
CreateTypeComposantDto,
UpdateTypeComposantDto,
CreateTypePieceDto,
UpdateTypePieceDto
UpdateTypePieceDto,
CreateComposantModelDto,
UpdateComposantModelDto,
CreatePieceModelDto,
UpdatePieceModelDto
} from '../shared/dto/type.dto';
@Injectable()
@@ -15,7 +19,7 @@ export class TypesService {
// TypeMachine methods
async createTypeMachine(createTypeMachineDto: CreateTypeMachineDto) {
const { customFields, ...typeData } = createTypeMachineDto;
const { customFields, componentRequirements, pieceRequirements, ...typeData } = createTypeMachineDto;
return this.prisma.typeMachine.create({
data: {
@@ -28,10 +32,44 @@ export class TypesService {
defaultValue: field.defaultValue,
options: field.options
}))
} : undefined
} : undefined,
componentRequirements: componentRequirements && componentRequirements.length > 0 ? {
create: componentRequirements.map(requirement => ({
label: requirement.label,
minCount: requirement.minCount ?? 1,
maxCount: requirement.maxCount ?? null,
required: requirement.required ?? true,
allowNewModels: requirement.allowNewModels ?? true,
typeComposant: {
connect: { id: requirement.typeComposantId },
},
}))
} : undefined,
pieceRequirements: pieceRequirements && pieceRequirements.length > 0 ? {
create: pieceRequirements.map(requirement => ({
label: requirement.label,
minCount: requirement.minCount ?? 0,
maxCount: requirement.maxCount ?? null,
required: requirement.required ?? false,
allowNewModels: requirement.allowNewModels ?? true,
typePiece: {
connect: { id: requirement.typePieceId },
},
}))
} : undefined,
},
include: {
customFields: true,
componentRequirements: {
include: {
typeComposant: true,
},
},
pieceRequirements: {
include: {
typePiece: true,
},
},
},
});
}
@@ -41,6 +79,16 @@ export class TypesService {
include: {
machines: true,
customFields: true,
componentRequirements: {
include: {
typeComposant: true,
},
},
pieceRequirements: {
include: {
typePiece: true,
},
},
},
});
}
@@ -51,12 +99,22 @@ export class TypesService {
include: {
machines: true,
customFields: true,
componentRequirements: {
include: {
typeComposant: true,
},
},
pieceRequirements: {
include: {
typePiece: true,
},
},
},
});
}
async updateTypeMachine(id: string, updateTypeMachineDto: UpdateTypeMachineDto) {
const { customFields, ...typeData } = updateTypeMachineDto;
const { customFields, componentRequirements, pieceRequirements, ...typeData } = updateTypeMachineDto;
// Si des champs personnalisés sont fournis, on les met à jour
if (customFields !== undefined) {
@@ -79,12 +137,64 @@ export class TypesService {
});
}
}
if (componentRequirements !== undefined) {
await this.prisma.typeMachineComponentRequirement.deleteMany({
where: { typeMachineId: id },
});
if (componentRequirements.length > 0) {
await this.prisma.typeMachineComponentRequirement.createMany({
data: componentRequirements.map(requirement => ({
label: requirement.label ?? null,
minCount: requirement.minCount ?? 1,
maxCount: requirement.maxCount ?? null,
required: requirement.required ?? true,
allowNewModels: requirement.allowNewModels ?? true,
typeMachineId: id,
typeComposantId: requirement.typeComposantId,
})),
skipDuplicates: false,
});
}
}
if (pieceRequirements !== undefined) {
await this.prisma.typeMachinePieceRequirement.deleteMany({
where: { typeMachineId: id },
});
if (pieceRequirements.length > 0) {
await this.prisma.typeMachinePieceRequirement.createMany({
data: pieceRequirements.map(requirement => ({
label: requirement.label ?? null,
minCount: requirement.minCount ?? 0,
maxCount: requirement.maxCount ?? null,
required: requirement.required ?? false,
allowNewModels: requirement.allowNewModels ?? true,
typeMachineId: id,
typePieceId: requirement.typePieceId,
})),
skipDuplicates: false,
});
}
}
return this.prisma.typeMachine.update({
where: { id },
data: typeData,
include: {
customFields: true,
componentRequirements: {
include: {
typeComposant: true,
},
},
pieceRequirements: {
include: {
typePiece: true,
},
},
},
});
}
@@ -142,6 +252,7 @@ export class TypesService {
include: {
composants: true,
customFields: true,
models: true,
},
});
}
@@ -152,6 +263,7 @@ export class TypesService {
include: {
composants: true,
customFields: true,
models: true,
},
});
}
@@ -224,6 +336,7 @@ export class TypesService {
include: {
pieces: true,
customFields: true,
models: true,
},
});
}
@@ -234,6 +347,7 @@ export class TypesService {
include: {
pieces: true,
customFields: true,
models: true,
},
});
}
@@ -277,4 +391,134 @@ export class TypesService {
where: { id },
});
}
// ComposantModel methods
async createComposantModel(createComposantModelDto: CreateComposantModelDto) {
const { typeComposantId, ...data } = createComposantModelDto;
return this.prisma.composantModel.create({
data: {
...data,
typeComposant: {
connect: { id: typeComposantId },
},
},
include: {
typeComposant: true,
},
});
}
async findAllComposantModels(typeComposantId?: string) {
return this.prisma.composantModel.findMany({
where: typeComposantId ? { typeComposantId } : undefined,
include: {
typeComposant: true,
},
orderBy: {
name: 'asc',
},
});
}
async findOneComposantModel(id: string) {
return this.prisma.composantModel.findUnique({
where: { id },
include: {
typeComposant: true,
},
});
}
async updateComposantModel(id: string, updateComposantModelDto: UpdateComposantModelDto) {
const { typeComposantId, ...data } = updateComposantModelDto;
return this.prisma.composantModel.update({
where: { id },
data: {
...data,
...(typeComposantId
? {
typeComposant: {
connect: { id: typeComposantId },
},
}
: {}),
},
include: {
typeComposant: true,
},
});
}
async removeComposantModel(id: string) {
return this.prisma.composantModel.delete({
where: { id },
});
}
// PieceModel methods
async createPieceModel(createPieceModelDto: CreatePieceModelDto) {
const { typePieceId, ...data } = createPieceModelDto;
return this.prisma.pieceModel.create({
data: {
...data,
typePiece: {
connect: { id: typePieceId },
},
},
include: {
typePiece: true,
},
});
}
async findAllPieceModels(typePieceId?: string) {
return this.prisma.pieceModel.findMany({
where: typePieceId ? { typePieceId } : undefined,
include: {
typePiece: true,
},
orderBy: {
name: 'asc',
},
});
}
async findOnePieceModel(id: string) {
return this.prisma.pieceModel.findUnique({
where: { id },
include: {
typePiece: true,
},
});
}
async updatePieceModel(id: string, updatePieceModelDto: UpdatePieceModelDto) {
const { typePieceId, ...data } = updatePieceModelDto;
return this.prisma.pieceModel.update({
where: { id },
data: {
...data,
...(typePieceId
? {
typePiece: {
connect: { id: typePieceId },
},
}
: {}),
},
include: {
typePiece: true,
},
});
}
async removePieceModel(id: string) {
return this.prisma.pieceModel.delete({
where: { id },
});
}
}