feat: Add Model gestion for piece and component
This commit is contained in:
21
package-lock.json
generated
21
package-lock.json
generated
@@ -12,6 +12,7 @@
|
||||
"@nestjs/common": "^11.0.1",
|
||||
"@nestjs/config": "^4.0.2",
|
||||
"@nestjs/core": "^11.0.1",
|
||||
"@nestjs/mapped-types": "^2.0.5",
|
||||
"@nestjs/platform-express": "^11.0.1",
|
||||
"@nestjs/typeorm": "^11.0.0",
|
||||
"@prisma/client": "^6.12.0",
|
||||
@@ -2535,6 +2536,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/mapped-types": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.1.0.tgz",
|
||||
"integrity": "sha512-W+n+rM69XsFdwORF11UqJahn4J3xi4g/ZEOlJNL6KoW5ygWSmBB2p0S2BZ4FQeS/NDH72e6xIcu35SfJnE8bXw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^10.0.0 || ^11.0.0",
|
||||
"class-transformer": "^0.4.0 || ^0.5.0",
|
||||
"class-validator": "^0.13.0 || ^0.14.0",
|
||||
"reflect-metadata": "^0.1.12 || ^0.2.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"class-transformer": {
|
||||
"optional": true
|
||||
},
|
||||
"class-validator": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express": {
|
||||
"version": "11.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.5.tgz",
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"@nestjs/common": "^11.0.1",
|
||||
"@nestjs/config": "^4.0.2",
|
||||
"@nestjs/core": "^11.0.1",
|
||||
"@nestjs/mapped-types": "^2.0.5",
|
||||
"@nestjs/platform-express": "^11.0.1",
|
||||
"@nestjs/typeorm": "^11.0.0",
|
||||
"@prisma/client": "^6.12.0",
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ModelCategory" AS ENUM ('COMPONENT', 'PIECE');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ModelType" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" VARCHAR(120) NOT NULL,
|
||||
"code" VARCHAR(60) NOT NULL,
|
||||
"category" "ModelCategory" NOT NULL,
|
||||
"notes" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "ModelType_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "ModelType_code_key" ON "ModelType"("code");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ModelType_category_name_idx" ON "ModelType"("category", "name");
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the `type_composants` table. If the table is not empty, all the data it contains will be lost.
|
||||
|
||||
*/
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "composant_models" DROP CONSTRAINT "composant_models_typeComposantId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "composants" DROP CONSTRAINT "composants_typeComposantId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "custom_fields" DROP CONSTRAINT "custom_fields_typeComposantId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "type_machine_component_requirements" DROP CONSTRAINT "type_machine_component_requirements_typeComposantId_fkey";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "ModelType" ADD COLUMN "description" TEXT;
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "type_composants";
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "composants" ADD CONSTRAINT "composants_typeComposantId_fkey" FOREIGN KEY ("typeComposantId") REFERENCES "ModelType"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "custom_fields" ADD CONSTRAINT "custom_fields_typeComposantId_fkey" FOREIGN KEY ("typeComposantId") REFERENCES "ModelType"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "composant_models" ADD CONSTRAINT "composant_models_typeComposantId_fkey" FOREIGN KEY ("typeComposantId") REFERENCES "ModelType"("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 "ModelType"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Migration: migrate legacy TypePiece references to ModelType entries
|
||||
*/
|
||||
|
||||
-- Insert existing type_pieces into ModelType if not already present
|
||||
INSERT INTO "ModelType" ("id", "name", "code", "category", "notes", "description", "createdAt", "updatedAt")
|
||||
SELECT
|
||||
tp."id",
|
||||
tp."name",
|
||||
tp."id" AS "code",
|
||||
'PIECE'::"ModelCategory",
|
||||
tp."description",
|
||||
tp."description",
|
||||
tp."createdAt",
|
||||
tp."updatedAt"
|
||||
FROM "type_pieces" tp
|
||||
ON CONFLICT ("id") DO NOTHING;
|
||||
|
||||
-- Drop existing foreign keys referencing type_pieces
|
||||
ALTER TABLE "pieces" DROP CONSTRAINT IF EXISTS "pieces_typePieceId_fkey";
|
||||
ALTER TABLE "custom_fields" DROP CONSTRAINT IF EXISTS "custom_fields_typePieceId_fkey";
|
||||
ALTER TABLE "piece_models" DROP CONSTRAINT IF EXISTS "piece_models_typePieceId_fkey";
|
||||
ALTER TABLE "type_machine_piece_requirements" DROP CONSTRAINT IF EXISTS "type_machine_piece_requirements_typePieceId_fkey";
|
||||
|
||||
-- Drop legacy table
|
||||
DROP TABLE IF EXISTS "type_pieces";
|
||||
|
||||
-- Recreate foreign keys pointing to ModelType
|
||||
ALTER TABLE "pieces"
|
||||
ADD CONSTRAINT "pieces_typePieceId_fkey"
|
||||
FOREIGN KEY ("typePieceId") REFERENCES "ModelType"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE "custom_fields"
|
||||
ADD CONSTRAINT "custom_fields_typePieceId_fkey"
|
||||
FOREIGN KEY ("typePieceId") REFERENCES "ModelType"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE "piece_models"
|
||||
ADD CONSTRAINT "piece_models_typePieceId_fkey"
|
||||
FOREIGN KEY ("typePieceId") REFERENCES "ModelType"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE "type_machine_piece_requirements"
|
||||
ADD CONSTRAINT "type_machine_piece_requirements_typePieceId_fkey"
|
||||
FOREIGN KEY ("typePieceId") REFERENCES "ModelType"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
@@ -51,38 +51,6 @@ model TypeMachine {
|
||||
@@map("type_machines")
|
||||
}
|
||||
|
||||
model TypeComposant {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
description String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
// Relations
|
||||
composants Composant[]
|
||||
customFields CustomField[] @relation("TypeComposantCustomFields")
|
||||
models ComposantModel[]
|
||||
componentRequirements TypeMachineComponentRequirement[]
|
||||
|
||||
@@map("type_composants")
|
||||
}
|
||||
|
||||
model TypePiece {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
description String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
// Relations
|
||||
pieces Piece[]
|
||||
customFields CustomField[] @relation("TypePieceCustomFields")
|
||||
models PieceModel[]
|
||||
pieceRequirements TypeMachinePieceRequirement[]
|
||||
|
||||
@@map("type_pieces")
|
||||
}
|
||||
|
||||
model Machine {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
@@ -128,7 +96,7 @@ model Composant {
|
||||
sousComposants Composant[] @relation("ComposantHierarchy")
|
||||
|
||||
typeComposantId String?
|
||||
typeComposant TypeComposant? @relation(fields: [typeComposantId], references: [id])
|
||||
typeComposant ModelType? @relation("ModelTypeComponentAssignments", fields: [typeComposantId], references: [id])
|
||||
|
||||
composantModelId String?
|
||||
composantModel ComposantModel? @relation(fields: [composantModelId], references: [id], onDelete: SetNull)
|
||||
@@ -163,7 +131,7 @@ model Piece {
|
||||
composant Composant? @relation(fields: [composantId], references: [id], onDelete: Cascade)
|
||||
|
||||
typePieceId String?
|
||||
typePiece TypePiece? @relation(fields: [typePieceId], references: [id])
|
||||
typePiece ModelType? @relation("ModelTypePieceAssignments", fields: [typePieceId], references: [id])
|
||||
|
||||
pieceModelId String?
|
||||
pieceModel PieceModel? @relation(fields: [pieceModelId], references: [id], onDelete: SetNull)
|
||||
@@ -180,6 +148,33 @@ model Piece {
|
||||
@@map("pieces")
|
||||
}
|
||||
|
||||
enum ModelCategory {
|
||||
COMPONENT
|
||||
PIECE
|
||||
}
|
||||
|
||||
model ModelType {
|
||||
id String @id @default(cuid())
|
||||
name String @db.VarChar(120)
|
||||
code String @unique @db.VarChar(60)
|
||||
category ModelCategory
|
||||
notes String? @db.Text
|
||||
description String? @db.Text
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([category, name])
|
||||
|
||||
composants Composant[] @relation("ModelTypeComponentAssignments")
|
||||
models ComposantModel[] @relation("ModelTypeComponentModels")
|
||||
componentRequirements TypeMachineComponentRequirement[] @relation("ModelTypeComponentRequirements")
|
||||
customFields CustomField[] @relation("ModelTypeCustomFields")
|
||||
pieceModels PieceModel[] @relation("ModelTypePieceModels")
|
||||
pieceRequirements TypeMachinePieceRequirement[] @relation("ModelTypePieceRequirements")
|
||||
pieces Piece[] @relation("ModelTypePieceAssignments")
|
||||
pieceCustomFields CustomField[] @relation("ModelTypePieceCustomFields")
|
||||
}
|
||||
|
||||
model Constructeur {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
@@ -247,10 +242,10 @@ model CustomField {
|
||||
typeMachine TypeMachine? @relation("TypeMachineCustomFields", fields: [typeMachineId], references: [id], onDelete: Cascade)
|
||||
|
||||
typeComposantId String?
|
||||
typeComposant TypeComposant? @relation("TypeComposantCustomFields", fields: [typeComposantId], references: [id], onDelete: Cascade)
|
||||
typeComposant ModelType? @relation("ModelTypeCustomFields", fields: [typeComposantId], references: [id], onDelete: Cascade)
|
||||
|
||||
typePieceId String?
|
||||
typePiece TypePiece? @relation("TypePieceCustomFields", fields: [typePieceId], references: [id], onDelete: Cascade)
|
||||
typePiece ModelType? @relation("ModelTypePieceCustomFields", fields: [typePieceId], references: [id], onDelete: Cascade)
|
||||
|
||||
// Relations avec les valeurs
|
||||
customFieldValues CustomFieldValue[]
|
||||
@@ -289,7 +284,7 @@ model ComposantModel {
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
typeComposantId String
|
||||
typeComposant TypeComposant @relation(fields: [typeComposantId], references: [id], onDelete: Cascade)
|
||||
typeComposant ModelType @relation("ModelTypeComponentModels", fields: [typeComposantId], references: [id], onDelete: Cascade)
|
||||
|
||||
composants Composant[]
|
||||
|
||||
@@ -305,7 +300,7 @@ model PieceModel {
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
typePieceId String
|
||||
typePiece TypePiece @relation(fields: [typePieceId], references: [id], onDelete: Cascade)
|
||||
typePiece ModelType @relation("ModelTypePieceModels", fields: [typePieceId], references: [id], onDelete: Cascade)
|
||||
|
||||
pieces Piece[]
|
||||
|
||||
@@ -326,7 +321,7 @@ model TypeMachineComponentRequirement {
|
||||
typeMachine TypeMachine @relation(fields: [typeMachineId], references: [id], onDelete: Cascade)
|
||||
|
||||
typeComposantId String
|
||||
typeComposant TypeComposant @relation(fields: [typeComposantId], references: [id])
|
||||
typeComposant ModelType @relation("ModelTypeComponentRequirements", fields: [typeComposantId], references: [id])
|
||||
|
||||
composants Composant[]
|
||||
|
||||
@@ -347,7 +342,7 @@ model TypeMachinePieceRequirement {
|
||||
typeMachine TypeMachine @relation(fields: [typeMachineId], references: [id], onDelete: Cascade)
|
||||
|
||||
typePieceId String
|
||||
typePiece TypePiece @relation(fields: [typePieceId], references: [id])
|
||||
typePiece ModelType @relation("ModelTypePieceRequirements", fields: [typePieceId], references: [id])
|
||||
|
||||
pieces Piece[]
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import { CustomFieldsModule } from './custom-fields/custom-fields.module';
|
||||
import { ConstructeursModule } from './constructeurs/constructeurs.module';
|
||||
import { ProfilesModule } from './profiles/profiles.module';
|
||||
import { SessionModule } from './session/session.module';
|
||||
import { ModelTypeModule } from './model-type/model-type.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -30,6 +31,7 @@ import { SessionModule } from './session/session.module';
|
||||
ConstructeursModule,
|
||||
ProfilesModule,
|
||||
SessionModule,
|
||||
ModelTypeModule,
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Patch,
|
||||
Param,
|
||||
Delete,
|
||||
} from '@nestjs/common';
|
||||
import { ComposantsService } from './composants.service';
|
||||
import { CreateComposantDto, UpdateComposantDto } from '../shared/dto/composant.dto';
|
||||
import {
|
||||
CreateComposantDto,
|
||||
UpdateComposantDto,
|
||||
} from '../shared/dto/composant.dto';
|
||||
|
||||
@Controller('composants')
|
||||
export class ComposantsController {
|
||||
@@ -32,7 +43,10 @@ export class ComposantsController {
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(@Param('id') id: string, @Body() updateComposantDto: UpdateComposantDto) {
|
||||
update(
|
||||
@Param('id') id: string,
|
||||
@Body() updateComposantDto: UpdateComposantDto,
|
||||
) {
|
||||
return this.composantsService.update(id, updateComposantDto);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,6 @@ import { ComposantsService } from './composants.service';
|
||||
|
||||
@Module({
|
||||
controllers: [ComposantsController],
|
||||
providers: [ComposantsService]
|
||||
providers: [ComposantsService],
|
||||
})
|
||||
export class ComposantsModule {}
|
||||
|
||||
@@ -20,7 +20,10 @@ describe('ComposantsService', () => {
|
||||
};
|
||||
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [ComposantsService, { provide: PrismaService, useValue: prisma }],
|
||||
providers: [
|
||||
ComposantsService,
|
||||
{ provide: PrismaService, useValue: prisma },
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<ComposantsService>(ComposantsService);
|
||||
@@ -53,9 +56,9 @@ describe('ComposantsService', () => {
|
||||
await expect(service.create(dto)).resolves.toEqual(created);
|
||||
|
||||
expect(prisma.composant.create).toHaveBeenCalled();
|
||||
expect(
|
||||
prisma.composant.create.mock.calls[0][0].data.typeComposantId,
|
||||
).toBe('type-comp-1');
|
||||
expect(prisma.composant.create.mock.calls[0][0].data.typeComposantId).toBe(
|
||||
'type-comp-1',
|
||||
);
|
||||
});
|
||||
|
||||
it('should refuse creation when requirement does not belong to machine skeleton', async () => {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { CreateComposantDto, UpdateComposantDto } from '../shared/dto/composant.dto';
|
||||
import {
|
||||
CreateComposantDto,
|
||||
UpdateComposantDto,
|
||||
} from '../shared/dto/composant.dto';
|
||||
|
||||
@Injectable()
|
||||
export class ComposantsService {
|
||||
@@ -324,9 +327,9 @@ export class ComposantsService {
|
||||
async findHierarchy(machineId: string) {
|
||||
// Récupérer tous les composants de premier niveau (sans parent)
|
||||
const rootComposants = await this.prisma.composant.findMany({
|
||||
where: {
|
||||
where: {
|
||||
machineId,
|
||||
parentComposantId: null
|
||||
parentComposantId: null,
|
||||
},
|
||||
include: {
|
||||
typeComposant: true,
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
import { Controller, Get, Post, Patch, Delete, Param, Body, Query } from '@nestjs/common'
|
||||
import { ConstructeursService } from './constructeurs.service'
|
||||
import { CreateConstructeurDto, UpdateConstructeurDto } from '../shared/dto/constructeur.dto'
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Patch,
|
||||
Delete,
|
||||
Param,
|
||||
Body,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { ConstructeursService } from './constructeurs.service';
|
||||
import {
|
||||
CreateConstructeurDto,
|
||||
UpdateConstructeurDto,
|
||||
} from '../shared/dto/constructeur.dto';
|
||||
|
||||
@Controller('constructeurs')
|
||||
export class ConstructeursController {
|
||||
@@ -8,26 +20,26 @@ export class ConstructeursController {
|
||||
|
||||
@Post()
|
||||
create(@Body() payload: CreateConstructeurDto) {
|
||||
return this.constructeursService.create(payload)
|
||||
return this.constructeursService.create(payload);
|
||||
}
|
||||
|
||||
@Get()
|
||||
findAll(@Query('search') search?: string) {
|
||||
return this.constructeursService.findAll(search)
|
||||
return this.constructeursService.findAll(search);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.constructeursService.findOne(id)
|
||||
return this.constructeursService.findOne(id);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(@Param('id') id: string, @Body() payload: UpdateConstructeurDto) {
|
||||
return this.constructeursService.update(id, payload)
|
||||
return this.constructeursService.update(id, payload);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.constructeursService.remove(id)
|
||||
return this.constructeursService.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { ConstructeursService } from './constructeurs.service'
|
||||
import { ConstructeursController } from './constructeurs.controller'
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConstructeursService } from './constructeurs.service';
|
||||
import { ConstructeursController } from './constructeurs.controller';
|
||||
|
||||
@Module({
|
||||
controllers: [ConstructeursController],
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { PrismaService } from '../prisma/prisma.service'
|
||||
import { CreateConstructeurDto, UpdateConstructeurDto } from '../shared/dto/constructeur.dto'
|
||||
import { Prisma } from '@prisma/client'
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import {
|
||||
CreateConstructeurDto,
|
||||
UpdateConstructeurDto,
|
||||
} from '../shared/dto/constructeur.dto';
|
||||
import { Prisma } from '@prisma/client';
|
||||
|
||||
@Injectable()
|
||||
export class ConstructeursService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
private buildSearchWhere(search?: string): Prisma.ConstructeurWhereInput {
|
||||
if (!search) return {}
|
||||
const term = search.trim()
|
||||
if (!term) return {}
|
||||
if (!search) return {};
|
||||
const term = search.trim();
|
||||
if (!term) return {};
|
||||
return {
|
||||
OR: [
|
||||
{ name: { contains: term, mode: 'insensitive' } },
|
||||
{ email: { contains: term, mode: 'insensitive' } },
|
||||
{ phone: { contains: term, mode: 'insensitive' } },
|
||||
],
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async create(data: CreateConstructeurDto) {
|
||||
@@ -28,20 +31,20 @@ export class ConstructeursService {
|
||||
email: data.email?.trim(),
|
||||
phone: data.phone?.trim(),
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
async findAll(search?: string) {
|
||||
return this.prisma.constructeur.findMany({
|
||||
where: this.buildSearchWhere(search),
|
||||
orderBy: { name: 'asc' },
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
async findOne(id: string) {
|
||||
return this.prisma.constructeur.findUnique({
|
||||
where: { id },
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
async update(id: string, data: UpdateConstructeurDto) {
|
||||
@@ -53,12 +56,12 @@ export class ConstructeursService {
|
||||
email: data.email?.trim(),
|
||||
phone: data.phone?.trim(),
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
async remove(id: string) {
|
||||
return this.prisma.constructeur.delete({
|
||||
where: { id },
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Patch,
|
||||
Param,
|
||||
Delete,
|
||||
} from '@nestjs/common';
|
||||
import { CustomFieldsService } from './custom-fields.service';
|
||||
import {
|
||||
CreateCustomFieldValueDto,
|
||||
@@ -12,8 +20,12 @@ export class CustomFieldsController {
|
||||
constructor(private readonly customFieldsService: CustomFieldsService) {}
|
||||
|
||||
@Post('values')
|
||||
createCustomFieldValue(@Body() createCustomFieldValueDto: CreateCustomFieldValueDto) {
|
||||
return this.customFieldsService.createCustomFieldValue(createCustomFieldValueDto);
|
||||
createCustomFieldValue(
|
||||
@Body() createCustomFieldValueDto: CreateCustomFieldValueDto,
|
||||
) {
|
||||
return this.customFieldsService.createCustomFieldValue(
|
||||
createCustomFieldValueDto,
|
||||
);
|
||||
}
|
||||
|
||||
@Get('values/:entityType/:entityId')
|
||||
@@ -34,7 +46,10 @@ export class CustomFieldsController {
|
||||
@Param('id') id: string,
|
||||
@Body() updateCustomFieldValueDto: UpdateCustomFieldValueDto,
|
||||
) {
|
||||
return this.customFieldsService.updateCustomFieldValue(id, updateCustomFieldValueDto);
|
||||
return this.customFieldsService.updateCustomFieldValue(
|
||||
id,
|
||||
updateCustomFieldValueDto,
|
||||
);
|
||||
}
|
||||
|
||||
@Delete('values/:id')
|
||||
@@ -51,4 +66,4 @@ export class CustomFieldsController {
|
||||
body.value,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,4 +9,4 @@ import { PrismaModule } from '../prisma/prisma.module';
|
||||
providers: [CustomFieldsService],
|
||||
exports: [CustomFieldsService],
|
||||
})
|
||||
export class CustomFieldsModule {}
|
||||
export class CustomFieldsModule {}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import {
|
||||
BadRequestException,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import {
|
||||
CreateCustomFieldValueDto,
|
||||
@@ -11,7 +15,9 @@ export class CustomFieldsService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
// Créer une valeur de champ personnalisé
|
||||
async createCustomFieldValue(createCustomFieldValueDto: CreateCustomFieldValueDto) {
|
||||
async createCustomFieldValue(
|
||||
createCustomFieldValueDto: CreateCustomFieldValueDto,
|
||||
) {
|
||||
return this.prisma.customFieldValue.create({
|
||||
data: createCustomFieldValueDto,
|
||||
include: {
|
||||
@@ -30,11 +36,16 @@ export class CustomFieldsService {
|
||||
case CustomFieldEntityType.PIECE:
|
||||
return 'pieceId' as const;
|
||||
default:
|
||||
throw new BadRequestException('Type d\'entité de champ personnalisé invalide.');
|
||||
throw new BadRequestException(
|
||||
"Type d'entité de champ personnalisé invalide.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async resolveEntityContext(entityType: CustomFieldEntityType, entityId: string) {
|
||||
private async resolveEntityContext(
|
||||
entityType: CustomFieldEntityType,
|
||||
entityId: string,
|
||||
) {
|
||||
switch (entityType) {
|
||||
case CustomFieldEntityType.MACHINE: {
|
||||
const machine = await this.prisma.machine.findUnique({
|
||||
@@ -103,7 +114,9 @@ export class CustomFieldsService {
|
||||
};
|
||||
}
|
||||
default:
|
||||
throw new BadRequestException('Type d\'entité de champ personnalisé invalide.');
|
||||
throw new BadRequestException(
|
||||
"Type d'entité de champ personnalisé invalide.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +148,10 @@ export class CustomFieldsService {
|
||||
}
|
||||
|
||||
// Mettre à jour une valeur de champ personnalisé
|
||||
async updateCustomFieldValue(id: string, updateCustomFieldValueDto: UpdateCustomFieldValueDto) {
|
||||
async updateCustomFieldValue(
|
||||
id: string,
|
||||
updateCustomFieldValueDto: UpdateCustomFieldValueDto,
|
||||
) {
|
||||
return this.prisma.customFieldValue.update({
|
||||
where: { id },
|
||||
data: updateCustomFieldValueDto,
|
||||
@@ -159,10 +175,8 @@ export class CustomFieldsService {
|
||||
entityId: string,
|
||||
value: string,
|
||||
) {
|
||||
const { typeId, customFieldTypeField, valueKey } = await this.resolveEntityContext(
|
||||
entityType,
|
||||
entityId,
|
||||
);
|
||||
const { typeId, customFieldTypeField, valueKey } =
|
||||
await this.resolveEntityContext(entityType, entityId);
|
||||
|
||||
const allowedCustomField = await this.prisma.customField.findFirst({
|
||||
where: {
|
||||
@@ -173,7 +187,7 @@ export class CustomFieldsService {
|
||||
|
||||
if (!allowedCustomField) {
|
||||
throw new BadRequestException(
|
||||
'Le champ personnalisé n\'est pas autorisé pour cette entité.',
|
||||
"Le champ personnalisé n'est pas autorisé pour cette entité.",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Patch,
|
||||
Param,
|
||||
Delete,
|
||||
} from '@nestjs/common';
|
||||
import { DocumentsService } from './documents.service';
|
||||
import { CreateDocumentDto, UpdateDocumentDto } from '../shared/dto/document.dto';
|
||||
import {
|
||||
CreateDocumentDto,
|
||||
UpdateDocumentDto,
|
||||
} from '../shared/dto/document.dto';
|
||||
|
||||
@Controller('documents')
|
||||
export class DocumentsController {
|
||||
@@ -42,7 +53,10 @@ export class DocumentsController {
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(@Param('id') id: string, @Body() updateDocumentDto: UpdateDocumentDto) {
|
||||
update(
|
||||
@Param('id') id: string,
|
||||
@Body() updateDocumentDto: UpdateDocumentDto,
|
||||
) {
|
||||
return this.documentsService.update(id, updateDocumentDto);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,6 @@ import { DocumentsService } from './documents.service';
|
||||
|
||||
@Module({
|
||||
controllers: [DocumentsController],
|
||||
providers: [DocumentsService]
|
||||
providers: [DocumentsService],
|
||||
})
|
||||
export class DocumentsModule {}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { CreateDocumentDto, UpdateDocumentDto } from '../shared/dto/document.dto';
|
||||
import {
|
||||
CreateDocumentDto,
|
||||
UpdateDocumentDto,
|
||||
} from '../shared/dto/document.dto';
|
||||
|
||||
@Injectable()
|
||||
export class DocumentsService {
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Patch,
|
||||
Param,
|
||||
Delete,
|
||||
} from '@nestjs/common';
|
||||
import { MachinesService } from './machines.service';
|
||||
import {
|
||||
CreateMachineDto,
|
||||
|
||||
@@ -4,6 +4,6 @@ import { MachinesService } from './machines.service';
|
||||
|
||||
@Module({
|
||||
controllers: [MachinesController],
|
||||
providers: [MachinesService]
|
||||
providers: [MachinesService],
|
||||
})
|
||||
export class MachinesModule {}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ModelCategory } from '@prisma/client';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import {
|
||||
CreateMachineDto,
|
||||
@@ -90,6 +91,36 @@ const MACHINE_DEFAULT_INCLUDE = {
|
||||
export class MachinesService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
private slugifyName(name: string): string {
|
||||
return name
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/^-+|-+$/g, '')
|
||||
.replace(/-+/g, '-');
|
||||
}
|
||||
|
||||
private async generateUniqueComponentTypeCode(
|
||||
prisma: any,
|
||||
name: string,
|
||||
): Promise<string> {
|
||||
const base = this.slugifyName(name) || 'type';
|
||||
let candidate = base;
|
||||
let suffix = 1;
|
||||
|
||||
while (
|
||||
await prisma.modelType.findUnique({
|
||||
where: { code: candidate },
|
||||
select: { id: true },
|
||||
})
|
||||
) {
|
||||
candidate = `${base}-${suffix++}`;
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
|
||||
private async getTypeMachineConfiguration(typeMachineId: string) {
|
||||
const typeMachine = await this.prisma.typeMachine.findUnique({
|
||||
where: { id: typeMachineId },
|
||||
@@ -108,25 +139,40 @@ export class MachinesService {
|
||||
componentSelections: MachineComponentSelectionDto[],
|
||||
pieceSelections: MachinePieceSelectionDto[],
|
||||
) {
|
||||
const componentRequirements = (Array.isArray(typeMachine.componentRequirements)
|
||||
? typeMachine.componentRequirements
|
||||
: []) as any[];
|
||||
const pieceRequirements = (Array.isArray(typeMachine.pieceRequirements)
|
||||
? typeMachine.pieceRequirements
|
||||
: []) as any[];
|
||||
const componentRequirements = (
|
||||
Array.isArray(typeMachine.componentRequirements)
|
||||
? typeMachine.componentRequirements
|
||||
: []
|
||||
) as any[];
|
||||
const pieceRequirements = (
|
||||
Array.isArray(typeMachine.pieceRequirements)
|
||||
? typeMachine.pieceRequirements
|
||||
: []
|
||||
) as any[];
|
||||
|
||||
const componentRequirementMap = new Map(
|
||||
componentRequirements.map((requirement: any) => [requirement.id, requirement]),
|
||||
componentRequirements.map((requirement: any) => [
|
||||
requirement.id,
|
||||
requirement,
|
||||
]),
|
||||
);
|
||||
const pieceRequirementMap = new Map(
|
||||
pieceRequirements.map((requirement: any) => [requirement.id, requirement]),
|
||||
pieceRequirements.map((requirement: any) => [
|
||||
requirement.id,
|
||||
requirement,
|
||||
]),
|
||||
);
|
||||
|
||||
const componentSelectionMap = new Map<string, MachineComponentSelectionDto[]>();
|
||||
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}`);
|
||||
throw new Error(
|
||||
`Sélection de composant invalide: requirementId=${selection.requirementId}`,
|
||||
);
|
||||
}
|
||||
if (!componentSelectionMap.has(requirement.id)) {
|
||||
componentSelectionMap.set(requirement.id, []);
|
||||
@@ -138,7 +184,16 @@ export class MachinesService {
|
||||
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}`);
|
||||
throw new Error(
|
||||
`Sélection de pièce invalide: requirementId=${selection.requirementId}`,
|
||||
);
|
||||
}
|
||||
if (!selection.pieceModelId) {
|
||||
throw new Error(
|
||||
`Le groupe de pièces "${
|
||||
requirement.label || requirement.typePiece?.name || requirement.id
|
||||
}" nécessite la sélection d'un modèle de pièce.`,
|
||||
);
|
||||
}
|
||||
if (!pieceSelectionMap.has(requirement.id)) {
|
||||
pieceSelectionMap.set(requirement.id, []);
|
||||
@@ -147,24 +202,36 @@ export class MachinesService {
|
||||
}
|
||||
|
||||
const componentModelIds = Array.from(
|
||||
new Set(componentSelections.map((selection) => selection.componentModelId).filter(Boolean)),
|
||||
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 componentModelMap = new Map(
|
||||
componentModels.map((model) => [model.id, model]),
|
||||
);
|
||||
|
||||
const pieceModelIds = Array.from(
|
||||
new Set(pieceSelections.map((selection) => selection.pieceModelId).filter(Boolean)),
|
||||
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]));
|
||||
const pieceModelMap = new Map(
|
||||
pieceModels.map((model) => [model.id, model]),
|
||||
);
|
||||
|
||||
for (const requirement of componentRequirements) {
|
||||
const selections = componentSelectionMap.get(requirement.id) ?? [];
|
||||
@@ -184,7 +251,9 @@ export class MachinesService {
|
||||
}
|
||||
|
||||
if (!requirement.allowNewModels) {
|
||||
const missingModel = selections.find((selection) => !selection.componentModelId);
|
||||
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.`,
|
||||
@@ -210,14 +279,6 @@ export class MachinesService {
|
||||
);
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -226,11 +287,15 @@ export class MachinesService {
|
||||
}
|
||||
const model = componentModelMap.get(selection.componentModelId);
|
||||
if (!model) {
|
||||
throw new Error(`Modèle de composant introuvable: ${selection.componentModelId}`);
|
||||
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}`);
|
||||
throw new Error(
|
||||
`Requirement de composant introuvable: ${selection.requirementId}`,
|
||||
);
|
||||
}
|
||||
if (model.typeComposantId !== requirement.typeComposantId) {
|
||||
throw new Error(
|
||||
@@ -240,16 +305,17 @@ export class MachinesService {
|
||||
}
|
||||
|
||||
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}`);
|
||||
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}`);
|
||||
throw new Error(
|
||||
`Requirement de pièce introuvable: ${selection.requirementId}`,
|
||||
);
|
||||
}
|
||||
if (model.typePieceId !== requirement.typePieceId) {
|
||||
throw new Error(
|
||||
@@ -274,24 +340,36 @@ export class MachinesService {
|
||||
} = createMachineDto;
|
||||
|
||||
if (!machineData.typeMachineId) {
|
||||
throw new Error('typeMachineId est requis pour créer une machine à partir d\'un squelette.');
|
||||
throw new Error(
|
||||
"typeMachineId est requis pour créer une machine à partir d'un squelette.",
|
||||
);
|
||||
}
|
||||
|
||||
const typeMachine = await this.getTypeMachineConfiguration(machineData.typeMachineId);
|
||||
const typeMachine = await this.getTypeMachineConfiguration(
|
||||
machineData.typeMachineId,
|
||||
);
|
||||
|
||||
const {
|
||||
componentSelectionMap,
|
||||
pieceSelectionMap,
|
||||
componentModelMap,
|
||||
pieceModelMap,
|
||||
} = await this.buildConfigurationContext(typeMachine, componentSelections, pieceSelections);
|
||||
} = await this.buildConfigurationContext(
|
||||
typeMachine,
|
||||
componentSelections,
|
||||
pieceSelections,
|
||||
);
|
||||
|
||||
const componentRequirements = (Array.isArray(typeMachine.componentRequirements)
|
||||
? typeMachine.componentRequirements
|
||||
: []) as any[];
|
||||
const pieceRequirements = (Array.isArray(typeMachine.pieceRequirements)
|
||||
? typeMachine.pieceRequirements
|
||||
: []) as any[];
|
||||
const componentRequirements = (
|
||||
Array.isArray(typeMachine.componentRequirements)
|
||||
? typeMachine.componentRequirements
|
||||
: []
|
||||
) as any[];
|
||||
const pieceRequirements = (
|
||||
Array.isArray(typeMachine.pieceRequirements)
|
||||
? typeMachine.pieceRequirements
|
||||
: []
|
||||
) as any[];
|
||||
|
||||
return this.prisma.$transaction(async (prisma) => {
|
||||
const machine = await prisma.machine.create({
|
||||
@@ -307,15 +385,27 @@ export class MachinesService {
|
||||
for (const requirement of 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]);
|
||||
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);
|
||||
await this.createComponentsFromType(
|
||||
prisma,
|
||||
machine.id,
|
||||
legacyComponents,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,20 +413,36 @@ export class MachinesService {
|
||||
for (const requirement of 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]);
|
||||
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);
|
||||
await this.createMachinePiecesFromType(
|
||||
prisma,
|
||||
machine.id,
|
||||
legacyPieces,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeMachine.customFields && typeMachine.customFields.length > 0) {
|
||||
await this.createMachineCustomFieldsFromType(prisma, machine.id, typeMachine.customFields);
|
||||
await this.createMachineCustomFieldsFromType(
|
||||
prisma,
|
||||
machine.id,
|
||||
typeMachine.customFields,
|
||||
);
|
||||
}
|
||||
|
||||
return prisma.machine.findUnique({
|
||||
@@ -371,28 +477,43 @@ export class MachinesService {
|
||||
requirement: any,
|
||||
model?: any,
|
||||
): any {
|
||||
const baseDefinition = selection.definition ?? (model?.structure ?? {});
|
||||
const baseDefinition = selection.definition ?? model?.structure ?? {};
|
||||
const definition = this.cloneStructure(baseDefinition);
|
||||
const prepared: any = definition && typeof definition === 'object' && !Array.isArray(definition) ? definition : {};
|
||||
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.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.customFields = Array.isArray(prepared.customFields)
|
||||
? prepared.customFields
|
||||
: [];
|
||||
prepared.pieces = Array.isArray(prepared.pieces)
|
||||
? prepared.pieces
|
||||
: prepared.pieces
|
||||
? [prepared.pieces]
|
||||
: [];
|
||||
? [prepared.pieces]
|
||||
: [];
|
||||
prepared.subComponents = Array.isArray(prepared.subComponents)
|
||||
? prepared.subComponents
|
||||
: prepared.subComponents
|
||||
? [prepared.subComponents]
|
||||
: [];
|
||||
? [prepared.subComponents]
|
||||
: [];
|
||||
|
||||
prepared.typeComposantId = prepared.typeComposantId || requirement?.typeComposantId || model?.typeComposantId || null;
|
||||
prepared.typeComposantId =
|
||||
prepared.typeComposantId ||
|
||||
requirement?.typeComposantId ||
|
||||
model?.typeComposantId ||
|
||||
null;
|
||||
prepared.__componentModelId = selection.componentModelId ?? null;
|
||||
prepared.__requirementId = requirement?.id ?? null;
|
||||
|
||||
@@ -404,43 +525,80 @@ export class MachinesService {
|
||||
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 : {};
|
||||
if (!model) {
|
||||
throw new Error(
|
||||
`Modèle de pièce introuvable: ${selection.pieceModelId}`,
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
const baseDefinition = model.structure ?? {};
|
||||
const definition = this.cloneStructure(baseDefinition);
|
||||
const prepared: any =
|
||||
definition && typeof definition === 'object' && !Array.isArray(definition)
|
||||
? definition
|
||||
: {};
|
||||
|
||||
prepared.name = prepared.name || model.name || 'Pièce';
|
||||
prepared.customFields = Array.isArray(prepared.customFields)
|
||||
? prepared.customFields
|
||||
: [];
|
||||
prepared.typePieceId =
|
||||
prepared.typePieceId ||
|
||||
model.typePieceId ||
|
||||
requirement?.typePieceId ||
|
||||
null;
|
||||
prepared.__pieceModelId = selection.pieceModelId;
|
||||
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) {
|
||||
if (!component || !component.name) continue;
|
||||
|
||||
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 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);
|
||||
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 },
|
||||
let typeComposant = await prisma.modelType.findFirst({
|
||||
where: {
|
||||
name: component.name,
|
||||
category: ModelCategory.COMPONENT,
|
||||
},
|
||||
});
|
||||
|
||||
if (!typeComposant) {
|
||||
typeComposant = await prisma.typeComposant.create({
|
||||
typeComposant = await prisma.modelType.create({
|
||||
data: {
|
||||
name: component.name,
|
||||
code: await this.generateUniqueComponentTypeCode(
|
||||
prisma,
|
||||
component.name,
|
||||
),
|
||||
category: ModelCategory.COMPONENT,
|
||||
description: component.description || '',
|
||||
},
|
||||
});
|
||||
@@ -466,7 +624,10 @@ export class MachinesService {
|
||||
data: {
|
||||
name: component.name,
|
||||
reference: component.reference || '',
|
||||
constructeurId: await this.resolveConstructeurId(prisma, component.constructeur),
|
||||
constructeurId: await this.resolveConstructeurId(
|
||||
prisma,
|
||||
component.constructeur,
|
||||
),
|
||||
emplacement: component.emplacement || '',
|
||||
prix: component.prix ?? null,
|
||||
machineId,
|
||||
@@ -483,7 +644,9 @@ export class MachinesService {
|
||||
});
|
||||
|
||||
for (const customField of typeCustomFields) {
|
||||
const defaultValue = customFields.find((cf) => cf.name === customField.name)?.defaultValue || '';
|
||||
const defaultValue =
|
||||
customFields.find((cf) => cf.name === customField.name)
|
||||
?.defaultValue || '';
|
||||
await prisma.customFieldValue.create({
|
||||
data: {
|
||||
value: defaultValue,
|
||||
@@ -497,11 +660,14 @@ export class MachinesService {
|
||||
for (const piece of componentPieces) {
|
||||
if (!piece || !piece.name) continue;
|
||||
|
||||
const pieceCustomFields = Array.isArray(piece.customFields) ? piece.customFields : [];
|
||||
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);
|
||||
const providedTypePieceId =
|
||||
piece.typePieceId ??
|
||||
(piece.typePiece && piece.typePiece.id ? piece.typePiece.id : null);
|
||||
|
||||
let typePieceId: string | null = providedTypePieceId ?? null;
|
||||
|
||||
@@ -539,7 +705,10 @@ export class MachinesService {
|
||||
data: {
|
||||
name: piece.name,
|
||||
reference: piece.reference || '',
|
||||
constructeurId: await this.resolveConstructeurId(prisma, piece.constructeur),
|
||||
constructeurId: await this.resolveConstructeurId(
|
||||
prisma,
|
||||
piece.constructeur,
|
||||
),
|
||||
emplacement: piece.emplacement || '',
|
||||
prix: piece.prix ?? null,
|
||||
composantId: createdComposant.id,
|
||||
@@ -555,7 +724,9 @@ export class MachinesService {
|
||||
});
|
||||
|
||||
for (const customField of typePieceCustomFields) {
|
||||
const defaultValue = pieceCustomFields.find((cf) => cf.name === customField.name)?.defaultValue || '';
|
||||
const defaultValue =
|
||||
pieceCustomFields.find((cf) => cf.name === customField.name)
|
||||
?.defaultValue || '';
|
||||
await prisma.customFieldValue.create({
|
||||
data: {
|
||||
value: defaultValue,
|
||||
@@ -568,20 +739,32 @@ export class MachinesService {
|
||||
}
|
||||
|
||||
if (subComponents.length > 0) {
|
||||
await this.createComponentsFromType(prisma, machineId, subComponents, createdComposant.id);
|
||||
await this.createComponentsFromType(
|
||||
prisma,
|
||||
machineId,
|
||||
subComponents,
|
||||
createdComposant.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async createMachinePiecesFromType(prisma: any, machineId: string, machinePieces: any[]) {
|
||||
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 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);
|
||||
const providedTypePieceId =
|
||||
piece.typePieceId ??
|
||||
(piece.typePiece && piece.typePiece.id ? piece.typePiece.id : null);
|
||||
|
||||
let typePieceId: string | null = providedTypePieceId ?? null;
|
||||
|
||||
@@ -619,7 +802,10 @@ export class MachinesService {
|
||||
data: {
|
||||
name: piece.name,
|
||||
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,
|
||||
@@ -635,7 +821,9 @@ export class MachinesService {
|
||||
});
|
||||
|
||||
for (const customField of typePieceCustomFields) {
|
||||
const defaultValue = customFields.find((cf) => cf.name === customField.name)?.defaultValue || '';
|
||||
const defaultValue =
|
||||
customFields.find((cf) => cf.name === customField.name)
|
||||
?.defaultValue || '';
|
||||
await prisma.customFieldValue.create({
|
||||
data: {
|
||||
value: defaultValue,
|
||||
@@ -669,10 +857,14 @@ export class MachinesService {
|
||||
}
|
||||
}
|
||||
|
||||
private async createMachineCustomFieldsFromType(prisma: any, machineId: string, machineCustomFields: any[]) {
|
||||
private async createMachineCustomFieldsFromType(
|
||||
prisma: any,
|
||||
machineId: string,
|
||||
machineCustomFields: any[],
|
||||
) {
|
||||
for (const customField of machineCustomFields) {
|
||||
if (!customField || !customField.name) continue;
|
||||
|
||||
|
||||
// Créer le champ personnalisé pour la machine
|
||||
const createdCustomField = await prisma.customField.create({
|
||||
data: {
|
||||
@@ -710,10 +902,8 @@ export class MachinesService {
|
||||
}
|
||||
|
||||
async reconfigure(id: string, reconfigureMachineDto: ReconfigureMachineDto) {
|
||||
const {
|
||||
componentSelections = [],
|
||||
pieceSelections = [],
|
||||
} = reconfigureMachineDto;
|
||||
const { componentSelections = [], pieceSelections = [] } =
|
||||
reconfigureMachineDto;
|
||||
|
||||
const machine = await this.prisma.machine.findUnique({
|
||||
where: { id },
|
||||
@@ -729,7 +919,9 @@ export class MachinesService {
|
||||
}
|
||||
|
||||
if (!machine.typeMachineId || !machine.typeMachine) {
|
||||
throw new Error('Impossible de reconfigurer une machine sans type de machine associé.');
|
||||
throw new Error(
|
||||
'Impossible de reconfigurer une machine sans type de machine associé.',
|
||||
);
|
||||
}
|
||||
|
||||
const typeMachine = machine.typeMachine;
|
||||
@@ -739,14 +931,22 @@ export class MachinesService {
|
||||
pieceSelectionMap,
|
||||
componentModelMap,
|
||||
pieceModelMap,
|
||||
} = await this.buildConfigurationContext(typeMachine, componentSelections, pieceSelections);
|
||||
} = await this.buildConfigurationContext(
|
||||
typeMachine,
|
||||
componentSelections,
|
||||
pieceSelections,
|
||||
);
|
||||
|
||||
const componentRequirements = (Array.isArray(typeMachine.componentRequirements)
|
||||
? typeMachine.componentRequirements
|
||||
: []) as any[];
|
||||
const pieceRequirements = (Array.isArray(typeMachine.pieceRequirements)
|
||||
? typeMachine.pieceRequirements
|
||||
: []) as any[];
|
||||
const componentRequirements = (
|
||||
Array.isArray(typeMachine.componentRequirements)
|
||||
? typeMachine.componentRequirements
|
||||
: []
|
||||
) as any[];
|
||||
const pieceRequirements = (
|
||||
Array.isArray(typeMachine.pieceRequirements)
|
||||
? typeMachine.pieceRequirements
|
||||
: []
|
||||
) as any[];
|
||||
|
||||
return this.prisma.$transaction(async (prisma) => {
|
||||
await prisma.customFieldValue.deleteMany({
|
||||
@@ -797,7 +997,11 @@ export class MachinesService {
|
||||
const model = selection.componentModelId
|
||||
? componentModelMap.get(selection.componentModelId)
|
||||
: undefined;
|
||||
const definition = this.normalizeComponentSelection(selection, requirement, model);
|
||||
const definition = this.normalizeComponentSelection(
|
||||
selection,
|
||||
requirement,
|
||||
model,
|
||||
);
|
||||
await this.createComponentsFromType(prisma, id, [definition]);
|
||||
}
|
||||
}
|
||||
@@ -815,7 +1019,11 @@ export class MachinesService {
|
||||
const model = selection.pieceModelId
|
||||
? pieceModelMap.get(selection.pieceModelId)
|
||||
: undefined;
|
||||
const definition = this.normalizePieceSelection(selection, requirement, model);
|
||||
const definition = this.normalizePieceSelection(
|
||||
selection,
|
||||
requirement,
|
||||
model,
|
||||
);
|
||||
await this.createMachinePiecesFromType(prisma, id, [definition]);
|
||||
}
|
||||
}
|
||||
@@ -842,9 +1050,9 @@ export class MachinesService {
|
||||
}
|
||||
|
||||
private async resolveConstructeurId(prisma: any, rawName?: string) {
|
||||
if (!rawName) return null
|
||||
const name = String(rawName).trim()
|
||||
if (!name) return null
|
||||
if (!rawName) return null;
|
||||
const name = String(rawName).trim();
|
||||
if (!name) return null;
|
||||
|
||||
const existing = await prisma.constructeur.findFirst({
|
||||
where: {
|
||||
@@ -853,15 +1061,15 @@ export class MachinesService {
|
||||
mode: 'insensitive',
|
||||
},
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
if (existing) return existing.id
|
||||
if (existing) return existing.id;
|
||||
|
||||
const created = await prisma.constructeur.create({
|
||||
data: { name },
|
||||
})
|
||||
});
|
||||
|
||||
return created.id
|
||||
return created.id;
|
||||
}
|
||||
|
||||
async remove(id: string) {
|
||||
@@ -983,17 +1191,31 @@ export class MachinesService {
|
||||
|
||||
// Traiter les composants existants
|
||||
for (const component of machine.composants) {
|
||||
const typeComponent = components.find((c: any) => c.name === component.name);
|
||||
if (typeComponent && typeComponent.customFields && typeComponent.customFields.length > 0) {
|
||||
const typeComponent = components.find(
|
||||
(c: any) => c.name === component.name,
|
||||
);
|
||||
if (
|
||||
typeComponent &&
|
||||
typeComponent.customFields &&
|
||||
typeComponent.customFields.length > 0
|
||||
) {
|
||||
// Créer le type de composant s'il n'existe pas
|
||||
let typeComposant = await this.prisma.typeComposant.findFirst({
|
||||
where: { name: component.name },
|
||||
let typeComposant = await this.prisma.modelType.findFirst({
|
||||
where: {
|
||||
name: component.name,
|
||||
category: ModelCategory.COMPONENT,
|
||||
},
|
||||
});
|
||||
|
||||
if (!typeComposant) {
|
||||
typeComposant = await this.prisma.typeComposant.create({
|
||||
typeComposant = await this.prisma.modelType.create({
|
||||
data: {
|
||||
name: component.name,
|
||||
code: await this.generateUniqueComponentTypeCode(
|
||||
this.prisma,
|
||||
component.name,
|
||||
),
|
||||
category: ModelCategory.COMPONENT,
|
||||
description: typeComponent.description || '',
|
||||
},
|
||||
});
|
||||
@@ -1042,7 +1264,10 @@ export class MachinesService {
|
||||
});
|
||||
|
||||
if (!existingValue) {
|
||||
const defaultValue = typeComponent.customFields.find((cf: any) => cf.name === customField.name)?.defaultValue || '';
|
||||
const defaultValue =
|
||||
typeComponent.customFields.find(
|
||||
(cf: any) => cf.name === customField.name,
|
||||
)?.defaultValue || '';
|
||||
await this.prisma.customFieldValue.create({
|
||||
data: {
|
||||
value: defaultValue,
|
||||
@@ -1055,18 +1280,33 @@ export class MachinesService {
|
||||
|
||||
// Traiter les pièces du composant
|
||||
for (const piece of component.pieces) {
|
||||
const typePiece = typeComponent.pieces?.find((p: any) => p.name === piece.name);
|
||||
if (typePiece && typePiece.customFields && typePiece.customFields.length > 0) {
|
||||
const typePiece = typeComponent.pieces?.find(
|
||||
(p: any) => p.name === piece.name,
|
||||
);
|
||||
if (
|
||||
typePiece &&
|
||||
typePiece.customFields &&
|
||||
typePiece.customFields.length > 0
|
||||
) {
|
||||
// Créer le type de pièce s'il n'existe pas
|
||||
let typePieceEntity = await this.prisma.typePiece.findFirst({
|
||||
where: { name: piece.name },
|
||||
let typePieceEntity = await this.prisma.modelType.findFirst({
|
||||
where: {
|
||||
name: piece.name,
|
||||
category: ModelCategory.PIECE,
|
||||
},
|
||||
});
|
||||
|
||||
if (!typePieceEntity) {
|
||||
typePieceEntity = await this.prisma.typePiece.create({
|
||||
typePieceEntity = await this.prisma.modelType.create({
|
||||
data: {
|
||||
name: piece.name,
|
||||
code: await this.generateUniqueComponentTypeCode(
|
||||
this.prisma,
|
||||
piece.name,
|
||||
),
|
||||
category: ModelCategory.PIECE,
|
||||
description: typePiece.description || '',
|
||||
notes: typePiece.description || '',
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1106,15 +1346,19 @@ export class MachinesService {
|
||||
});
|
||||
|
||||
for (const customField of customFields) {
|
||||
const existingValue = await this.prisma.customFieldValue.findFirst({
|
||||
where: {
|
||||
customFieldId: customField.id,
|
||||
pieceId: piece.id,
|
||||
},
|
||||
});
|
||||
const existingValue =
|
||||
await this.prisma.customFieldValue.findFirst({
|
||||
where: {
|
||||
customFieldId: customField.id,
|
||||
pieceId: piece.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (!existingValue) {
|
||||
const defaultValue = typePiece.customFields.find((cf: any) => cf.name === customField.name)?.defaultValue || '';
|
||||
const defaultValue =
|
||||
typePiece.customFields.find(
|
||||
(cf: any) => cf.name === customField.name,
|
||||
)?.defaultValue || '';
|
||||
await this.prisma.customFieldValue.create({
|
||||
data: {
|
||||
value: defaultValue,
|
||||
@@ -1132,17 +1376,30 @@ export class MachinesService {
|
||||
// Traiter les pièces de machine
|
||||
for (const piece of machine.pieces) {
|
||||
const typePiece = machinePieces.find((p: any) => p.name === piece.name);
|
||||
if (typePiece && typePiece.customFields && typePiece.customFields.length > 0) {
|
||||
if (
|
||||
typePiece &&
|
||||
typePiece.customFields &&
|
||||
typePiece.customFields.length > 0
|
||||
) {
|
||||
// Créer le type de pièce s'il n'existe pas
|
||||
let typePieceEntity = await this.prisma.typePiece.findFirst({
|
||||
where: { name: piece.name },
|
||||
let typePieceEntity = await this.prisma.modelType.findFirst({
|
||||
where: {
|
||||
name: piece.name,
|
||||
category: ModelCategory.PIECE,
|
||||
},
|
||||
});
|
||||
|
||||
if (!typePieceEntity) {
|
||||
typePieceEntity = await this.prisma.typePiece.create({
|
||||
typePieceEntity = await this.prisma.modelType.create({
|
||||
data: {
|
||||
name: piece.name,
|
||||
code: await this.generateUniqueComponentTypeCode(
|
||||
this.prisma,
|
||||
piece.name,
|
||||
),
|
||||
category: ModelCategory.PIECE,
|
||||
description: typePiece.description || '',
|
||||
notes: typePiece.description || '',
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1190,7 +1447,10 @@ export class MachinesService {
|
||||
});
|
||||
|
||||
if (!existingValue) {
|
||||
const defaultValue = typePiece.customFields.find((cf: any) => cf.name === customField.name)?.defaultValue || '';
|
||||
const defaultValue =
|
||||
typePiece.customFields.find(
|
||||
(cf: any) => cf.name === customField.name,
|
||||
)?.defaultValue || '';
|
||||
await this.prisma.customFieldValue.create({
|
||||
data: {
|
||||
value: defaultValue,
|
||||
|
||||
28
src/model-type/dto/create-model-type.dto.ts
Normal file
28
src/model-type/dto/create-model-type.dto.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { IsEnum, IsOptional, IsString, Length, Matches } from 'class-validator';
|
||||
|
||||
export enum ModelCategory {
|
||||
COMPONENT = 'COMPONENT',
|
||||
PIECE = 'PIECE',
|
||||
}
|
||||
|
||||
export class CreateModelTypeDto {
|
||||
@IsString()
|
||||
@Length(2, 120)
|
||||
name!: string;
|
||||
|
||||
@IsString()
|
||||
@Length(2, 60)
|
||||
@Matches(/^[a-z0-9\-_.]+$/i)
|
||||
code!: string;
|
||||
|
||||
@IsEnum(ModelCategory)
|
||||
category!: ModelCategory;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
notes?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string;
|
||||
}
|
||||
5
src/model-type/dto/update-model-type.dto.ts
Normal file
5
src/model-type/dto/update-model-type.dto.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateModelTypeDto } from './create-model-type.dto';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
export class UpdateModelTypeDto extends PartialType(CreateModelTypeDto) {}
|
||||
80
src/model-type/model-type.controller.ts
Normal file
80
src/model-type/model-type.controller.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Header,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
Param,
|
||||
Patch,
|
||||
Post,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { CreateModelTypeDto, ModelCategory } from './dto/create-model-type.dto';
|
||||
import { UpdateModelTypeDto } from './dto/update-model-type.dto';
|
||||
import { ModelTypeService } from './model-type.service';
|
||||
|
||||
@Controller('api/model-types')
|
||||
export class ModelTypeController {
|
||||
constructor(private readonly modelTypeService: ModelTypeService) {}
|
||||
|
||||
@Get()
|
||||
list(
|
||||
@Query('q') q?: string,
|
||||
@Query('category') category?: string,
|
||||
@Query('sort') sort?: 'name' | 'code' | 'createdAt',
|
||||
@Query('dir') dir?: 'asc' | 'desc',
|
||||
@Query('limit') limit?: string,
|
||||
@Query('offset') offset?: string,
|
||||
) {
|
||||
return this.modelTypeService.list({
|
||||
q,
|
||||
category: this.normalizeCategory(category),
|
||||
sort,
|
||||
dir,
|
||||
limit: this.parsePositiveNumber(limit),
|
||||
offset: this.parsePositiveNumber(offset),
|
||||
});
|
||||
}
|
||||
|
||||
@Post()
|
||||
@Header('Cache-Control', 'no-store')
|
||||
create(@Body() dto: CreateModelTypeDto) {
|
||||
return this.modelTypeService.create(dto);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.modelTypeService.findOne(id);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
@Header('Cache-Control', 'no-store')
|
||||
update(@Param('id') id: string, @Body() dto: UpdateModelTypeDto) {
|
||||
return this.modelTypeService.update(id, dto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@Header('Cache-Control', 'no-store')
|
||||
@HttpCode(HttpStatus.NO_CONTENT)
|
||||
async remove(@Param('id') id: string) {
|
||||
await this.modelTypeService.remove(id);
|
||||
}
|
||||
|
||||
private normalizeCategory(value?: string) {
|
||||
if (!value) return undefined;
|
||||
return Object.values(ModelCategory).includes(value as ModelCategory)
|
||||
? (value as ModelCategory)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
private parsePositiveNumber(value?: string) {
|
||||
if (value === undefined) return undefined;
|
||||
const parsed = Number.parseInt(value, 10);
|
||||
if (Number.isNaN(parsed) || parsed < 0) {
|
||||
return undefined;
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
9
src/model-type/model-type.module.ts
Normal file
9
src/model-type/model-type.module.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ModelTypeController } from './model-type.controller';
|
||||
import { ModelTypeService } from './model-type.service';
|
||||
|
||||
@Module({
|
||||
controllers: [ModelTypeController],
|
||||
providers: [ModelTypeService],
|
||||
})
|
||||
export class ModelTypeModule {}
|
||||
159
src/model-type/model-type.service.ts
Normal file
159
src/model-type/model-type.service.ts
Normal file
@@ -0,0 +1,159 @@
|
||||
import {
|
||||
ConflictException,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { ModelType as PrismaModelType, Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { CreateModelTypeDto, ModelCategory } from './dto/create-model-type.dto';
|
||||
import { UpdateModelTypeDto } from './dto/update-model-type.dto';
|
||||
|
||||
type SortField = 'name' | 'code' | 'createdAt';
|
||||
type SortDirection = 'asc' | 'desc';
|
||||
|
||||
interface ListParams {
|
||||
q?: string;
|
||||
category?: ModelCategory;
|
||||
sort?: SortField;
|
||||
dir?: SortDirection;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ModelTypeService {
|
||||
private readonly allowedSortFields: SortField[] = [
|
||||
'name',
|
||||
'code',
|
||||
'createdAt',
|
||||
];
|
||||
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async list(params: ListParams): Promise<{
|
||||
items: PrismaModelType[];
|
||||
total: number;
|
||||
offset: number;
|
||||
limit: number;
|
||||
}> {
|
||||
const {
|
||||
q,
|
||||
category,
|
||||
sort = 'createdAt',
|
||||
dir = 'desc',
|
||||
limit = 20,
|
||||
offset = 0,
|
||||
} = params;
|
||||
|
||||
const cappedLimit = Math.min(Math.max(limit ?? 20, 1), 100);
|
||||
const safeOffset = Math.max(offset ?? 0, 0);
|
||||
|
||||
const orderByField = this.allowedSortFields.includes(sort)
|
||||
? sort
|
||||
: 'createdAt';
|
||||
const orderByDir: SortDirection = dir === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
const where: Prisma.ModelTypeWhereInput = {};
|
||||
|
||||
if (category) {
|
||||
where.category = category;
|
||||
}
|
||||
|
||||
if (q?.trim()) {
|
||||
const term = q.trim();
|
||||
where.OR = [
|
||||
{ name: { contains: term, mode: 'insensitive' } },
|
||||
{ code: { contains: term, mode: 'insensitive' } },
|
||||
];
|
||||
}
|
||||
|
||||
const [items, total] = await this.prisma.$transaction([
|
||||
this.prisma.modelType.findMany({
|
||||
where,
|
||||
orderBy: { [orderByField]: orderByDir },
|
||||
skip: safeOffset,
|
||||
take: cappedLimit,
|
||||
}),
|
||||
this.prisma.modelType.count({ where }),
|
||||
]);
|
||||
|
||||
return {
|
||||
items,
|
||||
total,
|
||||
offset: safeOffset,
|
||||
limit: cappedLimit,
|
||||
};
|
||||
}
|
||||
|
||||
async create(dto: CreateModelTypeDto): Promise<PrismaModelType> {
|
||||
try {
|
||||
return await this.prisma.modelType.create({
|
||||
data: {
|
||||
name: dto.name,
|
||||
code: dto.code,
|
||||
category: dto.category,
|
||||
notes: dto.notes,
|
||||
description: dto.description ?? null,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
this.handlePrismaError(error);
|
||||
}
|
||||
}
|
||||
|
||||
async update(id: string, dto: UpdateModelTypeDto): Promise<PrismaModelType> {
|
||||
try {
|
||||
return await this.prisma.modelType.update({
|
||||
where: { id },
|
||||
data: {
|
||||
...dto,
|
||||
description:
|
||||
dto.description === undefined ? undefined : dto.description ?? null,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
this.handlePrismaError(error);
|
||||
}
|
||||
}
|
||||
|
||||
async remove(id: string): Promise<void> {
|
||||
try {
|
||||
await this.prisma.modelType.delete({ where: { id } });
|
||||
} catch (error) {
|
||||
this.handlePrismaError(error);
|
||||
}
|
||||
}
|
||||
|
||||
async findOne(id: string): Promise<PrismaModelType> {
|
||||
const modelType = await this.prisma.modelType.findUnique({ where: { id } });
|
||||
if (!modelType) {
|
||||
throw new NotFoundException('Type de modèle introuvable.');
|
||||
}
|
||||
return modelType;
|
||||
}
|
||||
|
||||
private handlePrismaError(error: unknown): never {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
if (error.code === 'P2002' && this.isUniqueCodeConstraint(error)) {
|
||||
throw new ConflictException('Ce code est déjà utilisé.');
|
||||
}
|
||||
|
||||
if (error.code === 'P2025') {
|
||||
throw new NotFoundException('Type de modèle introuvable.');
|
||||
}
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
private isUniqueCodeConstraint(error: Prisma.PrismaClientKnownRequestError) {
|
||||
const { target } = error.meta ?? {};
|
||||
if (Array.isArray(target)) {
|
||||
return target.includes('code');
|
||||
}
|
||||
if (typeof target === 'string') {
|
||||
return target === 'code';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,12 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Patch,
|
||||
Param,
|
||||
Delete,
|
||||
} from '@nestjs/common';
|
||||
import { PiecesService } from './pieces.service';
|
||||
import { CreatePieceDto, UpdatePieceDto } from '../shared/dto/piece.dto';
|
||||
|
||||
|
||||
@@ -4,6 +4,6 @@ import { PiecesService } from './pieces.service';
|
||||
|
||||
@Module({
|
||||
controllers: [PiecesController],
|
||||
providers: [PiecesService]
|
||||
providers: [PiecesService],
|
||||
})
|
||||
export class PiecesModule {}
|
||||
|
||||
@@ -43,9 +43,7 @@ describe('PiecesService', () => {
|
||||
prisma.machine.findUnique.mockResolvedValue({
|
||||
id: 'machine-1',
|
||||
typeMachine: {
|
||||
pieceRequirements: [
|
||||
{ id: 'req-1', typePieceId: 'type-piece-1' },
|
||||
],
|
||||
pieceRequirements: [{ id: 'req-1', typePieceId: 'type-piece-1' }],
|
||||
},
|
||||
});
|
||||
|
||||
@@ -70,9 +68,7 @@ describe('PiecesService', () => {
|
||||
prisma.machine.findUnique.mockResolvedValue({
|
||||
id: 'machine-1',
|
||||
typeMachine: {
|
||||
pieceRequirements: [
|
||||
{ id: 'req-1', typePieceId: 'type-piece-1' },
|
||||
],
|
||||
pieceRequirements: [{ id: 'req-1', typePieceId: 'type-piece-1' }],
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -180,9 +180,7 @@ export class PiecesService {
|
||||
});
|
||||
|
||||
if (!composant) {
|
||||
throw new BadRequestException(
|
||||
'Le composant spécifié est introuvable.',
|
||||
);
|
||||
throw new BadRequestException('Le composant spécifié est introuvable.');
|
||||
}
|
||||
|
||||
if (composant.machineId) {
|
||||
|
||||
@@ -6,4 +6,4 @@ import { PrismaService } from './prisma.service';
|
||||
providers: [PrismaService],
|
||||
exports: [PrismaService],
|
||||
})
|
||||
export class PrismaModule {}
|
||||
export class PrismaModule {}
|
||||
|
||||
@@ -2,7 +2,10 @@ import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
@Injectable()
|
||||
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
|
||||
export class PrismaService
|
||||
extends PrismaClient
|
||||
implements OnModuleInit, OnModuleDestroy
|
||||
{
|
||||
constructor() {
|
||||
super({
|
||||
log: ['query', 'info', 'warn', 'error'],
|
||||
@@ -16,4 +19,4 @@ export class PrismaService extends PrismaClient implements OnModuleInit, OnModul
|
||||
async onModuleDestroy() {
|
||||
await this.$disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common'
|
||||
import { ProfilesService } from './profiles.service'
|
||||
import { CreateProfileDto } from '../shared/dto/profile.dto'
|
||||
import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common';
|
||||
import { ProfilesService } from './profiles.service';
|
||||
import { CreateProfileDto } from '../shared/dto/profile.dto';
|
||||
|
||||
@Controller('profiles')
|
||||
export class ProfilesController {
|
||||
@@ -8,16 +8,16 @@ export class ProfilesController {
|
||||
|
||||
@Get()
|
||||
async findAll() {
|
||||
return this.profilesService.findAllActive()
|
||||
return this.profilesService.findAllActive();
|
||||
}
|
||||
|
||||
@Post()
|
||||
async create(@Body() dto: CreateProfileDto) {
|
||||
return this.profilesService.create(dto)
|
||||
return this.profilesService.create(dto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
async delete(@Param('id') id: string) {
|
||||
return this.profilesService.deactivate(id)
|
||||
return this.profilesService.deactivate(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { ProfilesController } from './profiles.controller'
|
||||
import { ProfilesService } from './profiles.service'
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ProfilesController } from './profiles.controller';
|
||||
import { ProfilesService } from './profiles.service';
|
||||
|
||||
@Module({
|
||||
controllers: [ProfilesController],
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import { Injectable, NotFoundException, BadRequestException, OnModuleInit } from '@nestjs/common'
|
||||
import { PrismaService } from '../prisma/prisma.service'
|
||||
import { CreateProfileDto } from '../shared/dto/profile.dto'
|
||||
import {
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
BadRequestException,
|
||||
OnModuleInit,
|
||||
} from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { CreateProfileDto } from '../shared/dto/profile.dto';
|
||||
|
||||
@Injectable()
|
||||
export class ProfilesService implements OnModuleInit {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async onModuleInit() {
|
||||
await this.ensureDefaultProfile()
|
||||
await this.ensureDefaultProfile();
|
||||
}
|
||||
|
||||
async findAllActive() {
|
||||
@@ -21,11 +26,11 @@ export class ProfilesService implements OnModuleInit {
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
async findActiveById(profileId: string) {
|
||||
if (!profileId) return null
|
||||
if (!profileId) return null;
|
||||
|
||||
return this.prisma.profile.findFirst({
|
||||
where: {
|
||||
@@ -40,15 +45,15 @@ export class ProfilesService implements OnModuleInit {
|
||||
updatedAt: true,
|
||||
isActive: true,
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
async create(dto: CreateProfileDto) {
|
||||
const firstName = dto.firstName.trim()
|
||||
const lastName = dto.lastName.trim()
|
||||
const firstName = dto.firstName.trim();
|
||||
const lastName = dto.lastName.trim();
|
||||
|
||||
if (!firstName || !lastName) {
|
||||
throw new BadRequestException('Le prénom et le nom sont obligatoires.')
|
||||
throw new BadRequestException('Le prénom et le nom sont obligatoires.');
|
||||
}
|
||||
|
||||
return this.prisma.profile.create({
|
||||
@@ -64,13 +69,15 @@ export class ProfilesService implements OnModuleInit {
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
async deactivate(profileId: string) {
|
||||
const existing = await this.prisma.profile.findUnique({ where: { id: profileId } })
|
||||
const existing = await this.prisma.profile.findUnique({
|
||||
where: { id: profileId },
|
||||
});
|
||||
if (!existing) {
|
||||
throw new NotFoundException('Profil introuvable')
|
||||
throw new NotFoundException('Profil introuvable');
|
||||
}
|
||||
|
||||
if (!existing.isActive) {
|
||||
@@ -84,7 +91,7 @@ export class ProfilesService implements OnModuleInit {
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
return this.prisma.profile.update({
|
||||
@@ -98,27 +105,34 @@ export class ProfilesService implements OnModuleInit {
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
private async ensureDefaultProfile() {
|
||||
const count = await this.prisma.profile.count({ where: { isActive: true } }).catch((err) => {
|
||||
console.error('Failed to count profiles during ensureDefaultProfile:', err.message)
|
||||
return 0
|
||||
})
|
||||
if (count > 0) return
|
||||
const count = await this.prisma.profile
|
||||
.count({ where: { isActive: true } })
|
||||
.catch((err) => {
|
||||
console.error(
|
||||
'Failed to count profiles during ensureDefaultProfile:',
|
||||
err.message,
|
||||
);
|
||||
return 0;
|
||||
});
|
||||
if (count > 0) return;
|
||||
|
||||
const firstName = process.env.DEFAULT_PROFILE_FIRST_NAME?.trim() || 'Admin'
|
||||
const lastName = process.env.DEFAULT_PROFILE_LAST_NAME?.trim() || 'Général'
|
||||
const firstName = process.env.DEFAULT_PROFILE_FIRST_NAME?.trim() || 'Admin';
|
||||
const lastName = process.env.DEFAULT_PROFILE_LAST_NAME?.trim() || 'Général';
|
||||
|
||||
await this.prisma.profile.create({
|
||||
data: {
|
||||
firstName,
|
||||
lastName,
|
||||
isActive: true,
|
||||
},
|
||||
}).catch((err) => {
|
||||
console.error('Failed to create default profile:', err.message)
|
||||
})
|
||||
await this.prisma.profile
|
||||
.create({
|
||||
data: {
|
||||
firstName,
|
||||
lastName,
|
||||
isActive: true,
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Failed to create default profile:', err.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import {
|
||||
Post,
|
||||
Req,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common'
|
||||
import { Request } from 'express'
|
||||
import { ProfilesService } from '../profiles/profiles.service'
|
||||
import { ActivateProfileDto } from '../shared/dto/profile.dto'
|
||||
} from '@nestjs/common';
|
||||
import { Request } from 'express';
|
||||
import { ProfilesService } from '../profiles/profiles.service';
|
||||
import { ActivateProfileDto } from '../shared/dto/profile.dto';
|
||||
|
||||
@Controller('session/profile')
|
||||
export class ProfileSessionController {
|
||||
@@ -19,46 +19,50 @@ export class ProfileSessionController {
|
||||
@Get()
|
||||
async getActiveProfile(@Req() req: Request) {
|
||||
if (!req.session?.profileId) {
|
||||
throw new UnauthorizedException('Aucun profil actif.')
|
||||
throw new UnauthorizedException('Aucun profil actif.');
|
||||
}
|
||||
|
||||
const profile = await this.profilesService.findActiveById(req.session.profileId)
|
||||
const profile = await this.profilesService.findActiveById(
|
||||
req.session.profileId,
|
||||
);
|
||||
if (!profile) {
|
||||
req.session.profileId = undefined
|
||||
throw new UnauthorizedException('Profil introuvable ou inactif.')
|
||||
req.session.profileId = undefined;
|
||||
throw new UnauthorizedException('Profil introuvable ou inactif.');
|
||||
}
|
||||
|
||||
return profile
|
||||
return profile;
|
||||
}
|
||||
|
||||
@Post()
|
||||
async activateProfile(@Req() req: Request, @Body() dto: ActivateProfileDto) {
|
||||
if (!dto.profileId) {
|
||||
throw new BadRequestException('profileId est requis.')
|
||||
throw new BadRequestException('profileId est requis.');
|
||||
}
|
||||
|
||||
const profile = await this.profilesService.findActiveById(dto.profileId)
|
||||
const profile = await this.profilesService.findActiveById(dto.profileId);
|
||||
if (!profile) {
|
||||
throw new UnauthorizedException('Profil introuvable ou inactif.')
|
||||
throw new UnauthorizedException('Profil introuvable ou inactif.');
|
||||
}
|
||||
|
||||
req.session.profileId = profile.id
|
||||
return profile
|
||||
req.session.profileId = profile.id;
|
||||
return profile;
|
||||
}
|
||||
|
||||
@Delete()
|
||||
async logout(@Req() req: Request) {
|
||||
if (!req.session) {
|
||||
return { success: true }
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
req.session.destroy((err) => {
|
||||
if (err) {
|
||||
return reject(new BadRequestException('Impossible de déconnecter la session.'))
|
||||
return reject(
|
||||
new BadRequestException('Impossible de déconnecter la session.'),
|
||||
);
|
||||
}
|
||||
resolve({ success: true })
|
||||
})
|
||||
})
|
||||
resolve({ success: true });
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { ProfileSessionController } from './session.controller'
|
||||
import { ProfilesModule } from '../profiles/profiles.module'
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ProfileSessionController } from './session.controller';
|
||||
import { ProfilesModule } from '../profiles/profiles.module';
|
||||
|
||||
@Module({
|
||||
imports: [ProfilesModule],
|
||||
|
||||
@@ -22,7 +22,7 @@ export class CreateComposantDto {
|
||||
constructeurId?: string;
|
||||
|
||||
@IsOptional()
|
||||
@Transform(({ value }) => value === '' ? null : value)
|
||||
@Transform(({ value }) => (value === '' ? null : value))
|
||||
@IsNumber({}, { message: 'prix must be a valid number' })
|
||||
prix?: number | null;
|
||||
|
||||
@@ -56,7 +56,7 @@ export class UpdateComposantDto {
|
||||
constructeurId?: string;
|
||||
|
||||
@IsOptional()
|
||||
@Transform(({ value }) => value === '' ? null : value)
|
||||
@Transform(({ value }) => (value === '' ? null : value))
|
||||
@IsNumber({}, { message: 'prix must be a valid number' })
|
||||
prix?: number | null;
|
||||
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
import { IsEmail, IsOptional, IsString } from 'class-validator'
|
||||
import { IsEmail, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class CreateConstructeurDto {
|
||||
@IsString()
|
||||
name: string
|
||||
name: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsEmail()
|
||||
email?: string
|
||||
email?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
phone?: string
|
||||
phone?: string;
|
||||
}
|
||||
|
||||
export class UpdateConstructeurDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
name?: string
|
||||
name?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsEmail()
|
||||
email?: string
|
||||
email?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
phone?: string
|
||||
phone?: string;
|
||||
}
|
||||
|
||||
@@ -58,4 +58,4 @@ export class UpdateCustomFieldValueDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
value?: string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,8 @@ export class MachinePieceSelectionDto {
|
||||
@IsString()
|
||||
requirementId: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
pieceModelId?: string;
|
||||
|
||||
@IsOptional()
|
||||
definition?: any;
|
||||
pieceModelId: string;
|
||||
}
|
||||
|
||||
export class CreateMachineDto {
|
||||
|
||||
@@ -22,7 +22,7 @@ export class CreatePieceDto {
|
||||
constructeurId?: string;
|
||||
|
||||
@IsOptional()
|
||||
@Transform(({ value }) => value === '' ? null : value)
|
||||
@Transform(({ value }) => (value === '' ? null : value))
|
||||
@IsNumber({}, { message: 'prix must be a valid number' })
|
||||
prix?: number | null;
|
||||
|
||||
@@ -56,7 +56,7 @@ export class UpdatePieceDto {
|
||||
constructeurId?: string;
|
||||
|
||||
@IsOptional()
|
||||
@Transform(({ value }) => value === '' ? null : value)
|
||||
@Transform(({ value }) => (value === '' ? null : value))
|
||||
@IsNumber({}, { message: 'prix must be a valid number' })
|
||||
prix?: number | null;
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { IsNotEmpty, IsString, MaxLength } from 'class-validator'
|
||||
import { IsNotEmpty, IsString, MaxLength } from 'class-validator';
|
||||
|
||||
export class CreateProfileDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MaxLength(100)
|
||||
firstName!: string
|
||||
firstName!: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MaxLength(100)
|
||||
lastName!: string
|
||||
lastName!: string;
|
||||
}
|
||||
|
||||
export class ActivateProfileDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
profileId!: string
|
||||
profileId!: string;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { IsString, IsOptional, IsArray, IsObject, IsBoolean, IsEnum, IsInt } from 'class-validator';
|
||||
import {
|
||||
IsString,
|
||||
IsOptional,
|
||||
IsArray,
|
||||
IsObject,
|
||||
IsBoolean,
|
||||
IsEnum,
|
||||
IsInt,
|
||||
} from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import { ValidateNested } from 'class-validator';
|
||||
|
||||
@@ -7,7 +15,7 @@ export enum CustomFieldType {
|
||||
NUMBER = 'number',
|
||||
SELECT = 'select',
|
||||
BOOLEAN = 'boolean',
|
||||
DATE = 'date'
|
||||
DATE = 'date',
|
||||
}
|
||||
|
||||
export class CreateCustomFieldDto {
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Patch,
|
||||
Param,
|
||||
Delete,
|
||||
} from '@nestjs/common';
|
||||
import { SitesService } from './sites.service';
|
||||
import { CreateSiteDto, UpdateSiteDto } from '../shared/dto/site.dto';
|
||||
|
||||
|
||||
@@ -4,6 +4,6 @@ import { SitesService } from './sites.service';
|
||||
|
||||
@Module({
|
||||
controllers: [SitesController],
|
||||
providers: [SitesService]
|
||||
providers: [SitesService],
|
||||
})
|
||||
export class SitesModule {}
|
||||
|
||||
@@ -70,10 +70,10 @@ export class SitesService {
|
||||
});
|
||||
|
||||
if (machinesInSite.length > 0) {
|
||||
const machineNames = machinesInSite.map(m => m.name).join(', ');
|
||||
const machineNames = machinesInSite.map((m) => m.name).join(', ');
|
||||
throw new Error(
|
||||
`Impossible de supprimer ce site car il contient ${machinesInSite.length} machine(s): ${machineNames}. ` +
|
||||
`Veuillez d'abord supprimer ou déplacer ces machines vers un autre site.`
|
||||
`Veuillez d'abord supprimer ou déplacer ces machines vers un autre site.`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
4
src/types/express-session.d.ts
vendored
4
src/types/express-session.d.ts
vendored
@@ -1,7 +1,7 @@
|
||||
import 'express-session'
|
||||
import 'express-session';
|
||||
|
||||
declare module 'express-session' {
|
||||
interface SessionData {
|
||||
profileId?: string
|
||||
profileId?: string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete, Query } from '@nestjs/common';
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Patch,
|
||||
Param,
|
||||
Delete,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { TypesService } from './types.service';
|
||||
import {
|
||||
CreateTypeMachineDto,
|
||||
import {
|
||||
CreateTypeMachineDto,
|
||||
UpdateTypeMachineDto,
|
||||
CreateTypeComposantDto,
|
||||
UpdateTypeComposantDto,
|
||||
@@ -10,7 +19,7 @@ import {
|
||||
CreateComposantModelDto,
|
||||
UpdateComposantModelDto,
|
||||
CreatePieceModelDto,
|
||||
UpdatePieceModelDto
|
||||
UpdatePieceModelDto,
|
||||
} from '../shared/dto/type.dto';
|
||||
|
||||
@Controller('types')
|
||||
@@ -34,7 +43,10 @@ export class TypesController {
|
||||
}
|
||||
|
||||
@Patch('machines/:id')
|
||||
updateTypeMachine(@Param('id') id: string, @Body() updateTypeMachineDto: UpdateTypeMachineDto) {
|
||||
updateTypeMachine(
|
||||
@Param('id') id: string,
|
||||
@Body() updateTypeMachineDto: UpdateTypeMachineDto,
|
||||
) {
|
||||
return this.typesService.updateTypeMachine(id, updateTypeMachineDto);
|
||||
}
|
||||
|
||||
@@ -56,7 +68,9 @@ export class TypesController {
|
||||
|
||||
// ComposantModel routes
|
||||
@Post('composants/models')
|
||||
createComposantModel(@Body() createComposantModelDto: CreateComposantModelDto) {
|
||||
createComposantModel(
|
||||
@Body() createComposantModelDto: CreateComposantModelDto,
|
||||
) {
|
||||
return this.typesService.createComposantModel(createComposantModelDto);
|
||||
}
|
||||
|
||||
@@ -71,7 +85,10 @@ export class TypesController {
|
||||
}
|
||||
|
||||
@Patch('composants/models/:id')
|
||||
updateComposantModel(@Param('id') id: string, @Body() updateComposantModelDto: UpdateComposantModelDto) {
|
||||
updateComposantModel(
|
||||
@Param('id') id: string,
|
||||
@Body() updateComposantModelDto: UpdateComposantModelDto,
|
||||
) {
|
||||
return this.typesService.updateComposantModel(id, updateComposantModelDto);
|
||||
}
|
||||
|
||||
@@ -86,7 +103,10 @@ export class TypesController {
|
||||
}
|
||||
|
||||
@Patch('composants/:id')
|
||||
updateTypeComposant(@Param('id') id: string, @Body() updateTypeComposantDto: UpdateTypeComposantDto) {
|
||||
updateTypeComposant(
|
||||
@Param('id') id: string,
|
||||
@Body() updateTypeComposantDto: UpdateTypeComposantDto,
|
||||
) {
|
||||
return this.typesService.updateTypeComposant(id, updateTypeComposantDto);
|
||||
}
|
||||
|
||||
@@ -123,7 +143,10 @@ export class TypesController {
|
||||
}
|
||||
|
||||
@Patch('pieces/models/:id')
|
||||
updatePieceModel(@Param('id') id: string, @Body() updatePieceModelDto: UpdatePieceModelDto) {
|
||||
updatePieceModel(
|
||||
@Param('id') id: string,
|
||||
@Body() updatePieceModelDto: UpdatePieceModelDto,
|
||||
) {
|
||||
return this.typesService.updatePieceModel(id, updatePieceModelDto);
|
||||
}
|
||||
|
||||
@@ -138,7 +161,10 @@ export class TypesController {
|
||||
}
|
||||
|
||||
@Patch('pieces/:id')
|
||||
updateTypePiece(@Param('id') id: string, @Body() updateTypePieceDto: UpdateTypePieceDto) {
|
||||
updateTypePiece(
|
||||
@Param('id') id: string,
|
||||
@Body() updateTypePieceDto: UpdateTypePieceDto,
|
||||
) {
|
||||
return this.typesService.updateTypePiece(id, updateTypePieceDto);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,6 @@ import { TypesService } from './types.service';
|
||||
|
||||
@Module({
|
||||
controllers: [TypesController],
|
||||
providers: [TypesService]
|
||||
providers: [TypesService],
|
||||
})
|
||||
export class TypesModule {}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ModelCategory } from '@prisma/client';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import {
|
||||
CreateTypeMachineDto,
|
||||
import {
|
||||
CreateTypeMachineDto,
|
||||
UpdateTypeMachineDto,
|
||||
CreateTypeComposantDto,
|
||||
UpdateTypeComposantDto,
|
||||
@@ -10,53 +11,93 @@ import {
|
||||
CreateComposantModelDto,
|
||||
UpdateComposantModelDto,
|
||||
CreatePieceModelDto,
|
||||
UpdatePieceModelDto
|
||||
UpdatePieceModelDto,
|
||||
} from '../shared/dto/type.dto';
|
||||
|
||||
@Injectable()
|
||||
export class TypesService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
private slugifyName(name: string): string {
|
||||
return name
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/^-+|-+$/g, '')
|
||||
.replace(/-+/g, '-');
|
||||
}
|
||||
|
||||
private async generateUniqueModelTypeCode(name: string): Promise<string> {
|
||||
const base = this.slugifyName(name) || 'type';
|
||||
let candidate = base;
|
||||
let suffix = 1;
|
||||
|
||||
while (
|
||||
await this.prisma.modelType.findUnique({
|
||||
where: { code: candidate },
|
||||
select: { id: true },
|
||||
})
|
||||
) {
|
||||
candidate = `${base}-${suffix++}`;
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
|
||||
// TypeMachine methods
|
||||
async createTypeMachine(createTypeMachineDto: CreateTypeMachineDto) {
|
||||
const { customFields, componentRequirements, pieceRequirements, ...typeData } = createTypeMachineDto;
|
||||
|
||||
const {
|
||||
customFields,
|
||||
componentRequirements,
|
||||
pieceRequirements,
|
||||
...typeData
|
||||
} = createTypeMachineDto;
|
||||
|
||||
return this.prisma.typeMachine.create({
|
||||
data: {
|
||||
...typeData,
|
||||
customFields: customFields ? {
|
||||
create: customFields.map(field => ({
|
||||
name: field.name,
|
||||
type: field.type,
|
||||
required: field.required || false,
|
||||
defaultValue: field.defaultValue,
|
||||
options: field.options
|
||||
}))
|
||||
} : 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,
|
||||
customFields: customFields
|
||||
? {
|
||||
create: customFields.map((field) => ({
|
||||
name: field.name,
|
||||
type: field.type,
|
||||
required: field.required || false,
|
||||
defaultValue: field.defaultValue,
|
||||
options: field.options,
|
||||
})),
|
||||
}
|
||||
: 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,
|
||||
@@ -113,27 +154,35 @@ export class TypesService {
|
||||
});
|
||||
}
|
||||
|
||||
async updateTypeMachine(id: string, updateTypeMachineDto: UpdateTypeMachineDto) {
|
||||
const { customFields, componentRequirements, pieceRequirements, ...typeData } = updateTypeMachineDto;
|
||||
|
||||
async updateTypeMachine(
|
||||
id: string,
|
||||
updateTypeMachineDto: UpdateTypeMachineDto,
|
||||
) {
|
||||
const {
|
||||
customFields,
|
||||
componentRequirements,
|
||||
pieceRequirements,
|
||||
...typeData
|
||||
} = updateTypeMachineDto;
|
||||
|
||||
// Si des champs personnalisés sont fournis, on les met à jour
|
||||
if (customFields !== undefined) {
|
||||
// Supprimer tous les champs personnalisés existants
|
||||
await this.prisma.customField.deleteMany({
|
||||
where: { typeMachineId: id }
|
||||
where: { typeMachineId: id },
|
||||
});
|
||||
|
||||
|
||||
// Créer les nouveaux champs personnalisés
|
||||
if (customFields.length > 0) {
|
||||
await this.prisma.customField.createMany({
|
||||
data: customFields.map(field => ({
|
||||
data: customFields.map((field) => ({
|
||||
name: field.name,
|
||||
type: field.type,
|
||||
required: field.required || false,
|
||||
defaultValue: field.defaultValue,
|
||||
options: field.options,
|
||||
typeMachineId: id
|
||||
}))
|
||||
typeMachineId: id,
|
||||
})),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -145,7 +194,7 @@ export class TypesService {
|
||||
|
||||
if (componentRequirements.length > 0) {
|
||||
await this.prisma.typeMachineComponentRequirement.createMany({
|
||||
data: componentRequirements.map(requirement => ({
|
||||
data: componentRequirements.map((requirement) => ({
|
||||
label: requirement.label ?? null,
|
||||
minCount: requirement.minCount ?? 1,
|
||||
maxCount: requirement.maxCount ?? null,
|
||||
@@ -166,7 +215,7 @@ export class TypesService {
|
||||
|
||||
if (pieceRequirements.length > 0) {
|
||||
await this.prisma.typeMachinePieceRequirement.createMany({
|
||||
data: pieceRequirements.map(requirement => ({
|
||||
data: pieceRequirements.map((requirement) => ({
|
||||
label: requirement.label ?? null,
|
||||
minCount: requirement.minCount ?? 0,
|
||||
maxCount: requirement.maxCount ?? null,
|
||||
@@ -179,7 +228,7 @@ export class TypesService {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return this.prisma.typeMachine.update({
|
||||
where: { id },
|
||||
data: typeData,
|
||||
@@ -207,10 +256,10 @@ export class TypesService {
|
||||
});
|
||||
|
||||
if (machinesWithType.length > 0) {
|
||||
const machineNames = machinesWithType.map(m => m.name).join(', ');
|
||||
const machineNames = machinesWithType.map((m) => m.name).join(', ');
|
||||
throw new Error(
|
||||
`Impossible de supprimer ce type de machine car il est utilisé par ${machinesWithType.length} machine(s): ${machineNames}. ` +
|
||||
`Veuillez d'abord supprimer ou modifier ces machines.`
|
||||
`Veuillez d'abord supprimer ou modifier ces machines.`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -224,22 +273,30 @@ export class TypesService {
|
||||
});
|
||||
}
|
||||
|
||||
// TypeComposant methods
|
||||
// TypeComposant methods (backed by ModelType with COMPONENT category)
|
||||
async createTypeComposant(createTypeComposantDto: CreateTypeComposantDto) {
|
||||
const { customFields, ...typeData } = createTypeComposantDto;
|
||||
|
||||
return this.prisma.typeComposant.create({
|
||||
const { customFields, description, name } = createTypeComposantDto;
|
||||
|
||||
const code = await this.generateUniqueModelTypeCode(name);
|
||||
|
||||
return this.prisma.modelType.create({
|
||||
data: {
|
||||
...typeData,
|
||||
customFields: customFields ? {
|
||||
create: customFields.map(field => ({
|
||||
name: field.name,
|
||||
type: field.type,
|
||||
required: field.required || false,
|
||||
defaultValue: field.defaultValue,
|
||||
options: field.options
|
||||
}))
|
||||
} : undefined
|
||||
name,
|
||||
code,
|
||||
category: ModelCategory.COMPONENT,
|
||||
description: description ?? null,
|
||||
notes: description ?? null,
|
||||
customFields: customFields
|
||||
? {
|
||||
create: customFields.map((field) => ({
|
||||
name: field.name,
|
||||
type: field.type,
|
||||
required: field.required || false,
|
||||
defaultValue: field.defaultValue,
|
||||
options: field.options,
|
||||
})),
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
include: {
|
||||
customFields: true,
|
||||
@@ -248,7 +305,8 @@ export class TypesService {
|
||||
}
|
||||
|
||||
async findAllTypeComposants() {
|
||||
return this.prisma.typeComposant.findMany({
|
||||
return this.prisma.modelType.findMany({
|
||||
where: { category: ModelCategory.COMPONENT },
|
||||
include: {
|
||||
composants: true,
|
||||
customFields: true,
|
||||
@@ -258,8 +316,11 @@ export class TypesService {
|
||||
}
|
||||
|
||||
async findOneTypeComposant(id: string) {
|
||||
return this.prisma.typeComposant.findUnique({
|
||||
where: { id },
|
||||
return this.prisma.modelType.findFirst({
|
||||
where: {
|
||||
id,
|
||||
category: ModelCategory.COMPONENT,
|
||||
},
|
||||
include: {
|
||||
composants: true,
|
||||
customFields: true,
|
||||
@@ -268,34 +329,43 @@ export class TypesService {
|
||||
});
|
||||
}
|
||||
|
||||
async updateTypeComposant(id: string, updateTypeComposantDto: UpdateTypeComposantDto) {
|
||||
const { customFields, ...typeData } = updateTypeComposantDto;
|
||||
|
||||
// Si des champs personnalisés sont fournis, on les met à jour
|
||||
async updateTypeComposant(
|
||||
id: string,
|
||||
updateTypeComposantDto: UpdateTypeComposantDto,
|
||||
) {
|
||||
const { customFields, description, name } = updateTypeComposantDto;
|
||||
|
||||
if (customFields !== undefined) {
|
||||
// Supprimer tous les champs personnalisés existants
|
||||
await this.prisma.customField.deleteMany({
|
||||
where: { typeComposantId: id }
|
||||
where: { typeComposantId: id },
|
||||
});
|
||||
|
||||
// Créer les nouveaux champs personnalisés
|
||||
|
||||
if (customFields.length > 0) {
|
||||
await this.prisma.customField.createMany({
|
||||
data: customFields.map(field => ({
|
||||
data: customFields.map((field) => ({
|
||||
name: field.name,
|
||||
type: field.type,
|
||||
required: field.required || false,
|
||||
defaultValue: field.defaultValue,
|
||||
options: field.options,
|
||||
typeComposantId: id
|
||||
}))
|
||||
typeComposantId: id,
|
||||
})),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return this.prisma.typeComposant.update({
|
||||
|
||||
const data: Record<string, unknown> = {};
|
||||
if (name !== undefined) {
|
||||
data.name = name;
|
||||
}
|
||||
if (description !== undefined) {
|
||||
data.description = description;
|
||||
data.notes = description;
|
||||
}
|
||||
|
||||
return this.prisma.modelType.update({
|
||||
where: { id },
|
||||
data: typeData,
|
||||
data,
|
||||
include: {
|
||||
customFields: true,
|
||||
},
|
||||
@@ -303,91 +373,145 @@ export class TypesService {
|
||||
}
|
||||
|
||||
async removeTypeComposant(id: string) {
|
||||
return this.prisma.typeComposant.delete({
|
||||
return this.prisma.modelType.delete({
|
||||
where: { id },
|
||||
});
|
||||
}
|
||||
|
||||
// TypePiece methods
|
||||
// TypePiece methods backed by ModelType
|
||||
private mapModelTypePiece(modelType: any) {
|
||||
if (!modelType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {
|
||||
pieceCustomFields,
|
||||
pieceModels,
|
||||
pieceRequirements,
|
||||
pieces,
|
||||
...rest
|
||||
} = modelType;
|
||||
|
||||
return {
|
||||
...rest,
|
||||
customFields: pieceCustomFields ?? [],
|
||||
models: pieceModels ?? [],
|
||||
pieceRequirements: pieceRequirements ?? [],
|
||||
pieces: pieces ?? [],
|
||||
};
|
||||
}
|
||||
|
||||
async createTypePiece(createTypePieceDto: CreateTypePieceDto) {
|
||||
const { customFields, ...typeData } = createTypePieceDto;
|
||||
|
||||
return this.prisma.typePiece.create({
|
||||
const { customFields, description, name } = createTypePieceDto;
|
||||
|
||||
const code = await this.generateUniqueModelTypeCode(name);
|
||||
|
||||
const created = await this.prisma.modelType.create({
|
||||
data: {
|
||||
...typeData,
|
||||
customFields: customFields ? {
|
||||
create: customFields.map(field => ({
|
||||
name: field.name,
|
||||
type: field.type,
|
||||
required: field.required || false,
|
||||
defaultValue: field.defaultValue,
|
||||
options: field.options
|
||||
}))
|
||||
} : undefined
|
||||
name,
|
||||
code,
|
||||
category: ModelCategory.PIECE,
|
||||
description: description ?? null,
|
||||
notes: description ?? null,
|
||||
pieceCustomFields: customFields
|
||||
? {
|
||||
create: customFields.map((field) => ({
|
||||
name: field.name,
|
||||
type: field.type,
|
||||
required: field.required || false,
|
||||
defaultValue: field.defaultValue,
|
||||
options: field.options,
|
||||
})),
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
include: {
|
||||
customFields: true,
|
||||
pieceCustomFields: true,
|
||||
pieceModels: true,
|
||||
pieceRequirements: true,
|
||||
pieces: true,
|
||||
},
|
||||
});
|
||||
|
||||
return this.mapModelTypePiece(created);
|
||||
}
|
||||
|
||||
async findAllTypePieces() {
|
||||
return this.prisma.typePiece.findMany({
|
||||
const items = await this.prisma.modelType.findMany({
|
||||
where: { category: ModelCategory.PIECE },
|
||||
include: {
|
||||
pieceCustomFields: true,
|
||||
pieceModels: true,
|
||||
pieceRequirements: true,
|
||||
pieces: true,
|
||||
customFields: true,
|
||||
models: true,
|
||||
},
|
||||
orderBy: { name: 'asc' },
|
||||
});
|
||||
|
||||
return items.map((item) => this.mapModelTypePiece(item));
|
||||
}
|
||||
|
||||
async findOneTypePiece(id: string) {
|
||||
return this.prisma.typePiece.findUnique({
|
||||
where: { id },
|
||||
const item = await this.prisma.modelType.findFirst({
|
||||
where: { id, category: ModelCategory.PIECE },
|
||||
include: {
|
||||
pieceCustomFields: true,
|
||||
pieceModels: true,
|
||||
pieceRequirements: true,
|
||||
pieces: true,
|
||||
customFields: true,
|
||||
models: true,
|
||||
},
|
||||
});
|
||||
|
||||
return this.mapModelTypePiece(item);
|
||||
}
|
||||
|
||||
async updateTypePiece(id: string, updateTypePieceDto: UpdateTypePieceDto) {
|
||||
const { customFields, ...typeData } = updateTypePieceDto;
|
||||
|
||||
// Si des champs personnalisés sont fournis, on les met à jour
|
||||
const { customFields, description, name } = updateTypePieceDto;
|
||||
|
||||
if (customFields !== undefined) {
|
||||
// Supprimer tous les champs personnalisés existants
|
||||
await this.prisma.customField.deleteMany({
|
||||
where: { typePieceId: id }
|
||||
where: { typePieceId: id },
|
||||
});
|
||||
|
||||
// Créer les nouveaux champs personnalisés
|
||||
|
||||
if (customFields.length > 0) {
|
||||
await this.prisma.customField.createMany({
|
||||
data: customFields.map(field => ({
|
||||
data: customFields.map((field) => ({
|
||||
name: field.name,
|
||||
type: field.type,
|
||||
required: field.required || false,
|
||||
defaultValue: field.defaultValue,
|
||||
options: field.options,
|
||||
typePieceId: id
|
||||
}))
|
||||
typePieceId: id,
|
||||
})),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return this.prisma.typePiece.update({
|
||||
|
||||
const updatePayload: Record<string, unknown> = {};
|
||||
if (name !== undefined) {
|
||||
updatePayload.name = name;
|
||||
}
|
||||
if (description !== undefined) {
|
||||
updatePayload.description = description;
|
||||
updatePayload.notes = description;
|
||||
}
|
||||
|
||||
const updated = await this.prisma.modelType.update({
|
||||
where: { id },
|
||||
data: typeData,
|
||||
data: updatePayload,
|
||||
include: {
|
||||
customFields: true,
|
||||
pieceCustomFields: true,
|
||||
pieceModels: true,
|
||||
pieceRequirements: true,
|
||||
pieces: true,
|
||||
},
|
||||
});
|
||||
|
||||
return this.mapModelTypePiece(updated);
|
||||
}
|
||||
|
||||
async removeTypePiece(id: string) {
|
||||
return this.prisma.typePiece.delete({
|
||||
return this.prisma.modelType.delete({
|
||||
where: { id },
|
||||
});
|
||||
}
|
||||
@@ -430,7 +554,10 @@ export class TypesService {
|
||||
});
|
||||
}
|
||||
|
||||
async updateComposantModel(id: string, updateComposantModelDto: UpdateComposantModelDto) {
|
||||
async updateComposantModel(
|
||||
id: string,
|
||||
updateComposantModelDto: UpdateComposantModelDto,
|
||||
) {
|
||||
const { typeComposantId, ...data } = updateComposantModelDto;
|
||||
|
||||
return this.prisma.composantModel.update({
|
||||
|
||||
@@ -156,9 +156,12 @@ class InMemoryPrismaService {
|
||||
private sites: SiteRecord[] = [];
|
||||
private typeComposants: TypeComposantRecord[] = [];
|
||||
private typePieces: TypePieceRecord[] = [];
|
||||
private modelTypeCodeCounter = 0;
|
||||
private typeMachines: TypeMachineRecord[] = [];
|
||||
private typeMachineComponentRequirements: TypeMachineComponentRequirementRecord[] = [];
|
||||
private typeMachinePieceRequirements: TypeMachinePieceRequirementRecord[] = [];
|
||||
private typeMachineComponentRequirements: TypeMachineComponentRequirementRecord[] =
|
||||
[];
|
||||
private typeMachinePieceRequirements: TypeMachinePieceRequirementRecord[] =
|
||||
[];
|
||||
private machines: MachineRecord[] = [];
|
||||
private composants: ComposantRecord[] = [];
|
||||
private pieces: PieceRecord[] = [];
|
||||
@@ -179,6 +182,7 @@ class InMemoryPrismaService {
|
||||
this.sites = [];
|
||||
this.typeComposants = [];
|
||||
this.typePieces = [];
|
||||
this.modelTypeCodeCounter = 0;
|
||||
this.typeMachines = [];
|
||||
this.typeMachineComponentRequirements = [];
|
||||
this.typeMachinePieceRequirements = [];
|
||||
@@ -268,7 +272,9 @@ class InMemoryPrismaService {
|
||||
return include?.customFields
|
||||
? {
|
||||
...record,
|
||||
customFields: this.customFields.filter((field) => field.typeComposantId === record.id),
|
||||
customFields: this.customFields.filter(
|
||||
(field) => field.typeComposantId === record.id,
|
||||
),
|
||||
}
|
||||
: { ...record };
|
||||
},
|
||||
@@ -280,7 +286,9 @@ class InMemoryPrismaService {
|
||||
return this.typeComposants.find((item) => item.id === where.id) ?? null;
|
||||
}
|
||||
if (where.name) {
|
||||
return this.typeComposants.find((item) => item.name === where.name) ?? null;
|
||||
return (
|
||||
this.typeComposants.find((item) => item.name === where.name) ?? null
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
@@ -325,7 +333,9 @@ class InMemoryPrismaService {
|
||||
return include?.customFields
|
||||
? {
|
||||
...record,
|
||||
customFields: this.customFields.filter((field) => field.typePieceId === record.id),
|
||||
customFields: this.customFields.filter(
|
||||
(field) => field.typePieceId === record.id,
|
||||
),
|
||||
}
|
||||
: { ...record };
|
||||
},
|
||||
@@ -383,20 +393,22 @@ class InMemoryPrismaService {
|
||||
|
||||
let componentRequirements: TypeMachineComponentRequirementRecord[] = [];
|
||||
if (data.componentRequirements?.create) {
|
||||
componentRequirements = data.componentRequirements.create.map((requirement) => {
|
||||
const req: TypeMachineComponentRequirementRecord = {
|
||||
id: generateId('tmc_req'),
|
||||
typeMachineId: record.id,
|
||||
typeComposantId: requirement.typeComposant.connect.id,
|
||||
label: requirement.label ?? null,
|
||||
minCount: requirement.minCount ?? 1,
|
||||
maxCount: requirement.maxCount ?? null,
|
||||
required: requirement.required ?? true,
|
||||
allowNewModels: requirement.allowNewModels ?? true,
|
||||
};
|
||||
this.typeMachineComponentRequirements.push(req);
|
||||
return req;
|
||||
});
|
||||
componentRequirements = data.componentRequirements.create.map(
|
||||
(requirement) => {
|
||||
const req: TypeMachineComponentRequirementRecord = {
|
||||
id: generateId('tmc_req'),
|
||||
typeMachineId: record.id,
|
||||
typeComposantId: requirement.typeComposant.connect.id,
|
||||
label: requirement.label ?? null,
|
||||
minCount: requirement.minCount ?? 1,
|
||||
maxCount: requirement.maxCount ?? null,
|
||||
required: requirement.required ?? true,
|
||||
allowNewModels: requirement.allowNewModels ?? true,
|
||||
};
|
||||
this.typeMachineComponentRequirements.push(req);
|
||||
return req;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let pieceRequirements: TypeMachinePieceRequirementRecord[] = [];
|
||||
@@ -417,28 +429,49 @@ class InMemoryPrismaService {
|
||||
});
|
||||
}
|
||||
|
||||
return this.buildTypeMachine(record, include ?? {}, componentRequirements, pieceRequirements);
|
||||
return this.buildTypeMachine(
|
||||
record,
|
||||
include ?? {},
|
||||
componentRequirements,
|
||||
pieceRequirements,
|
||||
);
|
||||
},
|
||||
findUnique: async ({ where, include }: any) => {
|
||||
const typeMachine = this.typeMachines.find((item) => item.id === where.id);
|
||||
const typeMachine = this.typeMachines.find(
|
||||
(item) => item.id === where.id,
|
||||
);
|
||||
if (!typeMachine) {
|
||||
return null;
|
||||
}
|
||||
const componentRequirements = this.typeMachineComponentRequirements.filter(
|
||||
(item) => item.typeMachineId === typeMachine.id,
|
||||
);
|
||||
const componentRequirements =
|
||||
this.typeMachineComponentRequirements.filter(
|
||||
(item) => item.typeMachineId === typeMachine.id,
|
||||
);
|
||||
const pieceRequirements = this.typeMachinePieceRequirements.filter(
|
||||
(item) => item.typeMachineId === typeMachine.id,
|
||||
);
|
||||
return this.buildTypeMachine(typeMachine, include ?? {}, componentRequirements, pieceRequirements);
|
||||
return this.buildTypeMachine(
|
||||
typeMachine,
|
||||
include ?? {},
|
||||
componentRequirements,
|
||||
pieceRequirements,
|
||||
);
|
||||
},
|
||||
findMany: async ({ include }: any = {}) => {
|
||||
return this.typeMachines.map((item) => {
|
||||
const componentRequirements = this.typeMachineComponentRequirements.filter(
|
||||
const componentRequirements =
|
||||
this.typeMachineComponentRequirements.filter(
|
||||
(req) => req.typeMachineId === item.id,
|
||||
);
|
||||
const pieceRequirements = this.typeMachinePieceRequirements.filter(
|
||||
(req) => req.typeMachineId === item.id,
|
||||
);
|
||||
const pieceRequirements = this.typeMachinePieceRequirements.filter((req) => req.typeMachineId === item.id);
|
||||
return this.buildTypeMachine(item, include ?? {}, componentRequirements, pieceRequirements);
|
||||
return this.buildTypeMachine(
|
||||
item,
|
||||
include ?? {},
|
||||
componentRequirements,
|
||||
pieceRequirements,
|
||||
);
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -471,9 +504,13 @@ class InMemoryPrismaService {
|
||||
findMany: async ({ include, where }: any = {}) => {
|
||||
let machines = this.machines;
|
||||
if (where?.siteId) {
|
||||
machines = machines.filter((machine) => machine.siteId === where.siteId);
|
||||
machines = machines.filter(
|
||||
(machine) => machine.siteId === where.siteId,
|
||||
);
|
||||
}
|
||||
return machines.map((machine) => this.buildMachine(machine, include ?? {}));
|
||||
return machines.map((machine) =>
|
||||
this.buildMachine(machine, include ?? {}),
|
||||
);
|
||||
},
|
||||
delete: async ({ where }: any) => {
|
||||
const index = this.machines.findIndex((item) => item.id === where.id);
|
||||
@@ -498,7 +535,8 @@ class InMemoryPrismaService {
|
||||
parentComposantId: data.parentComposantId ?? null,
|
||||
typeComposantId: data.typeComposantId ?? null,
|
||||
composantModelId: data.composantModelId ?? null,
|
||||
typeMachineComponentRequirementId: data.typeMachineComponentRequirementId ?? null,
|
||||
typeMachineComponentRequirementId:
|
||||
data.typeMachineComponentRequirementId ?? null,
|
||||
constructeurId: data.constructeurId ?? null,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
@@ -509,10 +547,14 @@ class InMemoryPrismaService {
|
||||
findMany: async ({ where }: any) => {
|
||||
let composants = this.composants;
|
||||
if (where?.machineId !== undefined) {
|
||||
composants = composants.filter((item) => item.machineId === where.machineId);
|
||||
composants = composants.filter(
|
||||
(item) => item.machineId === where.machineId,
|
||||
);
|
||||
}
|
||||
if (where?.parentComposantId !== undefined) {
|
||||
composants = composants.filter((item) => item.parentComposantId === where.parentComposantId);
|
||||
composants = composants.filter(
|
||||
(item) => item.parentComposantId === where.parentComposantId,
|
||||
);
|
||||
}
|
||||
return composants.map((item) => ({ ...item }));
|
||||
},
|
||||
@@ -531,7 +573,8 @@ class InMemoryPrismaService {
|
||||
composantId: data.composantId ?? null,
|
||||
typePieceId: data.typePieceId ?? null,
|
||||
pieceModelId: data.pieceModelId ?? null,
|
||||
typeMachinePieceRequirementId: data.typeMachinePieceRequirementId ?? null,
|
||||
typeMachinePieceRequirementId:
|
||||
data.typeMachinePieceRequirementId ?? null,
|
||||
constructeurId: data.constructeurId ?? null,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
@@ -545,7 +588,9 @@ class InMemoryPrismaService {
|
||||
pieces = pieces.filter((item) => item.machineId === where.machineId);
|
||||
}
|
||||
if (where?.composantId !== undefined) {
|
||||
pieces = pieces.filter((item) => item.composantId === where.composantId);
|
||||
pieces = pieces.filter(
|
||||
(item) => item.composantId === where.composantId,
|
||||
);
|
||||
}
|
||||
return pieces.map((item) => ({ ...item }));
|
||||
},
|
||||
@@ -574,14 +619,18 @@ class InMemoryPrismaService {
|
||||
return this.customFields
|
||||
.filter((field) => {
|
||||
if (!where) return true;
|
||||
return Object.entries(where).every(([key, value]) => (field as any)[key] === value);
|
||||
return Object.entries(where).every(
|
||||
([key, value]) => (field as any)[key] === value,
|
||||
);
|
||||
})
|
||||
.map((field) => ({ ...field }));
|
||||
},
|
||||
deleteMany: async ({ where }: any) => {
|
||||
const before = this.customFields.length;
|
||||
this.customFields = this.customFields.filter((field) => {
|
||||
return !Object.entries(where).every(([key, value]) => (field as any)[key] === value);
|
||||
return !Object.entries(where).every(
|
||||
([key, value]) => (field as any)[key] === value,
|
||||
);
|
||||
});
|
||||
return { count: before - this.customFields.length };
|
||||
},
|
||||
@@ -606,12 +655,18 @@ class InMemoryPrismaService {
|
||||
findMany: async ({ where, include }: any) => {
|
||||
const records = this.customFieldValues.filter((value) => {
|
||||
if (!where) return true;
|
||||
return Object.entries(where).every(([key, v]) => (value as any)[key] === v);
|
||||
return Object.entries(where).every(
|
||||
([key, v]) => (value as any)[key] === v,
|
||||
);
|
||||
});
|
||||
return records.map((record) => this.buildCustomFieldValue(record, include ?? {}));
|
||||
return records.map((record) =>
|
||||
this.buildCustomFieldValue(record, include ?? {}),
|
||||
);
|
||||
},
|
||||
findUnique: async ({ where, include }: any) => {
|
||||
const record = this.customFieldValues.find((value) => value.id === where.id);
|
||||
const record = this.customFieldValues.find(
|
||||
(value) => value.id === where.id,
|
||||
);
|
||||
if (!record) {
|
||||
return null;
|
||||
}
|
||||
@@ -625,7 +680,9 @@ class InMemoryPrismaService {
|
||||
);
|
||||
},
|
||||
update: async ({ where, data, include }: any) => {
|
||||
const record = this.customFieldValues.find((value) => value.id === where.id);
|
||||
const record = this.customFieldValues.find(
|
||||
(value) => value.id === where.id,
|
||||
);
|
||||
if (!record) {
|
||||
throw new Error('Custom field value not found');
|
||||
}
|
||||
@@ -636,7 +693,9 @@ class InMemoryPrismaService {
|
||||
return this.buildCustomFieldValue(record, include ?? {});
|
||||
},
|
||||
delete: async ({ where }: any) => {
|
||||
const index = this.customFieldValues.findIndex((value) => value.id === where.id);
|
||||
const index = this.customFieldValues.findIndex(
|
||||
(value) => value.id === where.id,
|
||||
);
|
||||
if (index === -1) {
|
||||
throw new Error('Custom field value not found');
|
||||
}
|
||||
@@ -649,24 +708,32 @@ class InMemoryPrismaService {
|
||||
count: async ({ where }: any) => {
|
||||
return this.profiles.filter((profile) => {
|
||||
if (!where) return true;
|
||||
return Object.entries(where).every(([key, value]) => (profile as any)[key] === value);
|
||||
return Object.entries(where).every(
|
||||
([key, value]) => (profile as any)[key] === value,
|
||||
);
|
||||
}).length;
|
||||
},
|
||||
findMany: async ({ where, orderBy, select }: any) => {
|
||||
let profiles = this.profiles;
|
||||
if (where) {
|
||||
profiles = profiles.filter((profile) =>
|
||||
Object.entries(where).every(([key, value]) => (profile as any)[key] === value),
|
||||
Object.entries(where).every(
|
||||
([key, value]) => (profile as any)[key] === value,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (orderBy?.createdAt === 'asc') {
|
||||
profiles = [...profiles].sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
|
||||
profiles = [...profiles].sort(
|
||||
(a, b) => a.createdAt.getTime() - b.createdAt.getTime(),
|
||||
);
|
||||
}
|
||||
return profiles.map((profile) => this.applySelect(profile, select));
|
||||
},
|
||||
findFirst: async ({ where, select }: any) => {
|
||||
const profile = this.profiles.find((item) =>
|
||||
Object.entries(where).every(([key, value]) => (item as any)[key] === value),
|
||||
Object.entries(where).every(
|
||||
([key, value]) => (item as any)[key] === value,
|
||||
),
|
||||
);
|
||||
return profile ? this.applySelect(profile, select) : null;
|
||||
},
|
||||
@@ -713,8 +780,12 @@ class InMemoryPrismaService {
|
||||
private buildSite(site: SiteRecord, include: any) {
|
||||
const base: any = { ...site };
|
||||
if (include?.machines) {
|
||||
const machines = this.machines.filter((machine) => machine.siteId === site.id);
|
||||
base.machines = machines.map((machine) => this.buildMachine(machine, include.machines.include ?? {}));
|
||||
const machines = this.machines.filter(
|
||||
(machine) => machine.siteId === site.id,
|
||||
);
|
||||
base.machines = machines.map((machine) =>
|
||||
this.buildMachine(machine, include.machines.include ?? {}),
|
||||
);
|
||||
}
|
||||
if (include?.documents) {
|
||||
base.documents = [];
|
||||
@@ -730,13 +801,17 @@ class InMemoryPrismaService {
|
||||
) {
|
||||
const base: any = { ...record };
|
||||
if (include?.customFields) {
|
||||
base.customFields = this.customFields.filter((field) => field.typeMachineId === record.id);
|
||||
base.customFields = this.customFields.filter(
|
||||
(field) => field.typeMachineId === record.id,
|
||||
);
|
||||
}
|
||||
if (include?.componentRequirements) {
|
||||
base.componentRequirements = componentRequirements.map((requirement) => ({
|
||||
...requirement,
|
||||
typeComposant: include.componentRequirements.include?.typeComposant
|
||||
? this.typeComposants.find((item) => item.id === requirement.typeComposantId) ?? null
|
||||
? (this.typeComposants.find(
|
||||
(item) => item.id === requirement.typeComposantId,
|
||||
) ?? null)
|
||||
: undefined,
|
||||
}));
|
||||
}
|
||||
@@ -744,14 +819,18 @@ class InMemoryPrismaService {
|
||||
base.pieceRequirements = pieceRequirements.map((requirement) => ({
|
||||
...requirement,
|
||||
typePiece: include.pieceRequirements.include?.typePiece
|
||||
? this.typePieces.find((item) => item.id === requirement.typePieceId) ?? null
|
||||
? (this.typePieces.find(
|
||||
(item) => item.id === requirement.typePieceId,
|
||||
) ?? null)
|
||||
: undefined,
|
||||
}));
|
||||
}
|
||||
if (include?.machines) {
|
||||
base.machines = this.machines
|
||||
.filter((machine) => machine.typeMachineId === record.id)
|
||||
.map((machine) => this.buildMachine(machine, include.machines.include ?? {}));
|
||||
.map((machine) =>
|
||||
this.buildMachine(machine, include.machines.include ?? {}),
|
||||
);
|
||||
}
|
||||
return base;
|
||||
}
|
||||
@@ -768,9 +847,10 @@ class InMemoryPrismaService {
|
||||
? this.typeMachines.find((item) => item.id === machine.typeMachineId)
|
||||
: null;
|
||||
if (typeMachine) {
|
||||
const componentRequirements = this.typeMachineComponentRequirements.filter(
|
||||
(req) => req.typeMachineId === typeMachine.id,
|
||||
);
|
||||
const componentRequirements =
|
||||
this.typeMachineComponentRequirements.filter(
|
||||
(req) => req.typeMachineId === typeMachine.id,
|
||||
);
|
||||
const pieceRequirements = this.typeMachinePieceRequirements.filter(
|
||||
(req) => req.typeMachineId === typeMachine.id,
|
||||
);
|
||||
@@ -790,20 +870,35 @@ class InMemoryPrismaService {
|
||||
}
|
||||
|
||||
if (include?.composants) {
|
||||
const composants = this.composants.filter((component) => component.machineId === machine.id);
|
||||
const composants = this.composants.filter(
|
||||
(component) => component.machineId === machine.id,
|
||||
);
|
||||
base.composants = composants
|
||||
.filter((component) => component.parentComposantId === null)
|
||||
.map((component) => this.buildComponent(component, include.composants.include ?? {}));
|
||||
.map((component) =>
|
||||
this.buildComponent(component, include.composants.include ?? {}),
|
||||
);
|
||||
}
|
||||
|
||||
if (include?.pieces) {
|
||||
const machinePieces = this.pieces.filter((piece) => piece.machineId === machine.id && piece.composantId === null);
|
||||
base.pieces = machinePieces.map((piece) => this.buildPiece(piece, include.pieces.include ?? {}));
|
||||
const machinePieces = this.pieces.filter(
|
||||
(piece) => piece.machineId === machine.id && piece.composantId === null,
|
||||
);
|
||||
base.pieces = machinePieces.map((piece) =>
|
||||
this.buildPiece(piece, include.pieces.include ?? {}),
|
||||
);
|
||||
}
|
||||
|
||||
if (include?.customFieldValues) {
|
||||
const values = this.customFieldValues.filter((value) => value.machineId === machine.id);
|
||||
base.customFieldValues = values.map((value) => this.buildCustomFieldValue(value, include.customFieldValues.include ?? {}));
|
||||
const values = this.customFieldValues.filter(
|
||||
(value) => value.machineId === machine.id,
|
||||
);
|
||||
base.customFieldValues = values.map((value) =>
|
||||
this.buildCustomFieldValue(
|
||||
value,
|
||||
include.customFieldValues.include ?? {},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (include?.documents) {
|
||||
@@ -818,7 +913,9 @@ class InMemoryPrismaService {
|
||||
|
||||
if (include?.typeComposant) {
|
||||
base.typeComposant = component.typeComposantId
|
||||
? this.typeComposants.find((item) => item.id === component.typeComposantId) ?? null
|
||||
? (this.typeComposants.find(
|
||||
(item) => item.id === component.typeComposantId,
|
||||
) ?? null)
|
||||
: null;
|
||||
}
|
||||
|
||||
@@ -828,26 +925,42 @@ class InMemoryPrismaService {
|
||||
|
||||
if (include?.typeMachineComponentRequirement) {
|
||||
const requirement = component.typeMachineComponentRequirementId
|
||||
? this.typeMachineComponentRequirements.find((item) => item.id === component.typeMachineComponentRequirementId) ?? null
|
||||
? (this.typeMachineComponentRequirements.find(
|
||||
(item) => item.id === component.typeMachineComponentRequirementId,
|
||||
) ?? null)
|
||||
: null;
|
||||
base.typeMachineComponentRequirement = requirement
|
||||
? {
|
||||
...requirement,
|
||||
typeComposant: include.typeMachineComponentRequirement.include?.typeComposant
|
||||
? this.typeComposants.find((item) => item.id === requirement.typeComposantId) ?? null
|
||||
typeComposant: include.typeMachineComponentRequirement.include
|
||||
?.typeComposant
|
||||
? (this.typeComposants.find(
|
||||
(item) => item.id === requirement.typeComposantId,
|
||||
) ?? null)
|
||||
: undefined,
|
||||
}
|
||||
: null;
|
||||
}
|
||||
|
||||
if (include?.sousComposants) {
|
||||
const children = this.composants.filter((item) => item.parentComposantId === component.id);
|
||||
base.sousComposants = children.map((child) => this.buildComponent(child, include.sousComposants.include ?? {}));
|
||||
const children = this.composants.filter(
|
||||
(item) => item.parentComposantId === component.id,
|
||||
);
|
||||
base.sousComposants = children.map((child) =>
|
||||
this.buildComponent(child, include.sousComposants.include ?? {}),
|
||||
);
|
||||
}
|
||||
|
||||
if (include?.customFieldValues) {
|
||||
const values = this.customFieldValues.filter((value) => value.composantId === component.id);
|
||||
base.customFieldValues = values.map((value) => this.buildCustomFieldValue(value, include.customFieldValues.include ?? {}));
|
||||
const values = this.customFieldValues.filter(
|
||||
(value) => value.composantId === component.id,
|
||||
);
|
||||
base.customFieldValues = values.map((value) =>
|
||||
this.buildCustomFieldValue(
|
||||
value,
|
||||
include.customFieldValues.include ?? {},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (include?.constructeur) {
|
||||
@@ -855,8 +968,12 @@ class InMemoryPrismaService {
|
||||
}
|
||||
|
||||
if (include?.pieces) {
|
||||
const relatedPieces = this.pieces.filter((piece) => piece.composantId === component.id);
|
||||
base.pieces = relatedPieces.map((piece) => this.buildPiece(piece, include.pieces.include ?? {}));
|
||||
const relatedPieces = this.pieces.filter(
|
||||
(piece) => piece.composantId === component.id,
|
||||
);
|
||||
base.pieces = relatedPieces.map((piece) =>
|
||||
this.buildPiece(piece, include.pieces.include ?? {}),
|
||||
);
|
||||
}
|
||||
|
||||
return base;
|
||||
@@ -866,8 +983,15 @@ class InMemoryPrismaService {
|
||||
const base: any = { ...piece };
|
||||
|
||||
if (include?.customFieldValues) {
|
||||
const values = this.customFieldValues.filter((value) => value.pieceId === piece.id);
|
||||
base.customFieldValues = values.map((value) => this.buildCustomFieldValue(value, include.customFieldValues.include ?? {}));
|
||||
const values = this.customFieldValues.filter(
|
||||
(value) => value.pieceId === piece.id,
|
||||
);
|
||||
base.customFieldValues = values.map((value) =>
|
||||
this.buildCustomFieldValue(
|
||||
value,
|
||||
include.customFieldValues.include ?? {},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (include?.constructeur) {
|
||||
@@ -880,13 +1004,17 @@ class InMemoryPrismaService {
|
||||
|
||||
if (include?.typeMachinePieceRequirement) {
|
||||
const requirement = piece.typeMachinePieceRequirementId
|
||||
? this.typeMachinePieceRequirements.find((item) => item.id === piece.typeMachinePieceRequirementId) ?? null
|
||||
? (this.typeMachinePieceRequirements.find(
|
||||
(item) => item.id === piece.typeMachinePieceRequirementId,
|
||||
) ?? null)
|
||||
: null;
|
||||
base.typeMachinePieceRequirement = requirement
|
||||
? {
|
||||
...requirement,
|
||||
typePiece: include.typeMachinePieceRequirement.include?.typePiece
|
||||
? this.typePieces.find((item) => item.id === requirement.typePieceId) ?? null
|
||||
? (this.typePieces.find(
|
||||
(item) => item.id === requirement.typePieceId,
|
||||
) ?? null)
|
||||
: undefined,
|
||||
}
|
||||
: null;
|
||||
@@ -894,7 +1022,8 @@ class InMemoryPrismaService {
|
||||
|
||||
if (include?.typePiece) {
|
||||
base.typePiece = piece.typePieceId
|
||||
? this.typePieces.find((item) => item.id === piece.typePieceId) ?? null
|
||||
? (this.typePieces.find((item) => item.id === piece.typePieceId) ??
|
||||
null)
|
||||
: null;
|
||||
}
|
||||
|
||||
@@ -904,7 +1033,9 @@ class InMemoryPrismaService {
|
||||
private buildCustomFieldValue(record: CustomFieldValueRecord, include: any) {
|
||||
const base: any = { ...record };
|
||||
if (include?.customField) {
|
||||
base.customField = this.customFields.find((field) => field.id === record.customFieldId) ?? null;
|
||||
base.customField =
|
||||
this.customFields.find((field) => field.id === record.customFieldId) ??
|
||||
null;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
@@ -918,7 +1049,6 @@ describe('Inventory flow (e2e)', () => {
|
||||
beforeAll(async () => {
|
||||
prismaStub = new InMemoryPrismaService();
|
||||
|
||||
|
||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
})
|
||||
@@ -945,14 +1075,16 @@ describe('Inventory flow (e2e)', () => {
|
||||
});
|
||||
|
||||
it('should create a type, create a machine with new component/piece selections, and edit technical fields', async () => {
|
||||
const siteResponse = await request(app.getHttpServer()).post('/sites').send({
|
||||
name: 'Site Principal',
|
||||
contactName: 'Responsable Maintenance',
|
||||
contactPhone: '+33 1 23 45 67 89',
|
||||
contactAddress: '1 rue de la Paix',
|
||||
contactPostalCode: '75000',
|
||||
contactCity: 'Paris',
|
||||
});
|
||||
const siteResponse = await request(app.getHttpServer())
|
||||
.post('/sites')
|
||||
.send({
|
||||
name: 'Site Principal',
|
||||
contactName: 'Responsable Maintenance',
|
||||
contactPhone: '+33 1 23 45 67 89',
|
||||
contactAddress: '1 rue de la Paix',
|
||||
contactPostalCode: '75000',
|
||||
contactCity: 'Paris',
|
||||
});
|
||||
|
||||
expect(siteResponse.status).toBe(201);
|
||||
const siteId = siteResponse.body.id;
|
||||
@@ -1069,7 +1201,9 @@ describe('Inventory flow (e2e)', () => {
|
||||
expect(machineResponse.status).toBe(201);
|
||||
const machineId = machineResponse.body.id;
|
||||
|
||||
const machineDetailsResponse = await request(app.getHttpServer()).get(`/machines/${machineId}`);
|
||||
const machineDetailsResponse = await request(app.getHttpServer()).get(
|
||||
`/machines/${machineId}`,
|
||||
);
|
||||
expect(machineDetailsResponse.status).toBe(200);
|
||||
const machine = machineDetailsResponse.body;
|
||||
|
||||
@@ -1092,7 +1226,9 @@ describe('Inventory flow (e2e)', () => {
|
||||
expect(updateResponse.status).toBe(200);
|
||||
expect(updateResponse.body.value).toBe('8 kW');
|
||||
|
||||
const refreshedMachineResponse = await request(app.getHttpServer()).get(`/machines/${machine.id}`);
|
||||
const refreshedMachineResponse = await request(app.getHttpServer()).get(
|
||||
`/machines/${machine.id}`,
|
||||
);
|
||||
expect(refreshedMachineResponse.status).toBe(200);
|
||||
const refreshedComponent = refreshedMachineResponse.body.composants[0];
|
||||
expect(refreshedComponent.customFieldValues[0].value).toBe('8 kW');
|
||||
@@ -1110,7 +1246,9 @@ describe('Inventory flow (e2e)', () => {
|
||||
} as any);
|
||||
|
||||
const created = { id: 'component-1' };
|
||||
const createSpy = jest.spyOn(prisma.composant, 'create').mockResolvedValue(created as any);
|
||||
const createSpy = jest
|
||||
.spyOn(prisma.composant, 'create')
|
||||
.mockResolvedValue(created as any);
|
||||
|
||||
const response = await request(app.getHttpServer())
|
||||
.post('/composants')
|
||||
@@ -1157,14 +1295,14 @@ describe('Inventory flow (e2e)', () => {
|
||||
jest.spyOn(prisma.machine, 'findUnique').mockResolvedValue({
|
||||
id: 'machine-1',
|
||||
typeMachine: {
|
||||
pieceRequirements: [
|
||||
{ id: 'req-1', typePieceId: 'type-piece-1' },
|
||||
],
|
||||
pieceRequirements: [{ id: 'req-1', typePieceId: 'type-piece-1' }],
|
||||
},
|
||||
} as any);
|
||||
|
||||
const created = { id: 'piece-1' };
|
||||
const createSpy = jest.spyOn(prisma.piece, 'create').mockResolvedValue(created as any);
|
||||
const createSpy = jest
|
||||
.spyOn(prisma.piece, 'create')
|
||||
.mockResolvedValue(created as any);
|
||||
|
||||
const response = await request(app.getHttpServer())
|
||||
.post('/pieces')
|
||||
@@ -1184,9 +1322,7 @@ describe('Inventory flow (e2e)', () => {
|
||||
jest.spyOn(prisma.machine, 'findUnique').mockResolvedValue({
|
||||
id: 'machine-1',
|
||||
typeMachine: {
|
||||
pieceRequirements: [
|
||||
{ id: 'req-1', typePieceId: 'type-piece-1' },
|
||||
],
|
||||
pieceRequirements: [{ id: 'req-1', typePieceId: 'type-piece-1' }],
|
||||
},
|
||||
} as any);
|
||||
|
||||
@@ -1203,7 +1339,6 @@ describe('Inventory flow (e2e)', () => {
|
||||
.expect(400);
|
||||
|
||||
expect(createSpy).not.toHaveBeenCalled();
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user