From 88e7e8de65c059f5002bb13653eaaaac12def5f2 Mon Sep 17 00:00:00 2001 From: enlineawork Date: Thu, 10 Sep 2026 21:32:12 -0300 Subject: [PATCH] feat(api): scope preventive candidates to inspection yacimiento --- ...nspection-preventive-candidates.service.ts | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 api-v3/src/inspection-visits/inspection-preventive-candidates.service.ts diff --git a/api-v3/src/inspection-visits/inspection-preventive-candidates.service.ts b/api-v3/src/inspection-visits/inspection-preventive-candidates.service.ts new file mode 100644 index 0000000..ecbbdc7 --- /dev/null +++ b/api-v3/src/inspection-visits/inspection-preventive-candidates.service.ts @@ -0,0 +1,77 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { DataSource } from 'typeorm'; + +export interface InspectionPreventiveCandidate { + id: string; + code: string; + name: string; + typeName: string; +} + +@Injectable() +export class InspectionPreventiveCandidatesService { + constructor(private readonly dataSource: DataSource) {} + + async list(visitId: string, search?: string): Promise<{ data: InspectionPreventiveCandidate[] }> { + const [context] = (await this.dataSource.query(` + SELECT + visit.operational_area_id AS "operationalAreaId", + COALESCE(visit.scope_asset_id, visit.operational_area_id) AS "scopeAssetId" + FROM inspection_visits visit + WHERE visit.id = $1::uuid + `, [visitId])) as Array<{ + operationalAreaId: string | null; + scopeAssetId: string | null; + }>; + + if (!context) { + throw new NotFoundException({ + code: 'INSPECTION_VISIT_NOT_FOUND', + message: 'Visita de inspección no encontrada', + }); + } + + if (!context.operationalAreaId || !context.scopeAssetId) return { data: [] }; + + const term = search?.trim().slice(0, 200) ?? ''; + const rows = (await this.dataSource.query(` + WITH RECURSIVE scope_tree AS ( + SELECT root.id + FROM assets root + WHERE root.id = $2::uuid + UNION ALL + SELECT child.id + FROM assets child + INNER JOIN scope_tree parent_scope ON parent_scope.id = child.parent_id + ) + SELECT + asset.id, + asset.code, + asset.name, + asset_type.name AS "typeName" + FROM scope_tree + INNER JOIN assets asset ON asset.id = scope_tree.id + INNER JOIN asset_types asset_type ON asset_type.id = asset.asset_type_id + WHERE asset.id <> $2::uuid + AND asset.operational_area_id = $3::uuid + AND asset.information_status <> 'INACTIVE' + AND lower(asset_type.code) IN ('instalacion', 'subinstalacion') + AND NOT EXISTS ( + SELECT 1 + FROM inspection_visit_assets linked + WHERE linked.visit_id = $1::uuid + AND linked.asset_id = asset.id + ) + AND ( + $4::text = '' + OR asset.code ILIKE '%' || $4::text || '%' + OR asset.name ILIKE '%' || $4::text || '%' + OR COALESCE(asset.common_name, '') ILIKE '%' || $4::text || '%' + ) + ORDER BY asset_type.name, asset.name, asset.code + LIMIT 100 + `, [visitId, context.scopeAssetId, context.operationalAreaId, term])) as InspectionPreventiveCandidate[]; + + return { data: rows }; + } +}