Sessoes 1-6 acumuladas: hardening B2, defesa em camadas, +192 testes
Repositorio estava ha ~5 sessoes sem commit. Consolida tudo desde d088a89.
Ver commit.md na raiz para descricao completa por sessao.
# Numeros
- A# auditoria abertos: 0/30
- V# verificacoes abertos: 5/52 (todos adiados com plano)
- T# testes escritos: 10/10
- Vitest: 192/192
- SQL integration: 33/33
- E2E (Playwright, novo): 5/5
- Migrations: 17 (10 novas Sessao 6)
- Areas auditadas: 7 (+documentos com 10 V#)
# Highlights Sessao 6 (hoje)
- V#34/V#41 Opcao B2: tenant_features com plano + override (RPC SECURITY DEFINER, tela /saas/tenant-features)
- A#20 rev2 self-hosted: defesa em 5 camadas (honeypot + rate limit + math captcha condicional + paranoid mode + dashboard /saas/security)
- Documentos hardening (V#43-V#49): tenant scoping em storage policies (vazamento entre clinicas eliminado), RPC validate_share_token, signatures policy granular
- SaaS Twilio Config (/saas/twilio-config): UI editavel para SID/webhook/cotacao; AUTH_TOKEN permanece em env var
- T#9 + T#10: useAgendaEvents.spec.js + Playwright E2E (descobriu bug no front que foi corrigido)
# Sessoes anteriores (1-5) consolidadas
- Sessao 1: auth/router/session, normalizeRole extraido
- Sessao 2: agenda - composables/services consolidados
- Sessao 3: pacientes - tenant_id em todas queries
- Sessao 4: security review pagina publica - 14/15 vulnerabilidades corrigidas
- Sessao 5: SaaS - P0 (A#30: 7 tabelas com RLS off corrigidas)
# .gitignore ajustado
- supabase/* + !supabase/functions/ (mantem 10 edge functions, ignora .temp/migrations gerados pelo CLI)
- database-novo/backups/ (regeneravel via db.cjs backup)
- test-results/ + playwright-report/
- .claude/settings.local.json (config local com senha de dev removida do tracking)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+4271
-8943
File diff suppressed because it is too large
Load Diff
@@ -1,256 +1,14 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Extensions e Schemas
|
||||
-- Extraído de schema.sql (2026-03-23)
|
||||
-- =============================================================================
|
||||
|
||||
--
|
||||
-- PostgreSQL database dump
|
||||
--
|
||||
|
||||
\restrict ABfzP9IZJ8pAzvgt6E9jKpFn1phQ3b3Lgk09BZZTle5el6ODr77nIXlXnCf1PS1
|
||||
|
||||
-- Dumped from database version 17.6
|
||||
-- Dumped by pg_dump version 17.6
|
||||
|
||||
SET statement_timeout = 0;
|
||||
SET lock_timeout = 0;
|
||||
SET idle_in_transaction_session_timeout = 0;
|
||||
SET transaction_timeout = 0;
|
||||
SET client_encoding = 'UTF8';
|
||||
SET standard_conforming_strings = on;
|
||||
SELECT pg_catalog.set_config('search_path', '', false);
|
||||
SET check_function_bodies = false;
|
||||
SET xmloption = content;
|
||||
SET client_min_messages = warning;
|
||||
SET row_security = off;
|
||||
|
||||
--
|
||||
-- Name: _realtime; Type: SCHEMA; Schema: -; Owner: postgres
|
||||
--
|
||||
|
||||
CREATE SCHEMA _realtime;
|
||||
|
||||
|
||||
ALTER SCHEMA _realtime OWNER TO postgres;
|
||||
|
||||
--
|
||||
-- Name: auth; Type: SCHEMA; Schema: -; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE SCHEMA auth;
|
||||
|
||||
|
||||
ALTER SCHEMA auth OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: pg_cron; Type: EXTENSION; Schema: -; Owner: -
|
||||
--
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS pg_cron WITH SCHEMA pg_catalog;
|
||||
|
||||
|
||||
--
|
||||
-- Name: EXTENSION pg_cron; Type: COMMENT; Schema: -; Owner:
|
||||
--
|
||||
|
||||
COMMENT ON EXTENSION pg_cron IS 'Job scheduler for PostgreSQL';
|
||||
|
||||
|
||||
--
|
||||
-- Name: extensions; Type: SCHEMA; Schema: -; Owner: postgres
|
||||
--
|
||||
|
||||
CREATE SCHEMA extensions;
|
||||
|
||||
|
||||
ALTER SCHEMA extensions OWNER TO postgres;
|
||||
|
||||
--
|
||||
-- Name: graphql; Type: SCHEMA; Schema: -; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE SCHEMA graphql;
|
||||
|
||||
|
||||
ALTER SCHEMA graphql OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: graphql_public; Type: SCHEMA; Schema: -; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE SCHEMA graphql_public;
|
||||
|
||||
|
||||
ALTER SCHEMA graphql_public OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: pg_net; Type: EXTENSION; Schema: -; Owner: -
|
||||
--
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS pg_net WITH SCHEMA extensions;
|
||||
|
||||
|
||||
--
|
||||
-- Name: EXTENSION pg_net; Type: COMMENT; Schema: -; Owner:
|
||||
--
|
||||
|
||||
COMMENT ON EXTENSION pg_net IS 'Async HTTP';
|
||||
|
||||
|
||||
--
|
||||
-- Name: pgbouncer; Type: SCHEMA; Schema: -; Owner: pgbouncer
|
||||
--
|
||||
|
||||
CREATE SCHEMA pgbouncer;
|
||||
|
||||
|
||||
ALTER SCHEMA pgbouncer OWNER TO pgbouncer;
|
||||
|
||||
--
|
||||
-- Name: realtime; Type: SCHEMA; Schema: -; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE SCHEMA realtime;
|
||||
|
||||
|
||||
ALTER SCHEMA realtime OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: storage; Type: SCHEMA; Schema: -; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE SCHEMA storage;
|
||||
|
||||
|
||||
ALTER SCHEMA storage OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: supabase_functions; Type: SCHEMA; Schema: -; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE SCHEMA supabase_functions;
|
||||
|
||||
|
||||
ALTER SCHEMA supabase_functions OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: vault; Type: SCHEMA; Schema: -; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE SCHEMA vault;
|
||||
|
||||
|
||||
ALTER SCHEMA vault OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: btree_gist; Type: EXTENSION; Schema: -; Owner: -
|
||||
--
|
||||
-- Extensions
|
||||
-- Gerado automaticamente em 2026-04-17T12:23:04.148Z
|
||||
-- Total: 10
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS btree_gist WITH SCHEMA public;
|
||||
|
||||
|
||||
--
|
||||
-- Name: EXTENSION btree_gist; Type: COMMENT; Schema: -; Owner:
|
||||
--
|
||||
|
||||
COMMENT ON EXTENSION btree_gist IS 'support for indexing common datatypes in GiST';
|
||||
|
||||
|
||||
--
|
||||
-- Name: citext; Type: EXTENSION; Schema: -; Owner: -
|
||||
--
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS citext WITH SCHEMA public;
|
||||
|
||||
|
||||
--
|
||||
-- Name: EXTENSION citext; Type: COMMENT; Schema: -; Owner:
|
||||
--
|
||||
|
||||
COMMENT ON EXTENSION citext IS 'data type for case-insensitive character strings';
|
||||
|
||||
|
||||
--
|
||||
-- Name: pg_graphql; Type: EXTENSION; Schema: -; Owner: -
|
||||
--
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS pg_cron WITH SCHEMA pg_catalog;
|
||||
CREATE EXTENSION IF NOT EXISTS pg_graphql WITH SCHEMA graphql;
|
||||
|
||||
|
||||
--
|
||||
-- Name: EXTENSION pg_graphql; Type: COMMENT; Schema: -; Owner:
|
||||
--
|
||||
|
||||
COMMENT ON EXTENSION pg_graphql IS 'pg_graphql: GraphQL support';
|
||||
|
||||
|
||||
--
|
||||
-- Name: pg_stat_statements; Type: EXTENSION; Schema: -; Owner: -
|
||||
--
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS pg_net WITH SCHEMA extensions;
|
||||
CREATE EXTENSION IF NOT EXISTS pg_stat_statements WITH SCHEMA extensions;
|
||||
|
||||
|
||||
--
|
||||
-- Name: EXTENSION pg_stat_statements; Type: COMMENT; Schema: -; Owner:
|
||||
--
|
||||
|
||||
COMMENT ON EXTENSION pg_stat_statements IS 'track planning and execution statistics of all SQL statements executed';
|
||||
|
||||
|
||||
--
|
||||
-- Name: pg_trgm; Type: EXTENSION; Schema: -; Owner: -
|
||||
--
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS pg_trgm WITH SCHEMA public;
|
||||
|
||||
|
||||
--
|
||||
-- Name: EXTENSION pg_trgm; Type: COMMENT; Schema: -; Owner:
|
||||
--
|
||||
|
||||
COMMENT ON EXTENSION pg_trgm IS 'text similarity measurement and index searching based on trigrams';
|
||||
|
||||
|
||||
--
|
||||
-- Name: pgcrypto; Type: EXTENSION; Schema: -; Owner: -
|
||||
--
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA extensions;
|
||||
|
||||
|
||||
--
|
||||
-- Name: EXTENSION pgcrypto; Type: COMMENT; Schema: -; Owner:
|
||||
--
|
||||
|
||||
COMMENT ON EXTENSION pgcrypto IS 'cryptographic functions';
|
||||
|
||||
|
||||
--
|
||||
-- Name: supabase_vault; Type: EXTENSION; Schema: -; Owner: -
|
||||
--
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS supabase_vault WITH SCHEMA vault;
|
||||
|
||||
|
||||
--
|
||||
-- Name: EXTENSION supabase_vault; Type: COMMENT; Schema: -; Owner:
|
||||
--
|
||||
|
||||
COMMENT ON EXTENSION supabase_vault IS 'Supabase Vault Extension';
|
||||
|
||||
|
||||
--
|
||||
-- Name: uuid-ossp; Type: EXTENSION; Schema: -; Owner: -
|
||||
--
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA extensions;
|
||||
|
||||
|
||||
--
|
||||
-- Name: EXTENSION "uuid-ossp"; Type: COMMENT; Schema: -; Owner:
|
||||
--
|
||||
|
||||
COMMENT ON EXTENSION "uuid-ossp" IS 'generate universally unique identifiers (UUIDs)';
|
||||
|
||||
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Types (Enums) — auth schema
|
||||
-- =============================================================================
|
||||
|
||||
--
|
||||
-- Name: aal_level; Type: TYPE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TYPE auth.aal_level AS ENUM (
|
||||
'aal1',
|
||||
'aal2',
|
||||
'aal3'
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE auth.aal_level OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: code_challenge_method; Type: TYPE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TYPE auth.code_challenge_method AS ENUM (
|
||||
's256',
|
||||
'plain'
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE auth.code_challenge_method OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: factor_status; Type: TYPE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TYPE auth.factor_status AS ENUM (
|
||||
'unverified',
|
||||
'verified'
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE auth.factor_status OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: factor_type; Type: TYPE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TYPE auth.factor_type AS ENUM (
|
||||
'totp',
|
||||
'webauthn',
|
||||
'phone'
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE auth.factor_type OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: oauth_authorization_status; Type: TYPE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TYPE auth.oauth_authorization_status AS ENUM (
|
||||
'pending',
|
||||
'approved',
|
||||
'denied',
|
||||
'expired'
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE auth.oauth_authorization_status OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: oauth_client_type; Type: TYPE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TYPE auth.oauth_client_type AS ENUM (
|
||||
'public',
|
||||
'confidential'
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE auth.oauth_client_type OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: oauth_registration_type; Type: TYPE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TYPE auth.oauth_registration_type AS ENUM (
|
||||
'dynamic',
|
||||
'manual'
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE auth.oauth_registration_type OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: oauth_response_type; Type: TYPE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TYPE auth.oauth_response_type AS ENUM (
|
||||
'code'
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE auth.oauth_response_type OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: one_time_token_type; Type: TYPE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TYPE auth.one_time_token_type AS ENUM (
|
||||
'confirmation_token',
|
||||
'reauthentication_token',
|
||||
'recovery_token',
|
||||
'email_change_token_new',
|
||||
'email_change_token_current',
|
||||
'phone_change_token'
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE auth.one_time_token_type OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: commitment_log_source; Type: TYPE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Types — realtime + storage schemas
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TYPE realtime.action AS ENUM (
|
||||
'INSERT',
|
||||
'UPDATE',
|
||||
'DELETE',
|
||||
'TRUNCATE',
|
||||
'ERROR'
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE realtime.action OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: equality_op; Type: TYPE; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TYPE realtime.equality_op AS ENUM (
|
||||
'eq',
|
||||
'neq',
|
||||
'lt',
|
||||
'lte',
|
||||
'gt',
|
||||
'gte',
|
||||
'in'
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE realtime.equality_op OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: user_defined_filter; Type: TYPE; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TYPE realtime.user_defined_filter AS (
|
||||
column_name text,
|
||||
op realtime.equality_op,
|
||||
value text
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE realtime.user_defined_filter OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: wal_column; Type: TYPE; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TYPE realtime.wal_column AS (
|
||||
name text,
|
||||
type_name text,
|
||||
type_oid oid,
|
||||
value jsonb,
|
||||
is_pkey boolean,
|
||||
is_selectable boolean
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE realtime.wal_column OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: wal_rls; Type: TYPE; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TYPE realtime.wal_rls AS (
|
||||
wal jsonb,
|
||||
is_rls_enabled boolean,
|
||||
subscription_ids uuid[],
|
||||
errors text[]
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE realtime.wal_rls OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: buckettype; Type: TYPE; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE TYPE storage.buckettype AS ENUM (
|
||||
'STANDARD',
|
||||
'ANALYTICS',
|
||||
'VECTOR'
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE storage.buckettype OWNER TO supabase_storage_admin;
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Types (Enums) — public schema
|
||||
-- =============================================================================
|
||||
-- commitment_log_source, determined_field_type, financial_record_type,
|
||||
-- recurrence_exception_type, recurrence_type, status_agenda_serie,
|
||||
-- status_evento_agenda, status_excecao_agenda, tipo_evento_agenda,
|
||||
-- tipo_excecao_agenda
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TYPE public.commitment_log_source AS ENUM (
|
||||
'manual',
|
||||
'auto'
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE public.commitment_log_source OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: determined_field_type; Type: TYPE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TYPE public.determined_field_type AS ENUM (
|
||||
'text',
|
||||
'textarea',
|
||||
'number',
|
||||
'date',
|
||||
'select',
|
||||
'boolean'
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE public.determined_field_type OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: financial_record_type; Type: TYPE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TYPE public.financial_record_type AS ENUM (
|
||||
'receita',
|
||||
'despesa'
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE public.financial_record_type OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: recurrence_exception_type; Type: TYPE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TYPE public.recurrence_exception_type AS ENUM (
|
||||
'cancel_session',
|
||||
'reschedule_session',
|
||||
'patient_missed',
|
||||
'therapist_canceled',
|
||||
'holiday_block'
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE public.recurrence_exception_type OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: recurrence_type; Type: TYPE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TYPE public.recurrence_type AS ENUM (
|
||||
'weekly',
|
||||
'biweekly',
|
||||
'monthly',
|
||||
'yearly',
|
||||
'custom_weekdays'
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE public.recurrence_type OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: status_agenda_serie; Type: TYPE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TYPE public.status_agenda_serie AS ENUM (
|
||||
'ativo',
|
||||
'pausado',
|
||||
'cancelado'
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE public.status_agenda_serie OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: status_evento_agenda; Type: TYPE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TYPE public.status_evento_agenda AS ENUM (
|
||||
'agendado',
|
||||
'realizado',
|
||||
'faltou',
|
||||
'cancelado',
|
||||
'remarcar'
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE public.status_evento_agenda OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: status_excecao_agenda; Type: TYPE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TYPE public.status_excecao_agenda AS ENUM (
|
||||
'pendente',
|
||||
'ativo',
|
||||
'arquivado'
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE public.status_excecao_agenda OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: tipo_evento_agenda; Type: TYPE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TYPE public.tipo_evento_agenda AS ENUM (
|
||||
'sessao',
|
||||
'bloqueio'
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE public.tipo_evento_agenda OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: tipo_excecao_agenda; Type: TYPE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TYPE public.tipo_excecao_agenda AS ENUM (
|
||||
'bloqueio',
|
||||
'horario_extra'
|
||||
);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,650 +0,0 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Functions — Agenda
|
||||
-- =============================================================================
|
||||
-- agenda_cfg_sync, agendador_dias_disponiveis, agendador_gerar_slug,
|
||||
-- agendador_slots_disponiveis, cancel_recurrence_from,
|
||||
-- cancelar_eventos_serie, fn_agenda_regras_semanais_no_overlap,
|
||||
-- split_recurrence_at, sync_busy_mirror, set_updated_at_recurrence
|
||||
-- =============================================================================
|
||||
|
||||
CREATE FUNCTION public.agenda_cfg_sync() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
begin
|
||||
if new.agenda_view_mode = 'custom' then
|
||||
new.usar_horario_admin_custom := true;
|
||||
new.admin_inicio_visualizacao := new.agenda_custom_start;
|
||||
new.admin_fim_visualizacao := new.agenda_custom_end;
|
||||
else
|
||||
new.usar_horario_admin_custom := false;
|
||||
end if;
|
||||
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.agenda_cfg_sync() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: agendador_dias_disponiveis(text, integer, integer); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.agendador_dias_disponiveis(p_slug text, p_ano integer, p_mes integer) RETURNS TABLE(data date, tem_slots boolean)
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
DECLARE
|
||||
v_owner_id uuid;
|
||||
v_antecedencia int;
|
||||
v_agora timestamptz;
|
||||
v_data date;
|
||||
v_data_inicio date;
|
||||
v_data_fim date;
|
||||
v_db_dow int;
|
||||
v_tem_slot boolean;
|
||||
v_bloqueado boolean;
|
||||
BEGIN
|
||||
SELECT c.owner_id, c.antecedencia_minima_horas
|
||||
INTO v_owner_id, v_antecedencia
|
||||
FROM public.agendador_configuracoes c
|
||||
WHERE c.link_slug = p_slug AND c.ativo = true
|
||||
LIMIT 1;
|
||||
|
||||
IF v_owner_id IS NULL THEN RETURN; END IF;
|
||||
|
||||
v_agora := now();
|
||||
v_data_inicio := make_date(p_ano, p_mes, 1);
|
||||
v_data_fim := (v_data_inicio + interval '1 month' - interval '1 day')::date;
|
||||
|
||||
v_data := v_data_inicio;
|
||||
WHILE v_data <= v_data_fim LOOP
|
||||
v_db_dow := extract(dow from v_data::timestamp)::int;
|
||||
|
||||
-- ── Dia inteiro bloqueado? (agenda_bloqueios) ─────────────────────────
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM public.agenda_bloqueios b
|
||||
WHERE b.owner_id = v_owner_id
|
||||
AND b.data_inicio <= v_data
|
||||
AND COALESCE(b.data_fim, v_data) >= v_data
|
||||
AND b.hora_inicio IS NULL -- bloqueio de dia inteiro
|
||||
AND (
|
||||
(NOT b.recorrente)
|
||||
OR (b.recorrente AND b.dia_semana = v_db_dow)
|
||||
)
|
||||
) INTO v_bloqueado;
|
||||
|
||||
IF v_bloqueado THEN
|
||||
v_data := v_data + 1;
|
||||
CONTINUE;
|
||||
END IF;
|
||||
|
||||
-- ── Tem slots disponíveis no dia? ─────────────────────────────────────
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM public.agenda_online_slots s
|
||||
WHERE s.owner_id = v_owner_id
|
||||
AND s.weekday = v_db_dow
|
||||
AND s.enabled = true
|
||||
AND (v_data::text || ' ' || s.time::text)::timestamp
|
||||
AT TIME ZONE 'America/Sao_Paulo'
|
||||
>= v_agora + (v_antecedencia || ' hours')::interval
|
||||
) INTO v_tem_slot;
|
||||
|
||||
IF v_tem_slot THEN
|
||||
data := v_data;
|
||||
tem_slots := true;
|
||||
RETURN NEXT;
|
||||
END IF;
|
||||
|
||||
v_data := v_data + 1;
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.agendador_dias_disponiveis(p_slug text, p_ano integer, p_mes integer) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: agendador_gerar_slug(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.agendador_gerar_slug() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_slug text;
|
||||
v_exists boolean;
|
||||
BEGIN
|
||||
-- só gera se ativou e não tem slug ainda
|
||||
IF NEW.ativo = true AND (NEW.link_slug IS NULL OR NEW.link_slug = '') THEN
|
||||
LOOP
|
||||
v_slug := lower(substring(replace(gen_random_uuid()::text, '-', ''), 1, 8));
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM public.agendador_configuracoes
|
||||
WHERE link_slug = v_slug AND owner_id <> NEW.owner_id
|
||||
) INTO v_exists;
|
||||
EXIT WHEN NOT v_exists;
|
||||
END LOOP;
|
||||
NEW.link_slug := v_slug;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.agendador_gerar_slug() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: agendador_slots_disponiveis(text, date); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.agendador_slots_disponiveis(p_slug text, p_data date) RETURNS TABLE(hora time without time zone, disponivel boolean)
|
||||
CREATE FUNCTION public.agendador_gerar_slug() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_slug text;
|
||||
v_exists boolean;
|
||||
BEGIN
|
||||
-- só gera se ativou e não tem slug ainda
|
||||
IF NEW.ativo = true AND (NEW.link_slug IS NULL OR NEW.link_slug = '') THEN
|
||||
LOOP
|
||||
v_slug := lower(substring(replace(gen_random_uuid()::text, '-', ''), 1, 8));
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM public.agendador_configuracoes
|
||||
WHERE link_slug = v_slug AND owner_id <> NEW.owner_id
|
||||
) INTO v_exists;
|
||||
EXIT WHEN NOT v_exists;
|
||||
END LOOP;
|
||||
NEW.link_slug := v_slug;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.agendador_gerar_slug() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: agendador_slots_disponiveis(text, date); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.agendador_slots_disponiveis(p_slug text, p_data date) RETURNS TABLE(hora time without time zone, disponivel boolean)
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
DECLARE
|
||||
v_owner_id uuid;
|
||||
v_duracao int;
|
||||
v_antecedencia int;
|
||||
v_agora timestamptz;
|
||||
v_db_dow int;
|
||||
v_slot time;
|
||||
v_slot_fim time;
|
||||
v_slot_ts timestamptz;
|
||||
v_ocupado boolean;
|
||||
-- loop de recorrências
|
||||
v_rule RECORD;
|
||||
v_rule_start_dow int;
|
||||
v_first_occ date;
|
||||
v_day_diff int;
|
||||
v_ex_type text;
|
||||
BEGIN
|
||||
SELECT c.owner_id, c.duracao_sessao_min, c.antecedencia_minima_horas
|
||||
INTO v_owner_id, v_duracao, v_antecedencia
|
||||
FROM public.agendador_configuracoes c
|
||||
WHERE c.link_slug = p_slug AND c.ativo = true
|
||||
LIMIT 1;
|
||||
|
||||
IF v_owner_id IS NULL THEN RETURN; END IF;
|
||||
|
||||
v_agora := now();
|
||||
v_db_dow := extract(dow from p_data::timestamp)::int;
|
||||
|
||||
-- ── Dia inteiro bloqueado? (agenda_bloqueios sem hora) ───────────────────
|
||||
-- Se sim, não há nenhum slot disponível — retorna vazio.
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM public.agenda_bloqueios b
|
||||
WHERE b.owner_id = v_owner_id
|
||||
AND b.data_inicio <= p_data
|
||||
AND COALESCE(b.data_fim, p_data) >= p_data
|
||||
AND b.hora_inicio IS NULL -- bloqueio de dia inteiro
|
||||
AND (
|
||||
(NOT b.recorrente)
|
||||
OR (b.recorrente AND b.dia_semana = v_db_dow)
|
||||
)
|
||||
) THEN
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
FOR v_slot IN
|
||||
SELECT s.time
|
||||
FROM public.agenda_online_slots s
|
||||
WHERE s.owner_id = v_owner_id
|
||||
AND s.weekday = v_db_dow
|
||||
AND s.enabled = true
|
||||
ORDER BY s.time
|
||||
LOOP
|
||||
v_slot_fim := v_slot + (v_duracao || ' minutes')::interval;
|
||||
v_ocupado := false;
|
||||
|
||||
-- ── Antecedência mínima ──────────────────────────────────────────────────
|
||||
v_slot_ts := (p_data::text || ' ' || v_slot::text)::timestamp
|
||||
AT TIME ZONE 'America/Sao_Paulo';
|
||||
IF v_slot_ts < v_agora + (v_antecedencia || ' hours')::interval THEN
|
||||
v_ocupado := true;
|
||||
END IF;
|
||||
|
||||
-- ── Bloqueio de horário específico (agenda_bloqueios com hora) ───────────
|
||||
IF NOT v_ocupado THEN
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM public.agenda_bloqueios b
|
||||
WHERE b.owner_id = v_owner_id
|
||||
AND b.data_inicio <= p_data
|
||||
AND COALESCE(b.data_fim, p_data) >= p_data
|
||||
AND b.hora_inicio IS NOT NULL
|
||||
AND b.hora_inicio < v_slot_fim
|
||||
AND b.hora_fim > v_slot
|
||||
AND (
|
||||
(NOT b.recorrente)
|
||||
OR (b.recorrente AND b.dia_semana = v_db_dow)
|
||||
)
|
||||
) INTO v_ocupado;
|
||||
END IF;
|
||||
|
||||
-- ── Eventos avulsos internos (agenda_eventos) ────────────────────────────
|
||||
IF NOT v_ocupado THEN
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM public.agenda_eventos e
|
||||
WHERE e.owner_id = v_owner_id
|
||||
AND e.status::text NOT IN ('cancelado', 'faltou')
|
||||
AND (e.inicio_em AT TIME ZONE 'America/Sao_Paulo')::date = p_data
|
||||
AND (e.inicio_em AT TIME ZONE 'America/Sao_Paulo')::time < v_slot_fim
|
||||
AND (e.fim_em AT TIME ZONE 'America/Sao_Paulo')::time > v_slot
|
||||
) INTO v_ocupado;
|
||||
END IF;
|
||||
|
||||
-- ── Recorrências ativas (recurrence_rules) ───────────────────────────────
|
||||
IF NOT v_ocupado THEN
|
||||
FOR v_rule IN
|
||||
SELECT
|
||||
r.id,
|
||||
r.start_date::date AS start_date,
|
||||
r.end_date::date AS end_date,
|
||||
r.start_time::time AS start_time,
|
||||
r.end_time::time AS end_time,
|
||||
COALESCE(r.interval, 1)::int AS interval
|
||||
FROM public.recurrence_rules r
|
||||
WHERE r.owner_id = v_owner_id
|
||||
AND r.status = 'ativo'
|
||||
AND p_data >= r.start_date::date
|
||||
AND (r.end_date IS NULL OR p_data <= r.end_date::date)
|
||||
AND v_db_dow = ANY(r.weekdays)
|
||||
AND r.start_time::time < v_slot_fim
|
||||
AND r.end_time::time > v_slot
|
||||
LOOP
|
||||
v_rule_start_dow := extract(dow from v_rule.start_date)::int;
|
||||
v_first_occ := v_rule.start_date
|
||||
+ (((v_db_dow - v_rule_start_dow + 7) % 7))::int;
|
||||
v_day_diff := (p_data - v_first_occ)::int;
|
||||
|
||||
IF v_day_diff >= 0 AND v_day_diff % (7 * v_rule.interval) = 0 THEN
|
||||
v_ex_type := NULL;
|
||||
SELECT ex.type INTO v_ex_type
|
||||
FROM public.recurrence_exceptions ex
|
||||
WHERE ex.recurrence_id = v_rule.id
|
||||
AND ex.original_date = p_data
|
||||
LIMIT 1;
|
||||
|
||||
IF v_ex_type IS NULL OR v_ex_type NOT IN (
|
||||
'cancel_session', 'patient_missed',
|
||||
'therapist_canceled', 'holiday_block',
|
||||
'reschedule_session'
|
||||
) THEN
|
||||
v_ocupado := true;
|
||||
EXIT;
|
||||
END IF;
|
||||
END IF;
|
||||
END LOOP;
|
||||
END IF;
|
||||
|
||||
-- ── Recorrências remarcadas para este dia ────────────────────────────────
|
||||
IF NOT v_ocupado THEN
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM public.recurrence_exceptions ex
|
||||
JOIN public.recurrence_rules r ON r.id = ex.recurrence_id
|
||||
WHERE r.owner_id = v_owner_id
|
||||
AND r.status = 'ativo'
|
||||
AND ex.type = 'reschedule_session'
|
||||
AND ex.new_date = p_data
|
||||
AND COALESCE(ex.new_start_time, r.start_time)::time < v_slot_fim
|
||||
AND COALESCE(ex.new_end_time, r.end_time)::time > v_slot
|
||||
) INTO v_ocupado;
|
||||
END IF;
|
||||
|
||||
-- ── Solicitações públicas pendentes ──────────────────────────────────────
|
||||
IF NOT v_ocupado THEN
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM public.agendador_solicitacoes sol
|
||||
WHERE sol.owner_id = v_owner_id
|
||||
AND sol.status = 'pendente'
|
||||
AND sol.data_solicitada = p_data
|
||||
AND sol.hora_solicitada = v_slot
|
||||
AND (sol.reservado_ate IS NULL OR sol.reservado_ate > v_agora)
|
||||
) INTO v_ocupado;
|
||||
END IF;
|
||||
|
||||
hora := v_slot;
|
||||
disponivel := NOT v_ocupado;
|
||||
RETURN NEXT;
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.agendador_slots_disponiveis(p_slug text, p_data date) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: auto_create_financial_record_from_session(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.auto_create_financial_record_from_session() RETURNS trigger
|
||||
CREATE FUNCTION public.cancel_recurrence_from(p_recurrence_id uuid, p_from_date date) RETURNS void
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
BEGIN
|
||||
UPDATE public.recurrence_rules
|
||||
SET
|
||||
end_date = p_from_date - INTERVAL '1 day',
|
||||
open_ended = false,
|
||||
status = CASE
|
||||
WHEN p_from_date <= start_date THEN 'cancelado'
|
||||
ELSE status
|
||||
END,
|
||||
updated_at = now()
|
||||
WHERE id = p_recurrence_id;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.cancel_recurrence_from(p_recurrence_id uuid, p_from_date date) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: cancel_subscription(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.cancel_subscription(p_subscription_id uuid) RETURNS public.subscriptions
|
||||
CREATE FUNCTION public.cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone DEFAULT now()) RETURNS integer
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
AS $$
|
||||
DECLARE
|
||||
v_count integer;
|
||||
BEGIN
|
||||
UPDATE public.agenda_eventos
|
||||
SET status = 'cancelado',
|
||||
updated_at = now()
|
||||
WHERE serie_id = p_serie_id
|
||||
AND inicio_em >= p_a_partir_de
|
||||
AND status NOT IN ('realizado', 'cancelado');
|
||||
|
||||
GET DIAGNOSTICS v_count = ROW_COUNT;
|
||||
RETURN v_count;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: FUNCTION cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone); Type: COMMENT; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
COMMENT ON FUNCTION public.cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone) IS 'Cancela todos os eventos futuros de uma série a partir de p_a_partir_de (inclusive).
|
||||
Não cancela eventos já realizados.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: change_subscription_plan(uuid, uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.change_subscription_plan(p_subscription_id uuid, p_new_plan_id uuid) RETURNS public.subscriptions
|
||||
CREATE FUNCTION public.fn_agenda_regras_semanais_no_overlap() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
declare
|
||||
v_count int;
|
||||
begin
|
||||
if new.ativo is false then
|
||||
return new;
|
||||
end if;
|
||||
|
||||
select count(*) into v_count
|
||||
from public.agenda_regras_semanais r
|
||||
where r.owner_id = new.owner_id
|
||||
and r.dia_semana = new.dia_semana
|
||||
and r.ativo is true
|
||||
and (tg_op = 'INSERT' or r.id <> new.id)
|
||||
and (new.hora_inicio < r.hora_fim and new.hora_fim > r.hora_inicio);
|
||||
|
||||
if v_count > 0 then
|
||||
raise exception 'Janela sobreposta: já existe uma regra ativa nesse intervalo.';
|
||||
end if;
|
||||
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.fn_agenda_regras_semanais_no_overlap() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: get_financial_report(uuid, date, date, text); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text DEFAULT 'month'::text) RETURNS TABLE(group_key text, group_label text, total_receitas numeric, total_despesas numeric, saldo numeric, total_pendente numeric, total_overdue numeric, count_records bigint)
|
||||
CREATE FUNCTION public.set_updated_at_recurrence() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN NEW.updated_at = now(); RETURN NEW; END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.set_updated_at_recurrence() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: split_recurrence_at(uuid, date); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.split_recurrence_at(p_recurrence_id uuid, p_from_date date) RETURNS uuid
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
DECLARE
|
||||
v_old public.recurrence_rules;
|
||||
v_new_id uuid;
|
||||
BEGIN
|
||||
-- busca a regra original
|
||||
SELECT * INTO v_old
|
||||
FROM public.recurrence_rules
|
||||
WHERE id = p_recurrence_id;
|
||||
|
||||
IF NOT FOUND THEN
|
||||
RAISE EXCEPTION 'recurrence_rule % não encontrada', p_recurrence_id;
|
||||
END IF;
|
||||
|
||||
-- encerra a regra antiga na data anterior
|
||||
UPDATE public.recurrence_rules
|
||||
SET
|
||||
end_date = p_from_date - INTERVAL '1 day',
|
||||
open_ended = false,
|
||||
updated_at = now()
|
||||
WHERE id = p_recurrence_id;
|
||||
|
||||
-- cria nova regra a partir de p_from_date
|
||||
INSERT INTO public.recurrence_rules (
|
||||
tenant_id, owner_id, therapist_id, patient_id,
|
||||
determined_commitment_id, type, interval, weekdays,
|
||||
start_time, end_time, timezone, duration_min,
|
||||
start_date, end_date, max_occurrences, open_ended,
|
||||
modalidade, titulo_custom, observacoes, extra_fields, status
|
||||
)
|
||||
SELECT
|
||||
tenant_id, owner_id, therapist_id, patient_id,
|
||||
determined_commitment_id, type, interval, weekdays,
|
||||
start_time, end_time, timezone, duration_min,
|
||||
p_from_date, v_old.end_date, v_old.max_occurrences, v_old.open_ended,
|
||||
modalidade, titulo_custom, observacoes, extra_fields, status
|
||||
FROM public.recurrence_rules
|
||||
WHERE id = p_recurrence_id
|
||||
RETURNING id INTO v_new_id;
|
||||
|
||||
RETURN v_new_id;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.split_recurrence_at(p_recurrence_id uuid, p_from_date date) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: subscription_intents_view_insert(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.subscription_intents_view_insert() RETURNS trigger
|
||||
CREATE FUNCTION public.sync_busy_mirror_agenda_eventos() RETURNS trigger
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
declare
|
||||
clinic_tenant uuid;
|
||||
is_personal boolean;
|
||||
should_mirror boolean;
|
||||
begin
|
||||
-- Anti-recursão: espelho não espelha
|
||||
if (tg_op <> 'DELETE') then
|
||||
if new.mirror_of_event_id is not null then
|
||||
return new;
|
||||
end if;
|
||||
else
|
||||
if old.mirror_of_event_id is not null then
|
||||
return old;
|
||||
end if;
|
||||
end if;
|
||||
|
||||
-- Define se é pessoal e se deve espelhar
|
||||
if (tg_op = 'DELETE') then
|
||||
is_personal := (old.tenant_id = old.owner_id);
|
||||
should_mirror := (old.visibility_scope in ('busy_only','private'));
|
||||
else
|
||||
is_personal := (new.tenant_id = new.owner_id);
|
||||
should_mirror := (new.visibility_scope in ('busy_only','private'));
|
||||
end if;
|
||||
|
||||
-- Se não é pessoal, não faz nada
|
||||
if not is_personal then
|
||||
if (tg_op = 'DELETE') then
|
||||
return old;
|
||||
end if;
|
||||
return new;
|
||||
end if;
|
||||
|
||||
-- DELETE: remove espelhos existentes
|
||||
if (tg_op = 'DELETE') then
|
||||
delete from public.agenda_eventos e
|
||||
where e.mirror_of_event_id = old.id
|
||||
and e.mirror_source = 'personal_busy_mirror';
|
||||
|
||||
return old;
|
||||
end if;
|
||||
|
||||
-- INSERT/UPDATE:
|
||||
-- Se não deve espelhar, remove espelhos e sai
|
||||
if not should_mirror then
|
||||
delete from public.agenda_eventos e
|
||||
where e.mirror_of_event_id = new.id
|
||||
and e.mirror_source = 'personal_busy_mirror';
|
||||
|
||||
return new;
|
||||
end if;
|
||||
|
||||
-- Para cada clínica onde o usuário é therapist active, cria/atualiza o "Ocupado"
|
||||
for clinic_tenant in
|
||||
select tm.tenant_id
|
||||
from public.tenant_members tm
|
||||
where tm.user_id = new.owner_id
|
||||
and tm.role = 'therapist'
|
||||
and tm.status = 'active'
|
||||
and tm.tenant_id <> new.owner_id
|
||||
loop
|
||||
insert into public.agenda_eventos (
|
||||
tenant_id,
|
||||
owner_id,
|
||||
terapeuta_id,
|
||||
paciente_id,
|
||||
tipo,
|
||||
status,
|
||||
titulo,
|
||||
observacoes,
|
||||
inicio_em,
|
||||
fim_em,
|
||||
mirror_of_event_id,
|
||||
mirror_source,
|
||||
visibility_scope,
|
||||
created_at,
|
||||
updated_at
|
||||
) values (
|
||||
clinic_tenant,
|
||||
new.owner_id,
|
||||
new.owner_id,
|
||||
null,
|
||||
'bloqueio'::public.tipo_evento_agenda,
|
||||
'agendado'::public.status_evento_agenda,
|
||||
'Ocupado',
|
||||
null,
|
||||
new.inicio_em,
|
||||
new.fim_em,
|
||||
new.id,
|
||||
'personal_busy_mirror',
|
||||
'public',
|
||||
now(),
|
||||
now()
|
||||
)
|
||||
on conflict (tenant_id, mirror_of_event_id) where mirror_of_event_id is not null
|
||||
do update set
|
||||
owner_id = excluded.owner_id,
|
||||
terapeuta_id = excluded.terapeuta_id,
|
||||
tipo = excluded.tipo,
|
||||
status = excluded.status,
|
||||
titulo = excluded.titulo,
|
||||
observacoes = excluded.observacoes,
|
||||
inicio_em = excluded.inicio_em,
|
||||
fim_em = excluded.fim_em,
|
||||
updated_at = now();
|
||||
end loop;
|
||||
|
||||
-- Limpa espelhos de clínicas onde o vínculo therapist active não existe mais
|
||||
delete from public.agenda_eventos e
|
||||
where e.mirror_of_event_id = new.id
|
||||
and e.mirror_source = 'personal_busy_mirror'
|
||||
and not exists (
|
||||
select 1
|
||||
from public.tenant_members tm
|
||||
where tm.user_id = new.owner_id
|
||||
and tm.role = 'therapist'
|
||||
and tm.status = 'active'
|
||||
and tm.tenant_id = e.tenant_id
|
||||
);
|
||||
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.sync_busy_mirror_agenda_eventos() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: sync_overdue_financial_records(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.sync_overdue_financial_records() RETURNS integer
|
||||
@@ -1,11 +1,6 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Functions — auth schema
|
||||
-- auth.email(), auth.jwt(), auth.role(), auth.uid()
|
||||
-- =============================================================================
|
||||
|
||||
--
|
||||
-- Name: email(); Type: FUNCTION; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
-- Functions: auth
|
||||
-- Gerado automaticamente em 2026-04-17T12:23:05.221Z
|
||||
-- Total: 4
|
||||
|
||||
CREATE FUNCTION auth.email() RETURNS text
|
||||
LANGUAGE sql STABLE
|
||||
@@ -17,20 +12,6 @@ CREATE FUNCTION auth.email() RETURNS text
|
||||
)::text
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION auth.email() OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: FUNCTION email(); Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON FUNCTION auth.email() IS 'Deprecated. Use auth.jwt() -> ''email'' instead.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: jwt(); Type: FUNCTION; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION auth.jwt() RETURNS jsonb
|
||||
LANGUAGE sql STABLE
|
||||
AS $$
|
||||
@@ -41,13 +22,6 @@ CREATE FUNCTION auth.jwt() RETURNS jsonb
|
||||
)::jsonb
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION auth.jwt() OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: role(); Type: FUNCTION; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION auth.role() RETURNS text
|
||||
LANGUAGE sql STABLE
|
||||
AS $$
|
||||
@@ -58,20 +32,6 @@ CREATE FUNCTION auth.role() RETURNS text
|
||||
)::text
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION auth.role() OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: FUNCTION role(); Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON FUNCTION auth.role() IS 'Deprecated. Use auth.jwt() -> ''role'' instead.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: uid(); Type: FUNCTION; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION auth.uid() RETURNS uuid
|
||||
LANGUAGE sql STABLE
|
||||
AS $$
|
||||
@@ -81,13 +41,3 @@ CREATE FUNCTION auth.uid() RETURNS uuid
|
||||
(nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'sub')
|
||||
)::uuid
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION auth.uid() OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: FUNCTION uid(); Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON FUNCTION auth.uid() IS 'Deprecated. Use auth.jwt() -> ''sub'' instead.';
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+3
-96
@@ -1,7 +1,6 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Functions — infraestrutura
|
||||
-- extensions.grant_pg_*, pgbouncer.get_auth, etc.
|
||||
-- =============================================================================
|
||||
-- Functions: extensions
|
||||
-- Gerado automaticamente em 2026-04-17T12:23:05.222Z
|
||||
-- Total: 6
|
||||
|
||||
CREATE FUNCTION extensions.grant_pg_cron_access() RETURNS event_trigger
|
||||
LANGUAGE plpgsql
|
||||
@@ -35,20 +34,6 @@ BEGIN
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION extensions.grant_pg_cron_access() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: FUNCTION grant_pg_cron_access(); Type: COMMENT; Schema: extensions; Owner: supabase_admin
|
||||
--
|
||||
|
||||
COMMENT ON FUNCTION extensions.grant_pg_cron_access() IS 'Grants access to pg_cron';
|
||||
|
||||
|
||||
--
|
||||
-- Name: grant_pg_graphql_access(); Type: FUNCTION; Schema: extensions; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION extensions.grant_pg_graphql_access() RETURNS event_trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $_$
|
||||
@@ -102,20 +87,6 @@ BEGIN
|
||||
END;
|
||||
$_$;
|
||||
|
||||
|
||||
ALTER FUNCTION extensions.grant_pg_graphql_access() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: FUNCTION grant_pg_graphql_access(); Type: COMMENT; Schema: extensions; Owner: supabase_admin
|
||||
--
|
||||
|
||||
COMMENT ON FUNCTION extensions.grant_pg_graphql_access() IS 'Grants access to pg_graphql';
|
||||
|
||||
|
||||
--
|
||||
-- Name: grant_pg_net_access(); Type: FUNCTION; Schema: extensions; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION extensions.grant_pg_net_access() RETURNS event_trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
@@ -145,20 +116,6 @@ BEGIN
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION extensions.grant_pg_net_access() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: FUNCTION grant_pg_net_access(); Type: COMMENT; Schema: extensions; Owner: supabase_admin
|
||||
--
|
||||
|
||||
COMMENT ON FUNCTION extensions.grant_pg_net_access() IS 'Grants access to pg_net';
|
||||
|
||||
|
||||
--
|
||||
-- Name: pgrst_ddl_watch(); Type: FUNCTION; Schema: extensions; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION extensions.pgrst_ddl_watch() RETURNS event_trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
@@ -187,13 +144,6 @@ BEGIN
|
||||
END LOOP;
|
||||
END; $$;
|
||||
|
||||
|
||||
ALTER FUNCTION extensions.pgrst_ddl_watch() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: pgrst_drop_watch(); Type: FUNCTION; Schema: extensions; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION extensions.pgrst_drop_watch() RETURNS event_trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
@@ -220,13 +170,6 @@ BEGIN
|
||||
END LOOP;
|
||||
END; $$;
|
||||
|
||||
|
||||
ALTER FUNCTION extensions.pgrst_drop_watch() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: set_graphql_placeholder(); Type: FUNCTION; Schema: extensions; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION extensions.set_graphql_placeholder() RETURNS event_trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $_$
|
||||
@@ -278,39 +221,3 @@ CREATE FUNCTION extensions.set_graphql_placeholder() RETURNS event_trigger
|
||||
|
||||
END;
|
||||
$_$;
|
||||
|
||||
|
||||
ALTER FUNCTION extensions.set_graphql_placeholder() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: FUNCTION set_graphql_placeholder(); Type: COMMENT; Schema: extensions; Owner: supabase_admin
|
||||
--
|
||||
|
||||
COMMENT ON FUNCTION extensions.set_graphql_placeholder() IS 'Reintroduces placeholder function for graphql_public.graphql';
|
||||
|
||||
|
||||
--
|
||||
-- Name: get_auth(text); Type: FUNCTION; Schema: pgbouncer; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION pgbouncer.get_auth(p_usename text) RETURNS TABLE(username text, password text)
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO ''
|
||||
AS $_$
|
||||
begin
|
||||
raise debug 'PgBouncer auth request: %', p_usename;
|
||||
|
||||
return query
|
||||
select
|
||||
rolname::text,
|
||||
case when rolvaliduntil < now()
|
||||
then null
|
||||
else rolpassword::text
|
||||
end
|
||||
from pg_authid
|
||||
where rolname=$1 and rolcanlogin;
|
||||
end;
|
||||
$_$;
|
||||
|
||||
|
||||
ALTER FUNCTION pgbouncer.get_auth(p_usename text) OWNER TO supabase_admin;
|
||||
@@ -1,818 +0,0 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Functions — Financeiro
|
||||
-- =============================================================================
|
||||
-- auto_create_financial_record_from_session, create_financial_record_for_session,
|
||||
-- create_therapist_payout, get_financial_report, get_financial_summary,
|
||||
-- list_financial_records, mark_as_paid, mark_payout_as_paid,
|
||||
-- seed_default_financial_categories, sync_overdue_financial_records,
|
||||
-- trg_fn_financial_records_auto_overdue, set_insurance/services_updated_at
|
||||
-- =============================================================================
|
||||
|
||||
CREATE FUNCTION public.auto_create_financial_record_from_session() RETURNS trigger
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
DECLARE
|
||||
v_price NUMERIC(10,2);
|
||||
v_services_total NUMERIC(10,2);
|
||||
v_already_billed BOOLEAN;
|
||||
BEGIN
|
||||
-- ── Guards de saída rápida ──────────────────────────────────────────────
|
||||
|
||||
-- Só processa quando o status muda PARA 'realizado'
|
||||
IF NEW.status::TEXT <> 'realizado' THEN
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
|
||||
-- Só processa quando houve mudança real de status
|
||||
IF OLD.status IS NOT DISTINCT FROM NEW.status THEN
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
|
||||
-- Só sessões (não bloqueios, feriados, etc.)
|
||||
IF NEW.tipo::TEXT <> 'sessao' THEN
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
|
||||
-- Paciente obrigatório para vincular a cobrança
|
||||
IF NEW.patient_id IS NULL THEN
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
|
||||
-- Sessões de pacote têm cobrança gerenciada por billing_contract
|
||||
IF NEW.billing_contract_id IS NOT NULL THEN
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
|
||||
-- Idempotência: já existe financial_record para este evento?
|
||||
SELECT billed INTO v_already_billed
|
||||
FROM public.agenda_eventos
|
||||
WHERE id = NEW.id;
|
||||
|
||||
IF v_already_billed = TRUE THEN
|
||||
-- Confirma no financial_records também (dupla verificação)
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM public.financial_records
|
||||
WHERE agenda_evento_id = NEW.id AND deleted_at IS NULL
|
||||
) THEN
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
-- ── Busca do preço ──────────────────────────────────────────────────────
|
||||
|
||||
v_price := NULL;
|
||||
|
||||
-- Prioridade 1: soma dos serviços da regra de recorrência
|
||||
IF NEW.recurrence_id IS NOT NULL THEN
|
||||
SELECT COALESCE(SUM(rrs.final_price), 0)
|
||||
INTO v_services_total
|
||||
FROM public.recurrence_rule_services rrs
|
||||
WHERE rrs.rule_id = NEW.recurrence_id;
|
||||
|
||||
IF v_services_total > 0 THEN
|
||||
v_price := v_services_total;
|
||||
END IF;
|
||||
|
||||
-- Prioridade 2: price direto da regra (fallback se sem serviços)
|
||||
IF v_price IS NULL OR v_price = 0 THEN
|
||||
SELECT price INTO v_price
|
||||
FROM public.recurrence_rules
|
||||
WHERE id = NEW.recurrence_id;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
-- Prioridade 3: price do próprio evento de agenda
|
||||
IF v_price IS NULL OR v_price = 0 THEN
|
||||
v_price := NEW.price;
|
||||
END IF;
|
||||
|
||||
-- Sem preço → não criar registro (não é erro, apenas skip silencioso)
|
||||
IF v_price IS NULL OR v_price <= 0 THEN
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
|
||||
-- ── Criação do financial_record ─────────────────────────────────────────
|
||||
|
||||
INSERT INTO public.financial_records (
|
||||
owner_id,
|
||||
tenant_id,
|
||||
patient_id,
|
||||
agenda_evento_id,
|
||||
type,
|
||||
amount,
|
||||
discount_amount,
|
||||
final_amount,
|
||||
clinic_fee_pct,
|
||||
clinic_fee_amount,
|
||||
status,
|
||||
due_date
|
||||
-- payment_method: NULL até o momento do pagamento (mark_as_paid preenche)
|
||||
) VALUES (
|
||||
NEW.owner_id,
|
||||
NEW.tenant_id,
|
||||
NEW.patient_id,
|
||||
NEW.id,
|
||||
'receita',
|
||||
v_price,
|
||||
0,
|
||||
v_price,
|
||||
0, -- clinic_fee_pct: sem campo de configuração global no schema atual.
|
||||
0, -- clinic_fee_amount: calculado manualmente ou via update posterior.
|
||||
'pending',
|
||||
(NEW.inicio_em::DATE + 7) -- vencimento padrão: 7 dias após a sessão
|
||||
);
|
||||
|
||||
-- ── Marca sessão como billed ────────────────────────────────────────────
|
||||
-- UPDATE em billed (não em status) → não re-dispara este trigger
|
||||
UPDATE public.agenda_eventos
|
||||
SET billed = TRUE
|
||||
WHERE id = NEW.id;
|
||||
|
||||
RETURN NEW;
|
||||
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
-- Log silencioso: nunca bloquear a agenda por falha financeira
|
||||
RAISE WARNING '[auto_create_financial_record_from_session] evento=% erro=%',
|
||||
NEW.id, SQLERRM;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.auto_create_financial_record_from_session() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: FUNCTION auto_create_financial_record_from_session(); Type: COMMENT; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
COMMENT ON FUNCTION public.auto_create_financial_record_from_session() IS 'Trigger que cria automaticamente um financial_record (receita, pending) quando uma sessão de agenda é marcada como realizada. Prioridade de preço: recurrence_rule_services > recurrence_rules.price > agenda_eventos.price. Skip silencioso se sem preço, pacote ou registro já existente.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: can_delete_patient(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.can_delete_patient(p_patient_id uuid) RETURNS boolean
|
||||
CREATE FUNCTION public.create_financial_record_for_session(p_tenant_id uuid, p_owner_id uuid, p_patient_id uuid, p_agenda_evento_id uuid, p_amount numeric, p_due_date date) RETURNS SETOF public.financial_records
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
DECLARE
|
||||
v_existing public.financial_records%ROWTYPE;
|
||||
v_new public.financial_records%ROWTYPE;
|
||||
BEGIN
|
||||
-- Idempotência: retorna o registro existente se já foi criado
|
||||
SELECT * INTO v_existing
|
||||
FROM public.financial_records
|
||||
WHERE agenda_evento_id = p_agenda_evento_id
|
||||
AND deleted_at IS NULL
|
||||
LIMIT 1;
|
||||
|
||||
IF FOUND THEN
|
||||
RETURN NEXT v_existing;
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
-- Cria o novo registro
|
||||
INSERT INTO public.financial_records (
|
||||
tenant_id,
|
||||
owner_id,
|
||||
patient_id,
|
||||
agenda_evento_id,
|
||||
amount,
|
||||
discount_amount,
|
||||
final_amount,
|
||||
status,
|
||||
due_date
|
||||
) VALUES (
|
||||
p_tenant_id,
|
||||
p_owner_id,
|
||||
p_patient_id,
|
||||
p_agenda_evento_id,
|
||||
p_amount,
|
||||
0,
|
||||
p_amount,
|
||||
'pending',
|
||||
p_due_date
|
||||
)
|
||||
RETURNING * INTO v_new;
|
||||
|
||||
-- Marca o evento da agenda como billed = true
|
||||
UPDATE public.agenda_eventos
|
||||
SET billed = TRUE
|
||||
WHERE id = p_agenda_evento_id;
|
||||
|
||||
RETURN NEXT v_new;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.create_financial_record_for_session(p_tenant_id uuid, p_owner_id uuid, p_patient_id uuid, p_agenda_evento_id uuid, p_amount numeric, p_due_date date) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: create_patient_intake_request(text, text, text, text, text, boolean); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
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
|
||||
CREATE FUNCTION public.create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date) RETURNS public.therapist_payouts
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
DECLARE
|
||||
v_payout public.therapist_payouts%ROWTYPE;
|
||||
v_total_sessions INTEGER;
|
||||
v_gross NUMERIC(10,2);
|
||||
v_clinic_fee NUMERIC(10,2);
|
||||
v_net NUMERIC(10,2);
|
||||
BEGIN
|
||||
-- ── Verificação de permissão ────────────────────────────────────────────
|
||||
-- Apenas o próprio terapeuta ou o tenant_admin pode criar o repasse
|
||||
IF auth.uid() <> p_therapist_id AND NOT public.is_tenant_admin(p_tenant_id) THEN
|
||||
RAISE EXCEPTION 'Sem permissão para criar repasse para este terapeuta.';
|
||||
END IF;
|
||||
|
||||
-- ── Verifica se já existe repasse para o mesmo período ─────────────────
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM public.therapist_payouts
|
||||
WHERE owner_id = p_therapist_id
|
||||
AND tenant_id = p_tenant_id
|
||||
AND period_start = p_period_start
|
||||
AND period_end = p_period_end
|
||||
AND status <> 'cancelled'
|
||||
) THEN
|
||||
RAISE EXCEPTION
|
||||
'Já existe um repasse ativo para o período % a % deste terapeuta.',
|
||||
p_period_start, p_period_end;
|
||||
END IF;
|
||||
|
||||
-- ── Agrega os financial_records elegíveis ──────────────────────────────
|
||||
-- Elegíveis: paid, receita, owner=terapeuta, tenant correto, paid_at no período,
|
||||
-- não soft-deleted, ainda não vinculados a nenhum payout.
|
||||
SELECT
|
||||
COUNT(*) AS total_sessions,
|
||||
COALESCE(SUM(amount), 0) AS gross_amount,
|
||||
COALESCE(SUM(clinic_fee_amount), 0) AS clinic_fee_total,
|
||||
COALESCE(SUM(net_amount), 0) AS net_amount
|
||||
INTO
|
||||
v_total_sessions, v_gross, v_clinic_fee, v_net
|
||||
FROM public.financial_records fr
|
||||
WHERE fr.owner_id = p_therapist_id
|
||||
AND fr.tenant_id = p_tenant_id
|
||||
AND fr.type = 'receita'
|
||||
AND fr.status = 'paid'
|
||||
AND fr.deleted_at IS NULL
|
||||
AND fr.paid_at::DATE BETWEEN p_period_start AND p_period_end
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM public.therapist_payout_records tpr
|
||||
WHERE tpr.financial_record_id = fr.id
|
||||
);
|
||||
|
||||
-- Sem registros elegíveis → não criar payout vazio
|
||||
IF v_total_sessions = 0 THEN
|
||||
RAISE EXCEPTION
|
||||
'Nenhum registro financeiro elegível encontrado para o período % a %.',
|
||||
p_period_start, p_period_end;
|
||||
END IF;
|
||||
|
||||
-- ── Cria o repasse ─────────────────────────────────────────────────────
|
||||
INSERT INTO public.therapist_payouts (
|
||||
owner_id,
|
||||
tenant_id,
|
||||
period_start,
|
||||
period_end,
|
||||
total_sessions,
|
||||
gross_amount,
|
||||
clinic_fee_total,
|
||||
net_amount,
|
||||
status
|
||||
) VALUES (
|
||||
p_therapist_id,
|
||||
p_tenant_id,
|
||||
p_period_start,
|
||||
p_period_end,
|
||||
v_total_sessions,
|
||||
v_gross,
|
||||
v_clinic_fee,
|
||||
v_net,
|
||||
'pending'
|
||||
)
|
||||
RETURNING * INTO v_payout;
|
||||
|
||||
-- ── Vincula os financial_records ao repasse ────────────────────────────
|
||||
INSERT INTO public.therapist_payout_records (payout_id, financial_record_id)
|
||||
SELECT v_payout.id, fr.id
|
||||
FROM public.financial_records fr
|
||||
WHERE fr.owner_id = p_therapist_id
|
||||
AND fr.tenant_id = p_tenant_id
|
||||
AND fr.type = 'receita'
|
||||
AND fr.status = 'paid'
|
||||
AND fr.deleted_at IS NULL
|
||||
AND fr.paid_at::DATE BETWEEN p_period_start AND p_period_end
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM public.therapist_payout_records tpr
|
||||
WHERE tpr.financial_record_id = fr.id
|
||||
);
|
||||
|
||||
RETURN v_payout;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: FUNCTION create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date); Type: COMMENT; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
COMMENT ON FUNCTION public.create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date) IS 'Cria um repasse para o terapeuta com todos os financial_records paid+receita do período que ainda não estejam vinculados a outro repasse. Lança exceção se não houver registros elegíveis ou se já houver repasse ativo no período.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: current_member_id(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.current_member_id(p_tenant_id uuid) RETURNS uuid
|
||||
CREATE FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text DEFAULT 'month'::text) RETURNS TABLE(group_key text, group_label text, total_receitas numeric, total_despesas numeric, saldo numeric, total_pendente numeric, total_overdue numeric, count_records bigint)
|
||||
LANGUAGE sql STABLE SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
|
||||
-- ── Valida p_group_by antes de executar ──────────────────────────────────
|
||||
-- (lança erro se valor inválido; plpgsql seria necessário para isso em SQL puro,
|
||||
-- então usamos um CTE de validação com CASE WHEN para retornar vazio em vez de erro)
|
||||
|
||||
WITH base AS (
|
||||
SELECT
|
||||
fr.type,
|
||||
fr.amount,
|
||||
fr.final_amount,
|
||||
fr.status,
|
||||
fr.deleted_at,
|
||||
-- Chave de agrupamento calculada conforme p_group_by
|
||||
CASE p_group_by
|
||||
WHEN 'month' THEN TO_CHAR(
|
||||
COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE),
|
||||
'YYYY-MM'
|
||||
)
|
||||
WHEN 'week' THEN TO_CHAR(
|
||||
COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE),
|
||||
'IYYY-"W"IW'
|
||||
)
|
||||
WHEN 'category' THEN COALESCE(fr.category_id::TEXT, fr.category, 'sem_categoria')
|
||||
WHEN 'patient' THEN COALESCE(fr.patient_id::TEXT, 'sem_paciente')
|
||||
ELSE NULL -- group_by inválido → group_key NULL → retorno vazio
|
||||
END AS gkey,
|
||||
-- Label legível (enriquecido via JOIN abaixo quando possível)
|
||||
CASE p_group_by
|
||||
WHEN 'month' THEN TO_CHAR(
|
||||
COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE),
|
||||
'YYYY-MM'
|
||||
)
|
||||
WHEN 'week' THEN TO_CHAR(
|
||||
COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE),
|
||||
'IYYY-"W"IW'
|
||||
)
|
||||
WHEN 'category' THEN COALESCE(fc.name, fr.category, 'Sem categoria')
|
||||
WHEN 'patient' THEN COALESCE(p.nome_completo, fr.patient_id::TEXT, 'Sem paciente')
|
||||
ELSE NULL
|
||||
END AS glabel
|
||||
FROM public.financial_records fr
|
||||
LEFT JOIN public.financial_categories fc
|
||||
ON fc.id = fr.category_id
|
||||
LEFT JOIN public.patients p
|
||||
ON p.id = fr.patient_id
|
||||
WHERE fr.owner_id = p_owner_id
|
||||
AND fr.deleted_at IS NULL
|
||||
AND COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE)
|
||||
BETWEEN p_start_date AND p_end_date
|
||||
)
|
||||
|
||||
SELECT
|
||||
gkey AS group_key,
|
||||
glabel AS group_label,
|
||||
|
||||
COALESCE(SUM(final_amount) FILTER (WHERE type = 'receita' AND status = 'paid'), 0)
|
||||
AS total_receitas,
|
||||
|
||||
COALESCE(SUM(final_amount) FILTER (WHERE type = 'despesa' AND status = 'paid'), 0)
|
||||
AS total_despesas,
|
||||
|
||||
COALESCE(SUM(final_amount) FILTER (WHERE type = 'receita' AND status = 'paid'), 0)
|
||||
- COALESCE(SUM(final_amount) FILTER (WHERE type = 'despesa' AND status = 'paid'), 0)
|
||||
AS saldo,
|
||||
|
||||
COALESCE(SUM(final_amount) FILTER (WHERE status = 'pending'), 0) AS total_pendente,
|
||||
|
||||
COALESCE(SUM(final_amount) FILTER (WHERE status = 'overdue'), 0) AS total_overdue,
|
||||
|
||||
COUNT(*) AS count_records
|
||||
|
||||
FROM base
|
||||
WHERE gkey IS NOT NULL -- descarta p_group_by inválido
|
||||
GROUP BY gkey, glabel
|
||||
ORDER BY gkey ASC;
|
||||
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: FUNCTION get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text); Type: COMMENT; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
COMMENT ON FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text) IS 'Relatório financeiro agrupado por mês, semana ISO, categoria ou paciente. p_group_by aceita: ''month'' | ''week'' | ''category'' | ''patient''. Totais de receita/despesa consideram apenas registros com status=paid. total_pendente e total_overdue incluem todos os tipos (receita + despesa).';
|
||||
|
||||
|
||||
--
|
||||
-- Name: get_financial_summary(uuid, integer, integer); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.get_financial_summary(p_owner_id uuid, p_year integer, p_month integer) RETURNS TABLE(total_receitas numeric, total_despesas numeric, total_pendente numeric, saldo_liquido numeric, total_repasse numeric, count_receitas bigint, count_despesas bigint)
|
||||
LANGUAGE sql STABLE SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
SELECT
|
||||
-- Receitas pagas no período
|
||||
COALESCE(SUM(amount) FILTER (
|
||||
WHERE type = 'receita' AND status = 'paid'
|
||||
), 0) AS total_receitas,
|
||||
|
||||
-- Despesas pagas no período
|
||||
COALESCE(SUM(amount) FILTER (
|
||||
WHERE type = 'despesa' AND status = 'paid'
|
||||
), 0) AS total_despesas,
|
||||
|
||||
-- Tudo pendente ou vencido (receitas + despesas)
|
||||
COALESCE(SUM(amount) FILTER (
|
||||
WHERE status IN ('pending', 'overdue')
|
||||
), 0) AS total_pendente,
|
||||
|
||||
-- Saldo líquido (receitas pagas − despesas pagas)
|
||||
COALESCE(SUM(amount) FILTER (
|
||||
WHERE type = 'receita' AND status = 'paid'
|
||||
), 0)
|
||||
- COALESCE(SUM(amount) FILTER (
|
||||
WHERE type = 'despesa' AND status = 'paid'
|
||||
), 0) AS saldo_liquido,
|
||||
|
||||
-- Total repassado à clínica (apenas receitas pagas)
|
||||
COALESCE(SUM(clinic_fee_amount) FILTER (
|
||||
WHERE type = 'receita' AND status = 'paid'
|
||||
), 0) AS total_repasse,
|
||||
|
||||
-- Contadores (excluindo soft-deleted)
|
||||
COUNT(*) FILTER (WHERE type = 'receita' AND deleted_at IS NULL) AS count_receitas,
|
||||
COUNT(*) FILTER (WHERE type = 'despesa' AND deleted_at IS NULL) AS count_despesas
|
||||
|
||||
FROM public.financial_records
|
||||
WHERE owner_id = p_owner_id
|
||||
AND deleted_at IS NULL
|
||||
AND EXTRACT(YEAR FROM COALESCE(paid_at::DATE, due_date, created_at::DATE)) = p_year
|
||||
AND EXTRACT(MONTH FROM COALESCE(paid_at::DATE, due_date, created_at::DATE)) = p_month;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.get_financial_summary(p_owner_id uuid, p_year integer, p_month integer) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: get_my_email(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.get_my_email() RETURNS text
|
||||
CREATE FUNCTION public.list_financial_records(p_owner_id uuid, p_year integer DEFAULT NULL::integer, p_month integer DEFAULT NULL::integer, p_type text DEFAULT NULL::text, p_status text DEFAULT NULL::text, p_patient_id uuid DEFAULT NULL::uuid, p_limit integer DEFAULT 50, p_offset integer DEFAULT 0) RETURNS SETOF public.financial_records
|
||||
LANGUAGE sql STABLE SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
SELECT *
|
||||
FROM public.financial_records
|
||||
WHERE owner_id = p_owner_id
|
||||
AND deleted_at IS NULL
|
||||
AND (p_type IS NULL OR type::TEXT = p_type)
|
||||
AND (p_status IS NULL OR status = p_status)
|
||||
AND (p_patient_id IS NULL OR patient_id = p_patient_id)
|
||||
AND (p_year IS NULL OR EXTRACT(YEAR FROM COALESCE(paid_at::DATE, due_date, created_at::DATE)) = p_year)
|
||||
AND (p_month IS NULL OR EXTRACT(MONTH FROM COALESCE(paid_at::DATE, due_date, created_at::DATE)) = p_month)
|
||||
ORDER BY COALESCE(paid_at, due_date::TIMESTAMPTZ, created_at) DESC
|
||||
LIMIT p_limit
|
||||
OFFSET p_offset;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.list_financial_records(p_owner_id uuid, p_year integer, p_month integer, p_type text, p_status text, p_patient_id uuid, p_limit integer, p_offset integer) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: mark_as_paid(uuid, text); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.mark_as_paid(p_financial_record_id uuid, p_payment_method text) RETURNS SETOF public.financial_records
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
DECLARE
|
||||
v_record public.financial_records%ROWTYPE;
|
||||
BEGIN
|
||||
-- Garante que o registro pertence ao usuário autenticado (RLS não aplica em SECURITY DEFINER)
|
||||
SELECT * INTO v_record
|
||||
FROM public.financial_records
|
||||
WHERE id = p_financial_record_id
|
||||
AND owner_id = auth.uid()
|
||||
AND deleted_at IS NULL;
|
||||
|
||||
IF NOT FOUND THEN
|
||||
RAISE EXCEPTION 'Registro financeiro não encontrado ou sem permissão.';
|
||||
END IF;
|
||||
|
||||
IF v_record.status NOT IN ('pending', 'overdue') THEN
|
||||
RAISE EXCEPTION 'Apenas cobranças pendentes ou vencidas podem ser marcadas como pagas.';
|
||||
END IF;
|
||||
|
||||
UPDATE public.financial_records
|
||||
SET status = 'paid',
|
||||
paid_at = NOW(),
|
||||
payment_method = p_payment_method,
|
||||
updated_at = NOW()
|
||||
WHERE id = p_financial_record_id
|
||||
RETURNING * INTO v_record;
|
||||
|
||||
RETURN NEXT v_record;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.mark_as_paid(p_financial_record_id uuid, p_payment_method text) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: mark_payout_as_paid(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.mark_payout_as_paid(p_payout_id uuid) RETURNS public.therapist_payouts
|
||||
CREATE FUNCTION public.mark_payout_as_paid(p_payout_id uuid) RETURNS public.therapist_payouts
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
DECLARE
|
||||
v_payout public.therapist_payouts%ROWTYPE;
|
||||
BEGIN
|
||||
-- Busca o payout
|
||||
SELECT * INTO v_payout
|
||||
FROM public.therapist_payouts
|
||||
WHERE id = p_payout_id;
|
||||
|
||||
IF NOT FOUND THEN
|
||||
RAISE EXCEPTION 'Repasse não encontrado: %', p_payout_id;
|
||||
END IF;
|
||||
|
||||
-- Verifica permissão: apenas tenant_admin do tenant do repasse
|
||||
IF NOT public.is_tenant_admin(v_payout.tenant_id) THEN
|
||||
RAISE EXCEPTION 'Apenas o administrador da clínica pode marcar repasses como pagos.';
|
||||
END IF;
|
||||
|
||||
-- Verifica status
|
||||
IF v_payout.status <> 'pending' THEN
|
||||
RAISE EXCEPTION
|
||||
'Repasse já está com status ''%''. Apenas repasses pendentes podem ser pagos.',
|
||||
v_payout.status;
|
||||
END IF;
|
||||
|
||||
-- Atualiza
|
||||
UPDATE public.therapist_payouts
|
||||
SET
|
||||
status = 'paid',
|
||||
paid_at = NOW(),
|
||||
updated_at = NOW()
|
||||
WHERE id = p_payout_id
|
||||
RETURNING * INTO v_payout;
|
||||
|
||||
RETURN v_payout;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.mark_payout_as_paid(p_payout_id uuid) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: FUNCTION mark_payout_as_paid(p_payout_id uuid); Type: COMMENT; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
COMMENT ON FUNCTION public.mark_payout_as_paid(p_payout_id uuid) IS 'Marca um repasse de terapeuta como pago. Apenas o tenant_admin pode chamar. Apenas repasses com status=pending podem ser finalizados.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: my_tenants(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.my_tenants() RETURNS TABLE(tenant_id uuid, role text, status text, kind text)
|
||||
CREATE FUNCTION public.seed_default_financial_categories(p_user_id uuid) RETURNS void
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
BEGIN
|
||||
INSERT INTO public.financial_categories (user_id, name, type, color, icon, sort_order)
|
||||
VALUES
|
||||
(p_user_id, 'Sessão', 'receita', '#22c55e', 'pi pi-heart', 1),
|
||||
(p_user_id, 'Supervisão', 'receita', '#6366f1', 'pi pi-users', 2),
|
||||
(p_user_id, 'Convênio', 'receita', '#3b82f6', 'pi pi-building', 3),
|
||||
(p_user_id, 'Grupo terapêutico', 'receita', '#f59e0b', 'pi pi-sitemap', 4),
|
||||
(p_user_id, 'Outro (receita)', 'receita', '#8b5cf6', 'pi pi-plus-circle', 5),
|
||||
(p_user_id, 'Aluguel sala', 'despesa', '#ef4444', 'pi pi-home', 1),
|
||||
(p_user_id, 'Plataforma/SaaS', 'despesa', '#f97316', 'pi pi-desktop', 2),
|
||||
(p_user_id, 'Repasse clínica', 'despesa', '#64748b', 'pi pi-arrow-right-arrow-left', 3),
|
||||
(p_user_id, 'Supervisão (custo)', 'despesa', '#6366f1', 'pi pi-users', 4),
|
||||
(p_user_id, 'Outro (despesa)', 'despesa', '#94a3b8', 'pi pi-minus-circle', 5)
|
||||
ON CONFLICT DO NOTHING;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.seed_default_financial_categories(p_user_id uuid) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: seed_determined_commitments(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.seed_determined_commitments(p_tenant_id uuid) RETURNS void
|
||||
CREATE FUNCTION public.set_insurance_plans_updated_at() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN NEW.updated_at = now(); RETURN NEW; END; $$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.set_insurance_plans_updated_at() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: set_owner_id(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.set_owner_id() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
begin
|
||||
if new.owner_id is null then
|
||||
new.owner_id := auth.uid();
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.set_owner_id() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: set_services_updated_at(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.set_services_updated_at() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = now();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.set_services_updated_at() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: set_tenant_feature_exception(uuid, text, boolean, text); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.set_tenant_feature_exception(p_tenant_id uuid, p_feature_key text, p_enabled boolean, p_reason text DEFAULT NULL::text) RETURNS void
|
||||
CREATE FUNCTION public.set_updated_at() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = now();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.set_updated_at() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: set_updated_at_recurrence(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.set_updated_at_recurrence() RETURNS trigger
|
||||
CREATE FUNCTION public.sync_overdue_financial_records() RETURNS integer
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
DECLARE
|
||||
v_count integer;
|
||||
BEGIN
|
||||
UPDATE public.financial_records
|
||||
SET
|
||||
status = 'overdue',
|
||||
updated_at = NOW()
|
||||
WHERE status = 'pending'
|
||||
AND due_date IS NOT NULL
|
||||
AND due_date < CURRENT_DATE
|
||||
AND deleted_at IS NULL;
|
||||
|
||||
GET DIAGNOSTICS v_count = ROW_COUNT;
|
||||
RETURN v_count;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.sync_overdue_financial_records() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: FUNCTION sync_overdue_financial_records(); Type: COMMENT; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
COMMENT ON FUNCTION public.sync_overdue_financial_records() IS 'Marca como overdue todos os financial_records pendentes com due_date vencido. Pode ser chamada manualmente, via pg_cron ou via Supabase Edge Function agendada.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: tenant_accept_invite(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.tenant_accept_invite(p_token uuid) RETURNS jsonb
|
||||
CREATE FUNCTION public.trg_fn_financial_records_auto_overdue() RETURNS trigger
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
BEGIN
|
||||
IF NEW.status = 'pending'
|
||||
AND NEW.due_date IS NOT NULL
|
||||
AND NEW.due_date < CURRENT_DATE
|
||||
THEN
|
||||
NEW.status := 'overdue';
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.trg_fn_financial_records_auto_overdue() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: unstick_notification_queue(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.unstick_notification_queue() RETURNS integer
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
AS $$
|
||||
DECLARE
|
||||
v_unstuck integer;
|
||||
BEGIN
|
||||
UPDATE public.notification_queue
|
||||
SET status = 'pendente',
|
||||
attempts = attempts + 1,
|
||||
last_error = 'Timeout: preso em processando por >10min',
|
||||
next_retry_at = now() + interval '2 minutes'
|
||||
WHERE status = 'processando'
|
||||
AND updated_at < now() - interval '10 minutes';
|
||||
|
||||
GET DIAGNOSTICS v_unstuck = ROW_COUNT;
|
||||
RETURN v_unstuck;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.unstick_notification_queue() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: update_payment_settings_updated_at(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.update_payment_settings_updated_at() RETURNS trigger
|
||||
CREATE FUNCTION public.update_payment_settings_updated_at() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = now();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.update_payment_settings_updated_at() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: update_professional_pricing_updated_at(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.update_professional_pricing_updated_at() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = now();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.update_professional_pricing_updated_at() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: user_has_feature(uuid, text); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.user_has_feature(_user_id uuid, _feature text) RETURNS boolean
|
||||
@@ -1,776 +0,0 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Functions — Compromissos, Suporte, SaaS
|
||||
-- =============================================================================
|
||||
-- seed_determined_commitments, delete_commitment_full,
|
||||
-- delete_determined_commitment, guard_locked_commitment,
|
||||
-- create_support_session, revoke_support_session, validate_support_session,
|
||||
-- saas_votar_doc, faq_votar, notice_track_click/view,
|
||||
-- sanitize_phone_br, create_clinic_tenant
|
||||
-- =============================================================================
|
||||
|
||||
CREATE FUNCTION public.create_clinic_tenant(p_name text) RETURNS uuid
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
AS $$
|
||||
declare
|
||||
v_uid uuid;
|
||||
v_tenant uuid;
|
||||
v_name text;
|
||||
begin
|
||||
v_uid := auth.uid();
|
||||
if v_uid is null then
|
||||
raise exception 'Not authenticated';
|
||||
end if;
|
||||
|
||||
v_name := nullif(trim(coalesce(p_name, '')), '');
|
||||
if v_name is null then
|
||||
v_name := 'Clínica';
|
||||
end if;
|
||||
|
||||
insert into public.tenants (name, kind, created_at)
|
||||
values (v_name, 'clinic', now())
|
||||
returning id into v_tenant;
|
||||
|
||||
insert into public.tenant_members (tenant_id, user_id, role, status, created_at)
|
||||
values (v_tenant, v_uid, 'tenant_admin', 'active', now());
|
||||
|
||||
return v_tenant;
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.create_clinic_tenant(p_name text) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: financial_records; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.financial_records (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
tenant_id uuid,
|
||||
type public.financial_record_type DEFAULT 'receita'::public.financial_record_type NOT NULL,
|
||||
amount numeric(10,2) NOT NULL,
|
||||
description text,
|
||||
category text,
|
||||
payment_method text,
|
||||
paid_at timestamp with time zone,
|
||||
due_date date,
|
||||
installments smallint DEFAULT 1,
|
||||
installment_number smallint DEFAULT 1,
|
||||
installment_group uuid,
|
||||
agenda_evento_id uuid,
|
||||
patient_id uuid,
|
||||
clinic_fee_pct numeric(5,2) DEFAULT 0,
|
||||
clinic_fee_amount numeric(10,2) DEFAULT 0,
|
||||
net_amount numeric(10,2) GENERATED ALWAYS AS ((amount - clinic_fee_amount)) STORED,
|
||||
insurance_plan_id uuid,
|
||||
notes text,
|
||||
tags text[],
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
deleted_at timestamp with time zone,
|
||||
discount_amount numeric(10,2) DEFAULT 0 NOT NULL,
|
||||
final_amount numeric(10,2) DEFAULT 0 NOT NULL,
|
||||
status text DEFAULT 'pending'::text NOT NULL,
|
||||
category_id uuid,
|
||||
CONSTRAINT financial_records_amount_check CHECK ((amount >= (0)::numeric)),
|
||||
CONSTRAINT financial_records_clinic_fee_amount_check CHECK ((clinic_fee_amount >= (0)::numeric)),
|
||||
CONSTRAINT financial_records_clinic_fee_pct_check CHECK (((clinic_fee_pct >= (0)::numeric) AND (clinic_fee_pct <= (100)::numeric))),
|
||||
CONSTRAINT financial_records_discount_amount_check CHECK ((discount_amount >= (0)::numeric)),
|
||||
CONSTRAINT financial_records_final_amount_check CHECK ((final_amount >= (0)::numeric)),
|
||||
CONSTRAINT financial_records_installments_check CHECK ((installments >= 1)),
|
||||
CONSTRAINT financial_records_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'paid'::text, 'partial'::text, 'overdue'::text, 'cancelled'::text, 'refunded'::text])))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.financial_records OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: create_financial_record_for_session(uuid, uuid, uuid, uuid, numeric, date); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.create_financial_record_for_session(p_tenant_id uuid, p_owner_id uuid, p_patient_id uuid, p_agenda_evento_id uuid, p_amount numeric, p_due_date date) RETURNS SETOF public.financial_records
|
||||
CREATE FUNCTION public.create_support_session(p_tenant_id uuid, p_ttl_minutes integer DEFAULT 60) RETURNS json
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
DECLARE
|
||||
v_admin_id uuid;
|
||||
v_role text;
|
||||
v_token text;
|
||||
v_expires timestamp with time zone;
|
||||
v_session support_sessions;
|
||||
BEGIN
|
||||
-- Verifica autenticação
|
||||
v_admin_id := auth.uid();
|
||||
IF v_admin_id IS NULL THEN
|
||||
RAISE EXCEPTION 'Não autenticado.' USING ERRCODE = 'P0001';
|
||||
END IF;
|
||||
|
||||
-- Verifica role saas_admin
|
||||
SELECT role INTO v_role
|
||||
FROM public.profiles
|
||||
WHERE id = v_admin_id;
|
||||
|
||||
IF v_role <> 'saas_admin' THEN
|
||||
RAISE EXCEPTION 'Acesso negado. Somente saas_admin pode criar sessões de suporte.'
|
||||
USING ERRCODE = 'P0002';
|
||||
END IF;
|
||||
|
||||
-- Valida TTL (1 a 120 minutos)
|
||||
IF p_ttl_minutes < 1 OR p_ttl_minutes > 120 THEN
|
||||
RAISE EXCEPTION 'TTL inválido. Use entre 1 e 120 minutos.'
|
||||
USING ERRCODE = 'P0003';
|
||||
END IF;
|
||||
|
||||
-- Valida tenant
|
||||
IF NOT EXISTS (SELECT 1 FROM public.tenants WHERE id = p_tenant_id) THEN
|
||||
RAISE EXCEPTION 'Tenant não encontrado.'
|
||||
USING ERRCODE = 'P0004';
|
||||
END IF;
|
||||
|
||||
-- Gera token único (64 chars hex, sem pgcrypto)
|
||||
v_token := replace(gen_random_uuid()::text, '-', '') || replace(gen_random_uuid()::text, '-', '');
|
||||
v_expires := now() + (p_ttl_minutes || ' minutes')::interval;
|
||||
|
||||
-- Insere sessão
|
||||
INSERT INTO public.support_sessions (tenant_id, admin_id, token, expires_at)
|
||||
VALUES (p_tenant_id, v_admin_id, v_token, v_expires)
|
||||
RETURNING * INTO v_session;
|
||||
|
||||
RETURN json_build_object(
|
||||
'token', v_session.token,
|
||||
'expires_at', v_session.expires_at,
|
||||
'session_id', v_session.id
|
||||
);
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.create_support_session(p_tenant_id uuid, p_ttl_minutes integer) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: therapist_payouts; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.therapist_payouts (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
period_start date NOT NULL,
|
||||
period_end date NOT NULL,
|
||||
total_sessions integer DEFAULT 0 NOT NULL,
|
||||
gross_amount numeric(10,2) DEFAULT 0 NOT NULL,
|
||||
clinic_fee_total numeric(10,2) DEFAULT 0 NOT NULL,
|
||||
net_amount numeric(10,2) DEFAULT 0 NOT NULL,
|
||||
status text DEFAULT 'pending'::text NOT NULL,
|
||||
paid_at timestamp with time zone,
|
||||
notes text,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT therapist_payouts_clinic_fee_total_check CHECK ((clinic_fee_total >= (0)::numeric)),
|
||||
CONSTRAINT therapist_payouts_gross_amount_check CHECK ((gross_amount >= (0)::numeric)),
|
||||
CONSTRAINT therapist_payouts_net_amount_check CHECK ((net_amount >= (0)::numeric)),
|
||||
CONSTRAINT therapist_payouts_period_chk CHECK ((period_end >= period_start)),
|
||||
CONSTRAINT therapist_payouts_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'paid'::text, 'cancelled'::text])))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.therapist_payouts OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: create_therapist_payout(uuid, uuid, date, date); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date) RETURNS public.therapist_payouts
|
||||
CREATE FUNCTION public.delete_commitment_full(p_tenant_id uuid, p_commitment_id uuid) RETURNS jsonb
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
declare
|
||||
v_is_native boolean;
|
||||
v_fields int := 0;
|
||||
v_logs int := 0;
|
||||
v_parent int := 0;
|
||||
begin
|
||||
if auth.uid() is null then
|
||||
raise exception 'Not authenticated';
|
||||
end if;
|
||||
|
||||
if not exists (
|
||||
select 1
|
||||
from public.tenant_members tm
|
||||
where tm.tenant_id = p_tenant_id
|
||||
and tm.user_id = auth.uid()
|
||||
and tm.status = 'active'
|
||||
) then
|
||||
raise exception 'Not allowed';
|
||||
end if;
|
||||
|
||||
select dc.is_native
|
||||
into v_is_native
|
||||
from public.determined_commitments dc
|
||||
where dc.tenant_id = p_tenant_id
|
||||
and dc.id = p_commitment_id;
|
||||
|
||||
if v_is_native is null then
|
||||
raise exception 'Commitment not found';
|
||||
end if;
|
||||
|
||||
if v_is_native = true then
|
||||
raise exception 'Cannot delete native commitment';
|
||||
end if;
|
||||
|
||||
delete from public.determined_commitment_fields
|
||||
where tenant_id = p_tenant_id
|
||||
and commitment_id = p_commitment_id;
|
||||
get diagnostics v_fields = row_count;
|
||||
|
||||
delete from public.commitment_time_logs
|
||||
where tenant_id = p_tenant_id
|
||||
and commitment_id = p_commitment_id;
|
||||
get diagnostics v_logs = row_count;
|
||||
|
||||
delete from public.determined_commitments
|
||||
where tenant_id = p_tenant_id
|
||||
and id = p_commitment_id;
|
||||
get diagnostics v_parent = row_count;
|
||||
|
||||
if v_parent <> 1 then
|
||||
raise exception 'Parent not deleted (RLS/owner issue).';
|
||||
end if;
|
||||
|
||||
return jsonb_build_object(
|
||||
'ok', true,
|
||||
'deleted', jsonb_build_object(
|
||||
'fields', v_fields,
|
||||
'logs', v_logs,
|
||||
'commitment', v_parent
|
||||
)
|
||||
);
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.delete_commitment_full(p_tenant_id uuid, p_commitment_id uuid) OWNER TO postgres;
|
||||
|
||||
--
|
||||
-- Name: delete_determined_commitment(uuid, uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.delete_determined_commitment(p_tenant_id uuid, p_commitment_id uuid) RETURNS jsonb
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
declare
|
||||
v_is_native boolean;
|
||||
v_fields_deleted int := 0;
|
||||
v_logs_deleted int := 0;
|
||||
v_commitment_deleted int := 0;
|
||||
begin
|
||||
if auth.uid() is null then
|
||||
raise exception 'Not authenticated';
|
||||
end if;
|
||||
|
||||
if not exists (
|
||||
select 1
|
||||
from public.tenant_members tm
|
||||
where tm.tenant_id = p_tenant_id
|
||||
and tm.user_id = auth.uid()
|
||||
and tm.status = 'active'
|
||||
) then
|
||||
raise exception 'Not allowed';
|
||||
end if;
|
||||
|
||||
select dc.is_native
|
||||
into v_is_native
|
||||
from public.determined_commitments dc
|
||||
where dc.tenant_id = p_tenant_id
|
||||
and dc.id = p_commitment_id;
|
||||
|
||||
if v_is_native is null then
|
||||
raise exception 'Commitment not found for tenant';
|
||||
end if;
|
||||
|
||||
if v_is_native = true then
|
||||
raise exception 'Cannot delete native commitment';
|
||||
end if;
|
||||
|
||||
delete from public.determined_commitment_fields f
|
||||
where f.tenant_id = p_tenant_id
|
||||
and f.commitment_id = p_commitment_id;
|
||||
get diagnostics v_fields_deleted = row_count;
|
||||
|
||||
delete from public.commitment_time_logs l
|
||||
where l.tenant_id = p_tenant_id
|
||||
and l.commitment_id = p_commitment_id;
|
||||
get diagnostics v_logs_deleted = row_count;
|
||||
|
||||
delete from public.determined_commitments dc
|
||||
where dc.tenant_id = p_tenant_id
|
||||
and dc.id = p_commitment_id;
|
||||
get diagnostics v_commitment_deleted = row_count;
|
||||
|
||||
if v_commitment_deleted <> 1 then
|
||||
raise exception 'Delete did not remove the commitment (tenant mismatch?)';
|
||||
end if;
|
||||
|
||||
return jsonb_build_object(
|
||||
'ok', true,
|
||||
'tenant_id', p_tenant_id,
|
||||
'commitment_id', p_commitment_id,
|
||||
'deleted', jsonb_build_object(
|
||||
'fields', v_fields_deleted,
|
||||
'logs', v_logs_deleted,
|
||||
'commitment', v_commitment_deleted
|
||||
)
|
||||
);
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.delete_determined_commitment(p_tenant_id uuid, p_commitment_id uuid) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: dev_list_auth_users(integer); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.dev_list_auth_users(p_limit integer DEFAULT 50) RETURNS TABLE(id uuid, email text, created_at timestamp with time zone)
|
||||
CREATE FUNCTION public.faq_votar(faq_id uuid) RETURNS void
|
||||
LANGUAGE sql SECURITY DEFINER
|
||||
AS $$
|
||||
update public.saas_faq
|
||||
set votos = votos + 1,
|
||||
updated_at = now()
|
||||
where id = faq_id
|
||||
and ativo = true;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.faq_votar(faq_id uuid) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: fix_all_subscription_mismatches(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.fix_all_subscription_mismatches() RETURNS void
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
AS $$
|
||||
declare
|
||||
r record;
|
||||
begin
|
||||
for r in
|
||||
select distinct s.user_id as owner_id
|
||||
from public.subscriptions s
|
||||
where s.status = 'active'
|
||||
and s.user_id is not null
|
||||
loop
|
||||
perform public.rebuild_owner_entitlements(r.owner_id);
|
||||
end loop;
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.fix_all_subscription_mismatches() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: fn_agenda_regras_semanais_no_overlap(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.fn_agenda_regras_semanais_no_overlap() RETURNS trigger
|
||||
CREATE FUNCTION public.guard_locked_commitment() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
begin
|
||||
if (old.is_locked = true) then
|
||||
if (tg_op = 'DELETE') then
|
||||
raise exception 'Compromisso bloqueado não pode ser excluído.';
|
||||
end if;
|
||||
|
||||
if (tg_op = 'UPDATE') then
|
||||
if (new.active = false) then
|
||||
raise exception 'Compromisso bloqueado não pode ser desativado.';
|
||||
end if;
|
||||
|
||||
-- trava renomear (mantém o "Sessão" sempre igual)
|
||||
if (new.name is distinct from old.name) then
|
||||
raise exception 'Compromisso bloqueado não pode ser renomeado.';
|
||||
end if;
|
||||
|
||||
-- se quiser travar descrição também, descomente:
|
||||
-- if (new.description is distinct from old.description) then
|
||||
-- raise exception 'Compromisso bloqueado não pode alterar descrição.';
|
||||
-- end if;
|
||||
end if;
|
||||
end if;
|
||||
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.guard_locked_commitment() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: guard_no_change_core_plan_key(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.guard_no_change_core_plan_key() RETURNS trigger
|
||||
CREATE FUNCTION public.notice_track_click(p_notice_id uuid) RETURNS void
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
AS $$
|
||||
begin
|
||||
update public.global_notices
|
||||
set clicks_count = clicks_count + 1
|
||||
where id = p_notice_id;
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.notice_track_click(p_notice_id uuid) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: notice_track_view(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.notice_track_view(p_notice_id uuid) RETURNS void
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
AS $$
|
||||
begin
|
||||
update public.global_notices
|
||||
set views_count = views_count + 1
|
||||
where id = p_notice_id;
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.notice_track_view(p_notice_id uuid) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: notify_on_intake(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.notify_on_intake() RETURNS trigger
|
||||
CREATE FUNCTION public.revoke_support_session(p_token text) RETURNS boolean
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
DECLARE
|
||||
v_admin_id uuid;
|
||||
v_role text;
|
||||
BEGIN
|
||||
v_admin_id := auth.uid();
|
||||
IF v_admin_id IS NULL THEN
|
||||
RAISE EXCEPTION 'Não autenticado.' USING ERRCODE = 'P0001';
|
||||
END IF;
|
||||
|
||||
SELECT role INTO v_role FROM public.profiles WHERE id = v_admin_id;
|
||||
IF v_role <> 'saas_admin' THEN
|
||||
RAISE EXCEPTION 'Acesso negado.' USING ERRCODE = 'P0002';
|
||||
END IF;
|
||||
|
||||
DELETE FROM public.support_sessions
|
||||
WHERE token = p_token
|
||||
AND admin_id = v_admin_id;
|
||||
|
||||
RETURN FOUND;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.revoke_support_session(p_token text) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: rotate_patient_invite_token(text); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.rotate_patient_invite_token(p_new_token text) RETURNS uuid
|
||||
CREATE FUNCTION public.saas_votar_doc(p_doc_id uuid, p_util boolean) RETURNS jsonb
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
AS $$
|
||||
declare
|
||||
v_uid uuid := auth.uid();
|
||||
v_voto_antigo boolean;
|
||||
begin
|
||||
if v_uid is null then
|
||||
raise exception 'Não autenticado';
|
||||
end if;
|
||||
|
||||
-- Verifica se já votou
|
||||
select util into v_voto_antigo
|
||||
from public.saas_doc_votos
|
||||
where doc_id = p_doc_id and user_id = v_uid;
|
||||
|
||||
if found then
|
||||
-- Já votou igual → cancela o voto (toggle)
|
||||
if v_voto_antigo = p_util then
|
||||
delete from public.saas_doc_votos
|
||||
where doc_id = p_doc_id and user_id = v_uid;
|
||||
|
||||
update public.saas_docs set
|
||||
votos_util = greatest(0, votos_util - (case when p_util then 1 else 0 end)),
|
||||
votos_nao_util = greatest(0, votos_nao_util - (case when not p_util then 1 else 0 end)),
|
||||
updated_at = now()
|
||||
where id = p_doc_id;
|
||||
|
||||
return jsonb_build_object('acao', 'removido', 'util', null);
|
||||
else
|
||||
-- Mudou de voto
|
||||
update public.saas_doc_votos set util = p_util, updated_at = now()
|
||||
where doc_id = p_doc_id and user_id = v_uid;
|
||||
|
||||
update public.saas_docs set
|
||||
votos_util = greatest(0, votos_util + (case when p_util then 1 else -1 end)),
|
||||
votos_nao_util = greatest(0, votos_nao_util + (case when not p_util then 1 else -1 end)),
|
||||
updated_at = now()
|
||||
where id = p_doc_id;
|
||||
|
||||
return jsonb_build_object('acao', 'atualizado', 'util', p_util);
|
||||
end if;
|
||||
else
|
||||
-- Primeiro voto
|
||||
insert into public.saas_doc_votos (doc_id, user_id, util)
|
||||
values (p_doc_id, v_uid, p_util);
|
||||
|
||||
update public.saas_docs set
|
||||
votos_util = votos_util + (case when p_util then 1 else 0 end),
|
||||
votos_nao_util = votos_nao_util + (case when not p_util then 1 else 0 end),
|
||||
updated_at = now()
|
||||
where id = p_doc_id;
|
||||
|
||||
return jsonb_build_object('acao', 'registrado', 'util', p_util);
|
||||
end if;
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.saas_votar_doc(p_doc_id uuid, p_util boolean) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: safe_delete_patient(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.safe_delete_patient(p_patient_id uuid) RETURNS jsonb
|
||||
CREATE FUNCTION public.sanitize_phone_br(raw_phone text) RETURNS text
|
||||
LANGUAGE plpgsql IMMUTABLE
|
||||
AS $$ DECLARE digits text;
|
||||
BEGIN
|
||||
digits := regexp_replace(COALESCE(raw_phone, ''), '[^0-9]', '', 'g');
|
||||
IF digits = '' THEN RETURN ''; END IF;
|
||||
IF length(digits) = 10 OR length(digits) = 11 THEN
|
||||
digits := '55' || digits;
|
||||
END IF;
|
||||
RETURN digits;
|
||||
END; $$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.sanitize_phone_br(raw_phone text) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: seed_default_financial_categories(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.seed_default_financial_categories(p_user_id uuid) RETURNS void
|
||||
CREATE FUNCTION public.seed_determined_commitments(p_tenant_id uuid) RETURNS void
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
AS $$
|
||||
declare
|
||||
v_id uuid;
|
||||
begin
|
||||
-- Sessão (locked + sempre ativa)
|
||||
if not exists (
|
||||
select 1 from public.determined_commitments
|
||||
where tenant_id = p_tenant_id and is_native = true and native_key = 'session'
|
||||
) then
|
||||
insert into public.determined_commitments
|
||||
(tenant_id, is_native, native_key, is_locked, active, name, description)
|
||||
values
|
||||
(p_tenant_id, true, 'session', true, true, 'Sessão', 'Sessão com paciente');
|
||||
end if;
|
||||
|
||||
-- Leitura
|
||||
if not exists (
|
||||
select 1 from public.determined_commitments
|
||||
where tenant_id = p_tenant_id and is_native = true and native_key = 'reading'
|
||||
) then
|
||||
insert into public.determined_commitments
|
||||
(tenant_id, is_native, native_key, is_locked, active, name, description)
|
||||
values
|
||||
(p_tenant_id, true, 'reading', false, true, 'Leitura', 'Praticar leitura');
|
||||
end if;
|
||||
|
||||
-- Supervisão
|
||||
if not exists (
|
||||
select 1 from public.determined_commitments
|
||||
where tenant_id = p_tenant_id and is_native = true and native_key = 'supervision'
|
||||
) then
|
||||
insert into public.determined_commitments
|
||||
(tenant_id, is_native, native_key, is_locked, active, name, description)
|
||||
values
|
||||
(p_tenant_id, true, 'supervision', false, true, 'Supervisão', 'Supervisão');
|
||||
end if;
|
||||
|
||||
-- Aula ✅ (corrigido)
|
||||
if not exists (
|
||||
select 1 from public.determined_commitments
|
||||
where tenant_id = p_tenant_id and is_native = true and native_key = 'class'
|
||||
) then
|
||||
insert into public.determined_commitments
|
||||
(tenant_id, is_native, native_key, is_locked, active, name, description)
|
||||
values
|
||||
(p_tenant_id, true, 'class', false, false, 'Aula', 'Dar aula');
|
||||
end if;
|
||||
|
||||
-- Análise pessoal
|
||||
if not exists (
|
||||
select 1 from public.determined_commitments
|
||||
where tenant_id = p_tenant_id and is_native = true and native_key = 'analysis'
|
||||
) then
|
||||
insert into public.determined_commitments
|
||||
(tenant_id, is_native, native_key, is_locked, active, name, description)
|
||||
values
|
||||
(p_tenant_id, true, 'analysis', false, true, 'Análise Pessoal', 'Minha análise pessoal');
|
||||
end if;
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Campos padrão (idempotentes por (commitment_id, key))
|
||||
-- -------------------------------------------------------
|
||||
|
||||
-- Leitura
|
||||
select id into v_id
|
||||
from public.determined_commitments
|
||||
where tenant_id = p_tenant_id and is_native = true and native_key = 'reading'
|
||||
limit 1;
|
||||
|
||||
if v_id is not null then
|
||||
if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'book') then
|
||||
insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order)
|
||||
values (p_tenant_id, v_id, 'book', 'Livro', 'text', false, 10);
|
||||
end if;
|
||||
|
||||
if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'author') then
|
||||
insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order)
|
||||
values (p_tenant_id, v_id, 'author', 'Autor', 'text', false, 20);
|
||||
end if;
|
||||
|
||||
if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'notes') then
|
||||
insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order)
|
||||
values (p_tenant_id, v_id, 'notes', 'Observação', 'textarea', false, 30);
|
||||
end if;
|
||||
end if;
|
||||
|
||||
-- Supervisão
|
||||
select id into v_id
|
||||
from public.determined_commitments
|
||||
where tenant_id = p_tenant_id and is_native = true and native_key = 'supervision'
|
||||
limit 1;
|
||||
|
||||
if v_id is not null then
|
||||
if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'supervisor') then
|
||||
insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order)
|
||||
values (p_tenant_id, v_id, 'supervisor', 'Supervisor', 'text', false, 10);
|
||||
end if;
|
||||
|
||||
if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'topic') then
|
||||
insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order)
|
||||
values (p_tenant_id, v_id, 'topic', 'Assunto', 'text', false, 20);
|
||||
end if;
|
||||
|
||||
if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'notes') then
|
||||
insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order)
|
||||
values (p_tenant_id, v_id, 'notes', 'Observação', 'textarea', false, 30);
|
||||
end if;
|
||||
end if;
|
||||
|
||||
-- Aula
|
||||
select id into v_id
|
||||
from public.determined_commitments
|
||||
where tenant_id = p_tenant_id and is_native = true and native_key = 'class'
|
||||
limit 1;
|
||||
|
||||
if v_id is not null then
|
||||
if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'theme') then
|
||||
insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order)
|
||||
values (p_tenant_id, v_id, 'theme', 'Tema', 'text', false, 10);
|
||||
end if;
|
||||
|
||||
if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'group') then
|
||||
insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order)
|
||||
values (p_tenant_id, v_id, 'group', 'Turma', 'text', false, 20);
|
||||
end if;
|
||||
|
||||
if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'notes') then
|
||||
insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order)
|
||||
values (p_tenant_id, v_id, 'notes', 'Observação', 'textarea', false, 30);
|
||||
end if;
|
||||
end if;
|
||||
|
||||
-- Análise
|
||||
select id into v_id
|
||||
from public.determined_commitments
|
||||
where tenant_id = p_tenant_id and is_native = true and native_key = 'analysis'
|
||||
limit 1;
|
||||
|
||||
if v_id is not null then
|
||||
if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'analyst') then
|
||||
insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order)
|
||||
values (p_tenant_id, v_id, 'analyst', 'Analista', 'text', false, 10);
|
||||
end if;
|
||||
|
||||
if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'focus') then
|
||||
insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order)
|
||||
values (p_tenant_id, v_id, 'focus', 'Foco', 'text', false, 20);
|
||||
end if;
|
||||
|
||||
if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'notes') then
|
||||
insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order)
|
||||
values (p_tenant_id, v_id, 'notes', 'Observação', 'textarea', false, 30);
|
||||
end if;
|
||||
end if;
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.seed_determined_commitments(p_tenant_id uuid) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: set_insurance_plans_updated_at(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.set_insurance_plans_updated_at() RETURNS trigger
|
||||
CREATE FUNCTION public.validate_support_session(p_token text) RETURNS json
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
DECLARE
|
||||
v_session support_sessions;
|
||||
BEGIN
|
||||
IF p_token IS NULL OR length(trim(p_token)) < 32 THEN
|
||||
RETURN json_build_object('valid', false, 'tenant_id', null);
|
||||
END IF;
|
||||
|
||||
SELECT * INTO v_session
|
||||
FROM public.support_sessions
|
||||
WHERE token = p_token
|
||||
AND expires_at > now()
|
||||
LIMIT 1;
|
||||
|
||||
IF NOT FOUND THEN
|
||||
RETURN json_build_object('valid', false, 'tenant_id', null);
|
||||
END IF;
|
||||
|
||||
RETURN json_build_object(
|
||||
'valid', true,
|
||||
'tenant_id', v_session.tenant_id
|
||||
);
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.validate_support_session(p_token text) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: whoami(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.whoami() RETURNS TABLE(uid uuid, role text)
|
||||
@@ -1,404 +0,0 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Functions — Notificações
|
||||
-- =============================================================================
|
||||
-- cancel_notifications_on_opt_out, cancel_notifications_on_session_cancel,
|
||||
-- cancel_patient_pending_notifications, cleanup_notification_queue,
|
||||
-- notify_on_intake, notify_on_scheduling, notify_on_session_status,
|
||||
-- populate_notification_queue, unstick_notification_queue
|
||||
-- =============================================================================
|
||||
|
||||
CREATE FUNCTION public.cancel_notifications_on_opt_out() RETURNS trigger
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
AS $$
|
||||
BEGIN
|
||||
-- WhatsApp opt-out
|
||||
IF OLD.whatsapp_opt_in = true AND NEW.whatsapp_opt_in = false THEN
|
||||
PERFORM public.cancel_patient_pending_notifications(
|
||||
NEW.patient_id, 'whatsapp'
|
||||
);
|
||||
END IF;
|
||||
-- Email opt-out
|
||||
IF OLD.email_opt_in = true AND NEW.email_opt_in = false THEN
|
||||
PERFORM public.cancel_patient_pending_notifications(
|
||||
NEW.patient_id, 'email'
|
||||
);
|
||||
END IF;
|
||||
-- SMS opt-out
|
||||
IF OLD.sms_opt_in = true AND NEW.sms_opt_in = false THEN
|
||||
PERFORM public.cancel_patient_pending_notifications(
|
||||
NEW.patient_id, 'sms'
|
||||
);
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.cancel_notifications_on_opt_out() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: cancel_notifications_on_session_cancel(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.cancel_notifications_on_session_cancel() RETURNS trigger
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
AS $$
|
||||
BEGIN
|
||||
IF NEW.status IN ('cancelado', 'excluido')
|
||||
AND OLD.status NOT IN ('cancelado', 'excluido')
|
||||
THEN
|
||||
PERFORM public.cancel_patient_pending_notifications(
|
||||
NEW.patient_id, NULL, NEW.id
|
||||
);
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.cancel_notifications_on_session_cancel() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: cancel_patient_pending_notifications(uuid, text, uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.cancel_patient_pending_notifications(p_patient_id uuid, p_channel text DEFAULT NULL::text, p_evento_id uuid DEFAULT NULL::uuid) RETURNS integer
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
AS $$
|
||||
DECLARE
|
||||
v_canceled integer;
|
||||
BEGIN
|
||||
UPDATE public.notification_queue
|
||||
SET status = 'cancelado',
|
||||
updated_at = now()
|
||||
WHERE patient_id = p_patient_id
|
||||
AND status IN ('pendente', 'processando')
|
||||
AND (p_channel IS NULL OR channel = p_channel)
|
||||
AND (p_evento_id IS NULL OR agenda_evento_id = p_evento_id);
|
||||
|
||||
GET DIAGNOSTICS v_canceled = ROW_COUNT;
|
||||
RETURN v_canceled;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.cancel_patient_pending_notifications(p_patient_id uuid, p_channel text, p_evento_id uuid) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: cancel_recurrence_from(uuid, date); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.cancel_recurrence_from(p_recurrence_id uuid, p_from_date date) RETURNS void
|
||||
CREATE FUNCTION public.cancel_patient_pending_notifications(p_patient_id uuid, p_channel text DEFAULT NULL::text, p_evento_id uuid DEFAULT NULL::uuid) RETURNS integer
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
AS $$
|
||||
DECLARE
|
||||
v_canceled integer;
|
||||
BEGIN
|
||||
UPDATE public.notification_queue
|
||||
SET status = 'cancelado',
|
||||
updated_at = now()
|
||||
WHERE patient_id = p_patient_id
|
||||
AND status IN ('pendente', 'processando')
|
||||
AND (p_channel IS NULL OR channel = p_channel)
|
||||
AND (p_evento_id IS NULL OR agenda_evento_id = p_evento_id);
|
||||
|
||||
GET DIAGNOSTICS v_canceled = ROW_COUNT;
|
||||
RETURN v_canceled;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.cancel_patient_pending_notifications(p_patient_id uuid, p_channel text, p_evento_id uuid) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: cancel_recurrence_from(uuid, date); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.cancel_recurrence_from(p_recurrence_id uuid, p_from_date date) RETURNS void
|
||||
CREATE FUNCTION public.cleanup_notification_queue() RETURNS integer
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
AS $$
|
||||
DECLARE
|
||||
v_deleted integer;
|
||||
BEGIN
|
||||
DELETE FROM public.notification_queue
|
||||
WHERE status IN ('enviado', 'cancelado', 'ignorado')
|
||||
AND created_at < now() - interval '90 days';
|
||||
|
||||
GET DIAGNOSTICS v_deleted = ROW_COUNT;
|
||||
RETURN v_deleted;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.cleanup_notification_queue() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: create_clinic_tenant(text); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.create_clinic_tenant(p_name text) RETURNS uuid
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
AS $$
|
||||
declare
|
||||
v_uid uuid;
|
||||
v_tenant uuid;
|
||||
v_name text;
|
||||
begin
|
||||
v_uid := auth.uid();
|
||||
if v_uid is null then
|
||||
raise exception 'Not authenticated';
|
||||
end if;
|
||||
|
||||
v_name := nullif(trim(coalesce(p_name, '')), '');
|
||||
if v_name is null then
|
||||
v_name := 'Clínica';
|
||||
end if;
|
||||
|
||||
insert into public.tenants (name, kind, created_at)
|
||||
values (v_name, 'clinic', now())
|
||||
returning id into v_tenant;
|
||||
|
||||
insert into public.tenant_members (tenant_id, user_id, role, status, created_at)
|
||||
values (v_tenant, v_uid, 'tenant_admin', 'active', now());
|
||||
|
||||
return v_tenant;
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.create_clinic_tenant(p_name text) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: financial_records; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.financial_records (
|
||||
CREATE FUNCTION public.notify_on_intake() RETURNS trigger
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
AS $$
|
||||
BEGIN
|
||||
IF NEW.status = 'new' THEN
|
||||
INSERT INTO public.notifications (
|
||||
owner_id,
|
||||
tenant_id,
|
||||
type,
|
||||
ref_id,
|
||||
ref_table,
|
||||
payload
|
||||
)
|
||||
VALUES (
|
||||
NEW.owner_id,
|
||||
NEW.tenant_id,
|
||||
'new_patient',
|
||||
NEW.id,
|
||||
'patient_intake_requests',
|
||||
jsonb_build_object(
|
||||
'title', 'Novo cadastro externo',
|
||||
'detail', COALESCE(NEW.nome_completo, 'Paciente'),
|
||||
'deeplink', '/therapist/patients/cadastro/recebidos',
|
||||
'avatar_initials', upper(left(COALESCE(NEW.nome_completo, '?'), 2))
|
||||
)
|
||||
);
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.notify_on_intake() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: notify_on_scheduling(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.notify_on_scheduling() RETURNS trigger
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
AS $$ BEGIN IF NEW.status = 'pendente' THEN
|
||||
INSERT INTO public.notifications ( owner_id, tenant_id, type, ref_id, ref_table, payload ) VALUES (
|
||||
NEW.owner_id, NEW.tenant_id,
|
||||
'new_scheduling', NEW.id, 'agendador_solicitacoes', jsonb_build_object( 'title', 'Nova solicitação de agendamento', 'detail', COALESCE(NEW.paciente_nome, 'Paciente') || ' ' || COALESCE(NEW.paciente_sobrenome, '') || ' — ' || COALESCE(NEW.tipo, ''), 'deeplink', '/therapist/agendamentos-recebidos', 'avatar_initials', upper(left(COALESCE(NEW.paciente_nome, '?'), 1) || left(COALESCE(NEW.paciente_sobrenome, ''), 1)) ) ); END IF; RETURN NEW; END; $$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.notify_on_scheduling() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: notify_on_session_status(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.notify_on_session_status() RETURNS trigger
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
AS $$
|
||||
DECLARE
|
||||
v_nome text;
|
||||
BEGIN
|
||||
IF NEW.status IN ('faltou', 'cancelado') AND OLD.status IS DISTINCT FROM NEW.status THEN
|
||||
|
||||
SELECT nome_completo
|
||||
INTO v_nome
|
||||
FROM public.patients
|
||||
WHERE id = NEW.patient_id
|
||||
LIMIT 1;
|
||||
|
||||
INSERT INTO public.notifications (
|
||||
owner_id,
|
||||
tenant_id,
|
||||
type,
|
||||
ref_id,
|
||||
ref_table,
|
||||
payload
|
||||
)
|
||||
VALUES (
|
||||
NEW.owner_id,
|
||||
NEW.tenant_id,
|
||||
'session_status',
|
||||
NEW.id,
|
||||
'agenda_eventos',
|
||||
jsonb_build_object(
|
||||
'title', CASE WHEN NEW.status = 'faltou' THEN 'Paciente faltou' ELSE 'Sessão cancelada' END,
|
||||
'detail', COALESCE(v_nome, 'Paciente') || ' — ' || to_char(NEW.inicio_em, 'DD/MM HH24:MI'),
|
||||
'deeplink', '/therapist/agenda',
|
||||
'avatar_initials', upper(left(COALESCE(v_nome, '?'), 2))
|
||||
)
|
||||
);
|
||||
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.notify_on_session_status() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: on_new_user_seed_patient_groups(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.on_new_user_seed_patient_groups() RETURNS trigger
|
||||
CREATE FUNCTION public.populate_notification_queue() RETURNS void
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
AS $$
|
||||
BEGIN
|
||||
INSERT INTO public.notification_queue (
|
||||
tenant_id, owner_id, agenda_evento_id, patient_id,
|
||||
channel, template_key, schedule_key,
|
||||
resolved_vars, recipient_address,
|
||||
scheduled_at, idempotency_key
|
||||
)
|
||||
SELECT
|
||||
ae.tenant_id,
|
||||
ae.owner_id,
|
||||
ae.id AS agenda_evento_id,
|
||||
ae.patient_id,
|
||||
ch.channel,
|
||||
'session.' || REPLACE(ns.event_type, '_sessao', '') || '.' || ch.channel,
|
||||
ns.schedule_key,
|
||||
jsonb_build_object(
|
||||
'nome_paciente', COALESCE(p.nome_completo, 'Paciente'),
|
||||
'data_sessao', TO_CHAR(ae.inicio_em AT TIME ZONE 'America/Sao_Paulo', 'DD/MM/YYYY'),
|
||||
'hora_sessao', TO_CHAR(ae.inicio_em AT TIME ZONE 'America/Sao_Paulo', 'HH24:MI'),
|
||||
'nome_terapeuta', COALESCE(prof.full_name, 'Terapeuta'),
|
||||
'modalidade', COALESCE(ae.modalidade, 'Presencial'),
|
||||
'titulo', COALESCE(ae.titulo, 'Sessão')
|
||||
),
|
||||
CASE ch.channel
|
||||
WHEN 'whatsapp' THEN COALESCE(p.telefone, '')
|
||||
WHEN 'sms' THEN COALESCE(p.telefone, '')
|
||||
WHEN 'email' THEN COALESCE(p.email_principal, '')
|
||||
END,
|
||||
CASE
|
||||
WHEN (ae.inicio_em - (ns.offset_minutes || ' minutes')::interval)::time
|
||||
< ns.allowed_time_start
|
||||
THEN DATE_TRUNC('day', ae.inicio_em - (ns.offset_minutes || ' minutes')::interval)
|
||||
+ ns.allowed_time_start
|
||||
WHEN (ae.inicio_em - (ns.offset_minutes || ' minutes')::interval)::time
|
||||
> ns.allowed_time_end
|
||||
THEN DATE_TRUNC('day', ae.inicio_em - (ns.offset_minutes || ' minutes')::interval)
|
||||
+ ns.allowed_time_start
|
||||
ELSE ae.inicio_em - (ns.offset_minutes || ' minutes')::interval
|
||||
END,
|
||||
ae.id::text || ':' || ns.schedule_key || ':' || ch.channel || ':'
|
||||
|| ae.inicio_em::date::text
|
||||
FROM public.agenda_eventos ae
|
||||
JOIN public.patients p ON p.id = ae.patient_id
|
||||
LEFT JOIN public.profiles prof ON prof.id = ae.owner_id
|
||||
JOIN public.notification_schedules ns
|
||||
ON ns.owner_id = ae.owner_id
|
||||
AND ns.is_active = true
|
||||
AND ns.deleted_at IS NULL
|
||||
AND ns.trigger_type = 'before_event'
|
||||
AND ns.event_type = 'lembrete_sessao'
|
||||
JOIN public.notification_channels nc
|
||||
ON nc.owner_id = ae.owner_id
|
||||
AND nc.is_active = true
|
||||
AND nc.deleted_at IS NULL
|
||||
CROSS JOIN LATERAL (
|
||||
SELECT 'whatsapp' AS channel WHERE ns.whatsapp_enabled AND nc.channel = 'whatsapp'
|
||||
UNION ALL
|
||||
SELECT 'email' AS channel WHERE ns.email_enabled AND nc.channel = 'email'
|
||||
UNION ALL
|
||||
SELECT 'sms' AS channel WHERE ns.sms_enabled AND nc.channel = 'sms'
|
||||
) ch
|
||||
LEFT JOIN public.notification_preferences np
|
||||
ON np.patient_id = ae.patient_id
|
||||
AND np.owner_id = ae.owner_id
|
||||
AND np.deleted_at IS NULL
|
||||
WHERE
|
||||
ae.inicio_em > now()
|
||||
AND ae.inicio_em <= now() + interval '48 hours'
|
||||
AND ae.status NOT IN ('cancelado', 'faltou')
|
||||
AND CASE ch.channel
|
||||
WHEN 'whatsapp' THEN COALESCE(p.telefone, '') != ''
|
||||
WHEN 'sms' THEN COALESCE(p.telefone, '') != ''
|
||||
WHEN 'email' THEN COALESCE(p.email_principal, '') != ''
|
||||
END
|
||||
AND CASE ch.channel
|
||||
WHEN 'whatsapp' THEN COALESCE(np.whatsapp_opt_in, true)
|
||||
WHEN 'email' THEN COALESCE(np.email_opt_in, true)
|
||||
WHEN 'sms' THEN COALESCE(np.sms_opt_in, false)
|
||||
END
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM public.profiles tp
|
||||
WHERE tp.id = ae.owner_id
|
||||
AND COALESCE(tp.notify_reminders, true) = true
|
||||
)
|
||||
ON CONFLICT (idempotency_key) DO NOTHING;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.populate_notification_queue() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: prevent_promoting_to_system(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.prevent_promoting_to_system() RETURNS trigger
|
||||
CREATE FUNCTION public.unstick_notification_queue() RETURNS integer
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
AS $$
|
||||
DECLARE
|
||||
v_unstuck integer;
|
||||
BEGIN
|
||||
UPDATE public.notification_queue
|
||||
SET status = 'pendente',
|
||||
attempts = attempts + 1,
|
||||
last_error = 'Timeout: preso em processando por >10min',
|
||||
next_retry_at = now() + interval '2 minutes'
|
||||
WHERE status = 'processando'
|
||||
AND updated_at < now() - interval '10 minutes';
|
||||
|
||||
GET DIAGNOSTICS v_unstuck = ROW_COUNT;
|
||||
RETURN v_unstuck;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.unstick_notification_queue() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: update_payment_settings_updated_at(); Type: FUNCTION; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.update_payment_settings_updated_at() RETURNS trigger
|
||||
@@ -1,433 +0,0 @@
|
||||
-- =============================================================================
|
||||
-- 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
|
||||
@@ -0,0 +1,22 @@
|
||||
-- Functions: pgbouncer
|
||||
-- Gerado automaticamente em 2026-04-17T12:23:05.222Z
|
||||
-- Total: 1
|
||||
|
||||
CREATE FUNCTION pgbouncer.get_auth(p_usename text) RETURNS TABLE(username text, password text)
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
SET search_path TO ''
|
||||
AS $_$
|
||||
begin
|
||||
raise debug 'PgBouncer auth request: %', p_usename;
|
||||
|
||||
return query
|
||||
select
|
||||
rolname::text,
|
||||
case when rolvaliduntil < now()
|
||||
then null
|
||||
else rolpassword::text
|
||||
end
|
||||
from pg_authid
|
||||
where rolname=$1 and rolcanlogin;
|
||||
end;
|
||||
$_$;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Functions — realtime schema
|
||||
-- =============================================================================
|
||||
-- Functions: realtime
|
||||
-- Gerado automaticamente em 2026-04-17T12:23:05.223Z
|
||||
-- Total: 12
|
||||
|
||||
CREATE FUNCTION realtime.apply_rls(wal jsonb, max_record_bytes integer DEFAULT (1024 * 1024)) RETURNS SETOF realtime.wal_rls
|
||||
LANGUAGE plpgsql
|
||||
@@ -302,13 +302,6 @@ perform set_config('role', null, true);
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION realtime.apply_rls(wal jsonb, max_record_bytes integer) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: broadcast_changes(text, text, text, text, text, record, record, text); Type: FUNCTION; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION realtime.broadcast_changes(topic_name text, event_name text, operation text, table_name text, table_schema text, new record, old record, level text DEFAULT 'ROW'::text) RETURNS void
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
@@ -333,13 +326,6 @@ END;
|
||||
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION realtime.broadcast_changes(topic_name text, event_name text, operation text, table_name text, table_schema text, new record, old record, level text) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: build_prepared_statement_sql(text, regclass, realtime.wal_column[]); Type: FUNCTION; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION realtime.build_prepared_statement_sql(prepared_statement_name text, entity regclass, columns realtime.wal_column[]) RETURNS text
|
||||
LANGUAGE sql
|
||||
AS $$
|
||||
@@ -368,13 +354,6 @@ CREATE FUNCTION realtime.build_prepared_statement_sql(prepared_statement_name te
|
||||
entity
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION realtime.build_prepared_statement_sql(prepared_statement_name text, entity regclass, columns realtime.wal_column[]) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: cast(text, regtype); Type: FUNCTION; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION realtime."cast"(val text, type_ regtype) RETURNS jsonb
|
||||
LANGUAGE plpgsql IMMUTABLE
|
||||
AS $$
|
||||
@@ -386,13 +365,6 @@ CREATE FUNCTION realtime."cast"(val text, type_ regtype) RETURNS jsonb
|
||||
end
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION realtime."cast"(val text, type_ regtype) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: check_equality_op(realtime.equality_op, regtype, text, text); Type: FUNCTION; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION realtime.check_equality_op(op realtime.equality_op, type_ regtype, val_1 text, val_2 text) RETURNS boolean
|
||||
LANGUAGE plpgsql IMMUTABLE
|
||||
AS $$
|
||||
@@ -427,13 +399,6 @@ CREATE FUNCTION realtime.check_equality_op(op realtime.equality_op, type_ regtyp
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION realtime.check_equality_op(op realtime.equality_op, type_ regtype, val_1 text, val_2 text) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: is_visible_through_filters(realtime.wal_column[], realtime.user_defined_filter[]); Type: FUNCTION; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION realtime.is_visible_through_filters(columns realtime.wal_column[], filters realtime.user_defined_filter[]) RETURNS boolean
|
||||
LANGUAGE sql IMMUTABLE
|
||||
AS $_$
|
||||
@@ -465,13 +430,6 @@ CREATE FUNCTION realtime.is_visible_through_filters(columns realtime.wal_column[
|
||||
on f.column_name = col.name;
|
||||
$_$;
|
||||
|
||||
|
||||
ALTER FUNCTION realtime.is_visible_through_filters(columns realtime.wal_column[], filters realtime.user_defined_filter[]) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: list_changes(name, name, integer, integer); Type: FUNCTION; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION realtime.list_changes(publication name, slot_name name, max_changes integer, max_record_bytes integer) RETURNS SETOF realtime.wal_rls
|
||||
LANGUAGE sql
|
||||
SET log_min_messages TO 'fatal'
|
||||
@@ -533,13 +491,6 @@ CREATE FUNCTION realtime.list_changes(publication name, slot_name name, max_chan
|
||||
and xyz.subscription_ids[1] is not null
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION realtime.list_changes(publication name, slot_name name, max_changes integer, max_record_bytes integer) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: quote_wal2json(regclass); Type: FUNCTION; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION realtime.quote_wal2json(entity regclass) RETURNS text
|
||||
LANGUAGE sql IMMUTABLE STRICT
|
||||
AS $$
|
||||
@@ -573,13 +524,6 @@ CREATE FUNCTION realtime.quote_wal2json(entity regclass) RETURNS text
|
||||
pc.oid = entity
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION realtime.quote_wal2json(entity regclass) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: send(jsonb, text, text, boolean); Type: FUNCTION; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION realtime.send(payload jsonb, event text, topic text, private boolean DEFAULT true) RETURNS void
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
@@ -612,13 +556,6 @@ BEGIN
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION realtime.send(payload jsonb, event text, topic text, private boolean) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: subscription_check_filters(); Type: FUNCTION; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION realtime.subscription_check_filters() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
@@ -688,34 +625,12 @@ CREATE FUNCTION realtime.subscription_check_filters() RETURNS trigger
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION realtime.subscription_check_filters() OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: to_regrole(text); Type: FUNCTION; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION realtime.to_regrole(role_name text) RETURNS regrole
|
||||
LANGUAGE sql IMMUTABLE
|
||||
AS $$ select role_name::regrole $$;
|
||||
|
||||
|
||||
ALTER FUNCTION realtime.to_regrole(role_name text) OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: topic(); Type: FUNCTION; Schema: realtime; Owner: supabase_realtime_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION realtime.topic() RETURNS text
|
||||
LANGUAGE sql STABLE
|
||||
AS $$
|
||||
select nullif(current_setting('realtime.topic', true), '')::text;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION realtime.topic() OWNER TO supabase_realtime_admin;
|
||||
|
||||
--
|
||||
-- Name: can_insert_object(text, text, uuid, jsonb); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Functions — storage schema
|
||||
-- =============================================================================
|
||||
-- Functions: storage
|
||||
-- Gerado automaticamente em 2026-04-17T12:23:05.224Z
|
||||
-- Total: 15
|
||||
|
||||
CREATE FUNCTION storage.can_insert_object(bucketid text, name text, owner uuid, metadata jsonb) RETURNS void
|
||||
LANGUAGE plpgsql
|
||||
@@ -14,13 +14,6 @@ BEGIN
|
||||
END
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION storage.can_insert_object(bucketid text, name text, owner uuid, metadata jsonb) OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: enforce_bucket_name_length(); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION storage.enforce_bucket_name_length() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
@@ -32,13 +25,6 @@ begin
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION storage.enforce_bucket_name_length() OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: extension(text); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION storage.extension(name text) RETURNS text
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
@@ -53,13 +39,6 @@ BEGIN
|
||||
END
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION storage.extension(name text) OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: filename(text); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION storage.filename(name text) RETURNS text
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
@@ -71,13 +50,6 @@ BEGIN
|
||||
END
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION storage.filename(name text) OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: foldername(text); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION storage.foldername(name text) RETURNS text[]
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
@@ -89,13 +61,6 @@ BEGIN
|
||||
END
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION storage.foldername(name text) OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: get_common_prefix(text, text, text); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION storage.get_common_prefix(p_key text, p_prefix text, p_delimiter text) RETURNS text
|
||||
LANGUAGE sql IMMUTABLE
|
||||
AS $$
|
||||
@@ -106,13 +71,6 @@ SELECT CASE
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION storage.get_common_prefix(p_key text, p_prefix text, p_delimiter text) OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: get_size_by_bucket(); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION storage.get_size_by_bucket() RETURNS TABLE(size bigint, bucket_id text)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
@@ -124,13 +82,6 @@ BEGIN
|
||||
END
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION storage.get_size_by_bucket() OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: list_multipart_uploads_with_delimiter(text, text, text, integer, text, text); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION storage.list_multipart_uploads_with_delimiter(bucket_id text, prefix_param text, delimiter_param text, max_keys integer DEFAULT 100, next_key_token text DEFAULT ''::text, next_upload_token text DEFAULT ''::text) RETURNS TABLE(key text, id text, created_at timestamp with time zone)
|
||||
LANGUAGE plpgsql
|
||||
AS $_$
|
||||
@@ -172,13 +123,6 @@ BEGIN
|
||||
END;
|
||||
$_$;
|
||||
|
||||
|
||||
ALTER FUNCTION storage.list_multipart_uploads_with_delimiter(bucket_id text, prefix_param text, delimiter_param text, max_keys integer, next_key_token text, next_upload_token text) OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: list_objects_with_delimiter(text, text, text, integer, text, text, text); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION storage.list_objects_with_delimiter(_bucket_id text, prefix_param text, delimiter_param text, max_keys integer DEFAULT 100, start_after text DEFAULT ''::text, next_token text DEFAULT ''::text, sort_order text DEFAULT 'asc'::text) RETURNS TABLE(name text, id uuid, metadata jsonb, updated_at timestamp with time zone, created_at timestamp with time zone, last_accessed_at timestamp with time zone)
|
||||
LANGUAGE plpgsql STABLE
|
||||
AS $_$
|
||||
@@ -389,13 +333,6 @@ BEGIN
|
||||
END;
|
||||
$_$;
|
||||
|
||||
|
||||
ALTER FUNCTION storage.list_objects_with_delimiter(_bucket_id text, prefix_param text, delimiter_param text, max_keys integer, start_after text, next_token text, sort_order text) OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: operation(); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION storage.operation() RETURNS text
|
||||
LANGUAGE plpgsql STABLE
|
||||
AS $$
|
||||
@@ -404,13 +341,6 @@ BEGIN
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION storage.operation() OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: protect_delete(); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION storage.protect_delete() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
@@ -425,13 +355,6 @@ BEGIN
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION storage.protect_delete() OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: search(text, text, integer, integer, integer, text, text, text); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION storage.search(prefix text, bucketname text, limits integer DEFAULT 100, levels integer DEFAULT 1, offsets integer DEFAULT 0, search text DEFAULT ''::text, sortcolumn text DEFAULT 'name'::text, sortorder text DEFAULT 'asc'::text) RETURNS TABLE(name text, id uuid, updated_at timestamp with time zone, created_at timestamp with time zone, last_accessed_at timestamp with time zone, metadata jsonb)
|
||||
LANGUAGE plpgsql STABLE
|
||||
AS $_$
|
||||
@@ -681,13 +604,6 @@ BEGIN
|
||||
END;
|
||||
$_$;
|
||||
|
||||
|
||||
ALTER FUNCTION storage.search(prefix text, bucketname text, limits integer, levels integer, offsets integer, search text, sortcolumn text, sortorder text) OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: search_by_timestamp(text, text, integer, integer, text, text, text, text); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION storage.search_by_timestamp(p_prefix text, p_bucket_id text, p_limit integer, p_level integer, p_start_after text, p_sort_order text, p_sort_column text, p_sort_column_after text) RETURNS TABLE(key text, name text, id uuid, updated_at timestamp with time zone, created_at timestamp with time zone, last_accessed_at timestamp with time zone, metadata jsonb)
|
||||
LANGUAGE plpgsql STABLE
|
||||
AS $_$
|
||||
@@ -790,13 +706,6 @@ BEGIN
|
||||
END;
|
||||
$_$;
|
||||
|
||||
|
||||
ALTER FUNCTION storage.search_by_timestamp(p_prefix text, p_bucket_id text, p_limit integer, p_level integer, p_start_after text, p_sort_order text, p_sort_column text, p_sort_column_after text) OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: search_v2(text, text, integer, integer, text, text, text, text); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION storage.search_v2(prefix text, bucket_name text, limits integer DEFAULT 100, levels integer DEFAULT 1, start_after text DEFAULT ''::text, sort_order text DEFAULT 'asc'::text, sort_column text DEFAULT 'name'::text, sort_column_after text DEFAULT ''::text) RETURNS TABLE(key text, name text, id uuid, updated_at timestamp with time zone, created_at timestamp with time zone, last_accessed_at timestamp with time zone, metadata jsonb)
|
||||
LANGUAGE plpgsql STABLE
|
||||
AS $$
|
||||
@@ -852,13 +761,6 @@ BEGIN
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION storage.search_v2(prefix text, bucket_name text, limits integer, levels integer, start_after text, sort_order text, sort_column text, sort_column_after text) OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: update_updated_at_column(); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE FUNCTION storage.update_updated_at_column() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
@@ -867,11 +769,3 @@ BEGIN
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION storage.update_updated_at_column() OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: http_request(); Type: FUNCTION; Schema: supabase_functions; Owner: supabase_functions_admin
|
||||
--
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Functions — supabase_functions schema
|
||||
-- =============================================================================
|
||||
-- Functions: supabase_functions
|
||||
-- Gerado automaticamente em 2026-04-17T12:23:05.224Z
|
||||
-- Total: 1
|
||||
|
||||
CREATE FUNCTION supabase_functions.http_request() RETURNS trigger
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
@@ -77,11 +77,3 @@ CREATE FUNCTION supabase_functions.http_request() RETURNS trigger
|
||||
RETURN NEW;
|
||||
END
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION supabase_functions.http_request() OWNER TO supabase_functions_admin;
|
||||
|
||||
--
|
||||
-- Name: extensions; Type: TABLE; Schema: _realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
-- Tables: Addons / Créditos
|
||||
-- Gerado automaticamente em 2026-04-17T12:23:05.228Z
|
||||
-- Total: 3
|
||||
|
||||
CREATE TABLE public.addon_credits (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
owner_id uuid,
|
||||
addon_type text NOT NULL,
|
||||
balance integer DEFAULT 0 NOT NULL,
|
||||
total_purchased integer DEFAULT 0 NOT NULL,
|
||||
total_consumed integer DEFAULT 0 NOT NULL,
|
||||
low_balance_threshold integer DEFAULT 10,
|
||||
low_balance_notified boolean DEFAULT false,
|
||||
daily_limit integer,
|
||||
hourly_limit integer,
|
||||
daily_used integer DEFAULT 0,
|
||||
hourly_used integer DEFAULT 0,
|
||||
daily_reset_at timestamp with time zone,
|
||||
hourly_reset_at timestamp with time zone,
|
||||
from_number_override text,
|
||||
expires_at timestamp with time zone,
|
||||
is_active boolean DEFAULT true,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
updated_at timestamp with time zone DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE public.addon_products (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
slug text NOT NULL,
|
||||
name text NOT NULL,
|
||||
description text,
|
||||
addon_type text NOT NULL,
|
||||
icon text DEFAULT 'pi pi-box'::text,
|
||||
credits_amount integer DEFAULT 0,
|
||||
price_cents integer DEFAULT 0 NOT NULL,
|
||||
currency text DEFAULT 'BRL'::text,
|
||||
is_active boolean DEFAULT true,
|
||||
is_visible boolean DEFAULT true,
|
||||
sort_order integer DEFAULT 0,
|
||||
metadata jsonb DEFAULT '{}'::jsonb,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
updated_at timestamp with time zone DEFAULT now(),
|
||||
deleted_at timestamp with time zone
|
||||
);
|
||||
|
||||
CREATE TABLE public.addon_transactions (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
owner_id uuid,
|
||||
addon_type text NOT NULL,
|
||||
type text NOT NULL,
|
||||
amount integer NOT NULL,
|
||||
balance_before integer DEFAULT 0 NOT NULL,
|
||||
balance_after integer DEFAULT 0 NOT NULL,
|
||||
product_id uuid,
|
||||
queue_id uuid,
|
||||
description text,
|
||||
admin_user_id uuid,
|
||||
payment_method text,
|
||||
payment_reference text,
|
||||
price_cents integer,
|
||||
currency text DEFAULT 'BRL'::text,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
metadata jsonb DEFAULT '{}'::jsonb
|
||||
);
|
||||
+5
-208
@@ -1,11 +1,6 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Tables — Agenda + Recorrências + Agendador Online
|
||||
-- =============================================================================
|
||||
-- agenda_bloqueios, agenda_configuracoes, agenda_eventos, agenda_excecoes,
|
||||
-- agenda_online_slots, agenda_regras_semanais, agenda_slots_bloqueados_semanais,
|
||||
-- agenda_slots_regras, recurrence_rules, recurrence_exceptions,
|
||||
-- recurrence_rule_services, agendador_configuracoes, agendador_solicitacoes
|
||||
-- =============================================================================
|
||||
-- Tables: Agenda / Agendamento
|
||||
-- Gerado automaticamente em 2026-04-17T12:23:05.229Z
|
||||
-- Total: 10
|
||||
|
||||
CREATE TABLE public.agenda_bloqueios (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
@@ -25,13 +20,6 @@ CREATE TABLE public.agenda_bloqueios (
|
||||
CONSTRAINT agenda_bloqueios_tipo_check CHECK ((tipo = ANY (ARRAY['feriado_nacional'::text, 'feriado_municipal'::text, 'bloqueio'::text])))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.agenda_bloqueios OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: agenda_configuracoes; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.agenda_configuracoes (
|
||||
owner_id uuid NOT NULL,
|
||||
duracao_padrao_minutos integer DEFAULT 50 NOT NULL,
|
||||
@@ -68,7 +56,9 @@ CREATE TABLE public.agenda_configuracoes (
|
||||
tenant_id uuid,
|
||||
jornada_igual_todos boolean DEFAULT true,
|
||||
slot_mode text DEFAULT 'fixed'::text NOT NULL,
|
||||
atendimento_mode text DEFAULT 'particular'::text,
|
||||
CONSTRAINT agenda_configuracoes_admin_slot_visual_minutos_check CHECK ((admin_slot_visual_minutos = ANY (ARRAY[5, 10, 15, 20, 30, 60]))),
|
||||
CONSTRAINT agenda_configuracoes_atendimento_mode_check CHECK (((atendimento_mode IS NULL) OR (atendimento_mode = ANY (ARRAY['particular'::text, 'convenio'::text, 'ambos'::text])))),
|
||||
CONSTRAINT agenda_configuracoes_check CHECK (((usar_horario_admin_custom = false) OR ((admin_inicio_visualizacao IS NOT NULL) AND (admin_fim_visualizacao IS NOT NULL) AND (admin_fim_visualizacao > admin_inicio_visualizacao)))),
|
||||
CONSTRAINT agenda_configuracoes_duracao_padrao_minutos_check CHECK (((duracao_padrao_minutos >= 10) AND (duracao_padrao_minutos <= 240))),
|
||||
CONSTRAINT agenda_configuracoes_granularidade_min_check CHECK (((granularidade_min IS NULL) OR (granularidade_min = ANY (ARRAY[5, 10, 15, 20, 30, 45, 50, 60])))),
|
||||
@@ -87,13 +77,6 @@ CREATE TABLE public.agenda_configuracoes (
|
||||
CONSTRAINT session_duration_min_chk CHECK (((session_duration_min >= 10) AND (session_duration_min <= 240)))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.agenda_configuracoes OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: agenda_eventos; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.agenda_eventos (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
@@ -129,20 +112,6 @@ CREATE TABLE public.agenda_eventos (
|
||||
CONSTRAINT agenda_eventos_check CHECK ((fim_em > inicio_em))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.agenda_eventos OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: COLUMN agenda_eventos.price; Type: COMMENT; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
COMMENT ON COLUMN public.agenda_eventos.price IS 'Valor da sessão em BRL. Preenchido automaticamente pela tabela professional_pricing do profissional.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: agenda_excecoes; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.agenda_excecoes (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
@@ -161,13 +130,6 @@ CREATE TABLE public.agenda_excecoes (
|
||||
CONSTRAINT agenda_excecoes_fonte_check CHECK ((fonte = ANY (ARRAY['manual'::text, 'feriado_google'::text, 'sistema'::text])))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.agenda_excecoes OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: agenda_online_slots; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.agenda_online_slots (
|
||||
id bigint NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
@@ -180,34 +142,6 @@ CREATE TABLE public.agenda_online_slots (
|
||||
CONSTRAINT agenda_online_slots_weekday_check CHECK ((weekday = ANY (ARRAY[0, 1, 2, 3, 4, 5, 6])))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.agenda_online_slots OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: agenda_online_slots_id_seq; Type: SEQUENCE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE SEQUENCE public.agenda_online_slots_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
ALTER SEQUENCE public.agenda_online_slots_id_seq OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: agenda_online_slots_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
ALTER SEQUENCE public.agenda_online_slots_id_seq OWNED BY public.agenda_online_slots.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: agenda_regras_semanais; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.agenda_regras_semanais (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
@@ -224,13 +158,6 @@ CREATE TABLE public.agenda_regras_semanais (
|
||||
CONSTRAINT agenda_regras_semanais_modalidade_check CHECK (((modalidade = ANY (ARRAY['online'::text, 'presencial'::text, 'ambos'::text])) OR (modalidade IS NULL)))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.agenda_regras_semanais OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: agenda_slots_bloqueados_semanais; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.agenda_slots_bloqueados_semanais (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
@@ -244,13 +171,6 @@ CREATE TABLE public.agenda_slots_bloqueados_semanais (
|
||||
CONSTRAINT agenda_slots_bloqueados_semanais_dia_semana_check CHECK (((dia_semana >= 0) AND (dia_semana <= 6)))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.agenda_slots_bloqueados_semanais OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: agenda_slots_regras; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.agenda_slots_regras (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
@@ -272,14 +192,6 @@ CREATE TABLE public.agenda_slots_regras (
|
||||
CONSTRAINT agenda_slots_regras_passo_minutos_check CHECK (((passo_minutos >= 5) AND (passo_minutos <= 240)))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.agenda_slots_regras OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: agendador_configuracoes; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
|
||||
CREATE TABLE public.agendador_configuracoes (
|
||||
owner_id uuid NOT NULL,
|
||||
tenant_id uuid,
|
||||
@@ -323,27 +235,6 @@ CREATE TABLE public.agendador_configuracoes (
|
||||
CONSTRAINT agendador_configuracoes_reserva_check CHECK (((reserva_horas >= 1) AND (reserva_horas <= 48)))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.agendador_configuracoes OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: COLUMN agendador_configuracoes.pagamento_modo; Type: COMMENT; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
COMMENT ON COLUMN public.agendador_configuracoes.pagamento_modo IS 'sem_pagamento | pagar_na_hora | pix_antecipado';
|
||||
|
||||
|
||||
--
|
||||
-- Name: COLUMN agendador_configuracoes.pagamento_metodos_visiveis; Type: COMMENT; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
COMMENT ON COLUMN public.agendador_configuracoes.pagamento_metodos_visiveis IS 'Métodos exibidos ao paciente quando pagamento_modo = pagar_na_hora. Ex: {pix, deposito, dinheiro, cartao, convenio}';
|
||||
|
||||
|
||||
--
|
||||
-- Name: agendador_solicitacoes; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.agendador_solicitacoes (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
@@ -376,97 +267,3 @@ CREATE TABLE public.agendador_solicitacoes (
|
||||
CONSTRAINT agendador_sol_status_check CHECK ((status = ANY (ARRAY['pendente'::text, 'autorizado'::text, 'recusado'::text, 'expirado'::text, 'convertido'::text]))),
|
||||
CONSTRAINT agendador_sol_tipo_check CHECK ((tipo = ANY (ARRAY['primeira'::text, 'retorno'::text, 'reagendar'::text])))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.agendador_solicitacoes OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: billing_contracts; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
|
||||
CREATE TABLE public.recurrence_exceptions (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
recurrence_id uuid NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
original_date date NOT NULL,
|
||||
type public.recurrence_exception_type NOT NULL,
|
||||
new_date date,
|
||||
new_start_time time without time zone,
|
||||
new_end_time time without time zone,
|
||||
modalidade text,
|
||||
observacoes text,
|
||||
titulo_custom text,
|
||||
extra_fields jsonb,
|
||||
reason text,
|
||||
agenda_evento_id uuid,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.recurrence_exceptions OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: recurrence_rule_services; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.recurrence_rule_services (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
rule_id uuid NOT NULL,
|
||||
service_id uuid NOT NULL,
|
||||
quantity integer DEFAULT 1 NOT NULL,
|
||||
unit_price numeric(10,2) NOT NULL,
|
||||
discount_pct numeric(5,2) DEFAULT 0,
|
||||
discount_flat numeric(10,2) DEFAULT 0,
|
||||
final_price numeric(10,2) NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
CONSTRAINT recurrence_rule_services_disc_flat_chk CHECK ((discount_flat >= (0)::numeric)),
|
||||
CONSTRAINT recurrence_rule_services_disc_pct_chk CHECK (((discount_pct >= (0)::numeric) AND (discount_pct <= (100)::numeric))),
|
||||
CONSTRAINT recurrence_rule_services_final_price_chk CHECK ((final_price >= (0)::numeric)),
|
||||
CONSTRAINT recurrence_rule_services_quantity_chk CHECK ((quantity > 0))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.recurrence_rule_services OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: recurrence_rules; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.recurrence_rules (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
therapist_id uuid,
|
||||
patient_id uuid,
|
||||
determined_commitment_id uuid,
|
||||
type public.recurrence_type DEFAULT 'weekly'::public.recurrence_type NOT NULL,
|
||||
"interval" smallint DEFAULT 1 NOT NULL,
|
||||
weekdays smallint[] DEFAULT '{}'::smallint[] NOT NULL,
|
||||
start_time time without time zone NOT NULL,
|
||||
end_time time without time zone NOT NULL,
|
||||
timezone text DEFAULT 'America/Sao_Paulo'::text NOT NULL,
|
||||
duration_min smallint DEFAULT 50 NOT NULL,
|
||||
start_date date NOT NULL,
|
||||
end_date date,
|
||||
max_occurrences integer,
|
||||
open_ended boolean DEFAULT true NOT NULL,
|
||||
modalidade text DEFAULT 'presencial'::text,
|
||||
titulo_custom text,
|
||||
observacoes text,
|
||||
extra_fields jsonb,
|
||||
status text DEFAULT 'ativo'::text NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
price numeric(10,2),
|
||||
insurance_plan_id uuid,
|
||||
insurance_guide_number text,
|
||||
insurance_value numeric(10,2),
|
||||
insurance_plan_service_id uuid,
|
||||
CONSTRAINT recurrence_rules_dates_chk CHECK (((end_date IS NULL) OR (end_date >= start_date))),
|
||||
CONSTRAINT recurrence_rules_interval_chk CHECK (("interval" >= 1)),
|
||||
CONSTRAINT recurrence_rules_status_check CHECK ((status = ANY (ARRAY['ativo'::text, 'pausado'::text, 'cancelado'::text]))),
|
||||
CONSTRAINT recurrence_rules_times_chk CHECK ((end_time > start_time))
|
||||
);
|
||||
|
||||
|
||||
@@ -1,608 +0,0 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Tables — auth schema (Supabase GoTrue)
|
||||
-- =============================================================================
|
||||
-- auth.users, auth.identities, auth.sessions, auth.refresh_tokens,
|
||||
-- auth.mfa_*, auth.saml_*, auth.sso_*, auth.flow_state, etc.
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE auth.audit_log_entries (
|
||||
instance_id uuid,
|
||||
id uuid NOT NULL,
|
||||
payload json,
|
||||
created_at timestamp with time zone,
|
||||
ip_address character varying(64) DEFAULT ''::character varying NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE auth.audit_log_entries OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: TABLE audit_log_entries; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON TABLE auth.audit_log_entries IS 'Auth: Audit trail for user actions.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: flow_state; Type: TABLE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TABLE auth.flow_state (
|
||||
id uuid NOT NULL,
|
||||
user_id uuid,
|
||||
auth_code text,
|
||||
code_challenge_method auth.code_challenge_method,
|
||||
code_challenge text,
|
||||
provider_type text NOT NULL,
|
||||
provider_access_token text,
|
||||
provider_refresh_token text,
|
||||
created_at timestamp with time zone,
|
||||
updated_at timestamp with time zone,
|
||||
authentication_method text NOT NULL,
|
||||
auth_code_issued_at timestamp with time zone,
|
||||
invite_token text,
|
||||
referrer text,
|
||||
oauth_client_state_id uuid,
|
||||
linking_target_id uuid,
|
||||
email_optional boolean DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE auth.flow_state OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: TABLE flow_state; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON TABLE auth.flow_state IS 'Stores metadata for all OAuth/SSO login flows';
|
||||
|
||||
|
||||
--
|
||||
-- Name: identities; Type: TABLE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TABLE auth.identities (
|
||||
provider_id text NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
identity_data jsonb NOT NULL,
|
||||
provider text NOT NULL,
|
||||
last_sign_in_at timestamp with time zone,
|
||||
created_at timestamp with time zone,
|
||||
updated_at timestamp with time zone,
|
||||
email text GENERATED ALWAYS AS (lower((identity_data ->> 'email'::text))) STORED,
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE auth.identities OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: TABLE identities; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON TABLE auth.identities IS 'Auth: Stores identities associated to a user.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: COLUMN identities.email; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON COLUMN auth.identities.email IS 'Auth: Email is a generated column that references the optional email property in the identity_data';
|
||||
|
||||
|
||||
--
|
||||
-- Name: instances; Type: TABLE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TABLE auth.instances (
|
||||
id uuid NOT NULL,
|
||||
uuid uuid,
|
||||
raw_base_config text,
|
||||
created_at timestamp with time zone,
|
||||
updated_at timestamp with time zone
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE auth.instances OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: TABLE instances; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON TABLE auth.instances IS 'Auth: Manages users across multiple sites.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: mfa_amr_claims; Type: TABLE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TABLE auth.mfa_amr_claims (
|
||||
session_id uuid NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
updated_at timestamp with time zone NOT NULL,
|
||||
authentication_method text NOT NULL,
|
||||
id uuid NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE auth.mfa_amr_claims OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: TABLE mfa_amr_claims; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON TABLE auth.mfa_amr_claims IS 'auth: stores authenticator method reference claims for multi factor authentication';
|
||||
|
||||
|
||||
--
|
||||
-- Name: mfa_challenges; Type: TABLE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TABLE auth.mfa_challenges (
|
||||
id uuid NOT NULL,
|
||||
factor_id uuid NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
verified_at timestamp with time zone,
|
||||
ip_address inet NOT NULL,
|
||||
otp_code text,
|
||||
web_authn_session_data jsonb
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE auth.mfa_challenges OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: TABLE mfa_challenges; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON TABLE auth.mfa_challenges IS 'auth: stores metadata about challenge requests made';
|
||||
|
||||
|
||||
--
|
||||
-- Name: mfa_factors; Type: TABLE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TABLE auth.mfa_factors (
|
||||
id uuid NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
friendly_name text,
|
||||
factor_type auth.factor_type NOT NULL,
|
||||
status auth.factor_status NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
updated_at timestamp with time zone NOT NULL,
|
||||
secret text,
|
||||
phone text,
|
||||
last_challenged_at timestamp with time zone,
|
||||
web_authn_credential jsonb,
|
||||
web_authn_aaguid uuid,
|
||||
last_webauthn_challenge_data jsonb
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE auth.mfa_factors OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: TABLE mfa_factors; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON TABLE auth.mfa_factors IS 'auth: stores metadata about factors';
|
||||
|
||||
|
||||
--
|
||||
-- Name: COLUMN mfa_factors.last_webauthn_challenge_data; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON COLUMN auth.mfa_factors.last_webauthn_challenge_data IS 'Stores the latest WebAuthn challenge data including attestation/assertion for customer verification';
|
||||
|
||||
|
||||
--
|
||||
-- Name: oauth_authorizations; Type: TABLE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TABLE auth.oauth_authorizations (
|
||||
id uuid NOT NULL,
|
||||
authorization_id text NOT NULL,
|
||||
client_id uuid NOT NULL,
|
||||
user_id uuid,
|
||||
redirect_uri text NOT NULL,
|
||||
scope text NOT NULL,
|
||||
state text,
|
||||
resource text,
|
||||
code_challenge text,
|
||||
code_challenge_method auth.code_challenge_method,
|
||||
response_type auth.oauth_response_type DEFAULT 'code'::auth.oauth_response_type NOT NULL,
|
||||
status auth.oauth_authorization_status DEFAULT 'pending'::auth.oauth_authorization_status NOT NULL,
|
||||
authorization_code text,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
expires_at timestamp with time zone DEFAULT (now() + '00:03:00'::interval) NOT NULL,
|
||||
approved_at timestamp with time zone,
|
||||
nonce text,
|
||||
CONSTRAINT oauth_authorizations_authorization_code_length CHECK ((char_length(authorization_code) <= 255)),
|
||||
CONSTRAINT oauth_authorizations_code_challenge_length CHECK ((char_length(code_challenge) <= 128)),
|
||||
CONSTRAINT oauth_authorizations_expires_at_future CHECK ((expires_at > created_at)),
|
||||
CONSTRAINT oauth_authorizations_nonce_length CHECK ((char_length(nonce) <= 255)),
|
||||
CONSTRAINT oauth_authorizations_redirect_uri_length CHECK ((char_length(redirect_uri) <= 2048)),
|
||||
CONSTRAINT oauth_authorizations_resource_length CHECK ((char_length(resource) <= 2048)),
|
||||
CONSTRAINT oauth_authorizations_scope_length CHECK ((char_length(scope) <= 4096)),
|
||||
CONSTRAINT oauth_authorizations_state_length CHECK ((char_length(state) <= 4096))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE auth.oauth_authorizations OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: oauth_client_states; Type: TABLE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TABLE auth.oauth_client_states (
|
||||
id uuid NOT NULL,
|
||||
provider_type text NOT NULL,
|
||||
code_verifier text,
|
||||
created_at timestamp with time zone NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE auth.oauth_client_states OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: TABLE oauth_client_states; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON TABLE auth.oauth_client_states IS 'Stores OAuth states for third-party provider authentication flows where Supabase acts as the OAuth client.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: oauth_clients; Type: TABLE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TABLE auth.oauth_clients (
|
||||
id uuid NOT NULL,
|
||||
client_secret_hash text,
|
||||
registration_type auth.oauth_registration_type NOT NULL,
|
||||
redirect_uris text NOT NULL,
|
||||
grant_types text NOT NULL,
|
||||
client_name text,
|
||||
client_uri text,
|
||||
logo_uri text,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
deleted_at timestamp with time zone,
|
||||
client_type auth.oauth_client_type DEFAULT 'confidential'::auth.oauth_client_type NOT NULL,
|
||||
token_endpoint_auth_method text NOT NULL,
|
||||
CONSTRAINT oauth_clients_client_name_length CHECK ((char_length(client_name) <= 1024)),
|
||||
CONSTRAINT oauth_clients_client_uri_length CHECK ((char_length(client_uri) <= 2048)),
|
||||
CONSTRAINT oauth_clients_logo_uri_length CHECK ((char_length(logo_uri) <= 2048)),
|
||||
CONSTRAINT oauth_clients_token_endpoint_auth_method_check CHECK ((token_endpoint_auth_method = ANY (ARRAY['client_secret_basic'::text, 'client_secret_post'::text, 'none'::text])))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE auth.oauth_clients OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: oauth_consents; Type: TABLE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TABLE auth.oauth_consents (
|
||||
id uuid NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
client_id uuid NOT NULL,
|
||||
scopes text NOT NULL,
|
||||
granted_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
revoked_at timestamp with time zone,
|
||||
CONSTRAINT oauth_consents_revoked_after_granted CHECK (((revoked_at IS NULL) OR (revoked_at >= granted_at))),
|
||||
CONSTRAINT oauth_consents_scopes_length CHECK ((char_length(scopes) <= 2048)),
|
||||
CONSTRAINT oauth_consents_scopes_not_empty CHECK ((char_length(TRIM(BOTH FROM scopes)) > 0))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE auth.oauth_consents OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: one_time_tokens; Type: TABLE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TABLE auth.one_time_tokens (
|
||||
id uuid NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
token_type auth.one_time_token_type NOT NULL,
|
||||
token_hash text NOT NULL,
|
||||
relates_to text NOT NULL,
|
||||
created_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT one_time_tokens_token_hash_check CHECK ((char_length(token_hash) > 0))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE auth.one_time_tokens OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: refresh_tokens; Type: TABLE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TABLE auth.refresh_tokens (
|
||||
instance_id uuid,
|
||||
id bigint NOT NULL,
|
||||
token character varying(255),
|
||||
user_id character varying(255),
|
||||
revoked boolean,
|
||||
created_at timestamp with time zone,
|
||||
updated_at timestamp with time zone,
|
||||
parent character varying(255),
|
||||
session_id uuid
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE auth.refresh_tokens OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: TABLE refresh_tokens; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON TABLE auth.refresh_tokens IS 'Auth: Store of tokens used to refresh JWT tokens once they expire.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: refresh_tokens_id_seq; Type: SEQUENCE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE SEQUENCE auth.refresh_tokens_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
ALTER SEQUENCE auth.refresh_tokens_id_seq OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: refresh_tokens_id_seq; Type: SEQUENCE OWNED BY; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
ALTER SEQUENCE auth.refresh_tokens_id_seq OWNED BY auth.refresh_tokens.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: saml_providers; Type: TABLE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TABLE auth.saml_providers (
|
||||
id uuid NOT NULL,
|
||||
sso_provider_id uuid NOT NULL,
|
||||
entity_id text NOT NULL,
|
||||
metadata_xml text NOT NULL,
|
||||
metadata_url text,
|
||||
attribute_mapping jsonb,
|
||||
created_at timestamp with time zone,
|
||||
updated_at timestamp with time zone,
|
||||
name_id_format text,
|
||||
CONSTRAINT "entity_id not empty" CHECK ((char_length(entity_id) > 0)),
|
||||
CONSTRAINT "metadata_url not empty" CHECK (((metadata_url = NULL::text) OR (char_length(metadata_url) > 0))),
|
||||
CONSTRAINT "metadata_xml not empty" CHECK ((char_length(metadata_xml) > 0))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE auth.saml_providers OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: TABLE saml_providers; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON TABLE auth.saml_providers IS 'Auth: Manages SAML Identity Provider connections.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: saml_relay_states; Type: TABLE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TABLE auth.saml_relay_states (
|
||||
id uuid NOT NULL,
|
||||
sso_provider_id uuid NOT NULL,
|
||||
request_id text NOT NULL,
|
||||
for_email text,
|
||||
redirect_to text,
|
||||
created_at timestamp with time zone,
|
||||
updated_at timestamp with time zone,
|
||||
flow_state_id uuid,
|
||||
CONSTRAINT "request_id not empty" CHECK ((char_length(request_id) > 0))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE auth.saml_relay_states OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: TABLE saml_relay_states; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON TABLE auth.saml_relay_states IS 'Auth: Contains SAML Relay State information for each Service Provider initiated login.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: schema_migrations; Type: TABLE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TABLE auth.schema_migrations (
|
||||
version character varying(255) NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE auth.schema_migrations OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: TABLE schema_migrations; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON TABLE auth.schema_migrations IS 'Auth: Manages updates to the auth system.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: sessions; Type: TABLE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TABLE auth.sessions (
|
||||
id uuid NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
created_at timestamp with time zone,
|
||||
updated_at timestamp with time zone,
|
||||
factor_id uuid,
|
||||
aal auth.aal_level,
|
||||
not_after timestamp with time zone,
|
||||
refreshed_at timestamp without time zone,
|
||||
user_agent text,
|
||||
ip inet,
|
||||
tag text,
|
||||
oauth_client_id uuid,
|
||||
refresh_token_hmac_key text,
|
||||
refresh_token_counter bigint,
|
||||
scopes text,
|
||||
CONSTRAINT sessions_scopes_length CHECK ((char_length(scopes) <= 4096))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE auth.sessions OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: TABLE sessions; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON TABLE auth.sessions IS 'Auth: Stores session data associated to a user.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: COLUMN sessions.not_after; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON COLUMN auth.sessions.not_after IS 'Auth: Not after is a nullable column that contains a timestamp after which the session should be regarded as expired.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: COLUMN sessions.refresh_token_hmac_key; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON COLUMN auth.sessions.refresh_token_hmac_key IS 'Holds a HMAC-SHA256 key used to sign refresh tokens for this session.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: COLUMN sessions.refresh_token_counter; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON COLUMN auth.sessions.refresh_token_counter IS 'Holds the ID (counter) of the last issued refresh token.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: sso_domains; Type: TABLE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TABLE auth.sso_domains (
|
||||
id uuid NOT NULL,
|
||||
sso_provider_id uuid NOT NULL,
|
||||
domain text NOT NULL,
|
||||
created_at timestamp with time zone,
|
||||
updated_at timestamp with time zone,
|
||||
CONSTRAINT "domain not empty" CHECK ((char_length(domain) > 0))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE auth.sso_domains OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: TABLE sso_domains; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON TABLE auth.sso_domains IS 'Auth: Manages SSO email address domain mapping to an SSO Identity Provider.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: sso_providers; Type: TABLE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TABLE auth.sso_providers (
|
||||
id uuid NOT NULL,
|
||||
resource_id text,
|
||||
created_at timestamp with time zone,
|
||||
updated_at timestamp with time zone,
|
||||
disabled boolean,
|
||||
CONSTRAINT "resource_id not empty" CHECK (((resource_id = NULL::text) OR (char_length(resource_id) > 0)))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE auth.sso_providers OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: TABLE sso_providers; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON TABLE auth.sso_providers IS 'Auth: Manages SSO identity provider information; see saml_providers for SAML.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: COLUMN sso_providers.resource_id; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON COLUMN auth.sso_providers.resource_id IS 'Auth: Uniquely identifies a SSO provider according to a user-chosen resource ID (case insensitive), useful in infrastructure as code.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: users; Type: TABLE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TABLE auth.users (
|
||||
instance_id uuid,
|
||||
id uuid NOT NULL,
|
||||
aud character varying(255),
|
||||
role character varying(255),
|
||||
email character varying(255),
|
||||
encrypted_password character varying(255),
|
||||
email_confirmed_at timestamp with time zone,
|
||||
invited_at timestamp with time zone,
|
||||
confirmation_token character varying(255),
|
||||
confirmation_sent_at timestamp with time zone,
|
||||
recovery_token character varying(255),
|
||||
recovery_sent_at timestamp with time zone,
|
||||
email_change_token_new character varying(255),
|
||||
email_change character varying(255),
|
||||
email_change_sent_at timestamp with time zone,
|
||||
last_sign_in_at timestamp with time zone,
|
||||
raw_app_meta_data jsonb,
|
||||
raw_user_meta_data jsonb,
|
||||
is_super_admin boolean,
|
||||
created_at timestamp with time zone,
|
||||
updated_at timestamp with time zone,
|
||||
phone text DEFAULT NULL::character varying,
|
||||
phone_confirmed_at timestamp with time zone,
|
||||
phone_change text DEFAULT ''::character varying,
|
||||
phone_change_token character varying(255) DEFAULT ''::character varying,
|
||||
phone_change_sent_at timestamp with time zone,
|
||||
confirmed_at timestamp with time zone GENERATED ALWAYS AS (LEAST(email_confirmed_at, phone_confirmed_at)) STORED,
|
||||
email_change_token_current character varying(255) DEFAULT ''::character varying,
|
||||
email_change_confirm_status smallint DEFAULT 0,
|
||||
banned_until timestamp with time zone,
|
||||
reauthentication_token character varying(255) DEFAULT ''::character varying,
|
||||
reauthentication_sent_at timestamp with time zone,
|
||||
is_sso_user boolean DEFAULT false NOT NULL,
|
||||
deleted_at timestamp with time zone,
|
||||
is_anonymous boolean DEFAULT false NOT NULL,
|
||||
CONSTRAINT users_email_change_confirm_status_check CHECK (((email_change_confirm_status >= 0) AND (email_change_confirm_status <= 2)))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE auth.users OWNER TO supabase_auth_admin;
|
||||
|
||||
--
|
||||
-- Name: TABLE users; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON TABLE auth.users IS 'Auth: Stores user login data within a secure schema.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: COLUMN users.is_sso_user; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
COMMENT ON COLUMN auth.users.is_sso_user IS 'Auth: Set this column to true when the account comes from SSO. These accounts can have duplicate emails.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: addon_credits; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
-- Tables: Central SaaS (docs/FAQ)
|
||||
-- Gerado automaticamente em 2026-04-17T12:23:05.230Z
|
||||
-- Total: 4
|
||||
|
||||
CREATE TABLE public.saas_doc_votos (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
doc_id uuid NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
util boolean NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.saas_docs (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
titulo text NOT NULL,
|
||||
conteudo text DEFAULT ''::text NOT NULL,
|
||||
medias jsonb DEFAULT '[]'::jsonb NOT NULL,
|
||||
tipo_acesso text DEFAULT 'usuario'::text NOT NULL,
|
||||
pagina_path text NOT NULL,
|
||||
docs_relacionados uuid[] DEFAULT '{}'::uuid[] NOT NULL,
|
||||
ativo boolean DEFAULT true NOT NULL,
|
||||
ordem integer DEFAULT 0 NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
categoria text,
|
||||
exibir_no_faq boolean DEFAULT false NOT NULL,
|
||||
votos_util integer DEFAULT 0 NOT NULL,
|
||||
votos_nao_util integer DEFAULT 0 NOT NULL,
|
||||
CONSTRAINT saas_docs_tipo_acesso_check CHECK ((tipo_acesso = ANY (ARRAY['admin'::text, 'usuario'::text])))
|
||||
);
|
||||
|
||||
CREATE TABLE public.saas_faq (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
pergunta text NOT NULL,
|
||||
categoria text,
|
||||
publico boolean DEFAULT false NOT NULL,
|
||||
votos integer DEFAULT 0 NOT NULL,
|
||||
titulo text,
|
||||
conteudo text,
|
||||
tipo_acesso text DEFAULT 'usuario'::text NOT NULL,
|
||||
pagina_path text NOT NULL,
|
||||
pagina_label text,
|
||||
medias jsonb DEFAULT '[]'::jsonb NOT NULL,
|
||||
faqs_relacionados uuid[] DEFAULT '{}'::uuid[] NOT NULL,
|
||||
ativo boolean DEFAULT true NOT NULL,
|
||||
ordem integer DEFAULT 0 NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.saas_faq_itens (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
doc_id uuid NOT NULL,
|
||||
pergunta text NOT NULL,
|
||||
resposta text,
|
||||
ordem integer DEFAULT 0 NOT NULL,
|
||||
ativo boolean DEFAULT true NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
+118
-101
@@ -1,11 +1,99 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Tables — Notificações + Email Templates
|
||||
-- =============================================================================
|
||||
-- notification_channels, notification_logs, notification_preferences,
|
||||
-- notification_queue, notification_schedules, notification_templates,
|
||||
-- notifications, email_templates_global, email_templates_tenant,
|
||||
-- email_layout_config
|
||||
-- =============================================================================
|
||||
-- Tables: Comunicação / Notificações
|
||||
-- Gerado automaticamente em 2026-04-17T12:23:05.230Z
|
||||
-- Total: 14
|
||||
|
||||
CREATE TABLE public.email_layout_config (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
header_config jsonb DEFAULT '{"layout": null, "content": "", "enabled": false}'::jsonb NOT NULL,
|
||||
footer_config jsonb DEFAULT '{"layout": null, "content": "", "enabled": false}'::jsonb NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.email_templates_global (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
key text NOT NULL,
|
||||
domain text NOT NULL,
|
||||
channel text DEFAULT 'email'::text NOT NULL,
|
||||
subject text NOT NULL,
|
||||
body_html text NOT NULL,
|
||||
body_text text,
|
||||
version integer DEFAULT 1 NOT NULL,
|
||||
is_active boolean DEFAULT true NOT NULL,
|
||||
variables jsonb,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.email_templates_tenant (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
owner_id uuid,
|
||||
template_key text NOT NULL,
|
||||
subject text,
|
||||
body_html text,
|
||||
body_text text,
|
||||
enabled boolean DEFAULT true NOT NULL,
|
||||
synced_version integer,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.global_notices (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
title text,
|
||||
message text DEFAULT ''::text NOT NULL,
|
||||
variant text DEFAULT 'info'::text NOT NULL,
|
||||
roles text[] DEFAULT '{}'::text[] NOT NULL,
|
||||
contexts text[] DEFAULT '{}'::text[] NOT NULL,
|
||||
starts_at timestamp with time zone,
|
||||
ends_at timestamp with time zone,
|
||||
is_active boolean DEFAULT true NOT NULL,
|
||||
priority integer DEFAULT 0 NOT NULL,
|
||||
dismissible boolean DEFAULT true NOT NULL,
|
||||
persist_dismiss boolean DEFAULT true NOT NULL,
|
||||
dismiss_scope text DEFAULT 'device'::text NOT NULL,
|
||||
show_once boolean DEFAULT false NOT NULL,
|
||||
max_views integer,
|
||||
cooldown_minutes integer,
|
||||
version integer DEFAULT 1 NOT NULL,
|
||||
action_type text DEFAULT 'none'::text NOT NULL,
|
||||
action_label text,
|
||||
action_url text,
|
||||
action_route text,
|
||||
views_count integer DEFAULT 0 NOT NULL,
|
||||
clicks_count integer DEFAULT 0 NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
created_by uuid,
|
||||
content_align text DEFAULT 'left'::text NOT NULL,
|
||||
link_target text DEFAULT '_blank'::text NOT NULL,
|
||||
CONSTRAINT global_notices_action_type_check CHECK ((action_type = ANY (ARRAY['none'::text, 'internal'::text, 'external'::text]))),
|
||||
CONSTRAINT global_notices_content_align_check CHECK ((content_align = ANY (ARRAY['left'::text, 'center'::text, 'right'::text, 'justify'::text]))),
|
||||
CONSTRAINT global_notices_dismiss_scope_check CHECK ((dismiss_scope = ANY (ARRAY['session'::text, 'device'::text, 'user'::text]))),
|
||||
CONSTRAINT global_notices_link_target_check CHECK ((link_target = ANY (ARRAY['_blank'::text, '_self'::text, '_parent'::text, '_top'::text]))),
|
||||
CONSTRAINT global_notices_variant_check CHECK ((variant = ANY (ARRAY['info'::text, 'success'::text, 'warning'::text, 'error'::text])))
|
||||
);
|
||||
|
||||
CREATE TABLE public.login_carousel_slides (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
title text NOT NULL,
|
||||
body text NOT NULL,
|
||||
icon text DEFAULT 'pi-star'::text NOT NULL,
|
||||
ordem integer DEFAULT 0 NOT NULL,
|
||||
ativo boolean DEFAULT true NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
updated_at timestamp with time zone DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE public.notice_dismissals (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
notice_id uuid NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
version integer DEFAULT 1 NOT NULL,
|
||||
dismissed_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.notification_channels (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
@@ -23,18 +111,18 @@ CREATE TABLE public.notification_channels (
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
deleted_at timestamp with time zone,
|
||||
twilio_subaccount_sid text,
|
||||
twilio_phone_number text,
|
||||
twilio_phone_sid text,
|
||||
webhook_url text,
|
||||
cost_per_message_usd numeric(8,6) DEFAULT 0,
|
||||
price_per_message_brl numeric(8,4) DEFAULT 0,
|
||||
provisioned_at timestamp with time zone,
|
||||
CONSTRAINT notification_channels_channel_check CHECK ((channel = ANY (ARRAY['whatsapp'::text, 'email'::text, 'sms'::text]))),
|
||||
CONSTRAINT notification_channels_connection_status_check CHECK ((connection_status = ANY (ARRAY['connected'::text, 'disconnected'::text, 'connecting'::text, 'qr_pending'::text, 'error'::text]))),
|
||||
CONSTRAINT notification_channels_provider_check CHECK ((provider = ANY (ARRAY['evolution_api'::text, 'meta_official'::text, 'twilio'::text, 'zenvia'::text, 'sendgrid'::text, 'resend'::text, 'smtp'::text, 'zapi'::text])))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.notification_channels OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: notification_logs; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.notification_logs (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
@@ -64,13 +152,6 @@ CREATE TABLE public.notification_logs (
|
||||
CONSTRAINT notification_logs_status_check CHECK ((status = ANY (ARRAY['sent'::text, 'delivered'::text, 'read'::text, 'failed'::text, 'bounced'::text, 'opted_out'::text])))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.notification_logs OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: notification_preferences; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.notification_preferences (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
@@ -92,13 +173,6 @@ CREATE TABLE public.notification_preferences (
|
||||
deleted_at timestamp with time zone
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.notification_preferences OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: notification_queue; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.notification_queue (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
@@ -125,13 +199,6 @@ CREATE TABLE public.notification_queue (
|
||||
CONSTRAINT notification_queue_status_check CHECK ((status = ANY (ARRAY['pendente'::text, 'processando'::text, 'enviado'::text, 'falhou'::text, 'cancelado'::text, 'ignorado'::text])))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.notification_queue OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: notification_schedules; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.notification_schedules (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
@@ -156,13 +223,6 @@ CREATE TABLE public.notification_schedules (
|
||||
CONSTRAINT notification_schedules_trigger_type_check CHECK ((trigger_type = ANY (ARRAY['before_event'::text, 'after_event'::text, 'immediate'::text])))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.notification_schedules OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: notification_templates; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.notification_templates (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid,
|
||||
@@ -189,13 +249,6 @@ CREATE TABLE public.notification_templates (
|
||||
CONSTRAINT notification_templates_meta_status_check CHECK ((meta_status = ANY (ARRAY['draft'::text, 'pending_approval'::text, 'approved'::text, 'rejected'::text])))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.notification_templates OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: notifications; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.notifications (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
@@ -210,58 +263,22 @@ CREATE TABLE public.notifications (
|
||||
CONSTRAINT notifications_type_check CHECK ((type = ANY (ARRAY['new_scheduling'::text, 'new_patient'::text, 'recurrence_alert'::text, 'session_status'::text])))
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.email_layout_config (
|
||||
CREATE TABLE public.twilio_subaccount_usage (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
header_config jsonb DEFAULT '{"layout": null, "content": "", "enabled": false}'::jsonb NOT NULL,
|
||||
footer_config jsonb DEFAULT '{"layout": null, "content": "", "enabled": false}'::jsonb NOT NULL,
|
||||
channel_id uuid NOT NULL,
|
||||
twilio_subaccount_sid text NOT NULL,
|
||||
period_start date NOT NULL,
|
||||
period_end date NOT NULL,
|
||||
messages_sent integer DEFAULT 0 NOT NULL,
|
||||
messages_delivered integer DEFAULT 0 NOT NULL,
|
||||
messages_failed integer DEFAULT 0 NOT NULL,
|
||||
cost_usd numeric(12,6) DEFAULT 0 NOT NULL,
|
||||
cost_brl numeric(12,4) DEFAULT 0 NOT NULL,
|
||||
revenue_brl numeric(12,4) DEFAULT 0 NOT NULL,
|
||||
margin_brl numeric(12,4) GENERATED ALWAYS AS ((revenue_brl - cost_brl)) STORED,
|
||||
usd_brl_rate numeric(8,4) DEFAULT 0,
|
||||
synced_at timestamp with time zone DEFAULT now(),
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
CONSTRAINT twilio_subaccount_usage_period_check CHECK ((period_end >= period_start))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.email_layout_config OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: email_templates_global; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.email_templates_global (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
key text NOT NULL,
|
||||
domain text NOT NULL,
|
||||
channel text DEFAULT 'email'::text NOT NULL,
|
||||
subject text NOT NULL,
|
||||
body_html text NOT NULL,
|
||||
body_text text,
|
||||
version integer DEFAULT 1 NOT NULL,
|
||||
is_active boolean DEFAULT true NOT NULL,
|
||||
variables jsonb,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.email_templates_global OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: email_templates_tenant; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.email_templates_tenant (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
owner_id uuid,
|
||||
template_key text NOT NULL,
|
||||
subject text,
|
||||
body_html text,
|
||||
body_text text,
|
||||
enabled boolean DEFAULT true NOT NULL,
|
||||
synced_version integer,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
-- Tables: Documentos
|
||||
-- Gerado automaticamente em 2026-04-17T12:23:05.229Z
|
||||
-- Total: 6
|
||||
|
||||
CREATE TABLE public.document_access_logs (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
documento_id uuid NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
acao text NOT NULL,
|
||||
user_id uuid,
|
||||
ip inet,
|
||||
user_agent text,
|
||||
acessado_em timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT dal_acao_check CHECK ((acao = ANY (ARRAY['visualizou'::text, 'baixou'::text, 'imprimiu'::text, 'compartilhou'::text, 'assinou'::text])))
|
||||
);
|
||||
|
||||
CREATE TABLE public.document_generated (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
template_id uuid NOT NULL,
|
||||
patient_id uuid NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
dados_preenchidos jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
pdf_path text NOT NULL,
|
||||
storage_bucket text DEFAULT 'generated-docs'::text NOT NULL,
|
||||
documento_id uuid,
|
||||
gerado_por uuid NOT NULL,
|
||||
gerado_em timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.document_share_links (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
documento_id uuid NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
token text DEFAULT encode(extensions.gen_random_bytes(32), 'hex'::text) NOT NULL,
|
||||
expira_em timestamp with time zone NOT NULL,
|
||||
usos_max smallint DEFAULT 5 NOT NULL,
|
||||
usos smallint DEFAULT 0 NOT NULL,
|
||||
criado_por uuid NOT NULL,
|
||||
criado_em timestamp with time zone DEFAULT now(),
|
||||
ativo boolean DEFAULT true NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.document_signatures (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
documento_id uuid NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
signatario_tipo text NOT NULL,
|
||||
signatario_id uuid,
|
||||
signatario_nome text,
|
||||
signatario_email text,
|
||||
ordem smallint DEFAULT 1 NOT NULL,
|
||||
status text DEFAULT 'pendente'::text NOT NULL,
|
||||
ip inet,
|
||||
user_agent text,
|
||||
assinado_em timestamp with time zone,
|
||||
hash_documento text,
|
||||
criado_em timestamp with time zone DEFAULT now(),
|
||||
atualizado_em timestamp with time zone DEFAULT now(),
|
||||
CONSTRAINT ds_signatario_tipo_check CHECK ((signatario_tipo = ANY (ARRAY['paciente'::text, 'responsavel_legal'::text, 'terapeuta'::text]))),
|
||||
CONSTRAINT ds_status_check CHECK ((status = ANY (ARRAY['pendente'::text, 'enviado'::text, 'assinado'::text, 'recusado'::text, 'expirado'::text])))
|
||||
);
|
||||
|
||||
CREATE TABLE public.document_templates (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid,
|
||||
owner_id uuid,
|
||||
nome_template text NOT NULL,
|
||||
tipo text DEFAULT 'outro'::text NOT NULL,
|
||||
descricao text,
|
||||
corpo_html text DEFAULT ''::text NOT NULL,
|
||||
cabecalho_html text,
|
||||
rodape_html text,
|
||||
variaveis text[] DEFAULT '{}'::text[],
|
||||
logo_url text,
|
||||
is_global boolean DEFAULT false NOT NULL,
|
||||
ativo boolean DEFAULT true NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
updated_at timestamp with time zone DEFAULT now(),
|
||||
CONSTRAINT dt_tipo_check CHECK ((tipo = ANY (ARRAY['declaracao_comparecimento'::text, 'atestado_psicologico'::text, 'relatorio_acompanhamento'::text, 'recibo_pagamento'::text, 'termo_consentimento'::text, 'encaminhamento'::text, 'contrato_servicos'::text, 'tcle'::text, 'autorizacao_menor'::text, 'laudo_psicologico'::text, 'parecer_psicologico'::text, 'termo_sigilo'::text, 'declaracao_inicio_tratamento'::text, 'termo_alta'::text, 'tcle_online'::text, 'outro'::text])))
|
||||
);
|
||||
|
||||
CREATE TABLE public.documents (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
patient_id uuid NOT NULL,
|
||||
bucket_path text NOT NULL,
|
||||
storage_bucket text DEFAULT 'documents'::text NOT NULL,
|
||||
nome_original text NOT NULL,
|
||||
mime_type text,
|
||||
tamanho_bytes bigint,
|
||||
tipo_documento text DEFAULT 'outro'::text NOT NULL,
|
||||
categoria text,
|
||||
descricao text,
|
||||
tags text[] DEFAULT '{}'::text[],
|
||||
agenda_evento_id uuid,
|
||||
session_note_id uuid,
|
||||
visibilidade text DEFAULT 'privado'::text NOT NULL,
|
||||
compartilhado_portal boolean DEFAULT false NOT NULL,
|
||||
compartilhado_supervisor boolean DEFAULT false NOT NULL,
|
||||
compartilhado_em timestamp with time zone,
|
||||
expira_compartilhamento timestamp with time zone,
|
||||
enviado_pelo_paciente boolean DEFAULT false NOT NULL,
|
||||
status_revisao text DEFAULT 'aprovado'::text,
|
||||
revisado_por uuid,
|
||||
revisado_em timestamp with time zone,
|
||||
uploaded_by uuid NOT NULL,
|
||||
uploaded_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
deleted_at timestamp with time zone,
|
||||
deleted_by uuid,
|
||||
retencao_ate timestamp with time zone,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
updated_at timestamp with time zone DEFAULT now(),
|
||||
CONSTRAINT documents_status_revisao_check CHECK ((status_revisao = ANY (ARRAY['pendente'::text, 'aprovado'::text, 'rejeitado'::text]))),
|
||||
CONSTRAINT documents_tipo_check CHECK ((tipo_documento = ANY (ARRAY['laudo'::text, 'receita'::text, 'exame'::text, 'termo_assinado'::text, 'relatorio_externo'::text, 'identidade'::text, 'convenio'::text, 'declaracao'::text, 'atestado'::text, 'recibo'::text, 'outro'::text]))),
|
||||
CONSTRAINT documents_visibilidade_check CHECK ((visibilidade = ANY (ARRAY['privado'::text, 'compartilhado_supervisor'::text, 'compartilhado_portal'::text])))
|
||||
);
|
||||
@@ -0,0 +1,18 @@
|
||||
-- Tables: Estrutura / Calendário
|
||||
-- Gerado automaticamente em 2026-04-17T12:23:05.230Z
|
||||
-- Total: 1
|
||||
|
||||
CREATE TABLE public.feriados (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid,
|
||||
owner_id uuid,
|
||||
tipo text DEFAULT 'municipal'::text NOT NULL,
|
||||
nome text NOT NULL,
|
||||
data date NOT NULL,
|
||||
cidade text,
|
||||
estado text,
|
||||
observacao text,
|
||||
bloqueia_sessoes boolean DEFAULT false NOT NULL,
|
||||
criado_em timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT feriados_tipo_check CHECK ((tipo = ANY (ARRAY['municipal'::text, 'personalizado'::text])))
|
||||
);
|
||||
+88
-87
@@ -1,10 +1,6 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Tables — Financeiro
|
||||
-- =============================================================================
|
||||
-- financial_records, financial_categories, financial_exceptions,
|
||||
-- payment_settings, professional_pricing, therapist_payouts,
|
||||
-- therapist_payout_records, services, insurance_plans, insurance_plan_services
|
||||
-- =============================================================================
|
||||
-- Tables: Financeiro
|
||||
-- Gerado automaticamente em 2026-04-17T12:23:05.228Z
|
||||
-- Total: 10
|
||||
|
||||
CREATE TABLE public.financial_records (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
@@ -44,7 +40,27 @@ CREATE TABLE public.financial_records (
|
||||
CONSTRAINT financial_records_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'paid'::text, 'partial'::text, 'overdue'::text, 'cancelled'::text, 'refunded'::text])))
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE public.therapist_payouts (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
period_start date NOT NULL,
|
||||
period_end date NOT NULL,
|
||||
total_sessions integer DEFAULT 0 NOT NULL,
|
||||
gross_amount numeric(10,2) DEFAULT 0 NOT NULL,
|
||||
clinic_fee_total numeric(10,2) DEFAULT 0 NOT NULL,
|
||||
net_amount numeric(10,2) DEFAULT 0 NOT NULL,
|
||||
status text DEFAULT 'pending'::text NOT NULL,
|
||||
paid_at timestamp with time zone,
|
||||
notes text,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT therapist_payouts_clinic_fee_total_check CHECK ((clinic_fee_total >= (0)::numeric)),
|
||||
CONSTRAINT therapist_payouts_gross_amount_check CHECK ((gross_amount >= (0)::numeric)),
|
||||
CONSTRAINT therapist_payouts_net_amount_check CHECK ((net_amount >= (0)::numeric)),
|
||||
CONSTRAINT therapist_payouts_period_chk CHECK ((period_end >= period_start)),
|
||||
CONSTRAINT therapist_payouts_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'paid'::text, 'cancelled'::text])))
|
||||
);
|
||||
|
||||
CREATE TABLE public.financial_categories (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
@@ -57,13 +73,6 @@ CREATE TABLE public.financial_categories (
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.financial_categories OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: financial_exceptions; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.financial_exceptions (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid,
|
||||
@@ -79,8 +88,6 @@ CREATE TABLE public.financial_exceptions (
|
||||
CONSTRAINT financial_exceptions_type_chk CHECK ((exception_type = ANY (ARRAY['patient_no_show'::text, 'patient_cancellation'::text, 'professional_cancellation'::text])))
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.payment_settings (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
@@ -106,8 +113,6 @@ CREATE TABLE public.payment_settings (
|
||||
updated_at timestamp with time zone DEFAULT now()
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.professional_pricing (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
@@ -119,81 +124,77 @@ CREATE TABLE public.professional_pricing (
|
||||
updated_at timestamp with time zone DEFAULT now()
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.therapist_payouts (
|
||||
CREATE TABLE public.recurrence_exceptions (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
recurrence_id uuid NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
period_start date NOT NULL,
|
||||
period_end date NOT NULL,
|
||||
total_sessions integer DEFAULT 0 NOT NULL,
|
||||
gross_amount numeric(10,2) DEFAULT 0 NOT NULL,
|
||||
clinic_fee_total numeric(10,2) DEFAULT 0 NOT NULL,
|
||||
net_amount numeric(10,2) DEFAULT 0 NOT NULL,
|
||||
status text DEFAULT 'pending'::text NOT NULL,
|
||||
paid_at timestamp with time zone,
|
||||
notes text,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT therapist_payouts_clinic_fee_total_check CHECK ((clinic_fee_total >= (0)::numeric)),
|
||||
CONSTRAINT therapist_payouts_gross_amount_check CHECK ((gross_amount >= (0)::numeric)),
|
||||
CONSTRAINT therapist_payouts_net_amount_check CHECK ((net_amount >= (0)::numeric)),
|
||||
CONSTRAINT therapist_payouts_period_chk CHECK ((period_end >= period_start)),
|
||||
CONSTRAINT therapist_payouts_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'paid'::text, 'cancelled'::text])))
|
||||
original_date date NOT NULL,
|
||||
type public.recurrence_exception_type NOT NULL,
|
||||
new_date date,
|
||||
new_start_time time without time zone,
|
||||
new_end_time time without time zone,
|
||||
modalidade text,
|
||||
observacoes text,
|
||||
titulo_custom text,
|
||||
extra_fields jsonb,
|
||||
reason text,
|
||||
agenda_evento_id uuid,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.recurrence_rule_services (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
rule_id uuid NOT NULL,
|
||||
service_id uuid NOT NULL,
|
||||
quantity integer DEFAULT 1 NOT NULL,
|
||||
unit_price numeric(10,2) NOT NULL,
|
||||
discount_pct numeric(5,2) DEFAULT 0,
|
||||
discount_flat numeric(10,2) DEFAULT 0,
|
||||
final_price numeric(10,2) NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
CONSTRAINT recurrence_rule_services_disc_flat_chk CHECK ((discount_flat >= (0)::numeric)),
|
||||
CONSTRAINT recurrence_rule_services_disc_pct_chk CHECK (((discount_pct >= (0)::numeric) AND (discount_pct <= (100)::numeric))),
|
||||
CONSTRAINT recurrence_rule_services_final_price_chk CHECK ((final_price >= (0)::numeric)),
|
||||
CONSTRAINT recurrence_rule_services_quantity_chk CHECK ((quantity > 0))
|
||||
);
|
||||
|
||||
CREATE TABLE public.recurrence_rules (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
therapist_id uuid,
|
||||
patient_id uuid,
|
||||
determined_commitment_id uuid,
|
||||
type public.recurrence_type DEFAULT 'weekly'::public.recurrence_type NOT NULL,
|
||||
"interval" smallint DEFAULT 1 NOT NULL,
|
||||
weekdays smallint[] DEFAULT '{}'::smallint[] NOT NULL,
|
||||
start_time time without time zone NOT NULL,
|
||||
end_time time without time zone NOT NULL,
|
||||
timezone text DEFAULT 'America/Sao_Paulo'::text NOT NULL,
|
||||
duration_min smallint DEFAULT 50 NOT NULL,
|
||||
start_date date NOT NULL,
|
||||
end_date date,
|
||||
max_occurrences integer,
|
||||
open_ended boolean DEFAULT true NOT NULL,
|
||||
modalidade text DEFAULT 'presencial'::text,
|
||||
titulo_custom text,
|
||||
observacoes text,
|
||||
extra_fields jsonb,
|
||||
status text DEFAULT 'ativo'::text NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
price numeric(10,2),
|
||||
insurance_plan_id uuid,
|
||||
insurance_guide_number text,
|
||||
insurance_value numeric(10,2),
|
||||
insurance_plan_service_id uuid,
|
||||
CONSTRAINT recurrence_rules_dates_chk CHECK (((end_date IS NULL) OR (end_date >= start_date))),
|
||||
CONSTRAINT recurrence_rules_interval_chk CHECK (("interval" >= 1)),
|
||||
CONSTRAINT recurrence_rules_status_check CHECK ((status = ANY (ARRAY['ativo'::text, 'pausado'::text, 'cancelado'::text]))),
|
||||
CONSTRAINT recurrence_rules_times_chk CHECK ((end_time > start_time))
|
||||
);
|
||||
|
||||
CREATE TABLE public.therapist_payout_records (
|
||||
payout_id uuid NOT NULL,
|
||||
financial_record_id uuid NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.services (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
name text NOT NULL,
|
||||
description text,
|
||||
price numeric(10,2) NOT NULL,
|
||||
duration_min integer,
|
||||
active boolean DEFAULT true NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
updated_at timestamp with time zone DEFAULT now()
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.insurance_plan_services (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
insurance_plan_id uuid NOT NULL,
|
||||
name text NOT NULL,
|
||||
value numeric(10,2) NOT NULL,
|
||||
active boolean DEFAULT true NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
updated_at timestamp with time zone DEFAULT now()
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.insurance_plan_services OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: insurance_plans; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.insurance_plans (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
name text NOT NULL,
|
||||
notes text,
|
||||
default_value numeric(10,2),
|
||||
active boolean DEFAULT true NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
updated_at timestamp with time zone DEFAULT now()
|
||||
);
|
||||
|
||||
|
||||
@@ -1,500 +0,0 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Tables — Infraestrutura (realtime, storage, supabase_functions)
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE _realtime.extensions (
|
||||
id uuid NOT NULL,
|
||||
type text,
|
||||
settings jsonb,
|
||||
tenant_external_id text,
|
||||
inserted_at timestamp(0) without time zone NOT NULL,
|
||||
updated_at timestamp(0) without time zone NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE _realtime.extensions OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: schema_migrations; Type: TABLE; Schema: _realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE _realtime.schema_migrations (
|
||||
version bigint NOT NULL,
|
||||
inserted_at timestamp(0) without time zone
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE _realtime.schema_migrations OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: tenants; Type: TABLE; Schema: _realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE _realtime.tenants (
|
||||
id uuid NOT NULL,
|
||||
name text,
|
||||
external_id text,
|
||||
jwt_secret text,
|
||||
max_concurrent_users integer DEFAULT 200 NOT NULL,
|
||||
inserted_at timestamp(0) without time zone NOT NULL,
|
||||
updated_at timestamp(0) without time zone NOT NULL,
|
||||
max_events_per_second integer DEFAULT 100 NOT NULL,
|
||||
postgres_cdc_default text DEFAULT 'postgres_cdc_rls'::text,
|
||||
max_bytes_per_second integer DEFAULT 100000 NOT NULL,
|
||||
max_channels_per_client integer DEFAULT 100 NOT NULL,
|
||||
max_joins_per_second integer DEFAULT 500 NOT NULL,
|
||||
suspend boolean DEFAULT false,
|
||||
jwt_jwks jsonb,
|
||||
notify_private_alpha boolean DEFAULT false,
|
||||
private_only boolean DEFAULT false NOT NULL,
|
||||
migrations_ran integer DEFAULT 0,
|
||||
broadcast_adapter character varying(255) DEFAULT 'gen_rpc'::character varying,
|
||||
max_presence_events_per_second integer DEFAULT 1000,
|
||||
max_payload_size_in_kb integer DEFAULT 3000,
|
||||
CONSTRAINT jwt_secret_or_jwt_jwks_required CHECK (((jwt_secret IS NOT NULL) OR (jwt_jwks IS NOT NULL)))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE _realtime.tenants OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: audit_log_entries; Type: TABLE; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
|
||||
CREATE TABLE realtime.messages (
|
||||
topic text NOT NULL,
|
||||
extension text NOT NULL,
|
||||
payload jsonb,
|
||||
event text,
|
||||
private boolean DEFAULT false,
|
||||
updated_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
inserted_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL
|
||||
)
|
||||
PARTITION BY RANGE (inserted_at);
|
||||
|
||||
|
||||
ALTER TABLE realtime.messages OWNER TO supabase_realtime_admin;
|
||||
|
||||
--
|
||||
-- Name: messages_2026_03_20; Type: TABLE; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE realtime.messages_2026_03_20 (
|
||||
topic text NOT NULL,
|
||||
extension text NOT NULL,
|
||||
payload jsonb,
|
||||
event text,
|
||||
private boolean DEFAULT false,
|
||||
updated_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
inserted_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE realtime.messages_2026_03_20 OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: messages_2026_03_21; Type: TABLE; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE realtime.messages_2026_03_21 (
|
||||
topic text NOT NULL,
|
||||
extension text NOT NULL,
|
||||
payload jsonb,
|
||||
event text,
|
||||
private boolean DEFAULT false,
|
||||
updated_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
inserted_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE realtime.messages_2026_03_21 OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: messages_2026_03_22; Type: TABLE; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE realtime.messages_2026_03_22 (
|
||||
topic text NOT NULL,
|
||||
extension text NOT NULL,
|
||||
payload jsonb,
|
||||
event text,
|
||||
private boolean DEFAULT false,
|
||||
updated_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
inserted_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE realtime.messages_2026_03_22 OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: messages_2026_03_23; Type: TABLE; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE realtime.messages_2026_03_23 (
|
||||
topic text NOT NULL,
|
||||
extension text NOT NULL,
|
||||
payload jsonb,
|
||||
event text,
|
||||
private boolean DEFAULT false,
|
||||
updated_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
inserted_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE realtime.messages_2026_03_23 OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: messages_2026_03_24; Type: TABLE; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE realtime.messages_2026_03_24 (
|
||||
topic text NOT NULL,
|
||||
extension text NOT NULL,
|
||||
payload jsonb,
|
||||
event text,
|
||||
private boolean DEFAULT false,
|
||||
updated_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
inserted_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE realtime.messages_2026_03_24 OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: messages_2026_03_25; Type: TABLE; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE realtime.messages_2026_03_25 (
|
||||
topic text NOT NULL,
|
||||
extension text NOT NULL,
|
||||
payload jsonb,
|
||||
event text,
|
||||
private boolean DEFAULT false,
|
||||
updated_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
inserted_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE realtime.messages_2026_03_25 OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: messages_2026_03_26; Type: TABLE; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE realtime.messages_2026_03_26 (
|
||||
topic text NOT NULL,
|
||||
extension text NOT NULL,
|
||||
payload jsonb,
|
||||
event text,
|
||||
private boolean DEFAULT false,
|
||||
updated_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
inserted_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE realtime.messages_2026_03_26 OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: schema_migrations; Type: TABLE; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE realtime.schema_migrations (
|
||||
version bigint NOT NULL,
|
||||
inserted_at timestamp(0) without time zone
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE realtime.schema_migrations OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: subscription; Type: TABLE; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE realtime.subscription (
|
||||
id bigint NOT NULL,
|
||||
subscription_id uuid NOT NULL,
|
||||
entity regclass NOT NULL,
|
||||
filters realtime.user_defined_filter[] DEFAULT '{}'::realtime.user_defined_filter[] NOT NULL,
|
||||
claims jsonb NOT NULL,
|
||||
claims_role regrole GENERATED ALWAYS AS (realtime.to_regrole((claims ->> 'role'::text))) STORED NOT NULL,
|
||||
created_at timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE realtime.subscription OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: subscription_id_seq; Type: SEQUENCE; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
ALTER TABLE realtime.subscription ALTER COLUMN id ADD GENERATED ALWAYS AS IDENTITY (
|
||||
SEQUENCE NAME realtime.subscription_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: buckets; Type: TABLE; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
|
||||
CREATE TABLE storage.buckets (
|
||||
id text NOT NULL,
|
||||
name text NOT NULL,
|
||||
owner uuid,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
updated_at timestamp with time zone DEFAULT now(),
|
||||
public boolean DEFAULT false,
|
||||
avif_autodetection boolean DEFAULT false,
|
||||
file_size_limit bigint,
|
||||
allowed_mime_types text[],
|
||||
owner_id text,
|
||||
type storage.buckettype DEFAULT 'STANDARD'::storage.buckettype NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE storage.buckets OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: COLUMN buckets.owner; Type: COMMENT; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
COMMENT ON COLUMN storage.buckets.owner IS 'Field is deprecated, use owner_id instead';
|
||||
|
||||
|
||||
--
|
||||
-- Name: buckets_analytics; Type: TABLE; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE TABLE storage.buckets_analytics (
|
||||
name text NOT NULL,
|
||||
type storage.buckettype DEFAULT 'ANALYTICS'::storage.buckettype NOT NULL,
|
||||
format text DEFAULT 'ICEBERG'::text NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
deleted_at timestamp with time zone
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE storage.buckets_analytics OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: buckets_vectors; Type: TABLE; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE TABLE storage.buckets_vectors (
|
||||
id text NOT NULL,
|
||||
type storage.buckettype DEFAULT 'VECTOR'::storage.buckettype NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE storage.buckets_vectors OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: iceberg_namespaces; Type: TABLE; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE TABLE storage.iceberg_namespaces (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
bucket_name text NOT NULL,
|
||||
name text NOT NULL COLLATE pg_catalog."C",
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
metadata jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
catalog_id uuid NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE storage.iceberg_namespaces OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: iceberg_tables; Type: TABLE; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE TABLE storage.iceberg_tables (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
namespace_id uuid NOT NULL,
|
||||
bucket_name text NOT NULL,
|
||||
name text NOT NULL COLLATE pg_catalog."C",
|
||||
location text NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
remote_table_id text,
|
||||
shard_key text,
|
||||
shard_id text,
|
||||
catalog_id uuid NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE storage.iceberg_tables OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: migrations; Type: TABLE; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE TABLE storage.migrations (
|
||||
id integer NOT NULL,
|
||||
name character varying(100) NOT NULL,
|
||||
hash character varying(40) NOT NULL,
|
||||
executed_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE storage.migrations OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: objects; Type: TABLE; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE TABLE storage.objects (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
bucket_id text,
|
||||
name text,
|
||||
owner uuid,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
updated_at timestamp with time zone DEFAULT now(),
|
||||
last_accessed_at timestamp with time zone DEFAULT now(),
|
||||
metadata jsonb,
|
||||
path_tokens text[] GENERATED ALWAYS AS (string_to_array(name, '/'::text)) STORED,
|
||||
version text,
|
||||
owner_id text,
|
||||
user_metadata jsonb
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE storage.objects OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: COLUMN objects.owner; Type: COMMENT; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
COMMENT ON COLUMN storage.objects.owner IS 'Field is deprecated, use owner_id instead';
|
||||
|
||||
|
||||
--
|
||||
-- Name: s3_multipart_uploads; Type: TABLE; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE TABLE storage.s3_multipart_uploads (
|
||||
id text NOT NULL,
|
||||
in_progress_size bigint DEFAULT 0 NOT NULL,
|
||||
upload_signature text NOT NULL,
|
||||
bucket_id text NOT NULL,
|
||||
key text NOT NULL COLLATE pg_catalog."C",
|
||||
version text NOT NULL,
|
||||
owner_id text,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
user_metadata jsonb
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE storage.s3_multipart_uploads OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: s3_multipart_uploads_parts; Type: TABLE; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE TABLE storage.s3_multipart_uploads_parts (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
upload_id text NOT NULL,
|
||||
size bigint DEFAULT 0 NOT NULL,
|
||||
part_number integer NOT NULL,
|
||||
bucket_id text NOT NULL,
|
||||
key text NOT NULL COLLATE pg_catalog."C",
|
||||
etag text NOT NULL,
|
||||
owner_id text,
|
||||
version text NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE storage.s3_multipart_uploads_parts OWNER TO supabase_storage_admin;
|
||||
|
||||
--
|
||||
-- Name: vector_indexes; Type: TABLE; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE TABLE storage.vector_indexes (
|
||||
id text DEFAULT gen_random_uuid() NOT NULL,
|
||||
name text NOT NULL COLLATE pg_catalog."C",
|
||||
bucket_id text NOT NULL,
|
||||
data_type text NOT NULL,
|
||||
dimension integer NOT NULL,
|
||||
distance_metric text NOT NULL,
|
||||
metadata_configuration jsonb,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE supabase_functions.hooks (
|
||||
id bigint NOT NULL,
|
||||
hook_table_id integer NOT NULL,
|
||||
hook_name text NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
request_id bigint
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE supabase_functions.hooks OWNER TO supabase_functions_admin;
|
||||
|
||||
--
|
||||
-- Name: TABLE hooks; Type: COMMENT; Schema: supabase_functions; Owner: supabase_functions_admin
|
||||
--
|
||||
|
||||
COMMENT ON TABLE supabase_functions.hooks IS 'Supabase Functions Hooks: Audit trail for triggered hooks.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: hooks_id_seq; Type: SEQUENCE; Schema: supabase_functions; Owner: supabase_functions_admin
|
||||
--
|
||||
|
||||
CREATE SEQUENCE supabase_functions.hooks_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
ALTER SEQUENCE supabase_functions.hooks_id_seq OWNER TO supabase_functions_admin;
|
||||
|
||||
--
|
||||
-- Name: hooks_id_seq; Type: SEQUENCE OWNED BY; Schema: supabase_functions; Owner: supabase_functions_admin
|
||||
--
|
||||
|
||||
ALTER SEQUENCE supabase_functions.hooks_id_seq OWNED BY supabase_functions.hooks.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: migrations; Type: TABLE; Schema: supabase_functions; Owner: supabase_functions_admin
|
||||
--
|
||||
|
||||
CREATE TABLE supabase_functions.migrations (
|
||||
version text NOT NULL,
|
||||
inserted_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE supabase_functions.migrations OWNER TO supabase_functions_admin;
|
||||
|
||||
--
|
||||
-- Name: messages_2026_03_20; Type: TABLE ATTACH; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
@@ -0,0 +1,11 @@
|
||||
-- Tables: outros
|
||||
-- Gerado automaticamente em 2026-04-17T12:23:05.228Z
|
||||
-- Total: 1
|
||||
|
||||
CREATE TABLE public._db_migrations (
|
||||
id integer NOT NULL,
|
||||
filename text NOT NULL,
|
||||
hash text NOT NULL,
|
||||
category text DEFAULT 'migration'::text NOT NULL,
|
||||
applied_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
+199
-133
@@ -1,10 +1,177 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Tables — Pacientes
|
||||
-- =============================================================================
|
||||
-- patients, patient_groups, patient_group_patient, patient_tags,
|
||||
-- patient_patient_tag, patient_intake_requests, patient_invites,
|
||||
-- patient_discounts
|
||||
-- =============================================================================
|
||||
-- Tables: Pacientes
|
||||
-- Gerado automaticamente em 2026-04-17T12:23:05.230Z
|
||||
-- Total: 12
|
||||
|
||||
CREATE TABLE public.patient_contacts (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
patient_id uuid NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
nome text NOT NULL,
|
||||
tipo text NOT NULL,
|
||||
relacao text,
|
||||
telefone text,
|
||||
email text,
|
||||
cpf text,
|
||||
especialidade text,
|
||||
registro_profissional text,
|
||||
is_primario boolean DEFAULT false NOT NULL,
|
||||
ativo boolean DEFAULT true NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT patient_contacts_tipo_check CHECK ((tipo = ANY (ARRAY['emergencia'::text, 'responsavel_legal'::text, 'profissional_saude'::text, 'outro'::text])))
|
||||
);
|
||||
|
||||
CREATE TABLE public.patient_discounts (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
patient_id uuid NOT NULL,
|
||||
discount_pct numeric(5,2) DEFAULT 0,
|
||||
discount_flat numeric(10,2) DEFAULT 0,
|
||||
reason text,
|
||||
active boolean DEFAULT true NOT NULL,
|
||||
active_from timestamp with time zone DEFAULT now(),
|
||||
active_to timestamp with time zone,
|
||||
created_at timestamp with time zone DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE public.patient_group_patient (
|
||||
patient_group_id uuid NOT NULL,
|
||||
patient_id uuid NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
tenant_id uuid NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.patient_groups (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
nome text NOT NULL,
|
||||
descricao text,
|
||||
cor text,
|
||||
is_active boolean DEFAULT true NOT NULL,
|
||||
is_system boolean DEFAULT false NOT NULL,
|
||||
owner_id uuid DEFAULT auth.uid() NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
therapist_id uuid,
|
||||
tenant_id uuid NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.patient_intake_requests (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
token text NOT NULL,
|
||||
consent boolean DEFAULT false NOT NULL,
|
||||
status text DEFAULT 'new'::text NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
converted_patient_id uuid,
|
||||
rejected_reason text,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
cpf text,
|
||||
rg text,
|
||||
cep text,
|
||||
nome_completo text,
|
||||
email_principal text,
|
||||
telefone text,
|
||||
pais text,
|
||||
cidade text,
|
||||
estado text,
|
||||
endereco text,
|
||||
numero text,
|
||||
bairro text,
|
||||
complemento text,
|
||||
data_nascimento date,
|
||||
naturalidade text,
|
||||
genero text,
|
||||
estado_civil text,
|
||||
onde_nos_conheceu text,
|
||||
encaminhado_por text,
|
||||
observacoes text,
|
||||
notas_internas text,
|
||||
email_alternativo text,
|
||||
telefone_alternativo text,
|
||||
profissao text,
|
||||
escolaridade text,
|
||||
nacionalidade text,
|
||||
avatar_url text,
|
||||
tenant_id uuid,
|
||||
CONSTRAINT chk_intakes_status CHECK ((status = ANY (ARRAY['new'::text, 'converted'::text, 'rejected'::text])))
|
||||
);
|
||||
|
||||
CREATE TABLE public.patient_invites (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
token text NOT NULL,
|
||||
active boolean DEFAULT true NOT NULL,
|
||||
expires_at timestamp with time zone,
|
||||
max_uses integer,
|
||||
uses integer DEFAULT 0 NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
tenant_id uuid
|
||||
);
|
||||
|
||||
CREATE TABLE public.patient_patient_tag (
|
||||
owner_id uuid NOT NULL,
|
||||
patient_id uuid NOT NULL,
|
||||
tag_id uuid NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
tenant_id uuid NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.patient_status_history (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
patient_id uuid NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
status_anterior text,
|
||||
status_novo text NOT NULL,
|
||||
motivo text,
|
||||
encaminhado_para text,
|
||||
data_saida date,
|
||||
alterado_por uuid,
|
||||
alterado_em timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT psh_status_novo_check CHECK ((status_novo = ANY (ARRAY['Ativo'::text, 'Inativo'::text, 'Alta'::text, 'Encaminhado'::text, 'Arquivado'::text])))
|
||||
);
|
||||
|
||||
CREATE TABLE public.patient_support_contacts (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
patient_id uuid NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
nome text,
|
||||
relacao text,
|
||||
tipo text,
|
||||
telefone text,
|
||||
email text,
|
||||
is_primario boolean DEFAULT false NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
updated_at timestamp with time zone DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE public.patient_tags (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
nome text NOT NULL,
|
||||
cor text,
|
||||
is_padrao boolean DEFAULT false NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone,
|
||||
tenant_id uuid NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.patient_timeline (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
patient_id uuid NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
evento_tipo text NOT NULL,
|
||||
titulo text NOT NULL,
|
||||
descricao text,
|
||||
icone_cor text DEFAULT 'gray'::text,
|
||||
link_ref_tipo text,
|
||||
link_ref_id uuid,
|
||||
gerado_por uuid,
|
||||
ocorrido_em timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT pt_evento_tipo_check CHECK ((evento_tipo = ANY (ARRAY['primeira_sessao'::text, 'sessao_realizada'::text, 'sessao_cancelada'::text, 'falta'::text, 'status_alterado'::text, 'risco_sinalizado'::text, 'risco_removido'::text, 'documento_assinado'::text, 'documento_adicionado'::text, 'escala_respondida'::text, 'escala_enviada'::text, 'pagamento_vencido'::text, 'pagamento_recebido'::text, 'tarefa_combinada'::text, 'contato_adicionado'::text, 'prontuario_editado'::text, 'nota_adicionada'::text, 'manual'::text]))),
|
||||
CONSTRAINT pt_icone_cor_check CHECK ((icone_cor = ANY (ARRAY['green'::text, 'blue'::text, 'amber'::text, 'red'::text, 'gray'::text, 'purple'::text])))
|
||||
);
|
||||
|
||||
CREATE TABLE public.patients (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
@@ -54,132 +221,31 @@ CREATE TABLE public.patients (
|
||||
user_id uuid,
|
||||
patient_scope text DEFAULT 'clinic'::text NOT NULL,
|
||||
therapist_member_id uuid,
|
||||
nome_social text,
|
||||
pronomes text,
|
||||
etnia text,
|
||||
religiao text,
|
||||
faixa_renda text,
|
||||
canal_preferido text DEFAULT 'whatsapp'::text,
|
||||
horario_contato_inicio time without time zone DEFAULT '08:00:00'::time without time zone,
|
||||
horario_contato_fim time without time zone DEFAULT '20:00:00'::time without time zone,
|
||||
idioma text DEFAULT 'pt-BR'::text,
|
||||
origem text,
|
||||
metodo_pagamento_preferido text,
|
||||
motivo_saida text,
|
||||
data_saida date,
|
||||
encaminhado_para text,
|
||||
risco_elevado boolean DEFAULT false NOT NULL,
|
||||
risco_nota text,
|
||||
risco_sinalizado_em timestamp with time zone,
|
||||
risco_sinalizado_por uuid,
|
||||
horario_contato text,
|
||||
convenio text,
|
||||
convenio_id uuid,
|
||||
CONSTRAINT cpf_responsavel_format_check CHECK (((cpf_responsavel IS NULL) OR (cpf_responsavel ~ '^\d{11}$'::text))),
|
||||
CONSTRAINT patients_cpf_format_check CHECK (((cpf IS NULL) OR (cpf ~ '^\d{11}$'::text))),
|
||||
CONSTRAINT patients_patient_scope_check CHECK ((patient_scope = ANY (ARRAY['clinic'::text, 'therapist'::text]))),
|
||||
CONSTRAINT patients_status_check CHECK ((status = ANY (ARRAY['Ativo'::text, 'Inativo'::text, 'Alta'::text, 'Encaminhado'::text, 'Arquivado'::text]))),
|
||||
CONSTRAINT patients_therapist_scope_consistency CHECK ((((patient_scope = 'clinic'::text) AND (therapist_member_id IS NULL)) OR ((patient_scope = 'therapist'::text) AND (therapist_member_id IS NOT NULL))))
|
||||
CONSTRAINT patients_faixa_renda_check CHECK (((faixa_renda IS NULL) OR (faixa_renda = ANY (ARRAY['ate_1sm'::text, '1_3sm'::text, '3_6sm'::text, '6_10sm'::text, 'acima_10sm'::text, 'nao_informado'::text])))),
|
||||
CONSTRAINT patients_metodo_pagamento_check CHECK (((metodo_pagamento_preferido IS NULL) OR (metodo_pagamento_preferido = ANY (ARRAY['pix'::text, 'cartao'::text, 'dinheiro'::text, 'deposito'::text, 'convenio'::text])))),
|
||||
CONSTRAINT patients_risco_consistency_check CHECK (((risco_elevado = false) OR ((risco_elevado = true) AND (risco_nota IS NOT NULL) AND (risco_sinalizado_por IS NOT NULL)))),
|
||||
CONSTRAINT patients_status_check CHECK ((status = ANY (ARRAY['Ativo'::text, 'Em espera'::text, 'Inativo'::text, 'Alta'::text, 'Encaminhado'::text, 'Arquivado'::text])))
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.patient_groups (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
nome text NOT NULL,
|
||||
descricao text,
|
||||
cor text,
|
||||
is_active boolean DEFAULT true NOT NULL,
|
||||
is_system boolean DEFAULT false NOT NULL,
|
||||
owner_id uuid DEFAULT auth.uid() NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
therapist_id uuid,
|
||||
tenant_id uuid NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.patient_group_patient (
|
||||
patient_group_id uuid NOT NULL,
|
||||
patient_id uuid NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
tenant_id uuid NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.patient_tags (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
nome text NOT NULL,
|
||||
cor text,
|
||||
is_padrao boolean DEFAULT false NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone,
|
||||
tenant_id uuid NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.patient_patient_tag (
|
||||
owner_id uuid NOT NULL,
|
||||
patient_id uuid NOT NULL,
|
||||
tag_id uuid NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
tenant_id uuid NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.patient_intake_requests (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
token text NOT NULL,
|
||||
consent boolean DEFAULT false NOT NULL,
|
||||
status text DEFAULT 'new'::text NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
converted_patient_id uuid,
|
||||
rejected_reason text,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
cpf text,
|
||||
rg text,
|
||||
cep text,
|
||||
nome_completo text,
|
||||
email_principal text,
|
||||
telefone text,
|
||||
pais text,
|
||||
cidade text,
|
||||
estado text,
|
||||
endereco text,
|
||||
numero text,
|
||||
bairro text,
|
||||
complemento text,
|
||||
data_nascimento date,
|
||||
naturalidade text,
|
||||
genero text,
|
||||
estado_civil text,
|
||||
onde_nos_conheceu text,
|
||||
encaminhado_por text,
|
||||
observacoes text,
|
||||
notas_internas text,
|
||||
email_alternativo text,
|
||||
telefone_alternativo text,
|
||||
profissao text,
|
||||
escolaridade text,
|
||||
nacionalidade text,
|
||||
avatar_url text,
|
||||
tenant_id uuid,
|
||||
CONSTRAINT chk_intakes_status CHECK ((status = ANY (ARRAY['new'::text, 'converted'::text, 'rejected'::text])))
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.patient_invites (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
token text NOT NULL,
|
||||
active boolean DEFAULT true NOT NULL,
|
||||
expires_at timestamp with time zone,
|
||||
max_uses integer,
|
||||
uses integer DEFAULT 0 NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
tenant_id uuid
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.patient_discounts (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
patient_id uuid NOT NULL,
|
||||
discount_pct numeric(5,2) DEFAULT 0,
|
||||
discount_flat numeric(10,2) DEFAULT 0,
|
||||
reason text,
|
||||
active boolean DEFAULT true NOT NULL,
|
||||
active_from timestamp with time zone DEFAULT now(),
|
||||
active_to timestamp with time zone,
|
||||
created_at timestamp with time zone DEFAULT now()
|
||||
);
|
||||
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Tables — SaaS Admin, FAQ, Docs, UI
|
||||
-- =============================================================================
|
||||
-- saas_docs, saas_doc_votos, saas_faq, saas_faq_itens,
|
||||
-- feriados, global_notices, login_carousel_slides, notice_dismissals,
|
||||
-- support_sessions
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE public.saas_doc_votos (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
doc_id uuid NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
util boolean NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.saas_doc_votos OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: saas_docs; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.saas_docs (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
titulo text NOT NULL,
|
||||
conteudo text DEFAULT ''::text NOT NULL,
|
||||
medias jsonb DEFAULT '[]'::jsonb NOT NULL,
|
||||
tipo_acesso text DEFAULT 'usuario'::text NOT NULL,
|
||||
pagina_path text NOT NULL,
|
||||
docs_relacionados uuid[] DEFAULT '{}'::uuid[] NOT NULL,
|
||||
ativo boolean DEFAULT true NOT NULL,
|
||||
ordem integer DEFAULT 0 NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
categoria text,
|
||||
exibir_no_faq boolean DEFAULT false NOT NULL,
|
||||
votos_util integer DEFAULT 0 NOT NULL,
|
||||
votos_nao_util integer DEFAULT 0 NOT NULL,
|
||||
CONSTRAINT saas_docs_tipo_acesso_check CHECK ((tipo_acesso = ANY (ARRAY['admin'::text, 'usuario'::text])))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.saas_docs OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: COLUMN saas_docs.categoria; Type: COMMENT; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
COMMENT ON COLUMN public.saas_docs.categoria IS 'Agrupa docs no portal FAQ (ex: Conta, Agenda, Pagamentos)';
|
||||
|
||||
|
||||
--
|
||||
-- Name: COLUMN saas_docs.exibir_no_faq; Type: COMMENT; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
COMMENT ON COLUMN public.saas_docs.exibir_no_faq IS 'Se true, a doc e seus itens FAQ aparecem no portal de FAQ';
|
||||
|
||||
|
||||
--
|
||||
-- Name: saas_faq; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.saas_faq (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
pergunta text NOT NULL,
|
||||
categoria text,
|
||||
publico boolean DEFAULT false NOT NULL,
|
||||
votos integer DEFAULT 0 NOT NULL,
|
||||
titulo text,
|
||||
conteudo text,
|
||||
tipo_acesso text DEFAULT 'usuario'::text NOT NULL,
|
||||
pagina_path text NOT NULL,
|
||||
pagina_label text,
|
||||
medias jsonb DEFAULT '[]'::jsonb NOT NULL,
|
||||
faqs_relacionados uuid[] DEFAULT '{}'::uuid[] NOT NULL,
|
||||
ativo boolean DEFAULT true NOT NULL,
|
||||
ordem integer DEFAULT 0 NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.saas_faq OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: saas_faq_itens; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.saas_faq_itens (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
doc_id uuid NOT NULL,
|
||||
pergunta text NOT NULL,
|
||||
resposta text,
|
||||
ordem integer DEFAULT 0 NOT NULL,
|
||||
ativo boolean DEFAULT true NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.saas_faq_itens OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: TABLE saas_faq_itens; Type: COMMENT; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
COMMENT ON TABLE public.saas_faq_itens IS 'Pares pergunta/resposta vinculados a um documento de ajuda';
|
||||
|
||||
|
||||
--
|
||||
-- Name: services; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
|
||||
CREATE TABLE public.feriados (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid,
|
||||
owner_id uuid,
|
||||
tipo text DEFAULT 'municipal'::text NOT NULL,
|
||||
nome text NOT NULL,
|
||||
data date NOT NULL,
|
||||
cidade text,
|
||||
estado text,
|
||||
observacao text,
|
||||
bloqueia_sessoes boolean DEFAULT false NOT NULL,
|
||||
criado_em timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT feriados_tipo_check CHECK ((tipo = ANY (ARRAY['municipal'::text, 'personalizado'::text])))
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.global_notices (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
title text,
|
||||
message text DEFAULT ''::text NOT NULL,
|
||||
variant text DEFAULT 'info'::text NOT NULL,
|
||||
roles text[] DEFAULT '{}'::text[] NOT NULL,
|
||||
contexts text[] DEFAULT '{}'::text[] NOT NULL,
|
||||
starts_at timestamp with time zone,
|
||||
ends_at timestamp with time zone,
|
||||
is_active boolean DEFAULT true NOT NULL,
|
||||
priority integer DEFAULT 0 NOT NULL,
|
||||
dismissible boolean DEFAULT true NOT NULL,
|
||||
persist_dismiss boolean DEFAULT true NOT NULL,
|
||||
dismiss_scope text DEFAULT 'device'::text NOT NULL,
|
||||
show_once boolean DEFAULT false NOT NULL,
|
||||
max_views integer,
|
||||
cooldown_minutes integer,
|
||||
version integer DEFAULT 1 NOT NULL,
|
||||
action_type text DEFAULT 'none'::text NOT NULL,
|
||||
action_label text,
|
||||
action_url text,
|
||||
action_route text,
|
||||
views_count integer DEFAULT 0 NOT NULL,
|
||||
clicks_count integer DEFAULT 0 NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
created_by uuid,
|
||||
content_align text DEFAULT 'left'::text NOT NULL,
|
||||
link_target text DEFAULT '_blank'::text NOT NULL,
|
||||
CONSTRAINT global_notices_action_type_check CHECK ((action_type = ANY (ARRAY['none'::text, 'internal'::text, 'external'::text]))),
|
||||
CONSTRAINT global_notices_content_align_check CHECK ((content_align = ANY (ARRAY['left'::text, 'center'::text, 'right'::text, 'justify'::text]))),
|
||||
CONSTRAINT global_notices_dismiss_scope_check CHECK ((dismiss_scope = ANY (ARRAY['session'::text, 'device'::text, 'user'::text]))),
|
||||
CONSTRAINT global_notices_link_target_check CHECK ((link_target = ANY (ARRAY['_blank'::text, '_self'::text, '_parent'::text, '_top'::text]))),
|
||||
CONSTRAINT global_notices_variant_check CHECK ((variant = ANY (ARRAY['info'::text, 'success'::text, 'warning'::text, 'error'::text])))
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.login_carousel_slides (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
title text NOT NULL,
|
||||
body text NOT NULL,
|
||||
icon text DEFAULT 'pi-star'::text NOT NULL,
|
||||
ordem integer DEFAULT 0 NOT NULL,
|
||||
ativo boolean DEFAULT true NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
updated_at timestamp with time zone DEFAULT now()
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.notice_dismissals (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
notice_id uuid NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
version integer DEFAULT 1 NOT NULL,
|
||||
dismissed_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.support_sessions (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
admin_id uuid NOT NULL,
|
||||
token text DEFAULT encode(extensions.gen_random_bytes(32), 'hex'::text) NOT NULL,
|
||||
expires_at timestamp with time zone DEFAULT (now() + '01:00:00'::interval) NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
+166
-281
@@ -1,115 +1,6 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Tables — Plans, Billing, Subscriptions
|
||||
-- =============================================================================
|
||||
-- plans, plan_prices, plan_features, plan_public, plan_public_bullets,
|
||||
-- features, entitlements_invalidation, subscriptions, subscription_events,
|
||||
-- subscription_intents_personal, subscription_intents_tenant,
|
||||
-- subscription_intents_legacy, billing_contracts,
|
||||
-- addon_credits, addon_products, addon_transactions,
|
||||
-- modules, module_features, tenant_modules
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE public.plans (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
key text NOT NULL,
|
||||
name text NOT NULL,
|
||||
description text,
|
||||
is_active boolean DEFAULT true NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
price_cents integer DEFAULT 0 NOT NULL,
|
||||
currency text DEFAULT 'BRL'::text NOT NULL,
|
||||
billing_interval text DEFAULT 'month'::text NOT NULL,
|
||||
target text,
|
||||
max_supervisees integer,
|
||||
CONSTRAINT plans_target_check CHECK ((target = ANY (ARRAY['patient'::text, 'therapist'::text, 'clinic'::text, 'supervisor'::text])))
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.plan_prices (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
plan_id uuid NOT NULL,
|
||||
currency text DEFAULT 'BRL'::text NOT NULL,
|
||||
"interval" text NOT NULL,
|
||||
amount_cents integer NOT NULL,
|
||||
is_active boolean DEFAULT true NOT NULL,
|
||||
active_from timestamp with time zone DEFAULT now() NOT NULL,
|
||||
active_to timestamp with time zone,
|
||||
source text DEFAULT 'manual'::text NOT NULL,
|
||||
provider text,
|
||||
provider_price_id text,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT plan_prices_amount_cents_check CHECK ((amount_cents >= 0)),
|
||||
CONSTRAINT plan_prices_interval_check CHECK (("interval" = ANY (ARRAY['month'::text, 'year'::text])))
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.plan_features (
|
||||
plan_id uuid NOT NULL,
|
||||
feature_id uuid NOT NULL,
|
||||
enabled boolean DEFAULT true NOT NULL,
|
||||
limits jsonb,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.plan_public (
|
||||
plan_id uuid NOT NULL,
|
||||
public_name text DEFAULT ''::text NOT NULL,
|
||||
public_description text DEFAULT ''::text NOT NULL,
|
||||
badge text,
|
||||
is_featured boolean DEFAULT false NOT NULL,
|
||||
is_visible boolean DEFAULT true NOT NULL,
|
||||
sort_order integer DEFAULT 0 NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.plan_public_bullets (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
plan_id uuid NOT NULL,
|
||||
text text NOT NULL,
|
||||
sort_order integer DEFAULT 0 NOT NULL,
|
||||
highlight boolean DEFAULT false NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.features (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
key text NOT NULL,
|
||||
description text,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
descricao text DEFAULT ''::text NOT NULL,
|
||||
name text DEFAULT ''::text NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.features OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: COLUMN features.descricao; Type: COMMENT; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
COMMENT ON COLUMN public.features.descricao IS 'Descrição humana da feature (exibição no admin e documentação).';
|
||||
|
||||
|
||||
--
|
||||
-- Name: feriados; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
|
||||
CREATE TABLE public.entitlements_invalidation (
|
||||
owner_id uuid NOT NULL,
|
||||
changed_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
-- Tables: SaaS / Planos
|
||||
-- Gerado automaticamente em 2026-04-17T12:23:05.227Z
|
||||
-- Total: 18
|
||||
|
||||
CREATE TABLE public.subscriptions (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
@@ -142,92 +33,6 @@ CREATE TABLE public.subscriptions (
|
||||
CONSTRAINT subscriptions_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'active'::text, 'past_due'::text, 'suspended'::text, 'cancelled'::text, 'expired'::text])))
|
||||
);
|
||||
|
||||
CREATE TABLE public.subscription_events (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
subscription_id uuid NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
event_type text NOT NULL,
|
||||
old_plan_id uuid,
|
||||
new_plan_id uuid,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
created_by uuid,
|
||||
source text DEFAULT 'admin_ui'::text,
|
||||
reason text,
|
||||
metadata jsonb,
|
||||
owner_type text NOT NULL,
|
||||
owner_ref uuid NOT NULL,
|
||||
CONSTRAINT subscription_events_owner_ref_consistency_chk CHECK ((owner_id = owner_ref)),
|
||||
CONSTRAINT subscription_events_owner_type_chk CHECK (((owner_type IS NULL) OR (owner_type = ANY (ARRAY['clinic'::text, 'therapist'::text]))))
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.subscription_intents_personal (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
created_by_user_id uuid,
|
||||
email text NOT NULL,
|
||||
plan_id uuid NOT NULL,
|
||||
plan_key text,
|
||||
"interval" text,
|
||||
amount_cents integer,
|
||||
currency text,
|
||||
status text DEFAULT 'new'::text NOT NULL,
|
||||
source text DEFAULT 'manual'::text NOT NULL,
|
||||
notes text,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
paid_at timestamp with time zone,
|
||||
subscription_id uuid,
|
||||
CONSTRAINT sint_personal_interval_check CHECK ((("interval" IS NULL) OR ("interval" = ANY (ARRAY['month'::text, 'year'::text])))),
|
||||
CONSTRAINT sint_personal_status_check CHECK ((status = ANY (ARRAY['new'::text, 'waiting_payment'::text, 'paid'::text, 'canceled'::text])))
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.subscription_intents_tenant (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
created_by_user_id uuid,
|
||||
email text NOT NULL,
|
||||
plan_id uuid NOT NULL,
|
||||
plan_key text,
|
||||
"interval" text,
|
||||
amount_cents integer,
|
||||
currency text,
|
||||
status text DEFAULT 'new'::text NOT NULL,
|
||||
source text DEFAULT 'manual'::text NOT NULL,
|
||||
notes text,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
paid_at timestamp with time zone,
|
||||
tenant_id uuid NOT NULL,
|
||||
subscription_id uuid,
|
||||
CONSTRAINT sint_tenant_interval_check CHECK ((("interval" IS NULL) OR ("interval" = ANY (ARRAY['month'::text, 'year'::text])))),
|
||||
CONSTRAINT sint_tenant_status_check CHECK ((status = ANY (ARRAY['new'::text, 'waiting_payment'::text, 'paid'::text, 'canceled'::text])))
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.subscription_intents_legacy (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
user_id uuid,
|
||||
email text,
|
||||
plan_key text NOT NULL,
|
||||
"interval" text NOT NULL,
|
||||
amount_cents integer NOT NULL,
|
||||
currency text DEFAULT 'BRL'::text NOT NULL,
|
||||
status text DEFAULT 'new'::text NOT NULL,
|
||||
source text DEFAULT 'landing'::text NOT NULL,
|
||||
notes text,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
paid_at timestamp with time zone,
|
||||
tenant_id uuid NOT NULL,
|
||||
created_by_user_id uuid,
|
||||
CONSTRAINT subscription_intents_interval_check CHECK (("interval" = ANY (ARRAY['month'::text, 'year'::text]))),
|
||||
CONSTRAINT subscription_intents_status_check CHECK ((status = ANY (ARRAY['new'::text, 'waiting_payment'::text, 'paid'::text, 'canceled'::text])))
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.billing_contracts (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
@@ -250,90 +55,28 @@ CREATE TABLE public.billing_contracts (
|
||||
CONSTRAINT billing_contracts_type_chk CHECK ((type = ANY (ARRAY['per_session'::text, 'package'::text, 'subscription'::text])))
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.addon_credits (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
owner_id uuid,
|
||||
addon_type text NOT NULL,
|
||||
balance integer DEFAULT 0 NOT NULL,
|
||||
total_purchased integer DEFAULT 0 NOT NULL,
|
||||
total_consumed integer DEFAULT 0 NOT NULL,
|
||||
low_balance_threshold integer DEFAULT 10,
|
||||
low_balance_notified boolean DEFAULT false,
|
||||
daily_limit integer,
|
||||
hourly_limit integer,
|
||||
daily_used integer DEFAULT 0,
|
||||
hourly_used integer DEFAULT 0,
|
||||
daily_reset_at timestamp with time zone,
|
||||
hourly_reset_at timestamp with time zone,
|
||||
from_number_override text,
|
||||
expires_at timestamp with time zone,
|
||||
is_active boolean DEFAULT true,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
updated_at timestamp with time zone DEFAULT now()
|
||||
CREATE TABLE public.entitlements_invalidation (
|
||||
owner_id uuid NOT NULL,
|
||||
changed_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.addon_products (
|
||||
CREATE TABLE public.features (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
slug text NOT NULL,
|
||||
name text NOT NULL,
|
||||
key text NOT NULL,
|
||||
description text,
|
||||
addon_type text NOT NULL,
|
||||
icon text DEFAULT 'pi pi-box'::text,
|
||||
credits_amount integer DEFAULT 0,
|
||||
price_cents integer DEFAULT 0 NOT NULL,
|
||||
currency text DEFAULT 'BRL'::text,
|
||||
is_active boolean DEFAULT true,
|
||||
is_visible boolean DEFAULT true,
|
||||
sort_order integer DEFAULT 0,
|
||||
metadata jsonb DEFAULT '{}'::jsonb,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
updated_at timestamp with time zone DEFAULT now(),
|
||||
deleted_at timestamp with time zone
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
descricao text DEFAULT ''::text NOT NULL,
|
||||
name text DEFAULT ''::text NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.addon_transactions (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
owner_id uuid,
|
||||
addon_type text NOT NULL,
|
||||
type text NOT NULL,
|
||||
amount integer NOT NULL,
|
||||
balance_before integer DEFAULT 0 NOT NULL,
|
||||
balance_after integer DEFAULT 0 NOT NULL,
|
||||
product_id uuid,
|
||||
queue_id uuid,
|
||||
description text,
|
||||
admin_user_id uuid,
|
||||
payment_method text,
|
||||
payment_reference text,
|
||||
price_cents integer,
|
||||
currency text DEFAULT 'BRL'::text,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
metadata jsonb DEFAULT '{}'::jsonb
|
||||
CREATE TABLE public.module_features (
|
||||
module_id uuid NOT NULL,
|
||||
feature_id uuid NOT NULL,
|
||||
enabled boolean DEFAULT true NOT NULL,
|
||||
limits jsonb,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.addon_transactions OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: TABLE addon_transactions; Type: COMMENT; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
COMMENT ON TABLE public.addon_transactions IS 'Histórico de todas as transações de créditos: compras, consumo, ajustes, reembolsos.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: agenda_bloqueios; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
|
||||
CREATE TABLE public.modules (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
key text NOT NULL,
|
||||
@@ -343,18 +86,14 @@ CREATE TABLE public.modules (
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.module_features (
|
||||
module_id uuid NOT NULL,
|
||||
CREATE TABLE public.plan_features (
|
||||
plan_id uuid NOT NULL,
|
||||
feature_id uuid NOT NULL,
|
||||
enabled boolean DEFAULT true NOT NULL,
|
||||
limits jsonb,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.tenant_modules (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
@@ -367,5 +106,151 @@ CREATE TABLE public.tenant_modules (
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.plan_prices (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
plan_id uuid NOT NULL,
|
||||
currency text DEFAULT 'BRL'::text NOT NULL,
|
||||
"interval" text NOT NULL,
|
||||
amount_cents integer NOT NULL,
|
||||
is_active boolean DEFAULT true NOT NULL,
|
||||
active_from timestamp with time zone DEFAULT now() NOT NULL,
|
||||
active_to timestamp with time zone,
|
||||
source text DEFAULT 'manual'::text NOT NULL,
|
||||
provider text,
|
||||
provider_price_id text,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT plan_prices_amount_cents_check CHECK ((amount_cents >= 0)),
|
||||
CONSTRAINT plan_prices_interval_check CHECK (("interval" = ANY (ARRAY['month'::text, 'year'::text])))
|
||||
);
|
||||
|
||||
ALTER TABLE public.tenant_modules OWNER TO supabase_admin;
|
||||
CREATE TABLE public.plan_public (
|
||||
plan_id uuid NOT NULL,
|
||||
public_name text DEFAULT ''::text NOT NULL,
|
||||
public_description text DEFAULT ''::text NOT NULL,
|
||||
badge text,
|
||||
is_featured boolean DEFAULT false NOT NULL,
|
||||
is_visible boolean DEFAULT true NOT NULL,
|
||||
sort_order integer DEFAULT 0 NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.plan_public_bullets (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
plan_id uuid NOT NULL,
|
||||
text text NOT NULL,
|
||||
sort_order integer DEFAULT 0 NOT NULL,
|
||||
highlight boolean DEFAULT false NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.plans (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
key text NOT NULL,
|
||||
name text NOT NULL,
|
||||
description text,
|
||||
is_active boolean DEFAULT true NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
price_cents integer DEFAULT 0 NOT NULL,
|
||||
currency text DEFAULT 'BRL'::text NOT NULL,
|
||||
billing_interval text DEFAULT 'month'::text NOT NULL,
|
||||
target text,
|
||||
max_supervisees integer,
|
||||
CONSTRAINT plans_target_check CHECK ((target = ANY (ARRAY['patient'::text, 'therapist'::text, 'clinic'::text, 'supervisor'::text])))
|
||||
);
|
||||
|
||||
CREATE TABLE public.subscription_events (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
subscription_id uuid NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
event_type text NOT NULL,
|
||||
old_plan_id uuid,
|
||||
new_plan_id uuid,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
created_by uuid,
|
||||
source text DEFAULT 'admin_ui'::text,
|
||||
reason text,
|
||||
metadata jsonb,
|
||||
owner_type text NOT NULL,
|
||||
owner_ref uuid NOT NULL,
|
||||
CONSTRAINT subscription_events_owner_ref_consistency_chk CHECK ((owner_id = owner_ref)),
|
||||
CONSTRAINT subscription_events_owner_type_chk CHECK (((owner_type IS NULL) OR (owner_type = ANY (ARRAY['clinic'::text, 'therapist'::text]))))
|
||||
);
|
||||
|
||||
CREATE TABLE public.subscription_intents_personal (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
created_by_user_id uuid,
|
||||
email text NOT NULL,
|
||||
plan_id uuid NOT NULL,
|
||||
plan_key text,
|
||||
"interval" text,
|
||||
amount_cents integer,
|
||||
currency text,
|
||||
status text DEFAULT 'new'::text NOT NULL,
|
||||
source text DEFAULT 'manual'::text NOT NULL,
|
||||
notes text,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
paid_at timestamp with time zone,
|
||||
subscription_id uuid,
|
||||
CONSTRAINT sint_personal_interval_check CHECK ((("interval" IS NULL) OR ("interval" = ANY (ARRAY['month'::text, 'year'::text])))),
|
||||
CONSTRAINT sint_personal_status_check CHECK ((status = ANY (ARRAY['new'::text, 'waiting_payment'::text, 'paid'::text, 'canceled'::text])))
|
||||
);
|
||||
|
||||
CREATE TABLE public.subscription_intents_tenant (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
created_by_user_id uuid,
|
||||
email text NOT NULL,
|
||||
plan_id uuid NOT NULL,
|
||||
plan_key text,
|
||||
"interval" text,
|
||||
amount_cents integer,
|
||||
currency text,
|
||||
status text DEFAULT 'new'::text NOT NULL,
|
||||
source text DEFAULT 'manual'::text NOT NULL,
|
||||
notes text,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
paid_at timestamp with time zone,
|
||||
tenant_id uuid NOT NULL,
|
||||
subscription_id uuid,
|
||||
CONSTRAINT sint_tenant_interval_check CHECK ((("interval" IS NULL) OR ("interval" = ANY (ARRAY['month'::text, 'year'::text])))),
|
||||
CONSTRAINT sint_tenant_status_check CHECK ((status = ANY (ARRAY['new'::text, 'waiting_payment'::text, 'paid'::text, 'canceled'::text])))
|
||||
);
|
||||
|
||||
CREATE TABLE public.subscription_intents_legacy (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
user_id uuid,
|
||||
email text,
|
||||
plan_key text NOT NULL,
|
||||
"interval" text NOT NULL,
|
||||
amount_cents integer NOT NULL,
|
||||
currency text DEFAULT 'BRL'::text NOT NULL,
|
||||
status text DEFAULT 'new'::text NOT NULL,
|
||||
source text DEFAULT 'landing'::text NOT NULL,
|
||||
notes text,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
paid_at timestamp with time zone,
|
||||
tenant_id uuid NOT NULL,
|
||||
created_by_user_id uuid,
|
||||
CONSTRAINT subscription_intents_interval_check CHECK (("interval" = ANY (ARRAY['month'::text, 'year'::text]))),
|
||||
CONSTRAINT subscription_intents_status_check CHECK ((status = ANY (ARRAY['new'::text, 'waiting_payment'::text, 'paid'::text, 'canceled'::text])))
|
||||
);
|
||||
|
||||
CREATE TABLE public.tenant_feature_exceptions_log (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
feature_key text NOT NULL,
|
||||
enabled boolean NOT NULL,
|
||||
reason text,
|
||||
created_by uuid,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.tenant_features (
|
||||
tenant_id uuid NOT NULL,
|
||||
feature_key text NOT NULL,
|
||||
enabled boolean DEFAULT false NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
+84
-46
@@ -1,42 +1,6 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Tables — Compromissos Determinados
|
||||
-- =============================================================================
|
||||
-- determined_commitments, determined_commitment_fields,
|
||||
-- commitment_services, commitment_time_logs
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE public.determined_commitments (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
created_by uuid,
|
||||
is_native boolean DEFAULT false NOT NULL,
|
||||
native_key text,
|
||||
is_locked boolean DEFAULT false NOT NULL,
|
||||
active boolean DEFAULT true NOT NULL,
|
||||
name text NOT NULL,
|
||||
description text,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
bg_color text,
|
||||
text_color text
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.determined_commitment_fields (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
commitment_id uuid NOT NULL,
|
||||
key text NOT NULL,
|
||||
label text NOT NULL,
|
||||
field_type public.determined_field_type DEFAULT 'text'::public.determined_field_type NOT NULL,
|
||||
required boolean DEFAULT false NOT NULL,
|
||||
sort_order integer DEFAULT 0 NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
-- Tables: Serviços / Prontuários
|
||||
-- Gerado automaticamente em 2026-04-17T12:23:05.229Z
|
||||
-- Total: 8
|
||||
|
||||
CREATE TABLE public.commitment_services (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
@@ -54,13 +18,6 @@ CREATE TABLE public.commitment_services (
|
||||
CONSTRAINT commitment_services_quantity_chk CHECK ((quantity > 0))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.commitment_services OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: commitment_time_logs; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TABLE public.commitment_time_logs (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
@@ -74,4 +31,85 @@ CREATE TABLE public.commitment_time_logs (
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.determined_commitment_fields (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
commitment_id uuid NOT NULL,
|
||||
key text NOT NULL,
|
||||
label text NOT NULL,
|
||||
field_type public.determined_field_type DEFAULT 'text'::public.determined_field_type NOT NULL,
|
||||
required boolean DEFAULT false NOT NULL,
|
||||
sort_order integer DEFAULT 0 NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.determined_commitments (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
created_by uuid,
|
||||
is_native boolean DEFAULT false NOT NULL,
|
||||
native_key text,
|
||||
is_locked boolean DEFAULT false NOT NULL,
|
||||
active boolean DEFAULT true NOT NULL,
|
||||
name text NOT NULL,
|
||||
description text,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
bg_color text,
|
||||
text_color text
|
||||
);
|
||||
|
||||
CREATE TABLE public.insurance_plan_services (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
insurance_plan_id uuid NOT NULL,
|
||||
name text NOT NULL,
|
||||
value numeric(10,2) NOT NULL,
|
||||
active boolean DEFAULT true NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
updated_at timestamp with time zone DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE public.insurance_plans (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
name text NOT NULL,
|
||||
notes text,
|
||||
default_value numeric(10,2),
|
||||
active boolean DEFAULT true NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
updated_at timestamp with time zone DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE public.medicos (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
nome text NOT NULL,
|
||||
crm text,
|
||||
especialidade text,
|
||||
telefone_profissional text,
|
||||
telefone_pessoal text,
|
||||
email text,
|
||||
clinica text,
|
||||
cidade text,
|
||||
estado text DEFAULT 'SP'::text,
|
||||
observacoes text,
|
||||
ativo boolean DEFAULT true NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
updated_at timestamp with time zone DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE public.services (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
owner_id uuid NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
name text NOT NULL,
|
||||
description text,
|
||||
price numeric(10,2) NOT NULL,
|
||||
duration_min integer,
|
||||
active boolean DEFAULT true NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
updated_at timestamp with time zone DEFAULT now()
|
||||
);
|
||||
+92
-125
@@ -1,51 +1,6 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Tables — Core
|
||||
-- =============================================================================
|
||||
-- profiles, tenants, tenant_members, tenant_invites, tenant_features,
|
||||
-- tenant_feature_exceptions_log, saas_admins, owner_users, user_settings,
|
||||
-- company_profiles, dev_user_credentials
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE public.profiles (
|
||||
id uuid NOT NULL,
|
||||
role text DEFAULT 'tenant_member'::text NOT NULL,
|
||||
full_name text,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
avatar_url text,
|
||||
phone text,
|
||||
bio text,
|
||||
language text DEFAULT 'pt-BR'::text NOT NULL,
|
||||
timezone text DEFAULT 'America/Sao_Paulo'::text NOT NULL,
|
||||
notify_system_email boolean DEFAULT true NOT NULL,
|
||||
notify_reminders boolean DEFAULT true NOT NULL,
|
||||
notify_news boolean DEFAULT false NOT NULL,
|
||||
account_type text DEFAULT 'free'::text NOT NULL,
|
||||
platform_roles text[] DEFAULT '{}'::text[] NOT NULL,
|
||||
nickname text,
|
||||
work_description text,
|
||||
work_description_other text,
|
||||
site_url text,
|
||||
social_instagram text,
|
||||
social_youtube text,
|
||||
social_facebook text,
|
||||
social_x text,
|
||||
social_custom jsonb DEFAULT '[]'::jsonb NOT NULL,
|
||||
CONSTRAINT profiles_account_type_check CHECK ((account_type = ANY (ARRAY['free'::text, 'patient'::text, 'therapist'::text, 'clinic'::text]))),
|
||||
CONSTRAINT profiles_role_check CHECK ((role = ANY (ARRAY['saas_admin'::text, 'tenant_member'::text, 'portal_user'::text, 'patient'::text])))
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.tenants (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
name text,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
kind text DEFAULT 'saas'::text NOT NULL,
|
||||
CONSTRAINT tenants_kind_check CHECK ((kind = ANY (ARRAY['therapist'::text, 'clinic_coworking'::text, 'clinic_reception'::text, 'clinic_full'::text, 'clinic'::text, 'saas'::text, 'supervisor'::text])))
|
||||
);
|
||||
|
||||
|
||||
-- Tables: Tenants / Multi-tenant
|
||||
-- Gerado automaticamente em 2026-04-17T12:23:05.228Z
|
||||
-- Total: 10
|
||||
|
||||
CREATE TABLE public.tenant_members (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
@@ -56,81 +11,6 @@ CREATE TABLE public.tenant_members (
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.tenant_invites (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
email text NOT NULL,
|
||||
role text NOT NULL,
|
||||
token uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
invited_by uuid,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
expires_at timestamp with time zone DEFAULT (now() + '7 days'::interval) NOT NULL,
|
||||
accepted_at timestamp with time zone,
|
||||
accepted_by uuid,
|
||||
revoked_at timestamp with time zone,
|
||||
revoked_by uuid,
|
||||
CONSTRAINT tenant_invites_role_check CHECK ((role = ANY (ARRAY['therapist'::text, 'secretary'::text])))
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.tenant_features (
|
||||
tenant_id uuid NOT NULL,
|
||||
feature_key text NOT NULL,
|
||||
enabled boolean DEFAULT false NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.tenant_feature_exceptions_log (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
feature_key text NOT NULL,
|
||||
enabled boolean NOT NULL,
|
||||
reason text,
|
||||
created_by uuid,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.saas_admins (
|
||||
user_id uuid NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.owner_users (
|
||||
owner_id uuid NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
role text DEFAULT 'admin'::text NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.user_settings (
|
||||
user_id uuid NOT NULL,
|
||||
theme_mode text DEFAULT 'dark'::text NOT NULL,
|
||||
preset text DEFAULT 'Aura'::text NOT NULL,
|
||||
primary_color text DEFAULT 'noir'::text NOT NULL,
|
||||
surface_color text DEFAULT 'slate'::text NOT NULL,
|
||||
menu_mode text DEFAULT 'static'::text NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
layout_variant text DEFAULT 'classic'::text NOT NULL,
|
||||
CONSTRAINT user_settings_layout_variant_check CHECK ((layout_variant = ANY (ARRAY['classic'::text, 'rail'::text]))),
|
||||
CONSTRAINT user_settings_menu_mode_check CHECK ((menu_mode = ANY (ARRAY['static'::text, 'overlay'::text]))),
|
||||
CONSTRAINT user_settings_theme_mode_check CHECK ((theme_mode = ANY (ARRAY['light'::text, 'dark'::text])))
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.company_profiles (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
@@ -156,8 +36,6 @@ CREATE TABLE public.company_profiles (
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE public.dev_user_credentials (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
user_id uuid,
|
||||
@@ -168,4 +46,93 @@ CREATE TABLE public.dev_user_credentials (
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.owner_users (
|
||||
owner_id uuid NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
role text DEFAULT 'admin'::text NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.profiles (
|
||||
id uuid NOT NULL,
|
||||
role text DEFAULT 'tenant_member'::text NOT NULL,
|
||||
full_name text,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
avatar_url text,
|
||||
phone text,
|
||||
bio text,
|
||||
language text DEFAULT 'pt-BR'::text NOT NULL,
|
||||
timezone text DEFAULT 'America/Sao_Paulo'::text NOT NULL,
|
||||
notify_system_email boolean DEFAULT true NOT NULL,
|
||||
notify_reminders boolean DEFAULT true NOT NULL,
|
||||
notify_news boolean DEFAULT false NOT NULL,
|
||||
account_type text DEFAULT 'free'::text NOT NULL,
|
||||
platform_roles text[] DEFAULT '{}'::text[] NOT NULL,
|
||||
nickname text,
|
||||
work_description text,
|
||||
work_description_other text,
|
||||
site_url text,
|
||||
social_instagram text,
|
||||
social_youtube text,
|
||||
social_facebook text,
|
||||
social_x text,
|
||||
social_custom jsonb DEFAULT '[]'::jsonb NOT NULL,
|
||||
tenant_id uuid,
|
||||
CONSTRAINT profiles_account_type_check CHECK ((account_type = ANY (ARRAY['free'::text, 'patient'::text, 'therapist'::text, 'clinic'::text]))),
|
||||
CONSTRAINT profiles_role_check CHECK ((role = ANY (ARRAY['saas_admin'::text, 'tenant_member'::text, 'portal_user'::text, 'patient'::text])))
|
||||
);
|
||||
|
||||
CREATE TABLE public.saas_admins (
|
||||
user_id uuid NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.support_sessions (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
admin_id uuid NOT NULL,
|
||||
token text DEFAULT encode(extensions.gen_random_bytes(32), 'hex'::text) NOT NULL,
|
||||
expires_at timestamp with time zone DEFAULT (now() + '01:00:00'::interval) NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.tenant_invites (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
email text NOT NULL,
|
||||
role text NOT NULL,
|
||||
token uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
invited_by uuid,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
expires_at timestamp with time zone DEFAULT (now() + '7 days'::interval) NOT NULL,
|
||||
accepted_at timestamp with time zone,
|
||||
accepted_by uuid,
|
||||
revoked_at timestamp with time zone,
|
||||
revoked_by uuid,
|
||||
CONSTRAINT tenant_invites_role_check CHECK ((role = ANY (ARRAY['therapist'::text, 'secretary'::text])))
|
||||
);
|
||||
|
||||
CREATE TABLE public.tenants (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
name text,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
kind text DEFAULT 'saas'::text NOT NULL,
|
||||
papel_timbrado jsonb DEFAULT '{"footer": {"slots": {"left": null, "right": null, "center": {"type": "custom-text", "content": ""}}, "height": 40, "preset": "text-center", "divider": {"show": true, "color": "#cccccc", "style": "solid"}, "enabled": true, "showPageNumber": false}, "header": {"slots": {"left": {"size": "medium", "type": "logo"}, "right": {"type": "institution-data", "fields": ["nome", "cnpj", "endereco_linha"]}, "center": null}, "height": 80, "preset": "logo-left-text-right", "divider": {"show": true, "color": "#cccccc", "style": "solid"}, "enabled": true}, "margins": {"top": 20, "left": 25, "right": 25, "bottom": 20}}'::jsonb,
|
||||
CONSTRAINT tenants_kind_check CHECK ((kind = ANY (ARRAY['therapist'::text, 'clinic_coworking'::text, 'clinic_reception'::text, 'clinic_full'::text, 'clinic'::text, 'saas'::text, 'supervisor'::text])))
|
||||
);
|
||||
|
||||
CREATE TABLE public.user_settings (
|
||||
user_id uuid NOT NULL,
|
||||
theme_mode text DEFAULT 'dark'::text NOT NULL,
|
||||
preset text DEFAULT 'Aura'::text NOT NULL,
|
||||
primary_color text DEFAULT 'noir'::text NOT NULL,
|
||||
surface_color text DEFAULT 'slate'::text NOT NULL,
|
||||
menu_mode text DEFAULT 'static'::text NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
layout_variant text DEFAULT 'classic'::text NOT NULL,
|
||||
CONSTRAINT user_settings_layout_variant_check CHECK ((layout_variant = ANY (ARRAY['classic'::text, 'rail'::text]))),
|
||||
CONSTRAINT user_settings_menu_mode_check CHECK ((menu_mode = ANY (ARRAY['static'::text, 'overlay'::text]))),
|
||||
CONSTRAINT user_settings_theme_mode_check CHECK ((theme_mode = ANY (ARRAY['light'::text, 'dark'::text])))
|
||||
);
|
||||
@@ -1,28 +1,10 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Views
|
||||
-- =============================================================================
|
||||
-- current_tenant_id, owner_feature_entitlements, subscription_intents,
|
||||
-- v_auth_users_public, v_cashflow_projection, v_commitment_totals,
|
||||
-- v_patient_groups_with_counts, v_plan_active_prices, v_public_pricing,
|
||||
-- v_subscription_feature_mismatch, v_subscription_health, v_subscription_health_v2,
|
||||
-- v_tag_patient_counts, v_tenant_active_subscription, v_tenant_entitlements,
|
||||
-- v_tenant_entitlements_full, v_tenant_entitlements_json,
|
||||
-- v_tenant_feature_exceptions, v_tenant_feature_mismatch,
|
||||
-- v_tenant_members_with_profiles, v_tenant_people, v_tenant_staff,
|
||||
-- v_user_active_subscription, v_user_entitlements
|
||||
-- =============================================================================
|
||||
-- Views
|
||||
-- Gerado automaticamente em 2026-04-17T12:23:05.233Z
|
||||
-- Total: 27
|
||||
|
||||
CREATE VIEW public.current_tenant_id AS
|
||||
SELECT current_setting('request.jwt.claim.tenant_id'::text, true) AS current_setting;
|
||||
|
||||
|
||||
ALTER VIEW public.current_tenant_id OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: determined_commitment_fields; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
|
||||
CREATE VIEW public.owner_feature_entitlements AS
|
||||
WITH base AS (
|
||||
SELECT s.user_id AS owner_id,
|
||||
@@ -51,14 +33,6 @@ CREATE VIEW public.owner_feature_entitlements AS
|
||||
FROM base
|
||||
GROUP BY owner_id, feature_key;
|
||||
|
||||
|
||||
ALTER VIEW public.owner_feature_entitlements OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: owner_users; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
|
||||
CREATE VIEW public.subscription_intents AS
|
||||
SELECT t.id,
|
||||
t.user_id,
|
||||
@@ -98,14 +72,6 @@ UNION ALL
|
||||
'therapist'::text AS plan_target
|
||||
FROM public.subscription_intents_personal p;
|
||||
|
||||
|
||||
ALTER VIEW public.subscription_intents OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: subscription_intents_legacy; Type: TABLE; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
|
||||
CREATE VIEW public.v_auth_users_public AS
|
||||
SELECT id AS user_id,
|
||||
email,
|
||||
@@ -113,13 +79,6 @@ CREATE VIEW public.v_auth_users_public AS
|
||||
last_sign_in_at
|
||||
FROM auth.users u;
|
||||
|
||||
|
||||
ALTER VIEW public.v_auth_users_public OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: v_cashflow_projection; Type: VIEW; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE VIEW public.v_cashflow_projection WITH (security_invoker='on') AS
|
||||
SELECT gs.mes,
|
||||
to_char(gs.mes, 'YYYY-MM'::text) AS mes_label,
|
||||
@@ -136,20 +95,6 @@ CREATE VIEW public.v_cashflow_projection WITH (security_invoker='on') AS
|
||||
GROUP BY gs.mes
|
||||
ORDER BY gs.mes;
|
||||
|
||||
|
||||
ALTER VIEW public.v_cashflow_projection OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: VIEW v_cashflow_projection; Type: COMMENT; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
COMMENT ON VIEW public.v_cashflow_projection IS 'Fluxo de caixa projetado: próximos 6 meses com totais de pending+overdue por due_date. Usa security_invoker=on — filtra automaticamente pelo auth.uid() via RLS de financial_records.';
|
||||
|
||||
|
||||
--
|
||||
-- Name: v_commitment_totals; Type: VIEW; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE VIEW public.v_commitment_totals AS
|
||||
SELECT c.tenant_id,
|
||||
c.id AS commitment_id,
|
||||
@@ -158,12 +103,76 @@ CREATE VIEW public.v_commitment_totals AS
|
||||
LEFT JOIN public.commitment_time_logs l ON ((l.commitment_id = c.id)))
|
||||
GROUP BY c.tenant_id, c.id;
|
||||
|
||||
|
||||
ALTER VIEW public.v_commitment_totals OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: v_patient_groups_with_counts; Type: VIEW; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
CREATE VIEW public.v_patient_engajamento WITH (security_invoker='on') AS
|
||||
WITH sessoes AS (
|
||||
SELECT ae.patient_id,
|
||||
ae.tenant_id,
|
||||
count(*) FILTER (WHERE (ae.status = 'realizado'::public.status_evento_agenda)) AS total_realizadas,
|
||||
count(*) FILTER (WHERE (ae.status = ANY (ARRAY['realizado'::public.status_evento_agenda, 'cancelado'::public.status_evento_agenda, 'faltou'::public.status_evento_agenda]))) AS total_marcadas,
|
||||
count(*) FILTER (WHERE (ae.status = 'faltou'::public.status_evento_agenda)) AS total_faltas,
|
||||
max(ae.inicio_em) FILTER (WHERE (ae.status = 'realizado'::public.status_evento_agenda)) AS ultima_sessao_em,
|
||||
min(ae.inicio_em) FILTER (WHERE (ae.status = 'realizado'::public.status_evento_agenda)) AS primeira_sessao_em,
|
||||
count(*) FILTER (WHERE ((ae.status = 'realizado'::public.status_evento_agenda) AND (ae.inicio_em >= (now() - '30 days'::interval)))) AS sessoes_ultimo_mes
|
||||
FROM public.agenda_eventos ae
|
||||
WHERE (ae.patient_id IS NOT NULL)
|
||||
GROUP BY ae.patient_id, ae.tenant_id
|
||||
), financeiro AS (
|
||||
SELECT fr.patient_id,
|
||||
fr.tenant_id,
|
||||
COALESCE(sum(fr.final_amount) FILTER (WHERE (fr.status = 'paid'::text)), (0)::numeric) AS total_pago,
|
||||
COALESCE(avg(fr.final_amount) FILTER (WHERE (fr.status = 'paid'::text)), (0)::numeric) AS ticket_medio,
|
||||
count(*) FILTER (WHERE ((fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])) AND (fr.due_date < now()))) AS cobr_vencidas,
|
||||
count(*) FILTER (WHERE (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text]))) AS cobr_pendentes,
|
||||
count(*) FILTER (WHERE ((fr.type = 'receita'::public.financial_record_type) AND (fr.status = 'paid'::text))) AS cobr_pagas
|
||||
FROM public.financial_records fr
|
||||
WHERE ((fr.patient_id IS NOT NULL) AND (fr.deleted_at IS NULL))
|
||||
GROUP BY fr.patient_id, fr.tenant_id
|
||||
)
|
||||
SELECT p.id AS patient_id,
|
||||
p.tenant_id,
|
||||
p.nome_completo,
|
||||
p.status,
|
||||
p.risco_elevado,
|
||||
COALESCE(s.total_realizadas, (0)::bigint) AS total_sessoes,
|
||||
COALESCE(s.sessoes_ultimo_mes, (0)::bigint) AS sessoes_ultimo_mes,
|
||||
s.primeira_sessao_em,
|
||||
s.ultima_sessao_em,
|
||||
(EXTRACT(day FROM (now() - s.ultima_sessao_em)))::integer AS dias_sem_sessao,
|
||||
CASE
|
||||
WHEN (COALESCE(s.total_marcadas, (0)::bigint) = 0) THEN NULL::numeric
|
||||
ELSE round((((s.total_realizadas)::numeric / (s.total_marcadas)::numeric) * (100)::numeric), 1)
|
||||
END AS taxa_comparecimento,
|
||||
COALESCE(f.total_pago, (0)::numeric) AS ltv_total,
|
||||
round(COALESCE(f.ticket_medio, (0)::numeric), 2) AS ticket_medio,
|
||||
COALESCE(f.cobr_vencidas, (0)::bigint) AS cobr_vencidas,
|
||||
COALESCE(f.cobr_pagas, (0)::bigint) AS cobr_pagas,
|
||||
CASE
|
||||
WHEN (COALESCE((f.cobr_pagas + f.cobr_vencidas), (0)::bigint) = 0) THEN NULL::numeric
|
||||
ELSE round((((f.cobr_pagas)::numeric / ((f.cobr_pagas + f.cobr_vencidas))::numeric) * (100)::numeric), 1)
|
||||
END AS taxa_pagamentos_dia,
|
||||
round(LEAST((100)::numeric, COALESCE(((
|
||||
CASE
|
||||
WHEN (COALESCE(s.total_marcadas, (0)::bigint) = 0) THEN (50)::numeric
|
||||
ELSE LEAST((50)::numeric, (((s.total_realizadas)::numeric / (s.total_marcadas)::numeric) * (50)::numeric))
|
||||
END +
|
||||
CASE
|
||||
WHEN (COALESCE((f.cobr_pagas + f.cobr_vencidas), (0)::bigint) = 0) THEN (30)::numeric
|
||||
ELSE LEAST((30)::numeric, (((f.cobr_pagas)::numeric / ((f.cobr_pagas + f.cobr_vencidas))::numeric) * (30)::numeric))
|
||||
END) + (
|
||||
CASE
|
||||
WHEN (s.ultima_sessao_em IS NULL) THEN 0
|
||||
WHEN (EXTRACT(day FROM (now() - s.ultima_sessao_em)) <= (14)::numeric) THEN 20
|
||||
WHEN (EXTRACT(day FROM (now() - s.ultima_sessao_em)) <= (30)::numeric) THEN 15
|
||||
WHEN (EXTRACT(day FROM (now() - s.ultima_sessao_em)) <= (60)::numeric) THEN 8
|
||||
ELSE 0
|
||||
END)::numeric), (0)::numeric)), 0) AS engajamento_score,
|
||||
CASE
|
||||
WHEN (s.primeira_sessao_em IS NULL) THEN NULL::integer
|
||||
ELSE (EXTRACT(day FROM (now() - s.primeira_sessao_em)))::integer
|
||||
END AS duracao_tratamento_dias
|
||||
FROM ((public.patients p
|
||||
LEFT JOIN sessoes s ON (((s.patient_id = p.id) AND (s.tenant_id = p.tenant_id))))
|
||||
LEFT JOIN financeiro f ON (((f.patient_id = p.id) AND (f.tenant_id = p.tenant_id))));
|
||||
|
||||
CREATE VIEW public.v_patient_groups_with_counts AS
|
||||
SELECT pg.id,
|
||||
@@ -179,12 +188,27 @@ CREATE VIEW public.v_patient_groups_with_counts AS
|
||||
LEFT JOIN public.patient_group_patient pgp ON ((pgp.patient_group_id = pg.id)))
|
||||
GROUP BY pg.id, pg.nome, pg.cor, pg.owner_id, pg.is_system, pg.is_active, pg.created_at, pg.updated_at;
|
||||
|
||||
|
||||
ALTER VIEW public.v_patient_groups_with_counts OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: v_plan_active_prices; Type: VIEW; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
CREATE VIEW public.v_patients_risco WITH (security_invoker='on') AS
|
||||
SELECT p.id,
|
||||
p.tenant_id,
|
||||
p.nome_completo,
|
||||
p.status,
|
||||
p.risco_elevado,
|
||||
p.risco_nota,
|
||||
p.risco_sinalizado_em,
|
||||
e.dias_sem_sessao,
|
||||
e.engajamento_score,
|
||||
e.taxa_comparecimento,
|
||||
CASE
|
||||
WHEN p.risco_elevado THEN 'risco_sinalizado'::text
|
||||
WHEN ((COALESCE(e.dias_sem_sessao, 999) > 30) AND (p.status = 'Ativo'::text)) THEN 'sem_sessao_30d'::text
|
||||
WHEN (COALESCE(e.taxa_comparecimento, (100)::numeric) < (60)::numeric) THEN 'baixo_comparecimento'::text
|
||||
WHEN (COALESCE(e.cobr_vencidas, (0)::bigint) > 0) THEN 'cobranca_vencida'::text
|
||||
ELSE 'ok'::text
|
||||
END AS alerta_tipo
|
||||
FROM (public.patients p
|
||||
JOIN public.v_patient_engajamento e ON ((e.patient_id = p.id)))
|
||||
WHERE ((p.status = 'Ativo'::text) AND ((p.risco_elevado = true) OR (COALESCE(e.dias_sem_sessao, 999) > 30) OR (COALESCE(e.taxa_comparecimento, (100)::numeric) < (60)::numeric) OR (COALESCE(e.cobr_vencidas, (0)::bigint) > 0)));
|
||||
|
||||
CREATE VIEW public.v_plan_active_prices AS
|
||||
SELECT plan_id,
|
||||
@@ -211,13 +235,6 @@ CREATE VIEW public.v_plan_active_prices AS
|
||||
FROM public.plan_prices
|
||||
GROUP BY plan_id;
|
||||
|
||||
|
||||
ALTER VIEW public.v_plan_active_prices OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: v_public_pricing; Type: VIEW; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE VIEW public.v_public_pricing AS
|
||||
SELECT p.id AS plan_id,
|
||||
p.key AS plan_key,
|
||||
@@ -241,13 +258,6 @@ CREATE VIEW public.v_public_pricing AS
|
||||
LEFT JOIN public.v_plan_active_prices ap ON ((ap.plan_id = p.id)))
|
||||
ORDER BY COALESCE(pp.sort_order, 0), p.key;
|
||||
|
||||
|
||||
ALTER VIEW public.v_public_pricing OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: v_subscription_feature_mismatch; Type: VIEW; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE VIEW public.v_subscription_feature_mismatch AS
|
||||
WITH expected AS (
|
||||
SELECT s.user_id AS owner_id,
|
||||
@@ -272,13 +282,6 @@ CREATE VIEW public.v_subscription_feature_mismatch AS
|
||||
FULL JOIN actual ON (((expected.owner_id = actual.owner_id) AND (expected.feature_key = actual.feature_key))))
|
||||
WHERE ((expected.feature_key IS NULL) OR (actual.feature_key IS NULL));
|
||||
|
||||
|
||||
ALTER VIEW public.v_subscription_feature_mismatch OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: v_subscription_health; Type: VIEW; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE VIEW public.v_subscription_health AS
|
||||
SELECT s.id AS subscription_id,
|
||||
s.user_id AS owner_id,
|
||||
@@ -303,13 +306,6 @@ CREATE VIEW public.v_subscription_health AS
|
||||
FROM (public.subscriptions s
|
||||
LEFT JOIN public.plans p ON ((p.id = s.plan_id)));
|
||||
|
||||
|
||||
ALTER VIEW public.v_subscription_health OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: v_subscription_health_v2; Type: VIEW; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE VIEW public.v_subscription_health_v2 AS
|
||||
SELECT s.id AS subscription_id,
|
||||
s.user_id AS owner_id,
|
||||
@@ -334,13 +330,6 @@ CREATE VIEW public.v_subscription_health_v2 AS
|
||||
FROM (public.subscriptions s
|
||||
LEFT JOIN public.plans p ON ((p.id = s.plan_id)));
|
||||
|
||||
|
||||
ALTER VIEW public.v_subscription_health_v2 OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: v_tag_patient_counts; Type: VIEW; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE VIEW public.v_tag_patient_counts AS
|
||||
SELECT t.id,
|
||||
t.owner_id,
|
||||
@@ -355,13 +344,6 @@ CREATE VIEW public.v_tag_patient_counts AS
|
||||
LEFT JOIN public.patient_patient_tag ppt ON (((ppt.tag_id = t.id) AND (ppt.owner_id = t.owner_id))))
|
||||
GROUP BY t.id, t.owner_id, t.nome, t.cor, t.is_padrao, t.created_at, t.updated_at;
|
||||
|
||||
|
||||
ALTER VIEW public.v_tag_patient_counts OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: v_tenant_active_subscription; Type: VIEW; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE VIEW public.v_tenant_active_subscription AS
|
||||
SELECT DISTINCT ON (tenant_id) tenant_id,
|
||||
plan_id,
|
||||
@@ -375,13 +357,6 @@ CREATE VIEW public.v_tenant_active_subscription AS
|
||||
WHERE ((tenant_id IS NOT NULL) AND (status = 'active'::text) AND ((current_period_end IS NULL) OR (current_period_end > now())))
|
||||
ORDER BY tenant_id, created_at DESC;
|
||||
|
||||
|
||||
ALTER VIEW public.v_tenant_active_subscription OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: v_tenant_entitlements; Type: VIEW; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE VIEW public.v_tenant_entitlements AS
|
||||
SELECT a.tenant_id,
|
||||
f.key AS feature_key,
|
||||
@@ -390,13 +365,6 @@ CREATE VIEW public.v_tenant_entitlements AS
|
||||
JOIN public.plan_features pf ON (((pf.plan_id = a.plan_id) AND (pf.enabled = true))))
|
||||
JOIN public.features f ON ((f.id = pf.feature_id)));
|
||||
|
||||
|
||||
ALTER VIEW public.v_tenant_entitlements OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: v_tenant_entitlements_full; Type: VIEW; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE VIEW public.v_tenant_entitlements_full AS
|
||||
SELECT a.tenant_id,
|
||||
f.key AS feature_key,
|
||||
@@ -409,13 +377,6 @@ CREATE VIEW public.v_tenant_entitlements_full AS
|
||||
JOIN public.features f ON ((f.id = pf.feature_id)))
|
||||
JOIN public.plans p ON ((p.id = a.plan_id)));
|
||||
|
||||
|
||||
ALTER VIEW public.v_tenant_entitlements_full OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: v_tenant_entitlements_json; Type: VIEW; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE VIEW public.v_tenant_entitlements_json AS
|
||||
SELECT tenant_id,
|
||||
max(plan_key) AS plan_key,
|
||||
@@ -423,13 +384,6 @@ CREATE VIEW public.v_tenant_entitlements_json AS
|
||||
FROM public.v_tenant_entitlements_full
|
||||
GROUP BY tenant_id;
|
||||
|
||||
|
||||
ALTER VIEW public.v_tenant_entitlements_json OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: v_tenant_feature_exceptions; Type: VIEW; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE VIEW public.v_tenant_feature_exceptions AS
|
||||
SELECT tf.tenant_id,
|
||||
a.plan_key,
|
||||
@@ -440,13 +394,6 @@ CREATE VIEW public.v_tenant_feature_exceptions AS
|
||||
LEFT JOIN public.v_tenant_entitlements_full v ON (((v.tenant_id = tf.tenant_id) AND (v.feature_key = tf.feature_key))))
|
||||
WHERE ((tf.enabled = true) AND (COALESCE(v.allowed, false) = false));
|
||||
|
||||
|
||||
ALTER VIEW public.v_tenant_feature_exceptions OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: v_tenant_feature_mismatch; Type: VIEW; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE VIEW public.v_tenant_feature_mismatch AS
|
||||
WITH plan_allowed AS (
|
||||
SELECT v.tenant_id,
|
||||
@@ -469,13 +416,6 @@ CREATE VIEW public.v_tenant_feature_mismatch AS
|
||||
LEFT JOIN plan_allowed p ON (((p.tenant_id = o.tenant_id) AND (p.feature_key = o.feature_key))))
|
||||
WHERE ((o.enabled = true) AND (COALESCE(p.allowed, false) = false));
|
||||
|
||||
|
||||
ALTER VIEW public.v_tenant_feature_mismatch OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: v_tenant_members_with_profiles; Type: VIEW; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE VIEW public.v_tenant_members_with_profiles AS
|
||||
SELECT tm.id AS tenant_member_id,
|
||||
tm.tenant_id,
|
||||
@@ -489,13 +429,6 @@ CREATE VIEW public.v_tenant_members_with_profiles AS
|
||||
LEFT JOIN public.profiles p ON ((p.id = tm.user_id)))
|
||||
LEFT JOIN auth.users au ON ((au.id = tm.user_id)));
|
||||
|
||||
|
||||
ALTER VIEW public.v_tenant_members_with_profiles OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: v_tenant_people; Type: VIEW; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE VIEW public.v_tenant_people AS
|
||||
SELECT 'member'::text AS type,
|
||||
m.tenant_id,
|
||||
@@ -519,13 +452,6 @@ UNION ALL
|
||||
FROM public.tenant_invites i
|
||||
WHERE ((i.accepted_at IS NULL) AND (i.revoked_at IS NULL));
|
||||
|
||||
|
||||
ALTER VIEW public.v_tenant_people OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: v_tenant_staff; Type: VIEW; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE VIEW public.v_tenant_staff AS
|
||||
SELECT ('m_'::text || (tm.id)::text) AS row_id,
|
||||
tm.tenant_id,
|
||||
@@ -552,12 +478,31 @@ UNION ALL
|
||||
FROM public.tenant_invites ti
|
||||
WHERE ((ti.accepted_at IS NULL) AND (ti.revoked_at IS NULL) AND (ti.expires_at > now()));
|
||||
|
||||
|
||||
ALTER VIEW public.v_tenant_staff OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: v_user_active_subscription; Type: VIEW; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
CREATE VIEW public.v_twilio_whatsapp_overview AS
|
||||
SELECT nc.id AS channel_id,
|
||||
nc.tenant_id,
|
||||
nc.owner_id,
|
||||
nc.is_active,
|
||||
nc.connection_status,
|
||||
nc.display_name,
|
||||
nc.twilio_subaccount_sid,
|
||||
nc.twilio_phone_number,
|
||||
nc.twilio_phone_sid,
|
||||
nc.cost_per_message_usd,
|
||||
nc.price_per_message_brl,
|
||||
nc.provisioned_at,
|
||||
nc.created_at,
|
||||
nc.updated_at,
|
||||
COALESCE(u.messages_sent, 0) AS current_month_sent,
|
||||
COALESCE(u.messages_delivered, 0) AS current_month_delivered,
|
||||
COALESCE(u.messages_failed, 0) AS current_month_failed,
|
||||
COALESCE(u.cost_usd, (0)::numeric) AS current_month_cost_usd,
|
||||
COALESCE(u.cost_brl, (0)::numeric) AS current_month_cost_brl,
|
||||
COALESCE(u.revenue_brl, (0)::numeric) AS current_month_revenue_brl,
|
||||
COALESCE(u.margin_brl, (0)::numeric) AS current_month_margin_brl
|
||||
FROM (public.notification_channels nc
|
||||
LEFT JOIN public.twilio_subaccount_usage u ON (((u.channel_id = nc.id) AND (u.period_start = (date_trunc('month'::text, (CURRENT_DATE)::timestamp with time zone))::date))))
|
||||
WHERE ((nc.channel = 'whatsapp'::text) AND (nc.provider = 'twilio'::text) AND (nc.deleted_at IS NULL));
|
||||
|
||||
CREATE VIEW public.v_user_active_subscription AS
|
||||
SELECT DISTINCT ON (user_id) user_id,
|
||||
@@ -572,13 +517,6 @@ CREATE VIEW public.v_user_active_subscription AS
|
||||
WHERE ((tenant_id IS NULL) AND (user_id IS NOT NULL) AND (status = 'active'::text) AND ((current_period_end IS NULL) OR (current_period_end > now())))
|
||||
ORDER BY user_id, created_at DESC;
|
||||
|
||||
|
||||
ALTER VIEW public.v_user_active_subscription OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: v_user_entitlements; Type: VIEW; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE VIEW public.v_user_entitlements AS
|
||||
SELECT a.user_id,
|
||||
f.key AS feature_key,
|
||||
@@ -586,11 +524,3 @@ CREATE VIEW public.v_user_entitlements AS
|
||||
FROM ((public.v_user_active_subscription a
|
||||
JOIN public.plan_features pf ON (((pf.plan_id = a.plan_id) AND (pf.enabled = true))))
|
||||
JOIN public.features f ON ((f.id = pf.feature_id)));
|
||||
|
||||
|
||||
ALTER VIEW public.v_user_entitlements OWNER TO supabase_admin;
|
||||
|
||||
--
|
||||
-- Name: messages; Type: TABLE; Schema: realtime; Owner: supabase_realtime_admin
|
||||
--
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,481 +1,163 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Triggers
|
||||
-- =============================================================================
|
||||
-- Triggers
|
||||
-- Gerado automaticamente em 2026-04-17T12:23:05.238Z
|
||||
-- Total: 80
|
||||
|
||||
CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();
|
||||
|
||||
|
||||
--
|
||||
-- Name: users trg_seed_patient_groups; Type: TRIGGER; Schema: auth; Owner: supabase_auth_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_seed_patient_groups AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION public.on_new_user_seed_patient_groups();
|
||||
|
||||
|
||||
--
|
||||
-- Name: agenda_bloqueios agenda_bloqueios_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER agenda_bloqueios_updated_at BEFORE UPDATE ON public.agenda_bloqueios FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: agendador_configuracoes agendador_slug_trigger; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER agendador_slug_trigger BEFORE INSERT OR UPDATE ON public.agendador_configuracoes FOR EACH ROW EXECUTE FUNCTION public.agendador_gerar_slug();
|
||||
|
||||
|
||||
--
|
||||
-- Name: tenant_members prevent_saas_membership_trigger; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER prevent_saas_membership_trigger BEFORE INSERT ON public.tenant_members FOR EACH ROW EXECUTE FUNCTION public.prevent_saas_membership();
|
||||
|
||||
|
||||
--
|
||||
-- Name: insurance_plan_services set_insurance_plan_services_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER set_insurance_plan_services_updated_at BEFORE UPDATE ON public.insurance_plan_services FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: user_settings t_user_settings_set_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER t_user_settings_set_updated_at BEFORE UPDATE ON public.user_settings FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: agenda_configuracoes tg_agenda_configuracoes_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER tg_agenda_configuracoes_updated_at BEFORE UPDATE ON public.agenda_configuracoes FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: agenda_eventos tg_agenda_eventos_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER tg_agenda_eventos_updated_at BEFORE UPDATE ON public.agenda_eventos FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: agenda_excecoes tg_agenda_excecoes_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER tg_agenda_excecoes_updated_at BEFORE UPDATE ON public.agenda_excecoes FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: agenda_regras_semanais tg_agenda_regras_semanais_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER tg_agenda_regras_semanais_updated_at BEFORE UPDATE ON public.agenda_regras_semanais FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: recurrence_rules tg_recurrence_rules_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER tg_recurrence_rules_updated_at BEFORE UPDATE ON public.recurrence_rules FOR EACH ROW EXECUTE FUNCTION public.set_updated_at_recurrence();
|
||||
|
||||
|
||||
--
|
||||
-- Name: plan_public tr_plan_public_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER tr_plan_public_updated_at BEFORE UPDATE ON public.plan_public FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: profiles trg_account_type_immutable; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_account_type_immutable BEFORE UPDATE OF account_type ON public.profiles FOR EACH ROW EXECUTE FUNCTION public.guard_account_type_immutable();
|
||||
|
||||
|
||||
--
|
||||
-- Name: agenda_configuracoes trg_agenda_cfg_sync; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_agenda_cfg_sync BEFORE INSERT OR UPDATE ON public.agenda_configuracoes FOR EACH ROW EXECUTE FUNCTION public.agenda_cfg_sync();
|
||||
|
||||
|
||||
--
|
||||
-- Name: agenda_eventos trg_agenda_eventos_busy_mirror_del; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_agenda_eventos_busy_mirror_del AFTER DELETE ON public.agenda_eventos FOR EACH ROW WHEN (((old.mirror_of_event_id IS NULL) AND (old.tenant_id = old.owner_id))) EXECUTE FUNCTION public.sync_busy_mirror_agenda_eventos();
|
||||
|
||||
|
||||
--
|
||||
-- Name: agenda_eventos trg_agenda_eventos_busy_mirror_ins; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_agenda_eventos_busy_mirror_ins AFTER INSERT ON public.agenda_eventos FOR EACH ROW WHEN (((new.mirror_of_event_id IS NULL) AND (new.tenant_id = new.owner_id) AND (new.visibility_scope = ANY (ARRAY['busy_only'::text, 'private'::text])))) EXECUTE FUNCTION public.sync_busy_mirror_agenda_eventos();
|
||||
|
||||
|
||||
--
|
||||
-- Name: agenda_eventos trg_agenda_eventos_busy_mirror_upd; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_agenda_eventos_busy_mirror_upd AFTER UPDATE ON public.agenda_eventos FOR EACH ROW WHEN (((new.mirror_of_event_id IS NULL) AND (new.tenant_id = new.owner_id) AND ((new.visibility_scope IS DISTINCT FROM old.visibility_scope) OR (new.inicio_em IS DISTINCT FROM old.inicio_em) OR (new.fim_em IS DISTINCT FROM old.fim_em) OR (new.owner_id IS DISTINCT FROM old.owner_id) OR (new.tenant_id IS DISTINCT FROM old.tenant_id)))) EXECUTE FUNCTION public.sync_busy_mirror_agenda_eventos();
|
||||
|
||||
|
||||
--
|
||||
-- Name: agenda_regras_semanais trg_agenda_regras_semanais_no_overlap; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_agenda_regras_semanais_no_overlap BEFORE INSERT OR UPDATE ON public.agenda_regras_semanais FOR EACH ROW EXECUTE FUNCTION public.fn_agenda_regras_semanais_no_overlap();
|
||||
|
||||
|
||||
--
|
||||
-- Name: agenda_eventos trg_auto_financial_from_session; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_auto_financial_from_session AFTER UPDATE OF status ON public.agenda_eventos FOR EACH ROW EXECUTE FUNCTION public.auto_create_financial_record_from_session();
|
||||
|
||||
|
||||
--
|
||||
-- Name: notification_preferences trg_cancel_notifs_on_opt_out; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_cancel_notifs_on_opt_out AFTER UPDATE ON public.notification_preferences FOR EACH ROW EXECUTE FUNCTION public.cancel_notifications_on_opt_out();
|
||||
|
||||
|
||||
--
|
||||
-- Name: agenda_eventos trg_cancel_notifs_on_session_cancel; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_cancel_notifs_on_session_cancel AFTER UPDATE ON public.agenda_eventos FOR EACH ROW WHEN ((new.status IS DISTINCT FROM old.status)) EXECUTE FUNCTION public.cancel_notifications_on_session_cancel();
|
||||
|
||||
|
||||
--
|
||||
-- Name: company_profiles trg_company_profiles_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_company_profiles_updated_at BEFORE UPDATE ON public.company_profiles FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: determined_commitment_fields trg_determined_commitment_fields_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_determined_commitment_fields_updated_at BEFORE UPDATE ON public.determined_commitment_fields FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: determined_commitments trg_determined_commitments_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_determined_commitments_updated_at BEFORE UPDATE ON public.determined_commitments FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_documents_timeline_insert AFTER INSERT ON public.documents FOR EACH ROW EXECUTE FUNCTION public.fn_documents_timeline_insert();
|
||||
|
||||
--
|
||||
-- Name: email_layout_config trg_email_layout_config_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
CREATE TRIGGER trg_documents_updated_at BEFORE UPDATE ON public.documents FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_ds_timeline AFTER UPDATE ON public.document_signatures FOR EACH ROW EXECUTE FUNCTION public.fn_document_signature_timeline();
|
||||
|
||||
CREATE TRIGGER trg_ds_updated_at BEFORE UPDATE ON public.document_signatures FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_dt_updated_at BEFORE UPDATE ON public.document_templates FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_email_layout_config_updated_at BEFORE UPDATE ON public.email_layout_config FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: email_templates_global trg_email_templates_global_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_email_templates_global_updated_at BEFORE UPDATE ON public.email_templates_global FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: email_templates_tenant trg_email_templates_tenant_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_email_templates_tenant_updated_at BEFORE UPDATE ON public.email_templates_tenant FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: financial_exceptions trg_financial_exceptions_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_financial_exceptions_updated_at BEFORE UPDATE ON public.financial_exceptions FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: financial_records trg_financial_records_auto_overdue; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_financial_records_auto_overdue BEFORE UPDATE ON public.financial_records FOR EACH ROW EXECUTE FUNCTION public.trg_fn_financial_records_auto_overdue();
|
||||
|
||||
|
||||
--
|
||||
-- Name: financial_records trg_financial_records_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_financial_records_updated_at BEFORE UPDATE ON public.financial_records FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: global_notices trg_global_notices_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_global_notices_updated_at BEFORE UPDATE ON public.global_notices FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: insurance_plans trg_insurance_plans_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_insurance_plans_updated_at BEFORE UPDATE ON public.insurance_plans FOR EACH ROW EXECUTE FUNCTION public.set_insurance_plans_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: plans trg_no_change_core_plan_key; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
CREATE TRIGGER trg_medicos_updated_at BEFORE UPDATE ON public.medicos FOR EACH ROW EXECUTE FUNCTION public.set_medicos_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_no_change_core_plan_key BEFORE UPDATE ON public.plans FOR EACH ROW EXECUTE FUNCTION public.guard_no_change_core_plan_key();
|
||||
|
||||
|
||||
--
|
||||
-- Name: plans trg_no_change_plan_target; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_no_change_plan_target BEFORE UPDATE ON public.plans FOR EACH ROW EXECUTE FUNCTION public.guard_no_change_plan_target();
|
||||
|
||||
|
||||
--
|
||||
-- Name: plans trg_no_delete_core_plans; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_no_delete_core_plans BEFORE DELETE ON public.plans FOR EACH ROW EXECUTE FUNCTION public.guard_no_delete_core_plans();
|
||||
|
||||
|
||||
--
|
||||
-- Name: notification_channels trg_notification_channels_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_notification_channels_updated_at BEFORE UPDATE ON public.notification_channels FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: notification_logs trg_notification_logs_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_notification_logs_updated_at BEFORE UPDATE ON public.notification_logs FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: notification_preferences trg_notification_preferences_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_notification_preferences_updated_at BEFORE UPDATE ON public.notification_preferences FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: notification_queue trg_notification_queue_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_notification_queue_updated_at BEFORE UPDATE ON public.notification_queue FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: notification_schedules trg_notification_schedules_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_notification_schedules_updated_at BEFORE UPDATE ON public.notification_schedules FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: notification_templates trg_notification_templates_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_notification_templates_updated_at BEFORE UPDATE ON public.notification_templates FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: patient_intake_requests trg_notify_on_intake; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_notify_on_intake AFTER INSERT ON public.patient_intake_requests FOR EACH ROW EXECUTE FUNCTION public.notify_on_intake();
|
||||
|
||||
|
||||
--
|
||||
-- Name: agendador_solicitacoes trg_notify_on_scheduling; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_notify_on_scheduling AFTER INSERT ON public.agendador_solicitacoes FOR EACH ROW EXECUTE FUNCTION public.notify_on_scheduling();
|
||||
|
||||
|
||||
--
|
||||
-- Name: agenda_eventos trg_notify_on_session_status; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_notify_on_session_status AFTER UPDATE OF status ON public.agenda_eventos FOR EACH ROW EXECUTE FUNCTION public.notify_on_session_status();
|
||||
|
||||
|
||||
--
|
||||
-- Name: tenant_members trg_patient_cannot_own_tenant; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_patient_cannot_own_tenant BEFORE INSERT OR UPDATE ON public.tenant_members FOR EACH ROW EXECUTE FUNCTION public.guard_patient_cannot_own_tenant();
|
||||
|
||||
|
||||
--
|
||||
-- Name: patient_groups trg_patient_groups_set_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
CREATE TRIGGER trg_patient_contacts_updated_at BEFORE UPDATE ON public.patient_contacts FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_patient_groups_set_updated_at BEFORE UPDATE ON public.patient_groups FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: patient_intake_requests trg_patient_intake_requests_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_patient_intake_requests_updated_at BEFORE UPDATE ON public.patient_intake_requests FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_patient_risco_timeline AFTER UPDATE OF risco_elevado ON public.patients FOR EACH ROW EXECUTE FUNCTION public.trg_fn_patient_risco_timeline();
|
||||
|
||||
--
|
||||
-- Name: patient_tags trg_patient_tags_set_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
CREATE TRIGGER trg_patient_status_history AFTER INSERT OR UPDATE OF status ON public.patients FOR EACH ROW EXECUTE FUNCTION public.trg_fn_patient_status_history();
|
||||
|
||||
CREATE TRIGGER trg_patient_status_timeline AFTER INSERT OR UPDATE OF status ON public.patients FOR EACH ROW EXECUTE FUNCTION public.trg_fn_patient_status_timeline();
|
||||
|
||||
CREATE TRIGGER trg_patient_tags_set_updated_at BEFORE UPDATE ON public.patient_tags FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: patients trg_patients_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_patients_updated_at BEFORE UPDATE ON public.patients FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: patients trg_patients_validate_members; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_patients_validate_members BEFORE INSERT OR UPDATE OF tenant_id, responsible_member_id, patient_scope, therapist_member_id ON public.patients FOR EACH ROW EXECUTE FUNCTION public.patients_validate_member_consistency();
|
||||
|
||||
|
||||
--
|
||||
-- Name: payment_settings trg_payment_settings_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_payment_settings_updated_at BEFORE UPDATE ON public.payment_settings FOR EACH ROW EXECUTE FUNCTION public.update_payment_settings_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: patient_groups trg_prevent_promoting_to_system; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_prevent_promoting_to_system BEFORE UPDATE ON public.patient_groups FOR EACH ROW EXECUTE FUNCTION public.prevent_promoting_to_system();
|
||||
|
||||
|
||||
--
|
||||
-- Name: patient_groups trg_prevent_system_group_changes; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_prevent_system_group_changes BEFORE DELETE OR UPDATE ON public.patient_groups FOR EACH ROW EXECUTE FUNCTION public.prevent_system_group_changes();
|
||||
|
||||
|
||||
--
|
||||
-- Name: professional_pricing trg_professional_pricing_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_professional_pricing_updated_at BEFORE UPDATE ON public.professional_pricing FOR EACH ROW EXECUTE FUNCTION public.update_professional_pricing_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: profiles trg_profiles_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_profiles_updated_at BEFORE UPDATE ON public.profiles FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: services trg_services_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
CREATE TRIGGER trg_psc_updated_at BEFORE UPDATE ON public.patient_support_contacts FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_services_updated_at BEFORE UPDATE ON public.services FOR EACH ROW EXECUTE FUNCTION public.set_services_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: subscription_intents trg_subscription_intents_view_insert; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_subscription_intents_view_insert INSTEAD OF INSERT ON public.subscription_intents FOR EACH ROW EXECUTE FUNCTION public.subscription_intents_view_insert();
|
||||
|
||||
|
||||
--
|
||||
-- Name: subscriptions trg_subscriptions_validate_scope; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_subscriptions_validate_scope BEFORE INSERT OR UPDATE ON public.subscriptions FOR EACH ROW EXECUTE FUNCTION public.subscriptions_validate_scope();
|
||||
|
||||
|
||||
--
|
||||
-- Name: tenant_features trg_tenant_features_guard_with_plan; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_tenant_features_guard_with_plan BEFORE INSERT OR UPDATE ON public.tenant_features FOR EACH ROW EXECUTE FUNCTION public.tenant_features_guard_with_plan();
|
||||
|
||||
|
||||
--
|
||||
-- Name: tenant_features trg_tenant_features_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_tenant_features_updated_at BEFORE UPDATE ON public.tenant_features FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: tenants trg_tenant_kind_immutable; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_tenant_kind_immutable BEFORE UPDATE OF kind ON public.tenants FOR EACH ROW EXECUTE FUNCTION public.guard_tenant_kind_immutable();
|
||||
|
||||
|
||||
--
|
||||
-- Name: therapist_payouts trg_therapist_payouts_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_therapist_payouts_updated_at BEFORE UPDATE ON public.therapist_payouts FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: user_settings trg_user_settings_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER trg_user_settings_updated_at BEFORE UPDATE ON public.user_settings FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
||||
|
||||
|
||||
--
|
||||
-- Name: subscription tr_check_filters; Type: TRIGGER; Schema: realtime; Owner: supabase_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER tr_check_filters BEFORE INSERT OR UPDATE ON realtime.subscription FOR EACH ROW EXECUTE FUNCTION realtime.subscription_check_filters();
|
||||
|
||||
|
||||
--
|
||||
-- Name: buckets enforce_bucket_name_length_trigger; Type: TRIGGER; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER enforce_bucket_name_length_trigger BEFORE INSERT OR UPDATE OF name ON storage.buckets FOR EACH ROW EXECUTE FUNCTION storage.enforce_bucket_name_length();
|
||||
|
||||
|
||||
--
|
||||
-- Name: buckets protect_buckets_delete; Type: TRIGGER; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER protect_buckets_delete BEFORE DELETE ON storage.buckets FOR EACH STATEMENT EXECUTE FUNCTION storage.protect_delete();
|
||||
|
||||
|
||||
--
|
||||
-- Name: objects protect_objects_delete; Type: TRIGGER; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER protect_objects_delete BEFORE DELETE ON storage.objects FOR EACH STATEMENT EXECUTE FUNCTION storage.protect_delete();
|
||||
|
||||
|
||||
--
|
||||
-- Name: objects update_objects_updated_at; Type: TRIGGER; Schema: storage; Owner: supabase_storage_admin
|
||||
--
|
||||
|
||||
CREATE TRIGGER update_objects_updated_at BEFORE UPDATE ON storage.objects FOR EACH ROW EXECUTE FUNCTION storage.update_updated_at_column();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user