Correcao Sidebar Classico e Rail, Correcao Layout, Ajuste de Breakpoint para Tailwind, Ajuste AppTopbar, Ajuste Menu PopOver, Recriado Paleta de Cores, Inserido algumas animações leves, Reajuste Cor items NOVOS da tabela, Drawer Ajuda Corrigido no Logout, Whatsapp, sms, email, recursos extras
This commit is contained in:
433
database-novo/schema/03_functions/patients.sql
Normal file
433
database-novo/schema/03_functions/patients.sql
Normal file
@@ -0,0 +1,433 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Functions — Pacientes
|
||||
-- =============================================================================
|
||||
-- can_delete_patient, safe_delete_patient, create_patient_intake_request,
|
||||
-- create_patient_intake_request_v2, rotate_patient_invite_token,
|
||||
-- patients_validate_member_consistency, prevent_system_group_changes,
|
||||
-- seed_default_patient_groups
|
||||
-- =============================================================================
|
||||
|
||||
CREATE FUNCTION public.can_delete_patient(p_patient_id uuid) RETURNS boolean
|
||||
LANGUAGE sql STABLE SECURITY DEFINER
|
||||
AS $$
|
||||
SELECT NOT EXISTS (
|
||||
SELECT 1 FROM public.agenda_eventos WHERE patient_id = p_patient_id
|
||||
UNION ALL
|
||||
SELECT 1 FROM public.recurrence_rules WHERE patient_id = p_patient_id
|
||||
UNION ALL
|
||||
SELECT 1 FROM public.billing_contracts WHERE patient_id = p_patient_id
|
||||
);
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.can_delete_patient(p_patient_id uuid) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: cancel_notifications_on_opt_out(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.cancel_notifications_on_opt_out() RETURNS trigger
|
||||
CREATE FUNCTION public.create_patient_intake_request(p_token text, p_name text, p_email text DEFAULT NULL::text, p_phone text DEFAULT NULL::text, p_notes text DEFAULT NULL::text, p_consent boolean DEFAULT false) RETURNS uuid
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
declare
|
||||
v_owner uuid;
|
||||
v_active boolean;
|
||||
v_expires timestamptz;
|
||||
v_max_uses int;
|
||||
v_uses int;
|
||||
v_id uuid;
|
||||
begin
|
||||
select owner_id, active, expires_at, max_uses, uses
|
||||
into v_owner, v_active, v_expires, v_max_uses, v_uses
|
||||
from public.patient_invites
|
||||
where token = p_token
|
||||
limit 1;
|
||||
|
||||
if v_owner is null then
|
||||
raise exception 'Token inválido';
|
||||
end if;
|
||||
|
||||
if v_active is not true then
|
||||
raise exception 'Link desativado';
|
||||
end if;
|
||||
|
||||
if v_expires is not null and now() > v_expires then
|
||||
raise exception 'Link expirado';
|
||||
end if;
|
||||
|
||||
if v_max_uses is not null and v_uses >= v_max_uses then
|
||||
raise exception 'Limite de uso atingido';
|
||||
end if;
|
||||
|
||||
if p_name is null or length(trim(p_name)) = 0 then
|
||||
raise exception 'Nome é obrigatório';
|
||||
end if;
|
||||
|
||||
insert into public.patient_intake_requests
|
||||
(owner_id, token, name, email, phone, notes, consent, status)
|
||||
values
|
||||
(v_owner, p_token, trim(p_name),
|
||||
nullif(lower(trim(p_email)), ''),
|
||||
nullif(trim(p_phone), ''),
|
||||
nullif(trim(p_notes), ''),
|
||||
coalesce(p_consent, false),
|
||||
'new')
|
||||
returning id into v_id;
|
||||
|
||||
update public.patient_invites
|
||||
set uses = uses + 1
|
||||
where token = p_token;
|
||||
|
||||
return v_id;
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.create_patient_intake_request(p_token text, p_name text, p_email text, p_phone text, p_notes text, p_consent boolean) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: create_patient_intake_request_v2(text, jsonb); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.create_patient_intake_request_v2(p_token text, p_payload jsonb) RETURNS uuid
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $_$
|
||||
declare
|
||||
v_owner_id uuid;
|
||||
v_intake_id uuid;
|
||||
v_birth_raw text;
|
||||
v_birth date;
|
||||
begin
|
||||
select owner_id
|
||||
into v_owner_id
|
||||
from public.patient_invites
|
||||
where token = p_token;
|
||||
|
||||
if v_owner_id is null then
|
||||
raise exception 'Token inválido ou expirado';
|
||||
end if;
|
||||
|
||||
v_birth_raw := nullif(trim(coalesce(
|
||||
p_payload->>'data_nascimento',
|
||||
''
|
||||
)), '');
|
||||
|
||||
v_birth := case
|
||||
when v_birth_raw is null then null
|
||||
when v_birth_raw ~ '^\d{4}-\d{2}-\d{2}$' then v_birth_raw::date
|
||||
when v_birth_raw ~ '^\d{2}-\d{2}-\d{4}$' then to_date(v_birth_raw, 'DD-MM-YYYY')
|
||||
else null
|
||||
end;
|
||||
|
||||
insert into public.patient_intake_requests (
|
||||
owner_id,
|
||||
token,
|
||||
status,
|
||||
consent,
|
||||
|
||||
nome_completo,
|
||||
email_principal,
|
||||
telefone,
|
||||
|
||||
avatar_url, -- 🔥 AQUI
|
||||
|
||||
data_nascimento,
|
||||
cpf,
|
||||
rg,
|
||||
genero,
|
||||
estado_civil,
|
||||
profissao,
|
||||
escolaridade,
|
||||
nacionalidade,
|
||||
naturalidade,
|
||||
|
||||
cep,
|
||||
pais,
|
||||
cidade,
|
||||
estado,
|
||||
endereco,
|
||||
numero,
|
||||
complemento,
|
||||
bairro,
|
||||
|
||||
observacoes,
|
||||
notas_internas,
|
||||
|
||||
encaminhado_por,
|
||||
onde_nos_conheceu
|
||||
)
|
||||
values (
|
||||
v_owner_id,
|
||||
p_token,
|
||||
'new',
|
||||
coalesce((p_payload->>'consent')::boolean, false),
|
||||
|
||||
nullif(trim(p_payload->>'nome_completo'), ''),
|
||||
nullif(trim(p_payload->>'email_principal'), ''),
|
||||
nullif(regexp_replace(coalesce(p_payload->>'telefone',''), '\D', '', 'g'), ''),
|
||||
|
||||
nullif(trim(p_payload->>'avatar_url'), ''), -- 🔥 AQUI
|
||||
|
||||
v_birth,
|
||||
nullif(regexp_replace(coalesce(p_payload->>'cpf',''), '\D', '', 'g'), ''),
|
||||
nullif(trim(p_payload->>'rg'), ''),
|
||||
nullif(trim(p_payload->>'genero'), ''),
|
||||
nullif(trim(p_payload->>'estado_civil'), ''),
|
||||
nullif(trim(p_payload->>'profissao'), ''),
|
||||
nullif(trim(p_payload->>'escolaridade'), ''),
|
||||
nullif(trim(p_payload->>'nacionalidade'), ''),
|
||||
nullif(trim(p_payload->>'naturalidade'), ''),
|
||||
|
||||
nullif(regexp_replace(coalesce(p_payload->>'cep',''), '\D', '', 'g'), ''),
|
||||
nullif(trim(p_payload->>'pais'), ''),
|
||||
nullif(trim(p_payload->>'cidade'), ''),
|
||||
nullif(trim(p_payload->>'estado'), ''),
|
||||
nullif(trim(p_payload->>'endereco'), ''),
|
||||
nullif(trim(p_payload->>'numero'), ''),
|
||||
nullif(trim(p_payload->>'complemento'), ''),
|
||||
nullif(trim(p_payload->>'bairro'), ''),
|
||||
|
||||
nullif(trim(p_payload->>'observacoes'), ''),
|
||||
nullif(trim(p_payload->>'notas_internas'), ''),
|
||||
|
||||
nullif(trim(p_payload->>'encaminhado_por'), ''),
|
||||
nullif(trim(p_payload->>'onde_nos_conheceu'), '')
|
||||
)
|
||||
returning id into v_intake_id;
|
||||
|
||||
return v_intake_id;
|
||||
end;
|
||||
$_$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.create_patient_intake_request_v2(p_token text, p_payload jsonb) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: create_support_session(uuid, integer); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.create_support_session(p_tenant_id uuid, p_ttl_minutes integer DEFAULT 60) RETURNS json
|
||||
CREATE FUNCTION public.patients_validate_member_consistency() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_tenant_responsible uuid;
|
||||
v_tenant_therapist uuid;
|
||||
BEGIN
|
||||
-- responsible_member sempre deve existir e ser do tenant
|
||||
SELECT tenant_id INTO v_tenant_responsible
|
||||
FROM public.tenant_members
|
||||
WHERE id = NEW.responsible_member_id;
|
||||
|
||||
IF v_tenant_responsible IS NULL THEN
|
||||
RAISE EXCEPTION 'Responsible member not found';
|
||||
END IF;
|
||||
|
||||
IF NEW.tenant_id IS NULL THEN
|
||||
RAISE EXCEPTION 'tenant_id is required';
|
||||
END IF;
|
||||
|
||||
IF v_tenant_responsible <> NEW.tenant_id THEN
|
||||
RAISE EXCEPTION 'Responsible member must belong to the same tenant';
|
||||
END IF;
|
||||
|
||||
-- therapist scope: therapist_member_id deve existir e ser do mesmo tenant
|
||||
IF NEW.patient_scope = 'therapist' THEN
|
||||
IF NEW.therapist_member_id IS NULL THEN
|
||||
RAISE EXCEPTION 'therapist_member_id is required when patient_scope=therapist';
|
||||
END IF;
|
||||
|
||||
SELECT tenant_id INTO v_tenant_therapist
|
||||
FROM public.tenant_members
|
||||
WHERE id = NEW.therapist_member_id;
|
||||
|
||||
IF v_tenant_therapist IS NULL THEN
|
||||
RAISE EXCEPTION 'Therapist member not found';
|
||||
END IF;
|
||||
|
||||
IF v_tenant_therapist <> NEW.tenant_id THEN
|
||||
RAISE EXCEPTION 'Therapist member must belong to the same tenant';
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.patients_validate_member_consistency() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: patients_validate_responsible_member_tenant(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.patients_validate_responsible_member_tenant() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
declare
|
||||
m_tenant uuid;
|
||||
begin
|
||||
select tenant_id into m_tenant
|
||||
from public.tenant_members
|
||||
where id = new.responsible_member_id;
|
||||
|
||||
if m_tenant is null then
|
||||
raise exception 'Responsible member not found';
|
||||
end if;
|
||||
|
||||
if new.tenant_id is null then
|
||||
raise exception 'tenant_id is required';
|
||||
end if;
|
||||
|
||||
if m_tenant <> new.tenant_id then
|
||||
raise exception 'Responsible member must belong to the same tenant';
|
||||
end if;
|
||||
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.patients_validate_responsible_member_tenant() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: populate_notification_queue(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.populate_notification_queue() RETURNS void
|
||||
CREATE FUNCTION public.prevent_system_group_changes() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
begin
|
||||
-- Se for grupo do sistema, regras rígidas:
|
||||
if old.is_system = true then
|
||||
|
||||
-- nunca pode deletar
|
||||
if tg_op = 'DELETE' then
|
||||
raise exception 'Grupos padrão do sistema não podem ser alterados ou excluídos.';
|
||||
end if;
|
||||
|
||||
if tg_op = 'UPDATE' then
|
||||
-- permite SOMENTE mudar tenant_id e/ou updated_at
|
||||
-- qualquer mudança de conteúdo permanece proibida
|
||||
if
|
||||
new.nome is distinct from old.nome or
|
||||
new.descricao is distinct from old.descricao or
|
||||
new.cor is distinct from old.cor or
|
||||
new.is_active is distinct from old.is_active or
|
||||
new.is_system is distinct from old.is_system or
|
||||
new.owner_id is distinct from old.owner_id or
|
||||
new.therapist_id is distinct from old.therapist_id or
|
||||
new.created_at is distinct from old.created_at
|
||||
then
|
||||
raise exception 'Grupos padrão do sistema não podem ser alterados ou excluídos.';
|
||||
end if;
|
||||
|
||||
-- chegou aqui: só tenant_id/updated_at mudaram -> ok
|
||||
return new;
|
||||
end if;
|
||||
|
||||
end if;
|
||||
|
||||
-- não-system: deixa passar
|
||||
if tg_op = 'DELETE' then
|
||||
return old;
|
||||
end if;
|
||||
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.prevent_system_group_changes() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: provision_account_tenant(uuid, text, text); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text DEFAULT NULL::text) RETURNS uuid
|
||||
CREATE FUNCTION public.rotate_patient_invite_token(p_new_token text) RETURNS uuid
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
declare
|
||||
v_uid uuid;
|
||||
v_id uuid;
|
||||
begin
|
||||
-- pega o usuário logado
|
||||
v_uid := auth.uid();
|
||||
if v_uid is null then
|
||||
raise exception 'Usuário não autenticado';
|
||||
end if;
|
||||
|
||||
-- desativa tokens antigos ativos do usuário
|
||||
update public.patient_invites
|
||||
set active = false
|
||||
where owner_id = v_uid
|
||||
and active = true;
|
||||
|
||||
-- cria novo token
|
||||
insert into public.patient_invites (owner_id, token, active)
|
||||
values (v_uid, p_new_token, true)
|
||||
returning id into v_id;
|
||||
|
||||
return v_id;
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.rotate_patient_invite_token(p_new_token text) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: saas_votar_doc(uuid, boolean); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.saas_votar_doc(p_doc_id uuid, p_util boolean) RETURNS jsonb
|
||||
CREATE FUNCTION public.safe_delete_patient(p_patient_id uuid) RETURNS jsonb
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
AS $$
|
||||
BEGIN
|
||||
-- Bloqueia se houver histórico
|
||||
IF NOT public.can_delete_patient(p_patient_id) THEN
|
||||
RETURN jsonb_build_object(
|
||||
'ok', false,
|
||||
'error', 'has_history',
|
||||
'message', 'Este paciente possui histórico clínico ou financeiro e não pode ser removido. Você pode desativar ou arquivar o paciente.'
|
||||
);
|
||||
END IF;
|
||||
|
||||
-- Verifica ownership via RLS (owner_id ou responsible_member_id)
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM public.patients
|
||||
WHERE id = p_patient_id
|
||||
AND (
|
||||
owner_id = auth.uid()
|
||||
OR responsible_member_id IN (
|
||||
SELECT id FROM public.tenant_members WHERE user_id = auth.uid()
|
||||
)
|
||||
)
|
||||
) THEN
|
||||
RETURN jsonb_build_object(
|
||||
'ok', false,
|
||||
'error', 'forbidden',
|
||||
'message', 'Sem permissão para excluir este paciente.'
|
||||
);
|
||||
END IF;
|
||||
|
||||
DELETE FROM public.patients WHERE id = p_patient_id;
|
||||
|
||||
RETURN jsonb_build_object('ok', true);
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.safe_delete_patient(p_patient_id uuid) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: sanitize_phone_br(text); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.sanitize_phone_br(raw_phone text) RETURNS text
|
||||
Reference in New Issue
Block a user