Files
seguimiento-impuestos/supabase/actualizacion_bamboohr_google_calendar.sql
T

267 lines
9.2 KiB
PL/PgSQL

-- Seguimiento de Impuestos GLM
-- Actualización: contactos BambooHR, edición ampliada, Google Calendar y bloqueo de fechas pasadas.
-- Ejecutar una sola vez en Supabase SQL Editor antes de desplegar el frontend y activar los workflows.
begin;
-- ---------------------------------------------------------------------------
-- Contactos: metadatos de BambooHR y tercer canal de notificación
-- ---------------------------------------------------------------------------
alter table public.tax_contacts
add column if not exists department text,
add column if not exists division text,
add column if not exists location text,
add column if not exists source text not null default 'manual',
add column if not exists bamboo_employee_id text,
add column if not exists bamboo_employee_number text,
add column if not exists bamboo_status text,
add column if not exists bamboo_synced_at timestamptz,
add column if not exists calendar_enabled boolean not null default true;
alter table public.tax_contacts
drop constraint if exists tax_contacts_area_check;
alter table public.tax_contacts
add constraint tax_contacts_area_check
check (area in ('admin', 'nomina', 'regional', 'other'));
update public.tax_contacts
set calendar_enabled = false
where email is null or length(trim(email)) <= 3;
alter table public.tax_contacts
drop constraint if exists tax_contacts_calendar_email_when_enabled;
alter table public.tax_contacts
add constraint tax_contacts_calendar_email_when_enabled
check (not calendar_enabled or length(trim(coalesce(email, ''))) > 3);
create unique index if not exists tax_contacts_unique_active_bamboo_employee
on public.tax_contacts (bamboo_employee_id)
where active = true and bamboo_employee_id is not null;
create index if not exists tax_contacts_bamboo_employee_number_idx
on public.tax_contacts (bamboo_employee_number)
where bamboo_employee_number is not null;
-- Amplía la normalización existente para los nuevos campos.
create or replace function public.tax_normalize_contact()
returns trigger
language plpgsql
set search_path = ''
as $$
begin
new.full_name := trim(new.full_name);
new.email := nullif(lower(trim(coalesce(new.email, ''))), '');
new.whatsapp_number := nullif(regexp_replace(coalesce(new.whatsapp_number, ''), '[^0-9]', '', 'g'), '');
new.job_title := nullif(trim(coalesce(new.job_title, '')), '');
new.department := nullif(trim(coalesce(new.department, '')), '');
new.division := nullif(trim(coalesce(new.division, '')), '');
new.location := nullif(trim(coalesce(new.location, '')), '');
new.source := coalesce(nullif(lower(trim(coalesce(new.source, ''))), ''), 'manual');
new.bamboo_employee_id := nullif(trim(coalesce(new.bamboo_employee_id, '')), '');
new.bamboo_employee_number := nullif(trim(coalesce(new.bamboo_employee_number, '')), '');
new.bamboo_status := nullif(trim(coalesce(new.bamboo_status, '')), '');
if new.source = 'bamboohr' then
if tg_op = 'INSERT' then
new.bamboo_synced_at := now();
elsif new.bamboo_employee_id is distinct from old.bamboo_employee_id
or new.full_name is distinct from old.full_name
or new.email is distinct from old.email
or new.whatsapp_number is distinct from old.whatsapp_number then
new.bamboo_synced_at := now();
end if;
end if;
return new;
end;
$$;
-- ---------------------------------------------------------------------------
-- No permitir obligaciones nuevas o movidas a fechas pasadas
-- ---------------------------------------------------------------------------
create or replace function public.tax_validate_future_obligation()
returns trigger
language plpgsql
set search_path = ''
as $$
declare
local_today date := (now() at time zone 'America/Santo_Domingo')::date;
begin
if new.active = true
and new.is_historical = false
and new.due_date < local_today then
if tg_op = 'INSERT' then
raise exception 'No se puede crear una obligación en una fecha que ya pasó.'
using errcode = '22007';
elsif new.due_date is distinct from old.due_date then
raise exception 'No se puede mover una obligación a una fecha que ya pasó.'
using errcode = '22007';
end if;
end if;
return new;
end;
$$;
drop trigger if exists tax_validate_future_obligation_trigger on public.tax_obligations;
create trigger tax_validate_future_obligation_trigger
before insert or update on public.tax_obligations
for each row execute function public.tax_validate_future_obligation();
-- ---------------------------------------------------------------------------
-- Sincronización con Google Calendar
-- Un evento de Google Calendar por obligación, con todos los destinatarios
-- aplicables como invitados.
-- ---------------------------------------------------------------------------
create table if not exists public.tax_google_calendar_events (
obligation_id uuid primary key references public.tax_obligations(id) on delete cascade,
google_event_id text,
calendar_id text not null default 'primary',
attendees jsonb not null default '[]'::jsonb,
status text not null default 'active'
check (status in ('active', 'deleted', 'failed')),
last_action text,
provider_response jsonb,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
synced_at timestamptz
);
alter table public.tax_google_calendar_events enable row level security;
revoke all on table public.tax_google_calendar_events from anon, authenticated;
grant select, insert, update, delete on table public.tax_google_calendar_events to service_role;
create or replace function public.tax_google_calendar_payload(p_obligation_id uuid)
returns table (
obligation_id uuid,
active boolean,
due_date date,
description text,
category text,
country_id uuid,
country_name text,
country_code text,
attendees jsonb,
google_event_id text,
calendar_id text,
sync_status text
)
language sql
stable
security definer
set search_path = ''
as $$
select
obligation.id,
obligation.active,
obligation.due_date,
obligation.description,
obligation.category,
obligation.country_id,
country.name,
country.code,
coalesce(
(
select jsonb_agg(recipient.email order by recipient.email)
from (
select distinct lower(contact.email) as email
from public.tax_contacts contact
join public.tax_countries contact_country
on contact_country.id = contact.country_id
where contact.active = true
and contact.calendar_enabled = true
and contact.email is not null
and length(trim(contact.email)) > 3
and (
contact.area = 'regional'
or (
contact_country.is_regional = true
and contact.area = obligation.category
)
or (
contact.country_id = obligation.country_id
and contact.area = obligation.category
)
)
) recipient
),
'[]'::jsonb
) as attendees,
sync.google_event_id,
coalesce(sync.calendar_id, 'primary') as calendar_id,
sync.status
from public.tax_obligations obligation
join public.tax_countries country on country.id = obligation.country_id
left join public.tax_google_calendar_events sync
on sync.obligation_id = obligation.id
where obligation.id = p_obligation_id;
$$;
revoke all on function public.tax_google_calendar_payload(uuid)
from public, anon, authenticated;
grant execute on function public.tax_google_calendar_payload(uuid)
to service_role;
create or replace function public.tax_record_google_calendar_sync(
p_obligation_id uuid,
p_google_event_id text,
p_calendar_id text default 'primary',
p_attendees jsonb default '[]'::jsonb,
p_status text default 'active',
p_last_action text default null,
p_provider_response jsonb default null
)
returns void
language plpgsql
security definer
set search_path = ''
as $$
begin
insert into public.tax_google_calendar_events (
obligation_id,
google_event_id,
calendar_id,
attendees,
status,
last_action,
provider_response,
synced_at,
updated_at
)
values (
p_obligation_id,
nullif(trim(coalesce(p_google_event_id, '')), ''),
coalesce(nullif(trim(coalesce(p_calendar_id, '')), ''), 'primary'),
coalesce(p_attendees, '[]'::jsonb),
p_status,
p_last_action,
p_provider_response,
now(),
now()
)
on conflict (obligation_id)
do update set
google_event_id = coalesce(excluded.google_event_id, public.tax_google_calendar_events.google_event_id),
calendar_id = excluded.calendar_id,
attendees = excluded.attendees,
status = excluded.status,
last_action = excluded.last_action,
provider_response = excluded.provider_response,
synced_at = now(),
updated_at = now();
end;
$$;
revoke all on function public.tax_record_google_calendar_sync(uuid, text, text, jsonb, text, text, jsonb)
from public, anon, authenticated;
grant execute on function public.tax_record_google_calendar_sync(uuid, text, text, jsonb, text, text, jsonb)
to service_role;
commit;
-- Verificaciones opcionales:
-- select * from public.tax_google_calendar_payload('UUID_DE_OBLIGACION');
-- select * from public.tax_google_calendar_events order by updated_at desc;