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

@@ -12,39 +12,41 @@ datasource db {
// Entités principales // Entités principales
model Site { model Site {
id String @id @default(cuid()) id String @id @default(cuid())
name String name String
contactName String @default("") contactName String @default("")
contactPhone String @default("") contactPhone String @default("")
contactAddress String @default("") contactAddress String @default("")
contactPostalCode String @default("") contactPostalCode String @default("")
contactCity String @default("") contactCity String @default("")
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
// Relations // Relations
machines Machine[] machines Machine[]
documents Document[] @relation("SiteDocuments") documents Document[] @relation("SiteDocuments")
@@map("sites") @@map("sites")
} }
model TypeMachine { model TypeMachine {
id String @id @default(cuid()) id String @id @default(cuid())
name String @unique name String @unique
description String? description String?
category String? category String?
maintenanceFrequency String? maintenanceFrequency String?
components Json? // Stockage de la structure hiérarchique des composants components Json? // Stockage de la structure hiérarchique des composants
criticalParts Json? // Stockage des pièces critiques criticalParts Json? // Stockage des pièces critiques
machinePieces Json? // Stockage des pièces de machine machinePieces Json? // Stockage des pièces de machine
specifications Json? // Stockage des spécifications techniques specifications Json? // Stockage des spécifications techniques
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
// Relations // Relations
machines Machine[] machines Machine[]
customFields CustomField[] @relation("TypeMachineCustomFields") customFields CustomField[] @relation("TypeMachineCustomFields")
componentRequirements TypeMachineComponentRequirement[]
pieceRequirements TypeMachinePieceRequirement[]
@@map("type_machines") @@map("type_machines")
} }
@@ -57,8 +59,10 @@ model TypeComposant {
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
// Relations // Relations
composants Composant[] composants Composant[]
customFields CustomField[] @relation("TypeComposantCustomFields") customFields CustomField[] @relation("TypeComposantCustomFields")
models ComposantModel[]
componentRequirements TypeMachineComponentRequirement[]
@@map("type_composants") @@map("type_composants")
} }
@@ -71,8 +75,10 @@ model TypePiece {
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
// Relations // Relations
pieces Piece[] pieces Piece[]
customFields CustomField[] @relation("TypePieceCustomFields") customFields CustomField[] @relation("TypePieceCustomFields")
models PieceModel[]
pieceRequirements TypeMachinePieceRequirement[]
@@map("type_pieces") @@map("type_pieces")
} }
@@ -87,18 +93,18 @@ model Machine {
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
// Relations // Relations
siteId String siteId String
site Site @relation(fields: [siteId], references: [id], onDelete: Cascade) site Site @relation(fields: [siteId], references: [id], onDelete: Cascade)
typeMachineId String? typeMachineId String?
typeMachine TypeMachine? @relation(fields: [typeMachineId], references: [id]) typeMachine TypeMachine? @relation(fields: [typeMachineId], references: [id])
constructeurId String? constructeurId String?
constructeur Constructeur? @relation(fields: [constructeurId], references: [id], onDelete: SetNull) constructeur Constructeur? @relation(fields: [constructeurId], references: [id], onDelete: SetNull)
composants Composant[] composants Composant[]
pieces Piece[] pieces Piece[]
documents Document[] @relation("MachineDocuments") documents Document[] @relation("MachineDocuments")
customFieldValues CustomFieldValue[] @relation("MachineCustomFieldValues") customFieldValues CustomFieldValue[] @relation("MachineCustomFieldValues")
@@map("machines") @@map("machines")
@@ -114,21 +120,27 @@ model Composant {
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
// Relations hiérarchiques // Relations hiérarchiques
machineId String? machineId String?
machine Machine? @relation(fields: [machineId], references: [id], onDelete: Cascade) machine Machine? @relation(fields: [machineId], references: [id], onDelete: Cascade)
parentComposantId String? 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") sousComposants Composant[] @relation("ComposantHierarchy")
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)
pieces Piece[] pieces Piece[]
documents Document[] @relation("ComposantDocuments") documents Document[] @relation("ComposantDocuments")
customFieldValues CustomFieldValue[] @relation("ComposantCustomFieldValues") customFieldValues CustomFieldValue[] @relation("ComposantCustomFieldValues")
@@map("composants") @@map("composants")
@@ -144,19 +156,25 @@ model Piece {
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
// Relations // Relations
machineId String? machineId String?
machine Machine? @relation(fields: [machineId], references: [id], onDelete: Cascade) machine Machine? @relation(fields: [machineId], references: [id], onDelete: Cascade)
composantId String? composantId String?
composant Composant? @relation(fields: [composantId], references: [id], onDelete: Cascade) composant Composant? @relation(fields: [composantId], references: [id], onDelete: Cascade)
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)
documents Document[] @relation("PieceDocuments") documents Document[] @relation("PieceDocuments")
customFieldValues CustomFieldValue[] @relation("PieceCustomFieldValues") customFieldValues CustomFieldValue[] @relation("PieceCustomFieldValues")
@@map("pieces") @@map("pieces")
@@ -189,50 +207,50 @@ model Profile {
} }
model Document { model Document {
id String @id @default(cuid()) id String @id @default(cuid())
name String name String
filename String filename String
path String path String
mimeType String mimeType String
size Int size Int
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
// Relations polymorphiques // Relations polymorphiques
machineId String? machineId String?
machine Machine? @relation("MachineDocuments", fields: [machineId], references: [id], onDelete: Cascade) 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)
siteId String? composantId String?
site Site? @relation("SiteDocuments", fields: [siteId], references: [id], onDelete: Cascade) 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") @@map("documents")
} }
model CustomField { model CustomField {
id String @id @default(cuid()) id String @id @default(cuid())
name String name String
type String // 'string', 'number', 'boolean', 'date' type String // 'string', 'number', 'boolean', 'date'
required Boolean @default(false) required Boolean @default(false)
defaultValue String? defaultValue String?
options String[] // Pour les champs de type SELECT options String[] // Pour les champs de type SELECT
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
// Relations polymorphiques pour les types // Relations polymorphiques pour les types
typeMachineId String? typeMachineId String?
typeMachine TypeMachine? @relation("TypeMachineCustomFields", fields: [typeMachineId], references: [id], onDelete: Cascade) typeMachine TypeMachine? @relation("TypeMachineCustomFields", fields: [typeMachineId], references: [id], onDelete: Cascade)
typeComposantId String? typeComposantId String?
typeComposant TypeComposant? @relation("TypeComposantCustomFields", fields: [typeComposantId], references: [id], onDelete: Cascade) typeComposant TypeComposant? @relation("TypeComposantCustomFields", fields: [typeComposantId], references: [id], onDelete: Cascade)
typePieceId String? typePieceId String?
typePiece TypePiece? @relation("TypePieceCustomFields", fields: [typePieceId], references: [id], onDelete: Cascade) typePiece TypePiece? @relation("TypePieceCustomFields", fields: [typePieceId], references: [id], onDelete: Cascade)
// Relations avec les valeurs // Relations avec les valeurs
customFieldValues CustomFieldValue[] customFieldValues CustomFieldValue[]
@@ -241,23 +259,97 @@ model CustomField {
} }
model CustomFieldValue { model CustomFieldValue {
id String @id @default(cuid()) id String @id @default(cuid())
value String value String
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
// Relations // Relations
customFieldId String customFieldId String
customField CustomField @relation(fields: [customFieldId], references: [id], onDelete: Cascade) customField CustomField @relation(fields: [customFieldId], references: [id], onDelete: Cascade)
machineId String? machineId String?
machine Machine? @relation("MachineCustomFieldValues", fields: [machineId], references: [id], onDelete: Cascade) machine Machine? @relation("MachineCustomFieldValues", fields: [machineId], references: [id], onDelete: Cascade)
composantId String? composantId String?
composant Composant? @relation("ComposantCustomFieldValues", fields: [composantId], references: [id], onDelete: Cascade) composant Composant? @relation("ComposantCustomFieldValues", fields: [composantId], references: [id], onDelete: Cascade)
pieceId String? pieceId String?
piece Piece? @relation("PieceCustomFieldValues", fields: [pieceId], references: [id], onDelete: Cascade) piece Piece? @relation("PieceCustomFieldValues", fields: [pieceId], references: [id], onDelete: Cascade)
@@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,79 +456,81 @@ export class MachinesService {
} }
} }
// Créer les pièces du composant avec leurs champs personnalisés for (const piece of componentPieces) {
if (component.pieces) { if (!piece || !piece.name) continue;
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 }
});
// Si le type n'existe pas, le créer const pieceCustomFields = Array.isArray(piece.customFields) ? piece.customFields : [];
if (!typePiece) { const pieceModelId = piece.__pieceModelId ?? null;
typePiece = await prisma.typePiece.create({ const pieceRequirementId = piece.__requirementId ?? null;
data: { const providedTypePieceId = piece.typePieceId
name: piece.name, ?? (piece.typePiece && piece.typePiece.id ? piece.typePiece.id : null);
description: piece.description || '',
},
});
// Créer les champs personnalisés pour le type de pièce let typePieceId: string | null = providedTypePieceId ?? null;
for (const customField of piece.customFields) {
await prisma.customField.create({ if (!typePieceId && pieceCustomFields.length > 0) {
data: { let typePiece = await prisma.typePiece.findFirst({
name: customField.name, where: { name: piece.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,
},
}); });
// Créer les valeurs des champs personnalisés pour la pièce if (!typePiece) {
if (typePiece && typePiece.id) { typePiece = await prisma.typePiece.create({
const customFields = await prisma.customField.findMany({ data: {
where: { typePieceId: typePiece.id }, name: piece.name,
description: piece.description || '',
},
}); });
for (const customField of customFields) { for (const customField of pieceCustomFields) {
const defaultValue = piece.customFields?.find(cf => cf.name === customField.name)?.defaultValue || ''; await prisma.customField.create({
await prisma.customFieldValue.create({
data: { data: {
value: defaultValue, name: customField.name,
customFieldId: customField.id, type: customField.type,
pieceId: createdPiece.id, 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 (subComponents.length > 0) {
if (component.subComponents) { await this.createComponentsFromType(prisma, machineId, subComponents, createdComposant.id);
await this.createComponentsFromType(prisma, machineId, component.subComponents, createdComposant.id);
} }
} }
} }
@@ -245,19 +538,76 @@ export class MachinesService {
private async createMachinePiecesFromType(prisma: any, machineId: string, machinePieces: any[]) { private async createMachinePiecesFromType(prisma: any, machineId: string, machinePieces: any[]) {
for (const piece of machinePieces) { for (const piece of machinePieces) {
if (!piece || !piece.name) continue; 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({ const createdPiece = await prisma.piece.create({
data: { data: {
name: piece.name, name: piece.name,
machineId, reference: piece.reference || '',
constructeurId: await this.resolveConstructeurId(prisma, piece.constructeur), 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 (typePieceId) {
if (piece.customFields && piece.customFields.length > 0) { const typePieceCustomFields = await prisma.customField.findMany({
for (const customField of piece.customFields) { where: { typePieceId },
// Créer le champ personnalisé });
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 {
@@ -161,4 +237,68 @@ export class UpdateTypePieceDto {
@IsOptional() @IsOptional()
@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) {
@@ -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({ 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 },
});
}
} }