Files
cdc-project-management/supabase_project_time_entries.sql

189 lines
6.6 KiB
PL/PgSQL

-- =========================================================
-- TABLERO CDC - TIEMPO DEDICADO POR PROYECTO
-- Ejecutar una sola vez en Supabase SQL Editor antes de desplegar
-- la versión que incluye el botón "Tiempo".
--
-- Objetivo:
-- - Registrar tiempo dedicado a un proyecto.
-- - Desglosar ese tiempo por país y por tarea.
-- - Conservar fecha, notas y quién registró cada entrada.
-- - No modifica tablero_cdc_projects ni altera tarifarios, montos,
-- Google Sheets, n8n o datos históricos existentes.
-- =========================================================
begin;
create table if not exists public.tablero_cdc_project_time_entries (
id uuid primary key default gen_random_uuid(),
project_id uuid not null references public.tablero_cdc_projects(id) on delete cascade,
country text not null,
task_name text not null,
duration_minutes integer not null check (duration_minutes > 0),
work_date date not null default current_date,
notes text not null default '',
created_by uuid references auth.users(id),
created_by_email text,
created_by_name text,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
constraint tablero_cdc_project_time_entries_country_not_blank
check (length(trim(country)) > 0),
constraint tablero_cdc_project_time_entries_task_not_blank
check (length(trim(task_name)) > 0)
);
comment on table public.tablero_cdc_project_time_entries is
'Registros de tiempo dedicado a proyectos CDC, desglosados por país y tarea.';
comment on column public.tablero_cdc_project_time_entries.duration_minutes is
'Duración exacta almacenada en minutos para evitar errores de redondeo.';
create index if not exists tablero_cdc_project_time_entries_project_date_idx
on public.tablero_cdc_project_time_entries(project_id, work_date desc, created_at desc);
create index if not exists tablero_cdc_project_time_entries_project_country_idx
on public.tablero_cdc_project_time_entries(project_id, country);
create index if not exists tablero_cdc_project_time_entries_project_task_idx
on public.tablero_cdc_project_time_entries(project_id, task_name);
create or replace function public.set_tablero_cdc_project_time_entries_audit()
returns trigger
language plpgsql
security invoker
set search_path = public
as $$
begin
new.country = trim(new.country);
new.task_name = trim(new.task_name);
new.notes = coalesce(trim(new.notes), '');
new.updated_at = now();
if tg_op = 'INSERT' then
-- La identidad se toma de la sesión autenticada, no de valores enviados por el cliente.
new.created_by = auth.uid();
new.created_by_email = coalesce(
nullif(lower(trim(coalesce(auth.jwt() ->> 'email', ''))), ''),
nullif(lower(trim(coalesce(new.created_by_email, ''))), '')
);
new.created_by_name = coalesce(
nullif(trim(coalesce(auth.jwt() -> 'user_metadata' ->> 'full_name', '')), ''),
nullif(trim(coalesce(auth.jwt() -> 'user_metadata' ->> 'name', '')), ''),
nullif(trim(coalesce(new.created_by_name, '')), ''),
new.created_by_email,
'Usuario CDC'
);
end if;
return new;
end;
$$;
drop trigger if exists trg_tablero_cdc_project_time_entries_audit
on public.tablero_cdc_project_time_entries;
create trigger trg_tablero_cdc_project_time_entries_audit
before insert or update on public.tablero_cdc_project_time_entries
for each row
execute function public.set_tablero_cdc_project_time_entries_audit();
alter table public.tablero_cdc_project_time_entries enable row level security;
drop policy if exists "tablero_cdc_project_time_entries_select_allowed" on public.tablero_cdc_project_time_entries;
drop policy if exists "tablero_cdc_project_time_entries_insert_allowed" on public.tablero_cdc_project_time_entries;
drop policy if exists "tablero_cdc_project_time_entries_update_allowed" on public.tablero_cdc_project_time_entries;
drop policy if exists "tablero_cdc_project_time_entries_delete_allowed" on public.tablero_cdc_project_time_entries;
-- Se valida contra la misma tabla central de acceso del Tablero CDC.
-- Así, una sesión Google autenticada pero NO autorizada para el Tablero
-- tampoco puede leer o escribir tiempos mediante la API de Supabase.
create policy "tablero_cdc_project_time_entries_select_allowed"
on public.tablero_cdc_project_time_entries
for select
to authenticated
using (
exists (
select 1
from public.tablero_cdc_allowed_users u
where lower(trim(u.email)) = lower(trim(coalesce(auth.jwt() ->> 'email', '')))
and u.is_active = true
)
);
create policy "tablero_cdc_project_time_entries_insert_allowed"
on public.tablero_cdc_project_time_entries
for insert
to authenticated
with check (
exists (
select 1
from public.tablero_cdc_allowed_users u
where lower(trim(u.email)) = lower(trim(coalesce(auth.jwt() ->> 'email', '')))
and u.is_active = true
)
);
create policy "tablero_cdc_project_time_entries_update_allowed"
on public.tablero_cdc_project_time_entries
for update
to authenticated
using (
exists (
select 1
from public.tablero_cdc_allowed_users u
where lower(trim(u.email)) = lower(trim(coalesce(auth.jwt() ->> 'email', '')))
and u.is_active = true
)
)
with check (
exists (
select 1
from public.tablero_cdc_allowed_users u
where lower(trim(u.email)) = lower(trim(coalesce(auth.jwt() ->> 'email', '')))
and u.is_active = true
)
);
create policy "tablero_cdc_project_time_entries_delete_allowed"
on public.tablero_cdc_project_time_entries
for delete
to authenticated
using (
exists (
select 1
from public.tablero_cdc_allowed_users u
where lower(trim(u.email)) = lower(trim(coalesce(auth.jwt() ->> 'email', '')))
and u.is_active = true
)
);
grant select, insert, update, delete
on public.tablero_cdc_project_time_entries
to authenticated;
-- Realtime no es obligatorio para el funcionamiento, pero se habilita
-- para que el módulo quede preparado para sincronización multiusuario.
do $$
begin
if not exists (
select 1
from pg_publication_tables
where pubname = 'supabase_realtime'
and schemaname = 'public'
and tablename = 'tablero_cdc_project_time_entries'
) then
alter publication supabase_realtime add table public.tablero_cdc_project_time_entries;
end if;
end $$;
commit;
-- =========================================================
-- VERIFICACIÓN
-- Debe devolver la tabla y 0 registros si todavía no se ha agregado tiempo.
-- =========================================================
select
count(*) as registros_de_tiempo,
coalesce(sum(duration_minutes), 0) as minutos_totales
from public.tablero_cdc_project_time_entries;