Refactor types service into modular services and repositories

This commit is contained in:
MatthieuTD
2025-09-25 16:15:37 +02:00
parent 2ce164784f
commit b10a6baf47
21 changed files with 1533 additions and 589 deletions

View File

@@ -0,0 +1,7 @@
export const CUSTOM_FIELD_SELECT = {
id: true,
name: true,
type: true,
required: true,
options: true,
} as const;

View File

@@ -0,0 +1,55 @@
import { ModelTypeMapper } from './model-type.mapper';
describe('ModelTypeMapper', () => {
it('should map component create input', () => {
const dto = {
name: 'Comp',
description: 'Desc',
customFields: [{ name: 'Field', type: 'string', required: false, options: [] }],
} as any;
const input = ModelTypeMapper.toComponentCreateInput(dto, 'code');
expect(input).toMatchObject({
name: 'Comp',
code: 'code',
description: 'Desc',
notes: 'Desc',
});
expect(input.customFields?.create?.[0]).toMatchObject({ name: 'Field' });
});
it('should map piece model type to DTO shape', () => {
const mapped = ModelTypeMapper.mapPieceModelType({
id: '1',
name: 'Piece',
pieceCustomFields: [{ id: 'cf' }],
pieceModels: [{ id: 'model' }],
pieceRequirements: [{ id: 'req' }],
pieces: [{ id: 'piece' }],
});
expect(mapped).toMatchObject({
id: '1',
customFields: [{ id: 'cf' }],
models: [{ id: 'model' }],
pieceRequirements: [{ id: 'req' }],
pieces: [{ id: 'piece' }],
});
});
it('should map piece update input', () => {
const input = ModelTypeMapper.toPieceUpdateInput({
name: 'New',
description: 'D',
customFields: [],
} as any);
expect(input).toMatchObject({
name: 'New',
description: 'D',
notes: 'D',
});
expect(input.pieceCustomFields).toBeUndefined();
});
});

View File

@@ -0,0 +1,170 @@
import { Prisma } from '@prisma/client';
import {
CreateTypeComposantDto,
CreateTypePieceDto,
UpdateTypeComposantDto,
UpdateTypePieceDto,
} from '../../shared/dto/type.dto';
import { CUSTOM_FIELD_SELECT } from '../constants/custom-field.constant';
export const COMPONENT_TYPE_INCLUDE: Prisma.ModelTypeInclude = {
customFields: { select: CUSTOM_FIELD_SELECT },
composants: true,
models: true,
};
export const PIECE_TYPE_INCLUDE: Prisma.ModelTypeInclude = {
pieceCustomFields: { select: CUSTOM_FIELD_SELECT },
pieceModels: true,
pieceRequirements: true,
pieces: true,
};
type ModelTypeCreateWithoutCategory = Omit<
Prisma.ModelTypeCreateInput,
'category'
>;
export class ModelTypeMapper {
static toComponentCreateInput(
dto: CreateTypeComposantDto,
code: string,
): ModelTypeCreateWithoutCategory {
const { customFields, description, name } = dto;
return {
name,
code,
description: description ?? null,
notes: description ?? null,
customFields: customFields
? {
create: customFields.map((field) => ({
name: field.name,
type: field.type,
required: field.required ?? false,
options: field.options,
})),
}
: undefined,
};
}
static toComponentUpdateInput(
dto: UpdateTypeComposantDto,
): Prisma.ModelTypeUpdateInput {
const { customFields, description, name } = dto;
const data: Prisma.ModelTypeUpdateInput = {};
if (name !== undefined) {
data.name = name;
}
if (description !== undefined) {
data.description = description;
data.notes = description;
}
if (customFields !== undefined) {
data.customFields = undefined;
}
return data;
}
static toPieceCreateInput(
dto: CreateTypePieceDto,
code: string,
): ModelTypeCreateWithoutCategory {
const { customFields, description, name } = dto;
return {
name,
code,
description: description ?? null,
notes: description ?? null,
pieceCustomFields: customFields
? {
create: customFields.map((field) => ({
name: field.name,
type: field.type,
required: field.required ?? false,
options: field.options,
})),
}
: undefined,
};
}
static toPieceUpdateInput(dto: UpdateTypePieceDto): Prisma.ModelTypeUpdateInput {
const { customFields, description, name } = dto;
const data: Prisma.ModelTypeUpdateInput = {};
if (name !== undefined) {
data.name = name;
}
if (description !== undefined) {
data.description = description;
data.notes = description;
}
if (customFields !== undefined) {
data.pieceCustomFields = undefined;
}
return data;
}
static mapPieceModelType(modelType: any) {
if (!modelType) {
return null;
}
const {
pieceCustomFields,
pieceModels,
pieceRequirements,
pieces,
...rest
} = modelType;
return {
...rest,
customFields: pieceCustomFields ?? [],
models: pieceModels ?? [],
pieceRequirements: pieceRequirements ?? [],
pieces: pieces ?? [],
};
}
static mapComponentCustomFieldInputs(
fields?: CreateTypeComposantDto['customFields'],
) {
if (!fields || fields.length === 0) {
return [];
}
return fields.map((field) => ({
name: field.name,
type: field.type,
required: field.required ?? false,
options: field.options,
}));
}
static mapPieceCustomFieldInputs(
fields?: CreateTypePieceDto['customFields'],
) {
if (!fields || fields.length === 0) {
return [];
}
return fields.map((field) => ({
name: field.name,
type: field.type,
required: field.required ?? false,
options: field.options,
}));
}
}

View File

@@ -0,0 +1,83 @@
import { TypeMachineMapper } from './type-machine.mapper';
const baseDto = {
name: 'Machine A',
description: 'Desc',
customFields: [
{ name: 'Field', type: 'string', required: true, options: ['a'] },
],
componentRequirements: [
{
label: 'Comp',
minCount: 2,
maxCount: 4,
required: true,
allowNewModels: false,
typeComposantId: 'comp-id',
},
],
pieceRequirements: [
{
label: 'Piece',
minCount: 0,
maxCount: 2,
required: false,
allowNewModels: true,
typePieceId: 'piece-id',
},
],
};
describe('TypeMachineMapper', () => {
it('should map create input with nested relations', () => {
const input = TypeMachineMapper.toCreateInput(baseDto as any);
expect(input.customFields?.create).toHaveLength(1);
expect(input.componentRequirements?.create?.[0]).toMatchObject({
label: 'Comp',
minCount: 2,
maxCount: 4,
required: true,
allowNewModels: false,
});
expect(input.pieceRequirements?.create?.[0]).toMatchObject({
label: 'Piece',
minCount: 0,
maxCount: 2,
required: false,
allowNewModels: true,
});
});
it('should map custom field inputs for create many', () => {
const result = TypeMachineMapper.mapCustomFieldInputs(baseDto.customFields as any);
expect(result).toEqual([
{
name: 'Field',
type: 'string',
required: true,
options: ['a'],
},
]);
});
it('should map requirements for bulk insert', () => {
const component = TypeMachineMapper.mapComponentRequirementInputs(
baseDto.componentRequirements as any,
);
const piece = TypeMachineMapper.mapPieceRequirementInputs(
baseDto.pieceRequirements as any,
);
expect(component[0]).toMatchObject({
typeComposantId: 'comp-id',
minCount: 2,
maxCount: 4,
});
expect(piece[0]).toMatchObject({
typePieceId: 'piece-id',
minCount: 0,
maxCount: 2,
});
});
});

View File

@@ -0,0 +1,188 @@
import { Prisma } from '@prisma/client';
import {
CreateTypeMachineDto,
UpdateTypeMachineDto,
} from '../../shared/dto/type.dto';
import { CUSTOM_FIELD_SELECT } from '../constants/custom-field.constant';
type RequirementDto = {
label?: string | null;
minCount?: number | null;
maxCount?: number | null;
required?: boolean | null;
allowNewModels?: boolean | null;
typeComposantId?: string;
typePieceId?: string;
};
export const TYPE_MACHINE_DEFAULT_INCLUDE: Prisma.TypeMachineInclude = {
customFields: { select: CUSTOM_FIELD_SELECT },
componentRequirements: {
include: { typeComposant: true },
},
pieceRequirements: {
include: { typePiece: true },
},
};
export const TYPE_MACHINE_WITH_MACHINES_INCLUDE: Prisma.TypeMachineInclude = {
...TYPE_MACHINE_DEFAULT_INCLUDE,
machines: true,
};
export class TypeMachineMapper {
static toCreateInput(
dto: CreateTypeMachineDto,
): Prisma.TypeMachineCreateInput {
const { customFields, componentRequirements, pieceRequirements, ...data } =
dto;
return {
...data,
customFields: this.mapCustomFields(customFields),
componentRequirements: this.mapComponentRequirements(componentRequirements),
pieceRequirements: this.mapPieceRequirements(pieceRequirements),
};
}
static toUpdateData(dto: UpdateTypeMachineDto): Prisma.TypeMachineUpdateInput {
const { customFields, componentRequirements, pieceRequirements, ...data } =
dto;
const payload: Prisma.TypeMachineUpdateInput = { ...data };
if (customFields !== undefined) {
payload.customFields = undefined;
}
if (componentRequirements !== undefined) {
payload.componentRequirements = undefined;
}
if (pieceRequirements !== undefined) {
payload.pieceRequirements = undefined;
}
return payload;
}
static mapCustomFields(
fields?: CreateTypeMachineDto['customFields'],
): Prisma.CustomFieldCreateNestedManyWithoutTypeMachineInput | undefined {
if (!fields || fields.length === 0) {
return undefined;
}
return {
create: fields.map((field) => ({
name: field.name,
type: field.type,
required: field.required ?? false,
options: field.options,
})),
};
}
static mapCustomFieldInputs(
fields?: CreateTypeMachineDto['customFields'],
) {
if (!fields || fields.length === 0) {
return [];
}
return fields.map((field) => ({
name: field.name,
type: field.type,
required: field.required ?? false,
options: field.options,
}));
}
static mapComponentRequirements(
requirements?: RequirementDto[] | null,
): Prisma.TypeMachineComponentRequirementCreateNestedManyWithoutTypeMachineInput | undefined {
if (!requirements || requirements.length === 0) {
return undefined;
}
return {
create: requirements.map((requirement) => ({
label: requirement.label ?? null,
minCount: requirement.minCount ?? 1,
maxCount: requirement.maxCount ?? null,
required: requirement.required ?? true,
allowNewModels: requirement.allowNewModels ?? true,
typeComposant: requirement.typeComposantId
? {
connect: { id: requirement.typeComposantId },
}
: (() => {
throw new Error(
'typeComposantId est requis pour créer une contrainte de composant.',
);
})(),
})),
};
}
static mapComponentRequirementInputs(
requirements?: RequirementDto[] | null,
) {
if (!requirements || requirements.length === 0) {
return [];
}
return requirements.map((requirement) => ({
label: requirement.label ?? null,
minCount: requirement.minCount ?? 1,
maxCount: requirement.maxCount ?? null,
required: requirement.required ?? true,
allowNewModels: requirement.allowNewModels ?? true,
typeComposantId: requirement.typeComposantId!,
}));
}
static mapPieceRequirements(
requirements?: RequirementDto[] | null,
): Prisma.TypeMachinePieceRequirementCreateNestedManyWithoutTypeMachineInput | undefined {
if (!requirements || requirements.length === 0) {
return undefined;
}
return {
create: requirements.map((requirement) => ({
label: requirement.label ?? null,
minCount: requirement.minCount ?? 0,
maxCount: requirement.maxCount ?? null,
required: requirement.required ?? false,
allowNewModels: requirement.allowNewModels ?? true,
typePiece: requirement.typePieceId
? {
connect: { id: requirement.typePieceId },
}
: (() => {
throw new Error(
'typePieceId est requis pour créer une contrainte de pièce.',
);
})(),
})),
};
}
static mapPieceRequirementInputs(
requirements?: RequirementDto[] | null,
) {
if (!requirements || requirements.length === 0) {
return [];
}
return requirements.map((requirement) => ({
label: requirement.label ?? null,
minCount: requirement.minCount ?? 0,
maxCount: requirement.maxCount ?? null,
required: requirement.required ?? false,
allowNewModels: requirement.allowNewModels ?? true,
typePieceId: requirement.typePieceId!,
}));
}
}

View File

@@ -0,0 +1,58 @@
import { Injectable } from '@nestjs/common';
import { Prisma, PrismaClient } from '@prisma/client';
import { PrismaService } from '../../prisma/prisma.service';
@Injectable()
export class ComposantModelsRepository {
constructor(private readonly prisma: PrismaService) {}
private get client(): PrismaClient {
return this.prisma;
}
async create(
data: Prisma.ComposantModelCreateInput,
include?: Prisma.ComposantModelInclude,
) {
return this.client.composantModel.create({
data,
include,
});
}
async findAll(
typeComposantId?: string,
include?: Prisma.ComposantModelInclude,
) {
return this.client.composantModel.findMany({
where: typeComposantId ? { typeComposantId } : undefined,
include,
orderBy: { name: 'asc' },
});
}
async findOne(id: string, include?: Prisma.ComposantModelInclude) {
return this.client.composantModel.findUnique({
where: { id },
include,
});
}
async update(
id: string,
data: Prisma.ComposantModelUpdateInput,
include?: Prisma.ComposantModelInclude,
) {
return this.client.composantModel.update({
where: { id },
data,
include,
});
}
async delete(id: string) {
return this.client.composantModel.delete({
where: { id },
});
}
}

View File

@@ -0,0 +1,71 @@
import { ModelTypesRepository } from './model-types.repository';
describe('ModelTypesRepository', () => {
const prismaMock = {
modelType: {
findUnique: jest.fn(),
create: jest.fn(),
findMany: jest.fn(),
findFirst: jest.fn(),
update: jest.fn(),
delete: jest.fn(),
},
customField: {
deleteMany: jest.fn(),
createMany: jest.fn(),
},
} as any;
const repository = new ModelTypesRepository(prismaMock);
beforeEach(() => {
jest.clearAllMocks();
});
it('should generate unique code when conflicts exist', async () => {
prismaMock.modelType.findUnique
.mockResolvedValueOnce({ id: 'existing' })
.mockResolvedValueOnce(null);
const code = await repository.generateUniqueCode('My Type');
expect(code).toBe('my-type-1');
expect(prismaMock.modelType.findUnique).toHaveBeenCalledTimes(2);
});
it('should create component type custom fields with relation id', async () => {
await repository.createComponentTypeCustomFields('comp-id', [
{ name: 'Field', type: 'string', required: true, options: [] },
]);
expect(prismaMock.customField.createMany).toHaveBeenCalledWith({
data: [
{
name: 'Field',
type: 'string',
required: true,
options: [],
typeComposantId: 'comp-id',
},
],
});
});
it('should create piece type custom fields with relation id', async () => {
await repository.createPieceTypeCustomFields('piece-id', [
{ name: 'Field', type: 'string', required: false, options: [] },
]);
expect(prismaMock.customField.createMany).toHaveBeenCalledWith({
data: [
{
name: 'Field',
type: 'string',
required: false,
options: [],
typePieceId: 'piece-id',
},
],
});
});
});

View File

@@ -0,0 +1,172 @@
import { Injectable } from '@nestjs/common';
import { ModelCategory, Prisma, PrismaClient } from '@prisma/client';
import { PrismaService } from '../../prisma/prisma.service';
import { slugifyName } from '../utils/slug.util';
type CustomFieldInput = Omit<
Prisma.CustomFieldCreateManyInput,
'id' | 'typeMachineId' | 'typePieceId' | 'typeComposantId'
>;
type PieceCustomFieldInput = Omit<
Prisma.CustomFieldCreateManyInput,
'id' | 'typeMachineId' | 'typePieceId' | 'typeComposantId'
>;
type ModelTypeCreateData = Omit<Prisma.ModelTypeCreateInput, 'category'>;
@Injectable()
export class ModelTypesRepository {
constructor(private readonly prisma: PrismaService) {}
private get client(): PrismaClient {
return this.prisma;
}
async generateUniqueCode(name: string): Promise<string> {
const base = slugifyName(name) || 'type';
let candidate = base;
let suffix = 1;
while (await this.client.modelType.findUnique({ where: { code: candidate } })) {
candidate = `${base}-${suffix++}`;
}
return candidate;
}
async createComponentType(
data: ModelTypeCreateData,
include?: Prisma.ModelTypeInclude,
) {
return this.client.modelType.create({
data: {
...data,
category: ModelCategory.COMPONENT,
},
include,
});
}
async findComponentTypes(include?: Prisma.ModelTypeInclude) {
return this.client.modelType.findMany({
where: { category: ModelCategory.COMPONENT },
include,
});
}
async findComponentType(id: string, include?: Prisma.ModelTypeInclude) {
return this.client.modelType.findFirst({
where: { id, category: ModelCategory.COMPONENT },
include,
});
}
async updateComponentType(
id: string,
data: Prisma.ModelTypeUpdateInput,
include?: Prisma.ModelTypeInclude,
) {
return this.client.modelType.update({
where: { id },
data,
include,
});
}
async deleteComponentType(id: string) {
return this.client.modelType.delete({
where: { id },
});
}
async deleteComponentTypeCustomFields(typeComposantId: string) {
await this.client.customField.deleteMany({
where: { typeComposantId },
});
}
async createComponentTypeCustomFields(
typeComposantId: string,
customFields: CustomFieldInput[],
) {
if (!customFields.length) {
return;
}
await this.client.customField.createMany({
data: customFields.map((field) => ({
...field,
typeComposantId,
})),
});
}
async createPieceType(
data: ModelTypeCreateData,
include?: Prisma.ModelTypeInclude,
) {
return this.client.modelType.create({
data: {
...data,
category: ModelCategory.PIECE,
},
include,
});
}
async findPieceTypes(include?: Prisma.ModelTypeInclude) {
return this.client.modelType.findMany({
where: { category: ModelCategory.PIECE },
include,
orderBy: { name: 'asc' },
});
}
async findPieceType(id: string, include?: Prisma.ModelTypeInclude) {
return this.client.modelType.findFirst({
where: { id, category: ModelCategory.PIECE },
include,
});
}
async updatePieceType(
id: string,
data: Prisma.ModelTypeUpdateInput,
include?: Prisma.ModelTypeInclude,
) {
return this.client.modelType.update({
where: { id },
data,
include,
});
}
async deletePieceType(id: string) {
return this.client.modelType.delete({
where: { id },
});
}
async deletePieceTypeCustomFields(typePieceId: string) {
await this.client.customField.deleteMany({
where: { typePieceId },
});
}
async createPieceTypeCustomFields(
typePieceId: string,
customFields: PieceCustomFieldInput[],
) {
if (!customFields.length) {
return;
}
await this.client.customField.createMany({
data: customFields.map((field) => ({
...field,
typePieceId,
})),
});
}
}

View File

@@ -0,0 +1,55 @@
import { Injectable } from '@nestjs/common';
import { Prisma, PrismaClient } from '@prisma/client';
import { PrismaService } from '../../prisma/prisma.service';
@Injectable()
export class PieceModelsRepository {
constructor(private readonly prisma: PrismaService) {}
private get client(): PrismaClient {
return this.prisma;
}
async create(
data: Prisma.PieceModelCreateInput,
include?: Prisma.PieceModelInclude,
) {
return this.client.pieceModel.create({
data,
include,
});
}
async findAll(typePieceId?: string, include?: Prisma.PieceModelInclude) {
return this.client.pieceModel.findMany({
where: typePieceId ? { typePieceId } : undefined,
include,
orderBy: { name: 'asc' },
});
}
async findOne(id: string, include?: Prisma.PieceModelInclude) {
return this.client.pieceModel.findUnique({
where: { id },
include,
});
}
async update(
id: string,
data: Prisma.PieceModelUpdateInput,
include?: Prisma.PieceModelInclude,
) {
return this.client.pieceModel.update({
where: { id },
data,
include,
});
}
async delete(id: string) {
return this.client.pieceModel.delete({
where: { id },
});
}
}

View File

@@ -0,0 +1,117 @@
import { TypeMachinesRepository } from './type-machines.repository';
describe('TypeMachinesRepository', () => {
const prismaMock = {
customField: {
deleteMany: jest.fn(),
createMany: jest.fn(),
},
typeMachineComponentRequirement: {
deleteMany: jest.fn(),
createMany: jest.fn(),
},
typeMachinePieceRequirement: {
deleteMany: jest.fn(),
createMany: jest.fn(),
},
machine: {
findMany: jest.fn().mockResolvedValue([{ id: '1', name: 'Machine' }]),
},
typeMachine: {
create: jest.fn(),
findMany: jest.fn(),
findUnique: jest.fn(),
update: jest.fn(),
delete: jest.fn(),
},
} as any;
const repository = new TypeMachinesRepository(prismaMock);
beforeEach(() => {
jest.clearAllMocks();
});
it('should append typeMachineId when creating custom fields', async () => {
await repository.createCustomFields('machine-id', [
{ name: 'Field', type: 'string', required: true, options: [] },
]);
expect(prismaMock.customField.createMany).toHaveBeenCalledWith({
data: [
{
name: 'Field',
type: 'string',
required: true,
options: [],
typeMachineId: 'machine-id',
},
],
});
});
it('should append typeMachineId when creating component requirements', async () => {
await repository.createComponentRequirements('machine-id', [
{
label: 'Comp',
minCount: 1,
maxCount: 2,
required: true,
allowNewModels: true,
typeComposantId: 'comp',
},
]);
expect(
prismaMock.typeMachineComponentRequirement.createMany,
).toHaveBeenCalledWith({
data: [
{
label: 'Comp',
minCount: 1,
maxCount: 2,
required: true,
allowNewModels: true,
typeComposantId: 'comp',
typeMachineId: 'machine-id',
},
],
});
});
it('should append typeMachineId when creating piece requirements', async () => {
await repository.createPieceRequirements('machine-id', [
{
label: 'Piece',
minCount: 0,
maxCount: 1,
required: false,
allowNewModels: true,
typePieceId: 'piece',
},
]);
expect(prismaMock.typeMachinePieceRequirement.createMany).toHaveBeenCalledWith({
data: [
{
label: 'Piece',
minCount: 0,
maxCount: 1,
required: false,
allowNewModels: true,
typePieceId: 'piece',
typeMachineId: 'machine-id',
},
],
});
});
it('should find machines using type', async () => {
const result = await repository.findMachinesUsingType('machine-id');
expect(result).toEqual([{ id: '1', name: 'Machine' }]);
expect(prismaMock.machine.findMany).toHaveBeenCalledWith({
where: { typeMachineId: 'machine-id' },
select: { id: true, name: true },
});
});
});

View File

@@ -0,0 +1,141 @@
import { Injectable } from '@nestjs/common';
import { Prisma, PrismaClient } from '@prisma/client';
import { PrismaService } from '../../prisma/prisma.service';
type CustomFieldInput = Omit<
Prisma.CustomFieldCreateManyInput,
'id' | 'typeMachineId' | 'typePieceId' | 'typeComposantId'
>;
type ComponentRequirementInput = Omit<
Prisma.TypeMachineComponentRequirementCreateManyInput,
'id' | 'typeMachineId'
>;
type PieceRequirementInput = Omit<
Prisma.TypeMachinePieceRequirementCreateManyInput,
'id' | 'typeMachineId'
>;
@Injectable()
export class TypeMachinesRepository {
constructor(private readonly prisma: PrismaService) {}
private get client(): PrismaClient {
return this.prisma;
}
async create(
data: Prisma.TypeMachineCreateInput,
include?: Prisma.TypeMachineInclude,
) {
return this.client.typeMachine.create({
data,
include,
});
}
async findAll(include?: Prisma.TypeMachineInclude) {
return this.client.typeMachine.findMany({
include,
});
}
async findOne(id: string, include?: Prisma.TypeMachineInclude) {
return this.client.typeMachine.findUnique({
where: { id },
include,
});
}
async update(
id: string,
data: Prisma.TypeMachineUpdateInput,
include?: Prisma.TypeMachineInclude,
) {
return this.client.typeMachine.update({
where: { id },
data,
include,
});
}
async delete(id: string) {
return this.client.typeMachine.delete({
where: { id },
});
}
async deleteCustomFields(typeMachineId: string) {
await this.client.customField.deleteMany({
where: { typeMachineId },
});
}
async createCustomFields(
typeMachineId: string,
customFields: CustomFieldInput[],
) {
if (!customFields.length) {
return;
}
await this.client.customField.createMany({
data: customFields.map((field) => ({
...field,
typeMachineId,
})),
});
}
async deleteComponentRequirements(typeMachineId: string) {
await this.client.typeMachineComponentRequirement.deleteMany({
where: { typeMachineId },
});
}
async createComponentRequirements(
typeMachineId: string,
requirements: ComponentRequirementInput[],
) {
if (!requirements.length) {
return;
}
await this.client.typeMachineComponentRequirement.createMany({
data: requirements.map((requirement) => ({
...requirement,
typeMachineId,
})),
});
}
async deletePieceRequirements(typeMachineId: string) {
await this.client.typeMachinePieceRequirement.deleteMany({
where: { typeMachineId },
});
}
async createPieceRequirements(
typeMachineId: string,
requirements: PieceRequirementInput[],
) {
if (!requirements.length) {
return;
}
await this.client.typeMachinePieceRequirement.createMany({
data: requirements.map((requirement) => ({
...requirement,
typeMachineId,
})),
});
}
async findMachinesUsingType(typeMachineId: string) {
return this.client.machine.findMany({
where: { typeMachineId },
select: { id: true, name: true },
});
}
}

View File

@@ -0,0 +1,9 @@
export function slugifyName(name: string): string {
return name
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '')
.replace(/-+/g, '-');
}

View File

@@ -0,0 +1,53 @@
import { Injectable } from '@nestjs/common';
import { ComposantModelsRepository } from '../../common/repositories/composant-models.repository';
import {
CreateComposantModelDto,
UpdateComposantModelDto,
} from '../../shared/dto/type.dto';
const COMPOSANT_MODEL_INCLUDE = {
typeComposant: true,
} as const;
@Injectable()
export class ComposantModelService {
constructor(private readonly repository: ComposantModelsRepository) {}
async create(dto: CreateComposantModelDto) {
const { typeComposantId, ...data } = dto;
return this.repository.create(
{
...data,
typeComposant: { connect: { id: typeComposantId } },
},
COMPOSANT_MODEL_INCLUDE,
);
}
async findAll(typeComposantId?: string) {
return this.repository.findAll(typeComposantId, COMPOSANT_MODEL_INCLUDE);
}
async findOne(id: string) {
return this.repository.findOne(id, COMPOSANT_MODEL_INCLUDE);
}
async update(id: string, dto: UpdateComposantModelDto) {
const { typeComposantId, ...data } = dto;
return this.repository.update(
id,
{
...data,
...(typeComposantId
? { typeComposant: { connect: { id: typeComposantId } } }
: {}),
},
COMPOSANT_MODEL_INCLUDE,
);
}
async remove(id: string) {
return this.repository.delete(id);
}
}

View File

@@ -0,0 +1,49 @@
import { Injectable } from '@nestjs/common';
import { PieceModelsRepository } from '../../common/repositories/piece-models.repository';
import { CreatePieceModelDto, UpdatePieceModelDto } from '../../shared/dto/type.dto';
const PIECE_MODEL_INCLUDE = {
typePiece: true,
} as const;
@Injectable()
export class PieceModelService {
constructor(private readonly repository: PieceModelsRepository) {}
async create(dto: CreatePieceModelDto) {
const { typePieceId, ...data } = dto;
return this.repository.create(
{
...data,
typePiece: { connect: { id: typePieceId } },
},
PIECE_MODEL_INCLUDE,
);
}
async findAll(typePieceId?: string) {
return this.repository.findAll(typePieceId, PIECE_MODEL_INCLUDE);
}
async findOne(id: string) {
return this.repository.findOne(id, PIECE_MODEL_INCLUDE);
}
async update(id: string, dto: UpdatePieceModelDto) {
const { typePieceId, ...data } = dto;
return this.repository.update(
id,
{
...data,
...(typePieceId ? { typePiece: { connect: { id: typePieceId } } } : {}),
},
PIECE_MODEL_INCLUDE,
);
}
async remove(id: string) {
return this.repository.delete(id);
}
}

View File

@@ -0,0 +1,47 @@
import { Injectable } from '@nestjs/common';
import { ModelTypesRepository } from '../../common/repositories/model-types.repository';
import {
COMPONENT_TYPE_INCLUDE,
ModelTypeMapper,
} from '../../common/mappers/model-type.mapper';
import {
CreateTypeComposantDto,
UpdateTypeComposantDto,
} from '../../shared/dto/type.dto';
@Injectable()
export class TypeComponentService {
constructor(private readonly repository: ModelTypesRepository) {}
async create(dto: CreateTypeComposantDto) {
const code = await this.repository.generateUniqueCode(dto.name);
const data = ModelTypeMapper.toComponentCreateInput(dto, code);
return this.repository.createComponentType(data, COMPONENT_TYPE_INCLUDE);
}
async findAll() {
return this.repository.findComponentTypes(COMPONENT_TYPE_INCLUDE);
}
async findOne(id: string) {
return this.repository.findComponentType(id, COMPONENT_TYPE_INCLUDE);
}
async update(id: string, dto: UpdateTypeComposantDto) {
if (dto.customFields !== undefined) {
await this.repository.deleteComponentTypeCustomFields(id);
const fields = ModelTypeMapper.mapComponentCustomFieldInputs(
dto.customFields,
);
await this.repository.createComponentTypeCustomFields(id, fields);
}
const data = ModelTypeMapper.toComponentUpdateInput(dto);
return this.repository.updateComponentType(id, data, COMPONENT_TYPE_INCLUDE);
}
async remove(id: string) {
return this.repository.deleteComponentType(id);
}
}

View File

@@ -0,0 +1,72 @@
import { Injectable } from '@nestjs/common';
import { TypeMachinesRepository } from '../../common/repositories/type-machines.repository';
import {
TYPE_MACHINE_DEFAULT_INCLUDE,
TYPE_MACHINE_WITH_MACHINES_INCLUDE,
TypeMachineMapper,
} from '../../common/mappers/type-machine.mapper';
import {
CreateTypeMachineDto,
UpdateTypeMachineDto,
} from '../../shared/dto/type.dto';
@Injectable()
export class TypeMachineService {
constructor(private readonly repository: TypeMachinesRepository) {}
async create(dto: CreateTypeMachineDto) {
const data = TypeMachineMapper.toCreateInput(dto);
return this.repository.create(data, TYPE_MACHINE_DEFAULT_INCLUDE);
}
async findAll() {
return this.repository.findAll(TYPE_MACHINE_WITH_MACHINES_INCLUDE);
}
async findOne(id: string) {
return this.repository.findOne(id, TYPE_MACHINE_WITH_MACHINES_INCLUDE);
}
async update(id: string, dto: UpdateTypeMachineDto) {
const updateData = TypeMachineMapper.toUpdateData(dto);
if (dto.customFields !== undefined) {
await this.repository.deleteCustomFields(id);
const fields = TypeMachineMapper.mapCustomFieldInputs(dto.customFields);
await this.repository.createCustomFields(id, fields);
}
if (dto.componentRequirements !== undefined) {
await this.repository.deleteComponentRequirements(id);
const requirements = TypeMachineMapper.mapComponentRequirementInputs(
dto.componentRequirements,
);
await this.repository.createComponentRequirements(id, requirements);
}
if (dto.pieceRequirements !== undefined) {
await this.repository.deletePieceRequirements(id);
const requirements = TypeMachineMapper.mapPieceRequirementInputs(
dto.pieceRequirements,
);
await this.repository.createPieceRequirements(id, requirements);
}
return this.repository.update(id, updateData, TYPE_MACHINE_DEFAULT_INCLUDE);
}
async remove(id: string) {
const machines = await this.repository.findMachinesUsingType(id);
if (machines.length > 0) {
const names = machines.map((machine) => machine.name).join(', ');
throw new Error(
`Impossible de supprimer ce type de machine car il est utilisé par ${machines.length} machine(s): ${names}. Veuillez d'abord supprimer ou modifier ces machines.`,
);
}
await this.repository.deleteCustomFields(id);
return this.repository.delete(id);
}
}

View File

@@ -0,0 +1,54 @@
import { Injectable } from '@nestjs/common';
import { ModelTypesRepository } from '../../common/repositories/model-types.repository';
import {
ModelTypeMapper,
PIECE_TYPE_INCLUDE,
} from '../../common/mappers/model-type.mapper';
import {
CreateTypePieceDto,
UpdateTypePieceDto,
} from '../../shared/dto/type.dto';
@Injectable()
export class TypePieceService {
constructor(private readonly repository: ModelTypesRepository) {}
async create(dto: CreateTypePieceDto) {
const code = await this.repository.generateUniqueCode(dto.name);
const data = ModelTypeMapper.toPieceCreateInput(dto, code);
const created = await this.repository.createPieceType(data, PIECE_TYPE_INCLUDE);
return ModelTypeMapper.mapPieceModelType(created);
}
async findAll() {
const items = await this.repository.findPieceTypes(PIECE_TYPE_INCLUDE);
return items.map((item) => ModelTypeMapper.mapPieceModelType(item));
}
async findOne(id: string) {
const item = await this.repository.findPieceType(id, PIECE_TYPE_INCLUDE);
return ModelTypeMapper.mapPieceModelType(item);
}
async update(id: string, dto: UpdateTypePieceDto) {
if (dto.customFields !== undefined) {
await this.repository.deletePieceTypeCustomFields(id);
const fields = ModelTypeMapper.mapPieceCustomFieldInputs(dto.customFields);
await this.repository.createPieceTypeCustomFields(id, fields);
}
const data = ModelTypeMapper.toPieceUpdateInput(dto);
const updated = await this.repository.updatePieceType(
id,
data,
PIECE_TYPE_INCLUDE,
);
return ModelTypeMapper.mapPieceModelType(updated);
}
async remove(id: string) {
return this.repository.deletePieceType(id);
}
}

View File

@@ -2,6 +2,15 @@ import { Test, TestingModule } from '@nestjs/testing';
import { TypesController } from './types.controller';
import { TypesService } from './types.service';
import { PrismaService } from '../prisma/prisma.service';
import { TypeMachineService } from './services/type-machine.service';
import { TypeComponentService } from './services/type-component.service';
import { TypePieceService } from './services/type-piece.service';
import { ComposantModelService } from './services/composant-model.service';
import { PieceModelService } from './services/piece-model.service';
import { TypeMachinesRepository } from '../common/repositories/type-machines.repository';
import { ModelTypesRepository } from '../common/repositories/model-types.repository';
import { ComposantModelsRepository } from '../common/repositories/composant-models.repository';
import { PieceModelsRepository } from '../common/repositories/piece-models.repository';
describe('TypesController', () => {
let controller: TypesController;
@@ -9,7 +18,19 @@ describe('TypesController', () => {
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [TypesController],
providers: [TypesService, PrismaService],
providers: [
TypesService,
TypeMachineService,
TypeComponentService,
TypePieceService,
ComposantModelService,
PieceModelService,
TypeMachinesRepository,
ModelTypesRepository,
ComposantModelsRepository,
PieceModelsRepository,
PrismaService,
],
}).compile();
controller = module.get<TypesController>(TypesController);

View File

@@ -1,9 +1,29 @@
import { Module } from '@nestjs/common';
import { ComposantModelsRepository } from '../common/repositories/composant-models.repository';
import { ModelTypesRepository } from '../common/repositories/model-types.repository';
import { PieceModelsRepository } from '../common/repositories/piece-models.repository';
import { TypeMachinesRepository } from '../common/repositories/type-machines.repository';
import { TypesController } from './types.controller';
import { TypesService } from './types.service';
import { ComposantModelService } from './services/composant-model.service';
import { PieceModelService } from './services/piece-model.service';
import { TypeComponentService } from './services/type-component.service';
import { TypeMachineService } from './services/type-machine.service';
import { TypePieceService } from './services/type-piece.service';
@Module({
controllers: [TypesController],
providers: [TypesService],
providers: [
TypesService,
TypeMachineService,
TypeComponentService,
TypePieceService,
ComposantModelService,
PieceModelService,
TypeMachinesRepository,
ModelTypesRepository,
ComposantModelsRepository,
PieceModelsRepository,
],
})
export class TypesModule {}

View File

@@ -1,13 +1,34 @@
import { Test, TestingModule } from '@nestjs/testing';
import { TypesService } from './types.service';
import { PrismaService } from '../prisma/prisma.service';
import { TypeMachineService } from './services/type-machine.service';
import { TypeComponentService } from './services/type-component.service';
import { TypePieceService } from './services/type-piece.service';
import { ComposantModelService } from './services/composant-model.service';
import { PieceModelService } from './services/piece-model.service';
import { TypeMachinesRepository } from '../common/repositories/type-machines.repository';
import { ModelTypesRepository } from '../common/repositories/model-types.repository';
import { ComposantModelsRepository } from '../common/repositories/composant-models.repository';
import { PieceModelsRepository } from '../common/repositories/piece-models.repository';
describe('TypesService', () => {
let service: TypesService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [TypesService, PrismaService],
providers: [
TypesService,
TypeMachineService,
TypeComponentService,
TypePieceService,
ComposantModelService,
PieceModelService,
TypeMachinesRepository,
ModelTypesRepository,
ComposantModelsRepository,
PieceModelsRepository,
PrismaService,
],
}).compile();
service = module.get<TypesService>(TypesService);

View File

@@ -1,6 +1,9 @@
import { Injectable } from '@nestjs/common';
import { ModelCategory } from '@prisma/client';
import { PrismaService } from '../prisma/prisma.service';
import { ComposantModelService } from './services/composant-model.service';
import { PieceModelService } from './services/piece-model.service';
import { TypeComponentService } from './services/type-component.service';
import { TypeMachineService } from './services/type-machine.service';
import { TypePieceService } from './services/type-piece.service';
import {
CreateTypeMachineDto,
UpdateTypeMachineDto,
@@ -14,640 +17,118 @@ import {
UpdatePieceModelDto,
} from '../shared/dto/type.dto';
const CUSTOM_FIELD_SELECT = {
id: true,
name: true,
type: true,
required: true,
options: true,
} as const;
@Injectable()
export class TypesService {
constructor(private prisma: PrismaService) {}
constructor(
private readonly typeMachineService: TypeMachineService,
private readonly typeComponentService: TypeComponentService,
private readonly typePieceService: TypePieceService,
private readonly composantModelService: ComposantModelService,
private readonly pieceModelService: PieceModelService,
) {}
private slugifyName(name: string): string {
return name
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '')
.replace(/-+/g, '-');
// TypeMachine
createTypeMachine(dto: CreateTypeMachineDto) {
return this.typeMachineService.create(dto);
}
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;
findAllTypeMachines() {
return this.typeMachineService.findAll();
}
// TypeMachine methods
async createTypeMachine(createTypeMachineDto: 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,
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: { select: CUSTOM_FIELD_SELECT },
componentRequirements: {
include: {
typeComposant: true,
},
},
pieceRequirements: {
include: {
typePiece: true,
},
},
},
});
findOneTypeMachine(id: string) {
return this.typeMachineService.findOne(id);
}
async findAllTypeMachines() {
return this.prisma.typeMachine.findMany({
include: {
machines: true,
customFields: { select: CUSTOM_FIELD_SELECT },
componentRequirements: {
include: {
typeComposant: true,
},
},
pieceRequirements: {
include: {
typePiece: true,
},
},
},
});
updateTypeMachine(id: string, dto: UpdateTypeMachineDto) {
return this.typeMachineService.update(id, dto);
}
async findOneTypeMachine(id: string) {
return this.prisma.typeMachine.findUnique({
where: { id },
include: {
machines: true,
customFields: { select: CUSTOM_FIELD_SELECT },
componentRequirements: {
include: {
typeComposant: true,
},
},
pieceRequirements: {
include: {
typePiece: true,
},
},
},
});
removeTypeMachine(id: string) {
return this.typeMachineService.remove(id);
}
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 },
});
// Créer les nouveaux champs personnalisés
if (customFields.length > 0) {
await this.prisma.customField.createMany({
data: customFields.map((field) => ({
name: field.name,
type: field.type,
required: field.required || false,
options: field.options,
typeMachineId: id,
})),
});
}
}
if (componentRequirements !== undefined) {
await this.prisma.typeMachineComponentRequirement.deleteMany({
where: { typeMachineId: id },
});
if (componentRequirements.length > 0) {
await this.prisma.typeMachineComponentRequirement.createMany({
data: componentRequirements.map((requirement) => ({
label: requirement.label ?? null,
minCount: requirement.minCount ?? 1,
maxCount: requirement.maxCount ?? null,
required: requirement.required ?? true,
allowNewModels: requirement.allowNewModels ?? true,
typeMachineId: id,
typeComposantId: requirement.typeComposantId,
})),
skipDuplicates: false,
});
}
}
if (pieceRequirements !== undefined) {
await this.prisma.typeMachinePieceRequirement.deleteMany({
where: { typeMachineId: id },
});
if (pieceRequirements.length > 0) {
await this.prisma.typeMachinePieceRequirement.createMany({
data: pieceRequirements.map((requirement) => ({
label: requirement.label ?? null,
minCount: requirement.minCount ?? 0,
maxCount: requirement.maxCount ?? null,
required: requirement.required ?? false,
allowNewModels: requirement.allowNewModels ?? true,
typeMachineId: id,
typePieceId: requirement.typePieceId,
})),
skipDuplicates: false,
});
}
}
return this.prisma.typeMachine.update({
where: { id },
data: typeData,
include: {
customFields: { select: CUSTOM_FIELD_SELECT },
componentRequirements: {
include: {
typeComposant: true,
},
},
pieceRequirements: {
include: {
typePiece: true,
},
},
},
});
// TypeComposant
createTypeComposant(dto: CreateTypeComposantDto) {
return this.typeComponentService.create(dto);
}
async removeTypeMachine(id: string) {
// Vérifier s'il y a des machines liées à ce type
const machinesWithType = await this.prisma.machine.findMany({
where: { typeMachineId: id },
select: { id: true, name: true },
});
if (machinesWithType.length > 0) {
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.`,
);
}
// Supprimer les champs personnalisés associés
await this.prisma.customField.deleteMany({
where: { typeMachineId: id },
});
return this.prisma.typeMachine.delete({
where: { id },
});
findAllTypeComposants() {
return this.typeComponentService.findAll();
}
// TypeComposant methods (backed by ModelType with COMPONENT category)
async createTypeComposant(createTypeComposantDto: CreateTypeComposantDto) {
const { customFields, description, name } = createTypeComposantDto;
const code = await this.generateUniqueModelTypeCode(name);
return this.prisma.modelType.create({
data: {
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,
options: field.options,
})),
}
: undefined,
},
include: {
customFields: { select: CUSTOM_FIELD_SELECT },
},
});
findOneTypeComposant(id: string) {
return this.typeComponentService.findOne(id);
}
async findAllTypeComposants() {
return this.prisma.modelType.findMany({
where: { category: ModelCategory.COMPONENT },
include: {
composants: true,
customFields: { select: CUSTOM_FIELD_SELECT },
models: true,
},
});
updateTypeComposant(id: string, dto: UpdateTypeComposantDto) {
return this.typeComponentService.update(id, dto);
}
async findOneTypeComposant(id: string) {
return this.prisma.modelType.findFirst({
where: {
id,
category: ModelCategory.COMPONENT,
},
include: {
composants: true,
customFields: { select: CUSTOM_FIELD_SELECT },
models: true,
},
});
removeTypeComposant(id: string) {
return this.typeComponentService.remove(id);
}
async updateTypeComposant(
id: string,
updateTypeComposantDto: UpdateTypeComposantDto,
) {
const { customFields, description, name } = updateTypeComposantDto;
if (customFields !== undefined) {
await this.prisma.customField.deleteMany({
where: { typeComposantId: id },
});
if (customFields.length > 0) {
await this.prisma.customField.createMany({
data: customFields.map((field) => ({
name: field.name,
type: field.type,
required: field.required || false,
options: field.options,
typeComposantId: id,
})),
});
}
}
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,
include: {
customFields: { select: CUSTOM_FIELD_SELECT },
},
});
// TypePiece
createTypePiece(dto: CreateTypePieceDto) {
return this.typePieceService.create(dto);
}
async removeTypeComposant(id: string) {
return this.prisma.modelType.delete({
where: { id },
});
findAllTypePieces() {
return this.typePieceService.findAll();
}
// 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 ?? [],
};
findOneTypePiece(id: string) {
return this.typePieceService.findOne(id);
}
async createTypePiece(createTypePieceDto: CreateTypePieceDto) {
const { customFields, description, name } = createTypePieceDto;
const code = await this.generateUniqueModelTypeCode(name);
const created = await this.prisma.modelType.create({
data: {
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,
options: field.options,
})),
}
: undefined,
},
include: {
pieceCustomFields: { select: CUSTOM_FIELD_SELECT },
pieceModels: true,
pieceRequirements: true,
pieces: true,
},
});
return this.mapModelTypePiece(created);
updateTypePiece(id: string, dto: UpdateTypePieceDto) {
return this.typePieceService.update(id, dto);
}
async findAllTypePieces() {
const items = await this.prisma.modelType.findMany({
where: { category: ModelCategory.PIECE },
include: {
pieceCustomFields: { select: CUSTOM_FIELD_SELECT },
pieceModels: true,
pieceRequirements: true,
pieces: true,
},
orderBy: { name: 'asc' },
});
return items.map((item) => this.mapModelTypePiece(item));
removeTypePiece(id: string) {
return this.typePieceService.remove(id);
}
async findOneTypePiece(id: string) {
const item = await this.prisma.modelType.findFirst({
where: { id, category: ModelCategory.PIECE },
include: {
pieceCustomFields: { select: CUSTOM_FIELD_SELECT },
pieceModels: true,
pieceRequirements: true,
pieces: true,
},
});
return this.mapModelTypePiece(item);
// ComposantModel
createComposantModel(dto: CreateComposantModelDto) {
return this.composantModelService.create(dto);
}
async updateTypePiece(id: string, updateTypePieceDto: UpdateTypePieceDto) {
const { customFields, description, name } = updateTypePieceDto;
if (customFields !== undefined) {
await this.prisma.customField.deleteMany({
where: { typePieceId: id },
});
if (customFields.length > 0) {
await this.prisma.customField.createMany({
data: customFields.map((field) => ({
name: field.name,
type: field.type,
required: field.required || false,
options: field.options,
typePieceId: id,
})),
});
}
}
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: updatePayload,
include: {
pieceCustomFields: { select: CUSTOM_FIELD_SELECT },
pieceModels: true,
pieceRequirements: true,
pieces: true,
},
});
return this.mapModelTypePiece(updated);
findAllComposantModels(typeComposantId?: string) {
return this.composantModelService.findAll(typeComposantId);
}
async removeTypePiece(id: string) {
return this.prisma.modelType.delete({
where: { id },
});
findOneComposantModel(id: string) {
return this.composantModelService.findOne(id);
}
// ComposantModel methods
async createComposantModel(createComposantModelDto: CreateComposantModelDto) {
const { typeComposantId, ...data } = createComposantModelDto;
return this.prisma.composantModel.create({
data: {
...data,
typeComposant: {
connect: { id: typeComposantId },
},
},
include: {
typeComposant: true,
},
});
updateComposantModel(id: string, dto: UpdateComposantModelDto) {
return this.composantModelService.update(id, dto);
}
async findAllComposantModels(typeComposantId?: string) {
return this.prisma.composantModel.findMany({
where: typeComposantId ? { typeComposantId } : undefined,
include: {
typeComposant: true,
},
orderBy: {
name: 'asc',
},
});
removeComposantModel(id: string) {
return this.composantModelService.remove(id);
}
async findOneComposantModel(id: string) {
return this.prisma.composantModel.findUnique({
where: { id },
include: {
typeComposant: true,
},
});
// PieceModel
createPieceModel(dto: CreatePieceModelDto) {
return this.pieceModelService.create(dto);
}
async updateComposantModel(
id: string,
updateComposantModelDto: UpdateComposantModelDto,
) {
const { typeComposantId, ...data } = updateComposantModelDto;
return this.prisma.composantModel.update({
where: { id },
data: {
...data,
...(typeComposantId
? {
typeComposant: {
connect: { id: typeComposantId },
},
}
: {}),
},
include: {
typeComposant: true,
},
});
findAllPieceModels(typePieceId?: string) {
return this.pieceModelService.findAll(typePieceId);
}
async removeComposantModel(id: string) {
return this.prisma.composantModel.delete({
where: { id },
});
findOnePieceModel(id: string) {
return this.pieceModelService.findOne(id);
}
// PieceModel methods
async createPieceModel(createPieceModelDto: CreatePieceModelDto) {
const { typePieceId, ...data } = createPieceModelDto;
return this.prisma.pieceModel.create({
data: {
...data,
typePiece: {
connect: { id: typePieceId },
},
},
include: {
typePiece: true,
},
});
updatePieceModel(id: string, dto: UpdatePieceModelDto) {
return this.pieceModelService.update(id, dto);
}
async findAllPieceModels(typePieceId?: string) {
return this.prisma.pieceModel.findMany({
where: typePieceId ? { typePieceId } : undefined,
include: {
typePiece: true,
},
orderBy: {
name: 'asc',
},
});
}
async findOnePieceModel(id: string) {
return this.prisma.pieceModel.findUnique({
where: { id },
include: {
typePiece: true,
},
});
}
async updatePieceModel(id: string, updatePieceModelDto: UpdatePieceModelDto) {
const { typePieceId, ...data } = updatePieceModelDto;
return this.prisma.pieceModel.update({
where: { id },
data: {
...data,
...(typePieceId
? {
typePiece: {
connect: { id: typePieceId },
},
}
: {}),
},
include: {
typePiece: true,
},
});
}
async removePieceModel(id: string) {
return this.prisma.pieceModel.delete({
where: { id },
});
removePieceModel(id: string) {
return this.pieceModelService.remove(id);
}
}