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

1
.gitignore vendored
View File

@@ -3,3 +3,4 @@ node_modules
.env .env
/generated/prisma /generated/prisma
dist

View File

@@ -20,7 +20,8 @@ REQUEST_SIZE_LIMIT=10mb
SESSION_COOKIE_SECURE=true SESSION_COOKIE_SECURE=true
# Configuration de l'API # Configuration de l'API
API_PREFIX=api # API_PREFIX est désormais vide car les routes sont exposées à la racine
API_PREFIX=
API_VERSION=v1 API_VERSION=v1
# Configuration des logs # Configuration des logs

View File

@@ -0,0 +1,112 @@
/*
Warnings:
- A unique constraint covering the columns `[name]` on the table `constructeurs` will be added. If there are existing duplicate values, this will fail.
*/
-- AlterTable
ALTER TABLE "composants" ADD COLUMN "composantModelId" TEXT,
ADD COLUMN "typeMachineComponentRequirementId" TEXT;
-- AlterTable
ALTER TABLE "constructeurs" ALTER COLUMN "createdAt" SET DATA TYPE TIMESTAMP(3),
ALTER COLUMN "updatedAt" DROP DEFAULT,
ALTER COLUMN "updatedAt" SET DATA TYPE TIMESTAMP(3);
-- AlterTable
ALTER TABLE "pieces" ADD COLUMN "pieceModelId" TEXT,
ADD COLUMN "typeMachinePieceRequirementId" TEXT;
-- AlterTable
ALTER TABLE "profiles" ALTER COLUMN "updatedAt" DROP DEFAULT;
-- CreateTable
CREATE TABLE "composant_models" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT,
"structure" JSONB,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"typeComposantId" TEXT NOT NULL,
CONSTRAINT "composant_models_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "piece_models" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT,
"structure" JSONB,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"typePieceId" TEXT NOT NULL,
CONSTRAINT "piece_models_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "type_machine_component_requirements" (
"id" TEXT NOT NULL,
"label" TEXT,
"minCount" INTEGER NOT NULL DEFAULT 1,
"maxCount" INTEGER,
"required" BOOLEAN NOT NULL DEFAULT true,
"allowNewModels" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"typeMachineId" TEXT NOT NULL,
"typeComposantId" TEXT NOT NULL,
CONSTRAINT "type_machine_component_requirements_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "type_machine_piece_requirements" (
"id" TEXT NOT NULL,
"label" TEXT,
"minCount" INTEGER NOT NULL DEFAULT 0,
"maxCount" INTEGER,
"required" BOOLEAN NOT NULL DEFAULT false,
"allowNewModels" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"typeMachineId" TEXT NOT NULL,
"typePieceId" TEXT NOT NULL,
CONSTRAINT "type_machine_piece_requirements_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "constructeurs_name_key" ON "constructeurs"("name");
-- AddForeignKey
ALTER TABLE "composants" ADD CONSTRAINT "composants_composantModelId_fkey" FOREIGN KEY ("composantModelId") REFERENCES "composant_models"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "composants" ADD CONSTRAINT "composants_typeMachineComponentRequirementId_fkey" FOREIGN KEY ("typeMachineComponentRequirementId") REFERENCES "type_machine_component_requirements"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "pieces" ADD CONSTRAINT "pieces_pieceModelId_fkey" FOREIGN KEY ("pieceModelId") REFERENCES "piece_models"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "pieces" ADD CONSTRAINT "pieces_typeMachinePieceRequirementId_fkey" FOREIGN KEY ("typeMachinePieceRequirementId") REFERENCES "type_machine_piece_requirements"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "composant_models" ADD CONSTRAINT "composant_models_typeComposantId_fkey" FOREIGN KEY ("typeComposantId") REFERENCES "type_composants"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "piece_models" ADD CONSTRAINT "piece_models_typePieceId_fkey" FOREIGN KEY ("typePieceId") REFERENCES "type_pieces"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "type_machine_component_requirements" ADD CONSTRAINT "type_machine_component_requirements_typeMachineId_fkey" FOREIGN KEY ("typeMachineId") REFERENCES "type_machines"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "type_machine_component_requirements" ADD CONSTRAINT "type_machine_component_requirements_typeComposantId_fkey" FOREIGN KEY ("typeComposantId") REFERENCES "type_composants"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "type_machine_piece_requirements" ADD CONSTRAINT "type_machine_piece_requirements_typeMachineId_fkey" FOREIGN KEY ("typeMachineId") REFERENCES "type_machines"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "type_machine_piece_requirements" ADD CONSTRAINT "type_machine_piece_requirements_typePieceId_fkey" FOREIGN KEY ("typePieceId") REFERENCES "type_pieces"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@@ -45,6 +45,8 @@ model TypeMachine {
// Relations // Relations
machines Machine[] machines Machine[]
customFields CustomField[] @relation("TypeMachineCustomFields") customFields CustomField[] @relation("TypeMachineCustomFields")
componentRequirements TypeMachineComponentRequirement[]
pieceRequirements TypeMachinePieceRequirement[]
@@map("type_machines") @@map("type_machines")
} }
@@ -59,6 +61,8 @@ model TypeComposant {
// Relations // Relations
composants Composant[] composants Composant[]
customFields CustomField[] @relation("TypeComposantCustomFields") customFields CustomField[] @relation("TypeComposantCustomFields")
models ComposantModel[]
componentRequirements TypeMachineComponentRequirement[]
@@map("type_composants") @@map("type_composants")
} }
@@ -73,6 +77,8 @@ model TypePiece {
// Relations // Relations
pieces Piece[] pieces Piece[]
customFields CustomField[] @relation("TypePieceCustomFields") customFields CustomField[] @relation("TypePieceCustomFields")
models PieceModel[]
pieceRequirements TypeMachinePieceRequirement[]
@@map("type_pieces") @@map("type_pieces")
} }
@@ -124,6 +130,12 @@ model Composant {
typeComposantId String? typeComposantId String?
typeComposant TypeComposant? @relation(fields: [typeComposantId], references: [id]) typeComposant TypeComposant? @relation(fields: [typeComposantId], references: [id])
composantModelId String?
composantModel ComposantModel? @relation(fields: [composantModelId], references: [id], onDelete: SetNull)
typeMachineComponentRequirementId String?
typeMachineComponentRequirement TypeMachineComponentRequirement? @relation(fields: [typeMachineComponentRequirementId], references: [id], onDelete: SetNull)
constructeurId String? constructeurId String?
constructeur Constructeur? @relation(fields: [constructeurId], references: [id], onDelete: SetNull) constructeur Constructeur? @relation(fields: [constructeurId], references: [id], onDelete: SetNull)
@@ -153,6 +165,12 @@ model Piece {
typePieceId String? typePieceId String?
typePiece TypePiece? @relation(fields: [typePieceId], references: [id]) typePiece TypePiece? @relation(fields: [typePieceId], references: [id])
pieceModelId String?
pieceModel PieceModel? @relation(fields: [pieceModelId], references: [id], onDelete: SetNull)
typeMachinePieceRequirementId String?
typeMachinePieceRequirement TypeMachinePieceRequirement? @relation(fields: [typeMachinePieceRequirementId], references: [id], onDelete: SetNull)
constructeurId String? constructeurId String?
constructeur Constructeur? @relation(fields: [constructeurId], references: [id], onDelete: SetNull) constructeur Constructeur? @relation(fields: [constructeurId], references: [id], onDelete: SetNull)
@@ -261,3 +279,77 @@ model CustomFieldValue {
@@map("custom_field_values") @@map("custom_field_values")
} }
model ComposantModel {
id String @id @default(cuid())
name String
description String?
structure Json? // Définition du composant (sous-composants, pièces, champs personnalisés)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
typeComposantId String
typeComposant TypeComposant @relation(fields: [typeComposantId], references: [id], onDelete: Cascade)
composants Composant[]
@@map("composant_models")
}
model PieceModel {
id String @id @default(cuid())
name String
description String?
structure Json? // Définition de la pièce (champs personnalisés par défaut, etc.)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
typePieceId String
typePiece TypePiece @relation(fields: [typePieceId], references: [id], onDelete: Cascade)
pieces Piece[]
@@map("piece_models")
}
model TypeMachineComponentRequirement {
id String @id @default(cuid())
label String?
minCount Int @default(1)
maxCount Int?
required Boolean @default(true)
allowNewModels Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
typeMachineId String
typeMachine TypeMachine @relation(fields: [typeMachineId], references: [id], onDelete: Cascade)
typeComposantId String
typeComposant TypeComposant @relation(fields: [typeComposantId], references: [id])
composants Composant[]
@@map("type_machine_component_requirements")
}
model TypeMachinePieceRequirement {
id String @id @default(cuid())
label String?
minCount Int @default(0)
maxCount Int?
required Boolean @default(false)
allowNewModels Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
typeMachineId String
typeMachine TypeMachine @relation(fields: [typeMachineId], references: [id], onDelete: Cascade)
typePieceId String
typePiece TypePiece @relation(fields: [typePieceId], references: [id])
pieces Piece[]
@@map("type_machine_piece_requirements")
}

View File

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

View File

@@ -1,17 +1,41 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service'; 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() @Injectable()
export class MachinesService { export class MachinesService {
constructor(private prisma: PrismaService) {} constructor(private prisma: PrismaService) {}
async create(createMachineDto: CreateMachineDto) { 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({ const typeMachine = await this.prisma.typeMachine.findUnique({
where: { id: createMachineDto.typeMachineId }, where: { id: machineData.typeMachineId },
include: { include: {
customFields: true, 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é'); 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) => { return await this.prisma.$transaction(async (prisma) => {
// 1. Créer la machine
const machine = await prisma.machine.create({ const machine = await prisma.machine.create({
data: createMachineDto, data: machineData,
include: { include: {
site: true, site: true,
typeMachine: true, typeMachine: true,
@@ -31,37 +196,71 @@ export class MachinesService {
}, },
}); });
// 2. Créer les composants basés sur la structure du type if (typeMachine.componentRequirements.length > 0) {
const components = (typeMachine as any).components; for (const requirement of typeMachine.componentRequirements) {
if (components) { const selections = componentSelectionMap.get(requirement.id) ?? [];
await this.createComponentsFromType(prisma, machine.id, components); 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 if (typeMachine.pieceRequirements.length > 0) {
const machinePieces = (typeMachine as any).machinePieces; for (const requirement of typeMachine.pieceRequirements) {
if (machinePieces) { const selections = pieceSelectionMap.get(requirement.id) ?? [];
await this.createMachinePiecesFromType(prisma, machine.id, machinePieces); 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) { if (typeMachine.customFields && typeMachine.customFields.length > 0) {
await this.createMachineCustomFieldsFromType(prisma, machine.id, typeMachine.customFields); await this.createMachineCustomFieldsFromType(prisma, machine.id, typeMachine.customFields);
} }
// 5. Retourner la machine avec sa structure complète return prisma.machine.findUnique({
return await prisma.machine.findUnique({
where: { id: machine.id }, where: { id: machine.id },
include: { include: {
site: true, site: true,
typeMachine: { typeMachine: {
include: { include: {
customFields: true, customFields: true,
componentRequirements: {
include: {
typeComposant: true,
},
},
pieceRequirements: {
include: {
typePiece: true,
},
},
}, },
}, },
constructeur: true, constructeur: true,
composants: { composants: {
include: { include: {
typeComposant: true, typeComposant: true,
composantModel: true,
typeMachineComponentRequirement: {
include: {
typeComposant: true,
},
},
sousComposants: true, sousComposants: true,
pieces: { pieces: {
include: { include: {
@@ -71,6 +270,12 @@ export class MachinesService {
}, },
}, },
constructeur: true, constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
}, },
}, },
constructeur: true, constructeur: true,
@@ -84,6 +289,12 @@ export class MachinesService {
}, },
}, },
constructeur: true, constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
}, },
}, },
customFieldValues: { 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) { private async createComponentsFromType(prisma: any, machineId: string, components: any[], parentComposantId?: string) {
for (const component of components) { 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 const customFields = Array.isArray(component.customFields) ? component.customFields : [];
let typeComposant: any = null; const componentPieces = Array.isArray(component.pieces) ? component.pieces : [];
if (component.customFields && component.customFields.length > 0) { const subComponents = Array.isArray(component.subComponents) ? component.subComponents : [];
// Chercher d'abord si le type de composant existe déjà
typeComposant = await prisma.typeComposant.findFirst({ const componentModelId = component.__componentModelId ?? null;
where: { name: component.name } 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) { if (!typeComposant) {
typeComposant = await prisma.typeComposant.create({ typeComposant = await prisma.typeComposant.create({
data: { data: {
@@ -118,8 +407,7 @@ export class MachinesService {
}, },
}); });
// Créer les champs personnalisés pour le type de composant for (const customField of customFields) {
for (const customField of component.customFields) {
await prisma.customField.create({ await prisma.customField.create({
data: { data: {
name: customField.name, name: customField.name,
@@ -132,6 +420,8 @@ export class MachinesService {
}); });
} }
} }
typeComposantId = typeComposant.id;
} }
const createdComposant = await prisma.composant.create({ const createdComposant = await prisma.composant.create({
@@ -140,21 +430,22 @@ export class MachinesService {
reference: component.reference || '', reference: component.reference || '',
constructeurId: await this.resolveConstructeurId(prisma, component.constructeur), constructeurId: await this.resolveConstructeurId(prisma, component.constructeur),
emplacement: component.emplacement || '', emplacement: component.emplacement || '',
prix: component.prix || null, prix: component.prix ?? null,
machineId, machineId,
parentComposantId, parentComposantId,
typeComposantId: typeComposant?.id || null, typeComposantId,
composantModelId: componentModelId,
typeMachineComponentRequirementId: requirementId,
}, },
}); });
// Créer les valeurs des champs personnalisés pour le composant if (typeComposantId) {
if (typeComposant && typeComposant.id) { const typeCustomFields = await prisma.customField.findMany({
const customFields = await prisma.customField.findMany({ where: { typeComposantId },
where: { typeComposantId: typeComposant.id },
}); });
for (const customField of customFields) { for (const customField of typeCustomFields) {
const defaultValue = component.customFields?.find(cf => cf.name === customField.name)?.defaultValue || ''; const defaultValue = customFields.find((cf) => cf.name === customField.name)?.defaultValue || '';
await prisma.customFieldValue.create({ await prisma.customFieldValue.create({
data: { data: {
value: defaultValue, value: defaultValue,
@@ -165,20 +456,22 @@ export class MachinesService {
} }
} }
// Créer les pièces du composant avec leurs champs personnalisés for (const piece of componentPieces) {
if (component.pieces) {
for (const piece of component.pieces) {
if (!piece || !piece.name) continue; if (!piece || !piece.name) continue;
// Créer d'abord le type de pièce s'il n'existe pas const pieceCustomFields = Array.isArray(piece.customFields) ? piece.customFields : [];
let typePiece: any = null; const pieceModelId = piece.__pieceModelId ?? null;
if (piece.customFields && piece.customFields.length > 0) { const pieceRequirementId = piece.__requirementId ?? null;
// Chercher d'abord si le type de pièce existe déjà const providedTypePieceId = piece.typePieceId
typePiece = await prisma.typePiece.findFirst({ ?? (piece.typePiece && piece.typePiece.id ? piece.typePiece.id : null);
where: { name: piece.name }
let typePieceId: string | null = providedTypePieceId ?? null;
if (!typePieceId && pieceCustomFields.length > 0) {
let typePiece = await prisma.typePiece.findFirst({
where: { name: piece.name },
}); });
// Si le type n'existe pas, le créer
if (!typePiece) { if (!typePiece) {
typePiece = await prisma.typePiece.create({ typePiece = await prisma.typePiece.create({
data: { data: {
@@ -187,8 +480,7 @@ export class MachinesService {
}, },
}); });
// Créer les champs personnalisés pour le type de pièce for (const customField of pieceCustomFields) {
for (const customField of piece.customFields) {
await prisma.customField.create({ await prisma.customField.create({
data: { data: {
name: customField.name, name: customField.name,
@@ -201,6 +493,8 @@ export class MachinesService {
}); });
} }
} }
typePieceId = typePiece.id;
} }
const createdPiece = await prisma.piece.create({ const createdPiece = await prisma.piece.create({
@@ -209,20 +503,21 @@ export class MachinesService {
reference: piece.reference || '', reference: piece.reference || '',
constructeurId: await this.resolveConstructeurId(prisma, piece.constructeur), constructeurId: await this.resolveConstructeurId(prisma, piece.constructeur),
emplacement: piece.emplacement || '', emplacement: piece.emplacement || '',
prix: piece.prix || null, prix: piece.prix ?? null,
composantId: createdComposant.id, composantId: createdComposant.id,
typePieceId: typePiece?.id || null, typePieceId,
pieceModelId,
typeMachinePieceRequirementId: pieceRequirementId,
}, },
}); });
// Créer les valeurs des champs personnalisés pour la pièce if (typePieceId) {
if (typePiece && typePiece.id) { const typePieceCustomFields = await prisma.customField.findMany({
const customFields = await prisma.customField.findMany({ where: { typePieceId },
where: { typePieceId: typePiece.id },
}); });
for (const customField of customFields) { for (const customField of typePieceCustomFields) {
const defaultValue = piece.customFields?.find(cf => cf.name === customField.name)?.defaultValue || ''; const defaultValue = pieceCustomFields.find((cf) => cf.name === customField.name)?.defaultValue || '';
await prisma.customFieldValue.create({ await prisma.customFieldValue.create({
data: { data: {
value: defaultValue, value: defaultValue,
@@ -233,11 +528,9 @@ export class MachinesService {
} }
} }
} }
}
// Créer les sous-composants récursivement if (subComponents.length > 0) {
if (component.subComponents) { await this.createComponentsFromType(prisma, machineId, subComponents, createdComposant.id);
await this.createComponentsFromType(prisma, machineId, component.subComponents, createdComposant.id);
} }
} }
} }
@@ -246,18 +539,75 @@ export class MachinesService {
for (const piece of machinePieces) { for (const piece of machinePieces) {
if (!piece || !piece.name) continue; if (!piece || !piece.name) continue;
const createdPiece = await prisma.piece.create({ 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: { data: {
name: piece.name, name: piece.name,
machineId, description: piece.description || '',
constructeurId: await this.resolveConstructeurId(prisma, piece.constructeur),
}, },
}); });
// Copier les champs personnalisés du type vers la pièce for (const customField of customFields) {
if (piece.customFields && piece.customFields.length > 0) { await prisma.customField.create({
for (const customField of piece.customFields) { data: {
// Créer le champ personnalisé 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,
machineId,
typePieceId,
pieceModelId,
typeMachinePieceRequirementId: requirementId,
},
});
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({ const createdCustomField = await prisma.customField.create({
data: { data: {
name: customField.name, name: customField.name,
@@ -265,11 +615,10 @@ export class MachinesService {
required: customField.required || false, required: customField.required || false,
defaultValue: customField.defaultValue, defaultValue: customField.defaultValue,
options: customField.options || [], 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({ await prisma.customFieldValue.create({
data: { data: {
value: customField.defaultValue || '', value: customField.defaultValue || '',
@@ -316,12 +665,28 @@ export class MachinesService {
typeMachine: { typeMachine: {
include: { include: {
customFields: true, customFields: true,
componentRequirements: {
include: {
typeComposant: true,
},
},
pieceRequirements: {
include: {
typePiece: true,
},
},
}, },
}, },
constructeur: true, constructeur: true,
composants: { composants: {
include: { include: {
typeComposant: true, typeComposant: true,
composantModel: true,
typeMachineComponentRequirement: {
include: {
typeComposant: true,
},
},
sousComposants: true, sousComposants: true,
customFieldValues: { customFieldValues: {
include: { include: {
@@ -337,6 +702,12 @@ export class MachinesService {
}, },
}, },
constructeur: true, constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
}, },
}, },
}, },
@@ -349,6 +720,12 @@ export class MachinesService {
}, },
}, },
constructeur: true, constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
}, },
}, },
customFieldValues: { customFieldValues: {
@@ -369,12 +746,28 @@ export class MachinesService {
typeMachine: { typeMachine: {
include: { include: {
customFields: true, customFields: true,
componentRequirements: {
include: {
typeComposant: true,
},
},
pieceRequirements: {
include: {
typePiece: true,
},
},
}, },
}, },
constructeur: true, constructeur: true,
composants: { composants: {
include: { include: {
typeComposant: true, typeComposant: true,
composantModel: true,
typeMachineComponentRequirement: {
include: {
typeComposant: true,
},
},
sousComposants: true, sousComposants: true,
customFieldValues: { customFieldValues: {
include: { include: {
@@ -390,6 +783,12 @@ export class MachinesService {
}, },
}, },
constructeur: true, constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
}, },
}, },
}, },
@@ -402,6 +801,12 @@ export class MachinesService {
}, },
}, },
constructeur: true, constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
}, },
}, },
customFieldValues: { customFieldValues: {
@@ -423,17 +828,39 @@ export class MachinesService {
typeMachine: { typeMachine: {
include: { include: {
customFields: true, customFields: true,
componentRequirements: {
include: {
typeComposant: true,
},
},
pieceRequirements: {
include: {
typePiece: true,
},
},
}, },
}, },
constructeur: true, constructeur: true,
composants: { composants: {
include: { include: {
typeComposant: true, typeComposant: true,
composantModel: true,
typeMachineComponentRequirement: {
include: {
typeComposant: true,
},
},
sousComposants: true, sousComposants: true,
constructeur: true, constructeur: true,
pieces: { pieces: {
include: { include: {
constructeur: true, constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
customFieldValues: { customFieldValues: {
include: { include: {
customField: true, customField: true,
@@ -446,6 +873,12 @@ export class MachinesService {
pieces: { pieces: {
include: { include: {
constructeur: true, constructeur: true,
pieceModel: true,
typeMachinePieceRequirement: {
include: {
typePiece: true,
},
},
customFieldValues: { customFieldValues: {
include: { include: {
customField: true, customField: true,

View File

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

View File

@@ -33,6 +33,10 @@ export class CreateComposantDto {
@IsOptional() @IsOptional()
@IsString() @IsString()
typeComposantId?: string; typeComposantId?: string;
@IsOptional()
@IsString()
composantModelId?: string;
} }
export class UpdateComposantDto { export class UpdateComposantDto {
@@ -60,4 +64,8 @@ export class UpdateComposantDto {
@IsOptional() @IsOptional()
@IsString() @IsString()
typeComposantId?: string; 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 { export class CreateMachineDto {
@IsString() @IsString()
@@ -26,6 +52,18 @@ export class CreateMachineDto {
@IsOptional() @IsOptional()
@IsString() @IsString()
typeMachineId?: string; typeMachineId?: string;
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => MachineComponentSelectionDto)
componentSelections?: MachineComponentSelectionDto[];
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => MachinePieceSelectionDto)
pieceSelections?: MachinePieceSelectionDto[];
} }
export class UpdateMachineDto { export class UpdateMachineDto {

View File

@@ -33,6 +33,10 @@ export class CreatePieceDto {
@IsOptional() @IsOptional()
@IsString() @IsString()
typePieceId?: string; typePieceId?: string;
@IsOptional()
@IsString()
pieceModelId?: string;
} }
export class UpdatePieceDto { export class UpdatePieceDto {
@@ -60,4 +64,8 @@ export class UpdatePieceDto {
@IsOptional() @IsOptional()
@IsString() @IsString()
typePieceId?: string; 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 { export enum CustomFieldType {
TEXT = 'text', TEXT = 'text',
@@ -50,6 +52,56 @@ export class UpdateCustomFieldDto {
options?: string[]; 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 { export class CreateTypeMachineDto {
@IsString() @IsString()
name: string; name: string;
@@ -77,6 +129,18 @@ export class CreateTypeMachineDto {
@IsOptional() @IsOptional()
@IsArray() @IsArray()
customFields?: CreateCustomFieldDto[]; customFields?: CreateCustomFieldDto[];
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => TypeMachineComponentRequirementDto)
componentRequirements?: TypeMachineComponentRequirementDto[];
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => TypeMachinePieceRequirementDto)
pieceRequirements?: TypeMachinePieceRequirementDto[];
} }
export class UpdateTypeMachineDto { export class UpdateTypeMachineDto {
@@ -107,6 +171,18 @@ export class UpdateTypeMachineDto {
@IsOptional() @IsOptional()
@IsArray() @IsArray()
customFields?: CreateCustomFieldDto[]; customFields?: CreateCustomFieldDto[];
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => TypeMachineComponentRequirementDto)
componentRequirements?: TypeMachineComponentRequirementDto[];
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => TypeMachinePieceRequirementDto)
pieceRequirements?: TypeMachinePieceRequirementDto[];
} }
export class CreateTypeComposantDto { export class CreateTypeComposantDto {
@@ -162,3 +238,67 @@ export class UpdateTypePieceDto {
@IsArray() @IsArray()
customFields?: CreateCustomFieldDto[]; 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 { TypesService } from './types.service';
import { import {
CreateTypeMachineDto, CreateTypeMachineDto,
@@ -6,7 +6,11 @@ import {
CreateTypeComposantDto, CreateTypeComposantDto,
UpdateTypeComposantDto, UpdateTypeComposantDto,
CreateTypePieceDto, CreateTypePieceDto,
UpdateTypePieceDto UpdateTypePieceDto,
CreateComposantModelDto,
UpdateComposantModelDto,
CreatePieceModelDto,
UpdatePieceModelDto
} from '../shared/dto/type.dto'; } from '../shared/dto/type.dto';
@Controller('types') @Controller('types')
@@ -50,6 +54,32 @@ export class TypesController {
return this.typesService.findAllTypeComposants(); 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') @Get('composants/:id')
findOneTypeComposant(@Param('id') id: string) { findOneTypeComposant(@Param('id') id: string) {
return this.typesService.findOneTypeComposant(id); return this.typesService.findOneTypeComposant(id);
@@ -76,6 +106,32 @@ export class TypesController {
return this.typesService.findAllTypePieces(); 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') @Get('pieces/:id')
findOneTypePiece(@Param('id') id: string) { findOneTypePiece(@Param('id') id: string) {
return this.typesService.findOneTypePiece(id); return this.typesService.findOneTypePiece(id);

View File

@@ -6,7 +6,11 @@ import {
CreateTypeComposantDto, CreateTypeComposantDto,
UpdateTypeComposantDto, UpdateTypeComposantDto,
CreateTypePieceDto, CreateTypePieceDto,
UpdateTypePieceDto UpdateTypePieceDto,
CreateComposantModelDto,
UpdateComposantModelDto,
CreatePieceModelDto,
UpdatePieceModelDto
} from '../shared/dto/type.dto'; } from '../shared/dto/type.dto';
@Injectable() @Injectable()
@@ -15,7 +19,7 @@ export class TypesService {
// TypeMachine methods // TypeMachine methods
async createTypeMachine(createTypeMachineDto: CreateTypeMachineDto) { async createTypeMachine(createTypeMachineDto: CreateTypeMachineDto) {
const { customFields, ...typeData } = createTypeMachineDto; const { customFields, componentRequirements, pieceRequirements, ...typeData } = createTypeMachineDto;
return this.prisma.typeMachine.create({ return this.prisma.typeMachine.create({
data: { data: {
@@ -28,10 +32,44 @@ export class TypesService {
defaultValue: field.defaultValue, defaultValue: field.defaultValue,
options: field.options 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: { include: {
customFields: true, customFields: true,
componentRequirements: {
include: {
typeComposant: true,
},
},
pieceRequirements: {
include: {
typePiece: true,
},
},
}, },
}); });
} }
@@ -41,6 +79,16 @@ export class TypesService {
include: { include: {
machines: true, machines: true,
customFields: true, customFields: true,
componentRequirements: {
include: {
typeComposant: true,
},
},
pieceRequirements: {
include: {
typePiece: true,
},
},
}, },
}); });
} }
@@ -51,12 +99,22 @@ export class TypesService {
include: { include: {
machines: true, machines: true,
customFields: true, customFields: true,
componentRequirements: {
include: {
typeComposant: true,
},
},
pieceRequirements: {
include: {
typePiece: true,
},
},
}, },
}); });
} }
async updateTypeMachine(id: string, updateTypeMachineDto: UpdateTypeMachineDto) { 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 // Si des champs personnalisés sont fournis, on les met à jour
if (customFields !== undefined) { if (customFields !== undefined) {
@@ -80,11 +138,63 @@ 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({ return this.prisma.typeMachine.update({
where: { id }, where: { id },
data: typeData, data: typeData,
include: { include: {
customFields: true, customFields: true,
componentRequirements: {
include: {
typeComposant: true,
},
},
pieceRequirements: {
include: {
typePiece: true,
},
},
}, },
}); });
} }
@@ -142,6 +252,7 @@ export class TypesService {
include: { include: {
composants: true, composants: true,
customFields: true, customFields: true,
models: true,
}, },
}); });
} }
@@ -152,6 +263,7 @@ export class TypesService {
include: { include: {
composants: true, composants: true,
customFields: true, customFields: true,
models: true,
}, },
}); });
} }
@@ -224,6 +336,7 @@ export class TypesService {
include: { include: {
pieces: true, pieces: true,
customFields: true, customFields: true,
models: true,
}, },
}); });
} }
@@ -234,6 +347,7 @@ export class TypesService {
include: { include: {
pieces: true, pieces: true,
customFields: true, customFields: true,
models: true,
}, },
}); });
} }
@@ -277,4 +391,134 @@ export class TypesService {
where: { id }, 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 },
});
}
} }