Agrego carpeta del source sql de la Base de Datos
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user