feat(machine): support skeleton reconfiguration
This commit is contained in:
@@ -1,6 +1,10 @@
|
|||||||
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 { MachinesService } from './machines.service';
|
||||||
import { CreateMachineDto, UpdateMachineDto } from '../shared/dto/machine.dto';
|
import {
|
||||||
|
CreateMachineDto,
|
||||||
|
UpdateMachineDto,
|
||||||
|
ReconfigureMachineDto,
|
||||||
|
} from '../shared/dto/machine.dto';
|
||||||
|
|
||||||
@Controller('machines')
|
@Controller('machines')
|
||||||
export class MachinesController {
|
export class MachinesController {
|
||||||
@@ -26,6 +30,14 @@ export class MachinesController {
|
|||||||
return this.machinesService.update(id, updateMachineDto);
|
return this.machinesService.update(id, updateMachineDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Patch(':id/skeleton')
|
||||||
|
reconfigure(
|
||||||
|
@Param('id') id: string,
|
||||||
|
@Body() reconfigureMachineDto: ReconfigureMachineDto,
|
||||||
|
) {
|
||||||
|
return this.machinesService.reconfigure(id, reconfigureMachineDto);
|
||||||
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
remove(@Param('id') id: string) {
|
remove(@Param('id') id: string) {
|
||||||
return this.machinesService.remove(id);
|
return this.machinesService.remove(id);
|
||||||
|
|||||||
@@ -1,53 +1,125 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { PrismaService } from '../prisma/prisma.service';
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
import {
|
import {
|
||||||
CreateMachineDto,
|
CreateMachineDto,
|
||||||
UpdateMachineDto,
|
UpdateMachineDto,
|
||||||
|
ReconfigureMachineDto,
|
||||||
MachineComponentSelectionDto,
|
MachineComponentSelectionDto,
|
||||||
MachinePieceSelectionDto
|
MachinePieceSelectionDto,
|
||||||
} from '../shared/dto/machine.dto';
|
} from '../shared/dto/machine.dto';
|
||||||
|
|
||||||
|
const TYPE_MACHINE_CONFIGURATION_INCLUDE = {
|
||||||
|
customFields: true,
|
||||||
|
componentRequirements: {
|
||||||
|
include: {
|
||||||
|
typeComposant: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pieceRequirements: {
|
||||||
|
include: {
|
||||||
|
typePiece: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const MACHINE_DEFAULT_INCLUDE = {
|
||||||
|
site: true,
|
||||||
|
typeMachine: {
|
||||||
|
include: TYPE_MACHINE_CONFIGURATION_INCLUDE,
|
||||||
|
},
|
||||||
|
constructeur: true,
|
||||||
|
composants: {
|
||||||
|
include: {
|
||||||
|
typeComposant: true,
|
||||||
|
composantModel: true,
|
||||||
|
typeMachineComponentRequirement: {
|
||||||
|
include: {
|
||||||
|
typeComposant: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sousComposants: true,
|
||||||
|
customFieldValues: {
|
||||||
|
include: {
|
||||||
|
customField: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
constructeur: true,
|
||||||
|
pieces: {
|
||||||
|
include: {
|
||||||
|
customFieldValues: {
|
||||||
|
include: {
|
||||||
|
customField: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
constructeur: true,
|
||||||
|
pieceModel: true,
|
||||||
|
typeMachinePieceRequirement: {
|
||||||
|
include: {
|
||||||
|
typePiece: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pieces: {
|
||||||
|
include: {
|
||||||
|
customFieldValues: {
|
||||||
|
include: {
|
||||||
|
customField: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
constructeur: true,
|
||||||
|
pieceModel: true,
|
||||||
|
typeMachinePieceRequirement: {
|
||||||
|
include: {
|
||||||
|
typePiece: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
customFieldValues: {
|
||||||
|
include: {
|
||||||
|
customField: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
documents: true,
|
||||||
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MachinesService {
|
export class MachinesService {
|
||||||
constructor(private prisma: PrismaService) {}
|
constructor(private prisma: PrismaService) {}
|
||||||
|
|
||||||
async create(createMachineDto: CreateMachineDto) {
|
private async getTypeMachineConfiguration(typeMachineId: string) {
|
||||||
const {
|
|
||||||
componentSelections = [],
|
|
||||||
pieceSelections = [],
|
|
||||||
...machineData
|
|
||||||
} = createMachineDto;
|
|
||||||
|
|
||||||
if (!machineData.typeMachineId) {
|
|
||||||
throw new Error('typeMachineId est requis pour créer une machine à partir d\'un squelette.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const typeMachine = await this.prisma.typeMachine.findUnique({
|
const typeMachine = await this.prisma.typeMachine.findUnique({
|
||||||
where: { id: machineData.typeMachineId },
|
where: { id: typeMachineId },
|
||||||
include: {
|
include: TYPE_MACHINE_CONFIGURATION_INCLUDE,
|
||||||
customFields: true,
|
|
||||||
componentRequirements: {
|
|
||||||
include: {
|
|
||||||
typeComposant: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pieceRequirements: {
|
|
||||||
include: {
|
|
||||||
typePiece: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!typeMachine) {
|
if (!typeMachine) {
|
||||||
throw new Error('Type de machine non trouvé');
|
throw new Error('Type de machine non trouvé');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return typeMachine;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async buildConfigurationContext(
|
||||||
|
typeMachine: any,
|
||||||
|
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 componentRequirementMap = new Map(
|
const componentRequirementMap = new Map(
|
||||||
typeMachine.componentRequirements.map((requirement) => [requirement.id, requirement]),
|
componentRequirements.map((requirement: any) => [requirement.id, requirement]),
|
||||||
);
|
);
|
||||||
const pieceRequirementMap = new Map(
|
const pieceRequirementMap = new Map(
|
||||||
typeMachine.pieceRequirements.map((requirement) => [requirement.id, requirement]),
|
pieceRequirements.map((requirement: any) => [requirement.id, requirement]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const componentSelectionMap = new Map<string, MachineComponentSelectionDto[]>();
|
const componentSelectionMap = new Map<string, MachineComponentSelectionDto[]>();
|
||||||
@@ -94,7 +166,7 @@ export class MachinesService {
|
|||||||
: [];
|
: [];
|
||||||
const pieceModelMap = new Map(pieceModels.map((model) => [model.id, model]));
|
const pieceModelMap = new Map(pieceModels.map((model) => [model.id, model]));
|
||||||
|
|
||||||
for (const requirement of typeMachine.componentRequirements) {
|
for (const requirement of componentRequirements) {
|
||||||
const selections = componentSelectionMap.get(requirement.id) ?? [];
|
const selections = componentSelectionMap.get(requirement.id) ?? [];
|
||||||
const min = requirement.minCount ?? (requirement.required ? 1 : 0);
|
const min = requirement.minCount ?? (requirement.required ? 1 : 0);
|
||||||
const max = requirement.maxCount ?? undefined;
|
const max = requirement.maxCount ?? undefined;
|
||||||
@@ -121,7 +193,7 @@ export class MachinesService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const requirement of typeMachine.pieceRequirements) {
|
for (const requirement of pieceRequirements) {
|
||||||
const selections = pieceSelectionMap.get(requirement.id) ?? [];
|
const selections = pieceSelectionMap.get(requirement.id) ?? [];
|
||||||
const min = requirement.minCount ?? (requirement.required ? 1 : 0);
|
const min = requirement.minCount ?? (requirement.required ? 1 : 0);
|
||||||
const max = requirement.maxCount ?? undefined;
|
const max = requirement.maxCount ?? undefined;
|
||||||
@@ -186,7 +258,42 @@ export class MachinesService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.prisma.$transaction(async (prisma) => {
|
return {
|
||||||
|
componentSelectionMap,
|
||||||
|
pieceSelectionMap,
|
||||||
|
componentModelMap,
|
||||||
|
pieceModelMap,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(createMachineDto: CreateMachineDto) {
|
||||||
|
const {
|
||||||
|
componentSelections = [],
|
||||||
|
pieceSelections = [],
|
||||||
|
...machineData
|
||||||
|
} = createMachineDto;
|
||||||
|
|
||||||
|
if (!machineData.typeMachineId) {
|
||||||
|
throw new Error('typeMachineId est requis pour créer une machine à partir d\'un squelette.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const typeMachine = await this.getTypeMachineConfiguration(machineData.typeMachineId);
|
||||||
|
|
||||||
|
const {
|
||||||
|
componentSelectionMap,
|
||||||
|
pieceSelectionMap,
|
||||||
|
componentModelMap,
|
||||||
|
pieceModelMap,
|
||||||
|
} = 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[];
|
||||||
|
|
||||||
|
return this.prisma.$transaction(async (prisma) => {
|
||||||
const machine = await prisma.machine.create({
|
const machine = await prisma.machine.create({
|
||||||
data: machineData,
|
data: machineData,
|
||||||
include: {
|
include: {
|
||||||
@@ -196,8 +303,8 @@ export class MachinesService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (typeMachine.componentRequirements.length > 0) {
|
if (componentRequirements.length > 0) {
|
||||||
for (const requirement of typeMachine.componentRequirements) {
|
for (const requirement of componentRequirements) {
|
||||||
const selections = componentSelectionMap.get(requirement.id) ?? [];
|
const selections = componentSelectionMap.get(requirement.id) ?? [];
|
||||||
for (const selection of selections) {
|
for (const selection of selections) {
|
||||||
const model = selection.componentModelId ? componentModelMap.get(selection.componentModelId) : undefined;
|
const model = selection.componentModelId ? componentModelMap.get(selection.componentModelId) : undefined;
|
||||||
@@ -212,8 +319,8 @@ export class MachinesService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeMachine.pieceRequirements.length > 0) {
|
if (pieceRequirements.length > 0) {
|
||||||
for (const requirement of typeMachine.pieceRequirements) {
|
for (const requirement of pieceRequirements) {
|
||||||
const selections = pieceSelectionMap.get(requirement.id) ?? [];
|
const selections = pieceSelectionMap.get(requirement.id) ?? [];
|
||||||
for (const selection of selections) {
|
for (const selection of selections) {
|
||||||
const model = selection.pieceModelId ? pieceModelMap.get(selection.pieceModelId) : undefined;
|
const model = selection.pieceModelId ? pieceModelMap.get(selection.pieceModelId) : undefined;
|
||||||
@@ -234,76 +341,7 @@ export class MachinesService {
|
|||||||
|
|
||||||
return prisma.machine.findUnique({
|
return prisma.machine.findUnique({
|
||||||
where: { id: machine.id },
|
where: { id: machine.id },
|
||||||
include: {
|
include: MACHINE_DEFAULT_INCLUDE,
|
||||||
site: true,
|
|
||||||
typeMachine: {
|
|
||||||
include: {
|
|
||||||
customFields: true,
|
|
||||||
componentRequirements: {
|
|
||||||
include: {
|
|
||||||
typeComposant: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pieceRequirements: {
|
|
||||||
include: {
|
|
||||||
typePiece: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
constructeur: true,
|
|
||||||
composants: {
|
|
||||||
include: {
|
|
||||||
typeComposant: true,
|
|
||||||
composantModel: true,
|
|
||||||
typeMachineComponentRequirement: {
|
|
||||||
include: {
|
|
||||||
typeComposant: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
sousComposants: true,
|
|
||||||
pieces: {
|
|
||||||
include: {
|
|
||||||
customFieldValues: {
|
|
||||||
include: {
|
|
||||||
customField: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
constructeur: true,
|
|
||||||
pieceModel: true,
|
|
||||||
typeMachinePieceRequirement: {
|
|
||||||
include: {
|
|
||||||
typePiece: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
constructeur: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pieces: {
|
|
||||||
include: {
|
|
||||||
customFieldValues: {
|
|
||||||
include: {
|
|
||||||
customField: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
constructeur: true,
|
|
||||||
pieceModel: true,
|
|
||||||
typeMachinePieceRequirement: {
|
|
||||||
include: {
|
|
||||||
typePiece: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
customFieldValues: {
|
|
||||||
include: {
|
|
||||||
customField: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
documents: true,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -660,239 +698,146 @@ export class MachinesService {
|
|||||||
|
|
||||||
async findAll() {
|
async findAll() {
|
||||||
return this.prisma.machine.findMany({
|
return this.prisma.machine.findMany({
|
||||||
include: {
|
include: MACHINE_DEFAULT_INCLUDE,
|
||||||
site: true,
|
|
||||||
typeMachine: {
|
|
||||||
include: {
|
|
||||||
customFields: true,
|
|
||||||
componentRequirements: {
|
|
||||||
include: {
|
|
||||||
typeComposant: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pieceRequirements: {
|
|
||||||
include: {
|
|
||||||
typePiece: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
constructeur: true,
|
|
||||||
composants: {
|
|
||||||
include: {
|
|
||||||
typeComposant: true,
|
|
||||||
composantModel: true,
|
|
||||||
typeMachineComponentRequirement: {
|
|
||||||
include: {
|
|
||||||
typeComposant: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
sousComposants: true,
|
|
||||||
customFieldValues: {
|
|
||||||
include: {
|
|
||||||
customField: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
constructeur: true,
|
|
||||||
pieces: {
|
|
||||||
include: {
|
|
||||||
customFieldValues: {
|
|
||||||
include: {
|
|
||||||
customField: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
constructeur: true,
|
|
||||||
pieceModel: true,
|
|
||||||
typeMachinePieceRequirement: {
|
|
||||||
include: {
|
|
||||||
typePiece: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pieces: {
|
|
||||||
include: {
|
|
||||||
customFieldValues: {
|
|
||||||
include: {
|
|
||||||
customField: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
constructeur: true,
|
|
||||||
pieceModel: true,
|
|
||||||
typeMachinePieceRequirement: {
|
|
||||||
include: {
|
|
||||||
typePiece: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
customFieldValues: {
|
|
||||||
include: {
|
|
||||||
customField: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
documents: true,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOne(id: string) {
|
async findOne(id: string) {
|
||||||
return this.prisma.machine.findUnique({
|
return this.prisma.machine.findUnique({
|
||||||
|
where: { id },
|
||||||
|
include: MACHINE_DEFAULT_INCLUDE,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async reconfigure(id: string, reconfigureMachineDto: ReconfigureMachineDto) {
|
||||||
|
const {
|
||||||
|
componentSelections = [],
|
||||||
|
pieceSelections = [],
|
||||||
|
} = reconfigureMachineDto;
|
||||||
|
|
||||||
|
const machine = await this.prisma.machine.findUnique({
|
||||||
where: { id },
|
where: { id },
|
||||||
include: {
|
include: {
|
||||||
site: true,
|
|
||||||
typeMachine: {
|
typeMachine: {
|
||||||
include: {
|
include: TYPE_MACHINE_CONFIGURATION_INCLUDE,
|
||||||
customFields: true,
|
|
||||||
componentRequirements: {
|
|
||||||
include: {
|
|
||||||
typeComposant: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pieceRequirements: {
|
|
||||||
include: {
|
|
||||||
typePiece: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
constructeur: true,
|
|
||||||
composants: {
|
|
||||||
include: {
|
|
||||||
typeComposant: true,
|
|
||||||
composantModel: true,
|
|
||||||
typeMachineComponentRequirement: {
|
|
||||||
include: {
|
|
||||||
typeComposant: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
sousComposants: true,
|
|
||||||
customFieldValues: {
|
|
||||||
include: {
|
|
||||||
customField: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
constructeur: true,
|
|
||||||
pieces: {
|
|
||||||
include: {
|
|
||||||
customFieldValues: {
|
|
||||||
include: {
|
|
||||||
customField: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
constructeur: true,
|
|
||||||
pieceModel: true,
|
|
||||||
typeMachinePieceRequirement: {
|
|
||||||
include: {
|
|
||||||
typePiece: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pieces: {
|
|
||||||
include: {
|
|
||||||
customFieldValues: {
|
|
||||||
include: {
|
|
||||||
customField: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
constructeur: true,
|
|
||||||
pieceModel: true,
|
|
||||||
typeMachinePieceRequirement: {
|
|
||||||
include: {
|
|
||||||
typePiece: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
customFieldValues: {
|
|
||||||
include: {
|
|
||||||
customField: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
documents: true,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!machine) {
|
||||||
|
throw new Error('Machine non trouvée');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!machine.typeMachineId || !machine.typeMachine) {
|
||||||
|
throw new Error('Impossible de reconfigurer une machine sans type de machine associé.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const typeMachine = machine.typeMachine;
|
||||||
|
|
||||||
|
const {
|
||||||
|
componentSelectionMap,
|
||||||
|
pieceSelectionMap,
|
||||||
|
componentModelMap,
|
||||||
|
pieceModelMap,
|
||||||
|
} = 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[];
|
||||||
|
|
||||||
|
return this.prisma.$transaction(async (prisma) => {
|
||||||
|
await prisma.customFieldValue.deleteMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
composant: {
|
||||||
|
machineId: id,
|
||||||
|
typeMachineComponentRequirementId: { not: null },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
piece: {
|
||||||
|
machineId: id,
|
||||||
|
typeMachinePieceRequirementId: { not: null },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
piece: {
|
||||||
|
composant: {
|
||||||
|
machineId: id,
|
||||||
|
typeMachineComponentRequirementId: { not: null },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await prisma.piece.deleteMany({
|
||||||
|
where: {
|
||||||
|
machineId: id,
|
||||||
|
typeMachinePieceRequirementId: { not: null },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await prisma.composant.deleteMany({
|
||||||
|
where: {
|
||||||
|
machineId: id,
|
||||||
|
typeMachineComponentRequirementId: { not: null },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (componentRequirements.length > 0) {
|
||||||
|
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, id, [definition]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const legacyComponents = (typeMachine as any).components;
|
||||||
|
if (legacyComponents) {
|
||||||
|
await this.createComponentsFromType(prisma, id, legacyComponents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pieceRequirements.length > 0) {
|
||||||
|
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, id, [definition]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const legacyPieces = (typeMachine as any).machinePieces;
|
||||||
|
if (legacyPieces) {
|
||||||
|
await this.createMachinePiecesFromType(prisma, id, legacyPieces);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return prisma.machine.findUnique({
|
||||||
|
where: { id },
|
||||||
|
include: MACHINE_DEFAULT_INCLUDE,
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(id: string, updateMachineDto: UpdateMachineDto) {
|
async update(id: string, updateMachineDto: UpdateMachineDto) {
|
||||||
return this.prisma.machine.update({
|
return this.prisma.machine.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: updateMachineDto,
|
data: updateMachineDto,
|
||||||
include: {
|
include: MACHINE_DEFAULT_INCLUDE,
|
||||||
site: true,
|
|
||||||
typeMachine: {
|
|
||||||
include: {
|
|
||||||
customFields: true,
|
|
||||||
componentRequirements: {
|
|
||||||
include: {
|
|
||||||
typeComposant: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pieceRequirements: {
|
|
||||||
include: {
|
|
||||||
typePiece: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
constructeur: true,
|
|
||||||
composants: {
|
|
||||||
include: {
|
|
||||||
typeComposant: true,
|
|
||||||
composantModel: true,
|
|
||||||
typeMachineComponentRequirement: {
|
|
||||||
include: {
|
|
||||||
typeComposant: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
sousComposants: true,
|
|
||||||
constructeur: true,
|
|
||||||
pieces: {
|
|
||||||
include: {
|
|
||||||
constructeur: true,
|
|
||||||
pieceModel: true,
|
|
||||||
typeMachinePieceRequirement: {
|
|
||||||
include: {
|
|
||||||
typePiece: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
customFieldValues: {
|
|
||||||
include: {
|
|
||||||
customField: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pieces: {
|
|
||||||
include: {
|
|
||||||
constructeur: true,
|
|
||||||
pieceModel: true,
|
|
||||||
typeMachinePieceRequirement: {
|
|
||||||
include: {
|
|
||||||
typePiece: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
customFieldValues: {
|
|
||||||
include: {
|
|
||||||
customField: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
customFieldValues: {
|
|
||||||
include: {
|
|
||||||
customField: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
documents: true,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,4 +90,18 @@ export class UpdateMachineDto {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
typeMachineId?: string;
|
typeMachineId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ReconfigureMachineDto {
|
||||||
|
@IsOptional()
|
||||||
|
@IsArray()
|
||||||
|
@ValidateNested({ each: true })
|
||||||
|
@Type(() => MachineComponentSelectionDto)
|
||||||
|
componentSelections?: MachineComponentSelectionDto[];
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsArray()
|
||||||
|
@ValidateNested({ each: true })
|
||||||
|
@Type(() => MachinePieceSelectionDto)
|
||||||
|
pieceSelections?: MachinePieceSelectionDto[];
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,17 +3,31 @@ import { INestApplication } from '@nestjs/common';
|
|||||||
import * as request from 'supertest';
|
import * as request from 'supertest';
|
||||||
import { App } from 'supertest/types';
|
import { App } from 'supertest/types';
|
||||||
import { AppModule } from './../src/app.module';
|
import { AppModule } from './../src/app.module';
|
||||||
|
import { PrismaService } from '../src/prisma/prisma.service';
|
||||||
|
|
||||||
describe('AppController (e2e)', () => {
|
describe('AppController (e2e)', () => {
|
||||||
let app: INestApplication<App>;
|
let app: INestApplication<App>;
|
||||||
|
let prisma: PrismaService;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeAll(async () => {
|
||||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||||
imports: [AppModule],
|
imports: [AppModule],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
app = moduleFixture.createNestApplication();
|
app = moduleFixture.createNestApplication();
|
||||||
await app.init();
|
await app.init();
|
||||||
|
|
||||||
|
prisma = app.get(PrismaService);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await app.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await prisma.$executeRawUnsafe(
|
||||||
|
'TRUNCATE TABLE custom_field_values, documents, pieces, composants, machines, composant_models, piece_models, type_machine_component_requirements, type_machine_piece_requirements, custom_fields, type_machines, type_composants, type_pieces, constructeurs, sites RESTART IDENTITY CASCADE',
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('/ (GET)', () => {
|
it('/ (GET)', () => {
|
||||||
@@ -22,4 +36,256 @@ describe('AppController (e2e)', () => {
|
|||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Hello World!');
|
.expect('Hello World!');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Machines skeleton reconfiguration', () => {
|
||||||
|
it('reconfigures machine skeleton according to updated requirements', async () => {
|
||||||
|
const site = await prisma.site.create({
|
||||||
|
data: {
|
||||||
|
name: 'Site principal',
|
||||||
|
contactName: 'Jane Doe',
|
||||||
|
contactPhone: '0102030405',
|
||||||
|
contactAddress: '1 rue Principale',
|
||||||
|
contactPostalCode: '75000',
|
||||||
|
contactCity: 'Paris',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const typeComposant = await prisma.typeComposant.create({
|
||||||
|
data: {
|
||||||
|
name: 'Module',
|
||||||
|
description: 'Module principal',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await prisma.customField.create({
|
||||||
|
data: {
|
||||||
|
name: 'Serial',
|
||||||
|
type: 'text',
|
||||||
|
required: false,
|
||||||
|
options: [],
|
||||||
|
typeComposantId: typeComposant.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const typePiece = await prisma.typePiece.create({
|
||||||
|
data: {
|
||||||
|
name: 'Pièce de rechange',
|
||||||
|
description: 'Pièce critique',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await prisma.customField.create({
|
||||||
|
data: {
|
||||||
|
name: 'Batch',
|
||||||
|
type: 'text',
|
||||||
|
required: false,
|
||||||
|
options: [],
|
||||||
|
typePieceId: typePiece.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const componentModelV1 = await prisma.composantModel.create({
|
||||||
|
data: {
|
||||||
|
name: 'Module V1',
|
||||||
|
typeComposantId: typeComposant.id,
|
||||||
|
structure: {
|
||||||
|
name: 'Module V1',
|
||||||
|
customFields: [{ name: 'Serial', defaultValue: 'SERIAL-V1' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const componentModelV2 = await prisma.composantModel.create({
|
||||||
|
data: {
|
||||||
|
name: 'Module V2',
|
||||||
|
typeComposantId: typeComposant.id,
|
||||||
|
structure: {
|
||||||
|
name: 'Module V2',
|
||||||
|
customFields: [{ name: 'Serial', defaultValue: 'SERIAL-V2' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const pieceModelV1 = await prisma.pieceModel.create({
|
||||||
|
data: {
|
||||||
|
name: 'Pièce V1',
|
||||||
|
typePieceId: typePiece.id,
|
||||||
|
structure: {
|
||||||
|
name: 'Pièce V1',
|
||||||
|
customFields: [{ name: 'Batch', defaultValue: 'BATCH-V1' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const pieceModelV2 = await prisma.pieceModel.create({
|
||||||
|
data: {
|
||||||
|
name: 'Pièce V2',
|
||||||
|
typePieceId: typePiece.id,
|
||||||
|
structure: {
|
||||||
|
name: 'Pièce V2',
|
||||||
|
customFields: [{ name: 'Batch', defaultValue: 'BATCH-V2' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const typeMachine = await prisma.typeMachine.create({
|
||||||
|
data: {
|
||||||
|
name: 'Type Machine A',
|
||||||
|
componentRequirements: {
|
||||||
|
create: [
|
||||||
|
{
|
||||||
|
label: 'Module initial',
|
||||||
|
minCount: 1,
|
||||||
|
maxCount: 1,
|
||||||
|
required: true,
|
||||||
|
allowNewModels: false,
|
||||||
|
typeComposantId: typeComposant.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
pieceRequirements: {
|
||||||
|
create: [
|
||||||
|
{
|
||||||
|
label: 'Pièce initiale',
|
||||||
|
minCount: 1,
|
||||||
|
maxCount: 1,
|
||||||
|
required: true,
|
||||||
|
allowNewModels: false,
|
||||||
|
typePieceId: typePiece.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
componentRequirements: true,
|
||||||
|
pieceRequirements: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const initialComponentRequirement = typeMachine.componentRequirements[0];
|
||||||
|
const initialPieceRequirement = typeMachine.pieceRequirements[0];
|
||||||
|
|
||||||
|
const createResponse = await request(app.getHttpServer())
|
||||||
|
.post('/machines')
|
||||||
|
.send({
|
||||||
|
name: 'Machine Alpha',
|
||||||
|
siteId: site.id,
|
||||||
|
typeMachineId: typeMachine.id,
|
||||||
|
componentSelections: [
|
||||||
|
{
|
||||||
|
requirementId: initialComponentRequirement.id,
|
||||||
|
componentModelId: componentModelV1.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pieceSelections: [
|
||||||
|
{
|
||||||
|
requirementId: initialPieceRequirement.id,
|
||||||
|
pieceModelId: pieceModelV1.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.expect(201);
|
||||||
|
|
||||||
|
const machineId = createResponse.body.id as string;
|
||||||
|
|
||||||
|
const initialComponents = await prisma.composant.findMany({ where: { machineId } });
|
||||||
|
expect(initialComponents).toHaveLength(1);
|
||||||
|
const initialComponentId = initialComponents[0].id;
|
||||||
|
|
||||||
|
const initialPieces = await prisma.piece.findMany({ where: { machineId } });
|
||||||
|
expect(initialPieces).toHaveLength(1);
|
||||||
|
const initialPieceId = initialPieces[0].id;
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await prisma.customFieldValue.count({ where: { composantId: initialComponentId } }),
|
||||||
|
).toBe(1);
|
||||||
|
expect(await prisma.customFieldValue.count({ where: { pieceId: initialPieceId } })).toBe(1);
|
||||||
|
|
||||||
|
await prisma.typeMachineComponentRequirement.deleteMany({
|
||||||
|
where: { typeMachineId: typeMachine.id },
|
||||||
|
});
|
||||||
|
await prisma.typeMachinePieceRequirement.deleteMany({
|
||||||
|
where: { typeMachineId: typeMachine.id },
|
||||||
|
});
|
||||||
|
|
||||||
|
const newComponentRequirement = await prisma.typeMachineComponentRequirement.create({
|
||||||
|
data: {
|
||||||
|
label: 'Modules mis à jour',
|
||||||
|
minCount: 2,
|
||||||
|
maxCount: 2,
|
||||||
|
required: true,
|
||||||
|
allowNewModels: true,
|
||||||
|
typeMachineId: typeMachine.id,
|
||||||
|
typeComposantId: typeComposant.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const newPieceRequirement = await prisma.typeMachinePieceRequirement.create({
|
||||||
|
data: {
|
||||||
|
label: 'Pièce mise à jour',
|
||||||
|
minCount: 1,
|
||||||
|
maxCount: 1,
|
||||||
|
required: true,
|
||||||
|
allowNewModels: false,
|
||||||
|
typeMachineId: typeMachine.id,
|
||||||
|
typePieceId: typePiece.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const reconfigureResponse = await request(app.getHttpServer())
|
||||||
|
.patch(`/machines/${machineId}/skeleton`)
|
||||||
|
.send({
|
||||||
|
componentSelections: [
|
||||||
|
{
|
||||||
|
requirementId: newComponentRequirement.id,
|
||||||
|
componentModelId: componentModelV2.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
requirementId: newComponentRequirement.id,
|
||||||
|
definition: {
|
||||||
|
name: 'Module personnalisé',
|
||||||
|
customFields: [{ name: 'Serial', defaultValue: 'SERIAL-CUSTOM' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pieceSelections: [
|
||||||
|
{
|
||||||
|
requirementId: newPieceRequirement.id,
|
||||||
|
pieceModelId: pieceModelV2.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(reconfigureResponse.body.composants).toHaveLength(2);
|
||||||
|
expect(reconfigureResponse.body.pieces).toHaveLength(1);
|
||||||
|
|
||||||
|
const componentsAfter = await prisma.composant.findMany({ where: { machineId } });
|
||||||
|
expect(componentsAfter).toHaveLength(2);
|
||||||
|
const componentIdsAfter = componentsAfter.map((component) => component.id);
|
||||||
|
expect(componentIdsAfter).not.toContain(initialComponentId);
|
||||||
|
componentsAfter.forEach((component) => {
|
||||||
|
expect(component.typeMachineComponentRequirementId).toBe(newComponentRequirement.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
const componentValueCount = await prisma.customFieldValue.count({
|
||||||
|
where: { composantId: { in: componentIdsAfter } },
|
||||||
|
});
|
||||||
|
expect(componentValueCount).toBe(2);
|
||||||
|
expect(
|
||||||
|
await prisma.customFieldValue.count({ where: { composantId: initialComponentId } }),
|
||||||
|
).toBe(0);
|
||||||
|
|
||||||
|
const piecesAfter = await prisma.piece.findMany({ where: { machineId } });
|
||||||
|
expect(piecesAfter).toHaveLength(1);
|
||||||
|
const [updatedPiece] = piecesAfter;
|
||||||
|
expect(updatedPiece.typeMachinePieceRequirementId).toBe(newPieceRequirement.id);
|
||||||
|
expect(updatedPiece.id).not.toBe(initialPieceId);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await prisma.customFieldValue.count({ where: { pieceId: updatedPiece.id } }),
|
||||||
|
).toBe(1);
|
||||||
|
expect(await prisma.customFieldValue.count({ where: { pieceId: initialPieceId } })).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user