189 lines
6.6 KiB
PL/PgSQL
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;
|