From dd8ea8539bd55cb2ded92ae3d42c5ef44dbd9e6c Mon Sep 17 00:00:00 2001 From: enlineawork Date: Mon, 7 Sep 2026 08:25:14 -0300 Subject: [PATCH] F2.3: crear servicio de hallazgos desde Inventario --- .../field-findings.service.ts | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 api-v3/src/inspection-visits/field-findings.service.ts diff --git a/api-v3/src/inspection-visits/field-findings.service.ts b/api-v3/src/inspection-visits/field-findings.service.ts new file mode 100644 index 0000000..4f9b818 --- /dev/null +++ b/api-v3/src/inspection-visits/field-findings.service.ts @@ -0,0 +1,107 @@ +import { ConflictException, Injectable } from '@nestjs/common'; +import { DataSource } from 'typeorm'; +import type { AuthPrincipal, RequestWithContext } from '../common/http/request-context'; +import { FindingCatalogService } from '../inspection-findings/finding-catalog.service'; +import type { CreateInspectionFindingDto } from '../inspection-findings/dto/create-inspection-finding.dto'; +import { InspectionFindingsService } from '../inspection-findings/inspection-findings.service'; +import type { CreateFieldFindingDto } from './dto/create-field-finding.dto'; +import { FieldInventoryService } from './field-inventory.service'; + +interface DraftActRow { + id: string; + code: string; + status: string; +} + +@Injectable() +export class FieldFindingsService { + constructor( + private readonly dataSource: DataSource, + private readonly fieldInventory: FieldInventoryService, + private readonly catalog: FindingCatalogService, + private readonly findings: InspectionFindingsService, + ) {} + + async options(visitId: string, assetId: string, principal: AuthPrincipal) { + const gate = await this.fieldInventory.requireReadyForFinding(visitId, assetId, principal); + const act = await this.requireDraftAct(visitId); + const [catalog, findings] = await Promise.all([ + this.catalog.listApplicableForAsset(assetId, {}), + this.findings.listForAct(act.id), + ]); + + return { + context: gate.context, + act, + capture: gate.capture, + catalog, + findings: findings.data.filter((finding) => finding.assetId === assetId), + canAddAnother: true, + }; + } + + async list(visitId: string, assetId: string, principal: AuthPrincipal) { + const gate = await this.fieldInventory.requireReadyForFinding(visitId, assetId, principal); + const act = await this.requireDraftAct(visitId); + const findings = await this.findings.listForAct(act.id); + return { + context: gate.context, + act, + capture: gate.capture, + data: findings.data.filter((finding) => finding.assetId === assetId), + }; + } + + async create( + visitId: string, + assetId: string, + dto: CreateFieldFindingDto, + principal: AuthPrincipal, + request: RequestWithContext, + ) { + const gate = await this.fieldInventory.requireReadyForFinding(visitId, assetId, principal); + const act = await this.requireDraftAct(visitId); + const payload: CreateInspectionFindingDto = { + assetId, + catalogItemId: dto.catalogItemId ?? null, + customTitle: dto.customTitle ?? null, + customLegalBasis: dto.customLegalBasis ?? null, + description: dto.description, + severity: dto.severity, + correctionDueOn: dto.correctionDueOn ?? null, + }; + const finding = await this.findings.create(act.id, payload, principal, request); + return { + context: gate.context, + act, + capture: gate.capture, + finding, + canAddAnother: true, + }; + } + + private async requireDraftAct(visitId: string): Promise { + const rows = await this.dataSource.query(` + SELECT id, code, status + FROM inspection_acts + WHERE visit_id = $1::uuid + AND status = 'DRAFT' + ORDER BY created_at DESC, id DESC + LIMIT 2 + `, [visitId]) as DraftActRow[]; + + if (rows.length === 0) { + throw new ConflictException({ + code: 'FIELD_FINDING_DRAFT_ACT_REQUIRED', + message: 'La inspección no tiene un Acta borrador abierta para registrar hallazgos', + }); + } + if (rows.length > 1) { + throw new ConflictException({ + code: 'FIELD_FINDING_MULTIPLE_DRAFT_ACTS', + message: 'La inspección tiene más de un Acta borrador. Debe resolverse antes de continuar', + }); + } + return rows[0]; + } +}