469 lines
14 KiB
PL/PgSQL
469 lines
14 KiB
PL/PgSQL
-- =========================================================
|
|
-- TABLERO CDC - Acceso autorizado por Supabase
|
|
-- Ejecutar en Supabase SQL Editor ANTES de desplegar el ZIP.
|
|
--
|
|
-- Objetivo:
|
|
-- - El acceso a la app ya no depende de una lista fija en código.
|
|
-- - Los usuarios permitidos viven en public.tablero_cdc_allowed_users.
|
|
-- - Para agregar/quitar acceso luego, se actualiza esta tabla en Supabase.
|
|
-- - También centraliza permisos especiales usados por la app:
|
|
-- * can_manage_internal_pricing
|
|
-- * can_delete_projects
|
|
-- * can_control_pricing_summary
|
|
-- =========================================================
|
|
|
|
begin;
|
|
|
|
create table if not exists public.tablero_cdc_allowed_users (
|
|
email text primary key,
|
|
full_name text,
|
|
role text not null default 'user',
|
|
is_active boolean not null default true,
|
|
can_delete_projects boolean not null default false,
|
|
can_manage_internal_pricing boolean not null default false,
|
|
can_control_pricing_summary boolean not null default false,
|
|
created_at timestamptz not null default now(),
|
|
updated_at timestamptz not null default now()
|
|
);
|
|
|
|
comment on table public.tablero_cdc_allowed_users is
|
|
'Lista central de usuarios autorizados para entrar al Tablero CDC.';
|
|
|
|
comment on column public.tablero_cdc_allowed_users.role is
|
|
'Rol lógico de la app. director_creativo habilita comportamiento especial de Director Creativo.';
|
|
|
|
create or replace function public.set_tablero_cdc_allowed_users_updated_at()
|
|
returns trigger
|
|
language plpgsql
|
|
as $$
|
|
begin
|
|
new.email = lower(trim(new.email));
|
|
new.updated_at = now();
|
|
return new;
|
|
end;
|
|
$$;
|
|
|
|
drop trigger if exists trg_tablero_cdc_allowed_users_updated_at on public.tablero_cdc_allowed_users;
|
|
|
|
create trigger trg_tablero_cdc_allowed_users_updated_at
|
|
before insert or update on public.tablero_cdc_allowed_users
|
|
for each row
|
|
execute function public.set_tablero_cdc_allowed_users_updated_at();
|
|
|
|
-- Usuarios autorizados iniciales.
|
|
-- Nota: Re-ejecutar este SQL mantiene estos usuarios activos y actualiza sus permisos base.
|
|
insert into public.tablero_cdc_allowed_users (
|
|
email,
|
|
full_name,
|
|
role,
|
|
is_active,
|
|
can_delete_projects,
|
|
can_manage_internal_pricing,
|
|
can_control_pricing_summary
|
|
)
|
|
values
|
|
(
|
|
'iaracena@gomezleemarketing.com',
|
|
'Isaac Daniel Aracena Toribio',
|
|
'admin_it',
|
|
true,
|
|
true,
|
|
true,
|
|
true
|
|
),
|
|
(
|
|
'jgomez@gomezleemarketing.com',
|
|
'José Leopoldo Gomez',
|
|
'leadership',
|
|
true,
|
|
false,
|
|
false,
|
|
true
|
|
),
|
|
(
|
|
'gmarrero@gomezleemarketing.com',
|
|
'Gerardo Marrero',
|
|
'director_creativo',
|
|
true,
|
|
true,
|
|
true,
|
|
true
|
|
),
|
|
(
|
|
'areyes@gomezleemarketing.com',
|
|
'Alicia Thaía Reyes',
|
|
'user',
|
|
true,
|
|
false,
|
|
false,
|
|
false
|
|
),
|
|
(
|
|
'boliva@gomezleemarketing.com',
|
|
'Bonnie Oliva',
|
|
'user',
|
|
true,
|
|
false,
|
|
false,
|
|
false
|
|
),
|
|
(
|
|
'obrisceno@gomezleemarketing.com',
|
|
'Oscar Brisceño',
|
|
'user',
|
|
true,
|
|
false,
|
|
false,
|
|
false
|
|
),
|
|
(
|
|
'lavila01@gomezlee.marketing',
|
|
'Lady Avila Faura',
|
|
'user',
|
|
true,
|
|
false,
|
|
false,
|
|
false
|
|
),
|
|
(
|
|
'rmarcano@gomezleemarketing.com',
|
|
'Ramon Marcano',
|
|
'user',
|
|
true,
|
|
false,
|
|
false,
|
|
false
|
|
),
|
|
(
|
|
'lmatos@gomezleemarketing.com',
|
|
'Luis Matos',
|
|
'it',
|
|
true,
|
|
false,
|
|
false,
|
|
false
|
|
),
|
|
(
|
|
'mgomez@gomezleemarketing.com',
|
|
'Máximo Gomez',
|
|
'leadership',
|
|
true,
|
|
false,
|
|
false,
|
|
false
|
|
)
|
|
on conflict (email)
|
|
do update set
|
|
full_name = excluded.full_name,
|
|
role = excluded.role,
|
|
is_active = excluded.is_active,
|
|
can_delete_projects = excluded.can_delete_projects,
|
|
can_manage_internal_pricing = excluded.can_manage_internal_pricing,
|
|
can_control_pricing_summary = excluded.can_control_pricing_summary,
|
|
updated_at = now();
|
|
|
|
alter table public.tablero_cdc_allowed_users enable row level security;
|
|
|
|
drop policy if exists "tablero_cdc_allowed_users_select_own_active" on public.tablero_cdc_allowed_users;
|
|
drop policy if exists "tablero_cdc_allowed_users_no_insert_from_app" on public.tablero_cdc_allowed_users;
|
|
drop policy if exists "tablero_cdc_allowed_users_no_update_from_app" on public.tablero_cdc_allowed_users;
|
|
drop policy if exists "tablero_cdc_allowed_users_no_delete_from_app" on public.tablero_cdc_allowed_users;
|
|
|
|
-- La app solo puede leer la fila activa del usuario autenticado.
|
|
-- La administración real se hace desde Supabase SQL Editor.
|
|
create policy "tablero_cdc_allowed_users_select_own_active"
|
|
on public.tablero_cdc_allowed_users
|
|
for select
|
|
to authenticated
|
|
using (
|
|
is_active = true
|
|
and lower(email) = lower(coalesce(auth.jwt() ->> 'email', ''))
|
|
);
|
|
|
|
create policy "tablero_cdc_allowed_users_no_insert_from_app"
|
|
on public.tablero_cdc_allowed_users
|
|
for insert
|
|
to authenticated
|
|
with check (false);
|
|
|
|
create policy "tablero_cdc_allowed_users_no_update_from_app"
|
|
on public.tablero_cdc_allowed_users
|
|
for update
|
|
to authenticated
|
|
using (false)
|
|
with check (false);
|
|
|
|
create policy "tablero_cdc_allowed_users_no_delete_from_app"
|
|
on public.tablero_cdc_allowed_users
|
|
for delete
|
|
to authenticated
|
|
using (false);
|
|
|
|
grant select on public.tablero_cdc_allowed_users to authenticated;
|
|
|
|
-- Funciones auxiliares para políticas/RPC.
|
|
create or replace function public.tablero_cdc_current_user_is_allowed()
|
|
returns boolean
|
|
language sql
|
|
stable
|
|
security definer
|
|
set search_path = public
|
|
as $$
|
|
select exists (
|
|
select 1
|
|
from public.tablero_cdc_allowed_users u
|
|
where u.email = lower(coalesce(auth.jwt() ->> 'email', ''))
|
|
and u.is_active = true
|
|
);
|
|
$$;
|
|
|
|
create or replace function public.tablero_cdc_current_user_can_control_pricing_summary()
|
|
returns boolean
|
|
language sql
|
|
stable
|
|
security definer
|
|
set search_path = public
|
|
as $$
|
|
select exists (
|
|
select 1
|
|
from public.tablero_cdc_allowed_users u
|
|
where u.email = lower(coalesce(auth.jwt() ->> 'email', ''))
|
|
and u.is_active = true
|
|
and u.can_control_pricing_summary = true
|
|
);
|
|
$$;
|
|
|
|
grant execute on function public.tablero_cdc_current_user_is_allowed() to authenticated;
|
|
grant execute on function public.tablero_cdc_current_user_can_control_pricing_summary() to authenticated;
|
|
|
|
-- =========================================================
|
|
-- Total Tarifado: configuración pública/privada usando tabla de accesos
|
|
-- =========================================================
|
|
|
|
create table if not exists public.tablero_cdc_app_settings (
|
|
key text primary key,
|
|
value jsonb not null default '{}'::jsonb,
|
|
updated_by uuid references auth.users(id),
|
|
updated_at timestamptz not null default now()
|
|
);
|
|
|
|
insert into public.tablero_cdc_app_settings (key, value)
|
|
values ('pricing_summary_public', jsonb_build_object('enabled', false))
|
|
on conflict (key) do nothing;
|
|
|
|
alter table public.tablero_cdc_app_settings enable row level security;
|
|
|
|
drop policy if exists "tablero_cdc_app_settings_select_authenticated" on public.tablero_cdc_app_settings;
|
|
drop policy if exists "tablero_cdc_app_settings_select_allowed_users" on public.tablero_cdc_app_settings;
|
|
drop policy if exists "tablero_cdc_app_settings_insert_summary_controllers" on public.tablero_cdc_app_settings;
|
|
drop policy if exists "tablero_cdc_app_settings_update_summary_controllers" on public.tablero_cdc_app_settings;
|
|
drop policy if exists "tablero_cdc_app_settings_delete_none" on public.tablero_cdc_app_settings;
|
|
|
|
create policy "tablero_cdc_app_settings_select_allowed_users"
|
|
on public.tablero_cdc_app_settings
|
|
for select
|
|
to authenticated
|
|
using (public.tablero_cdc_current_user_is_allowed());
|
|
|
|
create policy "tablero_cdc_app_settings_insert_summary_controllers"
|
|
on public.tablero_cdc_app_settings
|
|
for insert
|
|
to authenticated
|
|
with check (
|
|
key = 'pricing_summary_public'
|
|
and public.tablero_cdc_current_user_can_control_pricing_summary()
|
|
);
|
|
|
|
create policy "tablero_cdc_app_settings_update_summary_controllers"
|
|
on public.tablero_cdc_app_settings
|
|
for update
|
|
to authenticated
|
|
using (
|
|
key = 'pricing_summary_public'
|
|
and public.tablero_cdc_current_user_can_control_pricing_summary()
|
|
)
|
|
with check (
|
|
key = 'pricing_summary_public'
|
|
and public.tablero_cdc_current_user_can_control_pricing_summary()
|
|
);
|
|
|
|
create policy "tablero_cdc_app_settings_delete_none"
|
|
on public.tablero_cdc_app_settings
|
|
for delete
|
|
to authenticated
|
|
using (false);
|
|
|
|
create or replace function public.set_tablero_cdc_app_settings_updated_at()
|
|
returns trigger
|
|
language plpgsql
|
|
as $$
|
|
begin
|
|
new.updated_at = now();
|
|
new.updated_by = auth.uid();
|
|
return new;
|
|
end;
|
|
$$;
|
|
|
|
drop trigger if exists trg_tablero_cdc_app_settings_updated_at on public.tablero_cdc_app_settings;
|
|
|
|
create trigger trg_tablero_cdc_app_settings_updated_at
|
|
before insert or update on public.tablero_cdc_app_settings
|
|
for each row
|
|
execute function public.set_tablero_cdc_app_settings_updated_at();
|
|
|
|
grant select on public.tablero_cdc_app_settings to authenticated;
|
|
|
|
create or replace function public.tablero_cdc_set_pricing_summary_public(p_enabled boolean)
|
|
returns jsonb
|
|
language plpgsql
|
|
security definer
|
|
set search_path = public
|
|
as $$
|
|
declare
|
|
v_enabled boolean := coalesce(p_enabled, false);
|
|
begin
|
|
if not public.tablero_cdc_current_user_can_control_pricing_summary() then
|
|
raise exception 'No tienes permiso para cambiar la visibilidad del total tarifado.';
|
|
end if;
|
|
|
|
insert into public.tablero_cdc_app_settings (key, value, updated_by, updated_at)
|
|
values (
|
|
'pricing_summary_public',
|
|
jsonb_build_object('enabled', v_enabled),
|
|
auth.uid(),
|
|
now()
|
|
)
|
|
on conflict (key)
|
|
do update set
|
|
value = excluded.value,
|
|
updated_by = auth.uid(),
|
|
updated_at = now();
|
|
|
|
return jsonb_build_object('enabled', v_enabled);
|
|
end;
|
|
$$;
|
|
|
|
grant execute on function public.tablero_cdc_set_pricing_summary_public(boolean) to authenticated;
|
|
|
|
-- Reemplaza la RPC de resumen para respetar:
|
|
-- 1) que el usuario esté autorizado para la app;
|
|
-- 2) que sea controlador del Total Tarifado o que el total esté público.
|
|
create or replace function public.tablero_cdc_get_pricing_summary(
|
|
p_tab text default 'todos',
|
|
p_search text default '',
|
|
p_country text default '',
|
|
p_brand text default '',
|
|
p_client text default '',
|
|
p_cm text default ''
|
|
)
|
|
returns jsonb
|
|
language sql
|
|
stable
|
|
security invoker
|
|
set search_path = public
|
|
as $$
|
|
with visibility as (
|
|
select coalesce((value ->> 'enabled')::boolean, false) as is_public
|
|
from public.tablero_cdc_app_settings
|
|
where key = 'pricing_summary_public'
|
|
),
|
|
allowed as (
|
|
select
|
|
public.tablero_cdc_current_user_is_allowed()
|
|
and (
|
|
public.tablero_cdc_current_user_can_control_pricing_summary()
|
|
or coalesce((select is_public from visibility), false)
|
|
) as can_view_pricing
|
|
),
|
|
params as (
|
|
select
|
|
lower(coalesce(nullif(trim(p_tab), ''), 'todos')) as tab,
|
|
trim(coalesce(p_search, '')) as search_text,
|
|
trim(coalesce(p_country, '')) as country_filter,
|
|
trim(coalesce(p_brand, '')) as brand_filter,
|
|
trim(coalesce(p_client, '')) as client_filter,
|
|
trim(coalesce(p_cm, '')) as cm_filter
|
|
),
|
|
base_filtered as (
|
|
select p.*
|
|
from public.tablero_cdc_projects p
|
|
cross join params prm
|
|
where
|
|
case
|
|
when prm.tab in ('abiertos', 'activos', 'active', 'open') then
|
|
coalesce(nullif(trim(p.status), ''), 'Activo') = 'Activo'
|
|
when prm.tab in ('cerrados', 'closed') then
|
|
coalesce(nullif(trim(p.status), ''), 'Activo') <> 'Activo'
|
|
else
|
|
true
|
|
end
|
|
and (
|
|
prm.search_text = ''
|
|
or coalesce(p.title, '') ilike '%' || prm.search_text || '%'
|
|
or coalesce(p.client, '') ilike '%' || prm.search_text || '%'
|
|
or coalesce(p.brand, '') ilike '%' || prm.search_text || '%'
|
|
or coalesce(p.country, '') ilike '%' || prm.search_text || '%'
|
|
or coalesce(p.requested_by, '') ilike '%' || prm.search_text || '%'
|
|
or coalesce(p.country_manager, '') ilike '%' || prm.search_text || '%'
|
|
or coalesce(p.description, '') ilike '%' || prm.search_text || '%'
|
|
)
|
|
),
|
|
filtered as (
|
|
select bf.*
|
|
from base_filtered bf
|
|
cross join params prm
|
|
where
|
|
(prm.country_filter = '' or coalesce(bf.country, '') ilike '%' || prm.country_filter || '%')
|
|
and (prm.brand_filter = '' or coalesce(bf.brand, '') ilike '%' || prm.brand_filter || '%')
|
|
and (prm.client_filter = '' or coalesce(bf.client, '') ilike '%' || prm.client_filter || '%')
|
|
and (prm.cm_filter = '' or coalesce(bf.country_manager, '') ilike '%' || prm.cm_filter || '%')
|
|
),
|
|
totals as (
|
|
select
|
|
count(*)::integer as project_count,
|
|
coalesce(
|
|
sum(
|
|
greatest(
|
|
coalesce(nullif(trim(internal_amount::text), '')::numeric, 0),
|
|
0
|
|
)
|
|
),
|
|
0
|
|
)::numeric as total_amount
|
|
from filtered
|
|
)
|
|
select case
|
|
when (select can_view_pricing from allowed) then
|
|
jsonb_build_object(
|
|
'total_amount', (select total_amount from totals),
|
|
'project_count', (select project_count from totals)
|
|
)
|
|
else
|
|
jsonb_build_object(
|
|
'total_amount', 0,
|
|
'project_count', 0
|
|
)
|
|
end;
|
|
$$;
|
|
|
|
grant execute on function public.tablero_cdc_get_pricing_summary(text, text, text, text, text, text) to authenticated;
|
|
|
|
-- Intenta habilitar realtime para que el cambio de visible/oculto se refleje sin recargar.
|
|
do $$
|
|
begin
|
|
alter publication supabase_realtime add table public.tablero_cdc_app_settings;
|
|
exception
|
|
when duplicate_object then null;
|
|
when undefined_object then null;
|
|
end $$;
|
|
|
|
commit;
|
|
|
|
select
|
|
'Acceso Tablero CDC listo' as status,
|
|
count(*) filter (where is_active) as usuarios_activos,
|
|
count(*) filter (where can_control_pricing_summary and is_active) as controlan_total_tarifado,
|
|
count(*) filter (where can_manage_internal_pricing and is_active) as ven_interno_cargado,
|
|
count(*) filter (where can_delete_projects and is_active) as pueden_eliminar
|
|
from public.tablero_cdc_allowed_users;
|