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
/generated/prisma
dist

View File

@@ -20,7 +20,8 @@ REQUEST_SIZE_LIMIT=10mb
SESSION_COOKIE_SECURE=true
# 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
# 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

@@ -12,39 +12,41 @@ datasource db {
// Entités principales
model Site {
id String @id @default(cuid())
name String
id String @id @default(cuid())
name String
contactName String @default("")
contactPhone String @default("")
contactAddress String @default("")
contactPostalCode String @default("")
contactCity String @default("")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
machines Machine[]
machines Machine[]
documents Document[] @relation("SiteDocuments")
@@map("sites")
}
model TypeMachine {
id String @id @default(cuid())
name String @unique
description String?
category String?
id String @id @default(cuid())
name String @unique
description String?
category String?
maintenanceFrequency String?
components Json? // Stockage de la structure hiérarchique des composants
criticalParts Json? // Stockage des pièces critiques
machinePieces Json? // Stockage des pièces de machine
specifications Json? // Stockage des spécifications techniques
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
components Json? // Stockage de la structure hiérarchique des composants
criticalParts Json? // Stockage des pièces critiques
machinePieces Json? // Stockage des pièces de machine
specifications Json? // Stockage des spécifications techniques
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
machines Machine[]
customFields CustomField[] @relation("TypeMachineCustomFields")
machines Machine[]
customFields CustomField[] @relation("TypeMachineCustomFields")
componentRequirements TypeMachineComponentRequirement[]
pieceRequirements TypeMachinePieceRequirement[]
@@map("type_machines")
}
@@ -57,8 +59,10 @@ model TypeComposant {
updatedAt DateTime @updatedAt
// Relations
composants Composant[]
customFields CustomField[] @relation("TypeComposantCustomFields")
composants Composant[]
customFields CustomField[] @relation("TypeComposantCustomFields")
models ComposantModel[]
componentRequirements TypeMachineComponentRequirement[]
@@map("type_composants")
}
@@ -71,8 +75,10 @@ model TypePiece {
updatedAt DateTime @updatedAt
// Relations
pieces Piece[]
customFields CustomField[] @relation("TypePieceCustomFields")
pieces Piece[]
customFields CustomField[] @relation("TypePieceCustomFields")
models PieceModel[]
pieceRequirements TypeMachinePieceRequirement[]
@@map("type_pieces")
}
@@ -87,18 +93,18 @@ model Machine {
updatedAt DateTime @updatedAt
// Relations
siteId String
site Site @relation(fields: [siteId], references: [id], onDelete: Cascade)
siteId String
site Site @relation(fields: [siteId], references: [id], onDelete: Cascade)
typeMachineId String?
typeMachine TypeMachine? @relation(fields: [typeMachineId], references: [id])
constructeurId String?
constructeur Constructeur? @relation(fields: [constructeurId], references: [id], onDelete: SetNull)
composants Composant[]
pieces Piece[]
documents Document[] @relation("MachineDocuments")
composants Composant[]
pieces Piece[]
documents Document[] @relation("MachineDocuments")
customFieldValues CustomFieldValue[] @relation("MachineCustomFieldValues")
@@map("machines")
@@ -114,21 +120,27 @@ model Composant {
updatedAt DateTime @updatedAt
// Relations hiérarchiques
machineId String?
machine Machine? @relation(fields: [machineId], references: [id], onDelete: Cascade)
machineId String?
machine Machine? @relation(fields: [machineId], references: [id], onDelete: Cascade)
parentComposantId String?
parentComposant Composant? @relation("ComposantHierarchy", fields: [parentComposantId], references: [id], onDelete: Cascade)
parentComposant Composant? @relation("ComposantHierarchy", fields: [parentComposantId], references: [id], onDelete: Cascade)
sousComposants Composant[] @relation("ComposantHierarchy")
typeComposantId String?
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?
constructeur Constructeur? @relation(fields: [constructeurId], references: [id], onDelete: SetNull)
pieces Piece[]
documents Document[] @relation("ComposantDocuments")
pieces Piece[]
documents Document[] @relation("ComposantDocuments")
customFieldValues CustomFieldValue[] @relation("ComposantCustomFieldValues")
@@map("composants")
@@ -144,19 +156,25 @@ model Piece {
updatedAt DateTime @updatedAt
// Relations
machineId String?
machine Machine? @relation(fields: [machineId], references: [id], onDelete: Cascade)
machineId String?
machine Machine? @relation(fields: [machineId], references: [id], onDelete: Cascade)
composantId String?
composant Composant? @relation(fields: [composantId], references: [id], onDelete: Cascade)
composant Composant? @relation(fields: [composantId], references: [id], onDelete: Cascade)
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?
constructeur Constructeur? @relation(fields: [constructeurId], references: [id], onDelete: SetNull)
documents Document[] @relation("PieceDocuments")
documents Document[] @relation("PieceDocuments")
customFieldValues CustomFieldValue[] @relation("PieceCustomFieldValues")
@@map("pieces")
@@ -189,50 +207,50 @@ model Profile {
}
model Document {
id String @id @default(cuid())
name String
filename String
path String
mimeType String
size Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
id String @id @default(cuid())
name String
filename String
path String
mimeType String
size Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations polymorphiques
machineId String?
machine Machine? @relation("MachineDocuments", fields: [machineId], references: [id], onDelete: Cascade)
composantId String?
composant Composant? @relation("ComposantDocuments", fields: [composantId], references: [id], onDelete: Cascade)
pieceId String?
piece Piece? @relation("PieceDocuments", fields: [pieceId], references: [id], onDelete: Cascade)
machineId String?
machine Machine? @relation("MachineDocuments", fields: [machineId], references: [id], onDelete: Cascade)
siteId String?
site Site? @relation("SiteDocuments", fields: [siteId], references: [id], onDelete: Cascade)
composantId String?
composant Composant? @relation("ComposantDocuments", fields: [composantId], references: [id], onDelete: Cascade)
pieceId String?
piece Piece? @relation("PieceDocuments", fields: [pieceId], references: [id], onDelete: Cascade)
siteId String?
site Site? @relation("SiteDocuments", fields: [siteId], references: [id], onDelete: Cascade)
@@map("documents")
}
model CustomField {
id String @id @default(cuid())
name String
type String // 'string', 'number', 'boolean', 'date'
required Boolean @default(false)
id String @id @default(cuid())
name String
type String // 'string', 'number', 'boolean', 'date'
required Boolean @default(false)
defaultValue String?
options String[] // Pour les champs de type SELECT
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
options String[] // Pour les champs de type SELECT
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations polymorphiques pour les types
typeMachineId String?
typeMachine TypeMachine? @relation("TypeMachineCustomFields", fields: [typeMachineId], references: [id], onDelete: Cascade)
typeMachineId String?
typeMachine TypeMachine? @relation("TypeMachineCustomFields", fields: [typeMachineId], references: [id], onDelete: Cascade)
typeComposantId String?
typeComposant TypeComposant? @relation("TypeComposantCustomFields", fields: [typeComposantId], references: [id], onDelete: Cascade)
typePieceId String?
typePiece TypePiece? @relation("TypePieceCustomFields", fields: [typePieceId], references: [id], onDelete: Cascade)
typePieceId String?
typePiece TypePiece? @relation("TypePieceCustomFields", fields: [typePieceId], references: [id], onDelete: Cascade)
// Relations avec les valeurs
customFieldValues CustomFieldValue[]
@@ -241,23 +259,97 @@ model CustomField {
}
model CustomFieldValue {
id String @id @default(cuid())
value String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
id String @id @default(cuid())
value String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
customFieldId String
customField CustomField @relation(fields: [customFieldId], references: [id], onDelete: Cascade)
machineId String?
machine Machine? @relation("MachineCustomFieldValues", fields: [machineId], references: [id], onDelete: Cascade)
machineId String?
machine Machine? @relation("MachineCustomFieldValues", fields: [machineId], references: [id], onDelete: Cascade)
composantId String?
composant Composant? @relation("ComposantCustomFieldValues", fields: [composantId], references: [id], onDelete: Cascade)
pieceId String?
piece Piece? @relation("PieceCustomFieldValues", fields: [pieceId], references: [id], onDelete: Cascade)
composant Composant? @relation("ComposantCustomFieldValues", fields: [composantId], references: [id], onDelete: Cascade)
pieceId String?
piece Piece? @relation("PieceCustomFieldValues", fields: [pieceId], references: [id], onDelete: Cascade)
@@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,
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 },
});
}
}