Files
cruce-cuentas-glm-centralizado/SUPABASE_Cruce_Cuentas_Bono14_Guatemala_READY.sql

304 lines
8.6 KiB
PL/PgSQL

-- ============================================================
-- Cruce de Cuentas GLM - Bono 14 Guatemala
-- Script incremental / idempotente
-- ============================================================
-- Objetivo:
-- Habilitar y verificar que Bono 14 use la MISMA infraestructura
-- de reporte único ya instalada, pero con una identidad independiente:
--
-- GT-<AÑO>-07-bono14
--
-- Ejemplo:
-- GT-2026-07-bono14
--
-- No crea otra tabla de históricos y no elimina datos existentes.
-- Puede ejecutarse nuevamente sin borrar reportes.
-- ============================================================
begin;
-- ------------------------------------------------------------
-- 1. Validación de prerrequisitos de la versión "reporte único"
-- ------------------------------------------------------------
do $$
declare
v_missing text[] := array[]::text[];
begin
if to_regclass('public.cruces_cuentas_gt_reportes') is null then
raise exception
'No existe public.cruces_cuentas_gt_reportes. Instala primero la estructura base de Cruce de Cuentas.';
end if;
if not exists (
select 1
from information_schema.columns
where table_schema = 'public'
and table_name = 'cruces_cuentas_gt_reportes'
and column_name = 'report_identity'
) then
v_missing := array_append(v_missing, 'report_identity');
end if;
if not exists (
select 1
from information_schema.columns
where table_schema = 'public'
and table_name = 'cruces_cuentas_gt_reportes'
and column_name = 'updated_at'
) then
v_missing := array_append(v_missing, 'updated_at');
end if;
if not exists (
select 1
from information_schema.columns
where table_schema = 'public'
and table_name = 'cruces_cuentas_gt_reportes'
and column_name = 'country'
) then
v_missing := array_append(v_missing, 'country');
end if;
if not exists (
select 1
from information_schema.columns
where table_schema = 'public'
and table_name = 'cruces_cuentas_gt_reportes'
and column_name = 'year'
) then
v_missing := array_append(v_missing, 'year');
end if;
if not exists (
select 1
from information_schema.columns
where table_schema = 'public'
and table_name = 'cruces_cuentas_gt_reportes'
and column_name = 'month'
) then
v_missing := array_append(v_missing, 'month');
end if;
if not exists (
select 1
from information_schema.columns
where table_schema = 'public'
and table_name = 'cruces_cuentas_gt_reportes'
and column_name = 'period_type'
) then
v_missing := array_append(v_missing, 'period_type');
end if;
if cardinality(v_missing) > 0 then
raise exception
'Faltan columnas requeridas en public.cruces_cuentas_gt_reportes: %. Ejecuta primero el script de REPORTE ÚNICO POR PERÍODO.',
array_to_string(v_missing, ', ');
end if;
end;
$$;
-- ------------------------------------------------------------
-- 2. Constructor canónico de identidad
-- ------------------------------------------------------------
-- Se mantiene genérico para nómina GT, nómina TT y Bono 14.
create or replace function public.cruce_cuentas_build_report_identity(
p_country text,
p_year integer,
p_month integer,
p_period_type text
)
returns text
language sql
immutable
as $$
select
upper(trim(coalesce(p_country, 'GT')))
|| '-' ||
coalesce(p_year, 0)::text
|| '-' ||
lpad(coalesce(p_month, 0)::text, 2, '0')
|| '-' ||
lower(trim(coalesce(p_period_type, 'periodo')));
$$;
-- ------------------------------------------------------------
-- 3. Trigger de identidad / updated_at
-- ------------------------------------------------------------
-- Reinstalado de forma idempotente para garantizar que un UPSERT de
-- Bono 14 mantenga la misma identidad y actualice updated_at.
create or replace function public.cruce_cuentas_set_report_identity()
returns trigger
language plpgsql
set search_path = public
as $$
declare
v_canonical text;
begin
v_canonical :=
public.cruce_cuentas_build_report_identity(
coalesce(new.country, 'GT'),
new.year,
new.month,
new.period_type
);
if tg_op = 'INSERT' then
if nullif(trim(coalesce(new.report_identity, '')), '') is null then
new.report_identity := v_canonical;
end if;
else
-- Los duplicados históricos antiguos conservan su sufijo :legacy:.
if coalesce(old.report_identity, '') like '%:legacy:%' then
new.report_identity := old.report_identity;
else
new.report_identity := v_canonical;
end if;
end if;
new.updated_at := now();
return new;
end;
$$;
drop trigger if exists trg_cruce_cuentas_report_identity
on public.cruces_cuentas_gt_reportes;
create trigger trg_cruce_cuentas_report_identity
before insert or update on public.cruces_cuentas_gt_reportes
for each row
execute function public.cruce_cuentas_set_report_identity();
-- ------------------------------------------------------------
-- 4. Garantizar unicidad por identidad
-- ------------------------------------------------------------
-- La versión de reporte único ya debe tener este índice. Se valida antes
-- de recrearlo para no alterar ni eliminar históricos existentes.
do $$
begin
if not exists (
select 1
from pg_indexes
where schemaname = 'public'
and tablename = 'cruces_cuentas_gt_reportes'
and indexname = 'ux_cruces_cuentas_reportes_report_identity'
) then
if exists (
select report_identity
from public.cruces_cuentas_gt_reportes
where report_identity is not null
group by report_identity
having count(*) > 1
) then
raise exception
'Existen report_identity duplicados. Ejecuta primero el script de REPORTE ÚNICO POR PERÍODO para aplicar su backfill seguro antes de Bono 14.';
end if;
create unique index ux_cruces_cuentas_reportes_report_identity
on public.cruces_cuentas_gt_reportes (report_identity);
end if;
end;
$$;
-- Índice auxiliar específico para consultas/soporte de Bono 14.
create index if not exists ix_cruces_cuentas_gt_reportes_bono14_updated
on public.cruces_cuentas_gt_reportes (year, updated_at desc)
where upper(coalesce(country, 'GT')) = 'GT'
and lower(coalesce(period_type, '')) = 'bono14';
-- ------------------------------------------------------------
-- 5. RPC que n8n consulta ANTES de crear/reutilizar el Google Sheet
-- ------------------------------------------------------------
create or replace function public.cruce_cuentas_get_reporte_periodo(
p_country text,
p_year integer,
p_month integer,
p_period_type text
)
returns jsonb
language plpgsql
stable
security definer
set search_path = public
as $$
declare
v_identity text;
v_report public.cruces_cuentas_gt_reportes%rowtype;
begin
v_identity :=
public.cruce_cuentas_build_report_identity(
p_country,
p_year,
p_month,
p_period_type
);
select r.*
into v_report
from public.cruces_cuentas_gt_reportes r
where r.report_identity = v_identity
order by
r.updated_at desc nulls last,
r.created_at desc nulls last
limit 1;
if not found then
return jsonb_build_object(
'ok', true,
'found', false,
'report_identity', v_identity,
'report', null
);
end if;
return jsonb_build_object(
'ok', true,
'found', true,
'report_identity', v_identity,
'report', jsonb_build_object(
'id', v_report.id,
'report_identity', v_report.report_identity,
'country', v_report.country,
'year', v_report.year,
'month', v_report.month,
'period_type', v_report.period_type,
'period_label', v_report.period_label,
'spreadsheet_id', v_report.spreadsheet_id,
'report_url', v_report.report_url,
'estado', v_report.estado,
'created_at', v_report.created_at,
'updated_at', v_report.updated_at
)
);
end;
$$;
revoke all on function public.cruce_cuentas_get_reporte_periodo(text, integer, integer, text)
from public, anon, authenticated;
grant execute on function public.cruce_cuentas_get_reporte_periodo(text, integer, integer, text)
to service_role;
commit;
-- ============================================================
-- VALIDACIÓN FINAL (solo lectura)
-- Debe devolver: GT-2026-07-bono14
-- ============================================================
select
public.cruce_cuentas_build_report_identity(
'GT',
2026,
7,
'bono14'
) as identidad_bono14_esperada;
-- Si ya existe un Bono 14 2026, esta consulta devuelve found=true.
-- Si todavía no se ha generado, devuelve found=false (lo esperado antes
-- de la primera ejecución).
select public.cruce_cuentas_get_reporte_periodo(
'GT',
2026,
7,
'bono14'
) as estado_bono14_2026;