Agregar Bono 14 Guatemala y actualizar dist
This commit is contained in:
@@ -0,0 +1,407 @@
|
||||
-- Cruce de Cuentas GLM
|
||||
-- Equivalencias validadas para falsos positivos de "Banco sin BambooHR"
|
||||
-- Ejecutar en el SQL Editor del Supabase empresarial.
|
||||
-- Script idempotente: puede ejecutarse nuevamente sin borrar datos existentes.
|
||||
|
||||
begin;
|
||||
|
||||
create extension if not exists pgcrypto;
|
||||
|
||||
create or replace function public.cruce_cuentas_normalizar_nombre(p_value text)
|
||||
returns text
|
||||
language sql
|
||||
immutable
|
||||
strict
|
||||
as $$
|
||||
select trim(
|
||||
regexp_replace(
|
||||
translate(
|
||||
lower(coalesce(p_value, '')),
|
||||
'áàäâãåéèëêíìïîóòöôõúùüûñçÁÀÄÂÃÅÉÈËÊÍÌÏÎÓÒÖÔÕÚÙÜÛÑÇ',
|
||||
'aaaaaaeeeeiiiiooooouuuuncAAAAAAEEEEIIIIOOOOOUUUUNC'
|
||||
),
|
||||
'[^a-z0-9]+',
|
||||
' ',
|
||||
'g'
|
||||
)
|
||||
);
|
||||
$$;
|
||||
|
||||
create table if not exists public.cruce_cuentas_bamboo_aliases (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
pais text not null check (pais in ('GT', 'TT')),
|
||||
nombre_origen text not null,
|
||||
nombre_origen_normalizado text not null,
|
||||
cuenta_bancaria text not null default '',
|
||||
bamboo_employee_number text,
|
||||
bamboo_employee_id text,
|
||||
bamboo_nombre text not null,
|
||||
motivo text,
|
||||
comentario text,
|
||||
activo boolean not null default true,
|
||||
validado_por_email text not null,
|
||||
validado_por_nombre text,
|
||||
last_reported_at timestamptz not null default now(),
|
||||
created_at timestamptz not null default now(),
|
||||
updated_at timestamptz not null default now()
|
||||
);
|
||||
|
||||
-- Compatibilidad si una versión anterior del script creó la tabla sin cuenta.
|
||||
alter table public.cruce_cuentas_bamboo_aliases
|
||||
add column if not exists cuenta_bancaria text not null default '';
|
||||
|
||||
drop index if exists public.ux_cruce_cuentas_bamboo_aliases_pais_nombre;
|
||||
|
||||
create unique index if not exists ux_cruce_cuentas_bamboo_aliases_pais_nombre_cuenta
|
||||
on public.cruce_cuentas_bamboo_aliases (pais, nombre_origen_normalizado, cuenta_bancaria);
|
||||
|
||||
create index if not exists ix_cruce_cuentas_bamboo_aliases_employee_number
|
||||
on public.cruce_cuentas_bamboo_aliases (pais, bamboo_employee_number)
|
||||
where activo = true and bamboo_employee_number is not null;
|
||||
|
||||
create table if not exists public.cruce_cuentas_bamboo_correcciones (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
pais text not null check (pais in ('GT', 'TT')),
|
||||
nombre_origen_principal text not null,
|
||||
nombres_origen text[] not null default array[]::text[],
|
||||
cuenta_bancaria text,
|
||||
monto_banco numeric(18,2),
|
||||
moneda text,
|
||||
bamboo_employee_number text,
|
||||
bamboo_employee_id text,
|
||||
bamboo_nombre text not null,
|
||||
motivo text not null,
|
||||
comentario text,
|
||||
execution_id text,
|
||||
report_url text,
|
||||
period_label text,
|
||||
period_start date,
|
||||
period_end date,
|
||||
solicitado_por_email text not null,
|
||||
solicitado_por_nombre text,
|
||||
destinatarios_rrhh text[] not null default array[]::text[],
|
||||
alias_ids uuid[] not null default array[]::uuid[],
|
||||
estado text not null default 'pendiente_notificacion'
|
||||
check (estado in ('pendiente_notificacion', 'notificado_rrhh', 'corregido_bamboo', 'cancelado')),
|
||||
correo_enviado boolean not null default false,
|
||||
correo_enviado_at timestamptz,
|
||||
created_at timestamptz not null default now(),
|
||||
updated_at timestamptz not null default now()
|
||||
);
|
||||
|
||||
create index if not exists ix_cruce_cuentas_bamboo_correcciones_pais_created
|
||||
on public.cruce_cuentas_bamboo_correcciones (pais, created_at desc);
|
||||
|
||||
create index if not exists ix_cruce_cuentas_bamboo_correcciones_estado
|
||||
on public.cruce_cuentas_bamboo_correcciones (estado, created_at desc);
|
||||
|
||||
create or replace function public.cruce_cuentas_bamboo_set_normalized()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
set search_path = public
|
||||
as $$
|
||||
begin
|
||||
new.pais := upper(trim(new.pais));
|
||||
new.nombre_origen := trim(new.nombre_origen);
|
||||
new.nombre_origen_normalizado := public.cruce_cuentas_normalizar_nombre(new.nombre_origen);
|
||||
new.cuenta_bancaria := regexp_replace(coalesce(new.cuenta_bancaria, ''), '[^0-9A-Za-z]+', '', 'g');
|
||||
new.validado_por_email := lower(trim(new.validado_por_email));
|
||||
new.updated_at := now();
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
drop trigger if exists trg_cruce_cuentas_bamboo_alias_normalized
|
||||
on public.cruce_cuentas_bamboo_aliases;
|
||||
|
||||
create trigger trg_cruce_cuentas_bamboo_alias_normalized
|
||||
before insert or update on public.cruce_cuentas_bamboo_aliases
|
||||
for each row execute function public.cruce_cuentas_bamboo_set_normalized();
|
||||
|
||||
create or replace function public.cruce_cuentas_bamboo_touch_updated_at()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
set search_path = public
|
||||
as $$
|
||||
begin
|
||||
new.updated_at := now();
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
drop trigger if exists trg_cruce_cuentas_bamboo_correcciones_updated_at
|
||||
on public.cruce_cuentas_bamboo_correcciones;
|
||||
|
||||
create trigger trg_cruce_cuentas_bamboo_correcciones_updated_at
|
||||
before update on public.cruce_cuentas_bamboo_correcciones
|
||||
for each row execute function public.cruce_cuentas_bamboo_touch_updated_at();
|
||||
|
||||
-- RPC consumida únicamente por los workflows generadores de reportes.
|
||||
-- Siempre devuelve un objeto con aliases=[], incluso cuando no existen registros.
|
||||
create or replace function public.cruce_cuentas_bamboo_aliases_activos(p_pais text)
|
||||
returns jsonb
|
||||
language sql
|
||||
stable
|
||||
security definer
|
||||
set search_path = public
|
||||
as $$
|
||||
select jsonb_build_object(
|
||||
'ok', true,
|
||||
'pais', upper(trim(coalesce(p_pais, ''))),
|
||||
'aliases', coalesce(
|
||||
jsonb_agg(
|
||||
jsonb_build_object(
|
||||
'id', a.id,
|
||||
'pais', a.pais,
|
||||
'nombre_origen', a.nombre_origen,
|
||||
'nombre_origen_normalizado', a.nombre_origen_normalizado,
|
||||
'cuenta_bancaria', a.cuenta_bancaria,
|
||||
'bamboo_employee_number', a.bamboo_employee_number,
|
||||
'bamboo_employee_id', a.bamboo_employee_id,
|
||||
'bamboo_nombre', a.bamboo_nombre,
|
||||
'motivo', a.motivo,
|
||||
'activo', a.activo
|
||||
)
|
||||
order by a.updated_at desc
|
||||
) filter (where a.id is not null),
|
||||
'[]'::jsonb
|
||||
)
|
||||
)
|
||||
from public.cruce_cuentas_bamboo_aliases a
|
||||
where a.activo = true
|
||||
and a.pais = upper(trim(coalesce(p_pais, '')))
|
||||
and upper(trim(coalesce(p_pais, ''))) in ('GT', 'TT');
|
||||
$$;
|
||||
|
||||
alter table public.cruce_cuentas_bamboo_aliases enable row level security;
|
||||
alter table public.cruce_cuentas_bamboo_correcciones enable row level security;
|
||||
|
||||
-- El navegador nunca escribe ni lee estas tablas directamente.
|
||||
-- Toda operación pasa por n8n con service_role.
|
||||
revoke all on table public.cruce_cuentas_bamboo_aliases from anon, authenticated;
|
||||
revoke all on table public.cruce_cuentas_bamboo_correcciones from anon, authenticated;
|
||||
grant all on table public.cruce_cuentas_bamboo_aliases to service_role;
|
||||
grant all on table public.cruce_cuentas_bamboo_correcciones to service_role;
|
||||
|
||||
revoke all on function public.cruce_cuentas_bamboo_aliases_activos(text) from public, anon, authenticated;
|
||||
grant execute on function public.cruce_cuentas_bamboo_aliases_activos(text) to service_role;
|
||||
|
||||
-- ============================================================
|
||||
-- HISTÓRICOS: conservar y recuperar Banco sin Bamboo por reporte
|
||||
-- ============================================================
|
||||
|
||||
-- Los workflows ya envían el detalle de cada caso. Esta columna hace que el
|
||||
-- detalle quede persistido y pueda abrirse días después desde Históricos.
|
||||
alter table public.cruces_cuentas_gt_reportes
|
||||
add column if not exists detalle_banco_sin_bamboo jsonb not null default '[]'::jsonb;
|
||||
|
||||
-- Identificador generado por el navegador para trazabilidad de una ejecución.
|
||||
-- No se usa como llave única porque no se reintenta automáticamente un POST
|
||||
-- de conciliación (evita duplicar reportes, correos o Sheets).
|
||||
alter table public.cruces_cuentas_gt_reportes
|
||||
add column if not exists client_request_id text;
|
||||
|
||||
update public.cruces_cuentas_gt_reportes
|
||||
set detalle_banco_sin_bamboo = '[]'::jsonb
|
||||
where detalle_banco_sin_bamboo is null;
|
||||
|
||||
create index if not exists ix_cruces_cuentas_reportes_client_request
|
||||
on public.cruces_cuentas_gt_reportes (client_request_id)
|
||||
where client_request_id is not null;
|
||||
|
||||
create index if not exists ix_cruce_cuentas_bamboo_correcciones_execution
|
||||
on public.cruce_cuentas_bamboo_correcciones (execution_id, created_at desc)
|
||||
where execution_id is not null;
|
||||
|
||||
-- Reemplaza la RPC histórica manteniendo exactamente la misma firma y
|
||||
-- estructura pública, pero excluye detalle_banco_sin_bamboo de la lista para
|
||||
-- no descargar todos los casos cada vez que se abre Históricos. El detalle se
|
||||
-- solicita únicamente cuando el usuario pulsa Banco sin Bamboo.
|
||||
create or replace function public.cruce_cuentas_get_historicos(
|
||||
p_country text,
|
||||
p_page integer default 1,
|
||||
p_page_size integer default 10,
|
||||
p_search text default null,
|
||||
p_status text default null
|
||||
)
|
||||
returns jsonb
|
||||
language plpgsql
|
||||
security definer
|
||||
set search_path = public
|
||||
as $$
|
||||
declare
|
||||
v_country text := upper(trim(coalesce(p_country, ''));
|
||||
v_page integer := greatest(coalesce(p_page, 1), 1);
|
||||
v_page_size integer := least(greatest(coalesce(p_page_size, 10), 1), 100);
|
||||
v_offset integer;
|
||||
v_search text := nullif(trim(coalesce(p_search, '')), '');
|
||||
v_status text := nullif(trim(coalesce(p_status, '')), '');
|
||||
v_total bigint;
|
||||
v_filtered_total bigint;
|
||||
v_reports jsonb;
|
||||
begin
|
||||
if v_country not in ('GT', 'TT') then
|
||||
raise exception 'País no permitido: %', v_country;
|
||||
end if;
|
||||
|
||||
v_offset := (v_page - 1) * v_page_size;
|
||||
|
||||
select count(*)
|
||||
into v_total
|
||||
from public.cruces_cuentas_gt_reportes r
|
||||
where upper(coalesce(r.country, 'GT')) = v_country;
|
||||
|
||||
select count(*)
|
||||
into v_filtered_total
|
||||
from public.cruces_cuentas_gt_reportes r
|
||||
where upper(coalesce(r.country, 'GT')) = v_country
|
||||
and (v_status is null or r.estado = v_status)
|
||||
and (
|
||||
v_search is null
|
||||
or coalesce(r.period_label, '') ilike '%' || v_search || '%'
|
||||
or coalesce(r.payroll_file_name, '') ilike '%' || v_search || '%'
|
||||
or coalesce(r.ejecutado_por_nombre, '') ilike '%' || v_search || '%'
|
||||
or coalesce(r.resuelto_por_nombre, '') ilike '%' || v_search || '%'
|
||||
or r.id::text ilike '%' || v_search || '%'
|
||||
);
|
||||
|
||||
select coalesce(
|
||||
jsonb_agg(
|
||||
(to_jsonb(q) - 'detalle_banco_sin_bamboo')
|
||||
order by q.created_at desc
|
||||
),
|
||||
'[]'::jsonb
|
||||
)
|
||||
into v_reports
|
||||
from (
|
||||
select r.*
|
||||
from public.cruces_cuentas_gt_reportes r
|
||||
where upper(coalesce(r.country, 'GT')) = v_country
|
||||
and (v_status is null or r.estado = v_status)
|
||||
and (
|
||||
v_search is null
|
||||
or coalesce(r.period_label, '') ilike '%' || v_search || '%'
|
||||
or coalesce(r.payroll_file_name, '') ilike '%' || v_search || '%'
|
||||
or coalesce(r.ejecutado_por_nombre, '') ilike '%' || v_search || '%'
|
||||
or coalesce(r.resuelto_por_nombre, '') ilike '%' || v_search || '%'
|
||||
or r.id::text ilike '%' || v_search || '%'
|
||||
)
|
||||
order by r.created_at desc
|
||||
limit v_page_size
|
||||
offset v_offset
|
||||
) q;
|
||||
|
||||
return jsonb_build_object(
|
||||
'ok', true,
|
||||
'country', v_country,
|
||||
'page', v_page,
|
||||
'page_size', v_page_size,
|
||||
'total', v_total,
|
||||
'filtered_total', v_filtered_total,
|
||||
'total_pages', greatest(ceil(v_filtered_total::numeric / v_page_size)::integer, 1),
|
||||
'reports', v_reports
|
||||
);
|
||||
end;
|
||||
$$;
|
||||
|
||||
revoke all on function public.cruce_cuentas_get_historicos(text, integer, integer, text, text) from public, anon;
|
||||
grant execute on function public.cruce_cuentas_get_historicos(text, integer, integer, text, text) to authenticated;
|
||||
|
||||
-- RPC de detalle utilizada por Históricos al abrir un reporte específico.
|
||||
-- No otorga SELECT directo sobre las tablas de aliases/correcciones.
|
||||
create or replace function public.cruce_cuentas_get_banco_sin_bamboo_reporte(
|
||||
p_report_id text,
|
||||
p_country text
|
||||
)
|
||||
returns jsonb
|
||||
language plpgsql
|
||||
stable
|
||||
security definer
|
||||
set search_path = public
|
||||
as $$
|
||||
declare
|
||||
v_country text := upper(trim(coalesce(p_country, ''));
|
||||
v_report public.cruces_cuentas_gt_reportes%rowtype;
|
||||
v_corrections jsonb := '[]'::jsonb;
|
||||
begin
|
||||
if auth.uid() is null then
|
||||
raise exception 'No autenticado.';
|
||||
end if;
|
||||
|
||||
if v_country not in ('GT', 'TT') then
|
||||
raise exception 'País no permitido: %', v_country;
|
||||
end if;
|
||||
|
||||
select r.*
|
||||
into v_report
|
||||
from public.cruces_cuentas_gt_reportes r
|
||||
where r.id::text = trim(coalesce(p_report_id, ''))
|
||||
and upper(coalesce(r.country, 'GT')) = v_country
|
||||
limit 1;
|
||||
|
||||
if not found then
|
||||
return jsonb_build_object(
|
||||
'ok', false,
|
||||
'message', 'No se encontró el reporte solicitado.'
|
||||
);
|
||||
end if;
|
||||
|
||||
select coalesce(
|
||||
jsonb_agg(
|
||||
jsonb_build_object(
|
||||
'id', c.id,
|
||||
'pais', c.pais,
|
||||
'nombre_origen_principal', c.nombre_origen_principal,
|
||||
'nombres_origen', c.nombres_origen,
|
||||
'cuenta_bancaria', c.cuenta_bancaria,
|
||||
'bamboo_employee_number', c.bamboo_employee_number,
|
||||
'bamboo_nombre', c.bamboo_nombre,
|
||||
'estado', c.estado,
|
||||
'correo_enviado', c.correo_enviado,
|
||||
'correo_enviado_at', c.correo_enviado_at,
|
||||
'created_at', c.created_at
|
||||
)
|
||||
order by c.created_at desc
|
||||
),
|
||||
'[]'::jsonb
|
||||
)
|
||||
into v_corrections
|
||||
from public.cruce_cuentas_bamboo_correcciones c
|
||||
where c.pais = v_country
|
||||
and (
|
||||
c.execution_id = v_report.id::text
|
||||
or (
|
||||
nullif(trim(coalesce(c.execution_id, '')), '') is null
|
||||
and nullif(trim(coalesce(c.report_url, '')), '') is not null
|
||||
and c.report_url = v_report.report_url
|
||||
)
|
||||
);
|
||||
|
||||
return jsonb_build_object(
|
||||
'ok', true,
|
||||
'report', jsonb_build_object(
|
||||
'id', v_report.id,
|
||||
'country', v_report.country,
|
||||
'period_label', v_report.period_label,
|
||||
'period_start', v_report.period_start,
|
||||
'period_end', v_report.period_end,
|
||||
'report_url', v_report.report_url,
|
||||
'banco_sin_bamboo', v_report.banco_sin_bamboo
|
||||
),
|
||||
'cases', coalesce(v_report.detalle_banco_sin_bamboo, '[]'::jsonb),
|
||||
'corrections', v_corrections
|
||||
);
|
||||
end;
|
||||
$$;
|
||||
|
||||
revoke all on function public.cruce_cuentas_get_banco_sin_bamboo_reporte(text, text) from public, anon;
|
||||
grant execute on function public.cruce_cuentas_get_banco_sin_bamboo_reporte(text, text) to authenticated;
|
||||
|
||||
commit;
|
||||
|
||||
-- Validaciones opcionales después de ejecutar:
|
||||
-- select public.cruce_cuentas_bamboo_aliases_activos('GT');
|
||||
-- select public.cruce_cuentas_bamboo_aliases_activos('TT');
|
||||
-- select column_name, data_type from information_schema.columns
|
||||
-- where table_schema='public' and table_name='cruces_cuentas_gt_reportes'
|
||||
-- and column_name in ('detalle_banco_sin_bamboo', 'client_request_id');
|
||||
Reference in New Issue
Block a user