diff --git a/api-v3/src/asset-master/inventory-browser.service.ts b/api-v3/src/asset-master/inventory-browser.service.ts index 10e6d30..edc1230 100644 --- a/api-v3/src/asset-master/inventory-browser.service.ts +++ b/api-v3/src/asset-master/inventory-browser.service.ts @@ -16,9 +16,9 @@ export class InventoryBrowserService { async items(query: InventoryBrowserQueryDto) { const params: unknown[] = []; const conditions = [ - 'asset.is_inventory_instance=true', "asset.information_status<>'INACTIVE'", 'type.is_active=true', + "(type.operational_role='COMPANY' OR lower(type.code) IN ('departamento','area','yacimiento','instalacion','subinstalacion'))", ]; const add = (value: unknown): string => { params.push(value); @@ -36,15 +36,21 @@ export class InventoryBrowserService { if (query.needsValidation === false) conditions.push("asset.information_status='VALIDATED'"); if (query.hasGeometry === true) conditions.push('EXISTS (SELECT 1 FROM asset_geometries geometry_filter WHERE geometry_filter.asset_id=asset.id)'); if (query.hasGeometry === false) conditions.push('NOT EXISTS (SELECT 1 FROM asset_geometries geometry_filter WHERE geometry_filter.asset_id=asset.id)'); - if (query.operationalAreaId) conditions.push(`asset.operational_area_id=${add(query.operationalAreaId)}::uuid`); + if (query.operationalAreaId) { + const area = add(query.operationalAreaId); + conditions.push(`(asset.id=${area}::uuid OR asset.operational_area_id=${area}::uuid)`); + } if (query.operatorCompanyId) { const company = add(query.operatorCompanyId); - conditions.push(`EXISTS ( - SELECT 1 FROM area_company_relations relation - WHERE relation.area_id=asset.operational_area_id - AND relation.company_id=${company}::uuid - AND relation.relation_role='OPERATOR' - AND relation.valid_until IS NULL + conditions.push(`( + asset.id=${company}::uuid + OR EXISTS ( + SELECT 1 FROM area_company_relations relation + WHERE (relation.area_id=asset.operational_area_id OR relation.area_id=asset.id) + AND relation.company_id=${company}::uuid + AND relation.relation_role='OPERATOR' + AND relation.valid_until IS NULL + ) )`); } @@ -76,7 +82,8 @@ export class InventoryBrowserService { SELECT JSONB_BUILD_OBJECT('id',company.id,'code',company.code,'name',company.name) FROM area_company_relations relation JOIN assets company ON company.id=relation.company_id - WHERE relation.area_id=asset.operational_area_id + WHERE relation.area_id=COALESCE(asset.operational_area_id, + CASE WHEN type.operational_role='AREA' THEN asset.id ELSE NULL END) AND relation.relation_role='OPERATOR' AND relation.valid_until IS NULL ORDER BY relation.valid_from DESC,relation.created_at DESC @@ -84,7 +91,8 @@ export class InventoryBrowserService { ) AS "operatorCompany", asset.information_status AS "informationStatus", asset.operational_status AS "operationalStatus", - 0::integer AS "childrenCount", + (SELECT COUNT(*)::integer FROM assets child + WHERE child.parent_id=asset.id AND child.information_status<>'INACTIVE') AS "childrenCount", EXISTS (SELECT 1 FROM asset_geometries geometry WHERE geometry.asset_id=asset.id) AS "hasGeometry", CASE WHEN geometry_type.type IS NULL THEN NULL ELSE geometry_type.type END AS "geometryType", (SELECT COUNT(*)::integer FROM asset_media media WHERE media.asset_id=asset.id AND media.deleted_at IS NULL) AS "mediaCount", @@ -104,7 +112,14 @@ export class InventoryBrowserService { LIMIT 1 ) geometry_type ON true WHERE ${where} - ORDER BY asset.name,asset.code + ORDER BY CASE + WHEN lower(type.code)='departamento' THEN 0 + WHEN type.operational_role='COMPANY' THEN 1 + WHEN lower(type.code)='area' THEN 2 + WHEN lower(type.code)='yacimiento' THEN 3 + WHEN lower(type.code)='instalacion' THEN 4 + WHEN lower(type.code)='subinstalacion' THEN 5 ELSE 9 END, + asset.name,asset.code LIMIT ${limit} OFFSET ${offsetParam} `,params); @@ -119,28 +134,30 @@ export class InventoryBrowserService { }; } - async areas(query: InventoryBrowserQueryDto) { + async departments(query: InventoryBrowserQueryDto) { const params: unknown[] = []; const conditions = [ - "type.operational_role='AREA'", - "area.information_status<>'INACTIVE'", + "lower(type.code)='departamento'", + "department.information_status<>'INACTIVE'", 'type.is_active=true', ]; const add = (value: unknown): string => { params.push(value); return `$${params.length}`; }; - if (query.search?.trim()) { const p = add(`%${query.search.trim()}%`); - conditions.push(`(area.code ILIKE ${p} OR area.name ILIKE ${p} OR COALESCE(area.common_name,'') ILIKE ${p})`); + conditions.push(`(department.code ILIKE ${p} OR department.name ILIKE ${p} OR COALESCE(department.common_name,'') ILIKE ${p})`); } - if (query.operationalAreaId) conditions.push(`area.id=${add(query.operationalAreaId)}::uuid`); if (query.operatorCompanyId) { const p = add(query.operatorCompanyId); conditions.push(`EXISTS ( - SELECT 1 FROM area_company_relations relation - WHERE relation.area_id=area.id + SELECT 1 + FROM assets area + JOIN asset_types atype ON atype.id=area.asset_type_id + JOIN area_company_relations relation ON relation.area_id=area.id + WHERE area.parent_id=department.id + AND lower(atype.code)='area' AND relation.company_id=${p}::uuid AND relation.relation_role='OPERATOR' AND relation.valid_until IS NULL @@ -149,41 +166,86 @@ export class InventoryBrowserService { const data = await this.dataSource.query(` SELECT - area.id,area.code,area.name,area.common_name AS "commonName", + department.id,department.code,department.name,department.common_name AS "commonName", JSONB_BUILD_OBJECT('id',type.id,'code',type.code,'name',type.name) AS type, - area.information_status AS "informationStatus", - area.operational_status AS "operationalStatus", - ( - SELECT JSONB_BUILD_OBJECT('id',company.id,'code',company.code,'name',company.name) - FROM area_company_relations relation - JOIN assets company ON company.id=relation.company_id - WHERE relation.area_id=area.id - AND relation.relation_role='OPERATOR' - AND relation.valid_until IS NULL - ORDER BY relation.valid_from DESC,relation.created_at DESC - LIMIT 1 - ) AS "currentOperator", - ( - SELECT COUNT(*)::integer - FROM assets yacimiento - JOIN asset_types ytype ON ytype.id=yacimiento.asset_type_id - WHERE yacimiento.parent_id=area.id - AND lower(ytype.code)='yacimiento' - AND yacimiento.information_status<>'INACTIVE' - ) AS "yacimientoCount", - ( - SELECT COUNT(*)::integer - FROM assets inventory - WHERE inventory.is_inventory_instance=true - AND inventory.information_status<>'INACTIVE' - AND inventory.operational_area_id=area.id - ) AS "inventoryCount" - FROM assets area - JOIN asset_types type ON type.id=area.asset_type_id + department.information_status AS "informationStatus", + department.operational_status AS "operationalStatus", + (SELECT COUNT(*)::integer FROM assets area + JOIN asset_types atype ON atype.id=area.asset_type_id + WHERE area.parent_id=department.id AND lower(atype.code)='area' + AND area.information_status<>'INACTIVE') AS "areaCount", + (SELECT COUNT(*)::integer FROM assets child + WHERE child.parent_id=department.id AND child.information_status<>'INACTIVE') AS "childrenCount" + FROM assets department + JOIN asset_types type ON type.id=department.asset_type_id + WHERE ${conditions.join(' AND ')} + ORDER BY department.name,department.code + `, params); + return { data, meta: { count: data.length } }; + } + + async companies(query: InventoryBrowserQueryDto) { + const params: unknown[] = []; + const conditions = [ + "type.operational_role='COMPANY'", + "company.information_status<>'INACTIVE'", + 'type.is_active=true', + ]; + if (query.search?.trim()) { + params.push(`%${query.search.trim()}%`); + conditions.push(`(company.code ILIKE $1 OR company.name ILIKE $1 OR COALESCE(company.common_name,'') ILIKE $1)`); + } + const data = await this.dataSource.query(` + SELECT company.id,company.code,company.name,company.common_name AS "commonName", + JSONB_BUILD_OBJECT('id',type.id,'code',type.code,'name',type.name) AS type, + company.information_status AS "informationStatus", + (SELECT COUNT(*)::integer FROM area_company_relations relation + WHERE relation.company_id=company.id AND relation.relation_role='OPERATOR' + AND relation.valid_until IS NULL) AS "areaCount" + FROM assets company + JOIN asset_types type ON type.id=company.asset_type_id + WHERE ${conditions.join(' AND ')} + ORDER BY company.name,company.code + `,params); + return { data,meta:{count:data.length} }; + } + + async areas(query: InventoryBrowserQueryDto) { + const params: unknown[] = []; + const conditions = [ + "type.operational_role='AREA'", + "area.information_status<>'INACTIVE'", + 'type.is_active=true', + ]; + const add = (value: unknown): string => { params.push(value); return `$${params.length}`; }; + if (query.search?.trim()) { + const p = add(`%${query.search.trim()}%`); + conditions.push(`(area.code ILIKE ${p} OR area.name ILIKE ${p} OR COALESCE(area.common_name,'') ILIKE ${p})`); + } + if (query.operationalAreaId) conditions.push(`area.id=${add(query.operationalAreaId)}::uuid`); + if (query.operatorCompanyId) { + const p = add(query.operatorCompanyId); + conditions.push(`EXISTS (SELECT 1 FROM area_company_relations relation + WHERE relation.area_id=area.id AND relation.company_id=${p}::uuid + AND relation.relation_role='OPERATOR' AND relation.valid_until IS NULL)`); + } + const data = await this.dataSource.query(` + SELECT area.id,area.code,area.name,area.common_name AS "commonName", + JSONB_BUILD_OBJECT('id',type.id,'code',type.code,'name',type.name) AS type, + area.information_status AS "informationStatus",area.operational_status AS "operationalStatus", + (SELECT JSONB_BUILD_OBJECT('id',company.id,'code',company.code,'name',company.name) + FROM area_company_relations relation JOIN assets company ON company.id=relation.company_id + WHERE relation.area_id=area.id AND relation.relation_role='OPERATOR' AND relation.valid_until IS NULL + ORDER BY relation.valid_from DESC,relation.created_at DESC LIMIT 1) AS "currentOperator", + (SELECT COUNT(*)::integer FROM assets yacimiento JOIN asset_types ytype ON ytype.id=yacimiento.asset_type_id + WHERE yacimiento.parent_id=area.id AND lower(ytype.code)='yacimiento' + AND yacimiento.information_status<>'INACTIVE') AS "yacimientoCount", + (SELECT COUNT(*)::integer FROM assets inventory + WHERE inventory.information_status<>'INACTIVE' AND inventory.operational_area_id=area.id) AS "inventoryCount" + FROM assets area JOIN asset_types type ON type.id=area.asset_type_id WHERE ${conditions.join(' AND ')} ORDER BY area.name,area.code `, params); - return { data, meta: { count: data.length } }; } @@ -199,9 +261,6 @@ export class InventoryBrowserService { "asset.information_status<>'INACTIVE'", 'type.is_active=true', ]; - if (allowedChildType === 'instalacion' || allowedChildType === 'subinstalacion') { - conditions.push('asset.is_inventory_instance=true'); - } if (query.search?.trim()) { params.push(`%${query.search.trim()}%`); const p = `$${params.length}`; @@ -223,20 +282,12 @@ export class InventoryBrowserService { CASE WHEN family.id IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( 'id',family.id,'code',family.code,'name',family.name,'level',family.level ) END AS "inventoryFamily", - ( - SELECT COUNT(*)::integer - FROM assets child - WHERE child.parent_id=asset.id - AND child.information_status<>'INACTIVE' - AND ( - lower(type.code)='area' - OR child.is_inventory_instance=true - OR EXISTS ( - SELECT 1 FROM asset_types child_type - WHERE child_type.id=child.asset_type_id AND lower(child_type.code)='yacimiento' - ) - ) - ) AS "childrenCount", + (SELECT COUNT(*)::integer FROM assets child + WHERE child.parent_id=asset.id AND child.information_status<>'INACTIVE') AS "childrenCount", + CASE WHEN family.id IS NULL THEN 0 ELSE ( + SELECT COUNT(*)::integer FROM finding_catalog_item_inventory_families mapping + WHERE mapping.inventory_family_id=family.id + ) END AS "findingCount", EXISTS (SELECT 1 FROM asset_geometries geometry WHERE geometry.asset_id=asset.id) AS "hasGeometry", asset.updated_at AS "updatedAt" FROM assets asset @@ -263,10 +314,10 @@ export class InventoryBrowserService { if (!parent) { throw new NotFoundException({ code:'INVENTORY_BROWSER_PARENT_NOT_FOUND',message:'El nivel de Inventario no existe' }); } - if (!['area','yacimiento','instalacion','subinstalacion'].includes(parent.typeCode.toLowerCase())) { + if (!['departamento','area','yacimiento','instalacion','subinstalacion'].includes(parent.typeCode.toLowerCase())) { throw new BadRequestException({ code:'INVENTORY_BROWSER_PARENT_TYPE_INVALID', - message:'La navegación de Inventarios admite Área → Yacimiento → Instalación → Subinstalación', + message:'La navegación de Inventarios admite Departamento → Área → Yacimiento → Instalación → Subinstalación', }); } return parent; @@ -274,6 +325,7 @@ export class InventoryBrowserService { private allowedChildType(typeCode: string): string | null { switch (typeCode.toLowerCase()) { + case 'departamento': return 'area'; case 'area': return 'yacimiento'; case 'yacimiento': return 'instalacion'; case 'instalacion': return 'subinstalacion';