[GAP-001,002,003] feat(ddl): Add KPIs, tool loans and depreciation tables
- Add 12-analytics-kpis-ddl.sql with kpis_config and kpis_values tables - Add tool_loans table with loan_status enum (GAP-002) - Add depreciation_schedule and depreciation_entries tables (GAP-003) - Add depreciation_method enum and calculate_monthly_depreciation function Implements 13 SP of critical gaps identified in EPIC-003. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
094cbe3ffb
commit
8a9db6f9d1
@ -941,6 +941,287 @@ CREATE TRIGGER trg_update_next_maintenance
|
||||
AFTER INSERT OR UPDATE OR DELETE ON assets.maintenance_schedules
|
||||
FOR EACH ROW EXECUTE FUNCTION assets.update_next_maintenance();
|
||||
|
||||
-- ============================================================================
|
||||
-- GAP-002: PRESTAMOS DE HERRAMIENTAS (2026-02-04)
|
||||
-- ============================================================================
|
||||
|
||||
-- Enum para estado de prestamo
|
||||
CREATE TYPE assets.loan_status AS ENUM (
|
||||
'active', -- Prestamo activo
|
||||
'returned', -- Devuelto
|
||||
'overdue', -- Vencido
|
||||
'lost', -- Extraviado
|
||||
'damaged' -- Danado
|
||||
);
|
||||
|
||||
-- ----------------------------------------------------------------------------
|
||||
-- 12. Prestamos de Herramientas
|
||||
-- Registro de prestamos de herramientas entre obras o a empleados
|
||||
-- ----------------------------------------------------------------------------
|
||||
CREATE TABLE assets.tool_loans (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
|
||||
-- Herramienta prestada
|
||||
tool_id UUID NOT NULL REFERENCES assets.assets(id),
|
||||
|
||||
-- Quien recibe el prestamo
|
||||
employee_id UUID NOT NULL,
|
||||
employee_name VARCHAR(255),
|
||||
|
||||
-- Origen del prestamo
|
||||
fraccionamiento_origen_id UUID,
|
||||
fraccionamiento_origen_name VARCHAR(255),
|
||||
|
||||
-- Destino del prestamo
|
||||
fraccionamiento_destino_id UUID,
|
||||
fraccionamiento_destino_name VARCHAR(255),
|
||||
|
||||
-- Fechas
|
||||
loan_date DATE NOT NULL,
|
||||
expected_return_date DATE,
|
||||
actual_return_date DATE,
|
||||
|
||||
-- Estado
|
||||
status assets.loan_status NOT NULL DEFAULT 'active',
|
||||
|
||||
-- Condicion
|
||||
condition_out TEXT,
|
||||
condition_out_photos JSONB,
|
||||
condition_in TEXT,
|
||||
condition_in_photos JSONB,
|
||||
|
||||
-- Aprobacion
|
||||
approved_by_id UUID,
|
||||
approved_by_name VARCHAR(255),
|
||||
approved_at TIMESTAMPTZ,
|
||||
|
||||
-- Devolucion
|
||||
received_by_id UUID,
|
||||
received_by_name VARCHAR(255),
|
||||
|
||||
-- Notas
|
||||
notes TEXT,
|
||||
metadata JSONB,
|
||||
|
||||
-- Auditoria
|
||||
created_by UUID,
|
||||
updated_by UUID,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Indices para tool_loans
|
||||
CREATE INDEX idx_tool_loans_tenant ON assets.tool_loans(tenant_id);
|
||||
CREATE INDEX idx_tool_loans_tool ON assets.tool_loans(tenant_id, tool_id);
|
||||
CREATE INDEX idx_tool_loans_employee ON assets.tool_loans(tenant_id, employee_id);
|
||||
CREATE INDEX idx_tool_loans_status ON assets.tool_loans(tenant_id, status);
|
||||
CREATE INDEX idx_tool_loans_origen ON assets.tool_loans(tenant_id, fraccionamiento_origen_id);
|
||||
CREATE INDEX idx_tool_loans_destino ON assets.tool_loans(tenant_id, fraccionamiento_destino_id);
|
||||
CREATE INDEX idx_tool_loans_active ON assets.tool_loans(tenant_id, status) WHERE status = 'active';
|
||||
CREATE INDEX idx_tool_loans_overdue ON assets.tool_loans(tenant_id, expected_return_date)
|
||||
WHERE status = 'active' AND expected_return_date < CURRENT_DATE;
|
||||
|
||||
ALTER TABLE assets.tool_loans ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE TRIGGER trg_tool_loans_updated_at
|
||||
BEFORE UPDATE ON assets.tool_loans
|
||||
FOR EACH ROW EXECUTE FUNCTION assets.set_updated_at();
|
||||
|
||||
-- ============================================================================
|
||||
-- GAP-003: DEPRECIACION DE ACTIVOS (2026-02-04)
|
||||
-- ============================================================================
|
||||
|
||||
-- Enum para metodo de depreciacion
|
||||
CREATE TYPE assets.depreciation_method AS ENUM (
|
||||
'straight_line', -- Linea recta
|
||||
'declining_balance', -- Saldo decreciente
|
||||
'double_declining', -- Doble saldo decreciente
|
||||
'sum_of_years', -- Suma de digitos de los anos
|
||||
'units_of_production' -- Unidades de produccion
|
||||
);
|
||||
|
||||
-- ----------------------------------------------------------------------------
|
||||
-- 13. Programacion de Depreciacion
|
||||
-- Configuracion de depreciacion para cada activo
|
||||
-- ----------------------------------------------------------------------------
|
||||
CREATE TABLE assets.depreciation_schedule (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
|
||||
-- Activo
|
||||
asset_id UUID NOT NULL REFERENCES assets.assets(id),
|
||||
|
||||
-- Tipo de activo
|
||||
asset_type VARCHAR(20) NOT NULL, -- equipment, machinery, vehicle, tool
|
||||
|
||||
-- Metodo de depreciacion
|
||||
method assets.depreciation_method NOT NULL DEFAULT 'straight_line',
|
||||
|
||||
-- Valores
|
||||
original_value DECIMAL(18,2) NOT NULL,
|
||||
salvage_value DECIMAL(18,2) DEFAULT 0,
|
||||
depreciable_amount DECIMAL(18,2) GENERATED ALWAYS AS
|
||||
(original_value - COALESCE(salvage_value, 0)) STORED,
|
||||
|
||||
-- Vida util
|
||||
useful_life_months INTEGER NOT NULL,
|
||||
useful_life_units INTEGER, -- Para units_of_production
|
||||
|
||||
-- Depreciacion mensual calculada (para straight_line)
|
||||
monthly_depreciation DECIMAL(12,2) GENERATED ALWAYS AS
|
||||
(CASE WHEN useful_life_months > 0
|
||||
THEN (original_value - COALESCE(salvage_value, 0)) / useful_life_months
|
||||
ELSE 0 END) STORED,
|
||||
|
||||
-- Fechas
|
||||
depreciation_start_date DATE NOT NULL,
|
||||
depreciation_end_date DATE,
|
||||
|
||||
-- Estado actual
|
||||
accumulated_depreciation DECIMAL(18,2) DEFAULT 0,
|
||||
current_book_value DECIMAL(18,2),
|
||||
last_entry_date DATE,
|
||||
|
||||
-- Estado
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
is_fully_depreciated BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
-- Notas
|
||||
notes TEXT,
|
||||
metadata JSONB,
|
||||
|
||||
-- Auditoria
|
||||
created_by UUID,
|
||||
updated_by UUID,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
CONSTRAINT uq_depreciation_schedule_asset UNIQUE (tenant_id, asset_id)
|
||||
);
|
||||
|
||||
-- ----------------------------------------------------------------------------
|
||||
-- 14. Entradas de Depreciacion
|
||||
-- Registro mensual de depreciacion aplicada
|
||||
-- ----------------------------------------------------------------------------
|
||||
CREATE TABLE assets.depreciation_entries (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
|
||||
-- Programacion
|
||||
schedule_id UUID NOT NULL REFERENCES assets.depreciation_schedule(id) ON DELETE CASCADE,
|
||||
|
||||
-- Periodo
|
||||
period_date DATE NOT NULL, -- Primer dia del mes
|
||||
fiscal_year INTEGER NOT NULL,
|
||||
fiscal_month INTEGER NOT NULL,
|
||||
|
||||
-- Valores
|
||||
depreciation_amount DECIMAL(12,2) NOT NULL,
|
||||
accumulated_depreciation DECIMAL(18,2) NOT NULL,
|
||||
book_value DECIMAL(18,2) NOT NULL,
|
||||
|
||||
-- Para units_of_production
|
||||
units_used INTEGER,
|
||||
|
||||
-- Contabilidad
|
||||
journal_entry_id UUID,
|
||||
is_posted BOOLEAN DEFAULT FALSE,
|
||||
posted_at TIMESTAMPTZ,
|
||||
posted_by UUID,
|
||||
|
||||
-- Estado
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'draft', -- draft, posted, reversed
|
||||
|
||||
-- Notas
|
||||
notes TEXT,
|
||||
|
||||
-- Auditoria
|
||||
created_by UUID,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
CONSTRAINT uq_depreciation_entries_period UNIQUE (schedule_id, period_date)
|
||||
);
|
||||
|
||||
-- Indices para depreciation_schedule
|
||||
CREATE INDEX idx_depreciation_schedule_tenant ON assets.depreciation_schedule(tenant_id);
|
||||
CREATE INDEX idx_depreciation_schedule_asset ON assets.depreciation_schedule(tenant_id, asset_id);
|
||||
CREATE INDEX idx_depreciation_schedule_active ON assets.depreciation_schedule(tenant_id, is_active) WHERE is_active = TRUE;
|
||||
CREATE INDEX idx_depreciation_schedule_not_depreciated ON assets.depreciation_schedule(tenant_id, is_fully_depreciated)
|
||||
WHERE is_fully_depreciated = FALSE;
|
||||
|
||||
-- Indices para depreciation_entries
|
||||
CREATE INDEX idx_depreciation_entries_tenant ON assets.depreciation_entries(tenant_id);
|
||||
CREATE INDEX idx_depreciation_entries_schedule ON assets.depreciation_entries(schedule_id);
|
||||
CREATE INDEX idx_depreciation_entries_period ON assets.depreciation_entries(tenant_id, period_date);
|
||||
CREATE INDEX idx_depreciation_entries_fiscal ON assets.depreciation_entries(tenant_id, fiscal_year, fiscal_month);
|
||||
CREATE INDEX idx_depreciation_entries_pending ON assets.depreciation_entries(tenant_id, status) WHERE status = 'draft';
|
||||
|
||||
ALTER TABLE assets.depreciation_schedule ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE assets.depreciation_entries ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE TRIGGER trg_depreciation_schedule_updated_at
|
||||
BEFORE UPDATE ON assets.depreciation_schedule
|
||||
FOR EACH ROW EXECUTE FUNCTION assets.set_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_depreciation_entries_updated_at
|
||||
BEFORE UPDATE ON assets.depreciation_entries
|
||||
FOR EACH ROW EXECUTE FUNCTION assets.set_updated_at();
|
||||
|
||||
-- ============================================================================
|
||||
-- FUNCION: Calcular depreciacion mensual
|
||||
-- ============================================================================
|
||||
CREATE OR REPLACE FUNCTION assets.calculate_monthly_depreciation(
|
||||
p_schedule_id UUID,
|
||||
p_period_date DATE
|
||||
)
|
||||
RETURNS DECIMAL(12,2) AS $$
|
||||
DECLARE
|
||||
v_schedule RECORD;
|
||||
v_months_elapsed INTEGER;
|
||||
v_depreciation DECIMAL(12,2);
|
||||
BEGIN
|
||||
SELECT * INTO v_schedule
|
||||
FROM assets.depreciation_schedule
|
||||
WHERE id = p_schedule_id AND is_active = TRUE;
|
||||
|
||||
IF NOT FOUND THEN
|
||||
RETURN 0;
|
||||
END IF;
|
||||
|
||||
-- Calcular meses transcurridos
|
||||
v_months_elapsed := (
|
||||
EXTRACT(YEAR FROM p_period_date) * 12 + EXTRACT(MONTH FROM p_period_date)
|
||||
) - (
|
||||
EXTRACT(YEAR FROM v_schedule.depreciation_start_date) * 12 + EXTRACT(MONTH FROM v_schedule.depreciation_start_date)
|
||||
);
|
||||
|
||||
-- Si ya esta completamente depreciado
|
||||
IF v_schedule.accumulated_depreciation >= v_schedule.depreciable_amount THEN
|
||||
RETURN 0;
|
||||
END IF;
|
||||
|
||||
-- Calcular segun metodo
|
||||
CASE v_schedule.method
|
||||
WHEN 'straight_line' THEN
|
||||
v_depreciation := v_schedule.monthly_depreciation;
|
||||
WHEN 'declining_balance' THEN
|
||||
v_depreciation := (v_schedule.original_value - v_schedule.accumulated_depreciation) *
|
||||
(1.0 / v_schedule.useful_life_months) * 2;
|
||||
ELSE
|
||||
v_depreciation := v_schedule.monthly_depreciation;
|
||||
END CASE;
|
||||
|
||||
-- No depreciar mas del valor depreciable restante
|
||||
IF (v_schedule.accumulated_depreciation + v_depreciation) > v_schedule.depreciable_amount THEN
|
||||
v_depreciation := v_schedule.depreciable_amount - v_schedule.accumulated_depreciation;
|
||||
END IF;
|
||||
|
||||
RETURN COALESCE(v_depreciation, 0);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- ============================================================================
|
||||
-- COMENTARIOS DE DOCUMENTACION
|
||||
-- ============================================================================
|
||||
@ -958,6 +1239,9 @@ COMMENT ON TABLE assets.maintenance_history IS 'Historial de mantenimientos real
|
||||
COMMENT ON TABLE assets.asset_costs IS 'Costos de operacion y mantenimiento de activos';
|
||||
COMMENT ON TABLE assets.asset_locations IS 'Historial de ubicaciones GPS de activos';
|
||||
COMMENT ON TABLE assets.fuel_logs IS 'Registro de cargas de combustible';
|
||||
COMMENT ON TABLE assets.tool_loans IS 'Prestamos de herramientas entre obras/empleados (GAP-002)';
|
||||
COMMENT ON TABLE assets.depreciation_schedule IS 'Configuracion de depreciacion por activo (GAP-003)';
|
||||
COMMENT ON TABLE assets.depreciation_entries IS 'Entradas mensuales de depreciacion (GAP-003)';
|
||||
|
||||
-- ============================================================================
|
||||
-- FIN DEL SCRIPT
|
||||
|
||||
274
schemas/12-analytics-kpis-ddl.sql
Normal file
274
schemas/12-analytics-kpis-ddl.sql
Normal file
@ -0,0 +1,274 @@
|
||||
-- ============================================================================
|
||||
-- 12-analytics-kpis-ddl.sql
|
||||
-- Schema: reports (extension)
|
||||
-- ERP Construccion - KPIs Configurables (GAP-001)
|
||||
-- ============================================================================
|
||||
-- Descripcion: Configuracion dinamica de KPIs incluyendo:
|
||||
-- - Definicion de KPIs con formulas configurables
|
||||
-- - Valores calculados historicos
|
||||
-- - Umbrales y semaforizacion
|
||||
-- ============================================================================
|
||||
-- Autor: Claude-Especialista-BD
|
||||
-- Fecha: 2026-02-04
|
||||
-- Version: 1.0.0
|
||||
-- Tarea: TASK-2026-02-03-ANALISIS-MODELADO-INTEGRAL / GAP-001
|
||||
-- ============================================================================
|
||||
|
||||
-- Usar schema reports existente
|
||||
-- CREATE SCHEMA IF NOT EXISTS reports;
|
||||
|
||||
-- ============================================================================
|
||||
-- TABLAS
|
||||
-- ============================================================================
|
||||
|
||||
-- ----------------------------------------------------------------------------
|
||||
-- 1. Configuracion de KPIs
|
||||
-- Permite definir KPIs dinamicos con formulas configurables
|
||||
-- ----------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS reports.kpis_config (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
|
||||
-- Identificacion
|
||||
code VARCHAR(50) NOT NULL,
|
||||
name VARCHAR(200) NOT NULL,
|
||||
description TEXT,
|
||||
|
||||
-- Clasificacion
|
||||
category VARCHAR(50) NOT NULL, -- financial, progress, quality, hse, hr, inventory, operational
|
||||
module VARCHAR(50) NOT NULL, -- MAI-006, MAE-014, etc.
|
||||
|
||||
-- Formula de calculo
|
||||
formula TEXT NOT NULL, -- SQL o expresion matematica
|
||||
formula_type VARCHAR(20) NOT NULL DEFAULT 'sql', -- sql, expression, function
|
||||
query_function VARCHAR(255), -- Nombre de funcion PL/pgSQL si aplica
|
||||
|
||||
-- Parametros de la formula
|
||||
parameters_schema JSONB DEFAULT '{}',
|
||||
|
||||
-- Unidad y formato
|
||||
unit VARCHAR(20), -- %, $, hrs, dias, etc.
|
||||
decimal_places INTEGER DEFAULT 2,
|
||||
format_pattern VARCHAR(50), -- Patron de formato para display
|
||||
|
||||
-- Umbrales de semaforizacion
|
||||
target_value DECIMAL(18,4),
|
||||
threshold_green DECIMAL(18,4), -- Valor >= este es verde
|
||||
threshold_yellow DECIMAL(18,4), -- Valor >= este es amarillo, < es rojo
|
||||
invert_colors BOOLEAN DEFAULT FALSE, -- TRUE si menor es mejor
|
||||
|
||||
-- Frecuencia de calculo
|
||||
calculation_frequency VARCHAR(20) DEFAULT 'daily', -- realtime, hourly, daily, weekly, monthly
|
||||
|
||||
-- Visualizacion
|
||||
display_order INTEGER DEFAULT 0,
|
||||
icon VARCHAR(50),
|
||||
color VARCHAR(20),
|
||||
|
||||
-- Estado
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
is_system BOOLEAN NOT NULL DEFAULT FALSE, -- TRUE = no editable por usuario
|
||||
|
||||
-- Metadatos
|
||||
metadata JSONB,
|
||||
|
||||
-- Auditoria
|
||||
created_by UUID,
|
||||
updated_by UUID,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
deleted_at TIMESTAMPTZ,
|
||||
|
||||
CONSTRAINT uq_kpis_config_tenant_code UNIQUE (tenant_id, code)
|
||||
);
|
||||
|
||||
-- ----------------------------------------------------------------------------
|
||||
-- 2. Valores Calculados de KPIs
|
||||
-- Almacena los valores calculados periodicamente
|
||||
-- ----------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS reports.kpis_values (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
|
||||
-- KPI
|
||||
kpi_id UUID NOT NULL REFERENCES reports.kpis_config(id) ON DELETE CASCADE,
|
||||
|
||||
-- Periodo
|
||||
period_start DATE NOT NULL,
|
||||
period_end DATE NOT NULL,
|
||||
period_type VARCHAR(20) NOT NULL DEFAULT 'daily', -- daily, weekly, monthly, quarterly, yearly
|
||||
|
||||
-- Contexto opcional
|
||||
project_id UUID, -- Fraccionamiento/Obra especifica
|
||||
department_id UUID, -- Departamento especifico
|
||||
|
||||
-- Valor calculado
|
||||
value DECIMAL(18,4) NOT NULL,
|
||||
previous_value DECIMAL(18,4),
|
||||
|
||||
-- Comparacion con objetivo
|
||||
target_value DECIMAL(18,4),
|
||||
variance_value DECIMAL(18,4), -- value - target_value
|
||||
variance_percentage DECIMAL(8,2), -- ((value - target) / target) * 100
|
||||
|
||||
-- Semaforizacion calculada
|
||||
status VARCHAR(10), -- green, yellow, red
|
||||
is_on_target BOOLEAN,
|
||||
|
||||
-- Tendencia
|
||||
trend_direction VARCHAR(10), -- up, down, stable
|
||||
change_percentage DECIMAL(8,2),
|
||||
|
||||
-- Desglose
|
||||
breakdown JSONB, -- Datos adicionales de calculo
|
||||
|
||||
-- Calculo
|
||||
calculated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
calculation_duration_ms INTEGER,
|
||||
calculation_error TEXT,
|
||||
|
||||
-- Auditoria
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- INDICES
|
||||
-- ============================================================================
|
||||
|
||||
-- KPIs Config
|
||||
CREATE INDEX IF NOT EXISTS idx_kpis_config_tenant ON reports.kpis_config(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_kpis_config_tenant_category ON reports.kpis_config(tenant_id, category);
|
||||
CREATE INDEX IF NOT EXISTS idx_kpis_config_tenant_module ON reports.kpis_config(tenant_id, module);
|
||||
CREATE INDEX IF NOT EXISTS idx_kpis_config_active ON reports.kpis_config(tenant_id, is_active) WHERE is_active = TRUE;
|
||||
|
||||
-- KPIs Values
|
||||
CREATE INDEX IF NOT EXISTS idx_kpis_values_tenant ON reports.kpis_values(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_kpis_values_kpi ON reports.kpis_values(kpi_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_kpis_values_period ON reports.kpis_values(tenant_id, period_start, period_end);
|
||||
CREATE INDEX IF NOT EXISTS idx_kpis_values_kpi_period ON reports.kpis_values(kpi_id, period_start DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_kpis_values_project ON reports.kpis_values(tenant_id, project_id) WHERE project_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_kpis_values_calculated ON reports.kpis_values(calculated_at DESC);
|
||||
|
||||
-- ============================================================================
|
||||
-- ROW LEVEL SECURITY (RLS)
|
||||
-- ============================================================================
|
||||
|
||||
ALTER TABLE reports.kpis_config ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE reports.kpis_values ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- ============================================================================
|
||||
-- TRIGGERS DE AUDITORIA
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION reports.set_kpis_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_kpis_config_updated_at ON reports.kpis_config;
|
||||
CREATE TRIGGER trg_kpis_config_updated_at
|
||||
BEFORE UPDATE ON reports.kpis_config
|
||||
FOR EACH ROW EXECUTE FUNCTION reports.set_kpis_updated_at();
|
||||
|
||||
-- ============================================================================
|
||||
-- FUNCIONES AUXILIARES
|
||||
-- ============================================================================
|
||||
|
||||
-- Funcion para calcular un KPI especifico
|
||||
CREATE OR REPLACE FUNCTION reports.calculate_kpi(
|
||||
p_kpi_id UUID,
|
||||
p_tenant_id UUID,
|
||||
p_period_start DATE,
|
||||
p_period_end DATE,
|
||||
p_project_id UUID DEFAULT NULL
|
||||
)
|
||||
RETURNS DECIMAL(18,4) AS $$
|
||||
DECLARE
|
||||
v_kpi RECORD;
|
||||
v_result DECIMAL(18,4);
|
||||
BEGIN
|
||||
-- Obtener configuracion del KPI
|
||||
SELECT * INTO v_kpi
|
||||
FROM reports.kpis_config
|
||||
WHERE id = p_kpi_id AND tenant_id = p_tenant_id AND is_active = TRUE;
|
||||
|
||||
IF NOT FOUND THEN
|
||||
RAISE EXCEPTION 'KPI not found or inactive: %', p_kpi_id;
|
||||
END IF;
|
||||
|
||||
-- Si es una funcion, ejecutarla
|
||||
IF v_kpi.formula_type = 'function' AND v_kpi.query_function IS NOT NULL THEN
|
||||
EXECUTE format('SELECT %s($1, $2, $3, $4)', v_kpi.query_function)
|
||||
INTO v_result
|
||||
USING p_tenant_id, p_period_start, p_period_end, p_project_id;
|
||||
ELSE
|
||||
-- Ejecutar formula SQL directamente (con cuidado de seguridad)
|
||||
-- En produccion esto debe ser mas restrictivo
|
||||
EXECUTE v_kpi.formula
|
||||
INTO v_result
|
||||
USING p_tenant_id, p_period_start, p_period_end, p_project_id;
|
||||
END IF;
|
||||
|
||||
RETURN v_result;
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
RAISE WARNING 'Error calculating KPI %: %', p_kpi_id, SQLERRM;
|
||||
RETURN NULL;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Funcion para determinar color de semaforo
|
||||
CREATE OR REPLACE FUNCTION reports.get_kpi_status(
|
||||
p_value DECIMAL(18,4),
|
||||
p_threshold_green DECIMAL(18,4),
|
||||
p_threshold_yellow DECIMAL(18,4),
|
||||
p_invert_colors BOOLEAN DEFAULT FALSE
|
||||
)
|
||||
RETURNS VARCHAR(10) AS $$
|
||||
BEGIN
|
||||
IF p_threshold_green IS NULL OR p_threshold_yellow IS NULL THEN
|
||||
RETURN NULL;
|
||||
END IF;
|
||||
|
||||
IF p_invert_colors THEN
|
||||
-- Menor es mejor
|
||||
IF p_value <= p_threshold_green THEN
|
||||
RETURN 'green';
|
||||
ELSIF p_value <= p_threshold_yellow THEN
|
||||
RETURN 'yellow';
|
||||
ELSE
|
||||
RETURN 'red';
|
||||
END IF;
|
||||
ELSE
|
||||
-- Mayor es mejor
|
||||
IF p_value >= p_threshold_green THEN
|
||||
RETURN 'green';
|
||||
ELSIF p_value >= p_threshold_yellow THEN
|
||||
RETURN 'yellow';
|
||||
ELSE
|
||||
RETURN 'red';
|
||||
END IF;
|
||||
END IF;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql IMMUTABLE;
|
||||
|
||||
-- ============================================================================
|
||||
-- COMENTARIOS DE DOCUMENTACION
|
||||
-- ============================================================================
|
||||
|
||||
COMMENT ON TABLE reports.kpis_config IS 'Configuracion de KPIs dinamicos con formulas (GAP-001)';
|
||||
COMMENT ON COLUMN reports.kpis_config.formula IS 'Formula SQL o expresion para calcular el KPI';
|
||||
COMMENT ON COLUMN reports.kpis_config.threshold_green IS 'Umbral para status verde';
|
||||
COMMENT ON COLUMN reports.kpis_config.threshold_yellow IS 'Umbral para status amarillo';
|
||||
COMMENT ON COLUMN reports.kpis_config.invert_colors IS 'TRUE si valores menores son mejores';
|
||||
|
||||
COMMENT ON TABLE reports.kpis_values IS 'Valores calculados historicos de KPIs (GAP-001)';
|
||||
COMMENT ON COLUMN reports.kpis_values.variance_value IS 'Diferencia absoluta: value - target';
|
||||
COMMENT ON COLUMN reports.kpis_values.variance_percentage IS 'Diferencia porcentual respecto al target';
|
||||
|
||||
-- ============================================================================
|
||||
-- FIN DEL SCRIPT
|
||||
-- ============================================================================
|
||||
Loading…
Reference in New Issue
Block a user