Files
agenciapsilmno/src/sql-arquivos/supabase_patient_groups.sql
2026-02-18 22:36:45 -03:00

175 lines
5.7 KiB
PL/PgSQL

/*
patient_groups_setup.sql
Setup completo para:
- public.patient_groups
- public.patient_group_patient (tabela ponte)
- view public.v_patient_groups_with_counts
- índice único por owner + nome (case-insensitive)
- 3 grupos padrão do sistema (Crianças, Adolescentes, Idosos) NÃO editáveis / NÃO removíveis
- triggers de proteção
Observação (importante):
- Os grupos padrão são criados com owner_id = '00000000-0000-0000-0000-000000000000' (SYSTEM_OWNER),
para ficarem "globais" e não dependerem de auth.uid() em migrations.
- Se você quiser que os grupos padrão pertençam a um owner específico (tenant),
basta trocar o SYSTEM_OWNER abaixo por esse UUID.
*/
begin;
-- ===========================
-- 0) Constante de "dono do sistema"
-- ===========================
-- Troque aqui se você quiser que os grupos padrão pertençam a um owner específico.
-- Ex.: '816b24fe-a0c3-4409-b79b-c6c0a6935d03'
do $$
begin
-- só para documentar; não cria nada
end $$;
-- ===========================
-- 1) Tabela principal: patient_groups
-- ===========================
create table if not exists public.patient_groups (
id uuid primary key default gen_random_uuid(),
name text not null,
description text,
color text,
is_active boolean not null default true,
is_system boolean not null default false,
owner_id uuid not null,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
-- (Opcional, mas recomendado) Garante que name não seja só espaços
-- e evita nomes vazios.
alter table public.patient_groups
drop constraint if exists patient_groups_name_not_blank_check;
alter table public.patient_groups
add constraint patient_groups_name_not_blank_check
check (length(btrim(name)) > 0);
-- ===========================
-- 2) Tabela ponte: patient_group_patient
-- ===========================
-- Se você já tiver essa tabela com FKs, ajuste aqui conforme seu schema.
create table if not exists public.patient_group_patient (
patient_group_id uuid not null references public.patient_groups(id) on delete cascade,
patient_id uuid not null references public.patients(id) on delete cascade,
created_at timestamptz not null default now()
);
-- Evita duplicar vínculo paciente<->grupo
create unique index if not exists patient_group_patient_unique
on public.patient_group_patient (patient_group_id, patient_id);
-- ===========================
-- 3) View com contagem
-- ===========================
create or replace view public.v_patient_groups_with_counts as
select
g.*,
coalesce(count(distinct pgp.patient_id), 0)::int as patients_count
from public.patient_groups g
left join public.patient_group_patient pgp
on pgp.patient_group_id = g.id
group by g.id;
-- ===========================
-- 4) Índice único: não permitir mesmo nome por owner (case-insensitive)
-- ===========================
-- Atenção: se já existirem duplicados, este índice pode falhar ao criar.
create unique index if not exists patient_groups_owner_name_unique
on public.patient_groups (owner_id, (lower(name)));
-- ===========================
-- 5) Triggers de proteção: system não edita / não remove
-- ===========================
create or replace function public.prevent_system_group_changes()
returns trigger
language plpgsql
as $$
begin
if old.is_system = true then
raise exception 'Grupos padrão do sistema não podem ser alterados ou excluídos.';
end if;
if tg_op = 'DELETE' then
return old;
end if;
return new;
end;
$$;
drop trigger if exists trg_prevent_system_group_changes on public.patient_groups;
create trigger trg_prevent_system_group_changes
before update or delete on public.patient_groups
for each row
execute function public.prevent_system_group_changes();
-- Impede "promover" um grupo comum para system
create or replace function public.prevent_promoting_to_system()
returns trigger
language plpgsql
as $$
begin
if new.is_system = true and old.is_system is distinct from true then
raise exception 'Não é permitido transformar um grupo comum em grupo do sistema.';
end if;
return new;
end;
$$;
drop trigger if exists trg_prevent_promoting_to_system on public.patient_groups;
create trigger trg_prevent_promoting_to_system
before update on public.patient_groups
for each row
execute function public.prevent_promoting_to_system();
-- ===========================
-- 6) Inserir 3 grupos padrão (imutáveis)
-- ===========================
-- Dono "global" do sistema (mude se quiser):
-- 00000000-0000-0000-0000-000000000000
with sys_owner as (
select '00000000-0000-0000-0000-000000000000'::uuid as owner_id
)
insert into public.patient_groups (name, description, color, is_active, is_system, owner_id)
select v.name, v.description, v.color, v.is_active, v.is_system, s.owner_id
from sys_owner s
join (values
('Crianças', 'Grupo padrão do sistema', null, true, true),
('Adolescentes', 'Grupo padrão do sistema', null, true, true),
('Idosos', 'Grupo padrão do sistema', null, true, true)
) as v(name, description, color, is_active, is_system)
on true
where not exists (
select 1
from public.patient_groups g
where g.owner_id = s.owner_id
and lower(g.name) = lower(v.name)
);
commit;
/*
Testes rápidos:
1) Ver tudo:
select * from public.v_patient_groups_with_counts order by is_system desc, name;
2) Tentar editar um system (deve falhar):
update public.patient_groups set name='X' where name='Crianças';
3) Tentar deletar um system (deve falhar):
delete from public.patient_groups where name='Crianças';
4) Tentar duplicar nome no mesmo owner (deve falhar por índice único):
insert into public.patient_groups (name, is_active, is_system, owner_id)
values ('teste22', true, false, '816b24fe-a0c3-4409-b79b-c6c0a6935d03');
*/