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 }; } }