From a98dfb955db37e59b069530d93f930befb401637 Mon Sep 17 00:00:00 2001 From: enlineawork Date: Fri, 11 Sep 2026 01:06:59 -0300 Subject: [PATCH] fix(api): validate inspection operator against Yacimiento --- .../inspection-planning-create.service.ts | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/api-v3/src/inspection-visits/inspection-planning-create.service.ts b/api-v3/src/inspection-visits/inspection-planning-create.service.ts index 09855cb..9abd691 100644 --- a/api-v3/src/inspection-visits/inspection-planning-create.service.ts +++ b/api-v3/src/inspection-visits/inspection-planning-create.service.ts @@ -10,9 +10,9 @@ import { validateInspectionPlanningScope } from './inspection-planning-scope'; import { InspectionVisitsService } from './inspection-visits.service'; /** - * Creación WEB F6.1. Separa explícitamente ubicación territorial y alcance: - * Departamento → Área → Yacimiento. La Operadora no es un padre físico; se - * valida contra la relación temporal vigente del Área para la fecha planificada. + * Creación WEB. La ubicación territorial es Departamento → Área → Yacimiento. + * Empresa y Tipo de concesión pertenecen directamente al Yacimiento; por eso + * la Operadora elegida debe coincidir con el Yacimiento seleccionado. */ @Injectable() export class InspectionPlanningCreateService { @@ -52,27 +52,27 @@ export class InspectionPlanningCreateService { stage = 'operator:validate'; const [context] = await manager.query(` - SELECT relation.id - FROM area_company_relations relation - INNER JOIN assets area ON area.id=relation.area_id - INNER JOIN asset_types area_type ON area_type.id=area.asset_type_id - INNER JOIN assets company ON company.id=relation.company_id + SELECT yacimiento.id + FROM assets yacimiento + INNER JOIN asset_types yacimiento_type ON yacimiento_type.id=yacimiento.asset_type_id + INNER JOIN assets company ON company.id=yacimiento.operator_company_id INNER JOIN asset_types company_type ON company_type.id=company.asset_type_id - WHERE relation.area_id=$1::uuid - AND relation.company_id=$2::uuid - AND relation.relation_role='OPERATOR' - AND relation.valid_from <= $3::timestamptz - AND (relation.valid_until IS NULL OR relation.valid_until > $3::timestamptz) - AND area_type.operational_role='AREA' + WHERE yacimiento.id=$1::uuid + AND yacimiento.parent_id=$2::uuid + AND yacimiento.operator_company_id=$3::uuid + AND lower(yacimiento_type.code)='yacimiento' + AND yacimiento_type.is_active=true + AND yacimiento.information_status<>'INACTIVE' + AND yacimiento.concession_type_id IS NOT NULL AND company_type.operational_role='COMPANY' - AND area.information_status<>'INACTIVE' + AND company_type.is_active=true AND company.information_status<>'INACTIVE' LIMIT 1 - `, [dto.operationalAreaId, dto.operatorCompanyId, plannedStartAt]) as Array<{ id: string }>; + `, [scopeAssetId, dto.operationalAreaId, dto.operatorCompanyId]) as Array<{ id: string }>; if (!context) { throw new BadRequestException({ - code: 'INSPECTION_OPERATOR_NOT_ACTIVE_IN_AREA', - message: 'La Operadora seleccionada no está vigente para el Área en la fecha de la Inspección', + code: 'INSPECTION_OPERATOR_NOT_RELATED_TO_YACIMIENTO', + message: 'La Empresa seleccionada no es la Empresa relacionada con el Yacimiento', }); } @@ -129,8 +129,6 @@ export class InspectionPlanningCreateService { VALUES ($1::uuid,$2::uuid,true,$3::uuid) `, [visit.id, dto.leadInspectorUserId, principal.userId]); - // El checklist sigue el alcance físico del Yacimiento. No usa - // assets.operator_company_id, que en F6 es sólo snapshot histórico. stage = 'checklist:insert'; await manager.query(` INSERT INTO inspection_visit_checklist_items ( @@ -153,6 +151,7 @@ export class InspectionPlanningCreateService { FROM inspection_findings finding INNER JOIN assets inventory ON inventory.id=finding.asset_id WHERE inventory.operational_area_id=$2::uuid + AND inventory.operator_company_id=$5::uuid AND finding.status<>'VOIDED' AND EXISTS ( WITH RECURSIVE ancestors AS ( @@ -165,7 +164,7 @@ export class InspectionPlanningCreateService { SELECT 1 FROM ancestors WHERE id=$3::uuid LIMIT 1 ) ORDER BY finding.created_at,finding.id - `, [visit.id, dto.operationalAreaId, scopeAssetId, plannedStartAt.toISOString().slice(0, 10)]); + `, [visit.id, dto.operationalAreaId, scopeAssetId, plannedStartAt.toISOString().slice(0, 10), dto.operatorCompanyId]); stage = 'checklist:assets'; await manager.query(` @@ -197,7 +196,7 @@ export class InspectionPlanningCreateService { ) SELECT DISTINCT $1::uuid, item.asset_id, 'AUTO_INCLUDED', NULL, $2::uuid, - jsonb_build_object('generation',1,'source','F6.1_CREATE') + jsonb_build_object('generation',1,'source','AUTHORITATIVE_CREATE') FROM inspection_visit_checklist_items item WHERE item.visit_id=$1::uuid AND item.generation_number=1 @@ -224,6 +223,7 @@ export class InspectionPlanningCreateService { operationalAreaId: dto.operationalAreaId, scopeAssetId, operatorCompanyId: dto.operatorCompanyId, + operatorSource: 'YACIMIENTO', plannedStartAt: plannedStartAt.toISOString(), leadInspectorUserId: dto.leadInspectorUserId, }, @@ -243,7 +243,7 @@ export class InspectionPlanningCreateService { const technical = error instanceof Error ? `${error.name}: ${error.message}${error.stack ? `\n${error.stack}` : ''}` : String(error); - this.logger.error(`F6.1 inspection create failed at ${stage}: ${technical}`); + this.logger.error(`inspection create failed at ${stage}: ${technical}`); throw error; } }