import { Injectable, NotFoundException } from '@nestjs/common'; import { DataSource } from 'typeorm'; import type { ListFindingCatalogQueryDto } from './dto/list-finding-catalog-query.dto'; import { FindingCatalogService } from './finding-catalog.service'; @Injectable() export class F3FindingCatalogResolverService { constructor( private readonly dataSource: DataSource, private readonly contextualCatalog: FindingCatalogService, ) {} async listApplicableForAsset(assetId: string, query: ListFindingCatalogQueryDto) { const [asset] = await this.dataSource.query(` SELECT asset.id,asset.code,asset.name, type.code AS "typeCode", asset.inventory_family_id AS "familyId", family.code AS "familyCode",family.name AS "familyName",family.level AS "familyLevel", family.is_active AS "familyActive" FROM assets asset JOIN asset_types type ON type.id=asset.asset_type_id LEFT JOIN inventory_families family ON family.id=asset.inventory_family_id WHERE asset.id=$1::uuid `, [assetId]) as Array<{ id: string; code: string; name: string; typeCode: string; familyId: string | null; familyCode: string | null; familyName: string | null; familyLevel: string | null; familyActive: boolean | null; }>; if (!asset) { throw new NotFoundException({ code: 'ASSET_NOT_FOUND', message: 'Registro de Inventario no encontrado' }); } const other = { enabled: true, code: 'OTHER', label: 'OTROS', help: 'Usalo cuando el Hallazgo no exista entre los asociados a este Inventario. Quedará registrado para revisión en oficina.', }; if (asset.typeCode.toLowerCase() === 'yacimiento') { const contextual = await this.contextualCatalog.listApplicableForAsset(assetId, query); return { ...contextual, inventoryFamily: null, catalogSource: 'ASSET_TYPE' as const, configurationReason: contextual.configurationReason ?? 'Hallazgos asociados al tipo Yacimiento. OTROS permanece siempre disponible.', other, }; } if (!asset.familyId || asset.familyActive !== true) { return { asset: { id: asset.id, code: asset.code, name: asset.name }, inventoryFamily: null, catalogSource: 'INVENTORY_FAMILY' as const, typeConfigured: false, configurationReason: 'El elemento todavía no tiene una clasificación técnica activa.', categories: [], items: [], other, }; } const filters = [ 'mapping.inventory_family_id=$1::uuid', 'item.is_active=true', 'category.is_active=true', 'merge_record.source_item_id IS NULL', ]; const params: unknown[] = [asset.familyId]; if (query.categoryId) { params.push(query.categoryId); filters.push(`category.id=$${params.length}::uuid`); } if (query.search?.trim()) { params.push(`%${query.search.trim()}%`); const token = `$${params.length}`; filters.push(`(item.title ILIKE ${token} OR COALESCE(item.legal_basis,'') ILIKE ${token} OR COALESCE(item.glossary,'') ILIKE ${token})`); } const where = filters.join(' AND '); const [categories, items] = await Promise.all([ this.dataSource.query(` SELECT DISTINCT category.id,category.code,category.name,category.sort_order AS "sortOrder" FROM finding_catalog_item_inventory_families mapping JOIN finding_catalog_items item ON item.id=mapping.catalog_item_id JOIN finding_categories category ON category.id=item.category_id LEFT JOIN finding_catalog_item_merges merge_record ON merge_record.source_item_id=item.id WHERE ${where} ORDER BY category.sort_order,category.name,category.id `, params), this.dataSource.query(` SELECT item.id,item.category_id AS "categoryId",item.code, item.source_number AS "sourceNumber",item.title,item.legal_basis AS "legalBasis", item.glossary,item.suggested_severity AS "suggestedSeverity",item.revision, category.name AS "categoryName" FROM finding_catalog_item_inventory_families mapping JOIN finding_catalog_items item ON item.id=mapping.catalog_item_id JOIN finding_categories category ON category.id=item.category_id LEFT JOIN finding_catalog_item_merges merge_record ON merge_record.source_item_id=item.id WHERE ${where} ORDER BY category.sort_order,item.source_number,item.code `, params), ]); return { asset: { id: asset.id, code: asset.code, name: asset.name }, inventoryFamily: { id: asset.familyId, code: asset.familyCode, name: asset.familyName, level: asset.familyLevel, }, catalogSource: 'INVENTORY_FAMILY' as const, typeConfigured: true, configurationReason: `Hallazgos asociados a la clasificación técnica ${asset.familyName ?? asset.familyCode ?? ''}`.trim(), categories, items, other, }; } }