diff --git a/api-v3/src/database/migrations/1790110200000-f7-simple-inventory-context.ts b/api-v3/src/database/migrations/1790110200000-f7-simple-inventory-context.ts new file mode 100644 index 0000000..432e8bd --- /dev/null +++ b/api-v3/src/database/migrations/1790110200000-f7-simple-inventory-context.ts @@ -0,0 +1,179 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class F7SimpleInventoryContext1790110200000 implements MigrationInterface { + name = 'F7SimpleInventoryContext1790110200000'; + + public async up(q: QueryRunner): Promise { + await q.query(` + UPDATE asset_types + SET description='Yacimiento. Pertenece a un Área, tiene una única Empresa operadora vigente y puede recibir Hallazgos.', + updated_at=CURRENT_TIMESTAMP + WHERE lower(code)='yacimiento' + `); + + await q.query(` + CREATE OR REPLACE FUNCTION enforce_authoritative_inventory_relationships() + RETURNS trigger LANGUAGE plpgsql AS $$ + DECLARE + kind text; + asset_role asset_type_operational_role; + parent_kind text; + parent_area uuid; + parent_company uuid; + parent_family uuid; + company_role asset_type_operational_role; + family_level text; + BEGIN + SELECT lower(code),operational_role INTO kind,asset_role FROM asset_types WHERE id=NEW.asset_type_id; + IF kind IS NULL THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='asset type does not exist'; END IF; + + IF asset_role='COMPANY'::asset_type_operational_role THEN + IF NEW.parent_id IS NOT NULL THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='Empresa no admite padre'; END IF; + NEW.operational_area_id:=NULL; NEW.operator_company_id:=NULL; NEW.concession_type_id:=NULL; + NEW.inventory_family_id:=NULL; NEW.is_inventory_instance:=false; + RETURN NEW; + ELSIF kind='departamento' THEN + IF NEW.parent_id IS NOT NULL THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='Departamento no admite padre'; END IF; + NEW.operational_area_id:=NULL; NEW.operator_company_id:=NULL; NEW.concession_type_id:=NULL; + NEW.inventory_family_id:=NULL; NEW.is_inventory_instance:=false; + RETURN NEW; + END IF; + + IF NEW.parent_id IS NULL THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='El nivel requiere un padre estructural'; END IF; + SELECT lower(parent_type.code), parent.operational_area_id, parent.operator_company_id, parent.inventory_family_id + INTO parent_kind,parent_area,parent_company,parent_family + FROM assets parent JOIN asset_types parent_type ON parent_type.id=parent.asset_type_id + WHERE parent.id=NEW.parent_id AND parent.information_status<>'INACTIVE'; + IF parent_kind IS NULL THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='El padre estructural no existe o está inactivo'; END IF; + + IF kind='area' THEN + IF parent_kind<>'departamento' THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='Área debe pertenecer a un Departamento'; END IF; + NEW.operational_area_id:=NULL; NEW.operator_company_id:=NULL; NEW.concession_type_id:=NULL; + NEW.inventory_family_id:=NULL; NEW.is_inventory_instance:=false; + ELSIF kind='yacimiento' THEN + IF parent_kind<>'area' THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='Yacimiento debe pertenecer a un Área'; END IF; + IF NEW.operator_company_id IS NULL THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='Yacimiento requiere una Empresa operadora'; END IF; + SELECT type.operational_role INTO company_role + FROM assets company JOIN asset_types type ON type.id=company.asset_type_id + WHERE company.id=NEW.operator_company_id AND company.information_status<>'INACTIVE' AND type.is_active=true; + IF company_role IS DISTINCT FROM 'COMPANY'::asset_type_operational_role THEN + RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='La Empresa operadora del Yacimiento no es válida'; + END IF; + IF NOT EXISTS( + SELECT 1 FROM area_company_relations relation + WHERE relation.area_id=NEW.parent_id + AND relation.company_id=NEW.operator_company_id + AND relation.relation_role='OPERATOR'::area_organization_role + AND relation.valid_until IS NULL + ) THEN + RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='La Empresa del Yacimiento debe estar vinculada como explotadora del Área'; + END IF; + IF NEW.concession_type_id IS NOT NULL + AND NOT EXISTS(SELECT 1 FROM concession_types c WHERE c.id=NEW.concession_type_id AND c.is_active=true) THEN + RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='El Tipo de concesión indicado no es válido'; + END IF; + IF NEW.operational_area_id IS NOT NULL AND NEW.operational_area_id<>NEW.parent_id THEN + RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='El Área operativa del Yacimiento debe coincidir con su Área padre'; + END IF; + NEW.operational_area_id:=NEW.parent_id; NEW.inventory_family_id:=NULL; NEW.is_inventory_instance:=false; + ELSIF kind='instalacion' THEN + IF parent_kind<>'yacimiento' THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='Instalación debe pertenecer a un Yacimiento'; END IF; + SELECT level::text INTO family_level FROM inventory_families WHERE id=NEW.inventory_family_id AND is_active=true; + IF family_level IS DISTINCT FROM 'INSTALLATION' THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='Instalación requiere un Tipo de instalación válido'; END IF; + IF parent_company IS NULL THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='El Yacimiento debe tener una Empresa operadora'; END IF; + NEW.operational_area_id:=parent_area; NEW.operator_company_id:=parent_company; NEW.concession_type_id:=NULL; NEW.is_inventory_instance:=true; + ELSIF kind='subinstalacion' THEN + IF parent_kind<>'instalacion' THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='Subinstalación debe pertenecer a una Instalación'; END IF; + SELECT level::text INTO family_level FROM inventory_families WHERE id=NEW.inventory_family_id AND is_active=true; + IF family_level IS DISTINCT FROM 'SUBINSTALLATION' THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='Subinstalación requiere un Tipo de subinstalación válido'; END IF; + IF NOT EXISTS( + SELECT 1 FROM inventory_family_parent_rules rule + WHERE rule.child_family_id=NEW.inventory_family_id AND rule.parent_family_id=parent_family + ) THEN + RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='El Tipo de subinstalación no está habilitado dentro del Tipo de instalación padre'; + END IF; + IF parent_company IS NULL THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='La Instalación debe pertenecer a un Yacimiento con Empresa operadora'; END IF; + NEW.operational_area_id:=parent_area; NEW.operator_company_id:=parent_company; NEW.concession_type_id:=NULL; NEW.is_inventory_instance:=true; + END IF; + RETURN NEW; + END $$ + `); + } + + public async down(q: QueryRunner): Promise { + await q.query(` + UPDATE asset_types + SET description='Yacimiento. Pertenece a un Área y define directamente Tipo de concesión y Empresa relacionada.', + updated_at=CURRENT_TIMESTAMP + WHERE lower(code)='yacimiento' + `); + + await q.query(` + CREATE OR REPLACE FUNCTION enforce_authoritative_inventory_relationships() + RETURNS trigger LANGUAGE plpgsql AS $$ + DECLARE + kind text; + asset_role asset_type_operational_role; + parent_kind text; + parent_area uuid; + parent_company uuid; + parent_family uuid; + company_role asset_type_operational_role; + family_level text; + BEGIN + SELECT lower(code),operational_role INTO kind,asset_role FROM asset_types WHERE id=NEW.asset_type_id; + IF kind IS NULL THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='asset type does not exist'; END IF; + + IF asset_role='COMPANY'::asset_type_operational_role THEN + IF NEW.parent_id IS NOT NULL THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='Empresa no admite padre'; END IF; + NEW.operational_area_id:=NULL; NEW.operator_company_id:=NULL; NEW.concession_type_id:=NULL; + NEW.inventory_family_id:=NULL; NEW.is_inventory_instance:=false; + RETURN NEW; + ELSIF kind='departamento' THEN + IF NEW.parent_id IS NOT NULL THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='Departamento no admite padre'; END IF; + NEW.operational_area_id:=NULL; NEW.operator_company_id:=NULL; NEW.concession_type_id:=NULL; + NEW.inventory_family_id:=NULL; NEW.is_inventory_instance:=false; + RETURN NEW; + END IF; + + IF NEW.parent_id IS NULL THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='El nivel requiere un padre estructural'; END IF; + SELECT lower(parent_type.code), parent.operational_area_id, parent.operator_company_id, parent.inventory_family_id + INTO parent_kind,parent_area,parent_company,parent_family + FROM assets parent JOIN asset_types parent_type ON parent_type.id=parent.asset_type_id + WHERE parent.id=NEW.parent_id AND parent.information_status<>'INACTIVE'; + IF parent_kind IS NULL THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='El padre estructural no existe o está inactivo'; END IF; + + IF kind='area' THEN + IF parent_kind<>'departamento' THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='Área debe pertenecer a un Departamento'; END IF; + NEW.operational_area_id:=NULL; NEW.operator_company_id:=NULL; NEW.concession_type_id:=NULL; + NEW.inventory_family_id:=NULL; NEW.is_inventory_instance:=false; + ELSIF kind='yacimiento' THEN + IF parent_kind<>'area' THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='Yacimiento debe pertenecer a un Área'; END IF; + IF NEW.operator_company_id IS NULL THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='Yacimiento requiere Empresa relacionada'; END IF; + SELECT type.operational_role INTO company_role FROM assets company JOIN asset_types type ON type.id=company.asset_type_id + WHERE company.id=NEW.operator_company_id AND company.information_status<>'INACTIVE' AND type.is_active=true; + IF company_role IS DISTINCT FROM 'COMPANY'::asset_type_operational_role THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='La Empresa relacionada del Yacimiento no es válida'; END IF; + IF NEW.concession_type_id IS NULL OR NOT EXISTS(SELECT 1 FROM concession_types c WHERE c.id=NEW.concession_type_id AND c.is_active=true) THEN + RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='Yacimiento requiere un Tipo de concesión válido'; + END IF; + IF NEW.operational_area_id IS NOT NULL AND NEW.operational_area_id<>NEW.parent_id THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='El Área operativa del Yacimiento debe coincidir con su Área padre'; END IF; + NEW.operational_area_id:=NEW.parent_id; NEW.inventory_family_id:=NULL; NEW.is_inventory_instance:=false; + ELSIF kind='instalacion' THEN + IF parent_kind<>'yacimiento' THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='Instalación debe pertenecer a un Yacimiento'; END IF; + SELECT level::text INTO family_level FROM inventory_families WHERE id=NEW.inventory_family_id AND is_active=true; + IF family_level IS DISTINCT FROM 'INSTALLATION' THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='Instalación requiere un Tipo de instalación válido'; END IF; + NEW.operational_area_id:=parent_area; NEW.operator_company_id:=parent_company; NEW.concession_type_id:=NULL; NEW.is_inventory_instance:=true; + ELSIF kind='subinstalacion' THEN + IF parent_kind<>'instalacion' THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='Subinstalación debe pertenecer a una Instalación'; END IF; + SELECT level::text INTO family_level FROM inventory_families WHERE id=NEW.inventory_family_id AND is_active=true; + IF family_level IS DISTINCT FROM 'SUBINSTALLATION' THEN RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='Subinstalación requiere un Tipo de subinstalación válido'; END IF; + IF NOT EXISTS(SELECT 1 FROM inventory_family_parent_rules rule WHERE rule.child_family_id=NEW.inventory_family_id AND rule.parent_family_id=parent_family) THEN + RAISE EXCEPTION USING ERRCODE='23514',MESSAGE='El Tipo de subinstalación no es compatible con el Tipo de instalación padre'; + END IF; + NEW.operational_area_id:=parent_area; NEW.operator_company_id:=parent_company; NEW.concession_type_id:=NULL; NEW.is_inventory_instance:=true; + END IF; + RETURN NEW; + END $$ + `); + } +}