Agrego carpeta del source sql de la Base de Datos

This commit is contained in:
Pablo
2026-08-22 19:10:49 -03:00
parent 11e36bd6c2
commit 88d724fbcf
94 changed files with 7820 additions and 0 deletions
@@ -0,0 +1,101 @@
-- ============================================================================
-- internal.limpiar_huerfanas_resueltas
-- ============================================================================
-- PROPÓSITO
-- Higiene operativa: elimina filas de public.reservas_huerfanas que ya
-- pasaron a estado terminal (reubicado / resuelta) Y cumplieron la
-- ventana de gracia configurada. Una huérfana es un snapshot
-- desconectado que existe sólo para que el operador (o el cliente)
-- reubique o cierre la reserva rescatada; una vez resuelta y pasada la
-- gracia, cumplió su función y se descarta.
--
-- DOMINIO
-- Implementa la higiene de huérfanas descrita en
-- Documentation/PoliticaRetencion.md §7. El criterio combina ESTADO y
-- ANTIGÜEDAD del cambio de estado:
-- - `pendiente` nunca se purga (sigue siendo trabajo no hecho), por
-- más vieja que sea.
-- - Una terminal recién es elegible cuando pasaron al menos
-- `retencion.huerfanas_resueltas_dias` (default 14) desde su
-- `resuelto_en`.
-- La ventana es el piso de seguridad para que el operador pueda
-- revertir un cambio de estado por error: cambiar la fila otra vez a
-- `pendiente` resetea resuelto_en a NULL y le devuelve la inmunidad.
--
-- CRITERIO DE PURGA
-- Se elimina toda fila con `estado_resolucion <> 'pendiente'` Y
-- `resuelto_en < now() - INTERVAL N días`. Se expresa por negación
-- de `pendiente` (no enumerando los terminales) para que cualquier
-- estado terminal que se agregue en el futuro herede la purga
-- automáticamente, mientras `pendiente` siga siendo el único estado
-- protegido. El `IS NOT NULL` extra en resuelto_en es defensa en
-- profundidad: el CHECK de la tabla ya garantiza el invariante, pero
-- si una migración o un fix de datos dejara una fila inconsistente
-- no la queremos arrastrar.
--
-- INTERACCIÓN CON LA CADENCIA MENSUAL
-- Esta función la dispara el pipeline mensual (US-R17) el día 1 a
-- las 03:00 AR, junto con el resto de las purgas. La ventana de N
-- días es el PISO de vida; el techo lo pone la cadencia del cron.
-- En la práctica, una terminal vive entre ~N+1 y ~N+31 días según
-- en qué momento del mes se haya marcado. La ventana cubre el caso
-- patológico de "resuelta a las 23:59 del 31, purgada a las 03:00
-- del 1" que existía cuando el criterio era sólo de estado.
--
-- PARÁMETROS
-- Ninguno. Lee `retencion.huerfanas_resueltas_dias` (default 14).
--
-- AUTORIZACIÓN
-- No valida permiso propio. El schema `internal` no es alcanzable por
-- roles cliente (ver hardening en `database/schema/00_schemas.sql`).
-- Callers legítimos: pg_cron y fachadas `public.fc_*` SECURITY DEFINER
-- que orquesten el pipeline mensual (US-R17).
--
-- ERRORES (RAISE EXCEPTION)
-- Ninguno propio.
--
-- RETORNA
-- JSONB con {status, huerfanas_eliminadas, dias_gracia, mensaje}.
--
-- EFECTOS SECUNDARIOS
-- DELETE de reservas_huerfanas elegibles. No registra evento.
-- ============================================================================
CREATE OR REPLACE FUNCTION internal.limpiar_huerfanas_resueltas()
RETURNS JSONB
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
SET timezone = 'America/Argentina/Buenos_Aires'
VOLATILE
AS $$
DECLARE
v_dias_gracia INT;
v_eliminadas INT;
BEGIN
v_dias_gracia := internal.get_config_int('retencion.huerfanas_resueltas_dias', 14);
DELETE FROM reservas_huerfanas
WHERE estado_resolucion <> 'pendiente'
AND resuelto_en IS NOT NULL
AND resuelto_en < now() - (v_dias_gracia * INTERVAL '1 day');
GET DIAGNOSTICS v_eliminadas = ROW_COUNT;
RETURN jsonb_build_object(
'status', 'success',
'huerfanas_eliminadas', v_eliminadas,
'dias_gracia', v_dias_gracia,
'mensaje', format(
'Se eliminaron %s huérfanas resueltas/reubicadas con más de %s días desde su cambio de estado (las pendientes y las en gracia se conservan).',
v_eliminadas, v_dias_gracia
)
);
END;
$$;
-- Defensa en profundidad: aunque el hardening del schema (00_schemas.sql)
-- ya bloquee USAGE y EXECUTE por default, revocamos explícitamente acá
-- por si en algún futuro alguien afloja las defensas a nivel schema.
REVOKE ALL ON FUNCTION internal.limpiar_huerfanas_resueltas()
FROM PUBLIC, anon, authenticated;
@@ -0,0 +1,90 @@
-- ============================================================================
-- internal.limpiar_turnos_antiguos
-- ============================================================================
-- PROPÓSITO
-- Higiene operativa: elimina turnos cuya `fecha` esté fuera de la
-- ventana de retención. La purga es incondicional: borra todos los
-- turnos pasados la ventana, hayan tenido reservas o no. Las reservas
-- vinculadas caen por CASCADE (reservas_turno_id_fkey ON DELETE
-- CASCADE).
--
-- DOMINIO
-- Implementa la higiene de turnos descrita en
-- Documentation/PoliticaRetencion.md §7. El sistema es operativo, no
-- archivo histórico: lo que pasó la ventana se descarta. La
-- concurrencia mensual que sí vale conservar se archiva agregada en
-- el XLSX (§3, §4.1), no en la base.
--
-- El diseño anterior preservaba turnos con reservas como "histórico".
-- Ese trade-off quedó obsoleto: con la política operativa + archivo,
-- el histórico vive en el XLSX agregado y la base no carga ese rol.
-- La excepción se retira; los turnos pasados se purgan parejo.
--
-- PARÁMETROS
-- Ninguno. La ventana en meses se lee de `internal.app_config` con la
-- clave `retencion.turnos_meses` (default 2). El ajuste operativo se
-- hace en la tabla, sin redeploy.
--
-- AUTORIZACIÓN
-- No valida permiso propio. El schema `internal` no es alcanzable por
-- roles cliente (ver hardening en `database/schema/00_schemas.sql`).
-- Los callers legítimos son `pg_cron` (corre como postgres) y, si en
-- algún momento aplica, una fachada pública `SECURITY DEFINER` que
-- orqueste el pipeline mensual (US-R17). Ninguna ruta de cliente
-- llega acá.
--
-- ERRORES (RAISE EXCEPTION)
-- Ninguno propio.
--
-- RETORNA
-- JSONB con {status, fecha_corte, meses_conservados, turnos_eliminados,
-- mensaje}. Las reservas eliminadas por CASCADE no se cuentan: la
-- métrica que importa para el archivo (totales y cancelaciones por
-- actividad/mes) se calcula en otra función antes del DELETE.
--
-- EFECTOS SECUNDARIOS
-- DELETE de turnos con fecha < (CURRENT_DATE - ventana). Las reservas
-- asociadas caen por CASCADE. No registra evento.
-- ============================================================================
CREATE OR REPLACE FUNCTION internal.limpiar_turnos_antiguos()
RETURNS JSONB
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
SET timezone = 'America/Argentina/Buenos_Aires'
VOLATILE
AS $$
DECLARE
v_meses INT;
v_fecha_limite DATE;
v_eliminados INT;
BEGIN
v_meses := internal.get_config_int('retencion.turnos_meses', 2);
v_fecha_limite := CURRENT_DATE - (v_meses * INTERVAL '1 month');
DELETE FROM turnos
WHERE fecha < v_fecha_limite;
GET DIAGNOSTICS v_eliminados = ROW_COUNT;
RETURN jsonb_build_object(
'status', 'success',
'fecha_corte', v_fecha_limite,
'meses_conservados', v_meses,
'turnos_eliminados', v_eliminados,
'mensaje', format(
'Se eliminaron %s turnos anteriores a %s (reservas asociadas eliminadas por CASCADE).',
v_eliminados, v_fecha_limite
)
);
END;
$$;
-- Defensa en profundidad: aunque el hardening del schema (00_schemas.sql)
-- ya bloquee USAGE y EXECUTE por default, revocamos explícitamente acá
-- por si en algún futuro alguien afloja las defensas a nivel schema.
REVOKE ALL ON FUNCTION internal.limpiar_turnos_antiguos()
FROM PUBLIC, anon, authenticated;