From b9b936c9eab502d8eba83123737f0fd849621374 Mon Sep 17 00:00:00 2001 From: MaKarin Date: Tue, 24 Mar 2026 12:49:23 +0300 Subject: [PATCH] fix after review --- client/src/dataProvider.ts | 2 +- .../equipment-type/EquipmentTypeCreate.tsx | 12 +- .../equipment-type/EquipmentTypeEdit.tsx | 12 +- .../equipment-type/EquipmentTypeList.tsx | 14 +- .../resources/equipment/EquipmentCreate.tsx | 34 ++-- .../src/resources/equipment/EquipmentEdit.tsx | 34 ++-- .../src/resources/equipment/EquipmentList.tsx | 109 +++++++++--- .../repair-order/RepairOrderCreate.tsx | 48 ++--- .../repair-order/RepairOrderEdit.tsx | 48 ++--- .../repair-order/RepairOrderList.tsx | 64 +++---- docker-compose.yml | 2 +- generation/generate.mjs | 166 ++++++++++++++---- .../dto/update-equipment-type.dto.ts | 10 +- .../equipment-type/equipment-type.service.ts | 29 ++- .../equipment/dto/update-equipment.dto.ts | 24 +-- .../modules/equipment/equipment.service.ts | 38 +++- .../dto/update-repair-order.dto.ts | 24 +-- .../repair-order/repair-order.service.ts | 37 +++- 18 files changed, 461 insertions(+), 246 deletions(-) diff --git a/client/src/dataProvider.ts b/client/src/dataProvider.ts index 3ce2577..fe35284 100644 --- a/client/src/dataProvider.ts +++ b/client/src/dataProvider.ts @@ -1,6 +1,6 @@ import { DataProvider, fetchUtils } from 'react-admin'; -const apiUrl = 'http://localhost:3001'; +const apiUrl = 'http://localhost:3000'; const httpClient = fetchUtils.fetchJson; function buildQueryString(query: Record) { diff --git a/client/src/resources/equipment-type/EquipmentTypeCreate.tsx b/client/src/resources/equipment-type/EquipmentTypeCreate.tsx index dd45ce2..4068f40 100644 --- a/client/src/resources/equipment-type/EquipmentTypeCreate.tsx +++ b/client/src/resources/equipment-type/EquipmentTypeCreate.tsx @@ -1,14 +1,14 @@ -import { Create, SimpleForm, TextInput } from 'react-admin'; +import { Create, SimpleForm, TextInput, NumberInput } from 'react-admin'; export const EquipmentTypeCreate = () => ( - - - - - + + + + + ); diff --git a/client/src/resources/equipment-type/EquipmentTypeEdit.tsx b/client/src/resources/equipment-type/EquipmentTypeEdit.tsx index 91ee269..b44ed79 100644 --- a/client/src/resources/equipment-type/EquipmentTypeEdit.tsx +++ b/client/src/resources/equipment-type/EquipmentTypeEdit.tsx @@ -1,14 +1,14 @@ -import { Edit, SimpleForm, TextInput } from 'react-admin'; +import { Edit, SimpleForm, TextInput, NumberInput } from 'react-admin'; export const EquipmentTypeEdit = () => ( - - - - - + + + + + ); diff --git a/client/src/resources/equipment-type/EquipmentTypeList.tsx b/client/src/resources/equipment-type/EquipmentTypeList.tsx index 2334bfd..5a98f6c 100644 --- a/client/src/resources/equipment-type/EquipmentTypeList.tsx +++ b/client/src/resources/equipment-type/EquipmentTypeList.tsx @@ -13,8 +13,8 @@ import { const equipmentTypeFilters = [ , - , - + , + ]; const EquipmentTypeListActions = () => ( @@ -28,11 +28,11 @@ const EquipmentTypeListActions = () => ( export const EquipmentTypeList = () => ( } filters={equipmentTypeFilters} sort={{ field: 'code', order: 'ASC' }}> - - - - - + + + + + ); diff --git a/client/src/resources/equipment/EquipmentCreate.tsx b/client/src/resources/equipment/EquipmentCreate.tsx index 705773a..fd177e1 100644 --- a/client/src/resources/equipment/EquipmentCreate.tsx +++ b/client/src/resources/equipment/EquipmentCreate.tsx @@ -1,28 +1,28 @@ -import { Create, SimpleForm, TextInput, SelectInput, ReferenceInput, AutocompleteInput } from 'react-admin'; +import { Create, SimpleForm, TextInput, NumberInput, DateInput, SelectInput, ReferenceInput, AutocompleteInput } from 'react-admin'; const statusChoices = [ - { id: 'Active', name: 'Active' }, - { id: 'Repair', name: 'Repair' }, - { id: 'Reserve', name: 'Reserve' }, - { id: 'WriteOff', name: 'WriteOff' }, + { id: 'Active', name: 'В эксплуатации' }, + { id: 'Repair', name: 'В ремонте' }, + { id: 'Reserve', name: 'В резерве' }, + { id: 'WriteOff', name: 'Списано' }, ]; export const EquipmentCreate = () => ( - - - - - record.code ? `${record.code} — ${record.name ?? record.code}` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} /> + + + + + record.code ? `${record.code} — ${record.name ?? record.code}` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} /> - - - - - - - + + + + + + + ); diff --git a/client/src/resources/equipment/EquipmentEdit.tsx b/client/src/resources/equipment/EquipmentEdit.tsx index 7044507..98a3a1e 100644 --- a/client/src/resources/equipment/EquipmentEdit.tsx +++ b/client/src/resources/equipment/EquipmentEdit.tsx @@ -1,29 +1,29 @@ -import { Edit, SimpleForm, TextInput, SelectInput, ReferenceInput, AutocompleteInput } from 'react-admin'; +import { Edit, SimpleForm, TextInput, NumberInput, DateInput, SelectInput, ReferenceInput, AutocompleteInput } from 'react-admin'; const statusChoices = [ - { id: 'Active', name: 'Active' }, - { id: 'Repair', name: 'Repair' }, - { id: 'Reserve', name: 'Reserve' }, - { id: 'WriteOff', name: 'WriteOff' }, + { id: 'Active', name: 'В эксплуатации' }, + { id: 'Repair', name: 'В ремонте' }, + { id: 'Reserve', name: 'В резерве' }, + { id: 'WriteOff', name: 'Списано' }, ]; export const EquipmentEdit = () => ( - - - - - record.code ? `${record.code} — ${record.name ?? record.code}` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} /> + + + + + record.code ? `${record.code} — ${record.name ?? record.code}` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} /> - - - - - - - + + + + + + + ); diff --git a/client/src/resources/equipment/EquipmentList.tsx b/client/src/resources/equipment/EquipmentList.tsx index 89e3361..5e70e88 100644 --- a/client/src/resources/equipment/EquipmentList.tsx +++ b/client/src/resources/equipment/EquipmentList.tsx @@ -13,27 +13,60 @@ import { ReferenceField, SelectArrayInput, ReferenceInput, - AutocompleteInput -} from 'react-admin'; + AutocompleteInput, +} from "react-admin"; const statusChoices = [ - { id: 'Active', name: 'Active' }, - { id: 'Repair', name: 'Repair' }, - { id: 'Reserve', name: 'Reserve' }, - { id: 'WriteOff', name: 'WriteOff' }, + { id: "Active", name: "В эксплуатации" }, + { id: "Repair", name: "В ремонте" }, + { id: "Reserve", name: "В резерве" }, + { id: "WriteOff", name: "Списано" }, ]; const equipmentFilters = [ , - , - , - , - - record.code ? `${record.code} — ${record.name ?? record.code}` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} /> + , + , + , + + + record.code + ? `${record.code} — ${record.name ?? record.code}` + : (record.name ?? record.id) + } + filterToQuery={(searchText) => ({ q: searchText })} + /> , - , - , - + , + , + , ]; const EquipmentListActions = () => ( @@ -45,22 +78,44 @@ const EquipmentListActions = () => ( ); export const EquipmentList = () => ( - } filters={equipmentFilters} sort={{ field: 'id', order: 'ASC' }}> + } + filters={equipmentFilters} + sort={{ field: "inventoryNumber", order: "ASC" }} + > - - - - - + + + + + - - - - - - - + + + + + + + ); diff --git a/client/src/resources/repair-order/RepairOrderCreate.tsx b/client/src/resources/repair-order/RepairOrderCreate.tsx index 26c67c2..890734a 100644 --- a/client/src/resources/repair-order/RepairOrderCreate.tsx +++ b/client/src/resources/repair-order/RepairOrderCreate.tsx @@ -1,38 +1,38 @@ -import { Create, SimpleForm, TextInput, SelectInput, ReferenceInput, AutocompleteInput } from 'react-admin'; +import { Create, SimpleForm, TextInput, NumberInput, DateInput, SelectInput, ReferenceInput, AutocompleteInput } from 'react-admin'; const repairKindChoices = [ - { id: 'TO', name: 'TO' }, - { id: 'TR', name: 'TR' }, - { id: 'TRE', name: 'TRE' }, - { id: 'KR', name: 'KR' }, - { id: 'AR', name: 'AR' }, - { id: 'MP', name: 'MP' }, + { id: 'TO', name: 'Техническое обслуживание' }, + { id: 'TR', name: 'Текущий ремонт' }, + { id: 'TRE', name: 'Текущий расширенный ремонт' }, + { id: 'KR', name: 'Капитальный ремонт' }, + { id: 'AR', name: 'Аварийный ремонт' }, + { id: 'MP', name: 'Метрологическая поверка' }, ]; const statusChoices = [ - { id: 'Draft', name: 'Draft' }, - { id: 'Approved', name: 'Approved' }, - { id: 'InWork', name: 'InWork' }, - { id: 'Done', name: 'Done' }, - { id: 'Cancelled', name: 'Cancelled' }, + { id: 'Draft', name: 'Черновик' }, + { id: 'Approved', name: 'Утверждена' }, + { id: 'InWork', name: 'В работе' }, + { id: 'Done', name: 'Выполнена' }, + { id: 'Cancelled', name: 'Отменена' }, ]; export const RepairOrderCreate = () => ( - - - record.code ? `${record.code} — ${record.name ?? record.code}` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} /> + + + record.inventoryNumber ? `${record.inventoryNumber} — ${record.name ?? record.inventoryNumber}` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} /> - - - - - - - - - + + + + + + + + + ); diff --git a/client/src/resources/repair-order/RepairOrderEdit.tsx b/client/src/resources/repair-order/RepairOrderEdit.tsx index 677bb64..043730d 100644 --- a/client/src/resources/repair-order/RepairOrderEdit.tsx +++ b/client/src/resources/repair-order/RepairOrderEdit.tsx @@ -1,39 +1,39 @@ -import { Edit, SimpleForm, TextInput, SelectInput, ReferenceInput, AutocompleteInput } from 'react-admin'; +import { Edit, SimpleForm, TextInput, NumberInput, DateInput, SelectInput, ReferenceInput, AutocompleteInput } from 'react-admin'; const repairKindChoices = [ - { id: 'TO', name: 'TO' }, - { id: 'TR', name: 'TR' }, - { id: 'TRE', name: 'TRE' }, - { id: 'KR', name: 'KR' }, - { id: 'AR', name: 'AR' }, - { id: 'MP', name: 'MP' }, + { id: 'TO', name: 'Техническое обслуживание' }, + { id: 'TR', name: 'Текущий ремонт' }, + { id: 'TRE', name: 'Текущий расширенный ремонт' }, + { id: 'KR', name: 'Капитальный ремонт' }, + { id: 'AR', name: 'Аварийный ремонт' }, + { id: 'MP', name: 'Метрологическая поверка' }, ]; const statusChoices = [ - { id: 'Draft', name: 'Draft' }, - { id: 'Approved', name: 'Approved' }, - { id: 'InWork', name: 'InWork' }, - { id: 'Done', name: 'Done' }, - { id: 'Cancelled', name: 'Cancelled' }, + { id: 'Draft', name: 'Черновик' }, + { id: 'Approved', name: 'Утверждена' }, + { id: 'InWork', name: 'В работе' }, + { id: 'Done', name: 'Выполнена' }, + { id: 'Cancelled', name: 'Отменена' }, ]; export const RepairOrderEdit = () => ( - - - record.code ? `${record.code} — ${record.name ?? record.code}` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} /> + + + record.inventoryNumber ? `${record.inventoryNumber} — ${record.name ?? record.inventoryNumber}` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} /> - - - - - - - - - + + + + + + + + + ); diff --git a/client/src/resources/repair-order/RepairOrderList.tsx b/client/src/resources/repair-order/RepairOrderList.tsx index 3bf4717..0e1e9f3 100644 --- a/client/src/resources/repair-order/RepairOrderList.tsx +++ b/client/src/resources/repair-order/RepairOrderList.tsx @@ -18,33 +18,33 @@ import { } from 'react-admin'; const repairKindChoices = [ - { id: 'TO', name: 'TO' }, - { id: 'TR', name: 'TR' }, - { id: 'TRE', name: 'TRE' }, - { id: 'KR', name: 'KR' }, - { id: 'AR', name: 'AR' }, - { id: 'MP', name: 'MP' }, + { id: 'TO', name: 'Техническое обслуживание' }, + { id: 'TR', name: 'Текущий ремонт' }, + { id: 'TRE', name: 'Текущий расширенный ремонт' }, + { id: 'KR', name: 'Капитальный ремонт' }, + { id: 'AR', name: 'Аварийный ремонт' }, + { id: 'MP', name: 'Метрологическая поверка' }, ]; const statusChoices = [ - { id: 'Draft', name: 'Draft' }, - { id: 'Approved', name: 'Approved' }, - { id: 'InWork', name: 'InWork' }, - { id: 'Done', name: 'Done' }, - { id: 'Cancelled', name: 'Cancelled' }, + { id: 'Draft', name: 'Черновик' }, + { id: 'Approved', name: 'Утверждена' }, + { id: 'InWork', name: 'В работе' }, + { id: 'Done', name: 'Выполнена' }, + { id: 'Cancelled', name: 'Отменена' }, ]; const repairOrderFilters = [ , - , - - record.code ? `${record.code} — ${record.name ?? record.code}` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} /> + , + + record.inventoryNumber ? `${record.inventoryNumber} — ${record.name ?? record.inventoryNumber}` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} /> , - , - , - , - , - + , + , + , + , + ]; const RepairOrderListActions = () => ( @@ -56,22 +56,22 @@ const RepairOrderListActions = () => ( ); export const RepairOrderList = () => ( - } filters={repairOrderFilters} sort={{ field: 'id', order: 'ASC' }}> + } filters={repairOrderFilters} sort={{ field: 'number', order: 'ASC' }}> - - - + + + - - - - - - - - - + + + + + + + + + ); diff --git a/docker-compose.yml b/docker-compose.yml index b0a1a9e..c591e3b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,7 @@ services: POSTGRES_PASSWORD: postgres POSTGRES_DB: toir ports: - - "5433:5432" + - "5432:5432" volumes: - postgres_data:/var/lib/postgresql/data diff --git a/generation/generate.mjs b/generation/generate.mjs index 622eb80..280b0cb 100644 --- a/generation/generate.mjs +++ b/generation/generate.mjs @@ -74,14 +74,20 @@ function parseBlocks(text, kind) { function parseEnum(body) { const values = []; - const re = /^\s*value\s+([A-Za-z_][A-Za-z0-9_]*)\s*\{/gm; + const labels = {}; + const re = /value\s+([A-Za-z_][A-Za-z0-9_]*)\s*\{([\s\S]*?)\}/gm; let m; - while ((m = re.exec(body))) values.push(m[1]); - return { values }; + while ((m = re.exec(body))) { + values.push(m[1]); + const label = (m[2].match(/label\s+"([^"]+)"/m) || [])[1]; + labels[m[1]] = label || m[1]; + } + return { values, labels }; } function parseEntity(body) { const attrs = []; + const entityLabel = (body.match(/description\s+"([^"]+)"/m) || [])[1]; const lines = body.split(/\r?\n/); for (let i = 0; i < lines.length; i++) { const m = lines[i].match(/^\s*attribute\s+([A-Za-z_][A-Za-z0-9_]*)\s*\{\s*$/); @@ -106,10 +112,12 @@ function parseEntity(body) { const isPrimary = /^\s*key primary\s*;/m.test(abody); const defaultValue = (abody.match(/^\s*default\s+([A-Za-z_][A-Za-z0-9_]*)\s*;/m) || [])[1]; const foreignRel = (abody.match(/relates\s+([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)\s*;/m) || []).slice(1); + const description = (abody.match(/description\s+"([^"]+)"/m) || [])[1]; attrs.push({ name, type, + label: description || name, isRequired, isUnique, isPrimary, @@ -122,7 +130,7 @@ function parseEntity(body) { const pk = attrs.find((a) => a.isPrimary); if (!pk) throw new Error('Entity missing primary key attribute'); - return { attributes: attrs, primaryKey: pk.name }; + return { attributes: attrs, primaryKey: pk.name, label: entityLabel }; } function parseDomainDSL(dslText) { @@ -138,6 +146,45 @@ function parseDomainDSL(dslText) { return { enums, entities }; } +function getEntityAttrNames(entity) { + return new Set(entity.attributes.map((a) => a.name)); +} + +function getBestSortField(entity, pk) { + const attrs = getEntityAttrNames(entity); + if (attrs.has('inventoryNumber')) return 'inventoryNumber'; + if (attrs.has('number')) return 'number'; + if (attrs.has('code')) return 'code'; + if (attrs.has('name')) return 'name'; + return pk; +} + +function getReferenceDisplayExpr(foreignEntity) { + const attrs = getEntityAttrNames(foreignEntity); + if (attrs.has('inventoryNumber')) { + return "(record) => record.inventoryNumber ? `${record.inventoryNumber} — ${record.name ?? record.inventoryNumber}` : (record.name ?? record.id)"; + } + if (attrs.has('code')) { + return "(record) => record.code ? `${record.code} — ${record.name ?? record.code}` : (record.name ?? record.id)"; + } + if (attrs.has('number')) { + return "(record) => record.number ? `${record.number} — ${record.name ?? record.number}` : (record.name ?? record.id)"; + } + if (attrs.has('name')) { + return "(record) => record.name ?? record.id"; + } + return "(record) => record.id"; +} + +function getAttributeLabel(attr, allEntities) { + if (attr.label && attr.label !== attr.name) return attr.label; + if (attr.name === 'status') return 'Статус'; + if (attr.name === 'equipmentId') return 'Оборудование'; + if (attr.name === 'equipmentTypeCode') return 'Вид оборудования'; + if (attr.foreign) return allEntities[attr.foreign.entity]?.label || attr.name; + return attr.name; +} + function prismaScalarType(dslType) { switch (dslType) { case 'string': @@ -281,23 +328,44 @@ function renderBackendModule(entityName, entity, resourceName, pk) { if (pk !== 'id') updateDtoLines.push(` id?: string;`); for (const a of entity.attributes) { if (pk !== 'id' && a.name === 'id') continue; - updateDtoLines.push(` ${a.name}?: ${dtoType(a)} | null;`); + updateDtoLines.push(` ${a.name}?: ${dtoType(a)};`); } updateDtoLines.push('}'); const controller = `import { Controller, Get, Post, Patch, Delete, Param, Body, Query, Res } from '@nestjs/common';\nimport { Response } from 'express';\nimport { ${serviceName} } from './${folder}.service';\nimport { Create${className}Dto } from './dto/create-${folder}.dto';\nimport { Update${className}Dto } from './dto/update-${folder}.dto';\n\n@Controller('${resourceName}')\nexport class ${controllerName} {\n constructor(private readonly service: ${serviceName}) {}\n\n @Get()\n async findAll(@Query() query: any, @Res() res: Response) {\n const result = await this.service.findAll(query);\n res.set('Content-Range', \`${resourceName} \${query._start || 0}-\${query._end || result.total}/\${result.total}\`);\n res.set('Access-Control-Expose-Headers', 'Content-Range');\n return res.json(result.data);\n }\n\n @Get(':${pk}')\n findOne(@Param('${pk}') id: string) {\n return this.service.findOne(id);\n }\n\n @Post()\n create(@Body() dto: Create${className}Dto) {\n return this.service.create(dto);\n }\n\n @Patch(':${pk}')\n update(@Param('${pk}') id: string, @Body() dto: Update${className}Dto) {\n return this.service.update(id, dto);\n }\n\n @Delete(':${pk}')\n remove(@Param('${pk}') id: string) {\n return this.service.remove(id);\n }\n}\n`; - const service = `import { Injectable } from '@nestjs/common';\nimport { Prisma } from '@prisma/client';\nimport { PrismaService } from '../../prisma/prisma.service';\nimport { Create${className}Dto } from './dto/create-${folder}.dto';\nimport { Update${className}Dto } from './dto/update-${folder}.dto';\n\n@Injectable()\nexport class ${serviceName} {\n constructor(private readonly prisma: PrismaService) {}\n\n async findAll(query: { _start?: string; _end?: string; _sort?: string; _order?: string; [key: string]: any }) {\n const start = parseInt(query._start) || 0;\n const end = parseInt(query._end) || 10;\n const take = end - start;\n const skip = start;\n const sortField = query._sort || '${pk}';\n const sortOrder = (query._order || 'ASC').toLowerCase() as 'asc' | 'desc';\n\n const where: any = {};\n\n if (query.q) {\n const q = String(query.q);\n const ors: any[] = [];\n ${entity.attributes + const service = `import { Injectable } from '@nestjs/common';\nimport { Prisma } from '@prisma/client';\nimport { PrismaService } from '../../prisma/prisma.service';\nimport { Create${className}Dto } from './dto/create-${folder}.dto';\nimport { Update${className}Dto } from './dto/update-${folder}.dto';\n\nfunction serializeRecord(record: any) {\n return {\n ...record,\n${entity.attributes + .filter((a) => a.type === 'decimal') + .map((a) => ` ${a.name}: record.${a.name}?.toString() ?? null,`) + .join('\n')}\n${entity.attributes + .filter((a) => a.type === 'date') + .map((a) => ` ${a.name}: record.${a.name}?.toISOString() ?? null,`) + .join('\n')}\n };\n}\n\n@Injectable()\nexport class ${serviceName} {\n constructor(private readonly prisma: PrismaService) {}\n\n async findAll(query: { _start?: string; _end?: string; _sort?: string; _order?: string; [key: string]: any }) {\n const start = parseInt(query._start) || 0;\n const end = parseInt(query._end) || 10;\n const take = end - start;\n const skip = start;\n const sortField = query._sort || '${getBestSortField(entity, pk)}';\n const sortOrder = (query._order || 'ASC').toLowerCase() as 'asc' | 'desc';\n\n const where: any = {};\n\n if (query.q) {\n const q = String(query.q);\n const ors: any[] = [];\n ${entity.attributes .filter((a) => ['string', 'text'].includes(a.type)) .slice(0, 6) .map((a) => `ors.push({ ${a.name}: { contains: q, mode: 'insensitive' } });`) .join('\n ')}\n if (ors.length) where.OR = ors;\n }\n\n ${entity.attributes - .filter((a) => ['string', 'text'].includes(a.type)) + .filter((a) => ['string', 'text'].includes(a.type) && !a.foreign) .map((a) => `if (query.${a.name}) where.${a.name} = { contains: query.${a.name}, mode: 'insensitive' };`) + .join('\n ')}\n\n ${entity.attributes + .filter((a) => a.foreign) + .map((a) => `if (query.${a.name}) where.${a.name} = query.${a.name};`) .join('\n ')}\n\n // Enum multi-value support (e.g. status=A&status=B)\n ${entity.attributes .filter((a) => !['string', 'text', 'uuid', 'integer', 'decimal', 'date'].includes(a.type)) .map((a) => `if (query.${a.name}) { const vals = Array.isArray(query.${a.name}) ? query.${a.name} : [query.${a.name}]; where.${a.name} = vals.length > 1 ? { in: vals } : vals[0]; }`) - .join('\n ')}\n\n if (query.id) {\n const ids = Array.isArray(query.id) ? query.id : [query.id];\n where.${pk} = { in: ids };\n }\n\n const [data, total] = await Promise.all([\n this.prisma.${lowerFirst(className)}.findMany({ where, skip, take, orderBy: { [sortField]: sortOrder } }),\n this.prisma.${lowerFirst(className)}.count({ where }),\n ]);\n\n const mapped = ${pk === 'id' ? 'data' : `data.map((r: any) => ({ id: r.${pk}, ...r }))`};\n return { data: mapped, total };\n }\n\n async findOne(id: string) {\n const record = await this.prisma.${lowerFirst(className)}.findUniqueOrThrow({ where: { ${pk}: id } as any });\n return ${pk === 'id' ? 'record' : `{ id: (record as any).${pk}, ...record }`};\n }\n\n async create(dto: Create${className}Dto) {\n const record = await this.prisma.${lowerFirst(className)}.create({ data: dto as any });\n return ${pk === 'id' ? 'record' : `{ id: (record as any).${pk}, ...record }`};\n }\n\n async update(id: string, dto: Update${className}Dto) {\n const data: any = { ...(dto as any) };\n delete data.id;\n delete data.${pk};\n const record = await this.prisma.${lowerFirst(className)}.update({ where: { ${pk}: id } as any, data });\n return ${pk === 'id' ? 'record' : `{ id: (record as any).${pk}, ...record }`};\n }\n\n async remove(id: string) {\n const record = await this.prisma.${lowerFirst(className)}.delete({ where: { ${pk}: id } as any });\n return ${pk === 'id' ? 'record' : `{ id: (record as any).${pk}, ...record }`};\n }\n}\n`; + .join('\n ')}\n\n if (query.id) {\n const ids = Array.isArray(query.id) ? query.id : [query.id];\n where.${pk} = { in: ids };\n }\n\n const [data, total] = await Promise.all([\n this.prisma.${lowerFirst(className)}.findMany({ where, skip, take, orderBy: { [sortField]: sortOrder } }),\n this.prisma.${lowerFirst(className)}.count({ where }),\n ]);\n\n const mapped = ${pk === 'id' ? 'data.map(serializeRecord)' : `data.map((r: any) => ({ id: r.${pk}, ...serializeRecord(r) }))`};\n return { data: mapped, total };\n }\n\n async findOne(id: string) {\n const record = await this.prisma.${lowerFirst(className)}.findUniqueOrThrow({ where: { ${pk}: id } as any });\n return ${pk === 'id' ? 'serializeRecord(record)' : `{ id: (record as any).${pk}, ...serializeRecord(record) }`};\n }\n\n async create(dto: Create${className}Dto) {\n const data: any = { ...(dto as any) };\n${entity.attributes + .filter((a) => a.type === 'date') + .map((a) => ` if (data.${a.name}) data.${a.name} = new Date(data.${a.name});`) + .join('\n')}\n${entity.attributes + .filter((a) => a.type === 'decimal') + .map((a) => ` if (data.${a.name}) data.${a.name} = new Prisma.Decimal(data.${a.name});`) + .join('\n')}\n\n const record = await this.prisma.${lowerFirst(className)}.create({ data });\n return ${pk === 'id' ? 'serializeRecord(record)' : `{ id: (record as any).${pk}, ...serializeRecord(record) }`};\n }\n\n async update(id: string, dto: Update${className}Dto) {\n const data: any = { ...(dto as any) };\n delete data.id;\n delete data.${pk};\n${entity.attributes + .filter((a) => a.type === 'date') + .map((a) => ` if (data.${a.name}) data.${a.name} = new Date(data.${a.name});`) + .join('\n')}\n${entity.attributes + .filter((a) => a.type === 'decimal') + .map((a) => ` if (data.${a.name} !== undefined && data.${a.name} !== null) data.${a.name} = new Prisma.Decimal(data.${a.name});`) + .join('\n')}\n\n const record = await this.prisma.${lowerFirst(className)}.update({ where: { ${pk}: id } as any, data });\n return ${pk === 'id' ? 'serializeRecord(record)' : `{ id: (record as any).${pk}, ...serializeRecord(record) }`};\n }\n\n async remove(id: string) {\n const record = await this.prisma.${lowerFirst(className)}.delete({ where: { ${pk}: id } as any });\n return ${pk === 'id' ? 'serializeRecord(record)' : `{ id: (record as any).${pk}, ...serializeRecord(record) }`};\n }\n}\n`; const mod = `import { Module } from '@nestjs/common';\nimport { ${controllerName} } from './${folder}.controller';\nimport { ${serviceName} } from './${folder}.service';\n\n@Module({\n controllers: [${controllerName}],\n providers: [${serviceName}],\n})\nexport class ${moduleName} {}\n`; @@ -315,7 +383,7 @@ function renderBackendModule(entityName, entity, resourceName, pk) { }; } -function renderFrontendResource(entityName, entity, resourceName, pk, enums) { +function renderFrontendResource(entityName, entity, resourceName, pk, enums, allEntities) { const folder = toKebab(entityName); const className = entityName; const enumAttrs = entity.attributes.filter( @@ -325,6 +393,7 @@ function renderFrontendResource(entityName, entity, resourceName, pk, enums) { const identBase = toIdentifierFromKebab(folder); const filtersIdent = `${identBase}Filters`; + const sortField = getBestSortField(entity, pk); const hasNumber = entity.attributes.some((a) => ['integer', 'decimal'].includes(a.type)); const hasDate = entity.attributes.some((a) => a.type === 'date'); @@ -357,14 +426,15 @@ function renderFrontendResource(entityName, entity, resourceName, pk, enums) { for (const a of enumAttrs) { const enumName = a.type; const values = enums?.[enumName]?.values ?? []; + const labels = enums?.[enumName]?.labels ?? {}; const constName = `${a.name}Choices`; if (a.name === 'status') { choiceConsts.push( - `const statusChoices = [\n${values.map((v) => ` { id: '${v}', name: '${v}' },`).join('\n')}\n];\n` + `const statusChoices = [\n${values.map((v) => ` { id: '${v}', name: '${labels[v] ?? v}' },`).join('\n')}\n];\n` ); } else { choiceConsts.push( - `const ${constName} = [\n${values.map((v) => ` { id: '${v}', name: '${v}' },`).join('\n')}\n];\n` + `const ${constName} = [\n${values.map((v) => ` { id: '${v}', name: '${labels[v] ?? v}' },`).join('\n')}\n];\n` ); } } @@ -375,66 +445,82 @@ function renderFrontendResource(entityName, entity, resourceName, pk, enums) { filterInputs.push(``); } for (const a of entity.attributes) { + const label = getAttributeLabel(a, allEntities); if (a.name === pk) continue; if (a.foreign) { + const referenceDisplay = getReferenceDisplayExpr(allEntities[a.foreign.entity]); filterInputs.push( - `\n record.code ? \`\${record.code} — \${record.name ?? record.code}\` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} />\n ` + `\n ({ q: searchText })} />\n ` ); continue; } if (['string', 'text', 'uuid'].includes(a.type)) { - filterInputs.push(``); + filterInputs.push(``); continue; } if (['integer', 'decimal'].includes(a.type)) continue; if (a.type === 'date') continue; // enum if (a.name === 'status') { - filterInputs.push(``); + filterInputs.push(``); } else { - filterInputs.push(``); + filterInputs.push(``); } } const listFields = []; for (const a of entity.attributes) { + const label = getAttributeLabel(a, allEntities); if (a.foreign) { + const referenceEntity = allEntities[a.foreign.entity]; + const referenceAttrs = getEntityAttrNames(referenceEntity); + const fieldSource = referenceAttrs.has('inventoryNumber') + ? 'inventoryNumber' + : referenceAttrs.has('code') + ? 'code' + : referenceAttrs.has('number') + ? 'number' + : 'name'; listFields.push( - `\n \n ` + `\n \n ` ); continue; } if (a.type === 'date') { - listFields.push(``); + listFields.push(``); } else if (['integer', 'decimal'].includes(a.type)) { - listFields.push(``); + listFields.push(``); } else if (!['string', 'text', 'uuid', 'integer', 'decimal', 'date'].includes(a.type)) { - listFields.push(``); + listFields.push(``); } else { - listFields.push(``); + listFields.push(``); } } - const list = `import {\n ${listImports.join(',\n ')}\n} from 'react-admin';\n\n${choiceConsts.join('\n')}\nconst ${filtersIdent} = [\n ${filterInputs.join(',\n ')}\n];\n\nconst ${className}ListActions = () => (\n \n \n \n \n \n);\n\nexport const ${className}List = () => (\n } filters={${filtersIdent}} sort={{ field: '${pk}', order: 'ASC' }}>\n \n ${listFields.join('\n ')}\n \n \n);\n`; + const list = `import {\n ${listImports.join(',\n ')}\n} from 'react-admin';\n\n${choiceConsts.join('\n')}\nconst ${filtersIdent} = [\n ${filterInputs.join(',\n ')}\n];\n\nconst ${className}ListActions = () => (\n \n \n \n \n \n);\n\nexport const ${className}List = () => (\n } filters={${filtersIdent}} sort={{ field: '${sortField}', order: 'ASC' }}>\n \n ${listFields.join('\n ')}\n \n \n);\n`; const formField = (a, mode) => { + const label = getAttributeLabel(a, allEntities); if (a.isPrimary && mode === 'create' && a.type === 'uuid') return null; if (a.isPrimary && mode === 'edit') { - return ``; + return ``; } if (a.foreign) { - return `\n record.code ? \`\${record.code} — \${record.name ?? record.code}\` : (record.name ?? record.id)} filterToQuery={(searchText) => ({ q: searchText })} />\n `; + const referenceDisplay = getReferenceDisplayExpr(allEntities[a.foreign.entity]); + return `\n ({ q: searchText })} />\n `; } - if (a.type === 'date') return ``; - if (['integer', 'decimal'].includes(a.type)) return ``; + if (a.type === 'date') return ``; + if (['integer', 'decimal'].includes(a.type)) return ``; if (!['string', 'text', 'uuid', 'integer', 'decimal', 'date'].includes(a.type)) { - if (a.name === 'status' && statusEnumAttr) return ``; - return ``; + if (a.name === 'status' && statusEnumAttr) return ``; + return ``; } - return ``; + return ``; }; const formImportSet = new Set(['SimpleForm', 'TextInput']); + if (hasNumber) formImportSet.add('NumberInput'); + if (hasDate) formImportSet.add('DateInput'); if (enumAttrs.length) formImportSet.add('SelectInput'); if (hasFK) { formImportSet.add('ReferenceInput'); @@ -474,10 +560,15 @@ function ensureAppModule(apply, backendModules) { let out = src; for (const m of backendModules) { if (!out.includes(`import { ${m.moduleName} }`)) { - out = out.replace( - /import\s+\{\s*RepairOrderModule\s*\}[^;]*;\s*/m, - (x) => `${x}import { ${m.moduleName} } from '${m.importPath}';\n` - ); + const importLine = `import { ${m.moduleName} } from '${m.importPath}';`; + const importMatches = [...out.matchAll(/^import\s+.*;$/gm)]; + if (importMatches.length) { + const lastImport = importMatches[importMatches.length - 1]; + const insertAt = lastImport.index + lastImport[0].length; + out = `${out.slice(0, insertAt)}\n${importLine}${out.slice(insertAt)}`; + } else { + out = `${importLine}\n${out}`; + } } } out = out.replace(/imports:\s*\[\s*([\s\S]*?)\s*\],/m, (match, inner) => { @@ -504,7 +595,14 @@ function ensureClientApp(apply, frontendResources) { ]; for (const imp of imports) { if (!out.includes(imp)) { - out = out.replace(/import\s+\{\s*RepairOrderShow\s*\}[^;]*;\s*/m, (x) => `${x}\n${imports.join('\n')}\n`); + const importMatches = [...out.matchAll(/^import\s+.*;$/gm)]; + if (importMatches.length) { + const lastImport = importMatches[importMatches.length - 1]; + const insertAt = lastImport.index + lastImport[0].length; + out = `${out.slice(0, insertAt)}\n${imports.join('\n')}${out.slice(insertAt)}`; + } else { + out = `${imports.join('\n')}\n${out}`; + } break; } } @@ -540,7 +638,7 @@ function main() { const pk = ent.primaryKey; const resource = pluralize(toKebab(entityName)); const be = renderBackendModule(entityName, ent, resource, pk); - const fe = renderFrontendResource(entityName, ent, resource, pk, parsed.enums); + const fe = renderFrontendResource(entityName, ent, resource, pk, parsed.enums, parsed.entities); backendModules.push(be); frontendResources.push(fe); diff --git a/server/src/modules/equipment-type/dto/update-equipment-type.dto.ts b/server/src/modules/equipment-type/dto/update-equipment-type.dto.ts index 441980e..7589324 100644 --- a/server/src/modules/equipment-type/dto/update-equipment-type.dto.ts +++ b/server/src/modules/equipment-type/dto/update-equipment-type.dto.ts @@ -1,8 +1,8 @@ export class UpdateEquipmentTypeDto { id?: string; - code?: string | null; - name?: string | null; - manufacturer?: string | null; - maintenanceIntervalHours?: number | null; - overhaulIntervalHours?: number | null; + code?: string; + name?: string; + manufacturer?: string; + maintenanceIntervalHours?: number; + overhaulIntervalHours?: number; } diff --git a/server/src/modules/equipment-type/equipment-type.service.ts b/server/src/modules/equipment-type/equipment-type.service.ts index f564c21..cd76f52 100644 --- a/server/src/modules/equipment-type/equipment-type.service.ts +++ b/server/src/modules/equipment-type/equipment-type.service.ts @@ -4,6 +4,14 @@ import { PrismaService } from '../../prisma/prisma.service'; import { CreateEquipmentTypeDto } from './dto/create-equipment-type.dto'; import { UpdateEquipmentTypeDto } from './dto/update-equipment-type.dto'; +function serializeRecord(record: any) { + return { + ...record, + + + }; +} + @Injectable() export class EquipmentTypeService { constructor(private readonly prisma: PrismaService) {} @@ -31,6 +39,8 @@ export class EquipmentTypeService { if (query.name) where.name = { contains: query.name, mode: 'insensitive' }; if (query.manufacturer) where.manufacturer = { contains: query.manufacturer, mode: 'insensitive' }; + + // Enum multi-value support (e.g. status=A&status=B) @@ -44,30 +54,37 @@ export class EquipmentTypeService { this.prisma.equipmentType.count({ where }), ]); - const mapped = data.map((r: any) => ({ id: r.code, ...r })); + const mapped = data.map((r: any) => ({ id: r.code, ...serializeRecord(r) })); return { data: mapped, total }; } async findOne(id: string) { const record = await this.prisma.equipmentType.findUniqueOrThrow({ where: { code: id } as any }); - return { id: (record as any).code, ...record }; + return { id: (record as any).code, ...serializeRecord(record) }; } async create(dto: CreateEquipmentTypeDto) { - const record = await this.prisma.equipmentType.create({ data: dto as any }); - return { id: (record as any).code, ...record }; + const data: any = { ...(dto as any) }; + + + + const record = await this.prisma.equipmentType.create({ data }); + return { id: (record as any).code, ...serializeRecord(record) }; } async update(id: string, dto: UpdateEquipmentTypeDto) { const data: any = { ...(dto as any) }; delete data.id; delete data.code; + + + const record = await this.prisma.equipmentType.update({ where: { code: id } as any, data }); - return { id: (record as any).code, ...record }; + return { id: (record as any).code, ...serializeRecord(record) }; } async remove(id: string) { const record = await this.prisma.equipmentType.delete({ where: { code: id } as any }); - return { id: (record as any).code, ...record }; + return { id: (record as any).code, ...serializeRecord(record) }; } } diff --git a/server/src/modules/equipment/dto/update-equipment.dto.ts b/server/src/modules/equipment/dto/update-equipment.dto.ts index 83b599f..a44c3c9 100644 --- a/server/src/modules/equipment/dto/update-equipment.dto.ts +++ b/server/src/modules/equipment/dto/update-equipment.dto.ts @@ -1,14 +1,14 @@ export class UpdateEquipmentDto { - id?: string | null; - inventoryNumber?: string | null; - serialNumber?: string | null; - name?: string | null; - equipmentTypeCode?: string | null; - status?: string | null; - location?: string | null; - commissionedAt?: string | null; - totalEngineHours?: string | null; - engineHoursSinceLastRepair?: string | null; - lastRepairAt?: string | null; - notes?: string | null; + id?: string; + inventoryNumber?: string; + serialNumber?: string; + name?: string; + equipmentTypeCode?: string; + status?: string; + location?: string; + commissionedAt?: string; + totalEngineHours?: string; + engineHoursSinceLastRepair?: string; + lastRepairAt?: string; + notes?: string; } diff --git a/server/src/modules/equipment/equipment.service.ts b/server/src/modules/equipment/equipment.service.ts index 3d05add..9459f87 100644 --- a/server/src/modules/equipment/equipment.service.ts +++ b/server/src/modules/equipment/equipment.service.ts @@ -4,6 +4,16 @@ import { PrismaService } from '../../prisma/prisma.service'; import { CreateEquipmentDto } from './dto/create-equipment.dto'; import { UpdateEquipmentDto } from './dto/update-equipment.dto'; +function serializeRecord(record: any) { + return { + ...record, + totalEngineHours: record.totalEngineHours?.toString() ?? null, + engineHoursSinceLastRepair: record.engineHoursSinceLastRepair?.toString() ?? null, + commissionedAt: record.commissionedAt?.toISOString() ?? null, + lastRepairAt: record.lastRepairAt?.toISOString() ?? null, + }; +} + @Injectable() export class EquipmentService { constructor(private readonly prisma: PrismaService) {} @@ -13,7 +23,7 @@ export class EquipmentService { const end = parseInt(query._end) || 10; const take = end - start; const skip = start; - const sortField = query._sort || 'id'; + const sortField = query._sort || 'inventoryNumber'; const sortOrder = (query._order || 'ASC').toLowerCase() as 'asc' | 'desc'; const where: any = {}; @@ -33,10 +43,11 @@ export class EquipmentService { if (query.inventoryNumber) where.inventoryNumber = { contains: query.inventoryNumber, mode: 'insensitive' }; if (query.serialNumber) where.serialNumber = { contains: query.serialNumber, mode: 'insensitive' }; if (query.name) where.name = { contains: query.name, mode: 'insensitive' }; - if (query.equipmentTypeCode) where.equipmentTypeCode = { contains: query.equipmentTypeCode, mode: 'insensitive' }; if (query.location) where.location = { contains: query.location, mode: 'insensitive' }; if (query.notes) where.notes = { contains: query.notes, mode: 'insensitive' }; + if (query.equipmentTypeCode) where.equipmentTypeCode = query.equipmentTypeCode; + // Enum multi-value support (e.g. status=A&status=B) if (query.status) { const vals = Array.isArray(query.status) ? query.status : [query.status]; where.status = vals.length > 1 ? { in: vals } : vals[0]; } @@ -50,30 +61,41 @@ export class EquipmentService { this.prisma.equipment.count({ where }), ]); - const mapped = data; + const mapped = data.map(serializeRecord); return { data: mapped, total }; } async findOne(id: string) { const record = await this.prisma.equipment.findUniqueOrThrow({ where: { id: id } as any }); - return record; + return serializeRecord(record); } async create(dto: CreateEquipmentDto) { - const record = await this.prisma.equipment.create({ data: dto as any }); - return record; + const data: any = { ...(dto as any) }; + if (data.commissionedAt) data.commissionedAt = new Date(data.commissionedAt); + if (data.lastRepairAt) data.lastRepairAt = new Date(data.lastRepairAt); + if (data.totalEngineHours) data.totalEngineHours = new Prisma.Decimal(data.totalEngineHours); + if (data.engineHoursSinceLastRepair) data.engineHoursSinceLastRepair = new Prisma.Decimal(data.engineHoursSinceLastRepair); + + const record = await this.prisma.equipment.create({ data }); + return serializeRecord(record); } async update(id: string, dto: UpdateEquipmentDto) { const data: any = { ...(dto as any) }; delete data.id; delete data.id; + if (data.commissionedAt) data.commissionedAt = new Date(data.commissionedAt); + if (data.lastRepairAt) data.lastRepairAt = new Date(data.lastRepairAt); + if (data.totalEngineHours !== undefined && data.totalEngineHours !== null) data.totalEngineHours = new Prisma.Decimal(data.totalEngineHours); + if (data.engineHoursSinceLastRepair !== undefined && data.engineHoursSinceLastRepair !== null) data.engineHoursSinceLastRepair = new Prisma.Decimal(data.engineHoursSinceLastRepair); + const record = await this.prisma.equipment.update({ where: { id: id } as any, data }); - return record; + return serializeRecord(record); } async remove(id: string) { const record = await this.prisma.equipment.delete({ where: { id: id } as any }); - return record; + return serializeRecord(record); } } diff --git a/server/src/modules/repair-order/dto/update-repair-order.dto.ts b/server/src/modules/repair-order/dto/update-repair-order.dto.ts index 3609ea1..e42bcb1 100644 --- a/server/src/modules/repair-order/dto/update-repair-order.dto.ts +++ b/server/src/modules/repair-order/dto/update-repair-order.dto.ts @@ -1,14 +1,14 @@ export class UpdateRepairOrderDto { - id?: string | null; - number?: string | null; - equipmentId?: string | null; - repairKind?: string | null; - status?: string | null; - plannedAt?: string | null; - startedAt?: string | null; - completedAt?: string | null; - contractor?: string | null; - engineHoursAtRepair?: string | null; - description?: string | null; - notes?: string | null; + id?: string; + number?: string; + equipmentId?: string; + repairKind?: string; + status?: string; + plannedAt?: string; + startedAt?: string; + completedAt?: string; + contractor?: string; + engineHoursAtRepair?: string; + description?: string; + notes?: string; } diff --git a/server/src/modules/repair-order/repair-order.service.ts b/server/src/modules/repair-order/repair-order.service.ts index 68e4127..bcc0517 100644 --- a/server/src/modules/repair-order/repair-order.service.ts +++ b/server/src/modules/repair-order/repair-order.service.ts @@ -4,6 +4,16 @@ import { PrismaService } from '../../prisma/prisma.service'; import { CreateRepairOrderDto } from './dto/create-repair-order.dto'; import { UpdateRepairOrderDto } from './dto/update-repair-order.dto'; +function serializeRecord(record: any) { + return { + ...record, + engineHoursAtRepair: record.engineHoursAtRepair?.toString() ?? null, + plannedAt: record.plannedAt?.toISOString() ?? null, + startedAt: record.startedAt?.toISOString() ?? null, + completedAt: record.completedAt?.toISOString() ?? null, + }; +} + @Injectable() export class RepairOrderService { constructor(private readonly prisma: PrismaService) {} @@ -13,7 +23,7 @@ export class RepairOrderService { const end = parseInt(query._end) || 10; const take = end - start; const skip = start; - const sortField = query._sort || 'id'; + const sortField = query._sort || 'number'; const sortOrder = (query._order || 'ASC').toLowerCase() as 'asc' | 'desc'; const where: any = {}; @@ -33,6 +43,8 @@ export class RepairOrderService { if (query.description) where.description = { contains: query.description, mode: 'insensitive' }; if (query.notes) where.notes = { contains: query.notes, mode: 'insensitive' }; + if (query.equipmentId) where.equipmentId = query.equipmentId; + // Enum multi-value support (e.g. status=A&status=B) if (query.repairKind) { const vals = Array.isArray(query.repairKind) ? query.repairKind : [query.repairKind]; where.repairKind = vals.length > 1 ? { in: vals } : vals[0]; } if (query.status) { const vals = Array.isArray(query.status) ? query.status : [query.status]; where.status = vals.length > 1 ? { in: vals } : vals[0]; } @@ -47,30 +59,41 @@ export class RepairOrderService { this.prisma.repairOrder.count({ where }), ]); - const mapped = data; + const mapped = data.map(serializeRecord); return { data: mapped, total }; } async findOne(id: string) { const record = await this.prisma.repairOrder.findUniqueOrThrow({ where: { id: id } as any }); - return record; + return serializeRecord(record); } async create(dto: CreateRepairOrderDto) { - const record = await this.prisma.repairOrder.create({ data: dto as any }); - return record; + const data: any = { ...(dto as any) }; + if (data.plannedAt) data.plannedAt = new Date(data.plannedAt); + if (data.startedAt) data.startedAt = new Date(data.startedAt); + if (data.completedAt) data.completedAt = new Date(data.completedAt); + if (data.engineHoursAtRepair) data.engineHoursAtRepair = new Prisma.Decimal(data.engineHoursAtRepair); + + const record = await this.prisma.repairOrder.create({ data }); + return serializeRecord(record); } async update(id: string, dto: UpdateRepairOrderDto) { const data: any = { ...(dto as any) }; delete data.id; delete data.id; + if (data.plannedAt) data.plannedAt = new Date(data.plannedAt); + if (data.startedAt) data.startedAt = new Date(data.startedAt); + if (data.completedAt) data.completedAt = new Date(data.completedAt); + if (data.engineHoursAtRepair !== undefined && data.engineHoursAtRepair !== null) data.engineHoursAtRepair = new Prisma.Decimal(data.engineHoursAtRepair); + const record = await this.prisma.repairOrder.update({ where: { id: id } as any, data }); - return record; + return serializeRecord(record); } async remove(id: string) { const record = await this.prisma.repairOrder.delete({ where: { id: id } as any }); - return record; + return serializeRecord(record); } }