fix: corrige les associations constructeurs
This commit is contained in:
@@ -14,7 +14,7 @@ export const COMPONENT_WITH_RELATIONS_INCLUDE = {
|
||||
customFields: true,
|
||||
},
|
||||
},
|
||||
constructeur: true,
|
||||
constructeurs: true,
|
||||
customFieldValues: {
|
||||
include: {
|
||||
customField: { select: CUSTOM_FIELD_SELECT },
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ComposantsService } from './composants.service';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { CreateComposantDto, UpdateComposantDto } from '../shared/dto/composant.dto';
|
||||
import {
|
||||
CreateComposantDto,
|
||||
UpdateComposantDto,
|
||||
} from '../shared/dto/composant.dto';
|
||||
|
||||
describe('ComposantsService', () => {
|
||||
let service: ComposantsService;
|
||||
@@ -45,7 +48,10 @@ describe('ComposantsService', () => {
|
||||
it('updates a component', async () => {
|
||||
const dto: UpdateComposantDto = { name: 'Updated' };
|
||||
|
||||
prisma.composant.update.mockResolvedValue({ id: 'comp-1', name: 'Updated' });
|
||||
prisma.composant.update.mockResolvedValue({
|
||||
id: 'comp-1',
|
||||
name: 'Updated',
|
||||
});
|
||||
|
||||
await service.update('comp-1', dto);
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@ import {
|
||||
export class ComposantsService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
private buildCreateInput(
|
||||
private async buildCreateInput(
|
||||
createComposantDto: CreateComposantDto,
|
||||
): Prisma.ComposantCreateInput {
|
||||
): Promise<Prisma.ComposantCreateInput> {
|
||||
const data: Prisma.ComposantCreateInput = {
|
||||
name: createComposantDto.name,
|
||||
reference: createComposantDto.reference ?? null,
|
||||
@@ -24,9 +24,14 @@ export class ComposantsService {
|
||||
createComposantDto.prix !== undefined ? createComposantDto.prix : null,
|
||||
};
|
||||
|
||||
if (createComposantDto.constructeurId) {
|
||||
data.constructeur = {
|
||||
connect: { id: createComposantDto.constructeurId },
|
||||
const constructeurIds = this.normalizeConstructeurIds(
|
||||
createComposantDto.constructeurIds,
|
||||
);
|
||||
const resolvedConstructeurIds =
|
||||
await this.resolveExistingConstructeurIds(constructeurIds);
|
||||
if (resolvedConstructeurIds.length) {
|
||||
data.constructeurs = {
|
||||
connect: resolvedConstructeurIds.map((id) => ({ id })),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,7 +51,7 @@ export class ComposantsService {
|
||||
async create(createComposantDto: CreateComposantDto) {
|
||||
try {
|
||||
const created = await this.prisma.composant.create({
|
||||
data: this.buildCreateInput(createComposantDto),
|
||||
data: await this.buildCreateInput(createComposantDto),
|
||||
include: COMPONENT_WITH_RELATIONS_INCLUDE,
|
||||
});
|
||||
|
||||
@@ -85,10 +90,15 @@ export class ComposantsService {
|
||||
data.prix = updateComposantDto.prix;
|
||||
}
|
||||
|
||||
if (updateComposantDto.constructeurId !== undefined) {
|
||||
data.constructeur = updateComposantDto.constructeurId
|
||||
? { connect: { id: updateComposantDto.constructeurId } }
|
||||
: { disconnect: true };
|
||||
if (updateComposantDto.constructeurIds !== undefined) {
|
||||
const constructeurIds = this.normalizeConstructeurIds(
|
||||
updateComposantDto.constructeurIds,
|
||||
);
|
||||
const resolvedConstructeurIds =
|
||||
await this.resolveExistingConstructeurIds(constructeurIds);
|
||||
data.constructeurs = {
|
||||
set: resolvedConstructeurIds.map((id) => ({ id })),
|
||||
};
|
||||
}
|
||||
|
||||
if (updateComposantDto.typeComposantId !== undefined) {
|
||||
@@ -170,6 +180,16 @@ export class ComposantsService {
|
||||
});
|
||||
}
|
||||
|
||||
private normalizeConstructeurIds(ids?: string[] | null): string[] {
|
||||
if (!Array.isArray(ids)) {
|
||||
return [];
|
||||
}
|
||||
const cleaned = ids
|
||||
.map((item) => (typeof item === 'string' ? item.trim() : ''))
|
||||
.filter((item) => item.length > 0);
|
||||
return Array.from(new Set(cleaned));
|
||||
}
|
||||
|
||||
private handlePrismaError(error: unknown): never {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
if (error.code === 'P2002' && this.isNameConstraint(error)) {
|
||||
@@ -190,4 +210,17 @@ export class ComposantsService {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private async resolveExistingConstructeurIds(
|
||||
ids: string[],
|
||||
): Promise<string[]> {
|
||||
if (!ids.length) {
|
||||
return [];
|
||||
}
|
||||
const existing = await this.prisma.constructeur.findMany({
|
||||
where: { id: { in: ids } },
|
||||
select: { id: true },
|
||||
});
|
||||
const existingIds = new Set(existing.map(({ id }) => id));
|
||||
return ids.filter((id) => existingIds.has(id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,11 +59,10 @@ describe('MachinesService', () => {
|
||||
prix: null,
|
||||
createdAt: timestamp,
|
||||
updatedAt: timestamp,
|
||||
constructeurId: null,
|
||||
constructeurs: [],
|
||||
typePieceId: null,
|
||||
documents: [],
|
||||
customFieldValues: [],
|
||||
constructeur: null,
|
||||
typePiece: null,
|
||||
},
|
||||
typeMachinePieceRequirement: null,
|
||||
@@ -81,7 +80,7 @@ describe('MachinesService', () => {
|
||||
id: 'piece-root',
|
||||
name: 'Root piece',
|
||||
},
|
||||
} as any;
|
||||
};
|
||||
|
||||
const componentChildLink = {
|
||||
id: 'component-child',
|
||||
@@ -101,11 +100,10 @@ describe('MachinesService', () => {
|
||||
prix: null,
|
||||
createdAt: timestamp,
|
||||
updatedAt: timestamp,
|
||||
constructeurId: null,
|
||||
constructeurs: [],
|
||||
typeComposantId: null,
|
||||
documents: [],
|
||||
customFieldValues: [],
|
||||
constructeur: null,
|
||||
typeComposant: null,
|
||||
},
|
||||
typeMachineComponentRequirement: null,
|
||||
@@ -125,7 +123,7 @@ describe('MachinesService', () => {
|
||||
name: 'Root component',
|
||||
},
|
||||
pieceLinks: [componentPieceLink],
|
||||
} as any;
|
||||
};
|
||||
|
||||
return {
|
||||
id: 'machine-1',
|
||||
@@ -135,11 +133,10 @@ describe('MachinesService', () => {
|
||||
createdAt: timestamp,
|
||||
updatedAt: timestamp,
|
||||
typeMachineId: null,
|
||||
constructeurId: null,
|
||||
constructeurs: [],
|
||||
siteId: 'site-1',
|
||||
site: null,
|
||||
typeMachine: null,
|
||||
constructeur: null,
|
||||
componentLinks: [componentRootLink, componentChildLink],
|
||||
pieceLinks: [rootPieceLink, componentPieceLink],
|
||||
customFieldValues: [],
|
||||
@@ -165,9 +162,7 @@ describe('MachinesService', () => {
|
||||
expect(rootLink.pieceLinks[0].parent?.overrides.name).toBe(
|
||||
'Root component override',
|
||||
);
|
||||
expect(rootLink.pieceLinks[0].originalPiece.name).toBe(
|
||||
'Piece component',
|
||||
);
|
||||
expect(rootLink.pieceLinks[0].originalPiece.name).toBe('Piece component');
|
||||
expect(rootLink.pieceLinks[0].piece.name).toBe('Component piece name');
|
||||
expect(rootLink.pieceLinks[0].overrides.reference).toBe('CP-001');
|
||||
expect(rootLink.overrides.name).toBe('Root component override');
|
||||
@@ -193,9 +188,7 @@ describe('MachinesService', () => {
|
||||
const root = result?.componentLinks[0];
|
||||
expect(root?.childLinks[0].parent?.id).toBe('component-root');
|
||||
expect(root?.childLinks[0].parent?.composantId).toBe('component-root');
|
||||
expect(root?.childLinks[0].originalComposant.name).toBe(
|
||||
'Child component',
|
||||
);
|
||||
expect(root?.childLinks[0].originalComposant.name).toBe('Child component');
|
||||
expect(root?.childLinks[0].composant.name).toBe('Child component');
|
||||
expect(root?.pieceLinks[0].parent?.id).toBe('component-root');
|
||||
expect(root?.pieceLinks[0].parent?.composantId).toBe('component-root');
|
||||
|
||||
@@ -49,7 +49,7 @@ const MACHINE_PIECE_LINK_INCLUDE: Prisma.MachinePieceLinkInclude = {
|
||||
customField: { select: CUSTOM_FIELD_SELECT },
|
||||
},
|
||||
},
|
||||
constructeur: true,
|
||||
constructeurs: true,
|
||||
typePiece: {
|
||||
include: {
|
||||
customFields: true,
|
||||
@@ -75,7 +75,7 @@ const buildComponentLinkInclude = (
|
||||
const include: Prisma.MachineComponentLinkInclude = {
|
||||
composant: {
|
||||
include: {
|
||||
constructeur: true,
|
||||
constructeurs: true,
|
||||
typeComposant: {
|
||||
include: {
|
||||
customFields: true,
|
||||
@@ -119,7 +119,7 @@ const MACHINE_DEFAULT_INCLUDE = {
|
||||
typeMachine: {
|
||||
include: TYPE_MACHINE_CONFIGURATION_INCLUDE,
|
||||
},
|
||||
constructeur: true,
|
||||
constructeurs: true,
|
||||
componentLinks: {
|
||||
include: MACHINE_COMPONENT_LINK_INCLUDE,
|
||||
},
|
||||
@@ -200,7 +200,7 @@ type ComponentWithType = Prisma.ComposantGetPayload<{
|
||||
}>;
|
||||
|
||||
type PieceWithType = Prisma.PieceGetPayload<{
|
||||
include: { typePiece: true };
|
||||
include: { typePiece: true; constructeurs: true };
|
||||
}>;
|
||||
|
||||
type CreatedComponentLinkInfo = {
|
||||
@@ -246,9 +246,7 @@ type PendingPieceLink = {
|
||||
|
||||
@Injectable()
|
||||
export class MachinesService {
|
||||
constructor(
|
||||
private prisma: PrismaService,
|
||||
) {}
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
private toLinkOverride(source: {
|
||||
nameOverride?: string | null;
|
||||
@@ -273,7 +271,7 @@ export class MachinesService {
|
||||
const prix =
|
||||
overrides.prix !== null && overrides.prix !== undefined
|
||||
? overrides.prix
|
||||
: composant.prix ?? null;
|
||||
: (composant.prix ?? null);
|
||||
|
||||
return {
|
||||
...composant,
|
||||
@@ -294,7 +292,7 @@ export class MachinesService {
|
||||
const prix =
|
||||
overrides.prix !== null && overrides.prix !== undefined
|
||||
? overrides.prix
|
||||
: piece.prix ?? null;
|
||||
: (piece.prix ?? null);
|
||||
|
||||
return {
|
||||
...piece,
|
||||
@@ -346,7 +344,10 @@ export class MachinesService {
|
||||
: [];
|
||||
|
||||
const hydratedLink: HydratedComponentLink = {
|
||||
...(rest as Omit<MachineComponentLinkWithRelations, 'pieceLinks' | 'composant'>),
|
||||
...(rest as Omit<
|
||||
MachineComponentLinkWithRelations,
|
||||
'pieceLinks' | 'composant'
|
||||
>),
|
||||
parent: parentSummary,
|
||||
overrides,
|
||||
originalComposant: composant,
|
||||
@@ -375,26 +376,27 @@ export class MachinesService {
|
||||
sousComposants: [] as HierarchicalComponentLink[],
|
||||
}));
|
||||
|
||||
const hierarchy = buildComponentHierarchy<HierarchicalComponentLink>(decorated);
|
||||
const hierarchy =
|
||||
buildComponentHierarchy<HierarchicalComponentLink>(decorated);
|
||||
|
||||
return hierarchy.map((link) => this.convertComponentLinkNode(link));
|
||||
}
|
||||
|
||||
private hydrateMachine(
|
||||
machine: MachineWithRelations | null,
|
||||
): (MachineWithRelations & {
|
||||
componentLinks: HydratedComponentLink[];
|
||||
pieceLinks: HydratedPieceLink[];
|
||||
}) | null {
|
||||
private hydrateMachine(machine: MachineWithRelations | null):
|
||||
| (MachineWithRelations & {
|
||||
componentLinks: HydratedComponentLink[];
|
||||
pieceLinks: HydratedPieceLink[];
|
||||
})
|
||||
| null {
|
||||
if (!machine) {
|
||||
return machine;
|
||||
}
|
||||
|
||||
const componentLinks = this.hydrateComponentLinks(
|
||||
(machine.componentLinks ?? []) as MachineComponentLinkWithRelations[],
|
||||
machine.componentLinks ?? [],
|
||||
);
|
||||
|
||||
const rootPieceLinks = ((machine.pieceLinks ?? []) as MachinePieceLinkWithRelations[])
|
||||
const rootPieceLinks = (machine.pieceLinks ?? [])
|
||||
.filter((link) => !link.parentLinkId)
|
||||
.map((link) => this.hydratePieceLink(link));
|
||||
|
||||
@@ -533,7 +535,8 @@ export class MachinesService {
|
||||
}
|
||||
|
||||
for (const requirement of componentRequirements) {
|
||||
const linksForRequirement = componentLinksByRequirement.get(requirement.id) ?? [];
|
||||
const linksForRequirement =
|
||||
componentLinksByRequirement.get(requirement.id) ?? [];
|
||||
const min = requirement.minCount ?? (requirement.required ? 1 : 0);
|
||||
const max = requirement.maxCount ?? undefined;
|
||||
|
||||
@@ -559,7 +562,8 @@ export class MachinesService {
|
||||
}
|
||||
|
||||
for (const requirement of pieceRequirements) {
|
||||
const linksForRequirement = pieceLinksByRequirement.get(requirement.id) ?? [];
|
||||
const linksForRequirement =
|
||||
pieceLinksByRequirement.get(requirement.id) ?? [];
|
||||
const min = requirement.minCount ?? (requirement.required ? 1 : 0);
|
||||
const max = requirement.maxCount ?? undefined;
|
||||
|
||||
@@ -646,6 +650,30 @@ export class MachinesService {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private normalizeConstructeurIds(ids?: string[] | null): string[] {
|
||||
if (!Array.isArray(ids)) {
|
||||
return [];
|
||||
}
|
||||
const cleaned = ids
|
||||
.map((item) => (typeof item === 'string' ? item.trim() : ''))
|
||||
.filter((item) => item.length > 0);
|
||||
return Array.from(new Set(cleaned));
|
||||
}
|
||||
|
||||
private async resolveExistingConstructeurIds(
|
||||
ids: string[],
|
||||
): Promise<string[]> {
|
||||
if (!ids.length) {
|
||||
return [];
|
||||
}
|
||||
const existing = await this.prisma.constructeur.findMany({
|
||||
where: { id: { in: ids } },
|
||||
select: { id: true },
|
||||
});
|
||||
const existingIds = new Set(existing.map(({ id }) => id));
|
||||
return ids.filter((id) => existingIds.has(id));
|
||||
}
|
||||
|
||||
private async resolveConstructeurId(
|
||||
input: unknown,
|
||||
): Promise<string | undefined> {
|
||||
@@ -692,9 +720,12 @@ export class MachinesService {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private resolveLinkIdentifier(link: { id?: string; linkId?: string }): string | undefined {
|
||||
private resolveLinkIdentifier(link: {
|
||||
id?: string;
|
||||
linkId?: string;
|
||||
}): string | undefined {
|
||||
const candidate =
|
||||
(typeof link.id === 'string' && link.id.trim())
|
||||
typeof link.id === 'string' && link.id.trim()
|
||||
? link.id.trim()
|
||||
: typeof link.linkId === 'string'
|
||||
? link.linkId.trim()
|
||||
@@ -703,9 +734,7 @@ export class MachinesService {
|
||||
return candidate && candidate.length > 0 ? candidate : undefined;
|
||||
}
|
||||
|
||||
private buildLinkOverrideMutation(
|
||||
overrides?: Record<string, unknown>,
|
||||
): {
|
||||
private buildLinkOverrideMutation(overrides?: Record<string, unknown>): {
|
||||
nameOverride?: string | null;
|
||||
referenceOverride?: string | null;
|
||||
prixOverride?: Prisma.Decimal | null;
|
||||
@@ -725,9 +754,7 @@ export class MachinesService {
|
||||
Object.prototype.hasOwnProperty.call(container, 'name') ||
|
||||
Object.prototype.hasOwnProperty.call(container, 'nom')
|
||||
) {
|
||||
const value = this.extractString(
|
||||
container.name ?? container.nom,
|
||||
);
|
||||
const value = this.extractString(container.name ?? container.nom);
|
||||
mutation.nameOverride = value ?? null;
|
||||
}
|
||||
|
||||
@@ -755,7 +782,9 @@ export class MachinesService {
|
||||
container.priceOverride;
|
||||
const normalized = this.normalizePrice(rawPrice);
|
||||
if (normalized === undefined) {
|
||||
throw new Error('La valeur de prix fournie dans les overrides est invalide.');
|
||||
throw new Error(
|
||||
'La valeur de prix fournie dans les overrides est invalide.',
|
||||
);
|
||||
}
|
||||
mutation.prixOverride =
|
||||
normalized === null ? null : new Prisma.Decimal(normalized);
|
||||
@@ -800,7 +829,9 @@ export class MachinesService {
|
||||
return [...direct, ...legacy];
|
||||
}
|
||||
|
||||
private extractStructurePieces(structure: Record<string, unknown> | null | undefined) {
|
||||
private extractStructurePieces(
|
||||
structure: Record<string, unknown> | null | undefined,
|
||||
) {
|
||||
if (!structure || typeof structure !== 'object') {
|
||||
return [];
|
||||
}
|
||||
@@ -815,7 +846,9 @@ export class MachinesService {
|
||||
return [...pieces, ...legacy];
|
||||
}
|
||||
|
||||
private extractStructureAlias(entry: Record<string, unknown> | null | undefined) {
|
||||
private extractStructureAlias(
|
||||
entry: Record<string, unknown> | null | undefined,
|
||||
) {
|
||||
if (!entry || typeof entry !== 'object') {
|
||||
return null;
|
||||
}
|
||||
@@ -848,7 +881,9 @@ export class MachinesService {
|
||||
return this.extractString(definition.reference) ?? null;
|
||||
}
|
||||
|
||||
private extractStructurePieceName(entry: Record<string, unknown> | null | undefined) {
|
||||
private extractStructurePieceName(
|
||||
entry: Record<string, unknown> | null | undefined,
|
||||
) {
|
||||
if (!entry || typeof entry !== 'object') {
|
||||
return null;
|
||||
}
|
||||
@@ -959,7 +994,7 @@ export class MachinesService {
|
||||
where: { id: pieceId },
|
||||
include: {
|
||||
typePiece: true,
|
||||
constructeur: true,
|
||||
constructeurs: true,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -994,9 +1029,7 @@ export class MachinesService {
|
||||
for (const rawPiece of structurePieces) {
|
||||
const pieceEntry = this.ensurePlainObject(rawPiece);
|
||||
const selectedPieceId = this.extractString(
|
||||
pieceEntry.selectedPieceId ??
|
||||
pieceEntry.pieceId ??
|
||||
pieceEntry.id,
|
||||
pieceEntry.selectedPieceId ?? pieceEntry.pieceId ?? pieceEntry.id,
|
||||
);
|
||||
|
||||
if (!selectedPieceId) {
|
||||
@@ -1045,7 +1078,8 @@ export class MachinesService {
|
||||
data: {
|
||||
parentLinkId: linkInfo.id,
|
||||
nameOverride:
|
||||
pieceNameOverride !== null && pieceNameOverride !== undefined
|
||||
pieceNameOverride !== null &&
|
||||
pieceNameOverride !== undefined
|
||||
? pieceNameOverride
|
||||
: existingPieceLink.nameOverride,
|
||||
referenceOverride:
|
||||
@@ -1069,7 +1103,8 @@ export class MachinesService {
|
||||
where: { id: existingPieceLink.id },
|
||||
data: {
|
||||
nameOverride:
|
||||
pieceNameOverride !== null && pieceNameOverride !== undefined
|
||||
pieceNameOverride !== null &&
|
||||
pieceNameOverride !== undefined
|
||||
? pieceNameOverride
|
||||
: existingPieceLink.nameOverride,
|
||||
referenceOverride:
|
||||
@@ -1116,9 +1151,7 @@ export class MachinesService {
|
||||
for (const rawEntry of subcomponents) {
|
||||
const entry = this.ensurePlainObject(rawEntry);
|
||||
const selectedComponentId = this.extractString(
|
||||
entry.selectedComponentId ??
|
||||
entry.componentId ??
|
||||
entry.composantId,
|
||||
entry.selectedComponentId ?? entry.componentId ?? entry.composantId,
|
||||
);
|
||||
|
||||
if (!selectedComponentId) {
|
||||
@@ -1299,7 +1332,8 @@ export class MachinesService {
|
||||
createData.nameOverride = entry.overrideMutation.nameOverride;
|
||||
}
|
||||
if (entry.overrideMutation?.referenceOverride !== undefined) {
|
||||
createData.referenceOverride = entry.overrideMutation.referenceOverride;
|
||||
createData.referenceOverride =
|
||||
entry.overrideMutation.referenceOverride;
|
||||
}
|
||||
if (entry.overrideMutation?.prixOverride !== undefined) {
|
||||
createData.prixOverride = entry.overrideMutation.prixOverride;
|
||||
@@ -1469,7 +1503,7 @@ export class MachinesService {
|
||||
|
||||
const pieces = await prisma.piece.findMany({
|
||||
where: { id: { in: Array.from(pieceIds) } },
|
||||
include: { typePiece: true },
|
||||
include: { typePiece: true, constructeurs: true },
|
||||
});
|
||||
const pieceMap = new Map<string, PieceWithType>(
|
||||
pieces.map((piece) => [piece.id, piece]),
|
||||
@@ -1567,7 +1601,8 @@ export class MachinesService {
|
||||
|
||||
if (parentComponentRequirementId) {
|
||||
const matches =
|
||||
componentLinkIndex.byRequirementId.get(parentComponentRequirementId) ?? [];
|
||||
componentLinkIndex.byRequirementId.get(parentComponentRequirementId) ??
|
||||
[];
|
||||
if (matches.length === 1) {
|
||||
return matches[0].id;
|
||||
}
|
||||
@@ -1658,6 +1693,7 @@ export class MachinesService {
|
||||
const {
|
||||
componentLinks = [],
|
||||
pieceLinks = [],
|
||||
constructeurIds,
|
||||
...machineData
|
||||
} = createMachineDto;
|
||||
|
||||
@@ -1667,27 +1703,38 @@ export class MachinesService {
|
||||
);
|
||||
}
|
||||
|
||||
const normalizedConstructeurIds =
|
||||
this.normalizeConstructeurIds(constructeurIds);
|
||||
|
||||
const resolvedConstructeurIds = await this.resolveExistingConstructeurIds(
|
||||
normalizedConstructeurIds,
|
||||
);
|
||||
|
||||
const typeMachine = await this.getTypeMachineConfiguration(
|
||||
machineData.typeMachineId,
|
||||
);
|
||||
|
||||
const {
|
||||
componentRequirementMap,
|
||||
pieceRequirementMap,
|
||||
} = this.buildConfigurationContext(
|
||||
typeMachine,
|
||||
componentLinks,
|
||||
pieceLinks,
|
||||
);
|
||||
const { componentRequirementMap, pieceRequirementMap } =
|
||||
this.buildConfigurationContext(typeMachine, componentLinks, pieceLinks);
|
||||
|
||||
let machine: Awaited<ReturnType<typeof this.prisma.machine.create>>;
|
||||
try {
|
||||
const createData: any = {
|
||||
...machineData,
|
||||
};
|
||||
|
||||
if (resolvedConstructeurIds.length) {
|
||||
createData.constructeurs = {
|
||||
connect: resolvedConstructeurIds.map((id) => ({ id })),
|
||||
};
|
||||
}
|
||||
|
||||
machine = await this.prisma.machine.create({
|
||||
data: machineData,
|
||||
data: createData,
|
||||
include: {
|
||||
site: true,
|
||||
typeMachine: true,
|
||||
constructeur: true,
|
||||
constructeurs: true,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -1776,11 +1823,7 @@ export class MachinesService {
|
||||
const typeMachine = machine.typeMachine as TypeMachineConfiguration;
|
||||
|
||||
const { componentRequirementMap, pieceRequirementMap } =
|
||||
this.buildConfigurationContext(
|
||||
typeMachine,
|
||||
componentLinks,
|
||||
pieceLinks,
|
||||
);
|
||||
this.buildConfigurationContext(typeMachine, componentLinks, pieceLinks);
|
||||
|
||||
await this.prisma.$transaction(async (tx) => {
|
||||
await tx.machinePieceLink.deleteMany({ where: { machineId: id } });
|
||||
@@ -1811,7 +1854,7 @@ export class MachinesService {
|
||||
}
|
||||
|
||||
async update(id: string, updateMachineDto: UpdateMachineDto) {
|
||||
const { name, reference, constructeurId, prix, typeMachineId } =
|
||||
const { name, reference, constructeurIds, prix, typeMachineId } =
|
||||
updateMachineDto;
|
||||
|
||||
const data: Prisma.MachineUpdateInput = {};
|
||||
@@ -1824,11 +1867,15 @@ export class MachinesService {
|
||||
data.reference = reference;
|
||||
}
|
||||
|
||||
if (constructeurId !== undefined) {
|
||||
const resolvedConstructeurId = this.extractString(constructeurId);
|
||||
data.constructeur = resolvedConstructeurId
|
||||
? { connect: { id: resolvedConstructeurId } }
|
||||
: { disconnect: true };
|
||||
if (constructeurIds !== undefined) {
|
||||
const normalizedConstructeurIds =
|
||||
this.normalizeConstructeurIds(constructeurIds);
|
||||
const resolvedConstructeurIds = await this.resolveExistingConstructeurIds(
|
||||
normalizedConstructeurIds,
|
||||
);
|
||||
data.constructeurs = {
|
||||
set: resolvedConstructeurIds.map((id) => ({ id })),
|
||||
};
|
||||
}
|
||||
|
||||
if (prix !== undefined) {
|
||||
@@ -1836,7 +1883,8 @@ export class MachinesService {
|
||||
if (normalizedPrice === undefined) {
|
||||
throw new Error('Le prix fourni est invalide.');
|
||||
}
|
||||
data.prix = normalizedPrice === null ? null : new Prisma.Decimal(normalizedPrice);
|
||||
data.prix =
|
||||
normalizedPrice === null ? null : new Prisma.Decimal(normalizedPrice);
|
||||
}
|
||||
|
||||
if (typeMachineId !== undefined) {
|
||||
|
||||
@@ -218,9 +218,7 @@ export class ModelTypeService {
|
||||
}
|
||||
|
||||
if (this.isUniqueNameConstraint(error)) {
|
||||
throw new ConflictException(
|
||||
'Une catégorie avec ce nom existe déjà.',
|
||||
);
|
||||
throw new ConflictException('Une catégorie avec ce nom existe déjà.');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,10 +27,7 @@ describe('PiecesService', () => {
|
||||
};
|
||||
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
PiecesService,
|
||||
{ provide: PrismaService, useValue: prisma },
|
||||
],
|
||||
providers: [PiecesService, { provide: PrismaService, useValue: prisma }],
|
||||
}).compile();
|
||||
|
||||
service = module.get<PiecesService>(PiecesService);
|
||||
@@ -43,7 +40,10 @@ describe('PiecesService', () => {
|
||||
};
|
||||
|
||||
prisma.piece.create.mockResolvedValue({ id: 'piece-1', name: dto.name });
|
||||
prisma.piece.findUnique.mockResolvedValue({ id: 'piece-1', name: dto.name });
|
||||
prisma.piece.findUnique.mockResolvedValue({
|
||||
id: 'piece-1',
|
||||
name: dto.name,
|
||||
});
|
||||
prisma.customField.findMany.mockResolvedValue([]);
|
||||
prisma.customFieldValue.findMany.mockResolvedValue([]);
|
||||
|
||||
@@ -56,8 +56,14 @@ describe('PiecesService', () => {
|
||||
it('updates a piece', async () => {
|
||||
const dto: UpdatePieceDto = { name: 'Updated piece' };
|
||||
|
||||
prisma.piece.update.mockResolvedValue({ id: 'piece-1', name: 'Updated piece' });
|
||||
prisma.piece.findUnique.mockResolvedValue({ id: 'piece-1', name: 'Updated piece' });
|
||||
prisma.piece.update.mockResolvedValue({
|
||||
id: 'piece-1',
|
||||
name: 'Updated piece',
|
||||
});
|
||||
prisma.piece.findUnique.mockResolvedValue({
|
||||
id: 'piece-1',
|
||||
name: 'Updated piece',
|
||||
});
|
||||
prisma.customField.findMany.mockResolvedValue([]);
|
||||
prisma.customFieldValue.findMany.mockResolvedValue([]);
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const PIECE_WITH_RELATIONS_INCLUDE = {
|
||||
pieceCustomFields: true,
|
||||
},
|
||||
},
|
||||
constructeur: true,
|
||||
constructeurs: true,
|
||||
documents: true,
|
||||
customFieldValues: {
|
||||
include: {
|
||||
@@ -31,16 +31,23 @@ const PIECE_WITH_RELATIONS_INCLUDE = {
|
||||
export class PiecesService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
private buildCreateInput(createPieceDto: CreatePieceDto): Prisma.PieceCreateInput {
|
||||
private async buildCreateInput(
|
||||
createPieceDto: CreatePieceDto,
|
||||
): Promise<Prisma.PieceCreateInput> {
|
||||
const data: Prisma.PieceCreateInput = {
|
||||
name: createPieceDto.name,
|
||||
reference: createPieceDto.reference ?? null,
|
||||
prix: createPieceDto.prix !== undefined ? createPieceDto.prix : null,
|
||||
};
|
||||
|
||||
if (createPieceDto.constructeurId) {
|
||||
data.constructeur = {
|
||||
connect: { id: createPieceDto.constructeurId },
|
||||
const constructeurIds = this.normalizeConstructeurIds(
|
||||
createPieceDto.constructeurIds,
|
||||
);
|
||||
const resolvedConstructeurIds =
|
||||
await this.resolveExistingConstructeurIds(constructeurIds);
|
||||
if (resolvedConstructeurIds.length) {
|
||||
data.constructeurs = {
|
||||
connect: resolvedConstructeurIds.map((id) => ({ id })),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -56,7 +63,7 @@ export class PiecesService {
|
||||
async create(createPieceDto: CreatePieceDto) {
|
||||
try {
|
||||
const created = await this.prisma.piece.create({
|
||||
data: this.buildCreateInput(createPieceDto),
|
||||
data: await this.buildCreateInput(createPieceDto),
|
||||
include: PIECE_WITH_RELATIONS_INCLUDE,
|
||||
});
|
||||
|
||||
@@ -103,10 +110,15 @@ export class PiecesService {
|
||||
data.prix = updatePieceDto.prix;
|
||||
}
|
||||
|
||||
if (updatePieceDto.constructeurId !== undefined) {
|
||||
data.constructeur = updatePieceDto.constructeurId
|
||||
? { connect: { id: updatePieceDto.constructeurId } }
|
||||
: { disconnect: true };
|
||||
if (updatePieceDto.constructeurIds !== undefined) {
|
||||
const constructeurIds = this.normalizeConstructeurIds(
|
||||
updatePieceDto.constructeurIds,
|
||||
);
|
||||
const resolvedConstructeurIds =
|
||||
await this.resolveExistingConstructeurIds(constructeurIds);
|
||||
data.constructeurs = {
|
||||
set: resolvedConstructeurIds.map((id) => ({ id })),
|
||||
};
|
||||
}
|
||||
|
||||
if (updatePieceDto.typePieceId !== undefined) {
|
||||
@@ -224,6 +236,30 @@ export class PiecesService {
|
||||
);
|
||||
}
|
||||
|
||||
private normalizeConstructeurIds(ids?: string[] | null): string[] {
|
||||
if (!Array.isArray(ids)) {
|
||||
return [];
|
||||
}
|
||||
const cleaned = ids
|
||||
.map((item) => (typeof item === 'string' ? item.trim() : ''))
|
||||
.filter((item) => item.length > 0);
|
||||
return Array.from(new Set(cleaned));
|
||||
}
|
||||
|
||||
private async resolveExistingConstructeurIds(
|
||||
ids: string[],
|
||||
): Promise<string[]> {
|
||||
if (!ids.length) {
|
||||
return [];
|
||||
}
|
||||
const existing = await this.prisma.constructeur.findMany({
|
||||
where: { id: { in: ids } },
|
||||
select: { id: true },
|
||||
});
|
||||
const existingIds = new Set(existing.map(({ id }) => id));
|
||||
return ids.filter((id) => existingIds.has(id));
|
||||
}
|
||||
|
||||
private parsePieceSkeleton(value: unknown): PieceModelStructure | null {
|
||||
if (!value) {
|
||||
return null;
|
||||
@@ -430,4 +466,6 @@ type PieceTypeWithSkeleton = Prisma.ModelTypeGetPayload<{
|
||||
include: { pieceCustomFields: true };
|
||||
}>;
|
||||
|
||||
type PieceCustomFieldEntry = NonNullable<PieceModelStructure['customFields']>[number];
|
||||
type PieceCustomFieldEntry = NonNullable<
|
||||
PieceModelStructure['customFields']
|
||||
>[number];
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { IsString, IsOptional, IsNumber, IsObject } from 'class-validator';
|
||||
import {
|
||||
IsString,
|
||||
IsOptional,
|
||||
IsNumber,
|
||||
IsObject,
|
||||
IsArray,
|
||||
} from 'class-validator';
|
||||
import { Transform } from 'class-transformer';
|
||||
|
||||
export class CreateComposantDto {
|
||||
@@ -18,8 +24,10 @@ export class CreateComposantDto {
|
||||
reference?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
constructeurId?: string;
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
constructeurIds?: string[];
|
||||
|
||||
@IsOptional()
|
||||
@Transform(({ value }) => (value === '' ? null : value))
|
||||
@@ -49,8 +57,9 @@ export class UpdateComposantDto {
|
||||
reference?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
constructeurId?: string;
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
constructeurIds?: string[];
|
||||
|
||||
@IsOptional()
|
||||
@Transform(({ value }) => (value === '' ? null : value))
|
||||
|
||||
@@ -154,8 +154,9 @@ export class CreateMachineDto {
|
||||
reference?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
constructeurId?: string;
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
constructeurIds?: string[];
|
||||
|
||||
@IsOptional()
|
||||
@IsDecimal()
|
||||
@@ -188,8 +189,9 @@ export class UpdateMachineDto {
|
||||
reference?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
constructeurId?: string;
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
constructeurIds?: string[];
|
||||
|
||||
@IsOptional()
|
||||
@IsDecimal()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IsString, IsOptional, IsNumber } from 'class-validator';
|
||||
import { IsString, IsOptional, IsNumber, IsArray } from 'class-validator';
|
||||
import { Transform } from 'class-transformer';
|
||||
|
||||
export class CreatePieceDto {
|
||||
@@ -18,8 +18,10 @@ export class CreatePieceDto {
|
||||
reference?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
constructeurId?: string;
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
constructeurIds?: string[];
|
||||
|
||||
@IsOptional()
|
||||
@Transform(({ value }) => (value === '' ? null : value))
|
||||
@@ -45,8 +47,10 @@ export class UpdatePieceDto {
|
||||
reference?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
constructeurId?: string;
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
constructeurIds?: string[];
|
||||
|
||||
@IsOptional()
|
||||
@Transform(({ value }) => (value === '' ? null : value))
|
||||
|
||||
Reference in New Issue
Block a user