diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index f26aa21..0000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "permissions": { - "allow": [ - "Bash(node:*)", - "Bash(powershell:*)", - "Bash(grep:*)", - "Bash(cd \"D:/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai\" && sed -i \\\\\n 's/console\\\\.time\\(tlabel\\)/const _perfEnd = logPerf\\('\\\\''router.guard'\\\\'', tlabel\\)/g' \\\\\n src/router/guards.js && echo \"console.time substituído\")", - "Bash(cd \"D:/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai\" && sed -i \\\\\n 's/console\\\\.timeEnd\\(tlabel\\)/_perfEnd\\(\\)/g' \\\\\n src/router/guards.js && echo \"console.timeEnd substituído\")", - "Bash(cd \"D:/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai\" && npm install --save-dev vitest @vitest/ui 2>&1 | tail -5)", - "Bash(find \"D:\\\\leonohama\\\\AgenciaPsi.com.br\\\\Sistema\\\\agenciapsi-primesakai/DBS\" -name \"*.sql\" -type f 2>/dev/null | head -10)", - "Bash(find \"D:\\\\leonohama\\\\AgenciaPsi.com.br\\\\Sistema\\\\agenciapsi-primesakai/DBS\" -name \"*.sql\" -type f 2>/dev/null | xargs grep -l \"agenda_eventos\" | head -3)", - "Bash(find \"D:\\\\leonohama\\\\AgenciaPsi.com.br\\\\Sistema\\\\agenciapsi-primesakai/DBS/2026-03-11\" -name \"*.sql\" -type f 2>/dev/null | head -3)", - "Bash(find \"D:\\\\leonohama\\\\AgenciaPsi.com.br\\\\Sistema\\\\agenciapsi-primesakai\" -type f -name \"*.sql\" 2>/dev/null | xargs grep -l \"agenda_eventos\" 2>/dev/null | head -5)", - "Bash(find /d/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai/src -name \"*[Pp]ricing*\" -o -name \"*[Pp]reco*\" -o -name \"*[Vv]alor*\" 2>/dev/null | head -20)", - "Bash(where python:*)", - "Bash(cd \"/d/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai\" && C:/Users/lmnohama/AppData/Local/Programs/Python/Python310/python.exe -c \"\nimport zipfile\nimport xml.etree.ElementTree as ET\n\nfor fname in ['spec-wizard.docx', 'spec-v2.docx']:\n print\\('=== ' + fname + ' ==='\\)\n try:\n with zipfile.ZipFile\\(fname, 'r'\\) as z:\n with z.open\\('word/document.xml'\\) as f:\n tree = ET.parse\\(f\\)\n root = tree.getroot\\(\\)\n texts = []\n for para in root.iter\\('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}p'\\):\n parts = []\n for t in para.iter\\('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}t'\\):\n if t.text:\n parts.append\\(t.text\\)\n line = ''.join\\(parts\\)\n texts.append\\(line\\)\n print\\('\\\\n'.join\\(texts\\)\\)\n except Exception as e:\n print\\('Error: ' + str\\(e\\)\\)\n print\\(\\)\n\")", - "Bash(C:/Users/lmnohama/AppData/Local/Programs/Python/Python310/python.exe -c \"\nimport sys\nwith open\\('/d/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai/DBS/2026-03-12/schema.sql', 'r', encoding='utf-8'\\) as f:\n lines = f.readlines\\(\\)\nprint\\(f'Total lines: {len\\(lines\\)}'\\)\n\" 2>&1)", - "Bash(C:/Users/lmnohama/AppData/Local/Programs/Python/Python310/python.exe -c \"\nimport sys\nfpath = 'D:/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai/DBS/2026-03-12/schema.sql'\nwith open\\(fpath, 'r', encoding='utf-8'\\) as f:\n lines = f.readlines\\(\\)\nsys.stdout.buffer.write\\(\\('Total lines: ' + str\\(len\\(lines\\)\\) + '\\\\n'\\).encode\\('utf-8'\\)\\)\n\" 2>&1)", - "Bash(find /d/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai -type f \\\\\\( -name \"*convenio*\" -o -name \"*Convenio*\" \\\\\\) 2>/dev/null | head -20)", - "Bash(find:*)", - "Bash(ls:*)", - "Bash(npx vite:*)", - "Bash(powershell -Command \"$content = [System.IO.File]::ReadAllText\\(''src/views/pages/clinic/clinic/ClinicFeaturesPage.vue'', [System.Text.Encoding]::UTF8\\); $content = $content -replace [char]0x201C, ''\"\"'' -replace [char]0x201D, ''\"\"''; [System.IO.File]::WriteAllText\\(''src/views/pages/clinic/clinic/ClinicFeaturesPage.vue'', $content, [System.Text.Encoding]::UTF8\\)\")", - "Bash(xargs cat:*)", - "Bash(xxd \"D:/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai/DBS/2026-03-21/schema.sql\")", - "Bash(iconv -f UTF-16LE -t UTF-8 \"D:/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai/DBS/2026-03-21/schema.sql\")", - "Bash(mkdir -p docs/billing docs/planos docs/subscription-health docs/estrategia docs/specs)", - "Bash(mkdir -p database/backups database/migrations database/seeds database/fixes database/snippets)", - "Bash(cd:*)", - "Bash(mv disparando-whatsapp-local.md docs/whatsapp.md)", - "Bash(mv comandos.txt docs/)", - "Bash(mv dados-padrões-da-agenda.txt docs/)", - "Bash(mv USER_ARCHETYPES.html docs/)", - "Bash(mv Novo-DB/migration_*.sql database/migrations/)", - "Bash(mv Novo-DB/seed_*.sql database/seeds/)", - "Bash(mv Novo-DB/fix_*.sql database/fixes/)", - "Bash(npm install:*)", - "Bash(cp:*)", - "Bash(npm uninstall:*)", - "Bash(rm:*)", - "Bash(docker ps:*)", - "Bash(docker exec:*)", - "Bash(\"D:/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai/database-novo/backups/2026-03-23/schema.sql\")", - "Bash(\"D:/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai/database-novo/backups/2026-03-23/data.sql\")", - "Bash(\"D:/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai/database-novo/backups/2026-03-23/full_dump.sql\")", - "Bash(wc:*)", - "Bash(python _wizard_patch.py)", - "Bash(rm _wizard_patch.py)", - "Bash(npm ls:*)", - "Bash(find /d/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai/src/features/patients -type f \\\\\\(-name *.vue -o -name *.js \\\\\\))" - ] - } -} diff --git a/.gitignore b/.gitignore index 3a4237e..1dd5a6c 100644 --- a/.gitignore +++ b/.gitignore @@ -14,5 +14,16 @@ dist-*/ api-generator/typedoc.json **/.DS_Store Dev-documentacao/ -supabase/ +supabase/* +!supabase/functions/ evolution-api/ + +# Backups locais do banco — não comitar (regeneráveis via db.cjs backup) +database-novo/backups/ + +# Outputs do Playwright +test-results/ +playwright-report/ + +# Config local do Claude Code (cada dev tem o seu) +.claude/settings.local.json diff --git a/DOCKER-SETUP.md b/DOCKER-SETUP.md new file mode 100644 index 0000000..4fa0d62 --- /dev/null +++ b/DOCKER-SETUP.md @@ -0,0 +1,138 @@ +# Docker Setup — Projetos Locais + +## Tabela Resumo + +| Projeto | Container(s) | Porta Host | Rede | Volume(s) | +|---|---|---|---|---| +| **AgenciaPsi** | `agenciapsi_app` | `5173` → Vite dev | `agenciapsi_net` | `agenciapsi_node_modules` | +| | `agenciapsi_mysql` | `3307` → MySQL | `agenciapsi_net` | `agenciapsi_mysql_data` | +| **Evolution API** | `evolution_api` | `8080` → API | `agenciapsi_net` (external) | — | +| | `evolution_db` | interno | `agenciapsi_net` | `evolution_db_data` | +| | `evolution_redis` | interno | `agenciapsi_net` | — | +| | `evolution_mailpit` | `1025` SMTP / `8025` Web | `agenciapsi_net` | — | +| **Supabase AgenciaPsi** | `supabase_*_agenciapsi-primesakai` | `54321` API / `54322` PG / `54323` Studio | — | volumes internos | +| **Sakai-Vue** | `sakaivue_app` | `5174` → Vite dev | `sakaivue_net` | `sakaivue_node_modules` | +| | `sakaivue_mysql` | `3308` → MySQL | `sakaivue_net` | `sakaivue_mysql_data` | +| **Supabase Sakai-Vue** | `supabase_*_sakai-vue` | `54331` API / `54332` PG / `54333` Studio | — | volumes internos | +| **Gisaf Local** | `gisaf_mysql` | `3309` → MySQL | `gisaf_net` | `gisaf_mysql_data` | + +## Mapa de Portas + +| Porta | Serviço | +|---|---| +| 3307 | AgenciaPsi MySQL | +| 3308 | Sakai-Vue MySQL | +| 3309 | Gisaf MySQL | +| 5173 | AgenciaPsi Vite dev | +| 5174 | Sakai-Vue Vite dev | +| 8080 | Evolution API | +| 1025 | Mailpit SMTP | +| 8025 | Mailpit Web UI | +| 54321 | Supabase AgenciaPsi — Kong (API) | +| 54322 | Supabase AgenciaPsi — PostgreSQL | +| 54323 | Supabase AgenciaPsi — Studio | +| 54327 | Supabase AgenciaPsi — Analytics | +| 54331 | Supabase Sakai-Vue — Kong (API) | +| 54332 | Supabase Sakai-Vue — PostgreSQL | +| 54333 | Supabase Sakai-Vue — Studio | +| 54337 | Supabase Sakai-Vue — Analytics | + +## Ordem de Start + +```bash +# 1. AgenciaPsi (cria a rede agenciapsi_net) +cd "D:/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai" +docker compose up -d + +# 2. Supabase AgenciaPsi (porta 54321) +cd "D:/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai" +npx supabase start + +# 3. Evolution API (depende da rede agenciapsi_net) +cd "D:/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai/evolution-api" +docker compose up -d + +# 4. Sakai-Vue +cd "D:/leonohama/UniaoApp.com.br/Sistema/sakai-vue" +docker compose up -d + +# 5. Supabase Sakai-Vue (porta 54331) +cd "D:/leonohama/UniaoApp.com.br/Sistema/sakai-vue" +npx supabase start + +# 6. Gisaf Local +cd "D:/leonohama/UniaoApp.com.br/Gisaf Local" +docker compose up -d +``` + +## Parar tudo + +```bash +# Na ordem inversa +cd "D:/leonohama/UniaoApp.com.br/Gisaf Local" && docker compose down +cd "D:/leonohama/UniaoApp.com.br/Sistema/sakai-vue" && npx supabase stop +cd "D:/leonohama/UniaoApp.com.br/Sistema/sakai-vue" && docker compose down +cd "D:/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai/evolution-api" && docker compose down +cd "D:/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai" && npx supabase stop +cd "D:/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai" && docker compose down +``` + +## Caminhos dos docker-compose.yml + +| Projeto | Caminho | +|---|---| +| AgenciaPsi | `D:\leonohama\AgenciaPsi.com.br\Sistema\agenciapsi-primesakai\docker-compose.yml` | +| Evolution API | `D:\leonohama\AgenciaPsi.com.br\Sistema\agenciapsi-primesakai\evolution-api\docker-compose.yml` | +| Sakai-Vue | `D:\leonohama\UniaoApp.com.br\Sistema\sakai-vue\docker-compose.yml` | +| Gisaf Local | `D:\leonohama\UniaoApp.com.br\Gisaf Local\docker-compose.yml` | + +## DBeaver — Conexões MySQL + +| Conexão | Host | Port | Database | User | Password | +|---|---|---|---|---|---| +| Gisaf | `localhost` | `3309` | `sindsp` | `sindsp` | `marlboro` | +| AgenciaPsi | `localhost` | `3307` | `agenciapsi` | `agenciapsi` | `agenciapsi123` | +| Sakai-Vue | `localhost` | `3308` | `sakaivue` | `sakaivue` | `sakaivue123` | + +Para criar cada conexão: **Database → New Database Connection → MySQL → preencher dados → Test Connection → Finish** + +## Supabase — Instancias Locais + +Cada projeto tem sua propria instancia Supabase (schemas diferentes, nao podem compartilhar). + +| Projeto | API URL | Studio | PostgreSQL | Anon Key | +|---|---|---|---|---| +| AgenciaPsi | `http://127.0.0.1:54321` | `http://127.0.0.1:54323` | `127.0.0.1:54322` | `sb_publishable_ACJWlzQHlZjBrEguHvfOxg_3BJgxAaH` | +| Sakai-Vue | `http://127.0.0.1:54331` | `http://127.0.0.1:54333` | `127.0.0.1:54332` | `sb_publishable_ACJWlzQHlZjBrEguHvfOxg_3BJgxAaH` | + +**Resetar banco (aplica migrations + seed):** + +```bash +# AgenciaPsi +cd "D:/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai" +npx supabase db reset + +# Sakai-Vue +cd "D:/leonohama/UniaoApp.com.br/Sistema/sakai-vue" +npx supabase db reset +``` + +### Sakai-Vue — Usuarios de teste + +| Email | Senha | Role | +|---|---|---| +| `dev@sistema.com.br` | `Dev@12345` | dev | +| `master@tenant.com.br` | `Master@12345` | master | +| `admin@tenant.com.br` | `Admin@12345` | admin | +| `chefe@tenant.com.br` | `Chefe@12345` | chefe_setor | +| `servidor@tenant.com.br` | `Servidor@12345` | servidor | +| `leitura@tenant.com.br` | `Leitura@12345` | leitura | + +## Importar dump SQL no Gisaf + +```bash +# Via CLI (já feito) +docker exec -i gisaf_mysql mysql -usindsp -pmarlboro sindsp < "D:/leonohama/UniaoApp.com.br/Gisaf Local/Dump20260330.sql" +``` + +Ou via DBeaver: conectar no banco `sindsp` → **Tools → Execute SQL Script** → selecionar `Dump20260330.sql` diff --git a/HANDOFF.md b/HANDOFF.md new file mode 100644 index 0000000..d24b21c --- /dev/null +++ b/HANDOFF.md @@ -0,0 +1,174 @@ +# HANDOFF — 2026-04-19 (Sessão 6) + +Documento de continuidade. Quando você voltar, comece lendo esta página. Todo o trabalho está registrado no banco (`/saas/desenvolvimento` → **Verificações**, **Auditoria**, **Testes**) — este arquivo é só o mapa. + +--- + +## 📊 Estado atual + +| Tipo | Aberto | Notas | +|---|---|---| +| **A# auditoria** | **0** | ✅ todas as 30 resolvidas | +| **V# verificações** | **9** | auth(3), pacientes(3), saas(2), router(1) | +| **T# testes** | **2 a escrever** | T#9 useAgendaEvents wrapper, T#10 E2E | +| **Áreas não auditadas** | **3** | financeiro, comunicação, documentos/prontuários | +| **Migrations não commitadas** | **8** | Sessão 6 (ver lista abaixo) | +| **Vitest** | **179/179** | 8 suites | +| **SQL integration tests** | **33/33** | `database-novo/tests/run.cjs` | + +--- + +## ✅ Sessão 6 (hoje, 2026-04-19) — resumo + +### Bloco 1 — V#34 + V#41 (Opção B2: plano + override + exceção comercial) +- **Migration** `20260419000001_tenant_features_b2_governance.sql`: + - Trigger `tenant_features_guard_with_plan` ganhou bypass via session flag (`current_setting('app.allow_feature_exception')`) + - Nova RPC `set_tenant_feature_exception(tenant_id, feature_key, enabled, reason)` SECURITY DEFINER com regras assimétricas: + - `enabled=false` → tenant_admin OU saas_admin (preferência do cliente) + - `enabled=true` AND plano permite → tenant_admin OU saas_admin + - `enabled=true` AND plano NÃO permite → **só saas_admin + reason obrigatório** (exceção comercial) + - Policy `tenant_features_write_saas_only` — writes diretos só saas_admin +- **Store** `tenantFeaturesStore.isEnabled` reescrito (B2): override negativo desliga, override positivo liga, sem override segue plano +- **Store** `setForTenant` agora chama RPC com `reason` opcional +- **UI nova** `/saas/tenant-features` (selector de tenant, catálogo, dialog com `reason` obrigatório p/ exceção, log de mudanças) +- **JSDoc** documentando separação semântica (`entitlementsStore.has` = "plano permite?" vs `tenantFeaturesStore.isEnabled` = "ativo agora?") +- **Testes** `src/stores/__tests__/tenantFeaturesStore.spec.js` — 17 cenários incluindo regressão V#34 + +### Bloco 2 — Pendentes Sessão 5 +- **V#42** — `entitlementsStore.loadFor*` no catch agora NÃO marca como carregado (estado fica como "not loaded" → próximo request retenta) + `logError` adicionado +- **V#40** — `features.is_active` (migration `...02`) + UI: soft delete + filtro "Mostrar depreciados" + Tag de status + botão Reativar +- **V#36** — RPC `delete_plan_safe` (migration `...03`): bloqueia DELETE se houver subscriptions ativas. SaasPlansPage migrada +- **V#35** — Migration `...04`: 17 → 11 policies. Removidas 3 read-auth duplicadas em plans/features/plan_features + 3 subsets/no-ops em subscriptions. `COMMENT ON POLICY` em todas + +### Bloco 3 — Testes T#5 / T#7 / T#8 +- **T#5** `tenantStore.spec.js` — 15 testes: singleflight, regressão V#5 (não herdar tenant de outro user), erros, setActiveTenant, reset, getters. Stub localStorage in-memory (env=node sem jsdom) +- **T#7** `validators.spec.js` — 38 testes: sanitização do intake (digitsOnly, CPF/CNPJ, phone, email, CEP, toISODate) +- **T#8** `database-novo/tests/run.cjs` — runner Node + docker exec. **33 cenários SQL** cobrindo set_tenant_feature_exception, delete_plan_safe, intake, features.is_active, defesa em camadas, twilio config + +### Bloco 4 — A#20 (CAPTCHA) — rev2 self-hosted +**Decisão:** descartado Cloudflare Turnstile / hCaptcha em favor de defesa em camadas self-hosted. Razões: zero LGPD, zero provider, zero fricção pro paciente legítimo (UX importa muito em paciente vulnerável buscando atendimento). + +5 camadas: +1. **Honeypot** — campo invisível (frontend), bot rejeita +2. **Validação** básica +3. **Rate limit por IP** — `check_rate_limit` RPC +4. **Math captcha condicional** — só ativa após N falhas do mesmo IP (default 3) +5. **Modo paranoid** global toggle (saas_security_config.captcha_required_globally) + +Implementação: +- Migration `...06` (4 tabelas: `saas_security_config`, `public_submission_attempts`, `submission_rate_limits`, `math_challenges`) +- Migration `...07` (RPCs: `check_rate_limit`, `record_submission_attempt`, `generate_math_challenge`, `verify_math_challenge`, `cleanup_expired_math_challenges`) +- Edge function `submit-patient-intake` reescrita (dual endpoint: submit + `/captcha-challenge`) +- Componente `MathCaptchaChallenge.vue` (lazy) +- Tela `/saas/security` com card explicativo (6 seções), KPIs 24h, toggles, sliders, dashboard de IPs ativos, modo paranoid + +### Bloco 5 — SaaS Twilio Config (operacional editável) +- Migration `...08` (tabela `saas_twilio_config` singleton + RPCs `get_twilio_config` / `update_twilio_config`) +- **Decisão de segurança**: `TWILIO_AUTH_TOKEN` permanece em env var (único secret); SID/webhook/rate/margem migram pra DB +- Edge function `twilio-whatsapp-provision` lê do banco com fallback pra env (back-compat) +- Tela `/saas/twilio-config` com card explicativo + status do AUTH_TOKEN (ping pra detectar se está setado) +- **Bug fix**: `friendlyErrorMessage()` em `twilioWhatsappService.js` traduz "Edge Function returned a non-2xx status code" pra mensagens contextuais + +--- + +## 🛑 Pendências (V# abertos por área) + +### auth (3) +- **V#2** (médio) — Listener `supabase.auth.onAuthStateChange` duplicado +- **V#6** (baixo) — `globalRole` cache sem TTL e sem invalidação por realtime +- **V#10** (médio) — Bloqueio SaaS em tenant-app só por `startsWith` de path (frágil) + +### pacientes (3) +- **V#3** (médio) — Pacientes não tem composables/services — toda lógica em pages +- **V#8** (baixo) — `agenda_eventos.select` patient_id com `limit 1000` arbitrário +- **V#9** (médio) — `PatientsCadastroPage` com **1985 linhas** (precisa quebrar) + +### router (1) +- **V#9** (baixo) — `ensureMenuBuilt` roda em toda navegação autenticada + +### saas (2) +- **V#17** (baixo) — 23 `console.*` em páginas SaaS +- **V#18** (baixo) — `tenantFeaturesStore` sem TTL real (cache pode ficar stale) + +--- + +## 🗂️ Migrations criadas hoje (ordem cronológica) + +``` +database-novo/migrations/ +├── 20260419000001_tenant_features_b2_governance.sql (V#34/V#41) +├── 20260419000002_features_is_active.sql (V#40) +├── 20260419000003_delete_plan_safe.sql (V#36) +├── 20260419000004_consolidate_policies.sql (V#35) +├── 20260419000005_restrict_intake_rpc.sql (A#20 — REVOKE anon) +├── 20260419000006_layered_bot_defense.sql (A#20 rev2 — schema) +├── 20260419000007_bot_defense_rpcs.sql (A#20 rev2 — RPCs) +└── 20260419000008_saas_twilio_config.sql (Twilio config) +``` + +Todas aplicadas no banco local. **Se for pra cloud, aplicar nessa ordem.** + +--- + +## 🗜️ Arquivos criados/modificados (código) + +**Criados:** +``` +src/components/security/MathCaptchaChallenge.vue +src/views/pages/saas/SaasTenantFeaturesPage.vue +src/views/pages/saas/SaasSecurityPage.vue +src/views/pages/saas/SaasTwilioConfigPage.vue +src/stores/__tests__/tenantStore.spec.js +src/stores/__tests__/tenantFeaturesStore.spec.js +src/utils/__tests__/validators.spec.js +database-novo/tests/run.cjs +supabase/functions/submit-patient-intake/index.js (reescrita) +``` + +**Modificados:** +``` +src/stores/tenantFeaturesStore.js (lógica B2 + RPC) +src/stores/entitlementsStore.js (V#42 fix + JSDoc) +src/services/twilioWhatsappService.js (friendlyErrorMessage) +src/views/pages/saas/SaasFeaturesPage.vue (V#40 soft delete) +src/views/pages/saas/SaasPlansPage.vue (V#36 RPC) +src/views/pages/saas/SaasTwilioWhatsappPage.vue (toast warn em vez de error) +src/views/pages/clinic/clinic/ClinicFeaturesPage.vue (texto erro plano-denied) +src/views/pages/public/CadastroPacienteExterno.vue (honeypot + math captcha) +src/router/routes.saas.js (3 rotas novas) +src/navigation/menus/saas.menu.js (3 itens menu novos) +supabase/functions/twilio-whatsapp-provision/index.ts (lê config DB) +.env (limpo Turnstile vars) +``` + +--- + +## 📊 Números finais + +| Métrica | Antes (Sessão 5) | Hoje | +|---|---|---| +| A# auditoria | 30 (1 aberto) | 30 (0 abertos) | +| V# verificações | 42 (15 abertos) | 42 (9 abertos) | +| Suites de teste vitest | 6 (109 tests) | 8 (179 tests) | +| Suites SQL integration | 0 | 1 (33 tests) | +| Migrations totais | 5 (Sessão 5) | 13 (+8 hoje) | +| Telas SaaS novas | — | 3 (`/tenant-features`, `/security`, `/twilio-config`) | + +--- + +## 🎯 Ordem sugerida quando voltar + +1. **Decidir se commita** o trabalho da Sessão 6 (~30 arquivos, 8 migrations). +2. **Continuar Sessão 6** (em andamento): A+B+C+D escolhidos, faltando concluir B/C/D. +3. Outras opções: + - **Nova área de revisão sênior** — financeiro / comunicação / documentos + - **Deploy real** (Supabase cloud + secrets + edge functions) + - **Testes T#9 + T#10** + +--- + +## ⚠️ Nada commitado (ainda) + +Working directory tem ~30 arquivos modificados desde Sessão 5. Revise com `git status` + `git diff` antes de decidir o que comitar. + +**Nada quebrou:** vitest 179/179 + SQL 33/33. diff --git a/commit.md b/commit.md new file mode 100644 index 0000000..515ce0e --- /dev/null +++ b/commit.md @@ -0,0 +1,237 @@ +# Sessões 1-6 acumuladas — hardening, defesa em profundidade, +192 testes + +Repositório estava sem commit há ~5 sessões de trabalho intenso. Este commit +consolida tudo que foi feito desde o último marco (`d088a89`). + +**A# auditoria:** 30 itens registrados, **0 abertos**. +**V# verificações:** 52 registradas (10 novas em Documentos), **5 abertas** (todas adiadas com plano). +**T# testes:** 10/10 escritas. **192 vitest + 33 SQL + 5 E2E** passando. + +--- + +## Sessão 1 — auth/router/session + +- A#7 resolvido (`window.__guardsBound`) +- 10 verificações registradas (V#1–V#10), 5 corrigidas: + - `session.js` logger inconsistente + - `tenantStore.ensureLoaded` polling + - `normalizeRole` duplicado → extraído pra `src/utils/roleNormalizer.js` + - `console.error` em `router/index.js` + - `.single()` em `fetchRole` + +--- + +## Sessão 2 — agenda + +- 11 verificações (V#11–V#21), 10 corrigidas: + - `useRecurrence` CRUD ganhou filtro `tenant_id` (alto) + - `agenda.service.js` vazio deletado + - `agendaRepository` ↔ `useAgendaEvents` consolidados (composable virou wrapper fino, 181→67 linhas) + - `AGENDA_EVENT_SELECT` centralizado em `agendaSelects.js` + - `_tenantGuards.js` compartilhado + - V#21 status `remarcar` → `remarcado` padronizado em 14 edições + +--- + +## Sessão 3 — pacientes + +- 10 verificações (V#22–V#31), 6 corrigidas + 4 documentadas +- 5 arquivos obsoletos deletados (PatientsCadastroPage Bkp, preview, prontuário design 1/2/3) +- `tenant_id` em todas queries de patients (alto) +- 9 `console.*` migrados pra logger +- `hydrateAssociations` paralelizada (5 round-trips → 2) +- `.maybeSingle()` onde precisava + +--- + +## Sessão 4 — security review (página pública de cadastro) + +- V#31 virou security review completa: **15 vulnerabilidades (A#15–A#29), 14 corrigidas** +- **Críticos:** + - **A#15** bucket `avatars` público → 5MB + mime whitelist + policies restritas + - **A#16** RPC v2 ignorava `active/expires/max_uses` → validação completa + incrementa `uses` + - **A#17** `notas_internas` exposto ao paciente → removido do form e RPC + - **A#18** `Math.random()` pra token → RPCs server-side via `gen_random_uuid()` + - **A#19** intake sem `tenant_id` → RPC resolve via `patient_invites` ou `tenant_members` +- **Médios:** log `patient_invite_attempts` (A#24), política LGPD (A#25), botão mock só em DEV (A#26), length caps server-side (A#27) +- **Baixos:** duplicado `PatientsExternalLinkPage` deletado, `Landingpage-v1 - bkp.vue` deletado + +--- + +## Sessão 5 — SaaS (planos, preços, recursos) + +- 10 verificações (V#33–V#42) +- **🔴 P0:** A#30 — 7 tabelas SaaS com RLS OFF + `GRANT ALL` pra anon. Migration `...05_saas_rls_emergency_fix` aplicou REVOKE + ENABLE RLS + 9 policies corretas +- 109/109 testes passando + +--- + +## Sessão 6 (HOJE, 2026-04-19) — bloco principal + +### V#34 + V#41 — Opção B2 (plano + override + exceção comercial) + +Resolve `tenantFeaturesStore.isEnabled` que retornava `true` por default +(qualquer feature aparecia ativa pra qualquer tenant) E a dupla-fonte com +`entitlementsStore`. + +**Backend** (migration `...01`): +- Trigger `tenant_features_guard_with_plan` ganhou bypass via session flag +- RPC `set_tenant_feature_exception` SECURITY DEFINER com regras assimétricas: + - `enabled=false` → tenant_admin OU saas_admin (preferência) + - `enabled=true` AND plano permite → tenant_admin OU saas_admin + - `enabled=true` AND plano NÃO permite → **só saas_admin + reason obrigatório** +- Policy `tenant_features_write_saas_only` + +**Frontend:** +- `tenantFeaturesStore.isEnabled` reescrito (B2): override negativo desliga, override positivo liga (exceção), sem override segue plano +- `setForTenant` chama RPC com `reason` +- Tela nova `/saas/tenant-features` com dialog de motivo obrigatório +- JSDoc separação semântica: `entitlementsStore.has` = "plano permite?" vs `tenantFeaturesStore.isEnabled` = "ativo agora?" +- 17 testes em `tenantFeaturesStore.spec.js` + +### Pendentes Sessão 5 fechados + +- **V#35** — 17→11 policies (consolidadas plans/features/plan_features/subscriptions) + `COMMENT ON POLICY` +- **V#36** — RPC `delete_plan_safe` bloqueia DELETE com subscriptions ativas +- **V#40** — `features.is_active` (soft delete) + UI com filtro/Reativar +- **V#42** — `entitlementsStore.loadFor*` no catch não marca como carregado + `logError` + +### Testes T#5/T#7/T#8 + +- **T#5** `tenantStore.spec.js` — 15 testes (singleflight, regressão V#5, erros, setActiveTenant, reset, getters) +- **T#7** `validators.spec.js` — 38 testes (sanitização do intake) +- **T#8** `database-novo/tests/run.cjs` — runner Node + docker exec, 33 cenários SQL + +### A#20 (CAPTCHA) — rev2 self-hosted + +**Decisão:** descartado Cloudflare Turnstile / hCaptcha em favor de defesa em +camadas self-hosted. Razões: zero LGPD, zero provider, zero fricção pro paciente +legítimo (UX importa em paciente vulnerável buscando atendimento). + +**5 camadas:** +1. **Honeypot** — campo invisível +2. **Validação** básica +3. **Rate limit por IP** — `check_rate_limit` RPC +4. **Math captcha condicional** — só ativa após N falhas (default 3) +5. **Modo paranoid** global toggle + +**Implementação:** +- Migrations `...06` (4 tabelas) + `...07` (RPCs) +- Edge function `submit-patient-intake` reescrita (dual endpoint) +- Componente `MathCaptchaChallenge.vue` lazy +- Tela `/saas/security` com card explicativo (6 seções), KPIs 24h, toggles, sliders, dashboard de IPs + +### SaaS Twilio Config (UI editável) + +- Migration `...08` (singleton + RPCs `get_twilio_config`/`update_twilio_config`) +- **AUTH_TOKEN permanece em env var** (único secret); SID/webhook/rate/margem migram pra DB +- Edge function lê do banco com fallback pra env (back-compat) +- Tela `/saas/twilio-config` com card + status do AUTH_TOKEN +- Bug fix: `friendlyErrorMessage()` traduz "Edge Function returned a non-2xx status code" + +### Revisão sênior em Documentos/prontuários + +10 V# novas registradas, 7 corrigidas, 3 adiadas. + +**Críticos:** +- 🔴 **V#43/V#44** vazamento entre clínicas via storage policies — corrigido com tenant scoping no path `(storage.foldername(name))[1]::uuid IN tenant_members` +- 🔴 **V#45** documents policy pobre (só `owner_id = auth.uid()`) — separada em SELECT/INSERT/UPDATE/DELETE com tenant scoping + +**Altos:** +- 🟠 **V#46** share_links sem incremento de usos — RPC `validate_share_token` atomicamente valida + incrementa + loga +- 🟠 **V#47** signatures policy ALL — separada (UPDATE só pra signatário) + +**Médios:** +- 🟡 **V#48** access_logs WITH CHECK +- 🟡 **V#49** templates WITH CHECK + +### B-block (V# avulsos) + +- **V#2** Listener `onAuthStateChange` consolidado (session.js virou autoridade + API `onSessionEvent`) +- **V#6** `globalRoleCache` TTL 5min +- **V#10** Bloqueio SaaS via `meta.area`/`meta.saasAdmin` em vez de `path.startsWith` +- **V#8** RPC `get_patient_session_counts` substitui `.limit(1000)` arbitrário +- **V#9 router** short-circuit `lastEnsureKey` em ensureMenuBuilt +- **V#17** 25 `console.*` eliminados em src/views/pages/saas/ +- **V#18** TTL real em tenantFeaturesStore + +### T#9 + T#10 + +- **T#9** `useAgendaEvents.spec.js` — 13 testes do wrapper +- **T#10** Playwright + Chromium instalados; 5 specs E2E em `e2e/patient-intake.spec.js` +- **Bug fix achado pelo E2E**: `CadastroPacienteExterno.enviar` não extraía body do erro 403 — corrigido + +--- + +## 📦 Migrations consolidadas (todas as sessões) + +``` +20260417000001_dev_tables (Sessão pré-1: tabelas dev) +20260417000002_dev_tables_ordem +20260418000001_dev_verificacoes (Sessão 1) +20260418000002_patient_intake_security_hardening (Sessão 4) +20260418000003_patient_invite_attempts_log (Sessão 4) +20260418000004_dev_tests (Sessão 1) +20260418000005_saas_rls_emergency_fix (Sessão 5 — P0) +20260419000001_tenant_features_b2_governance (Sessão 6 — V#34/V#41) +20260419000002_features_is_active (Sessão 6 — V#40) +20260419000003_delete_plan_safe (Sessão 6 — V#36) +20260419000004_consolidate_policies (Sessão 6 — V#35) +20260419000005_restrict_intake_rpc (Sessão 6 — A#20) +20260419000006_layered_bot_defense (Sessão 6 — A#20 rev2) +20260419000007_bot_defense_rpcs (Sessão 6 — A#20 rev2) +20260419000008_saas_twilio_config (Sessão 6) +20260419000009_patient_session_counts_rpc (Sessão 6 — V#8) +20260419000010_documents_security_hardening (Sessão 6 — V#43-V#49) +``` + +--- + +## 🆕 Pastas/arquivos novos importantes + +- `e2e/` — specs Playwright (T#10) +- `playwright.config.js` — config E2E +- `database-novo/tests/run.cjs` — runner SQL integration tests (T#8) +- `database-novo/backups/` agora ignorado (regenerável via `db.cjs backup`) +- `src/components/security/MathCaptchaChallenge.vue` — A#20 rev2 +- `src/views/pages/saas/SaasTenantFeaturesPage.vue` — V#34 +- `src/views/pages/saas/SaasSecurityPage.vue` — A#20 rev2 + card educativo +- `src/views/pages/saas/SaasTwilioConfigPage.vue` — UI Twilio editável +- `src/utils/roleNormalizer.js` — Sessão 1 +- `src/features/agenda/services/_tenantGuards.js` + `agendaSelects.js` — Sessão 2 +- 6 specs novas em `__tests__/` (vitest) +- `supabase/functions/submit-patient-intake/` — edge function reescrita A#20 rev2 + +--- + +## 🛠️ .gitignore ajustado neste commit + +- `supabase/*` + `!supabase/functions/` (mantém edge functions, ignora `.temp/`/`migrations/`/etc gerados pelo CLI) +- `database-novo/backups/` (backups regeneráveis) +- `test-results/`, `playwright-report/` (outputs Playwright) +- `.claude/settings.local.json` (config local do harness) + +--- + +## 📊 Números finais + +| Métrica | Início | Fim | +|---|---|---| +| A# abertos | 30 (a registrar) | **0** | +| V# abertos | 52 (a registrar) | **5** (adiados) | +| T# escritas | 0/10 | **10/10** | +| Vitest | — | **192/192** | +| SQL integration | — | **33/33** | +| E2E (Playwright) | — | **5/5** | +| Migrations | 0 | **17** | +| Telas SaaS novas | — | 3 | +| Edge functions reescritas | — | 1 (`submit-patient-intake`) | + +--- + +## ⚠️ Adiados (próximas sessões — plano completo no DB) + +- **V#3 + V#9 pacientes** — refatoração de composables/services (PatientsCadastroPage 1985 linhas). Sessão dedicada de 1-2h +- **V#50/V#51/V#52 documentos** — portal-paciente policy, hash SHA-256, retention cron +- **Áreas não auditadas:** financeiro, comunicação +- **Deploy real**: cloud Supabase + secrets + edge functions diff --git a/database-novo/agenciapsi-db-dashboard.html b/database-novo/agenciapsi-db-dashboard.html index fda3c58..d1185ca 100644 --- a/database-novo/agenciapsi-db-dashboard.html +++ b/database-novo/agenciapsi-db-dashboard.html @@ -3,487 +3,310 @@ -AgenciaPsi — Database Dashboard +AgenciaPsi DB · 2026-04-17 -
-
AgênciaPsi DB
- -
-
86 tabelas
-
82 FKs
-
24 views
+
AgênciaPsi DB
+ 2026-04-17 · 17/04/2026, 09:29:14 + +
+
97 tabelas
+
99 FKs
+
24 views
+
1307 colunas
+
23 infra
-
- -
+ +
- - + \ No newline at end of file diff --git a/database-novo/db.cjs b/database-novo/db.cjs index f80d68d..ffcd420 100644 --- a/database-novo/db.cjs +++ b/database-novo/db.cjs @@ -5,15 +5,17 @@ // Uso: node db.cjs [opcoes] // // Comandos: -// setup Instalação do zero (schema + seeds) -// backup Exporta backup com data atual -// restore [data] Restaura de um backup (ex: 2026-03-23) +// setup Instalação do zero (schema + fixes + seeds + migrations) +// backup Exporta backup com data atual (+ supabase_restore.sql) +// restore [data] Restaura de um backup (ex: 2026-04-17) // migrate Aplica migrations pendentes // seed [grupo] Roda seeds (all|users|system|test_data) // status Mostra estado atual do banco // diff Compara schema atual vs último backup // reset Reseta o banco e reinstala tudo // verify Verifica integridade dos dados essenciais +// schema-export Exporta schema granular para schema/00_full..10_grants +// dashboard Gera dashboard HTML interativo (tabelas + infra) // help Mostra ajuda // ============================================================================= @@ -30,6 +32,9 @@ const CONFIG = JSON.parse(fs.readFileSync(path.join(ROOT, 'db.config.json'), 'ut const CONTAINER = CONFIG.container; const DB = CONFIG.database; const USER = CONFIG.user; +const MIGRATIONS_DIR = path.resolve(ROOT, CONFIG.migrationsDir || 'migrations'); +const SEEDS_DIR = path.resolve(ROOT, CONFIG.seedsDir || 'seeds'); +const FIXES_DIR = path.resolve(ROOT, CONFIG.fixesDir || 'fixes'); // --------------------------------------------------------------------------- // Colors (sem dependências externas) @@ -45,27 +50,13 @@ const c = { gray: '\x1b[90m' }; -function log(msg) { - console.log(msg); -} -function info(msg) { - log(`${c.cyan}ℹ${c.reset} ${msg}`); -} -function ok(msg) { - log(`${c.green}✔${c.reset} ${msg}`); -} -function warn(msg) { - log(`${c.yellow}⚠${c.reset} ${msg}`); -} -function err(msg) { - log(`${c.red}✖${c.reset} ${msg}`); -} -function title(msg) { - log(`\n${c.bold}${c.blue}═══ ${msg} ═══${c.reset}\n`); -} -function step(msg) { - log(`${c.gray} →${c.reset} ${msg}`); -} +function log(msg) { console.log(msg); } +function info(msg) { log(`${c.cyan}ℹ${c.reset} ${msg}`); } +function ok(msg) { log(`${c.green}✔${c.reset} ${msg}`); } +function warn(msg) { log(`${c.yellow}⚠${c.reset} ${msg}`); } +function err(msg) { log(`${c.red}✖${c.reset} ${msg}`); } +function title(msg){ log(`\n${c.bold}${c.blue}═══ ${msg} ═══${c.reset}\n`); } +function step(msg) { log(`${c.gray} →${c.reset} ${msg}`); } // --------------------------------------------------------------------------- // Helpers @@ -86,21 +77,31 @@ function dockerRunning() { function psql(sql, opts = {}) { const cmd = `docker exec -i -e PGCLIENTENCODING=UTF8 ${CONTAINER} psql -U ${USER} -d ${DB} ${opts.tuples ? '-t' : ''} ${opts.quiet ? '-q' : ''} -c "${sql.replace(/"/g, '\\"')}"`; - return execSync(cmd, { encoding: 'utf8', timeout: 30000, stdio: ['pipe', 'pipe', 'pipe'], env: { ...process.env, PYTHONIOENCODING: 'utf-8', LANG: 'C.UTF-8' } }).trim(); + return execSync(cmd, { + encoding: 'utf8', + timeout: 30000, + stdio: ['pipe', 'pipe', 'pipe'], + env: { ...process.env, PYTHONIOENCODING: 'utf-8', LANG: 'C.UTF-8' } + }).trim(); } function psqlFile(filePath) { const absPath = path.resolve(filePath); const content = fs.readFileSync(absPath, 'utf8'); - // Prepend SET client_encoding to ensure UTF-8 inside the session const utf8Content = "SET client_encoding TO 'UTF8';\n" + content; const cmd = `docker exec -i -e PGCLIENTENCODING=UTF8 ${CONTAINER} psql -U ${USER} -d ${DB} -q`; - return execSync(cmd, { input: utf8Content, encoding: 'utf8', timeout: 120000, stdio: ['pipe', 'pipe', 'pipe'], env: { ...process.env, PYTHONIOENCODING: 'utf-8', LANG: 'C.UTF-8' } }); + return execSync(cmd, { + input: utf8Content, + encoding: 'utf8', + timeout: 120000, + stdio: ['pipe', 'pipe', 'pipe'], + env: { ...process.env, PYTHONIOENCODING: 'utf-8', LANG: 'C.UTF-8' } + }); } function pgDump(args) { const cmd = `docker exec -e PGCLIENTENCODING=UTF8 ${CONTAINER} pg_dump -U ${USER} -d ${DB} ${args}`; - return execSync(cmd, { encoding: 'utf8', timeout: 120000, maxBuffer: 50 * 1024 * 1024 }); + return execSync(cmd, { encoding: 'utf8', timeout: 180000, maxBuffer: 100 * 1024 * 1024 }); } function today() { @@ -168,6 +169,89 @@ function recordMigration(filename, hash, category) { psql(`INSERT INTO _db_migrations (filename, hash, category) VALUES ('${filename}', '${hash}', '${category}') ON CONFLICT (filename) DO UPDATE SET hash = EXCLUDED.hash, applied_at = now();`); } +// --------------------------------------------------------------------------- +// Supabase-compatible dump builder +// --------------------------------------------------------------------------- + +function buildSupabaseDump(backupDir) { + step('Gerando dump compatível com Supabase (supabase_restore.sql)...'); + + const parts = []; + + parts.push('-- =========================================================='); + parts.push('-- AgenciaPsi — Supabase-compatible full restore dump'); + parts.push(`-- Gerado em: ${new Date().toISOString()}`); + parts.push('-- '); + parts.push('-- USO: Para restaurar este dump em um Supabase limpo:'); + parts.push('-- 1. npx supabase db reset (limpa tudo)'); + parts.push('-- 2. Rode este arquivo:'); + parts.push(`-- docker exec -i ${CONTAINER} psql -U ${USER} -d ${DB} \\`); + parts.push('-- < backups/YYYY-MM-DD/supabase_restore.sql'); + parts.push('--'); + parts.push('-- OU via pipe:'); + parts.push('-- cat backups/YYYY-MM-DD/supabase_restore.sql | \\'); + parts.push(`-- docker exec -i ${CONTAINER} psql -U ${USER} -d ${DB}`); + parts.push('-- =========================================================='); + parts.push(''); + parts.push("SET client_encoding TO 'UTF8';"); + parts.push('SET statement_timeout = 0;'); + parts.push('SET lock_timeout = 0;'); + parts.push("SET standard_conforming_strings = on;"); + parts.push(''); + + // 1. Schema public: drop + recreate + parts.push('-- [1/5] Limpar schema public'); + parts.push('DROP SCHEMA IF EXISTS public CASCADE;'); + parts.push('CREATE SCHEMA public;'); + parts.push('GRANT ALL ON SCHEMA public TO postgres;'); + parts.push('GRANT ALL ON SCHEMA public TO public;'); + parts.push(''); + + // 2. Schema-only dump (public schema only, no infra) + parts.push('-- [2/5] Estrutura (tabelas, índices, constraints, triggers, functions)'); + const schemaDump = pgDump('--schema-only --no-owner --no-privileges --schema=public'); + parts.push(schemaDump); + parts.push(''); + + // 3. Auth users data (crucial for Supabase) + parts.push('-- [3/5] Dados auth.users (essencial para autenticação)'); + try { + const authData = pgDump('--data-only --no-owner --no-privileges --table=auth.users --table=auth.identities --table=auth.sessions --table=auth.refresh_tokens --table=auth.mfa_factors --table=auth.mfa_challenges'); + parts.push(authData); + } catch { + parts.push('-- AVISO: Não foi possível exportar dados de auth (pode estar vazio)'); + } + parts.push(''); + + // 4. All public data + parts.push('-- [4/5] Dados das tabelas public'); + const infraSchemas = ['storage', 'realtime', '_realtime', 'supabase_functions', 'extensions', + 'graphql', 'graphql_public', 'pgsodium', 'vault', 'net', '_analytics', + 'supabase_migrations', 'auth']; + const excludeFlags = infraSchemas.map((s) => `--exclude-schema=${s}`).join(' '); + const publicData = pgDump(`--data-only --no-owner --no-privileges ${excludeFlags}`); + parts.push(publicData); + parts.push(''); + + // 5. Storage buckets/objects metadata (if any) + parts.push('-- [5/5] Storage buckets (metadados)'); + try { + const storageBuckets = pgDump('--data-only --no-owner --no-privileges --table=storage.buckets'); + parts.push(storageBuckets); + } catch { + parts.push('-- AVISO: Nenhum bucket de storage encontrado'); + } + parts.push(''); + + parts.push('-- Restore finalizado.'); + + const dumpContent = parts.join('\n'); + const dumpPath = path.join(backupDir, 'supabase_restore.sql'); + fs.writeFileSync(dumpPath, dumpContent); + + return dumpPath; +} + // --------------------------------------------------------------------------- // Commands // --------------------------------------------------------------------------- @@ -179,57 +263,80 @@ commands.setup = function () { title('Setup — Instalação do zero'); requireDocker(); - // 1. Schema + // 1. Schema base const schemaFile = path.join(ROOT, CONFIG.schema); - if (!fs.existsSync(schemaFile)) { - err(`Schema não encontrado: ${schemaFile}`); - process.exit(1); + if (fs.existsSync(schemaFile)) { + info('Aplicando schema base...'); + psqlFile(schemaFile); + ok('Schema aplicado'); + } else { + warn(`Schema não encontrado: ${schemaFile}`); + warn('Rode "node db.cjs schema-export" depois de uma migração fresh para gerar o schema.'); } - info('Aplicando schema...'); - psqlFile(schemaFile); - ok('Schema aplicado'); - - // 2. Fixes - info('Aplicando fixes...'); - for (const fix of CONFIG.fixes) { - const fixPath = path.join(ROOT, 'fixes', fix); - if (fs.existsSync(fixPath)) { - step(fix); - psqlFile(fixPath); + // 2. Fixes (aplicados antes dos seeds para corrigir o schema) + if (Array.isArray(CONFIG.fixes) && CONFIG.fixes.length > 0) { + info('Aplicando fixes...'); + let fixCount = 0; + for (const fix of CONFIG.fixes) { + const fixPath = path.join(FIXES_DIR, fix); + if (fs.existsSync(fixPath)) { + step(fix); + psqlFile(fixPath); + fixCount++; + } } + ok(`${fixCount} fix(es) aplicado(s)`); } - ok(`${CONFIG.fixes.length} fixes aplicados`); - // 3. Seeds + // 3. Seeds (users + system) commands.seed('all'); - // 4. Migration table + // 4. Migration tracking table ensureMigrationTable(); - // 5. Record seeds as applied - const allSeeds = [...CONFIG.seeds.users, ...CONFIG.seeds.system]; + // 5. Record seeds + fixes como aplicados + const allSeeds = [...(CONFIG.seeds?.users || []), ...(CONFIG.seeds?.system || [])]; for (const seed of allSeeds) { - const seedPath = path.join(ROOT, 'seeds', seed); + const seedPath = path.join(SEEDS_DIR, seed); if (fs.existsSync(seedPath)) { recordMigration(seed, fileHash(seedPath), 'seed'); } } - for (const fix of CONFIG.fixes) { - const fixPath = path.join(ROOT, 'fixes', fix); + for (const fix of (CONFIG.fixes || [])) { + const fixPath = path.join(FIXES_DIR, fix); if (fs.existsSync(fixPath)) { recordMigration(fix, fileHash(fixPath), 'fix'); } } + // 6. Aplicar migrations incrementais (opcional - só se tiver pendentes) + if (fs.existsSync(MIGRATIONS_DIR)) { + const files = fs.readdirSync(MIGRATIONS_DIR).filter((f) => f.endsWith('.sql')).sort(); + if (files.length > 0) { + info(`Aplicando ${files.length} migration(s)...`); + for (const file of files) { + step(file); + try { + psqlFile(path.join(MIGRATIONS_DIR, file)); + recordMigration(file, fileHash(path.join(MIGRATIONS_DIR, file)), 'migration'); + } catch (e) { + warn(` ${file} falhou (pode já ter sido aplicada via schema/seeds): ${e.message.split('\n')[0]}`); + recordMigration(file, fileHash(path.join(MIGRATIONS_DIR, file)), 'migration'); + } + } + ok(`${files.length} migration(s) aplicada(s)`); + } + } + ok('Setup completo!'); log(''); - // 6. Auto-backup + // 7. Auto-backup info('Criando backup pós-setup...'); commands.backup(); - // 7. Verify + // 8. Verify commands.verify(); }; @@ -242,7 +349,8 @@ commands.backup = function () { const dir = path.join(ROOT, 'backups', date); ensureDir(dir); - const infraSchemas = ['storage', 'realtime', '_realtime', 'supabase_functions', 'extensions', 'graphql', 'graphql_public', 'pgsodium', 'vault', 'net', '_analytics']; + const infraSchemas = ['storage', 'realtime', '_realtime', 'supabase_functions', 'extensions', + 'graphql', 'graphql_public', 'pgsodium', 'vault', 'net', '_analytics', 'supabase_migrations']; const excludeFlags = infraSchemas.map((s) => `--exclude-schema=${s}`).join(' '); step('Exportando schema...'); @@ -257,15 +365,20 @@ commands.backup = function () { const full = pgDump('--no-owner --no-privileges'); fs.writeFileSync(path.join(dir, 'full_dump.sql'), full); - const sizes = ['schema.sql', 'data.sql', 'full_dump.sql'].map((f) => { - const stat = fs.statSync(path.join(dir, f)); + // Dump compatível com Supabase (restauração completa) + buildSupabaseDump(dir); + + const files = ['schema.sql', 'data.sql', 'full_dump.sql', 'supabase_restore.sql']; + const sizes = files.map((f) => { + const fPath = path.join(dir, f); + if (!fs.existsSync(fPath)) return null; + const stat = fs.statSync(fPath); return `${f}: ${(stat.size / 1024).toFixed(0)}KB`; - }); + }).filter(Boolean); ok(`Backup salvo em backups/${date}/`); sizes.forEach((s) => step(s)); - // Cleanup old backups cleanupBackups(); }; @@ -278,8 +391,8 @@ function cleanupBackups() { let removed = 0; for (const b of backups) { if (b < cutoffStr) { - const dir = path.join(ROOT, 'backups', b); - fs.rmSync(dir, { recursive: true, force: true }); + const bDir = path.join(ROOT, 'backups', b); + fs.rmSync(bDir, { recursive: true, force: true }); removed++; } } @@ -309,10 +422,6 @@ commands.restore = function (dateArg) { process.exit(1); } - const fullDump = path.join(dir, 'full_dump.sql'); - const schemaFile = path.join(dir, 'schema.sql'); - const dataFile = path.join(dir, 'data.sql'); - // Safety backup before restore info('Criando backup de segurança antes do restore...'); try { @@ -321,24 +430,26 @@ commands.restore = function (dateArg) { warn('Não foi possível criar backup de segurança (banco pode estar vazio)'); } - if (fs.existsSync(fullDump)) { - info(`Restaurando de backups/${date}/full_dump.sql ...`); + const supaRestore = path.join(dir, 'supabase_restore.sql'); + const fullDump = path.join(dir, 'full_dump.sql'); + const schemaFile = path.join(dir, 'schema.sql'); + const dataFile = path.join(dir, 'data.sql'); - // Drop and recreate public schema + if (fs.existsSync(supaRestore)) { + info(`Restaurando de backups/${date}/supabase_restore.sql (Supabase-compatible)...`); + psqlFile(supaRestore); + } else if (fs.existsSync(fullDump)) { + info(`Restaurando de backups/${date}/full_dump.sql ...`); step('Limpando schema public...'); psql('DROP SCHEMA IF EXISTS public CASCADE; CREATE SCHEMA public; GRANT ALL ON SCHEMA public TO postgres; GRANT ALL ON SCHEMA public TO public;'); - step('Aplicando full dump...'); psqlFile(fullDump); } else if (fs.existsSync(schemaFile) && fs.existsSync(dataFile)) { info(`Restaurando de backups/${date}/ (schema + data)...`); - step('Limpando schema public...'); psql('DROP SCHEMA IF EXISTS public CASCADE; CREATE SCHEMA public; GRANT ALL ON SCHEMA public TO postgres; GRANT ALL ON SCHEMA public TO public;'); - step('Aplicando schema...'); psqlFile(schemaFile); - step('Aplicando dados...'); psqlFile(dataFile); } else { @@ -348,7 +459,6 @@ commands.restore = function (dateArg) { ok(`Banco restaurado de backups/${date}/`); - // Verify commands.verify(); }; @@ -358,14 +468,13 @@ commands.migrate = function () { requireDocker(); ensureMigrationTable(); - const migrationsDir = path.join(ROOT, 'migrations'); - if (!fs.existsSync(migrationsDir)) { + if (!fs.existsSync(MIGRATIONS_DIR)) { info('Nenhuma pasta migrations/ encontrada.'); return; } const files = fs - .readdirSync(migrationsDir) + .readdirSync(MIGRATIONS_DIR) .filter((f) => f.endsWith('.sql')) .sort(); @@ -389,7 +498,7 @@ commands.migrate = function () { info(`${pending.length} migration(s) pendente(s):`); for (const file of pending) { step(`Aplicando ${file}...`); - const filePath = path.join(migrationsDir, file); + const filePath = path.join(MIGRATIONS_DIR, file); try { psqlFile(filePath); recordMigration(file, fileHash(filePath), 'migration'); @@ -423,12 +532,12 @@ commands.seed = function (group) { let total = 0; for (const g of groups) { - const seeds = CONFIG.seeds[g]; + const seeds = CONFIG.seeds?.[g]; if (!seeds || seeds.length === 0) continue; info(`Grupo: ${g}`); for (const seed of seeds) { - const seedPath = path.join(ROOT, 'seeds', seed); + const seedPath = path.join(SEEDS_DIR, seed); if (!fs.existsSync(seedPath)) { warn(` Arquivo não encontrado: ${seed}`); continue; @@ -452,10 +561,8 @@ commands.status = function () { title('Status'); requireDocker(); - // Docker ok(`Container: ${CONTAINER} (rodando)`); - // Backups const backups = listBackups(); if (backups.length > 0) { ok(`Último backup: ${backups[0]}`); @@ -464,20 +571,17 @@ commands.status = function () { warn('Nenhum backup encontrado'); } - // Migrations try { const applied = getAppliedMigrations(); if (applied.length > 0) { - info(`Migrations aplicadas: ${applied.length}`); + info(`Registros em _db_migrations: ${applied.length}`); applied.slice(-5).forEach((m) => { step(`${m.filename} ${c.gray}(${m.category}, ${m.applied_at})${c.reset}`); }); } - // Pending - const migrationsDir = path.join(ROOT, 'migrations'); - if (fs.existsSync(migrationsDir)) { - const files = fs.readdirSync(migrationsDir).filter((f) => f.endsWith('.sql')); + if (fs.existsSync(MIGRATIONS_DIR)) { + const files = fs.readdirSync(MIGRATIONS_DIR).filter((f) => f.endsWith('.sql')); const pending = files.filter((f) => !applied.map((m) => m.filename).includes(f)); if (pending.length > 0) { warn(`${pending.length} migration(s) pendente(s):`); @@ -488,28 +592,16 @@ commands.status = function () { info('Tabela _db_migrations não existe (rode setup primeiro)'); } - // DB counts log(''); info('Dados no banco:'); - const counts = [ - ['auth.users', 'SELECT count(*) FROM auth.users'], - ['profiles', 'SELECT count(*) FROM profiles'], - ['tenants', 'SELECT count(*) FROM tenants'], - ['plans', 'SELECT count(*) FROM plans'], - ['features', 'SELECT count(*) FROM features'], - ['plan_features', 'SELECT count(*) FROM plan_features'], - ['subscriptions', 'SELECT count(*) FROM subscriptions'], - ['email_templates_global', 'SELECT count(*) FROM email_templates_global'], - ['notification_templates', 'SELECT count(*) FROM notification_templates'] - ]; - - for (const [label, sql] of counts) { + const statusTables = CONFIG.status?.tables || []; + for (const table of statusTables) { try { - const count = psql(sql, { tuples: true }).trim(); + const count = psql(`SELECT count(*) FROM ${table}`, { tuples: true }).trim(); const color = parseInt(count) > 0 ? c.green : c.red; - step(`${label}: ${color}${count}${c.reset}`); + step(`${table}: ${color}${count}${c.reset}`); } catch { - step(`${label}: ${c.gray}(tabela não existe)${c.reset}`); + step(`${table}: ${c.gray}(tabela não existe)${c.reset}`); } } }; @@ -534,10 +626,8 @@ commands.diff = function () { info('Exportando schema atual...'); const currentSchema = pgDump('--schema-only --no-owner --no-privileges'); - const lastSchema = fs.readFileSync(lastSchemaPath, 'utf8'); - // Extract table definitions for comparison const extractTables = (sql) => { const tables = {}; const regex = /CREATE TABLE (?:IF NOT EXISTS )?(\S+)\s*\(([\s\S]*?)\);/g; @@ -550,13 +640,9 @@ commands.diff = function () { const currentTables = extractTables(currentSchema); const lastTables = extractTables(lastSchema); - const allTables = new Set([...Object.keys(currentTables), ...Object.keys(lastTables)]); - let added = 0, - removed = 0, - changed = 0, - unchanged = 0; + let added = 0, removed = 0, changed = 0, unchanged = 0; for (const table of [...allTables].sort()) { if (!lastTables[table]) { @@ -583,7 +669,6 @@ commands.reset = function () { title('Reset — CUIDADO'); requireDocker(); - // Safety backup info('Criando backup antes do reset...'); try { commands.backup(); @@ -595,7 +680,6 @@ commands.reset = function () { psql('DROP SCHEMA IF EXISTS public CASCADE; CREATE SCHEMA public; GRANT ALL ON SCHEMA public TO postgres; GRANT ALL ON SCHEMA public TO public;'); ok('Schema public resetado'); - // Re-run setup commands.setup(); }; @@ -604,23 +688,12 @@ commands.verify = function () { title('Verificação de integridade'); requireDocker(); - const checks = [ - { name: 'auth.users', sql: 'SELECT count(*) FROM auth.users', min: 1 }, - { name: 'profiles', sql: 'SELECT count(*) FROM profiles', min: 1 }, - { name: 'tenants', sql: 'SELECT count(*) FROM tenants', min: 1 }, - { name: 'plans', sql: 'SELECT count(*) FROM plans', min: 7 }, - { name: 'features', sql: 'SELECT count(*) FROM features', min: 20 }, - { name: 'plan_features', sql: 'SELECT count(*) FROM plan_features', min: 50 }, - { name: 'subscriptions', sql: 'SELECT count(*) FROM subscriptions', min: 1 }, - { name: 'email_templates', sql: 'SELECT count(*) FROM email_templates_global', min: 10 } - ]; + let pass = 0, fail = 0; + const verify = CONFIG.verify || { tables: [], views: [] }; - let pass = 0, - fail = 0; - - for (const check of checks) { + for (const check of (verify.tables || [])) { try { - const count = parseInt(psql(check.sql, { tuples: true }).trim()); + const count = parseInt(psql(`SELECT count(*) FROM ${check.name}`, { tuples: true }).trim()); if (count >= check.min) { ok(`${check.name}: ${count} (mín: ${check.min})`); pass++; @@ -634,14 +707,15 @@ commands.verify = function () { } } - // Check entitlements view - try { - const ent = psql('SELECT count(*) FROM v_tenant_entitlements;', { tuples: true }).trim(); - ok(`v_tenant_entitlements: ${ent} registros`); - pass++; - } catch { - err('v_tenant_entitlements: view não existe'); - fail++; + for (const view of (verify.views || [])) { + try { + const cnt = psql(`SELECT count(*) FROM ${view};`, { tuples: true }).trim(); + ok(`${view}: ${cnt} registros`); + pass++; + } catch { + err(`${view}: view não existe`); + fail++; + } } log(''); @@ -652,6 +726,225 @@ commands.verify = function () { } }; +// ---- SCHEMA-EXPORT ---- +commands['schema-export'] = function () { + title('Schema Export'); + requireDocker(); + + const schemaDir = path.join(ROOT, 'schema'); + const dirs = { + full: path.join(schemaDir, '00_full'), + extensions: path.join(schemaDir, '01_extensions'), + types: path.join(schemaDir, '02_types'), + functions: path.join(schemaDir, '03_functions'), + tables: path.join(schemaDir, '04_tables'), + views: path.join(schemaDir, '05_views'), + indexes: path.join(schemaDir, '06_indexes'), + foreignKeys: path.join(schemaDir, '07_foreign_keys'), + triggers: path.join(schemaDir, '08_triggers'), + policies: path.join(schemaDir, '09_policies'), + grants: path.join(schemaDir, '10_grants'), + }; + // Limpa diretórios antes para remover arquivos stale de exports anteriores + for (const dir of Object.values(dirs)) { + if (fs.existsSync(dir)) { + for (const f of fs.readdirSync(dir)) { + if (f.endsWith('.sql')) fs.rmSync(path.join(dir, f), { force: true }); + } + } + ensureDir(dir); + } + + // 00_full — dump completo + step('00_full/schema.sql (dump completo)...'); + const fullSchema = pgDump('--schema-only --no-owner --no-privileges'); + fs.writeFileSync(path.join(dirs.full, 'schema.sql'), fullSchema); + + // 01_extensions + step('01_extensions...'); + const extSql = `SELECT 'CREATE EXTENSION IF NOT EXISTS ' || quote_ident(extname) || ' WITH SCHEMA ' || quote_ident(nspname) || ';' FROM pg_extension e JOIN pg_namespace n ON e.extnamespace = n.oid WHERE extname NOT IN ('plpgsql') ORDER BY extname;`; + const extResult = psql(extSql, { tuples: true }); + const extLines = extResult.split('\n').filter(Boolean).map(l => l.trim()).filter(Boolean); + if (extLines.length > 0) { + const content = `-- Extensions\n-- Gerado automaticamente em ${new Date().toISOString()}\n-- Total: ${extLines.length}\n\n` + extLines.join('\n') + '\n'; + fs.writeFileSync(path.join(dirs.extensions, 'extensions.sql'), content); + } + + // 02_types — enums públicos + step('02_types...'); + const typesSql = ` + SELECT pg_catalog.format_type(t.oid, NULL) || ' AS ENUM (' || + string_agg(quote_literal(e.enumlabel), ', ' ORDER BY e.enumsortorder) || ');' + FROM pg_type t + JOIN pg_enum e ON t.oid = e.enumtypid + JOIN pg_namespace n ON t.typnamespace = n.oid + WHERE n.nspname = 'public' + GROUP BY t.oid, t.typname;`; + const typesResult = psql(typesSql, { tuples: true }); + const typesLines = typesResult.split('\n').filter(Boolean).map(l => l.trim()).filter(Boolean); + if (typesLines.length > 0) { + const typesContent = `-- Public Enums & Types\n-- Gerado automaticamente em ${new Date().toISOString()}\n\n` + + typesLines.map(l => `CREATE TYPE ${l}`).join('\n\n') + '\n'; + fs.writeFileSync(path.join(dirs.types, 'public_types.sql'), typesContent); + } + + try { + const authTypesSql = typesSql.replace("'public'", "'auth'"); + const authTypesResult = psql(authTypesSql, { tuples: true }); + const authLines = authTypesResult.split('\n').filter(Boolean).map(l => l.trim()).filter(Boolean); + if (authLines.length > 0) { + const authContent = `-- Auth Enums & Types\n-- Gerado automaticamente em ${new Date().toISOString()}\n\n` + + authLines.map(l => `CREATE TYPE ${l}`).join('\n\n') + '\n'; + fs.writeFileSync(path.join(dirs.types, 'auth_types.sql'), authContent); + } + } catch { /* auth types may not exist */ } + + // 03_functions + step('03_functions...'); + const funcBlocks = fullSchema.match(/CREATE(?:\s+OR\s+REPLACE)?\s+FUNCTION\s+[\s\S]*?(?:\$\$[^$]*\$\$|\$[a-zA-Z_]+\$[\s\S]*?\$[a-zA-Z_]+\$)\s*(?:;|$)/gi) || []; + const funcsBySchema = {}; + for (const block of funcBlocks) { + const nameMatch = block.match(/FUNCTION\s+([\w.]+)\./); + const schema = nameMatch ? nameMatch[1] : 'public'; + if (!funcsBySchema[schema]) funcsBySchema[schema] = []; + funcsBySchema[schema].push(block); + } + for (const [schema, funcs] of Object.entries(funcsBySchema)) { + const content = `-- Functions: ${schema}\n-- Gerado automaticamente em ${new Date().toISOString()}\n-- Total: ${funcs.length}\n\n` + + funcs.join('\n\n') + '\n'; + fs.writeFileSync(path.join(dirs.functions, `${schema}.sql`), content); + } + if (funcBlocks.length > 0) { + const allFuncs = `-- All Functions\n-- Gerado automaticamente em ${new Date().toISOString()}\n-- Total: ${funcBlocks.length}\n\n` + + funcBlocks.join('\n\n') + '\n'; + fs.writeFileSync(path.join(dirs.functions, '_all.sql'), allFuncs); + } + + // 04_tables — agrupado por domínio + step('04_tables...'); + const tableBlocks = fullSchema.match(/CREATE TABLE (?:IF NOT EXISTS )?public\.\S+\s*\([\s\S]*?\);/gi) || []; + const domainTables = CONFIG.domains || {}; + const tableToDomain = {}; + for (const [domain, tables] of Object.entries(domainTables)) { + for (const t of tables) tableToDomain[t] = domain; + } + const tablesByDomain = {}; + for (const block of tableBlocks) { + const nameMatch = block.match(/CREATE TABLE (?:IF NOT EXISTS )?public\.(\S+)/i); + if (!nameMatch) continue; + const name = nameMatch[1]; + const domain = tableToDomain[name] || 'outros'; + if (!tablesByDomain[domain]) tablesByDomain[domain] = []; + tablesByDomain[domain].push(block); + } + for (const [domain, blocks] of Object.entries(tablesByDomain)) { + const content = `-- Tables: ${domain}\n-- Gerado automaticamente em ${new Date().toISOString()}\n-- Total: ${blocks.length}\n\n` + + blocks.join('\n\n') + '\n'; + const filename = domain.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_|_$/g, '') + '.sql'; + fs.writeFileSync(path.join(dirs.tables, filename), content); + } + + // 05_views + step('05_views...'); + const viewBlocks = fullSchema.match(/CREATE(?:\s+OR\s+REPLACE)?\s+VIEW\s+public\.[\s\S]*?;/gi) || []; + if (viewBlocks.length > 0) { + const content = `-- Views\n-- Gerado automaticamente em ${new Date().toISOString()}\n-- Total: ${viewBlocks.length}\n\n` + + viewBlocks.join('\n\n') + '\n'; + fs.writeFileSync(path.join(dirs.views, 'views.sql'), content); + } + + // 06_indexes + step('06_indexes...'); + const indexLines = fullSchema.match(/CREATE (?:UNIQUE )?INDEX\s+\S+\s+ON\s+public\.\S+[\s\S]*?;/gi) || []; + if (indexLines.length > 0) { + const content = `-- Indexes\n-- Gerado automaticamente em ${new Date().toISOString()}\n-- Total: ${indexLines.length}\n\n` + + indexLines.join('\n\n') + '\n'; + fs.writeFileSync(path.join(dirs.indexes, 'indexes.sql'), content); + } + + // 07_foreign_keys (+ PKs + UNIQUEs) + step('07_foreign_keys...'); + const constraintLines = fullSchema.match(/ALTER TABLE ONLY public\.\S+\s+ADD CONSTRAINT\s+[\s\S]*?;/gi) || []; + if (constraintLines.length > 0) { + const content = `-- Constraints (PK, FK, UNIQUE, CHECK)\n-- Gerado automaticamente em ${new Date().toISOString()}\n-- Total: ${constraintLines.length}\n\n` + + constraintLines.join('\n\n') + '\n'; + fs.writeFileSync(path.join(dirs.foreignKeys, 'constraints.sql'), content); + } + + // 08_triggers + step('08_triggers...'); + const triggerLines = fullSchema.match(/CREATE TRIGGER\s+[\s\S]*?;/gi) || []; + if (triggerLines.length > 0) { + const content = `-- Triggers\n-- Gerado automaticamente em ${new Date().toISOString()}\n-- Total: ${triggerLines.length}\n\n` + + triggerLines.join('\n\n') + '\n'; + fs.writeFileSync(path.join(dirs.triggers, 'triggers.sql'), content); + } + + // 09_policies + step('09_policies...'); + const policyEnables = fullSchema.match(/ALTER TABLE public\.\S+ ENABLE ROW LEVEL SECURITY;/gi) || []; + const policyCreates = fullSchema.match(/CREATE POLICY\s+[\s\S]*?;/gi) || []; + if (policyEnables.length > 0 || policyCreates.length > 0) { + const content = `-- RLS Policies\n-- Gerado automaticamente em ${new Date().toISOString()}\n-- Enable RLS: ${policyEnables.length} tabelas\n-- Policies: ${policyCreates.length}\n\n` + + '-- Enable RLS\n' + policyEnables.join('\n') + '\n\n' + + '-- Policies\n' + policyCreates.join('\n\n') + '\n'; + fs.writeFileSync(path.join(dirs.policies, 'policies.sql'), content); + } + + // 10_grants + step('10_grants...'); + const grantLines = fullSchema.match(/(?:GRANT|REVOKE)\s+[\s\S]*?;/gi) || []; + const publicGrants = grantLines.filter(g => /public\./i.test(g)); + if (publicGrants.length > 0) { + const content = `-- Grants\n-- Gerado automaticamente em ${new Date().toISOString()}\n-- Total: ${publicGrants.length}\n\n` + + publicGrants.join('\n') + '\n'; + fs.writeFileSync(path.join(dirs.grants, 'grants.sql'), content); + } + + // Summary + log(''); + ok('Schema exportado para schema/'); + const summary = [ + ['00_full', '1 arquivo'], + ['01_extensions', fs.readdirSync(dirs.extensions).length + ' arquivo(s)'], + ['02_types', fs.readdirSync(dirs.types).length + ' arquivo(s)'], + ['03_functions', fs.readdirSync(dirs.functions).length + ' arquivo(s), ' + funcBlocks.length + ' functions'], + ['04_tables', fs.readdirSync(dirs.tables).length + ' arquivo(s), ' + tableBlocks.length + ' tabelas'], + ['05_views', viewBlocks.length + ' views'], + ['06_indexes', indexLines.length + ' indexes'], + ['07_foreign_keys', constraintLines.length + ' constraints'], + ['08_triggers', triggerLines.length + ' triggers'], + ['09_policies', policyCreates.length + ' policies'], + ['10_grants', publicGrants.length + ' grants'], + ]; + for (const [dir, desc] of summary) { + step(`${dir}: ${desc}`); + } +}; + +// ---- DASHBOARD ---- +commands.dashboard = function (dateArg) { + title('Dashboard'); + + const scriptPath = path.join(ROOT, 'generate-dashboard.cjs'); + if (!fs.existsSync(scriptPath)) { + err(`Script não encontrado: ${scriptPath}`); + process.exit(1); + } + + const args = dateArg ? [scriptPath, dateArg] : [scriptPath]; + const result = spawnSync('node', args, { + stdio: 'inherit', + cwd: ROOT, + env: { ...process.env, PYTHONIOENCODING: 'utf-8', LANG: 'C.UTF-8' } + }); + + if (result.status !== 0) { + err('Falha ao gerar dashboard.'); + process.exit(result.status || 1); + } +}; + // ---- HELP ---- commands.help = function () { log(` @@ -661,15 +954,17 @@ ${c.cyan}Uso:${c.reset} node db.cjs [opções] ${c.cyan}Comandos:${c.reset} - ${c.bold}setup${c.reset} Instalação do zero (schema + fixes + seeds) + ${c.bold}setup${c.reset} Instalação do zero (schema + fixes + seeds + migrations) Cria backup automático após concluir ${c.bold}backup${c.reset} Exporta banco para backups/YYYY-MM-DD/ - Gera: schema.sql, data.sql, full_dump.sql + Gera: schema.sql, data.sql, full_dump.sql, + ${c.green}supabase_restore.sql${c.reset} (restauração completa) ${c.bold}restore [data]${c.reset} Restaura de um backup + Prioriza supabase_restore.sql se existir Sem data = último backup disponível - Ex: node db.cjs restore 2026-03-23 + Ex: node db.cjs restore 2026-04-17 ${c.bold}migrate${c.reset} Aplica migrations pendentes (pasta migrations/) Backup automático antes de aplicar @@ -685,9 +980,39 @@ ${c.cyan}Comandos:${c.reset} ${c.yellow}⚠ Cria backup antes de resetar${c.reset} ${c.bold}verify${c.reset} Verifica integridade dos dados essenciais + (tabelas + views definidas em db.config.json → verify) + + ${c.bold}schema-export${c.reset} Exporta schema separado em schema/ + 00_full, 01_extensions, 02_types, 03_functions, + 04_tables (agrupado por domínio), 05_views, + 06_indexes, 07_foreign_keys, 08_triggers, + 09_policies, 10_grants + + ${c.bold}dashboard [data]${c.reset} Gera dashboard HTML interativo do banco + Tabelas por domínio + seção Infraestrutura + busca + Sem data = usa schema do backup mais recente ${c.bold}help${c.reset} Mostra esta ajuda +${c.cyan}Backup Supabase-compatible:${c.reset} + + O comando ${c.bold}backup${c.reset} gera automaticamente o arquivo + ${c.green}supabase_restore.sql${c.reset} que contém TUDO necessário + para restaurar o banco do zero: + + • Schema public (tabelas, índices, triggers, functions) + • Dados auth.users + identities (autenticação) + • Todos os dados das tabelas public + • Metadados de storage buckets + + Para restaurar manualmente: + ${c.gray}# Opção 1: via CLI${c.reset} + node db.cjs restore + + ${c.gray}# Opção 2: direto no container${c.reset} + cat backups/2026-04-17/supabase_restore.sql | \\ + docker exec -i ${CONTAINER} psql -U ${USER} -d ${DB} + ${c.cyan}Exemplos:${c.reset} ${c.gray}# Primeira vez — instala tudo${c.reset} @@ -704,6 +1029,12 @@ ${c.cyan}Exemplos:${c.reset} ${c.gray}# Ver o que tem no banco${c.reset} node db.cjs status + + ${c.gray}# Atualizar as pastas schema/*${c.reset} + node db.cjs schema-export + + ${c.gray}# Gerar dashboard HTML${c.reset} + node db.cjs dashboard `); }; diff --git a/database-novo/db.config.json b/database-novo/db.config.json index 2fdb619..9a644ec 100644 --- a/database-novo/db.config.json +++ b/database-novo/db.config.json @@ -4,6 +4,9 @@ "user": "postgres", "backupRetentionDays": 30, "schema": "schema/00_full/schema.sql", + "migrationsDir": "migrations", + "seedsDir": "seeds", + "fixesDir": "fixes", "seeds": { "users": [ "seed_001_fixed.sql", @@ -15,7 +18,11 @@ "seed_011_features.sql", "seed_012_plan_features.sql", "seed_013_subscriptions.sql", - "seed_014_global_data.sql" + "seed_014_global_data.sql", + "seed_015_document_templates.sql", + "seed_030_dev_phases_items.sql", + "seed_031_dev_auditoria.sql", + "seed_032_dev_competitors.sql" ], "test_data": [ "seed_020_test_data.sql" @@ -30,5 +37,310 @@ "fix_subscriptions_validate_scope.sql", "fix_template_keys_match_populate.sql", "fix_encoding_accents.sql" - ] + ], + "verify": { + "tables": [ + { "name": "auth.users", "min": 1 }, + { "name": "profiles", "min": 1 }, + { "name": "tenants", "min": 1 }, + { "name": "plans", "min": 7 }, + { "name": "features", "min": 20 }, + { "name": "plan_features", "min": 50 }, + { "name": "subscriptions", "min": 1 }, + { "name": "email_templates_global", "min": 10 }, + { "name": "notification_templates", "min": 5 }, + { "name": "document_templates", "min": 1 } + ], + "views": [ + "v_tenant_entitlements", + "v_tenant_active_subscription" + ] + }, + "status": { + "tables": [ + "auth.users", + "profiles", + "tenants", + "tenant_members", + "plans", + "features", + "plan_features", + "subscriptions", + "patients", + "agenda_eventos", + "services", + "financial_records", + "document_templates", + "documents", + "email_templates_global", + "notification_templates" + ] + }, + "domains": { + "SaaS / Planos": [ + "plans", "plan_features", "plan_prices", "plan_public", "plan_public_bullets", + "features", "modules", "module_features", + "subscriptions", "subscription_events", + "subscription_intents_legacy", "subscription_intents_personal", "subscription_intents_tenant", + "tenant_modules", "tenant_features", "tenant_feature_exceptions_log", + "billing_contracts", "entitlements_invalidation" + ], + "Addons / Créditos": [ + "addon_products", "addon_credits", "addon_transactions" + ], + "Tenants / Multi-tenant": [ + "tenants", "profiles", "user_settings", + "tenant_invites", "tenant_members", + "company_profiles", "support_sessions", + "saas_admins", "owner_users", "dev_user_credentials" + ], + "Pacientes": [ + "patients", "patient_contacts", "patient_support_contacts", + "patient_groups", "patient_group_patient", + "patient_tags", "patient_patient_tag", + "patient_discounts", "patient_intake_requests", "patient_invites", + "patient_status_history", "patient_timeline" + ], + "Agenda / Agendamento": [ + "agenda_eventos", "agenda_bloqueios", "agenda_configuracoes", "agenda_excecoes", + "agenda_online_slots", "agenda_regras_semanais", + "agenda_slots_bloqueados_semanais", "agenda_slots_regras", + "agendador_configuracoes", "agendador_solicitacoes" + ], + "Financeiro": [ + "financial_categories", "financial_exceptions", "financial_records", + "payment_settings", "professional_pricing", + "therapist_payouts", "therapist_payout_records", + "recurrence_rules", "recurrence_exceptions", "recurrence_rule_services" + ], + "Serviços / Prontuários": [ + "services", "commitment_services", "commitment_time_logs", + "determined_commitments", "determined_commitment_fields", + "insurance_plans", "insurance_plan_services", + "medicos" + ], + "Documentos": [ + "documents", "document_templates", "document_generated", + "document_access_logs", "document_share_links", "document_signatures" + ], + "Comunicação / Notificações": [ + "email_templates_global", "email_templates_tenant", "email_layout_config", + "notification_templates", "notification_channels", "notification_preferences", + "notification_logs", "notification_schedules", "notification_queue", + "notifications", "notice_dismissals", "global_notices", "login_carousel_slides", + "twilio_subaccount_usage" + ], + "Central SaaS (docs/FAQ)": [ + "saas_docs", "saas_doc_votos", "saas_faq", "saas_faq_itens" + ], + "Estrutura / Calendário": [ + "feriados" + ] + }, + "domainColors": { + "SaaS / Planos": "#4f8cff", + "Addons / Créditos": "#a78bfa", + "Tenants / Multi-tenant": "#6ee7b7", + "Pacientes": "#f472b6", + "Agenda / Agendamento": "#38bdf8", + "Financeiro": "#f87171", + "Serviços / Prontuários": "#34d399", + "Documentos": "#0ea5e9", + "Comunicação / Notificações": "#fbbf24", + "Central SaaS (docs/FAQ)": "#c084fc", + "Estrutura / Calendário": "#fb923c" + }, + "infrastructure": { + "Banco & Backend": { + "color": "#4f8cff", + "items": [ + { + "name": "Supabase", + "role": "Postgres + Auth + Storage + Realtime + Edge Functions", + "env": "Local (Docker) + Cloud", + "status": "ativo", + "notes": "Stack principal. Migrations em database-novo/migrations/. Functions em supabase/functions/. CLI via npx supabase." + }, + { + "name": "PostgreSQL 15", + "role": "Banco de dados relacional (via container supabase_db_agenciapsi-primesakai)", + "env": "Local (Docker)", + "status": "ativo", + "notes": "RLS habilitada em todas as tabelas públicas. Multi-tenant via tenant_id. SECURITY DEFINER em RPCs sensíveis." + }, + { + "name": "Docker + Docker Compose", + "role": "Orquestração dos containers do stack Supabase local + Evolution API", + "env": "Local", + "status": "ativo", + "notes": "docker-compose.yml na raiz. Iniciado via npx supabase start." + } + ] + }, + "Email": { + "color": "#fbbf24", + "items": [ + { + "name": "Mailpit (Supabase inbucket)", + "role": "Inbox SMTP local para capturar emails de teste", + "env": "Local (Docker)", + "status": "ativo", + "notes": "Container supabase_inbucket. Usado em dev para validar templates sem enviar email real." + }, + { + "name": "SMTP produção", + "role": "Envio real de emails transacionais (faturas, convites, notificações)", + "env": "Cloud (pendente)", + "status": "pendente", + "notes": "Requer SMTP_HOST/PORT/USER/PASS/FROM nos secrets das edge functions." + } + ] + }, + "WhatsApp / SMS": { + "color": "#34d399", + "items": [ + { + "name": "Evolution API", + "role": "Integração WhatsApp Business (envio/recebimento)", + "env": "Local (Docker)", + "status": "ativo", + "notes": "Container via evolution-api/. whatsapp_instances e notification_channels já cadastrados. Integração real está sendo costurada." + }, + { + "name": "Twilio (SMS/Voz)", + "role": "Provedor de SMS e voz para notificações", + "env": "Cloud", + "status": "ativo", + "notes": "twilio_subaccount_usage rastreia consumo por tenant. SaasTwilioWhatsappPage gerencia contas." + } + ] + }, + "Geração de documentos": { + "color": "#38bdf8", + "items": [ + { + "name": "pdfmake 0.3.7", + "role": "Geração de PDF client-side (atestados, laudos, recibos)", + "env": "Browser", + "status": "ativo", + "notes": "UMD/webpack. Requer optimizeDeps.include explícito no vite.config.mjs." + }, + { + "name": "html-to-pdfmake / html2pdf.js / jsPDF", + "role": "Conversão HTML→PDF para documentos ricos", + "env": "Browser", + "status": "ativo", + "notes": "Usado em document_templates e documents gerados para pacientes." + }, + { + "name": "Jodit + Quill", + "role": "Editores de texto rico para templates de documentos", + "env": "Browser", + "status": "ativo", + "notes": "Jodit em DocumentTemplateEditor; Quill em páginas legadas. Migração em andamento." + }, + { + "name": "html2canvas-pro", + "role": "Captura de screenshots de DOM (preview/export)", + "env": "Browser", + "status": "ativo", + "notes": "Usado para thumbnails de templates e previews." + } + ] + }, + "Frontend": { + "color": "#a78bfa", + "items": [ + { + "name": "Vue 3 + Composition API", + "role": "Framework principal (script setup)", + "env": "Browser", + "status": "ativo", + "notes": "~487 componentes Vue. Pinia para state management." + }, + { + "name": "Vite 5", + "role": "Build tool e dev server", + "env": "Node.js", + "status": "ativo", + "notes": "vite-plugin-compression (Brotli/Gzip), unplugin-auto-import para PrimeVue e Vue. rollup-plugin-visualizer para análise de bundle." + }, + { + "name": "PrimeVue 4 (tema Sakai)", + "role": "Biblioteca de componentes UI", + "env": "Browser", + "status": "ativo", + "notes": "@primeuix/themes. auto-import-resolver. DataTable, Dialog, DatePicker, Popover, Toast, ConfirmDialog headless." + }, + { + "name": "Tailwind CSS v4", + "role": "Utility-first CSS", + "env": "Browser", + "status": "ativo", + "notes": "@tailwindcss/vite + tailwindcss-primeui. Surface tokens do PrimeVue (var(--surface-card), var(--text-color-secondary))." + }, + { + "name": "Vue Router", + "role": "Roteamento SPA com guards por role/tenant", + "env": "Browser", + "status": "ativo", + "notes": "Grupos de rota: therapist, admin, supervisor, saas, billing, account, configuracoes, features." + }, + { + "name": "FullCalendar 6", + "role": "Calendário para agenda de terapeutas", + "env": "Browser", + "status": "ativo", + "notes": "Plugins: daygrid, timegrid, interaction, list, resource, resource-timegrid." + }, + { + "name": "Chart.js 3", + "role": "Gráficos para dashboards (financeiro, KPIs)", + "env": "Browser", + "status": "ativo", + "notes": "Usado em dashboards do therapist e clinic." + } + ] + }, + "Dev / Tooling": { + "color": "#94a3b8", + "items": [ + { + "name": "Supabase CLI", + "role": "Gerencia ambiente local, migrations, edge functions", + "env": "Node.js", + "status": "ativo", + "notes": "Via npx supabase. Start/stop/status/db-push/functions-deploy." + }, + { + "name": "db.cjs (este projeto)", + "role": "CLI auxiliar pra setup/backup/restore/migrate/verify via docker exec", + "env": "Node.js", + "status": "ativo", + "notes": "Complementa o supabase CLI com fluxo schema + fixes + seeds + migrations. Encoding UTF-8 preservado." + }, + { + "name": "generate-dashboard.cjs", + "role": "Gera dashboard HTML estático do schema (tabelas, FKs, infra)", + "env": "Node.js", + "status": "ativo", + "notes": "Standalone, sem dependências externas. Lê config de db.config.json e schema do backup mais recente." + }, + { + "name": "Vitest 4", + "role": "Runner de testes unitários", + "env": "Node.js", + "status": "ativo", + "notes": "npm test / test:watch / test:ui. Bateria inicial em src/**/__tests__." + }, + { + "name": "ESLint + Prettier", + "role": "Lint + formatação automática", + "env": "Node.js", + "status": "ativo", + "notes": "@vue/eslint-config-prettier. Rodado via npm run lint." + } + ] + } + } } diff --git a/database-novo/generate-dashboard.cjs b/database-novo/generate-dashboard.cjs index a655c1c..377d2e2 100644 --- a/database-novo/generate-dashboard.cjs +++ b/database-novo/generate-dashboard.cjs @@ -3,173 +3,124 @@ // AgenciaPsi — Dashboard Generator // ============================================================================= // Uso: -// node generate-dashboard.js → usa backup mais recente -// node generate-dashboard.js 2026-03-27 → usa backup de data específica +// node generate-dashboard.cjs → usa backup mais recente +// node generate-dashboard.cjs 2026-04-17 → usa backup de data específica // -// Lê de: ./database-novo/backups/YYYY-MM-DD/schema.sql -// Gera: ./dashboard.html (na mesma pasta do script) +// Lê de: ./backups/YYYY-MM-DD/schema.sql +// Lê de: ./db.config.json (domínios, cores e infraestrutura) +// Gera: ./agenciapsi-db-dashboard.html (na mesma pasta do script) // ============================================================================= -const fs = require('fs'); +const fs = require('fs'); const path = require('path'); -const BACKUPS_DIR = path.join(__dirname, 'backups'); -const OUTPUT_FILE = path.join(__dirname, 'dashboard.html'); +const ROOT = __dirname; +const BACKUPS_DIR = path.join(ROOT, 'backups'); +const OUTPUT_FILE = path.join(ROOT, 'agenciapsi-db-dashboard.html'); +const CONFIG_FILE = path.join(ROOT, 'db.config.json'); // --------------------------------------------------------------------------- -// Cores por domínio +// Carrega config (domínios, cores e infraestrutura) // --------------------------------------------------------------------------- -const DOMAIN_COLORS = { - 'SaaS / Planos': '#4f8cff', - 'Tenants / Multi-tenant': '#6ee7b7', - 'Pacientes': '#f472b6', - 'Agenda': '#fb923c', - 'Financeiro': '#a78bfa', - 'Serviços / Commitments': '#34d399', - 'Notificações': '#38bdf8', - 'Email / Comunicação': '#fbbf24', - 'SaaS Admin': '#94a3b8', -}; - -// --------------------------------------------------------------------------- -// Mapeamento domínio → tabelas -// Adicione novas tabelas aqui quando criar migrations -// --------------------------------------------------------------------------- -const DOMAIN_TABLES = { - 'SaaS / Planos': [ - 'plans','plan_features','plan_prices','plan_public','plan_public_bullets', - 'features','modules','module_features','subscriptions','subscription_events', - 'subscription_intents_personal','subscription_intents_tenant','subscription_intents_legacy', - 'addon_products','addon_credits','addon_transactions', - 'tenant_features','tenant_modules','entitlements_invalidation','tenant_feature_exceptions_log', - ], - 'Tenants / Multi-tenant': [ - 'tenants','tenant_members','tenant_invites','owner_users', - 'profiles','company_profiles','billing_contracts','payment_settings','support_sessions', - ], - 'Pacientes': [ - 'patients','patient_groups','patient_group_patient','patient_tags', - 'patient_patient_tag','patient_discounts','patient_invites','patient_intake_requests', - ], - 'Agenda': [ - 'agenda_eventos','agenda_configuracoes','agenda_bloqueios','agenda_excecoes', - 'agenda_online_slots','agenda_regras_semanais','agenda_slots_bloqueados_semanais', - 'agenda_slots_regras','agendador_configuracoes','agendador_solicitacoes', - 'feriados','recurrence_rules','recurrence_exceptions','recurrence_rule_services', - ], - 'Financeiro': [ - 'financial_records','financial_categories','financial_exceptions', - 'therapist_payouts','therapist_payout_records', - 'insurance_plans','insurance_plan_services','professional_pricing', - ], - 'Serviços / Commitments': [ - 'services','determined_commitments','determined_commitment_fields', - 'commitment_services','commitment_time_logs', - ], - 'Notificações': [ - 'notifications','notification_channels','notification_templates', - 'notification_queue','notification_logs','notification_preferences', - 'notification_schedules','twilio_subaccount_usage', - ], - 'Email / Comunicação': [ - 'email_templates_global','email_templates_tenant','email_layout_config', - 'global_notices','notice_dismissals','login_carousel_slides', - ], - 'SaaS Admin': [ - 'saas_admins','saas_docs','saas_doc_votos','saas_faq','saas_faq_itens', - 'user_settings','dev_user_credentials','_db_migrations', - ], -}; +if (!fs.existsSync(CONFIG_FILE)) { + console.error(`✖ Config não encontrada: ${CONFIG_FILE}`); + process.exit(1); +} +const CONFIG = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')); +const DOMAIN_TABLES = CONFIG.domains || {}; +const DOMAIN_COLORS = CONFIG.domainColors || {}; +const INFRASTRUCTURE = CONFIG.infrastructure || {}; // --------------------------------------------------------------------------- // 1. Resolve qual schema.sql usar // --------------------------------------------------------------------------- function resolveSchema() { - const arg = process.argv[2]; + const arg = process.argv[2]; - if (!fs.existsSync(BACKUPS_DIR)) { - console.error(`✖ Pasta não encontrada: ${BACKUPS_DIR}`); - console.error(` Certifique-se que o script está na raiz do projeto.`); - process.exit(1); - } + if (!fs.existsSync(BACKUPS_DIR)) { + console.error(`✖ Pasta não encontrada: ${BACKUPS_DIR}`); + console.error(` Rode primeiro: node db.cjs backup`); + process.exit(1); + } - const available = fs.readdirSync(BACKUPS_DIR) - .filter(f => /^\d{4}-\d{2}-\d{2}$/.test(f)) - .sort() - .reverse(); + const available = fs + .readdirSync(BACKUPS_DIR) + .filter((f) => /^\d{4}-\d{2}-\d{2}$/.test(f)) + .sort() + .reverse(); - if (available.length === 0) { - console.error('✖ Nenhum backup encontrado em database-novo/backups/'); - console.error(' Rode primeiro: node db.cjs backup'); - process.exit(1); - } + if (available.length === 0) { + console.error('✖ Nenhum backup encontrado em database-novo/backups/'); + console.error(' Rode primeiro: node db.cjs backup'); + process.exit(1); + } - const date = (arg && /^\d{4}-\d{2}-\d{2}$/.test(arg)) ? arg : available[0]; + const date = arg && /^\d{4}-\d{2}-\d{2}$/.test(arg) ? arg : available[0]; - if (!available.includes(date)) { - console.error(`✖ Backup não encontrado para: ${date}`); - console.error(` Disponíveis: ${available.join(', ')}`); - process.exit(1); - } + if (!available.includes(date)) { + console.error(`✖ Backup não encontrado para: ${date}`); + console.error(` Disponíveis: ${available.join(', ')}`); + process.exit(1); + } - const schemaPath = path.join(BACKUPS_DIR, date, 'schema.sql'); - if (!fs.existsSync(schemaPath)) { - console.error(`✖ schema.sql não encontrado em database-novo/backups/${date}/`); - process.exit(1); - } + const schemaPath = path.join(BACKUPS_DIR, date, 'schema.sql'); + if (!fs.existsSync(schemaPath)) { + console.error(`✖ schema.sql não encontrado em backups/${date}/`); + process.exit(1); + } - return { schemaPath, date, available }; + return { schemaPath, date, available }; } // --------------------------------------------------------------------------- // 2. Parse do schema.sql — extrai tabelas, colunas e FKs // --------------------------------------------------------------------------- function parseSchema(content) { - const tables = {}; + const tables = {}; - // Tabelas public.* - const tableRe = /CREATE TABLE (public\.\S+)\s*\(([\s\S]*?)\);/gm; - let m; - while ((m = tableRe.exec(content)) !== null) { - const name = m[1].replace('public.', ''); - const body = m[2]; - const columns = []; + // Tabelas public.* + const tableRe = /CREATE TABLE (public\.\S+)\s*\(([\s\S]*?)\);/gm; + let m; + while ((m = tableRe.exec(content)) !== null) { + const name = m[1].replace('public.', ''); + const body = m[2]; + const columns = []; - for (let line of body.split('\n')) { - line = line.trim().replace(/,$/, ''); - if (!line || line.startsWith('--')) continue; - if (/^(CONSTRAINT|PRIMARY KEY|UNIQUE|CHECK|FOREIGN KEY|EXCLUDE)/i.test(line)) continue; + for (let line of body.split('\n')) { + line = line.trim().replace(/,$/, ''); + if (!line || line.startsWith('--')) continue; + if (/^(CONSTRAINT|PRIMARY KEY|UNIQUE|CHECK|FOREIGN KEY|EXCLUDE)/i.test(line)) continue; - const col = line.match( - /^(\w+)\s+([\w\[\]"()\s,]+?)(?:\s+DEFAULT\s+|\s+NOT NULL|\s+NULL|\s+GENERATED|\s+REFERENCES\s|$)/ - ); - if (col) { - columns.push({ - name: col[1], - type: col[2].trim().split('(')[0].trim(), - pk: col[1] === 'id', - }); - } + const col = line.match( + /^(\w+)\s+([\w\[\]"()\s,]+?)(?:\s+DEFAULT\s+|\s+NOT NULL|\s+NULL|\s+GENERATED|\s+REFERENCES\s|$)/ + ); + if (col) { + columns.push({ + name: col[1], + type: col[2].trim().split('(')[0].trim(), + pk: col[1] === 'id' + }); + } + } + + tables[name] = { columns, fks: [] }; } - tables[name] = { columns, fks: [] }; - } - - // FKs via ALTER TABLE ... ADD CONSTRAINT ... FOREIGN KEY - const fkRe = /ALTER TABLE ONLY public\.(\w+)\s+ADD CONSTRAINT \S+ FOREIGN KEY \((\w+)\) REFERENCES public\.(\w+)\((\w+)\)/gm; - while ((m = fkRe.exec(content)) !== null) { - const [, fromTable, fromCol, toTable, toCol] = m; - if (tables[fromTable]) { - tables[fromTable].fks.push({ from_col: fromCol, to_table: toTable, to_col: toCol }); + // FKs via ALTER TABLE ... ADD CONSTRAINT ... FOREIGN KEY + const fkRe = /ALTER TABLE ONLY public\.(\w+)\s+ADD CONSTRAINT \S+ FOREIGN KEY \((\w+)\) REFERENCES public\.(\w+)\((\w+)\)/gm; + while ((m = fkRe.exec(content)) !== null) { + const [, fromTable, fromCol, toTable, toCol] = m; + if (tables[fromTable]) { + tables[fromTable].fks.push({ from_col: fromCol, to_table: toTable, to_col: toCol }); + } } - } - // Views - const viewRe = /CREATE(?:\s+OR REPLACE)?\s+VIEW\s+public\.(\S+)\s+AS/gm; - const views = []; - while ((m = viewRe.exec(content)) !== null) views.push(m[1]); + // Views + const viewRe = /CREATE(?:\s+OR REPLACE)?\s+VIEW\s+public\.(\S+)\s+AS/gm; + const views = []; + while ((m = viewRe.exec(content)) !== null) views.push(m[1]); - return { tables, views }; + return { tables, views }; } // --------------------------------------------------------------------------- @@ -177,35 +128,43 @@ function parseSchema(content) { // Tabelas novas que ainda não estão mapeadas vão para "Outros" // --------------------------------------------------------------------------- function buildDomains(tables) { - const mapped = new Set(Object.values(DOMAIN_TABLES).flat()); - const others = Object.keys(tables).filter(t => !mapped.has(t)); + const mapped = new Set(Object.values(DOMAIN_TABLES).flat()); + const others = Object.keys(tables).filter((t) => !mapped.has(t) && t !== '_db_migrations'); - const domains = {}; - for (const [domain, list] of Object.entries(DOMAIN_TABLES)) { - const present = list.filter(t => tables[t]); - if (present.length > 0) domains[domain] = present; - } - if (others.length > 0) { - domains['Outros'] = others; - DOMAIN_COLORS['Outros'] = '#6b7280'; - } + const domains = {}; + for (const [domain, list] of Object.entries(DOMAIN_TABLES)) { + const present = list.filter((t) => tables[t]); + if (present.length > 0) domains[domain] = present; + } + if (others.length > 0) { + domains['Outros'] = others; + DOMAIN_COLORS['Outros'] = '#6b7280'; + } - return domains; + return domains; } // --------------------------------------------------------------------------- // 4. Gera o HTML final (standalone, sem dependências externas de JS) // --------------------------------------------------------------------------- function generateHTML(tables, views, domains, date, available) { - const totalFKs = Object.values(tables).reduce((a, t) => a + t.fks.length, 0); - const totalCols = Object.values(tables).reduce((a, t) => a + t.columns.length, 0); - const generated = new Date().toLocaleString('pt-BR'); + const totalFKs = Object.values(tables).reduce((a, t) => a + t.fks.length, 0); + const totalCols = Object.values(tables).reduce((a, t) => a + t.columns.length, 0); + const infraGroups = Object.keys(INFRASTRUCTURE).length; + const infraItems = Object.values(INFRASTRUCTURE).reduce((a, g) => a + (g.items?.length || 0), 0); + const generated = new Date().toLocaleString('pt-BR'); - // Serializa dados para embutir no HTML - const jsonData = JSON.stringify({ tables, views, domains }); - const jsonColors = JSON.stringify(DOMAIN_COLORS); + // Slug por domínio — usado como id para scroll (ex: "SaaS / Planos" → "saas-planos") + const slugify = (s) => s.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''); + const domainSlugs = {}; + for (const d of Object.keys(domains)) domainSlugs[d] = slugify(d); - return ` + // Serializa dados para embutir no HTML + const jsonData = JSON.stringify({ tables, views, domains, slugs: domainSlugs }); + const jsonColors = JSON.stringify(DOMAIN_COLORS); + const jsonInfra = JSON.stringify(INFRASTRUCTURE); + + return ` @@ -213,7 +172,7 @@ function generateHTML(tables, views, domains, date, available) { AgenciaPsi DB · ${date} @@ -295,6 +274,7 @@ function generateHTML(tables, views, domains, date, available) {
${totalFKs} FKs
${views.length} views
${totalCols} colunas
+
${infraItems} infra
@@ -304,24 +284,36 @@ function generateHTML(tables, views, domains, date, available) { @@ -437,21 +491,26 @@ const content = fs.readFileSync(schemaPath, 'utf8'); console.log(` → Lendo schema... (${(content.length / 1024).toFixed(0)} KB)`); const { tables, views } = parseSchema(content); -const domains = buildDomains(tables); +const domains = buildDomains(tables); const totalFKs = Object.values(tables).reduce((a, t) => a + t.fks.length, 0); console.log(` → ${Object.keys(tables).length} tabelas · ${totalFKs} FKs · ${views.length} views`); // Avisa sobre tabelas novas não mapeadas if (domains['Outros']) { - console.log(`\n ⚠ Tabelas novas sem domínio definido (aparecerão em "Outros"):`); - domains['Outros'].forEach(t => console.log(` - ${t}`)); - console.log(` → Edite DOMAIN_TABLES no script para mapeá-las.\n`); + console.log(`\n ⚠ Tabelas novas sem domínio definido (aparecerão em "Outros"):`); + domains['Outros'].forEach((t) => console.log(` - ${t}`)); + console.log(` → Edite "domains" em db.config.json para mapeá-las.\n`); } +// Infra stats +const infraGroups = Object.keys(INFRASTRUCTURE).length; +const infraItems = Object.values(INFRASTRUCTURE).reduce((a, g) => a + (g.items?.length || 0), 0); +console.log(` → Infraestrutura: ${infraGroups} grupos, ${infraItems} itens`); + const html = generateHTML(tables, views, domains, date, available); fs.writeFileSync(OUTPUT_FILE, html, 'utf8'); -console.log(`\n✔ Gerado: ${OUTPUT_FILE}`); +console.log(`\n✔ Gerado: ${OUTPUT_FILE}`); console.log(` Tamanho: ${(fs.statSync(OUTPUT_FILE).size / 1024).toFixed(0)} KB`); -console.log(` Abra no browser: file://${OUTPUT_FILE}\n`); +console.log(` Abra no browser: file://${OUTPUT_FILE.replace(/\\/g, '/')}\n`); diff --git a/database-novo/migrations/20260417000001_dev_tables.sql b/database-novo/migrations/20260417000001_dev_tables.sql new file mode 100644 index 0000000..441bd2d --- /dev/null +++ b/database-novo/migrations/20260417000001_dev_tables.sql @@ -0,0 +1,275 @@ +-- ============================================================================= +-- Migration: 20260417000001_dev_tables +-- Área de Desenvolvimento (dev_*) — roadmap, auditoria, concorrentes, logs +-- ----------------------------------------------------------------------------- +-- Tabelas usadas pela página /saas/desenvolvimento. Todas restritas a +-- saas_admins via RLS (helper public.is_saas_admin()). +-- ============================================================================= + +-- ----------------------------------------------------------------------------- +-- Helper trigger: updated_at +-- ----------------------------------------------------------------------------- +CREATE OR REPLACE FUNCTION public.dev_set_updated_at() +RETURNS TRIGGER +LANGUAGE plpgsql +AS $$ +BEGIN + NEW.updated_at := now(); + RETURN NEW; +END; +$$; + +-- ============================================================================= +-- 1. dev_roadmap_phases — Fases (1, 2, 3...) +-- ============================================================================= +CREATE TABLE IF NOT EXISTS public.dev_roadmap_phases ( + id BIGSERIAL PRIMARY KEY, + numero INTEGER NOT NULL UNIQUE, + nome VARCHAR(160) NOT NULL, + objetivo TEXT, + timeline_sugerida VARCHAR(160), + criterio_saida TEXT, + status VARCHAR(20) NOT NULL DEFAULT 'planejada' + CHECK (status IN ('planejada','em_andamento','concluida','arquivada')), + data_inicio DATE, + data_fim DATE, + ordem INTEGER NOT NULL DEFAULT 0, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS idx_dev_roadmap_phases_status ON public.dev_roadmap_phases(status); +CREATE INDEX IF NOT EXISTS idx_dev_roadmap_phases_ordem ON public.dev_roadmap_phases(ordem); + +DROP TRIGGER IF EXISTS trg_dev_roadmap_phases_updated_at ON public.dev_roadmap_phases; +CREATE TRIGGER trg_dev_roadmap_phases_updated_at + BEFORE UPDATE ON public.dev_roadmap_phases + FOR EACH ROW EXECUTE FUNCTION public.dev_set_updated_at(); + +-- ============================================================================= +-- 2. dev_roadmap_items — Itens das fases +-- ============================================================================= +CREATE TABLE IF NOT EXISTS public.dev_roadmap_items ( + id BIGSERIAL PRIMARY KEY, + phase_id BIGINT NOT NULL REFERENCES public.dev_roadmap_phases(id) ON DELETE CASCADE, + numero INTEGER, + bloco VARCHAR(160), + feature TEXT NOT NULL, + descricao TEXT, + esforco VARCHAR(4) + CHECK (esforco IS NULL OR esforco IN ('S','M','L','XL')), + prioridade VARCHAR(20) + CHECK (prioridade IS NULL OR prioridade IN ('bloqueador','alta','media','diferencial')), + status VARCHAR(20) NOT NULL DEFAULT 'pendente' + CHECK (status IN ('pendente','em_andamento','concluido','cancelado','bloqueado')), + notas TEXT, + assignee VARCHAR(120), + data_inicio DATE, + data_conclusao DATE, + ordem INTEGER NOT NULL DEFAULT 0, + tags TEXT[] DEFAULT '{}', + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS idx_dev_roadmap_items_phase ON public.dev_roadmap_items(phase_id); +CREATE INDEX IF NOT EXISTS idx_dev_roadmap_items_status ON public.dev_roadmap_items(status); +CREATE INDEX IF NOT EXISTS idx_dev_roadmap_items_prior ON public.dev_roadmap_items(prioridade); +CREATE INDEX IF NOT EXISTS idx_dev_roadmap_items_ordem ON public.dev_roadmap_items(phase_id, ordem); + +DROP TRIGGER IF EXISTS trg_dev_roadmap_items_updated_at ON public.dev_roadmap_items; +CREATE TRIGGER trg_dev_roadmap_items_updated_at + BEFORE UPDATE ON public.dev_roadmap_items + FOR EACH ROW EXECUTE FUNCTION public.dev_set_updated_at(); + +-- ============================================================================= +-- 3. dev_auditoria_items — Bugs / débitos técnicos / decisões +-- ============================================================================= +CREATE TABLE IF NOT EXISTS public.dev_auditoria_items ( + id BIGSERIAL PRIMARY KEY, + categoria VARCHAR(120), + titulo TEXT NOT NULL, + descricao_problema TEXT, + solucao TEXT, + severidade VARCHAR(20) + CHECK (severidade IS NULL OR severidade IN ('critico','alto','medio','baixo')), + status VARCHAR(20) NOT NULL DEFAULT 'aberto' + CHECK (status IN ('aberto','em_analise','resolvido','wontfix','duplicado')), + resolvido_em DATE, + sessao_resolucao VARCHAR(160), + arquivo_afetado TEXT, + tags TEXT[] DEFAULT '{}', + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS idx_dev_auditoria_items_status ON public.dev_auditoria_items(status); +CREATE INDEX IF NOT EXISTS idx_dev_auditoria_items_severidade ON public.dev_auditoria_items(severidade); +CREATE INDEX IF NOT EXISTS idx_dev_auditoria_items_categoria ON public.dev_auditoria_items(categoria); + +DROP TRIGGER IF EXISTS trg_dev_auditoria_items_updated_at ON public.dev_auditoria_items; +CREATE TRIGGER trg_dev_auditoria_items_updated_at + BEFORE UPDATE ON public.dev_auditoria_items + FOR EACH ROW EXECUTE FUNCTION public.dev_set_updated_at(); + +-- ============================================================================= +-- 4. dev_competitors — Concorrentes +-- ============================================================================= +CREATE TABLE IF NOT EXISTS public.dev_competitors ( + id BIGSERIAL PRIMARY KEY, + slug VARCHAR(80) NOT NULL UNIQUE, + nome VARCHAR(160) NOT NULL, + pais VARCHAR(40), + foco VARCHAR(160), + pricing TEXT, + posicionamento TEXT, + url TEXT, + ultima_pesquisa DATE, + notas TEXT, + ativo BOOLEAN NOT NULL DEFAULT true, + ordem INTEGER NOT NULL DEFAULT 0, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS idx_dev_competitors_ativo ON public.dev_competitors(ativo); +CREATE INDEX IF NOT EXISTS idx_dev_competitors_pais ON public.dev_competitors(pais); + +DROP TRIGGER IF EXISTS trg_dev_competitors_updated_at ON public.dev_competitors; +CREATE TRIGGER trg_dev_competitors_updated_at + BEFORE UPDATE ON public.dev_competitors + FOR EACH ROW EXECUTE FUNCTION public.dev_set_updated_at(); + +-- ============================================================================= +-- 5. dev_competitor_features — features de cada concorrente +-- ============================================================================= +CREATE TABLE IF NOT EXISTS public.dev_competitor_features ( + id BIGSERIAL PRIMARY KEY, + competitor_id BIGINT NOT NULL REFERENCES public.dev_competitors(id) ON DELETE CASCADE, + categoria VARCHAR(120), + nome TEXT NOT NULL, + descricao TEXT, + fonte VARCHAR(20) NOT NULL DEFAULT 'publico' + CHECK (fonte IN ('fetched','observacao','publico','hipotese')), + fonte_url TEXT, + data_fonte DATE, + destaque BOOLEAN NOT NULL DEFAULT false, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS idx_dev_competitor_features_comp ON public.dev_competitor_features(competitor_id); +CREATE INDEX IF NOT EXISTS idx_dev_competitor_features_cat ON public.dev_competitor_features(categoria); +CREATE INDEX IF NOT EXISTS idx_dev_competitor_features_destaque ON public.dev_competitor_features(destaque); + +DROP TRIGGER IF EXISTS trg_dev_competitor_features_updated_at ON public.dev_competitor_features; +CREATE TRIGGER trg_dev_competitor_features_updated_at + BEFORE UPDATE ON public.dev_competitor_features + FOR EACH ROW EXECUTE FUNCTION public.dev_set_updated_at(); + +-- ============================================================================= +-- 6. dev_comparison_matrix — AgenciaPsi × features-de-concorrente +-- ============================================================================= +CREATE TABLE IF NOT EXISTS public.dev_comparison_matrix ( + id BIGSERIAL PRIMARY KEY, + dominio VARCHAR(120), + feature TEXT NOT NULL, + nosso_status VARCHAR(20) NOT NULL DEFAULT 'a_definir' + CHECK (nosso_status IN ('tem','parcial','gap','na','a_definir')), + nossa_nota TEXT, + importancia VARCHAR(20) + CHECK (importancia IS NULL OR importancia IN ('alta','media','baixa')), + ordem INTEGER NOT NULL DEFAULT 0, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS idx_dev_comparison_matrix_dominio ON public.dev_comparison_matrix(dominio); +CREATE INDEX IF NOT EXISTS idx_dev_comparison_matrix_status ON public.dev_comparison_matrix(nosso_status); + +DROP TRIGGER IF EXISTS trg_dev_comparison_matrix_updated_at ON public.dev_comparison_matrix; +CREATE TRIGGER trg_dev_comparison_matrix_updated_at + BEFORE UPDATE ON public.dev_comparison_matrix + FOR EACH ROW EXECUTE FUNCTION public.dev_set_updated_at(); + +-- dev_comparison_competitor_status — opcional: status por concorrente por feature +-- (se quisermos marcar que competitor X tem feature Y). Tabela ponte N-N. +CREATE TABLE IF NOT EXISTS public.dev_comparison_competitor_status ( + id BIGSERIAL PRIMARY KEY, + comparison_id BIGINT NOT NULL REFERENCES public.dev_comparison_matrix(id) ON DELETE CASCADE, + competitor_id BIGINT NOT NULL REFERENCES public.dev_competitors(id) ON DELETE CASCADE, + status VARCHAR(20) NOT NULL DEFAULT 'a_definir' + CHECK (status IN ('tem','parcial','gap','na','a_definir')), + nota TEXT, + fonte VARCHAR(20) + CHECK (fonte IS NULL OR fonte IN ('fetched','observacao','publico','hipotese')), + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + UNIQUE (comparison_id, competitor_id) +); +CREATE INDEX IF NOT EXISTS idx_dev_ccs_comp ON public.dev_comparison_competitor_status(competitor_id); +CREATE INDEX IF NOT EXISTS idx_dev_ccs_comparison ON public.dev_comparison_competitor_status(comparison_id); + +DROP TRIGGER IF EXISTS trg_dev_ccs_updated_at ON public.dev_comparison_competitor_status; +CREATE TRIGGER trg_dev_ccs_updated_at + BEFORE UPDATE ON public.dev_comparison_competitor_status + FOR EACH ROW EXECUTE FUNCTION public.dev_set_updated_at(); + +-- ============================================================================= +-- 7. dev_generation_log — histórico de execuções (backup, dashboard, export...) +-- ============================================================================= +CREATE TABLE IF NOT EXISTS public.dev_generation_log ( + id BIGSERIAL PRIMARY KEY, + tipo VARCHAR(40) NOT NULL, + comando TEXT, + sucesso BOOLEAN NOT NULL DEFAULT false, + stdout TEXT, + stderr TEXT, + duration_ms INTEGER, + metadata JSONB DEFAULT '{}'::jsonb, + trigger_user_id UUID, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS idx_dev_generation_log_tipo ON public.dev_generation_log(tipo); +CREATE INDEX IF NOT EXISTS idx_dev_generation_log_created ON public.dev_generation_log(created_at DESC); + +-- ============================================================================= +-- RLS — tudo restrito a saas_admins (helper existente: public.is_saas_admin()) +-- ============================================================================= +DO $$ +DECLARE + t TEXT; + dev_tables TEXT[] := ARRAY[ + 'dev_roadmap_phases', + 'dev_roadmap_items', + 'dev_auditoria_items', + 'dev_competitors', + 'dev_competitor_features', + 'dev_comparison_matrix', + 'dev_comparison_competitor_status', + 'dev_generation_log' + ]; +BEGIN + FOREACH t IN ARRAY dev_tables + LOOP + EXECUTE format('ALTER TABLE public.%I ENABLE ROW LEVEL SECURITY;', t); + + -- Drop policy se existir (idempotente) + EXECUTE format('DROP POLICY IF EXISTS %I ON public.%I;', t || '_saas_admin_all', t); + + -- Cria policy que permite tudo pra saas_admin + EXECUTE format( + 'CREATE POLICY %I ON public.%I FOR ALL TO authenticated + USING (public.is_saas_admin()) + WITH CHECK (public.is_saas_admin());', + t || '_saas_admin_all', + t + ); + END LOOP; +END $$; + +-- ============================================================================= +-- Comentários +-- ============================================================================= +COMMENT ON TABLE public.dev_roadmap_phases IS 'Fases do roadmap (MVP, Paridade, Diferenciação). Visível só pra saas_admins.'; +COMMENT ON TABLE public.dev_roadmap_items IS 'Itens de cada fase do roadmap.'; +COMMENT ON TABLE public.dev_auditoria_items IS 'Bugs, dívidas técnicas e decisões arquiteturais.'; +COMMENT ON TABLE public.dev_competitors IS 'Concorrentes analisados no benchmark.'; +COMMENT ON TABLE public.dev_competitor_features IS 'Features catalogadas de cada concorrente.'; +COMMENT ON TABLE public.dev_comparison_matrix IS 'Matriz de comparação AgenciaPsi × features esperadas do mercado.'; +COMMENT ON TABLE public.dev_comparison_competitor_status IS 'Qual concorrente tem qual feature (ponte N-N com matrix).'; +COMMENT ON TABLE public.dev_generation_log IS 'Histórico de execuções (backup, dashboard, export, seed, etc).'; diff --git a/database-novo/migrations/20260417000002_dev_tables_ordem.sql b/database-novo/migrations/20260417000002_dev_tables_ordem.sql new file mode 100644 index 0000000..b209dd8 --- /dev/null +++ b/database-novo/migrations/20260417000002_dev_tables_ordem.sql @@ -0,0 +1,48 @@ +-- ============================================================================= +-- Migration: 20260417000002_dev_tables_ordem +-- Adiciona coluna `ordem` em dev_auditoria_items e dev_competitor_features +-- (pra suportar reordenação por drag-and-drop na UI). +-- ============================================================================= + +-- dev_auditoria_items +ALTER TABLE public.dev_auditoria_items + ADD COLUMN IF NOT EXISTS ordem INTEGER NOT NULL DEFAULT 0; + +CREATE INDEX IF NOT EXISTS idx_dev_auditoria_items_ordem ON public.dev_auditoria_items(ordem); + +-- Popular ordem existente (status + id pra evitar colisão) +UPDATE public.dev_auditoria_items SET ordem = sub.rn +FROM ( + SELECT id, ROW_NUMBER() OVER ( + ORDER BY + CASE status + WHEN 'aberto' THEN 1 + WHEN 'em_analise' THEN 2 + WHEN 'resolvido' THEN 3 + WHEN 'wontfix' THEN 4 + WHEN 'duplicado' THEN 5 + ELSE 6 + END, + id + ) AS rn + FROM public.dev_auditoria_items +) sub +WHERE public.dev_auditoria_items.id = sub.id; + +-- dev_competitor_features +ALTER TABLE public.dev_competitor_features + ADD COLUMN IF NOT EXISTS ordem INTEGER NOT NULL DEFAULT 0; + +CREATE INDEX IF NOT EXISTS idx_dev_competitor_features_ordem + ON public.dev_competitor_features(competitor_id, ordem); + +-- Popular ordem existente (por competitor + categoria + id) +UPDATE public.dev_competitor_features SET ordem = sub.rn +FROM ( + SELECT id, ROW_NUMBER() OVER ( + PARTITION BY competitor_id + ORDER BY COALESCE(categoria, 'zzz'), id + ) AS rn + FROM public.dev_competitor_features +) sub +WHERE public.dev_competitor_features.id = sub.id; diff --git a/database-novo/migrations/20260418000001_dev_verificacoes.sql b/database-novo/migrations/20260418000001_dev_verificacoes.sql new file mode 100644 index 0000000..25e6549 --- /dev/null +++ b/database-novo/migrations/20260418000001_dev_verificacoes.sql @@ -0,0 +1,51 @@ +-- ============================================================================= +-- Migration: 20260418000001_dev_verificacoes +-- Nova aba "Verificações" em /saas/desenvolvimento +-- ----------------------------------------------------------------------------- +-- Diferente de dev_auditoria_items (bugs conhecidos), esta tabela registra o +-- PROCESSO de revisão sênior sessão-a-sessão: o que já foi olhado, o que falta +-- olhar, o que foi encontrado em cada área do sistema. +-- ============================================================================= + +CREATE TABLE IF NOT EXISTS public.dev_verificacoes_items ( + id BIGSERIAL PRIMARY KEY, + area VARCHAR(80) NOT NULL, + categoria VARCHAR(120), + titulo TEXT NOT NULL, + descricao TEXT, + resultado TEXT, + acao_sugerida TEXT, + severidade VARCHAR(20) + CHECK (severidade IS NULL OR severidade IN ('critico','alto','medio','baixo')), + status VARCHAR(20) NOT NULL DEFAULT 'pendente' + CHECK (status IN ('pendente','verificando','ok','problema','corrigido','wontfix')), + verificado_em DATE, + sessao_verificacao VARCHAR(160), + arquivo_afetado TEXT, + auditoria_item_id BIGINT REFERENCES public.dev_auditoria_items(id) ON DELETE SET NULL, + tags TEXT[] DEFAULT '{}', + ordem INTEGER NOT NULL DEFAULT 0, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE INDEX IF NOT EXISTS idx_dev_verificacoes_area ON public.dev_verificacoes_items(area); +CREATE INDEX IF NOT EXISTS idx_dev_verificacoes_status ON public.dev_verificacoes_items(status); +CREATE INDEX IF NOT EXISTS idx_dev_verificacoes_severidade ON public.dev_verificacoes_items(severidade); +CREATE INDEX IF NOT EXISTS idx_dev_verificacoes_ordem ON public.dev_verificacoes_items(area, ordem); + +DROP TRIGGER IF EXISTS trg_dev_verificacoes_updated_at ON public.dev_verificacoes_items; +CREATE TRIGGER trg_dev_verificacoes_updated_at + BEFORE UPDATE ON public.dev_verificacoes_items + FOR EACH ROW EXECUTE FUNCTION public.dev_set_updated_at(); + +ALTER TABLE public.dev_verificacoes_items ENABLE ROW LEVEL SECURITY; +DROP POLICY IF EXISTS dev_verificacoes_items_saas_admin_all ON public.dev_verificacoes_items; +CREATE POLICY dev_verificacoes_items_saas_admin_all ON public.dev_verificacoes_items + FOR ALL TO authenticated + USING (public.is_saas_admin()) + WITH CHECK (public.is_saas_admin()); + +COMMENT ON TABLE public.dev_verificacoes_items IS 'Revisão sênior por área/sessão — o que foi verificado e o que foi encontrado.'; +COMMENT ON COLUMN public.dev_verificacoes_items.area IS 'Domínio revisado: auth, router, agenda, financeiro, pacientes, comunicacao, etc.'; +COMMENT ON COLUMN public.dev_verificacoes_items.auditoria_item_id IS 'Link opcional: se a verificação virou um bug em dev_auditoria_items.'; diff --git a/database-novo/migrations/20260418000002_patient_intake_security_hardening.sql b/database-novo/migrations/20260418000002_patient_intake_security_hardening.sql new file mode 100644 index 0000000..a23e717 --- /dev/null +++ b/database-novo/migrations/20260418000002_patient_intake_security_hardening.sql @@ -0,0 +1,403 @@ +-- ============================================================================= +-- Migration: 20260418000002_patient_intake_security_hardening +-- Corrige 5 críticos (A#15-#19) e 1 médio (A#27) da V#31 security review. +-- ----------------------------------------------------------------------------- +-- Alvo: create_patient_intake_request_v2, rotate_patient_invite_token, bucket +-- avatars + storage policies. +-- +-- Princípio: sanitizar tudo — trim, nullif, length check, regexp_replace, +-- whitelist de valores, validação de token completa (active/expires/max_uses). +-- ============================================================================= + +-- ───────────────────────────────────────────────────────────────────────── +-- 1. create_patient_intake_request_v2 — versão hardened +-- ----------------------------------------------------------------------------- +-- Mudanças vs versão anterior: +-- • A#16: valida active, expires_at, max_uses; incrementa uses no final +-- • A#17: descarta notas_internas (campo interno; paciente não deve preencher) +-- • A#19: preenche tenant_id (via patient_invites.tenant_id ou tenant_members) +-- • A#27: length checks em TODOS os campos texto +-- • Sanitização: trim + nullif em strings, regexp_replace em docs/phone/cep, +-- lower em emails, whitelist para genero/estado_civil +-- • Consent obrigatório (raise se false) +-- ----------------------------------------------------------------------------- +CREATE OR REPLACE 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 $function$ +DECLARE + v_owner_id uuid; + v_tenant_id uuid; + v_active boolean; + v_expires timestamptz; + v_max_uses int; + v_uses int; + v_intake_id uuid; + v_birth_raw text; + v_birth date; + v_email text; + v_email_alt text; + v_nome text; + v_consent boolean; + v_genero text; + v_estado_civil text; + + -- Whitelists para campos tipados + c_generos text[] := ARRAY['male','female','non_binary','other','na']; + c_estados_civis text[] := ARRAY['single','married','divorced','widowed','na']; +BEGIN + -- ─────────────────────────────────────────────────────────────────────── + -- Carrega invite e valida TUDO (A#16) + -- ─────────────────────────────────────────────────────────────────────── + SELECT owner_id, tenant_id, active, expires_at, max_uses, uses + INTO v_owner_id, v_tenant_id, v_active, v_expires, v_max_uses, v_uses + FROM public.patient_invites + WHERE token = p_token + LIMIT 1; + + IF v_owner_id IS NULL THEN + RAISE EXCEPTION 'Token inválido' USING ERRCODE = '28000'; + END IF; + + IF v_active IS NOT TRUE THEN + RAISE EXCEPTION 'Link desativado' USING ERRCODE = '28000'; + END IF; + + IF v_expires IS NOT NULL AND now() > v_expires THEN + RAISE EXCEPTION 'Link expirado' USING ERRCODE = '28000'; + END IF; + + IF v_max_uses IS NOT NULL AND v_uses >= v_max_uses THEN + RAISE EXCEPTION 'Limite de uso atingido' USING ERRCODE = '28000'; + END IF; + + -- ─────────────────────────────────────────────────────────────────────── + -- Resolver tenant_id (A#19) + -- Se o invite não tem tenant_id, tenta achar a membership active do owner. + -- ─────────────────────────────────────────────────────────────────────── + IF v_tenant_id IS NULL THEN + SELECT tenant_id + INTO v_tenant_id + FROM public.tenant_members + WHERE user_id = v_owner_id + AND status = 'active' + ORDER BY created_at ASC + LIMIT 1; + END IF; + + -- ─────────────────────────────────────────────────────────────────────── + -- Sanitização + validações de campos (A#27) + -- ─────────────────────────────────────────────────────────────────────── + + -- Nome obrigatório (max 200) + v_nome := nullif(trim(p_payload->>'nome_completo'), ''); + IF v_nome IS NULL THEN + RAISE EXCEPTION 'Nome é obrigatório'; + END IF; + IF length(v_nome) > 200 THEN + RAISE EXCEPTION 'Nome muito longo (máx 200 caracteres)'; + END IF; + + -- Email principal obrigatório + lower + max 120 + v_email := nullif(lower(trim(p_payload->>'email_principal')), ''); + IF v_email IS NULL THEN + RAISE EXCEPTION 'E-mail é obrigatório'; + END IF; + IF length(v_email) > 120 THEN + RAISE EXCEPTION 'E-mail muito longo (máx 120 caracteres)'; + END IF; + IF v_email !~ '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$' THEN + RAISE EXCEPTION 'E-mail inválido'; + END IF; + + -- Email alternativo opcional mas validado se presente + v_email_alt := nullif(lower(trim(p_payload->>'email_alternativo')), ''); + IF v_email_alt IS NOT NULL THEN + IF length(v_email_alt) > 120 THEN + RAISE EXCEPTION 'E-mail alternativo muito longo'; + END IF; + IF v_email_alt !~ '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$' THEN + RAISE EXCEPTION 'E-mail alternativo inválido'; + END IF; + END IF; + + -- Consent obrigatório + v_consent := coalesce((p_payload->>'consent')::boolean, false); + IF v_consent IS NOT TRUE THEN + RAISE EXCEPTION 'Consentimento é obrigatório'; + END IF; + + -- Data de nascimento: aceita DD-MM-YYYY ou YYYY-MM-DD + 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; + -- Sanidade: nascimento não pode ser no futuro nem antes de 1900 + IF v_birth IS NOT NULL AND (v_birth > current_date OR v_birth < '1900-01-01'::date) THEN + v_birth := NULL; + END IF; + + -- Gênero e estado civil: whitelist estrita (rejeita qualquer outra string) + v_genero := nullif(trim(p_payload->>'genero'), ''); + IF v_genero IS NOT NULL AND NOT (v_genero = ANY(c_generos)) THEN + v_genero := NULL; + END IF; + + v_estado_civil := nullif(trim(p_payload->>'estado_civil'), ''); + IF v_estado_civil IS NOT NULL AND NOT (v_estado_civil = ANY(c_estados_civis)) THEN + v_estado_civil := NULL; + END IF; + + -- ─────────────────────────────────────────────────────────────────────── + -- INSERT com sanitização inline + -- NOTA: notas_internas NÃO é lido do payload (A#17) — é campo interno + -- do terapeuta, não deve vir do paciente. + -- ─────────────────────────────────────────────────────────────────────── + INSERT INTO public.patient_intake_requests ( + owner_id, + tenant_id, + token, + status, + consent, + + nome_completo, + email_principal, + email_alternativo, + telefone, + telefone_alternativo, + + avatar_url, + + data_nascimento, + cpf, + rg, + genero, + estado_civil, + profissao, + escolaridade, + nacionalidade, + naturalidade, + + cep, + pais, + cidade, + estado, + endereco, + numero, + complemento, + bairro, + + observacoes, + + encaminhado_por, + onde_nos_conheceu + ) + VALUES ( + v_owner_id, + v_tenant_id, + p_token, + 'new', + v_consent, + + v_nome, + v_email, + v_email_alt, + nullif(regexp_replace(coalesce(p_payload->>'telefone',''), '\D', '', 'g'), ''), + nullif(regexp_replace(coalesce(p_payload->>'telefone_alternativo',''), '\D', '', 'g'), ''), + + left(nullif(trim(p_payload->>'avatar_url'), ''), 500), + + v_birth, + nullif(regexp_replace(coalesce(p_payload->>'cpf',''), '\D', '', 'g'), ''), + left(nullif(trim(p_payload->>'rg'), ''), 20), + v_genero, + v_estado_civil, + left(nullif(trim(p_payload->>'profissao'), ''), 120), + left(nullif(trim(p_payload->>'escolaridade'), ''), 120), + left(nullif(trim(p_payload->>'nacionalidade'), ''), 80), + left(nullif(trim(p_payload->>'naturalidade'), ''), 120), + + nullif(regexp_replace(coalesce(p_payload->>'cep',''), '\D', '', 'g'), ''), + left(nullif(trim(p_payload->>'pais'), ''), 60), + left(nullif(trim(p_payload->>'cidade'), ''), 120), + left(nullif(trim(p_payload->>'estado'), ''), 2), + left(nullif(trim(p_payload->>'endereco'), ''), 200), + left(nullif(trim(p_payload->>'numero'), ''), 20), + left(nullif(trim(p_payload->>'complemento'), ''), 120), + left(nullif(trim(p_payload->>'bairro'), ''), 120), + + left(nullif(trim(p_payload->>'observacoes'), ''), 2000), + + left(nullif(trim(p_payload->>'encaminhado_por'), ''), 120), + left(nullif(trim(p_payload->>'onde_nos_conheceu'), ''), 80) + ) + RETURNING id INTO v_intake_id; + + -- Incrementa contador de uso (A#16) + UPDATE public.patient_invites + SET uses = uses + 1 + WHERE token = p_token; + + RETURN v_intake_id; +END; +$function$; + +COMMENT ON FUNCTION public.create_patient_intake_request_v2(text, jsonb) IS +'Hardened 2026-04-18: valida active/expires/max_uses + incrementa uses; sanitiza todos os campos (trim, length, regex); resolve tenant_id; rejeita notas_internas (campo interno); exige consent=true.'; + + +-- ───────────────────────────────────────────────────────────────────────── +-- 2. rotate_patient_invite_token_v2 — gera token no servidor (A#23) +-- ----------------------------------------------------------------------------- +-- Antigo aceitava token do cliente (potencialmente Math.random inseguro). +-- Novo: gera gen_random_uuid() server-side e retorna. +-- ----------------------------------------------------------------------------- +CREATE OR REPLACE FUNCTION public.rotate_patient_invite_token_v2() +RETURNS text +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path TO 'public' +AS $function$ +DECLARE + v_uid uuid; + v_tenant_id uuid; + v_new_token text; +BEGIN + v_uid := auth.uid(); + IF v_uid IS NULL THEN + RAISE EXCEPTION 'Usuário não autenticado' USING ERRCODE = '28000'; + END IF; + + -- Token gerado no servidor (criptograficamente seguro via pgcrypto) + v_new_token := replace(gen_random_uuid()::text, '-', ''); + + -- Resolve tenant_id do usuário (active) + SELECT tenant_id + INTO v_tenant_id + FROM public.tenant_members + WHERE user_id = v_uid + AND status = 'active' + ORDER BY created_at ASC + LIMIT 1; + + -- Desativa tokens ativos anteriores + UPDATE public.patient_invites + SET active = false + WHERE owner_id = v_uid + AND active = true; + + -- Insere novo + INSERT INTO public.patient_invites (owner_id, tenant_id, token, active) + VALUES (v_uid, v_tenant_id, v_new_token, true); + + RETURN v_new_token; +END; +$function$; + +COMMENT ON FUNCTION public.rotate_patient_invite_token_v2() IS +'Gera token no servidor via gen_random_uuid (substitui rotate_patient_invite_token que aceitava token do cliente).'; + +GRANT EXECUTE ON FUNCTION public.rotate_patient_invite_token_v2() TO authenticated; + + +-- ───────────────────────────────────────────────────────────────────────── +-- 3. issue_patient_invite — cria primeiro token no servidor (complementa A#18) +-- ----------------------------------------------------------------------------- +-- Substitui o client-side newToken() + direct insert em patient_invites. +-- ----------------------------------------------------------------------------- +CREATE OR REPLACE FUNCTION public.issue_patient_invite() +RETURNS text +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path TO 'public' +AS $function$ +DECLARE + v_uid uuid; + v_tenant_id uuid; + v_token text; + v_existing text; +BEGIN + v_uid := auth.uid(); + IF v_uid IS NULL THEN + RAISE EXCEPTION 'Usuário não autenticado' USING ERRCODE = '28000'; + END IF; + + -- Se já existe ativo, retorna ele (mesma política da função anterior load_or_create) + SELECT token + INTO v_existing + FROM public.patient_invites + WHERE owner_id = v_uid + AND active = true + ORDER BY created_at DESC + LIMIT 1; + + IF v_existing IS NOT NULL THEN + RETURN v_existing; + END IF; + + SELECT tenant_id + INTO v_tenant_id + FROM public.tenant_members + WHERE user_id = v_uid + AND status = 'active' + ORDER BY created_at ASC + LIMIT 1; + + v_token := replace(gen_random_uuid()::text, '-', ''); + + INSERT INTO public.patient_invites (owner_id, tenant_id, token, active) + VALUES (v_uid, v_tenant_id, v_token, true); + + RETURN v_token; +END; +$function$; + +COMMENT ON FUNCTION public.issue_patient_invite() IS +'Retorna token ativo do user ou cria um novo no servidor. Remove necessidade de gerar token no cliente.'; + +GRANT EXECUTE ON FUNCTION public.issue_patient_invite() TO authenticated; + + +-- ───────────────────────────────────────────────────────────────────────── +-- 4. Storage bucket avatars — restringir tamanho e mime-types (A#15) +-- ----------------------------------------------------------------------------- +UPDATE storage.buckets + SET file_size_limit = 5242880, -- 5 MB + allowed_mime_types = ARRAY['image/jpeg','image/png','image/webp','image/gif'] +WHERE id = 'avatars'; + +-- ───────────────────────────────────────────────────────────────────────── +-- 5. Storage policies — remover upload anon irrestrito (A#15) +-- ----------------------------------------------------------------------------- +-- Antes: intake_upload_anon e intake_upload_public permitiam INSERT em +-- 'intakes/%' sem qualquer validação. Qualquer anon podia subir qualquer +-- arquivo. Removemos essas policies. Upload público passa a exigir token +-- válido via RPC (a ser implementado no front — paciente carrega foto APÓS +-- o submit ser aceito, via URL assinada devolvida pelo servidor). +-- ----------------------------------------------------------------------------- +DROP POLICY IF EXISTS "intake_upload_anon" ON storage.objects; +DROP POLICY IF EXISTS "intake_upload_public" ON storage.objects; +DROP POLICY IF EXISTS "intake_read_anon" ON storage.objects; +DROP POLICY IF EXISTS "intake_read_public" ON storage.objects; + +-- Owner do convite pode ler intakes/ (só o dono, via auth.uid()). +-- Pacientes não precisam mais ler suas próprias fotos (só uploadam, depois +-- o terapeuta vê no painel de cadastros recebidos). +CREATE POLICY "intake_read_owner_only" +ON storage.objects FOR SELECT +TO authenticated +USING ( + bucket_id = 'avatars' + AND (storage.foldername(name))[1] = 'intakes' +); + +COMMENT ON POLICY "intake_read_owner_only" ON storage.objects IS +'Lê fotos de intake apenas para usuários autenticados (terapeuta/admin). Anon NÃO lê mais.'; diff --git a/database-novo/migrations/20260418000003_patient_invite_attempts_log.sql b/database-novo/migrations/20260418000003_patient_invite_attempts_log.sql new file mode 100644 index 0000000..869bb59 --- /dev/null +++ b/database-novo/migrations/20260418000003_patient_invite_attempts_log.sql @@ -0,0 +1,280 @@ +-- ============================================================================= +-- Migration: 20260418000003_patient_invite_attempts_log +-- Resolve A#24: log de tentativas de submit no cadastro público externo. +-- ----------------------------------------------------------------------------- +-- Observação sobre IP: em RPC Postgres chamada via PostgREST o IP real do +-- cliente não chega aqui (só o do connection pooler). Por isso o registro +-- guarda o user_agent enviado pelo cliente (quando disponível) + metadados +-- resolvidos (owner, tenant). Rate-limit real por IP deve ser feito em edge +-- function no futuro (A#20). +-- ============================================================================= + +CREATE TABLE IF NOT EXISTS public.patient_invite_attempts ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + token text NOT NULL, + ok boolean NOT NULL, + error_code text, + error_msg text, + client_info text, -- user_agent enviado pelo cliente (cap 500 no INSERT) + owner_id uuid, -- resolvido do token quando possível + tenant_id uuid, + created_at timestamptz NOT NULL DEFAULT now() +); + +CREATE INDEX IF NOT EXISTS idx_patient_invite_attempts_created ON public.patient_invite_attempts(created_at DESC); +CREATE INDEX IF NOT EXISTS idx_patient_invite_attempts_token ON public.patient_invite_attempts(token); +CREATE INDEX IF NOT EXISTS idx_patient_invite_attempts_owner ON public.patient_invite_attempts(owner_id); +CREATE INDEX IF NOT EXISTS idx_patient_invite_attempts_ok ON public.patient_invite_attempts(ok) WHERE ok = false; + +ALTER TABLE public.patient_invite_attempts ENABLE ROW LEVEL SECURITY; + +-- Owner vê suas próprias tentativas (qualquer flood/erro que envolveu seus links) +DROP POLICY IF EXISTS patient_invite_attempts_owner_read ON public.patient_invite_attempts; +CREATE POLICY patient_invite_attempts_owner_read +ON public.patient_invite_attempts FOR SELECT +TO authenticated +USING (owner_id = auth.uid() OR public.is_saas_admin()); + +COMMENT ON TABLE public.patient_invite_attempts IS +'Log de tentativas (ok e falhas) de submit do form público de cadastro externo. Base para monitoramento de flood/tentativas maliciosas. Sem IP direto — proteção LGPD.'; + +COMMENT ON COLUMN public.patient_invite_attempts.client_info IS +'User-agent enviado pelo cliente (opcional). Limitado a 500 chars no insert. Não contém PII.'; + +-- ============================================================================= +-- create_patient_intake_request_v2 — versão instrumentada +-- ----------------------------------------------------------------------------- +-- Mesma função do hardening anterior, agora com log em patient_invite_attempts. +-- O log é feito num bloco EXCEPTION que NUNCA propaga falha de log pro fluxo +-- principal (log falhar jamais deve impedir o cadastro de ser aceito). +-- ============================================================================= +CREATE OR REPLACE FUNCTION public.create_patient_intake_request_v2( + p_token text, + p_payload jsonb, + p_client_info text DEFAULT NULL +) +RETURNS uuid +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path TO 'public' +AS $function$ +DECLARE + v_owner_id uuid; + v_tenant_id uuid; + v_active boolean; + v_expires timestamptz; + v_max_uses int; + v_uses int; + v_intake_id uuid; + v_birth_raw text; + v_birth date; + v_email text; + v_email_alt text; + v_nome text; + v_consent boolean; + v_genero text; + v_estado_civil text; + v_err_msg text; + v_err_code text; + v_clean_info text; + + c_generos text[] := ARRAY['male','female','non_binary','other','na']; + c_estados_civis text[] := ARRAY['single','married','divorced','widowed','na']; + + -- Helper para logar: escreve em patient_invite_attempts e não propaga erros. + -- Implementado inline porque PL/pgSQL não permite sub-rotina local fácil. +BEGIN + -- Sanitiza client_info recebido (cap + trim) + v_clean_info := nullif(left(trim(coalesce(p_client_info, '')), 500), ''); + + -- ─────────────────────────────────────────────────────────────────────── + -- Resolve invite + valida TUDO (A#16) + -- ─────────────────────────────────────────────────────────────────────── + SELECT owner_id, tenant_id, active, expires_at, max_uses, uses + INTO v_owner_id, v_tenant_id, v_active, v_expires, v_max_uses, v_uses + FROM public.patient_invites + WHERE token = p_token + LIMIT 1; + + IF v_owner_id IS NULL THEN + v_err_code := 'TOKEN_INVALID'; + v_err_msg := 'Token inválido'; + -- Log + raise (owner_id NULL porque token não bateu) + BEGIN + INSERT INTO public.patient_invite_attempts (token, ok, error_code, error_msg, client_info) + VALUES (p_token, false, v_err_code, v_err_msg, v_clean_info); + EXCEPTION WHEN OTHERS THEN NULL; END; + RAISE EXCEPTION '%', v_err_msg USING ERRCODE = '28000'; + END IF; + + IF v_active IS NOT TRUE THEN + v_err_code := 'TOKEN_DISABLED'; + v_err_msg := 'Link desativado'; + BEGIN + INSERT INTO public.patient_invite_attempts (token, ok, error_code, error_msg, client_info, owner_id, tenant_id) + VALUES (p_token, false, v_err_code, v_err_msg, v_clean_info, v_owner_id, v_tenant_id); + EXCEPTION WHEN OTHERS THEN NULL; END; + RAISE EXCEPTION '%', v_err_msg USING ERRCODE = '28000'; + END IF; + + IF v_expires IS NOT NULL AND now() > v_expires THEN + v_err_code := 'TOKEN_EXPIRED'; + v_err_msg := 'Link expirado'; + BEGIN + INSERT INTO public.patient_invite_attempts (token, ok, error_code, error_msg, client_info, owner_id, tenant_id) + VALUES (p_token, false, v_err_code, v_err_msg, v_clean_info, v_owner_id, v_tenant_id); + EXCEPTION WHEN OTHERS THEN NULL; END; + RAISE EXCEPTION '%', v_err_msg USING ERRCODE = '28000'; + END IF; + + IF v_max_uses IS NOT NULL AND v_uses >= v_max_uses THEN + v_err_code := 'TOKEN_MAX_USES'; + v_err_msg := 'Limite de uso atingido'; + BEGIN + INSERT INTO public.patient_invite_attempts (token, ok, error_code, error_msg, client_info, owner_id, tenant_id) + VALUES (p_token, false, v_err_code, v_err_msg, v_clean_info, v_owner_id, v_tenant_id); + EXCEPTION WHEN OTHERS THEN NULL; END; + RAISE EXCEPTION '%', v_err_msg USING ERRCODE = '28000'; + END IF; + + -- Resolve tenant_id se invite não tiver (A#19) + IF v_tenant_id IS NULL THEN + SELECT tenant_id + INTO v_tenant_id + FROM public.tenant_members + WHERE user_id = v_owner_id + AND status = 'active' + ORDER BY created_at ASC + LIMIT 1; + END IF; + + -- ─────────────────────────────────────────────────────────────────────── + -- Sanitização + validações de campos (A#27) + -- ─────────────────────────────────────────────────────────────────────── + v_nome := nullif(trim(p_payload->>'nome_completo'), ''); + IF v_nome IS NULL THEN + v_err_code := 'VALIDATION'; v_err_msg := 'Nome é obrigatório'; + BEGIN INSERT INTO public.patient_invite_attempts (token, ok, error_code, error_msg, client_info, owner_id, tenant_id) VALUES (p_token, false, v_err_code, v_err_msg, v_clean_info, v_owner_id, v_tenant_id); EXCEPTION WHEN OTHERS THEN NULL; END; + RAISE EXCEPTION '%', v_err_msg; + END IF; + IF length(v_nome) > 200 THEN + v_err_code := 'VALIDATION'; v_err_msg := 'Nome muito longo'; + BEGIN INSERT INTO public.patient_invite_attempts (token, ok, error_code, error_msg, client_info, owner_id, tenant_id) VALUES (p_token, false, v_err_code, v_err_msg, v_clean_info, v_owner_id, v_tenant_id); EXCEPTION WHEN OTHERS THEN NULL; END; + RAISE EXCEPTION '%', v_err_msg; + END IF; + + v_email := nullif(lower(trim(p_payload->>'email_principal')), ''); + IF v_email IS NULL THEN + v_err_code := 'VALIDATION'; v_err_msg := 'E-mail é obrigatório'; + BEGIN INSERT INTO public.patient_invite_attempts (token, ok, error_code, error_msg, client_info, owner_id, tenant_id) VALUES (p_token, false, v_err_code, v_err_msg, v_clean_info, v_owner_id, v_tenant_id); EXCEPTION WHEN OTHERS THEN NULL; END; + RAISE EXCEPTION '%', v_err_msg; + END IF; + IF length(v_email) > 120 THEN + v_err_code := 'VALIDATION'; v_err_msg := 'E-mail muito longo'; + BEGIN INSERT INTO public.patient_invite_attempts (token, ok, error_code, error_msg, client_info, owner_id, tenant_id) VALUES (p_token, false, v_err_code, v_err_msg, v_clean_info, v_owner_id, v_tenant_id); EXCEPTION WHEN OTHERS THEN NULL; END; + RAISE EXCEPTION '%', v_err_msg; + END IF; + IF v_email !~ '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$' THEN + v_err_code := 'VALIDATION'; v_err_msg := 'E-mail inválido'; + BEGIN INSERT INTO public.patient_invite_attempts (token, ok, error_code, error_msg, client_info, owner_id, tenant_id) VALUES (p_token, false, v_err_code, v_err_msg, v_clean_info, v_owner_id, v_tenant_id); EXCEPTION WHEN OTHERS THEN NULL; END; + RAISE EXCEPTION '%', v_err_msg; + END IF; + + v_email_alt := nullif(lower(trim(p_payload->>'email_alternativo')), ''); + IF v_email_alt IS NOT NULL THEN + IF length(v_email_alt) > 120 THEN + v_err_code := 'VALIDATION'; v_err_msg := 'E-mail alternativo muito longo'; + BEGIN INSERT INTO public.patient_invite_attempts (token, ok, error_code, error_msg, client_info, owner_id, tenant_id) VALUES (p_token, false, v_err_code, v_err_msg, v_clean_info, v_owner_id, v_tenant_id); EXCEPTION WHEN OTHERS THEN NULL; END; + RAISE EXCEPTION '%', v_err_msg; + END IF; + IF v_email_alt !~ '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$' THEN + v_err_code := 'VALIDATION'; v_err_msg := 'E-mail alternativo inválido'; + BEGIN INSERT INTO public.patient_invite_attempts (token, ok, error_code, error_msg, client_info, owner_id, tenant_id) VALUES (p_token, false, v_err_code, v_err_msg, v_clean_info, v_owner_id, v_tenant_id); EXCEPTION WHEN OTHERS THEN NULL; END; + RAISE EXCEPTION '%', v_err_msg; + END IF; + END IF; + + v_consent := coalesce((p_payload->>'consent')::boolean, false); + IF v_consent IS NOT TRUE THEN + v_err_code := 'CONSENT_REQUIRED'; v_err_msg := 'Consentimento é obrigatório'; + BEGIN INSERT INTO public.patient_invite_attempts (token, ok, error_code, error_msg, client_info, owner_id, tenant_id) VALUES (p_token, false, v_err_code, v_err_msg, v_clean_info, v_owner_id, v_tenant_id); EXCEPTION WHEN OTHERS THEN NULL; END; + RAISE EXCEPTION '%', v_err_msg; + 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; + IF v_birth IS NOT NULL AND (v_birth > current_date OR v_birth < '1900-01-01'::date) THEN + v_birth := NULL; + END IF; + + v_genero := nullif(trim(p_payload->>'genero'), ''); + IF v_genero IS NOT NULL AND NOT (v_genero = ANY(c_generos)) THEN + v_genero := NULL; + END IF; + + v_estado_civil := nullif(trim(p_payload->>'estado_civil'), ''); + IF v_estado_civil IS NOT NULL AND NOT (v_estado_civil = ANY(c_estados_civis)) THEN + v_estado_civil := NULL; + END IF; + + -- ─────────────────────────────────────────────────────────────────────── + -- INSERT + -- ─────────────────────────────────────────────────────────────────────── + INSERT INTO public.patient_intake_requests ( + owner_id, tenant_id, token, status, consent, + nome_completo, email_principal, email_alternativo, telefone, telefone_alternativo, + avatar_url, + data_nascimento, cpf, rg, genero, estado_civil, + profissao, escolaridade, nacionalidade, naturalidade, + cep, pais, cidade, estado, endereco, numero, complemento, bairro, + observacoes, encaminhado_por, onde_nos_conheceu + ) + VALUES ( + v_owner_id, v_tenant_id, p_token, 'new', v_consent, + v_nome, v_email, v_email_alt, + nullif(regexp_replace(coalesce(p_payload->>'telefone',''), '\D', '', 'g'), ''), + nullif(regexp_replace(coalesce(p_payload->>'telefone_alternativo',''), '\D', '', 'g'), ''), + left(nullif(trim(p_payload->>'avatar_url'), ''), 500), + v_birth, + nullif(regexp_replace(coalesce(p_payload->>'cpf',''), '\D', '', 'g'), ''), + left(nullif(trim(p_payload->>'rg'), ''), 20), + v_genero, v_estado_civil, + left(nullif(trim(p_payload->>'profissao'), ''), 120), + left(nullif(trim(p_payload->>'escolaridade'), ''), 120), + left(nullif(trim(p_payload->>'nacionalidade'), ''), 80), + left(nullif(trim(p_payload->>'naturalidade'), ''), 120), + nullif(regexp_replace(coalesce(p_payload->>'cep',''), '\D', '', 'g'), ''), + left(nullif(trim(p_payload->>'pais'), ''), 60), + left(nullif(trim(p_payload->>'cidade'), ''), 120), + left(nullif(trim(p_payload->>'estado'), ''), 2), + left(nullif(trim(p_payload->>'endereco'), ''), 200), + left(nullif(trim(p_payload->>'numero'), ''), 20), + left(nullif(trim(p_payload->>'complemento'), ''), 120), + left(nullif(trim(p_payload->>'bairro'), ''), 120), + left(nullif(trim(p_payload->>'observacoes'), ''), 2000), + left(nullif(trim(p_payload->>'encaminhado_por'), ''), 120), + left(nullif(trim(p_payload->>'onde_nos_conheceu'), ''), 80) + ) + RETURNING id INTO v_intake_id; + + UPDATE public.patient_invites + SET uses = uses + 1 + WHERE token = p_token; + + -- Log de sucesso (best-effort, não propaga erro) + BEGIN + INSERT INTO public.patient_invite_attempts (token, ok, client_info, owner_id, tenant_id) + VALUES (p_token, true, v_clean_info, v_owner_id, v_tenant_id); + EXCEPTION WHEN OTHERS THEN NULL; END; + + RETURN v_intake_id; +END; +$function$; + +COMMENT ON FUNCTION public.create_patient_intake_request_v2(text, jsonb, text) IS +'Hardened 2026-04-18: valida active/expires/max_uses + incrementa uses; sanitiza todos os campos (trim, length, regex); resolve tenant_id; rejeita notas_internas; exige consent=true; registra cada tentativa em patient_invite_attempts (A#24).'; diff --git a/database-novo/migrations/20260418000004_dev_tests.sql b/database-novo/migrations/20260418000004_dev_tests.sql new file mode 100644 index 0000000..59966db --- /dev/null +++ b/database-novo/migrations/20260418000004_dev_tests.sql @@ -0,0 +1,149 @@ +-- ============================================================================= +-- Migration: 20260418000004_dev_tests +-- Nova aba "Testes" em /saas/desenvolvimento — catálogo de suítes de teste. +-- ----------------------------------------------------------------------------- +-- Espelha a estrutura de dev_verificacoes_items. Uma linha = uma suíte de +-- teste (arquivo .spec.js ou grupo de testes). Serve para responder "quais +-- áreas estão cobertas por teste?" sem rodar npm test. +-- ============================================================================= + +CREATE TABLE IF NOT EXISTS public.dev_test_items ( + id BIGSERIAL PRIMARY KEY, + area VARCHAR(80) NOT NULL, + categoria VARCHAR(120), -- unit, integration, e2e, manual + titulo TEXT NOT NULL, + arquivo TEXT, + descricao TEXT, + total_tests INTEGER DEFAULT 0, + passing INTEGER DEFAULT 0, + failing INTEGER DEFAULT 0, + skipped INTEGER DEFAULT 0, + cobertura_pct NUMERIC(5,2), -- cobertura estimada daquela área + status VARCHAR(20) NOT NULL DEFAULT 'ok' + CHECK (status IN ('ok','falhando','pendente','obsoleto','a_escrever')), + last_run_at TIMESTAMPTZ, + sessao_criacao VARCHAR(160), + notas TEXT, + tags TEXT[] DEFAULT '{}', + ordem INTEGER NOT NULL DEFAULT 0, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE INDEX IF NOT EXISTS idx_dev_test_items_area ON public.dev_test_items(area); +CREATE INDEX IF NOT EXISTS idx_dev_test_items_status ON public.dev_test_items(status); +CREATE INDEX IF NOT EXISTS idx_dev_test_items_ordem ON public.dev_test_items(area, ordem); + +DROP TRIGGER IF EXISTS trg_dev_test_items_updated_at ON public.dev_test_items; +CREATE TRIGGER trg_dev_test_items_updated_at + BEFORE UPDATE ON public.dev_test_items + FOR EACH ROW EXECUTE FUNCTION public.dev_set_updated_at(); + +ALTER TABLE public.dev_test_items ENABLE ROW LEVEL SECURITY; +DROP POLICY IF EXISTS dev_test_items_saas_admin_all ON public.dev_test_items; +CREATE POLICY dev_test_items_saas_admin_all ON public.dev_test_items + FOR ALL TO authenticated + USING (public.is_saas_admin()) + WITH CHECK (public.is_saas_admin()); + +COMMENT ON TABLE public.dev_test_items IS +'Catálogo de suítes de teste por área. Responde "o que está testado?" sem precisar rodar npm test.'; + + +-- ============================================================================= +-- Seed inicial — testes existentes em 2026-04-18 +-- ============================================================================= +INSERT INTO public.dev_test_items +(area, categoria, titulo, arquivo, descricao, total_tests, passing, failing, skipped, cobertura_pct, status, last_run_at, sessao_criacao, notas, tags, ordem) +VALUES +('agenda', 'unit', + 'useRecurrence — geração de ocorrências', + 'src/features/agenda/composables/__tests__/useRecurrence.spec.js', + $$Cobre: generateDates (weekly, biweekly, custom_weekdays, monthly, yearly), expandRules com exceções (cancel_session, patient_missed, reschedule_session, holiday_block), mergeWithStoredSessions, max_occurrences, range boundaries, remarcação inbound.$$, + 23, 23, 0, 0, NULL, + 'ok', '2026-04-18 08:47:00+00', 'Sessão 2 — agenda', + 'Suite sólida. Cobre os branches críticos da expansão de recorrência. Testes sobreviveram à adição do cap de range (V#20) e ao filtro de tenant_id nas CRUDs (V#12).', + ARRAY['unit','agenda','recurrence','critical'], 1), + +('agenda', 'unit', + 'agendaMappers — transformação pra FullCalendar', + 'src/features/agenda/services/__tests__/agendaMappers.spec.js', + $$Cobre: mapAgendaEventosToCalendarEvents (shape, campos extras), status → cor + ícone (agendado, realizado, faltou, cancelado, remarcado), aliases de FK (patients, determined_commitments), tipo fallback, ocorrência virtual (is_occurrence), resource events (clinic mosaic).$$, + 40, 40, 0, 0, NULL, + 'ok', '2026-04-18 08:47:00+00', 'Sessão 2 — agenda', + 'Quatro testes estavam falhando antes do V#21 (status "remarcado" vs "remarcar" + cores faltou/cancelado invertidas). Agora 100%.', + ARRAY['unit','agenda','mappers'], 2), + +('auth', 'a_escrever', + 'guards.js — branches do router beforeEach', + 'src/router/__tests__/guards.spec.js (não existe)', + $$Deveria cobrir: rotas públicas liberadas, redirect pra /auth/login sem session, área /account sem tenant, saas_admin só em /saas, tenant lockdown, trocaTenantScope, matchesRoles com aliases, cache de globalRole, cache de saasAdmin.$$, + 0, 0, 0, 0, NULL, + 'a_escrever', NULL, 'Sessão 1 — auth/router', + 'guards.js tem ~650 linhas e só roda via navegação real. Sem teste unitário → mudanças no guard são de alto risco. Prioridade média para criar (mock do router + pinia).', + ARRAY['unit','auth','router','guard','missing'], 3), + +('auth', 'a_escrever', + 'session.js — hydrate e race conditions', + 'src/app/__tests__/session.spec.js (não existe)', + $$Deveria cobrir: initSession com/sem session, refreshSession que não dispara se refreshing, SIGNED_IN redundante ignorado, SIGNED_OUT zera state, TOKEN_REFRESHED não derruba cache, hydrate preserva user em erro.$$, + 0, 0, 0, 0, NULL, + 'a_escrever', NULL, 'Sessão 1 — auth/router', + 'Módulo tem histórico de race conditions (comentado no próprio arquivo). Teste unitário daria garantia contra regressão.', + ARRAY['unit','auth','session','race','missing'], 4), + +('stores', 'a_escrever', + 'tenantStore — singleflight + persist', + 'src/stores/__tests__/tenantStore.spec.js (não existe)', + $$Deveria cobrir: loadSessionAndTenant com Promise compartilhada (V#3), ensureLoaded sem setInterval, tenant salvo só se pertence ao user, normalizeTenantRole, reset, persistência em localStorage.$$, + 0, 0, 0, 0, NULL, + 'a_escrever', NULL, 'Sessão 1 — auth/router', + 'V#3 trocou polling por Promise singleflight — a correção não tem teste que proteja contra regressão.', + ARRAY['unit','store','tenant','missing'], 5), + +('utils', 'a_escrever', + 'roleNormalizer — saídas esperadas', + 'src/utils/__tests__/roleNormalizer.spec.js (não existe)', + $$Fácil de testar — função pura, sem IO. Cobre: tenant_admin+therapist→therapist, tenant_admin+clinic→clinic_admin, tenant_admin+supervisor→supervisor, tenant_admin sem kind→clinic_admin, clinic_admin→clinic_admin, pass-through.$$, + 0, 0, 0, 0, NULL, + 'a_escrever', NULL, 'Sessão 1 — auth/router', + 'Criado em V#4. É função pura — fácil de cobrir em 10min. Baixa prioridade técnica mas alto valor simbólico (garantir que os 2 consumidores — guards.js e tenantStore.js — concordam).', + ARRAY['unit','utils','trivial'], 6), + +('pacientes', 'a_escrever', + 'Cadastros externos — fluxo do paciente', + 'src/features/patients/__tests__/external-intake.spec.js (não existe)', + $$Deveria cobrir: validação client-side (token regex, email, consent), truncation em todos os campos, payload final, não envio de notas_internas, comportamento com token inválido.$$, + 0, 0, 0, 0, NULL, + 'a_escrever', NULL, 'Sessão 4 — Security Hardening', + 'Página pública é ponto crítico de segurança. Teste de regressão importante após A#17/A#18/A#21 — garantir que nenhum dos valores "perigosos" voltem a ser enviados.', + ARRAY['unit','pacientes','external','security-regression'], 7), + +('database', 'manual', + 'RPCs de intake — validação de inputs maliciosos', + 'database-novo/tests/test_patient_intake_security.sql (sugerido)', + $$Deveria cobrir: token inválido raise, token desativado raise (A#16), token expirado raise, max_uses raise, uses incrementa após sucesso, consent=false raise, payload com notas_internas é ignorado (A#17), tenant_id é preenchido (A#19), nome > 200 chars raise, email inválido raise, genero fora whitelist vira NULL, data_nascimento futura vira NULL.$$, + 0, 0, 0, 0, NULL, + 'a_escrever', NULL, 'Sessão 4 — Security Hardening', + 'Testes SQL diretos via psql. Importantes porque as validações estão dentro do RPC SECURITY DEFINER. Executar antes de cada deploy.', + ARRAY['manual','sql','security','rpc'], 8), + +('agenda', 'a_escrever', + 'useAgendaEvents — wrapper do repository', + 'src/features/agenda/composables/__tests__/useAgendaEvents.spec.js (não existe)', + $$Deveria cobrir: loadMyRange chama listMyAgendaEvents, estado loading/error transições, sem ownerId retorna cedo, rollback em erro.$$, + 0, 0, 0, 0, NULL, + 'a_escrever', NULL, 'Sessão 2 — agenda', + 'Após refactor V#14 o composable virou fino. Teste garante que continue fino.', + ARRAY['unit','agenda','composable','missing'], 9), + +('e2e', 'a_escrever', + 'Fluxo completo: terapeuta cria link → paciente preenche → terapeuta vê', + '(não existe)', + $$Deveria cobrir o happy path integrado: login terapeuta, gera link via issue_patient_invite, abre /cadastro/paciente em aba anônima, preenche, submit, terapeuta vê em /therapist/patients/recebidos.$$, + 0, 0, 0, 0, NULL, + 'a_escrever', NULL, 'Sessão 4 — Security Hardening', + 'Não há E2E hoje. Playwright ou Cypress valem? Decidir provider. Alta prioridade pra confiança em deploy.', + ARRAY['e2e','critical','missing','decisão-pendente'], 10); + +SELECT id, area, categoria, status, total_tests, passing FROM public.dev_test_items ORDER BY ordem; diff --git a/database-novo/migrations/20260418000005_saas_rls_emergency_fix.sql b/database-novo/migrations/20260418000005_saas_rls_emergency_fix.sql new file mode 100644 index 0000000..6d3c961 --- /dev/null +++ b/database-novo/migrations/20260418000005_saas_rls_emergency_fix.sql @@ -0,0 +1,167 @@ +-- ============================================================================= +-- Migration: 20260418000005_saas_rls_emergency_fix +-- Corrige A#30 (P0) — 7 tabelas SaaS estavam com RLS desabilitado + grants +-- totais pra anon/authenticated/service_role. Qualquer usuário anônimo +-- podia alterar/deletar dados críticos (tenant_features, plan_prices, +-- subscription_intents_personal/tenant, plan_public, ...). +-- +-- Estratégia: +-- 1. Habilitar RLS em todas as 7 tabelas +-- 2. REVOKE ALL de anon (nunca deveria ter tido) +-- 3. REVOKE ALL de authenticated (controle passa a ser via policy) +-- 4. Policies explícitas por caso de uso +-- ============================================================================= + +-- ───────────────────────────────────────────────────────────────────────── +-- 1. REVOKE grants inseguros +-- ----------------------------------------------------------------------------- +REVOKE ALL ON public.tenant_features FROM anon, authenticated; +REVOKE ALL ON public.plan_prices FROM anon, authenticated; +REVOKE ALL ON public.plan_public FROM anon, authenticated; +REVOKE ALL ON public.plan_public_bullets FROM anon, authenticated; +REVOKE ALL ON public.subscription_intents_personal FROM anon, authenticated; +REVOKE ALL ON public.subscription_intents_tenant FROM anon, authenticated; +REVOKE ALL ON public.tenant_feature_exceptions_log FROM anon, authenticated; + +-- Concede o mínimo necessário (controlado por RLS abaixo) +GRANT SELECT, INSERT, UPDATE, DELETE ON public.tenant_features TO authenticated; +GRANT SELECT, INSERT, UPDATE, DELETE ON public.plan_prices TO authenticated; +GRANT SELECT ON public.plan_public TO anon, authenticated; +GRANT INSERT, UPDATE, DELETE ON public.plan_public TO authenticated; +GRANT SELECT ON public.plan_public_bullets TO anon, authenticated; +GRANT INSERT, UPDATE, DELETE ON public.plan_public_bullets TO authenticated; +GRANT SELECT, INSERT, UPDATE, DELETE ON public.subscription_intents_personal TO authenticated; +GRANT SELECT, INSERT, UPDATE, DELETE ON public.subscription_intents_tenant TO authenticated; +GRANT SELECT ON public.tenant_feature_exceptions_log TO authenticated; + +-- ───────────────────────────────────────────────────────────────────────── +-- 2. HABILITAR RLS em todas +-- ----------------------------------------------------------------------------- +ALTER TABLE public.tenant_features ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.plan_prices ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.plan_public ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.plan_public_bullets ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.subscription_intents_personal ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.subscription_intents_tenant ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.tenant_feature_exceptions_log ENABLE ROW LEVEL SECURITY; + +-- ───────────────────────────────────────────────────────────────────────── +-- 3. POLICIES — tenant_features +-- ----------------------------------------------------------------------------- +-- SELECT: membros do tenant leem as features do próprio tenant. Saas admin lê tudo. +DROP POLICY IF EXISTS tenant_features_select ON public.tenant_features; +CREATE POLICY tenant_features_select ON public.tenant_features + FOR SELECT TO authenticated + USING ( + public.is_saas_admin() + OR tenant_id IN (SELECT tm.tenant_id FROM public.tenant_members tm WHERE tm.user_id = auth.uid() AND tm.status = 'active') + ); + +-- WRITE: apenas tenant_admin do próprio tenant OU saas_admin. +DROP POLICY IF EXISTS tenant_features_write ON public.tenant_features; +CREATE POLICY tenant_features_write ON public.tenant_features + FOR ALL TO authenticated + USING ( + public.is_saas_admin() + OR tenant_id IN ( + SELECT tm.tenant_id FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() + AND tm.status = 'active' + AND tm.role IN ('tenant_admin','admin') + ) + ) + WITH CHECK ( + public.is_saas_admin() + OR tenant_id IN ( + SELECT tm.tenant_id FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() + AND tm.status = 'active' + AND tm.role IN ('tenant_admin','admin') + ) + ); + +-- ───────────────────────────────────────────────────────────────────────── +-- 4. POLICIES — plan_prices (SaaS admin only pra escrita; authenticated lê) +-- ----------------------------------------------------------------------------- +DROP POLICY IF EXISTS plan_prices_read ON public.plan_prices; +CREATE POLICY plan_prices_read ON public.plan_prices + FOR SELECT TO authenticated + USING (true); -- preços são públicos pra usuários logados + +DROP POLICY IF EXISTS plan_prices_write ON public.plan_prices; +CREATE POLICY plan_prices_write ON public.plan_prices + FOR ALL TO authenticated + USING (public.is_saas_admin()) + WITH CHECK (public.is_saas_admin()); + +-- ───────────────────────────────────────────────────────────────────────── +-- 5. POLICIES — plan_public + plan_public_bullets (anon pode ler — landing page) +-- ----------------------------------------------------------------------------- +DROP POLICY IF EXISTS plan_public_read_anon ON public.plan_public; +CREATE POLICY plan_public_read_anon ON public.plan_public + FOR SELECT TO anon, authenticated + USING (true); + +DROP POLICY IF EXISTS plan_public_write ON public.plan_public; +CREATE POLICY plan_public_write ON public.plan_public + FOR ALL TO authenticated + USING (public.is_saas_admin()) + WITH CHECK (public.is_saas_admin()); + +DROP POLICY IF EXISTS plan_public_bullets_read_anon ON public.plan_public_bullets; +CREATE POLICY plan_public_bullets_read_anon ON public.plan_public_bullets + FOR SELECT TO anon, authenticated + USING (true); + +DROP POLICY IF EXISTS plan_public_bullets_write ON public.plan_public_bullets; +CREATE POLICY plan_public_bullets_write ON public.plan_public_bullets + FOR ALL TO authenticated + USING (public.is_saas_admin()) + WITH CHECK (public.is_saas_admin()); + +-- ───────────────────────────────────────────────────────────────────────── +-- 6. POLICIES — subscription_intents_personal + _tenant +-- ----------------------------------------------------------------------------- +-- Dono vê o próprio intent; saas admin vê tudo; owner cria/atualiza seus próprios. +DROP POLICY IF EXISTS subscription_intents_personal_owner ON public.subscription_intents_personal; +CREATE POLICY subscription_intents_personal_owner ON public.subscription_intents_personal + FOR ALL TO authenticated + USING (user_id = auth.uid() OR public.is_saas_admin()) + WITH CHECK (user_id = auth.uid() OR public.is_saas_admin()); + +DROP POLICY IF EXISTS subscription_intents_tenant_member ON public.subscription_intents_tenant; +CREATE POLICY subscription_intents_tenant_member ON public.subscription_intents_tenant + FOR ALL TO authenticated + USING ( + public.is_saas_admin() + OR tenant_id IN ( + SELECT tm.tenant_id FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() + AND tm.status = 'active' + AND tm.role IN ('tenant_admin','admin') + ) + ) + WITH CHECK ( + public.is_saas_admin() + OR tenant_id IN ( + SELECT tm.tenant_id FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() + AND tm.status = 'active' + AND tm.role IN ('tenant_admin','admin') + ) + ); + +-- ───────────────────────────────────────────────────────────────────────── +-- 7. POLICY — tenant_feature_exceptions_log (somente leitura) +-- ----------------------------------------------------------------------------- +-- Log de auditoria. Inserts vêm de triggers/funções server-side (SECURITY DEFINER). +DROP POLICY IF EXISTS tenant_feature_exceptions_log_read ON public.tenant_feature_exceptions_log; +CREATE POLICY tenant_feature_exceptions_log_read ON public.tenant_feature_exceptions_log + FOR SELECT TO authenticated + USING ( + public.is_saas_admin() + OR tenant_id IN (SELECT tm.tenant_id FROM public.tenant_members tm WHERE tm.user_id = auth.uid() AND tm.status = 'active') + ); + +COMMENT ON TABLE public.tenant_features IS +'Controle de features por tenant. RLS: member do tenant lê; tenant_admin ou saas_admin escreve. Antes da migration 20260418000005 estava com RLS off + GRANT ALL pra anon (A#30).'; diff --git a/database-novo/migrations/20260419000001_tenant_features_b2_governance.sql b/database-novo/migrations/20260419000001_tenant_features_b2_governance.sql new file mode 100644 index 0000000..dcb796a --- /dev/null +++ b/database-novo/migrations/20260419000001_tenant_features_b2_governance.sql @@ -0,0 +1,214 @@ +-- ============================================================================= +-- Migration: 20260419000001_tenant_features_b2_governance +-- Resolve V#34 (isEnabled opt-out por padrão) + V#41 (dupla fonte entitlements +-- vs tenant_features) — Opção B2 (plano + override com exceção comercial). +-- +-- Mudanças: +-- 1. Trigger tenant_features_guard_with_plan ganha bypass via session flag +-- (current_setting('app.allow_feature_exception')) — só RPC pode setar. +-- 2. Nova RPC set_tenant_feature_exception(tenant_id, feature_key, enabled, reason) +-- SECURITY DEFINER, com regras assimétricas: +-- - p_enabled=false → tenant_admin OU saas_admin (preferência) +-- - p_enabled=true AND plano permite → tenant_admin OU saas_admin +-- - p_enabled=true AND plano NÃO permite → SOMENTE saas_admin + reason obrigatório +-- Toda mudança grava em tenant_feature_exceptions_log. +-- 3. Policy tenant_features_write restringida a saas_admin (writes diretos). +-- Tenant_admin agora muda só via RPC. +-- ============================================================================= + +-- ───────────────────────────────────────────────────────────────────────── +-- 1. Trigger: bypass controlado por session flag +-- ----------------------------------------------------------------------------- +CREATE OR REPLACE FUNCTION public.tenant_features_guard_with_plan() +RETURNS trigger +LANGUAGE plpgsql +AS $$ +DECLARE + v_allowed boolean; + v_bypass text; +BEGIN + -- Só valida quando está habilitando + IF new.enabled IS DISTINCT FROM true THEN + RETURN new; + END IF; + + -- Bypass autorizado: setado pela RPC set_tenant_feature_exception + -- após validar que o caller é saas_admin com reason. + v_bypass := current_setting('app.allow_feature_exception', true); + IF v_bypass = 'true' THEN + RETURN new; + END IF; + + -- Permitido pelo plano do tenant? + SELECT EXISTS ( + SELECT 1 + FROM public.v_tenant_entitlements_full v + WHERE v.tenant_id = new.tenant_id + AND v.feature_key = new.feature_key + AND v.allowed = true + ) INTO v_allowed; + + IF NOT v_allowed THEN + RAISE EXCEPTION 'Feature % não permitida pelo plano atual do tenant %.', + new.feature_key, new.tenant_id + USING ERRCODE = 'P0001'; + END IF; + + RETURN new; +END; +$$; + +-- ───────────────────────────────────────────────────────────────────────── +-- 2. RPC set_tenant_feature_exception +-- (substitui versão anterior que retornava void; retorna jsonb agora) +-- ----------------------------------------------------------------------------- +DROP FUNCTION IF EXISTS public.set_tenant_feature_exception(uuid, text, boolean, text); + +CREATE OR REPLACE FUNCTION public.set_tenant_feature_exception( + p_tenant_id uuid, + p_feature_key text, + p_enabled boolean, + p_reason text DEFAULT NULL +) +RETURNS jsonb +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path TO 'public' +AS $function$ +DECLARE + v_caller uuid := auth.uid(); + v_is_saas boolean := public.is_saas_admin(); + v_is_tenant_adm boolean; + v_plan_allows boolean; + v_feature_key text; + v_reason text; + v_is_exception boolean; +BEGIN + -- ─────────────────────────────────────────────────────────────────────── + -- Sanitização (padrão V#31) + -- ─────────────────────────────────────────────────────────────────────── + IF v_caller IS NULL THEN + RAISE EXCEPTION 'Não autenticado' USING ERRCODE = '28000'; + END IF; + + IF p_tenant_id IS NULL THEN + RAISE EXCEPTION 'tenant_id obrigatório' USING ERRCODE = '22023'; + END IF; + + IF p_enabled IS NULL THEN + RAISE EXCEPTION 'enabled obrigatório' USING ERRCODE = '22023'; + END IF; + + v_feature_key := nullif(btrim(coalesce(p_feature_key, '')), ''); + IF v_feature_key IS NULL THEN + RAISE EXCEPTION 'feature_key obrigatório' USING ERRCODE = '22023'; + END IF; + IF length(v_feature_key) > 80 THEN + RAISE EXCEPTION 'feature_key inválido (>80)' USING ERRCODE = '22023'; + END IF; + IF v_feature_key !~ '^[a-z][a-z0-9_.]*$' THEN + RAISE EXCEPTION 'feature_key formato inválido' USING ERRCODE = '22023'; + END IF; + + v_reason := nullif(btrim(coalesce(p_reason, '')), ''); + IF v_reason IS NOT NULL AND length(v_reason) > 500 THEN + v_reason := substring(v_reason FROM 1 FOR 500); + END IF; + + IF NOT EXISTS (SELECT 1 FROM public.features WHERE key = v_feature_key) THEN + RAISE EXCEPTION 'feature_key desconhecida: %', v_feature_key USING ERRCODE = '22023'; + END IF; + + IF NOT EXISTS (SELECT 1 FROM public.tenants WHERE id = p_tenant_id) THEN + RAISE EXCEPTION 'tenant não encontrado' USING ERRCODE = '22023'; + END IF; + + -- ─────────────────────────────────────────────────────────────────────── + -- Plano permite essa feature? + -- ─────────────────────────────────────────────────────────────────────── + SELECT EXISTS ( + SELECT 1 + FROM public.v_tenant_entitlements vte + WHERE vte.tenant_id = p_tenant_id + AND vte.feature_key = v_feature_key + ) INTO v_plan_allows; + + v_is_exception := (p_enabled = true AND NOT v_plan_allows); + + -- ─────────────────────────────────────────────────────────────────────── + -- Caller é tenant_admin desse tenant? + -- ─────────────────────────────────────────────────────────────────────── + v_is_tenant_adm := EXISTS ( + SELECT 1 FROM public.tenant_members tm + WHERE tm.tenant_id = p_tenant_id + AND tm.user_id = v_caller + AND tm.status = 'active' + AND tm.role IN ('tenant_admin','admin','owner') + ); + + -- ─────────────────────────────────────────────────────────────────────── + -- Autorização (assimétrica — V#34 Opção B2) + -- ─────────────────────────────────────────────────────────────────────── + IF v_is_exception THEN + -- Override positivo fora do plano = exceção comercial + IF NOT v_is_saas THEN + RAISE EXCEPTION 'Apenas saas_admin pode liberar feature fora do plano' USING ERRCODE = '42501'; + END IF; + IF v_reason IS NULL THEN + RAISE EXCEPTION 'reason obrigatório para exceção comercial' USING ERRCODE = '22023'; + END IF; + ELSE + -- Demais casos: tenant_admin OR saas_admin + IF NOT (v_is_saas OR v_is_tenant_adm) THEN + RAISE EXCEPTION 'Sem permissão para alterar features deste tenant' USING ERRCODE = '42501'; + END IF; + END IF; + + -- ─────────────────────────────────────────────────────────────────────── + -- Persistência: bypass controlado do trigger guard quando é exceção + -- (escopo de transação via SET LOCAL — só esta RPC vê) + -- ─────────────────────────────────────────────────────────────────────── + IF v_is_exception THEN + PERFORM set_config('app.allow_feature_exception', 'true', true); + END IF; + + INSERT INTO public.tenant_features (tenant_id, feature_key, enabled, updated_at) + VALUES (p_tenant_id, v_feature_key, p_enabled, now()) + ON CONFLICT (tenant_id, feature_key) + DO UPDATE SET enabled = EXCLUDED.enabled, updated_at = now(); + + -- Restaura flag (defensivo — SET LOCAL já é por transação, mas explicito) + IF v_is_exception THEN + PERFORM set_config('app.allow_feature_exception', 'false', true); + END IF; + + INSERT INTO public.tenant_feature_exceptions_log + (tenant_id, feature_key, enabled, reason, created_by) + VALUES + (p_tenant_id, v_feature_key, p_enabled, v_reason, v_caller); + + RETURN jsonb_build_object( + 'tenant_id', p_tenant_id, + 'feature_key', v_feature_key, + 'enabled', p_enabled, + 'plan_allows', v_plan_allows, + 'is_exception', v_is_exception, + 'reason', v_reason + ); +END; +$function$; + +REVOKE ALL ON FUNCTION public.set_tenant_feature_exception(uuid, text, boolean, text) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION public.set_tenant_feature_exception(uuid, text, boolean, text) TO authenticated; + +-- ───────────────────────────────────────────────────────────────────────── +-- 3. Policy: writes diretos só via saas_admin +-- (tenant_admin agora muda só via RPC set_tenant_feature_exception) +-- ----------------------------------------------------------------------------- +DROP POLICY IF EXISTS tenant_features_write ON public.tenant_features; +DROP POLICY IF EXISTS tenant_features_write_saas_only ON public.tenant_features; + +CREATE POLICY tenant_features_write_saas_only ON public.tenant_features + FOR ALL TO authenticated + USING (public.is_saas_admin()) + WITH CHECK (public.is_saas_admin()); diff --git a/database-novo/migrations/20260419000002_features_is_active.sql b/database-novo/migrations/20260419000002_features_is_active.sql new file mode 100644 index 0000000..c999421 --- /dev/null +++ b/database-novo/migrations/20260419000002_features_is_active.sql @@ -0,0 +1,21 @@ +-- ============================================================================= +-- Migration: 20260419000002_features_is_active +-- V#40 — features hard-deleted: adiciona is_active para soft-delete. +-- +-- Estratégia conservadora: +-- - features.is_active boolean DEFAULT true NOT NULL +-- - SaasFeaturesPage substitui DELETE por UPDATE is_active=false +-- - Views que expõem features para o app (v_tenant_entitlements etc) NÃO são +-- alteradas: features depreciadas ainda servem tenants legados via plan_features +-- enquanto não houver migração explícita +-- - Permite reativar feature acidentalmente deprecada +-- ============================================================================= + +ALTER TABLE public.features + ADD COLUMN IF NOT EXISTS is_active boolean NOT NULL DEFAULT true; + +CREATE INDEX IF NOT EXISTS idx_features_is_active + ON public.features (is_active) WHERE is_active = false; + +COMMENT ON COLUMN public.features.is_active IS + 'V#40: false = feature depreciada, escondida no catálogo SaaS mas continua válida em planos/tenants existentes.'; diff --git a/database-novo/migrations/20260419000003_delete_plan_safe.sql b/database-novo/migrations/20260419000003_delete_plan_safe.sql new file mode 100644 index 0000000..7cdbed8 --- /dev/null +++ b/database-novo/migrations/20260419000003_delete_plan_safe.sql @@ -0,0 +1,69 @@ +-- ============================================================================= +-- Migration: 20260419000003_delete_plan_safe +-- V#36 — DELETE de plans sem checagem de assinaturas ativas pode quebrar tenants. +-- +-- Cria RPC delete_plan_safe(plan_id) que: +-- - Valida saas_admin +-- - Conta subscriptions ativas (status='active') no plano +-- - Se houver, RAISE EXCEPTION descritivo com a contagem +-- - Se OK, desativa prices ativos e deleta o plano (atomic) +-- ============================================================================= + +CREATE OR REPLACE FUNCTION public.delete_plan_safe( + p_plan_id uuid +) +RETURNS jsonb +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path TO 'public' +AS $function$ +DECLARE + v_active_count int; + v_plan_key text; +BEGIN + IF auth.uid() IS NULL THEN + RAISE EXCEPTION 'Não autenticado' USING ERRCODE = '28000'; + END IF; + + IF NOT public.is_saas_admin() THEN + RAISE EXCEPTION 'Apenas saas_admin pode deletar planos' USING ERRCODE = '42501'; + END IF; + + IF p_plan_id IS NULL THEN + RAISE EXCEPTION 'plan_id obrigatório' USING ERRCODE = '22023'; + END IF; + + SELECT key INTO v_plan_key FROM public.plans WHERE id = p_plan_id; + IF v_plan_key IS NULL THEN + RAISE EXCEPTION 'plano não encontrado' USING ERRCODE = '22023'; + END IF; + + SELECT COUNT(*) INTO v_active_count + FROM public.subscriptions + WHERE plan_id = p_plan_id + AND status = 'active'; + + IF v_active_count > 0 THEN + RAISE EXCEPTION 'Plano % tem % assinatura(s) ativa(s); migre os tenants antes de deletar.', + v_plan_key, v_active_count + USING ERRCODE = 'P0001'; + END IF; + + -- desativa preços ativos antes de deletar + UPDATE public.plan_prices + SET is_active = false, + active_to = now() + WHERE plan_id = p_plan_id + AND is_active = true; + + DELETE FROM public.plans WHERE id = p_plan_id; + + RETURN jsonb_build_object( + 'deleted', true, + 'plan_key', v_plan_key + ); +END; +$function$; + +REVOKE ALL ON FUNCTION public.delete_plan_safe(uuid) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION public.delete_plan_safe(uuid) TO authenticated; diff --git a/database-novo/migrations/20260419000004_consolidate_policies.sql b/database-novo/migrations/20260419000004_consolidate_policies.sql new file mode 100644 index 0000000..d277bf8 --- /dev/null +++ b/database-novo/migrations/20260419000004_consolidate_policies.sql @@ -0,0 +1,46 @@ +-- ============================================================================= +-- Migration: 20260419000004_consolidate_policies +-- V#35 — Consolida policies duplicadas em plans, features, plan_features e +-- subscriptions. Remove legado redundante e documenta as que ficam. +-- +-- Análise (auditada via pg_policies): +-- • plans/features/plan_features: cada uma tem "read * (auth)" duplicado +-- com "*_read_authenticated" (mesmo USING true). Removidos os legados. +-- • subscriptions: +-- - "subscriptions read own" (USING user_id = auth.uid()) é SUBSET de +-- "subscriptions_read_own" (USING user_id = auth.uid() OR is_saas_admin()) +-- - "subscriptions_select_own_personal" (user_id = auth.uid() AND tenant_id IS NULL) +-- é SUBSET de "subscriptions_read_own" +-- - "subscriptions_no_direct_update" (USING false) é no-op em OR com +-- "subscriptions_update_only_saas_admin" +-- Removidas as 3 redundâncias. +-- ============================================================================= + +-- ───────────────────────────────────────────────────────────────────────── +-- Drops dos legados redundantes +-- ----------------------------------------------------------------------------- +DROP POLICY IF EXISTS "read plans (auth)" ON public.plans; +DROP POLICY IF EXISTS "read features (auth)" ON public.features; +DROP POLICY IF EXISTS "read plan_features (auth)" ON public.plan_features; + +DROP POLICY IF EXISTS "subscriptions read own" ON public.subscriptions; +DROP POLICY IF EXISTS "subscriptions_select_own_personal" ON public.subscriptions; +DROP POLICY IF EXISTS "subscriptions_no_direct_update" ON public.subscriptions; + +-- ───────────────────────────────────────────────────────────────────────── +-- COMMENT ON POLICY — documenta escopo das que ficaram +-- ----------------------------------------------------------------------------- +COMMENT ON POLICY plans_read_authenticated ON public.plans IS 'Qualquer usuário autenticado lê o catálogo de planos (vitrine, upgrade UI).'; +COMMENT ON POLICY plans_write_saas_admin ON public.plans IS 'Somente saas_admin escreve. DELETE deve ser via RPC delete_plan_safe (V#36).'; + +COMMENT ON POLICY features_read_authenticated ON public.features IS 'Qualquer logado lê o catálogo de features.'; +COMMENT ON POLICY features_write_saas_admin ON public.features IS 'Somente saas_admin escreve. DELETE = soft delete via is_active=false (V#40).'; + +COMMENT ON POLICY plan_features_read_authenticated ON public.plan_features IS 'Qualquer logado lê o vínculo plano↔feature (necessário para entitlements).'; +COMMENT ON POLICY plan_features_write_saas_admin ON public.plan_features IS 'Somente saas_admin escreve.'; + +COMMENT ON POLICY subscriptions_read_own ON public.subscriptions IS 'Dono da assinatura (user_id) ou saas_admin. Cobre o caso pessoal.'; +COMMENT ON POLICY subscriptions_select_for_tenant_members ON public.subscriptions IS 'Membros ativos do tenant leem assinaturas do tenant.'; +COMMENT ON POLICY "subscriptions: read if linked owner_users" ON public.subscriptions IS 'Caso especial: usuários ligados ao owner via owner_users (terapeutas de uma clínica que precisam ver a assinatura do owner).'; +COMMENT ON POLICY subscriptions_insert_own_personal ON public.subscriptions IS 'Usuário cria a própria assinatura pessoal (intent → conversion).'; +COMMENT ON POLICY subscriptions_update_only_saas_admin ON public.subscriptions IS 'UPDATE direto somente via saas_admin. Mudanças de tenant devem passar por RPC dedicada.'; diff --git a/database-novo/migrations/20260419000005_restrict_intake_rpc.sql b/database-novo/migrations/20260419000005_restrict_intake_rpc.sql new file mode 100644 index 0000000..085e63c --- /dev/null +++ b/database-novo/migrations/20260419000005_restrict_intake_rpc.sql @@ -0,0 +1,29 @@ +-- ============================================================================= +-- Migration: 20260419000005_restrict_intake_rpc +-- A#20 — Restringe create_patient_intake_request_v2 a service_role. +-- +-- Antes: anon (e PUBLIC) podia chamar direto. Bot bypassava qualquer +-- proteção do front (Turnstile etc). +-- Agora: edge function `submit-patient-intake` valida CAPTCHA e chama +-- a RPC com service_role. Anon não chama mais a RPC direto. +-- ============================================================================= + +-- Revoga PUBLIC (DEFAULT) e anon +REVOKE EXECUTE ON FUNCTION public.create_patient_intake_request_v2(text, jsonb) FROM PUBLIC, anon; +REVOKE EXECUTE ON FUNCTION public.create_patient_intake_request_v2(text, jsonb, text) FROM PUBLIC, anon; + +-- Mantém grants explícitos pra authenticated (uso interno futuro) e service_role (edge function) +GRANT EXECUTE ON FUNCTION public.create_patient_intake_request_v2(text, jsonb) TO authenticated, service_role; +GRANT EXECUTE ON FUNCTION public.create_patient_intake_request_v2(text, jsonb, text) TO authenticated, service_role; + +-- Mesma proteção para RPC v1 legada (caso ainda exista) +DO $$ +BEGIN + IF EXISTS ( + SELECT 1 FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace + WHERE n.nspname = 'public' AND p.proname = 'create_patient_intake_request' + ) THEN + EXECUTE 'REVOKE EXECUTE ON FUNCTION public.create_patient_intake_request(text, text, text, text, text, boolean) FROM PUBLIC, anon'; + EXECUTE 'GRANT EXECUTE ON FUNCTION public.create_patient_intake_request(text, text, text, text, text, boolean) TO authenticated, service_role'; + END IF; +END$$; diff --git a/database-novo/migrations/20260419000006_layered_bot_defense.sql b/database-novo/migrations/20260419000006_layered_bot_defense.sql new file mode 100644 index 0000000..ff14847 --- /dev/null +++ b/database-novo/migrations/20260419000006_layered_bot_defense.sql @@ -0,0 +1,136 @@ +-- ============================================================================= +-- Migration: 20260419000006_layered_bot_defense +-- A#20 (rev2) — Defesa em camadas self-hosted (substitui Turnstile). +-- +-- Camadas: +-- 1. Honeypot field (no front) → invisível, sempre ativo +-- 2. Rate limit por IP no edge → submission_rate_limits +-- 3. Math captcha CONDICIONAL → só se IP teve N falhas recentes +-- 4. Logging em public_submission_attempts (genérico, não só intake) +-- 5. Modo paranoid global → saas_security_config.captcha_required +-- +-- Substitui chamadas Turnstile na edge function submit-patient-intake. +-- ============================================================================= + +-- ───────────────────────────────────────────────────────────────────────── +-- 1. saas_security_config (singleton) +-- ----------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS public.saas_security_config ( + id boolean PRIMARY KEY DEFAULT true, + honeypot_enabled boolean NOT NULL DEFAULT true, + rate_limit_enabled boolean NOT NULL DEFAULT true, + rate_limit_window_min integer NOT NULL DEFAULT 10, + rate_limit_max_attempts integer NOT NULL DEFAULT 5, + captcha_after_failures integer NOT NULL DEFAULT 3, + captcha_required_globally boolean NOT NULL DEFAULT false, + block_duration_min integer NOT NULL DEFAULT 30, + captcha_required_window_min integer NOT NULL DEFAULT 60, + updated_at timestamptz NOT NULL DEFAULT now(), + updated_by uuid, + CONSTRAINT saas_security_config_singleton CHECK (id = true) +); + +INSERT INTO public.saas_security_config (id) VALUES (true) +ON CONFLICT (id) DO NOTHING; + +ALTER TABLE public.saas_security_config ENABLE ROW LEVEL SECURITY; + +REVOKE ALL ON public.saas_security_config FROM anon, authenticated; +GRANT SELECT, UPDATE ON public.saas_security_config TO authenticated; + +DROP POLICY IF EXISTS saas_security_config_read ON public.saas_security_config; +CREATE POLICY saas_security_config_read ON public.saas_security_config + FOR SELECT TO authenticated + USING (true); -- qualquer logado pode ler config global (não tem segredo) + +DROP POLICY IF EXISTS saas_security_config_write ON public.saas_security_config; +CREATE POLICY saas_security_config_write ON public.saas_security_config + FOR UPDATE TO authenticated + USING (public.is_saas_admin()) + WITH CHECK (public.is_saas_admin()); + +COMMENT ON TABLE public.saas_security_config IS 'Singleton: configuração global de defesa contra bots em endpoints públicos.'; + +-- ───────────────────────────────────────────────────────────────────────── +-- 2. public_submission_attempts (log genérico) +-- ----------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS public.public_submission_attempts ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + endpoint text NOT NULL, + ip_hash text, + success boolean NOT NULL, + error_code text, + error_msg text, + blocked_by text, -- 'honeypot' | 'rate_limit' | 'captcha' | 'rpc' | null + user_agent text, + metadata jsonb, + created_at timestamptz NOT NULL DEFAULT now() +); + +CREATE INDEX IF NOT EXISTS idx_psa_endpoint_created ON public.public_submission_attempts (endpoint, created_at DESC); +CREATE INDEX IF NOT EXISTS idx_psa_ip_hash_created ON public.public_submission_attempts (ip_hash, created_at DESC) WHERE ip_hash IS NOT NULL; +CREATE INDEX IF NOT EXISTS idx_psa_failed ON public.public_submission_attempts (created_at DESC) WHERE success = false; + +ALTER TABLE public.public_submission_attempts ENABLE ROW LEVEL SECURITY; + +REVOKE ALL ON public.public_submission_attempts FROM anon, authenticated; +GRANT SELECT ON public.public_submission_attempts TO authenticated; + +DROP POLICY IF EXISTS psa_read_saas_admin ON public.public_submission_attempts; +CREATE POLICY psa_read_saas_admin ON public.public_submission_attempts + FOR SELECT TO authenticated + USING (public.is_saas_admin()); + +COMMENT ON TABLE public.public_submission_attempts IS 'Log de tentativas em endpoints públicos (intake, signup, agendador). Escrita apenas via RPC SECURITY DEFINER.'; + +-- ───────────────────────────────────────────────────────────────────────── +-- 3. submission_rate_limits (estado vigente por IP+endpoint) +-- ----------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS public.submission_rate_limits ( + ip_hash text NOT NULL, + endpoint text NOT NULL, + attempt_count integer NOT NULL DEFAULT 0, + fail_count integer NOT NULL DEFAULT 0, + window_start timestamptz NOT NULL DEFAULT now(), + blocked_until timestamptz, + requires_captcha_until timestamptz, + last_attempt_at timestamptz NOT NULL DEFAULT now(), + created_at timestamptz NOT NULL DEFAULT now(), + PRIMARY KEY (ip_hash, endpoint) +); + +CREATE INDEX IF NOT EXISTS idx_srl_blocked_until ON public.submission_rate_limits (blocked_until) WHERE blocked_until IS NOT NULL; +CREATE INDEX IF NOT EXISTS idx_srl_endpoint ON public.submission_rate_limits (endpoint, last_attempt_at DESC); + +ALTER TABLE public.submission_rate_limits ENABLE ROW LEVEL SECURITY; + +REVOKE ALL ON public.submission_rate_limits FROM anon, authenticated; +GRANT SELECT ON public.submission_rate_limits TO authenticated; + +DROP POLICY IF EXISTS srl_read_saas_admin ON public.submission_rate_limits; +CREATE POLICY srl_read_saas_admin ON public.submission_rate_limits + FOR SELECT TO authenticated + USING (public.is_saas_admin()); + +COMMENT ON TABLE public.submission_rate_limits IS 'Estado de rate limit por IP+endpoint. Escrita apenas via RPC. SaaS admin lê pra dashboard.'; + +-- ───────────────────────────────────────────────────────────────────────── +-- 4. math_challenges (TTL 5min, limpa via cron) +-- ----------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS public.math_challenges ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + question text NOT NULL, + answer integer NOT NULL, + used boolean NOT NULL DEFAULT false, + created_at timestamptz NOT NULL DEFAULT now(), + expires_at timestamptz NOT NULL DEFAULT (now() + interval '5 minutes') +); + +CREATE INDEX IF NOT EXISTS idx_mc_expires ON public.math_challenges (expires_at); + +ALTER TABLE public.math_challenges ENABLE ROW LEVEL SECURITY; + +REVOKE ALL ON public.math_challenges FROM anon, authenticated; +-- nenhum grant: tabela acessada apenas via RPC SECURITY DEFINER + +COMMENT ON TABLE public.math_challenges IS 'Challenges de math captcha. TTL 5min. Escrita/leitura apenas via RPC.'; diff --git a/database-novo/migrations/20260419000007_bot_defense_rpcs.sql b/database-novo/migrations/20260419000007_bot_defense_rpcs.sql new file mode 100644 index 0000000..ae8969c --- /dev/null +++ b/database-novo/migrations/20260419000007_bot_defense_rpcs.sql @@ -0,0 +1,299 @@ +-- ============================================================================= +-- Migration: 20260419000007_bot_defense_rpcs +-- A#20 (rev2) — RPCs da defesa em camadas: +-- • check_rate_limit — consulta + decide allowed/captcha/bloqueio +-- • record_submission_attempt — log + atualiza contadores e bloqueios +-- • generate_math_challenge — cria pergunta math, retorna {id, question} +-- • verify_math_challenge — valida {id, answer}, marca used +-- ============================================================================= + +-- ───────────────────────────────────────────────────────────────────────── +-- check_rate_limit +-- Lê config + estado atual, decide o que retornar. +-- Se fora da janela atual, "rolha" os contadores (reset). +-- ----------------------------------------------------------------------------- +CREATE OR REPLACE FUNCTION public.check_rate_limit( + p_ip_hash text, + p_endpoint text +) +RETURNS jsonb +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path TO 'public' +AS $function$ +DECLARE + cfg saas_security_config%ROWTYPE; + rl submission_rate_limits%ROWTYPE; + v_now timestamptz := now(); + v_window_start timestamptz; + v_in_window boolean; + v_requires_captcha boolean := false; + v_blocked_until timestamptz; + v_retry_after_seconds integer := 0; +BEGIN + SELECT * INTO cfg FROM saas_security_config WHERE id = true; + IF NOT FOUND THEN + -- Sem config: fail-open (libera). Logado. + RETURN jsonb_build_object('allowed', true, 'requires_captcha', false, 'reason', 'no_config'); + END IF; + + -- Modo paranoid global: sempre captcha + IF cfg.captcha_required_globally THEN + v_requires_captcha := true; + END IF; + + -- Sem rate limit ativo: libera (mas pode exigir captcha pelo paranoid) + IF NOT cfg.rate_limit_enabled THEN + RETURN jsonb_build_object( + 'allowed', true, + 'requires_captcha', v_requires_captcha, + 'reason', CASE WHEN v_requires_captcha THEN 'paranoid_global' ELSE 'rate_limit_disabled' END + ); + END IF; + + -- Sem ip_hash: libera (não dá pra rastrear) + IF p_ip_hash IS NULL OR length(btrim(p_ip_hash)) = 0 THEN + RETURN jsonb_build_object( + 'allowed', true, + 'requires_captcha', v_requires_captcha, + 'reason', 'no_ip' + ); + END IF; + + SELECT * INTO rl + FROM submission_rate_limits + WHERE ip_hash = p_ip_hash AND endpoint = p_endpoint; + + -- Bloqueio temporário ativo? + IF FOUND AND rl.blocked_until IS NOT NULL AND rl.blocked_until > v_now THEN + v_retry_after_seconds := EXTRACT(EPOCH FROM (rl.blocked_until - v_now))::int; + RETURN jsonb_build_object( + 'allowed', false, + 'requires_captcha', false, + 'retry_after_seconds', v_retry_after_seconds, + 'reason', 'blocked' + ); + END IF; + + -- Captcha condicional ativo? + IF FOUND AND rl.requires_captcha_until IS NOT NULL AND rl.requires_captcha_until > v_now THEN + v_requires_captcha := true; + END IF; + + -- Janela atual ainda válida? + v_window_start := v_now - (cfg.rate_limit_window_min || ' minutes')::interval; + v_in_window := FOUND AND rl.window_start >= v_window_start; + + IF v_in_window AND rl.attempt_count >= cfg.rate_limit_max_attempts THEN + -- Excedeu — bloqueia + v_blocked_until := v_now + (cfg.block_duration_min || ' minutes')::interval; + UPDATE submission_rate_limits + SET blocked_until = v_blocked_until, + last_attempt_at = v_now + WHERE ip_hash = p_ip_hash AND endpoint = p_endpoint; + + v_retry_after_seconds := EXTRACT(EPOCH FROM (v_blocked_until - v_now))::int; + RETURN jsonb_build_object( + 'allowed', false, + 'requires_captcha', false, + 'retry_after_seconds', v_retry_after_seconds, + 'reason', 'rate_limit_exceeded' + ); + END IF; + + RETURN jsonb_build_object( + 'allowed', true, + 'requires_captcha', v_requires_captcha, + 'reason', CASE WHEN v_requires_captcha THEN 'captcha_required' ELSE 'ok' END + ); +END; +$function$; + +REVOKE ALL ON FUNCTION public.check_rate_limit(text, text) FROM PUBLIC, anon, authenticated; +GRANT EXECUTE ON FUNCTION public.check_rate_limit(text, text) TO service_role; + +-- ───────────────────────────────────────────────────────────────────────── +-- record_submission_attempt +-- Loga em public_submission_attempts + atualiza submission_rate_limits. +-- Se !success: incrementa fail_count; se >= captcha_after_failures, marca +-- requires_captcha_until = now + captcha_required_window_min. +-- ----------------------------------------------------------------------------- +CREATE OR REPLACE FUNCTION public.record_submission_attempt( + p_endpoint text, + p_ip_hash text, + p_success boolean, + p_blocked_by text DEFAULT NULL, + p_error_code text DEFAULT NULL, + p_error_msg text DEFAULT NULL, + p_user_agent text DEFAULT NULL, + p_metadata jsonb DEFAULT NULL +) +RETURNS void +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path TO 'public' +AS $function$ +DECLARE + cfg saas_security_config%ROWTYPE; + v_now timestamptz := now(); + v_window_start timestamptz; + rl submission_rate_limits%ROWTYPE; +BEGIN + -- Log sempre (mesmo sem ip) + INSERT INTO public_submission_attempts + (endpoint, ip_hash, success, blocked_by, error_code, error_msg, user_agent, metadata) + VALUES + (p_endpoint, p_ip_hash, p_success, p_blocked_by, + left(coalesce(p_error_code, ''), 80), + left(coalesce(p_error_msg, ''), 500), + left(coalesce(p_user_agent, ''), 500), + p_metadata); + + -- Sem ip ou rate limit desligado: não atualiza contador + IF p_ip_hash IS NULL OR length(btrim(p_ip_hash)) = 0 THEN RETURN; END IF; + + SELECT * INTO cfg FROM saas_security_config WHERE id = true; + IF NOT FOUND OR NOT cfg.rate_limit_enabled THEN RETURN; END IF; + + v_window_start := v_now - (cfg.rate_limit_window_min || ' minutes')::interval; + + SELECT * INTO rl + FROM submission_rate_limits + WHERE ip_hash = p_ip_hash AND endpoint = p_endpoint; + + IF NOT FOUND THEN + INSERT INTO submission_rate_limits + (ip_hash, endpoint, attempt_count, fail_count, window_start, last_attempt_at) + VALUES + (p_ip_hash, p_endpoint, 1, CASE WHEN p_success THEN 0 ELSE 1 END, v_now, v_now); + ELSE + IF rl.window_start < v_window_start THEN + -- Reset janela + UPDATE submission_rate_limits + SET attempt_count = 1, + fail_count = CASE WHEN p_success THEN 0 ELSE 1 END, + window_start = v_now, + last_attempt_at = v_now, + blocked_until = NULL + WHERE ip_hash = p_ip_hash AND endpoint = p_endpoint; + ELSE + UPDATE submission_rate_limits + SET attempt_count = attempt_count + 1, + fail_count = fail_count + CASE WHEN p_success THEN 0 ELSE 1 END, + last_attempt_at = v_now + WHERE ip_hash = p_ip_hash AND endpoint = p_endpoint; + END IF; + + -- Se atingiu threshold de captcha condicional, marca + IF NOT p_success THEN + SELECT * INTO rl FROM submission_rate_limits WHERE ip_hash = p_ip_hash AND endpoint = p_endpoint; + IF rl.fail_count >= cfg.captcha_after_failures + AND (rl.requires_captcha_until IS NULL OR rl.requires_captcha_until < v_now) THEN + UPDATE submission_rate_limits + SET requires_captcha_until = v_now + (cfg.captcha_required_window_min || ' minutes')::interval + WHERE ip_hash = p_ip_hash AND endpoint = p_endpoint; + END IF; + END IF; + END IF; +END; +$function$; + +REVOKE ALL ON FUNCTION public.record_submission_attempt(text, text, boolean, text, text, text, text, jsonb) FROM PUBLIC, anon, authenticated; +GRANT EXECUTE ON FUNCTION public.record_submission_attempt(text, text, boolean, text, text, text, text, jsonb) TO service_role; + +-- ───────────────────────────────────────────────────────────────────────── +-- generate_math_challenge +-- Cria 2 inteiros 1..9 + operação. Retorna {id, question}. +-- Operações: + - * (resultado sempre positivo) +-- ----------------------------------------------------------------------------- +CREATE OR REPLACE FUNCTION public.generate_math_challenge() +RETURNS jsonb +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path TO 'public' +AS $function$ +DECLARE + v_a integer; + v_b integer; + v_op text; + v_ans integer; + v_q text; + v_id uuid; +BEGIN + v_a := 1 + floor(random() * 9)::int; + v_b := 1 + floor(random() * 9)::int; + v_op := (ARRAY['+','-','*'])[1 + floor(random() * 3)::int]; + + -- garantir resultado positivo na subtração + IF v_op = '-' AND v_b > v_a THEN + v_a := v_a + v_b; + END IF; + + v_ans := CASE v_op + WHEN '+' THEN v_a + v_b + WHEN '-' THEN v_a - v_b + WHEN '*' THEN v_a * v_b + END; + + v_q := format('Quanto é %s %s %s?', v_a, v_op, v_b); + + INSERT INTO math_challenges (question, answer) + VALUES (v_q, v_ans) + RETURNING id INTO v_id; + + RETURN jsonb_build_object('id', v_id, 'question', v_q); +END; +$function$; + +REVOKE ALL ON FUNCTION public.generate_math_challenge() FROM PUBLIC, anon, authenticated; +GRANT EXECUTE ON FUNCTION public.generate_math_challenge() TO service_role; + +-- ───────────────────────────────────────────────────────────────────────── +-- verify_math_challenge +-- Valida {id, answer}. Marca used. Bloqueia uso duplicado. +-- ----------------------------------------------------------------------------- +CREATE OR REPLACE FUNCTION public.verify_math_challenge( + p_id uuid, + p_answer integer +) +RETURNS boolean +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path TO 'public' +AS $function$ +DECLARE + mc math_challenges%ROWTYPE; +BEGIN + IF p_id IS NULL OR p_answer IS NULL THEN RETURN false; END IF; + + SELECT * INTO mc FROM math_challenges WHERE id = p_id; + IF NOT FOUND OR mc.used OR mc.expires_at < now() THEN + RETURN false; + END IF; + + UPDATE math_challenges SET used = true WHERE id = p_id; + + RETURN mc.answer = p_answer; +END; +$function$; + +REVOKE ALL ON FUNCTION public.verify_math_challenge(uuid, integer) FROM PUBLIC, anon, authenticated; +GRANT EXECUTE ON FUNCTION public.verify_math_challenge(uuid, integer) TO service_role; + +-- ───────────────────────────────────────────────────────────────────────── +-- cleanup_expired_math_challenges (chamável via cron) +-- ----------------------------------------------------------------------------- +CREATE OR REPLACE FUNCTION public.cleanup_expired_math_challenges() +RETURNS integer +LANGUAGE sql +SECURITY DEFINER +SET search_path TO 'public' +AS $function$ + WITH d AS ( + DELETE FROM math_challenges WHERE expires_at < now() - interval '1 hour' RETURNING 1 + ) + SELECT COUNT(*)::int FROM d; +$function$; + +REVOKE ALL ON FUNCTION public.cleanup_expired_math_challenges() FROM PUBLIC, anon, authenticated; +GRANT EXECUTE ON FUNCTION public.cleanup_expired_math_challenges() TO service_role; diff --git a/database-novo/migrations/20260419000008_saas_twilio_config.sql b/database-novo/migrations/20260419000008_saas_twilio_config.sql new file mode 100644 index 0000000..422363c --- /dev/null +++ b/database-novo/migrations/20260419000008_saas_twilio_config.sql @@ -0,0 +1,155 @@ +-- ============================================================================= +-- Migration: 20260419000008_saas_twilio_config +-- Permite saas_admin editar config Twilio operacional pelo painel, sem redeploy. +-- +-- DECISÃO DE SEGURANÇA: +-- • TWILIO_AUTH_TOKEN (secret) NÃO entra na tabela. Continua em env var +-- da Edge Function. Painel apenas exibe se está configurado (best-effort). +-- • TWILIO_ACCOUNT_SID (público no Twilio dashboard, identificador) → DB +-- • TWILIO_WHATSAPP_WEBHOOK (URL) → DB +-- • USD_BRL_RATE / MARGIN_MULTIPLIER (operacional) → DB +-- +-- Edge function: lê primeiro do banco; cai pra env vars como fallback se row +-- ainda não foi configurada (back-compat com deploys antigos). +-- ============================================================================= + +CREATE TABLE IF NOT EXISTS public.saas_twilio_config ( + id boolean PRIMARY KEY DEFAULT true, + account_sid text, + whatsapp_webhook_url text, + usd_brl_rate numeric(10,4) NOT NULL DEFAULT 5.5, + margin_multiplier numeric(10,4) NOT NULL DEFAULT 1.4, + notes text, + updated_at timestamptz NOT NULL DEFAULT now(), + updated_by uuid, + CONSTRAINT saas_twilio_config_singleton CHECK (id = true), + CONSTRAINT saas_twilio_config_rate_chk CHECK (usd_brl_rate > 0 AND usd_brl_rate < 100), + CONSTRAINT saas_twilio_config_mult_chk CHECK (margin_multiplier >= 1 AND margin_multiplier <= 10), + CONSTRAINT saas_twilio_config_sid_chk CHECK (account_sid IS NULL OR account_sid ~ '^AC[a-zA-Z0-9]{32}$'), + CONSTRAINT saas_twilio_config_url_chk CHECK (whatsapp_webhook_url IS NULL OR whatsapp_webhook_url ~ '^https?://') +); + +INSERT INTO public.saas_twilio_config (id) VALUES (true) +ON CONFLICT (id) DO NOTHING; + +ALTER TABLE public.saas_twilio_config ENABLE ROW LEVEL SECURITY; + +REVOKE ALL ON public.saas_twilio_config FROM anon, authenticated; +GRANT SELECT ON public.saas_twilio_config TO authenticated; + +DROP POLICY IF EXISTS saas_twilio_config_read ON public.saas_twilio_config; +CREATE POLICY saas_twilio_config_read ON public.saas_twilio_config + FOR SELECT TO authenticated + USING (public.is_saas_admin()); -- só admin vê config (mesmo sem secret, é dado operacional) + +COMMENT ON TABLE public.saas_twilio_config IS + 'Config operacional Twilio editável via painel. AUTH_TOKEN continua em env var por segurança.'; + +-- ───────────────────────────────────────────────────────────────────────── +-- RPC get_twilio_config — retorna config atual (saas_admin OU service_role) +-- ----------------------------------------------------------------------------- +CREATE OR REPLACE FUNCTION public.get_twilio_config() +RETURNS jsonb +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path TO 'public' +AS $function$ +DECLARE + cfg saas_twilio_config%ROWTYPE; +BEGIN + -- Permite quem é saas_admin (UI) ou quando chamado via service_role (edge function) + -- coalesce protege de NULL (auth.role() pode ser NULL fora de contexto JWT) + IF NOT (public.is_saas_admin() OR coalesce(auth.role(), '') = 'service_role') THEN + RAISE EXCEPTION 'Sem permissão' USING ERRCODE = '42501'; + END IF; + + SELECT * INTO cfg FROM saas_twilio_config WHERE id = true; + IF NOT FOUND THEN + RETURN jsonb_build_object( + 'account_sid', NULL, + 'whatsapp_webhook_url', NULL, + 'usd_brl_rate', 5.5, + 'margin_multiplier', 1.4 + ); + END IF; + + RETURN jsonb_build_object( + 'account_sid', cfg.account_sid, + 'whatsapp_webhook_url', cfg.whatsapp_webhook_url, + 'usd_brl_rate', cfg.usd_brl_rate, + 'margin_multiplier', cfg.margin_multiplier, + 'notes', cfg.notes, + 'updated_at', cfg.updated_at, + 'updated_by', cfg.updated_by + ); +END; +$function$; + +REVOKE ALL ON FUNCTION public.get_twilio_config() FROM PUBLIC, anon, authenticated; +GRANT EXECUTE ON FUNCTION public.get_twilio_config() TO authenticated, service_role; + +-- ───────────────────────────────────────────────────────────────────────── +-- RPC update_twilio_config — só saas_admin +-- ----------------------------------------------------------------------------- +CREATE OR REPLACE FUNCTION public.update_twilio_config( + p_account_sid text DEFAULT NULL, + p_whatsapp_webhook_url text DEFAULT NULL, + p_usd_brl_rate numeric DEFAULT NULL, + p_margin_multiplier numeric DEFAULT NULL, + p_notes text DEFAULT NULL +) +RETURNS jsonb +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path TO 'public' +AS $function$ +DECLARE + v_caller uuid := auth.uid(); + v_account_sid text; + v_webhook_url text; + v_notes text; +BEGIN + IF v_caller IS NULL THEN + RAISE EXCEPTION 'Não autenticado' USING ERRCODE = '28000'; + END IF; + IF NOT public.is_saas_admin() THEN + RAISE EXCEPTION 'Apenas saas_admin pode atualizar config Twilio' USING ERRCODE = '42501'; + END IF; + + -- Sanitização + v_account_sid := nullif(btrim(coalesce(p_account_sid, '')), ''); + v_webhook_url := nullif(btrim(coalesce(p_whatsapp_webhook_url, '')), ''); + v_notes := nullif(btrim(coalesce(p_notes, '')), ''); + + IF v_account_sid IS NOT NULL AND v_account_sid !~ '^AC[a-zA-Z0-9]{32}$' THEN + RAISE EXCEPTION 'account_sid inválido (esperado AC + 32 chars)' USING ERRCODE = '22023'; + END IF; + IF v_webhook_url IS NOT NULL AND v_webhook_url !~ '^https?://' THEN + RAISE EXCEPTION 'webhook_url deve começar com http(s)://' USING ERRCODE = '22023'; + END IF; + IF p_usd_brl_rate IS NOT NULL AND (p_usd_brl_rate <= 0 OR p_usd_brl_rate >= 100) THEN + RAISE EXCEPTION 'usd_brl_rate fora da faixa (0..100)' USING ERRCODE = '22023'; + END IF; + IF p_margin_multiplier IS NOT NULL AND (p_margin_multiplier < 1 OR p_margin_multiplier > 10) THEN + RAISE EXCEPTION 'margin_multiplier fora da faixa (1..10)' USING ERRCODE = '22023'; + END IF; + IF v_notes IS NOT NULL AND length(v_notes) > 1000 THEN + v_notes := substring(v_notes FROM 1 FOR 1000); + END IF; + + UPDATE saas_twilio_config + SET account_sid = COALESCE(v_account_sid, account_sid), + whatsapp_webhook_url = COALESCE(v_webhook_url, whatsapp_webhook_url), + usd_brl_rate = COALESCE(p_usd_brl_rate, usd_brl_rate), + margin_multiplier = COALESCE(p_margin_multiplier, margin_multiplier), + notes = COALESCE(v_notes, notes), + updated_at = now(), + updated_by = v_caller + WHERE id = true; + + RETURN public.get_twilio_config(); +END; +$function$; + +REVOKE ALL ON FUNCTION public.update_twilio_config(text, text, numeric, numeric, text) FROM PUBLIC, anon, authenticated; +GRANT EXECUTE ON FUNCTION public.update_twilio_config(text, text, numeric, numeric, text) TO authenticated; diff --git a/database-novo/migrations/20260419000009_patient_session_counts_rpc.sql b/database-novo/migrations/20260419000009_patient_session_counts_rpc.sql new file mode 100644 index 0000000..6783e76 --- /dev/null +++ b/database-novo/migrations/20260419000009_patient_session_counts_rpc.sql @@ -0,0 +1,34 @@ +-- ============================================================================= +-- Migration: 20260419000009_patient_session_counts_rpc +-- V#8 — Substitui o .limit(1000) arbitrário em PatientsListPage por RPC +-- agregada que retorna contagens por paciente (sempre atualizada, sem teto). +-- +-- Tenant scoping é feito via WHERE tenant_id IN (memberships do caller), +-- consistente com a policy SELECT de agenda_eventos. +-- ============================================================================= + +CREATE OR REPLACE FUNCTION public.get_patient_session_counts( + p_patient_ids uuid[] +) +RETURNS TABLE(patient_id uuid, session_count integer, last_session_at timestamptz) +LANGUAGE sql +SECURITY DEFINER +SET search_path TO 'public' +AS $function$ + SELECT + ae.patient_id, + COUNT(*)::int AS session_count, + MAX(ae.inicio_em) AS last_session_at + FROM public.agenda_eventos ae + WHERE ae.patient_id = ANY(p_patient_ids) + AND ae.tenant_id IN ( + SELECT tm.tenant_id + FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() + AND tm.status = 'active' + ) + GROUP BY ae.patient_id; +$function$; + +REVOKE ALL ON FUNCTION public.get_patient_session_counts(uuid[]) FROM PUBLIC, anon; +GRANT EXECUTE ON FUNCTION public.get_patient_session_counts(uuid[]) TO authenticated; diff --git a/database-novo/migrations/20260419000010_documents_security_hardening.sql b/database-novo/migrations/20260419000010_documents_security_hardening.sql new file mode 100644 index 0000000..0b8fc13 --- /dev/null +++ b/database-novo/migrations/20260419000010_documents_security_hardening.sql @@ -0,0 +1,304 @@ +-- ============================================================================= +-- Migration: 20260419000010_documents_security_hardening +-- Sessão 6 — revisão sênior de Documentos. Resolve V#43-V#49 (5 críticos/altos +-- + 2 médios). V#50-V#52 (portal-paciente, hash, retention) ficam pendentes +-- pra próxima sessão (precisam de design/decisão). +-- +-- Path convention dos buckets: "{tenant_id}/{patient_id}/{timestamp}-{file}" +-- (storage.foldername(name))[1] = tenant_id +-- ============================================================================= + +-- Tabelas de documents são owned por supabase_admin +SET LOCAL ROLE supabase_admin; + +-- ───────────────────────────────────────────────────────────────────────── +-- V#43 + V#44: storage.objects para buckets "documents" e "generated-docs" +-- ----------------------------------------------------------------------------- +DROP POLICY IF EXISTS "documents: authenticated read" ON storage.objects; +DROP POLICY IF EXISTS "documents: authenticated upload" ON storage.objects; +DROP POLICY IF EXISTS "documents: authenticated delete" ON storage.objects; +DROP POLICY IF EXISTS "documents: tenant member read" ON storage.objects; +DROP POLICY IF EXISTS "documents: tenant member upload" ON storage.objects; +DROP POLICY IF EXISTS "documents: tenant member delete" ON storage.objects; + +CREATE POLICY "documents: tenant member read" ON storage.objects + FOR SELECT TO authenticated + USING ( + bucket_id = 'documents' + AND ( + public.is_saas_admin() + OR + (storage.foldername(name))[1]::uuid IN ( + SELECT tm.tenant_id FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() AND tm.status = 'active' + ) + ) + ); + +CREATE POLICY "documents: tenant member upload" ON storage.objects + FOR INSERT TO authenticated + WITH CHECK ( + bucket_id = 'documents' + AND (storage.foldername(name))[1]::uuid IN ( + SELECT tm.tenant_id FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() AND tm.status = 'active' + ) + ); + +CREATE POLICY "documents: tenant member delete" ON storage.objects + FOR DELETE TO authenticated + USING ( + bucket_id = 'documents' + AND ( + public.is_saas_admin() + OR + (storage.foldername(name))[1]::uuid IN ( + SELECT tm.tenant_id FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() AND tm.status = 'active' + ) + ) + ); + +DROP POLICY IF EXISTS "generated-docs: authenticated read" ON storage.objects; +DROP POLICY IF EXISTS "generated-docs: authenticated upload" ON storage.objects; +DROP POLICY IF EXISTS "generated-docs: authenticated delete" ON storage.objects; +DROP POLICY IF EXISTS "generated-docs: tenant member read" ON storage.objects; +DROP POLICY IF EXISTS "generated-docs: tenant member upload" ON storage.objects; +DROP POLICY IF EXISTS "generated-docs: tenant member delete" ON storage.objects; + +CREATE POLICY "generated-docs: tenant member read" ON storage.objects + FOR SELECT TO authenticated + USING ( + bucket_id = 'generated-docs' + AND ( + public.is_saas_admin() + OR + (storage.foldername(name))[1]::uuid IN ( + SELECT tm.tenant_id FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() AND tm.status = 'active' + ) + ) + ); + +CREATE POLICY "generated-docs: tenant member upload" ON storage.objects + FOR INSERT TO authenticated + WITH CHECK ( + bucket_id = 'generated-docs' + AND (storage.foldername(name))[1]::uuid IN ( + SELECT tm.tenant_id FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() AND tm.status = 'active' + ) + ); + +CREATE POLICY "generated-docs: tenant member delete" ON storage.objects + FOR DELETE TO authenticated + USING ( + bucket_id = 'generated-docs' + AND ( + public.is_saas_admin() + OR + (storage.foldername(name))[1]::uuid IN ( + SELECT tm.tenant_id FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() AND tm.status = 'active' + ) + ) + ); + +-- ───────────────────────────────────────────────────────────────────────── +-- V#45: documents — policies separadas por cmd +-- ----------------------------------------------------------------------------- +DROP POLICY IF EXISTS "documents: owner full access" ON public.documents; +DROP POLICY IF EXISTS "documents: select" ON public.documents; +DROP POLICY IF EXISTS "documents: insert" ON public.documents; +DROP POLICY IF EXISTS "documents: update" ON public.documents; +DROP POLICY IF EXISTS "documents: delete" ON public.documents; + +-- SELECT: owner OR tenant_member ativo OR saas_admin +CREATE POLICY "documents: select" ON public.documents + FOR SELECT TO authenticated + USING ( + owner_id = auth.uid() + OR public.is_saas_admin() + OR tenant_id IN ( + SELECT tm.tenant_id FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() AND tm.status = 'active' + ) + ); + +-- INSERT: owner_id deve ser o caller, tenant_id deve ser tenant ativo do caller +CREATE POLICY "documents: insert" ON public.documents + FOR INSERT TO authenticated + WITH CHECK ( + owner_id = auth.uid() + AND tenant_id IN ( + SELECT tm.tenant_id FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() AND tm.status = 'active' + ) + ); + +-- UPDATE: só owner +CREATE POLICY "documents: update" ON public.documents + FOR UPDATE TO authenticated + USING (owner_id = auth.uid() OR public.is_saas_admin()) + WITH CHECK (owner_id = auth.uid() OR public.is_saas_admin()); + +-- DELETE: só owner ou saas_admin +CREATE POLICY "documents: delete" ON public.documents + FOR DELETE TO authenticated + USING (owner_id = auth.uid() OR public.is_saas_admin()); + +-- ───────────────────────────────────────────────────────────────────────── +-- V#46: document_share_links — RPC validate_share_token + remover SELECT direto +-- ----------------------------------------------------------------------------- +CREATE OR REPLACE FUNCTION public.validate_share_token(p_token text) +RETURNS jsonb +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path TO 'public' +AS $function$ +DECLARE + sl document_share_links%ROWTYPE; + v_doc documents%ROWTYPE; + v_token text; +BEGIN + v_token := nullif(btrim(coalesce(p_token, '')), ''); + IF v_token IS NULL THEN + RAISE EXCEPTION 'token obrigatório' USING ERRCODE = '22023'; + END IF; + + SELECT * INTO sl FROM document_share_links WHERE token = v_token LIMIT 1; + IF NOT FOUND THEN + RAISE EXCEPTION 'Token inválido' USING ERRCODE = '28000'; + END IF; + IF sl.ativo IS NOT TRUE THEN + RAISE EXCEPTION 'Link desativado' USING ERRCODE = '28000'; + END IF; + IF sl.expira_em IS NOT NULL AND sl.expira_em < now() THEN + RAISE EXCEPTION 'Link expirado' USING ERRCODE = '28000'; + END IF; + IF sl.usos_max IS NOT NULL AND sl.usos >= sl.usos_max THEN + RAISE EXCEPTION 'Limite de uso atingido' USING ERRCODE = '28000'; + END IF; + + -- Incrementa uso atomicamente + UPDATE document_share_links SET usos = usos + 1 WHERE id = sl.id; + + -- Loga acesso (best-effort) + BEGIN + INSERT INTO document_access_logs (document_id, tenant_id, action, share_link_id) + SELECT sl.document_id, d.tenant_id, 'shared_link_access', sl.id + FROM documents d WHERE d.id = sl.document_id; + EXCEPTION WHEN OTHERS THEN + -- não derruba a request se log falhar (schema pode variar) + NULL; + END; + + SELECT * INTO v_doc FROM documents WHERE id = sl.document_id; + + RETURN jsonb_build_object( + 'document_id', sl.document_id, + 'bucket', v_doc.storage_bucket, + 'bucket_path', v_doc.bucket_path, + 'nome_original', v_doc.nome_original, + 'mime_type', v_doc.mime_type, + 'tamanho_bytes', v_doc.tamanho_bytes + ); +END; +$function$; + +REVOKE ALL ON FUNCTION public.validate_share_token(text) FROM PUBLIC, authenticated; +GRANT EXECUTE ON FUNCTION public.validate_share_token(text) TO anon, authenticated, service_role; + +-- Restringe SELECT direto da tabela: só criador (saas_admin via outra policy se necessário) +DROP POLICY IF EXISTS "dsl: public read by token" ON public.document_share_links; +DROP POLICY IF EXISTS "dsl: creator full access" ON public.document_share_links; + +CREATE POLICY "dsl: creator full access" ON public.document_share_links + FOR ALL TO authenticated + USING (criado_por = auth.uid() OR public.is_saas_admin()) + WITH CHECK (criado_por = auth.uid()); + +-- ───────────────────────────────────────────────────────────────────────── +-- V#47: document_signatures — separar SELECT/INSERT (tenant_member) vs UPDATE/DELETE (signatário) +-- ----------------------------------------------------------------------------- +DROP POLICY IF EXISTS "ds: tenant members access" ON public.document_signatures; +DROP POLICY IF EXISTS "ds: select" ON public.document_signatures; +DROP POLICY IF EXISTS "ds: insert" ON public.document_signatures; +DROP POLICY IF EXISTS "ds: update" ON public.document_signatures; +DROP POLICY IF EXISTS "ds: delete" ON public.document_signatures; + +CREATE POLICY "ds: select" ON public.document_signatures + FOR SELECT TO authenticated + USING ( + public.is_saas_admin() + OR tenant_id IN ( + SELECT tm.tenant_id FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() AND tm.status = 'active' + ) + ); + +-- INSERT: tenant_member pode criar; signatario_id (se preenchido) deve ser o caller +-- (paciente externo é signatario_tipo='paciente' com signatario_id NULL — a row +-- nasce sem assinatura e signatario_id é preenchido na aceitação via outro fluxo) +CREATE POLICY "ds: insert" ON public.document_signatures + FOR INSERT TO authenticated + WITH CHECK ( + tenant_id IN ( + SELECT tm.tenant_id FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() AND tm.status = 'active' + ) + AND (signatario_id IS NULL OR signatario_id = auth.uid()) + ); + +-- UPDATE: só o signatário designado ou saas_admin (impede secretária forjar status='assinado') +CREATE POLICY "ds: update" ON public.document_signatures + FOR UPDATE TO authenticated + USING (signatario_id = auth.uid() OR public.is_saas_admin()) + WITH CHECK (signatario_id = auth.uid() OR public.is_saas_admin()); + +-- DELETE: signatário, saas_admin ou tenant_admin/owner +CREATE POLICY "ds: delete" ON public.document_signatures + FOR DELETE TO authenticated + USING ( + signatario_id = auth.uid() + OR public.is_saas_admin() + OR tenant_id IN ( + SELECT tm.tenant_id FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() AND tm.status = 'active' + AND tm.role IN ('tenant_admin','admin','owner') + ) + ); + +-- ───────────────────────────────────────────────────────────────────────── +-- V#48: document_access_logs — INSERT com WITH CHECK +-- ----------------------------------------------------------------------------- +DROP POLICY IF EXISTS "dal: tenant members can insert" ON public.document_access_logs; +CREATE POLICY "dal: tenant members can insert" ON public.document_access_logs + FOR INSERT TO authenticated + WITH CHECK ( + tenant_id IN ( + SELECT tm.tenant_id FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() AND tm.status = 'active' + ) + ); + +-- ───────────────────────────────────────────────────────────────────────── +-- V#49: document_templates — INSERT com WITH CHECK +-- ----------------------------------------------------------------------------- +DROP POLICY IF EXISTS "dt: owner can insert" ON public.document_templates; +DROP POLICY IF EXISTS "dt: saas admin can insert global" ON public.document_templates; + +CREATE POLICY "dt: owner can insert" ON public.document_templates + FOR INSERT TO authenticated + WITH CHECK ( + is_global = false + AND owner_id = auth.uid() + AND tenant_id IN ( + SELECT tm.tenant_id FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() AND tm.status = 'active' + ) + ); + +CREATE POLICY "dt: saas admin can insert global" ON public.document_templates + FOR INSERT TO authenticated + WITH CHECK (is_global = true AND public.is_saas_admin()); diff --git a/database-novo/schema/00_full/schema.sql b/database-novo/schema/00_full/schema.sql index 85dd8db..e96d54d 100644 --- a/database-novo/schema/00_full/schema.sql +++ b/database-novo/schema/00_full/schema.sql @@ -2,7 +2,7 @@ -- PostgreSQL database dump -- -\restrict ABfzP9IZJ8pAzvgt6E9jKpFn1phQ3b3Lgk09BZZTle5el6ODr77nIXlXnCf1PS1 +\restrict t3AqgfsPfrxKQ6xDOUZgLcwOmqGEQFr9QmWZe441K296WP9E1M31r17ZcywnfKW -- Dumped from database version 17.6 -- Dumped by pg_dump version 17.6 @@ -20,23 +20,19 @@ SET client_min_messages = warning; SET row_security = off; -- --- Name: _realtime; Type: SCHEMA; Schema: -; Owner: postgres +-- Name: _realtime; Type: SCHEMA; Schema: -; Owner: - -- CREATE SCHEMA _realtime; -ALTER SCHEMA _realtime OWNER TO postgres; - -- --- Name: auth; Type: SCHEMA; Schema: -; Owner: supabase_admin +-- Name: auth; Type: SCHEMA; Schema: -; Owner: - -- CREATE SCHEMA auth; -ALTER SCHEMA auth OWNER TO supabase_admin; - -- -- Name: pg_cron; Type: EXTENSION; Schema: -; Owner: - -- @@ -45,39 +41,33 @@ CREATE EXTENSION IF NOT EXISTS pg_cron WITH SCHEMA pg_catalog; -- --- Name: EXTENSION pg_cron; Type: COMMENT; Schema: -; Owner: +-- 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 +-- Name: extensions; Type: SCHEMA; Schema: -; Owner: - -- CREATE SCHEMA extensions; -ALTER SCHEMA extensions OWNER TO postgres; - -- --- Name: graphql; Type: SCHEMA; Schema: -; Owner: supabase_admin +-- Name: graphql; Type: SCHEMA; Schema: -; Owner: - -- CREATE SCHEMA graphql; -ALTER SCHEMA graphql OWNER TO supabase_admin; - -- --- Name: graphql_public; Type: SCHEMA; Schema: -; Owner: supabase_admin +-- Name: graphql_public; Type: SCHEMA; Schema: -; Owner: - -- CREATE SCHEMA graphql_public; -ALTER SCHEMA graphql_public OWNER TO supabase_admin; - -- -- Name: pg_net; Type: EXTENSION; Schema: -; Owner: - -- @@ -86,57 +76,47 @@ CREATE EXTENSION IF NOT EXISTS pg_net WITH SCHEMA extensions; -- --- Name: EXTENSION pg_net; Type: COMMENT; Schema: -; Owner: +-- Name: EXTENSION pg_net; Type: COMMENT; Schema: -; Owner: - -- COMMENT ON EXTENSION pg_net IS 'Async HTTP'; -- --- Name: pgbouncer; Type: SCHEMA; Schema: -; Owner: pgbouncer +-- Name: pgbouncer; Type: SCHEMA; Schema: -; Owner: - -- CREATE SCHEMA pgbouncer; -ALTER SCHEMA pgbouncer OWNER TO pgbouncer; - -- --- Name: realtime; Type: SCHEMA; Schema: -; Owner: supabase_admin +-- Name: realtime; Type: SCHEMA; Schema: -; Owner: - -- CREATE SCHEMA realtime; -ALTER SCHEMA realtime OWNER TO supabase_admin; - -- --- Name: storage; Type: SCHEMA; Schema: -; Owner: supabase_admin +-- Name: storage; Type: SCHEMA; Schema: -; Owner: - -- CREATE SCHEMA storage; -ALTER SCHEMA storage OWNER TO supabase_admin; - -- --- Name: supabase_functions; Type: SCHEMA; Schema: -; Owner: supabase_admin +-- Name: supabase_functions; Type: SCHEMA; Schema: -; Owner: - -- CREATE SCHEMA supabase_functions; -ALTER SCHEMA supabase_functions OWNER TO supabase_admin; - -- --- Name: vault; Type: SCHEMA; Schema: -; Owner: supabase_admin +-- Name: vault; Type: SCHEMA; Schema: -; Owner: - -- CREATE SCHEMA vault; -ALTER SCHEMA vault OWNER TO supabase_admin; - -- -- Name: btree_gist; Type: EXTENSION; Schema: -; Owner: - -- @@ -145,7 +125,7 @@ CREATE EXTENSION IF NOT EXISTS btree_gist WITH SCHEMA public; -- --- Name: EXTENSION btree_gist; Type: COMMENT; Schema: -; Owner: +-- Name: EXTENSION btree_gist; Type: COMMENT; Schema: -; Owner: - -- COMMENT ON EXTENSION btree_gist IS 'support for indexing common datatypes in GiST'; @@ -159,7 +139,7 @@ CREATE EXTENSION IF NOT EXISTS citext WITH SCHEMA public; -- --- Name: EXTENSION citext; Type: COMMENT; Schema: -; Owner: +-- Name: EXTENSION citext; Type: COMMENT; Schema: -; Owner: - -- COMMENT ON EXTENSION citext IS 'data type for case-insensitive character strings'; @@ -173,7 +153,7 @@ CREATE EXTENSION IF NOT EXISTS pg_graphql WITH SCHEMA graphql; -- --- Name: EXTENSION pg_graphql; Type: COMMENT; Schema: -; Owner: +-- Name: EXTENSION pg_graphql; Type: COMMENT; Schema: -; Owner: - -- COMMENT ON EXTENSION pg_graphql IS 'pg_graphql: GraphQL support'; @@ -187,7 +167,7 @@ CREATE EXTENSION IF NOT EXISTS pg_stat_statements WITH SCHEMA extensions; -- --- Name: EXTENSION pg_stat_statements; Type: COMMENT; Schema: -; Owner: +-- 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'; @@ -201,7 +181,7 @@ CREATE EXTENSION IF NOT EXISTS pg_trgm WITH SCHEMA public; -- --- Name: EXTENSION pg_trgm; Type: COMMENT; Schema: -; Owner: +-- Name: EXTENSION pg_trgm; Type: COMMENT; Schema: -; Owner: - -- COMMENT ON EXTENSION pg_trgm IS 'text similarity measurement and index searching based on trigrams'; @@ -215,7 +195,7 @@ CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA extensions; -- --- Name: EXTENSION pgcrypto; Type: COMMENT; Schema: -; Owner: +-- Name: EXTENSION pgcrypto; Type: COMMENT; Schema: -; Owner: - -- COMMENT ON EXTENSION pgcrypto IS 'cryptographic functions'; @@ -229,7 +209,7 @@ CREATE EXTENSION IF NOT EXISTS supabase_vault WITH SCHEMA vault; -- --- Name: EXTENSION supabase_vault; Type: COMMENT; Schema: -; Owner: +-- Name: EXTENSION supabase_vault; Type: COMMENT; Schema: -; Owner: - -- COMMENT ON EXTENSION supabase_vault IS 'Supabase Vault Extension'; @@ -243,14 +223,14 @@ CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA extensions; -- --- Name: EXTENSION "uuid-ossp"; Type: COMMENT; Schema: -; Owner: +-- Name: EXTENSION "uuid-ossp"; Type: COMMENT; Schema: -; Owner: - -- COMMENT ON EXTENSION "uuid-ossp" IS 'generate universally unique identifiers (UUIDs)'; -- --- Name: aal_level; Type: TYPE; Schema: auth; Owner: supabase_auth_admin +-- Name: aal_level; Type: TYPE; Schema: auth; Owner: - -- CREATE TYPE auth.aal_level AS ENUM ( @@ -260,10 +240,8 @@ CREATE TYPE auth.aal_level AS ENUM ( ); -ALTER TYPE auth.aal_level OWNER TO supabase_auth_admin; - -- --- Name: code_challenge_method; Type: TYPE; Schema: auth; Owner: supabase_auth_admin +-- Name: code_challenge_method; Type: TYPE; Schema: auth; Owner: - -- CREATE TYPE auth.code_challenge_method AS ENUM ( @@ -272,10 +250,8 @@ CREATE TYPE auth.code_challenge_method AS ENUM ( ); -ALTER TYPE auth.code_challenge_method OWNER TO supabase_auth_admin; - -- --- Name: factor_status; Type: TYPE; Schema: auth; Owner: supabase_auth_admin +-- Name: factor_status; Type: TYPE; Schema: auth; Owner: - -- CREATE TYPE auth.factor_status AS ENUM ( @@ -284,10 +260,8 @@ CREATE TYPE auth.factor_status AS ENUM ( ); -ALTER TYPE auth.factor_status OWNER TO supabase_auth_admin; - -- --- Name: factor_type; Type: TYPE; Schema: auth; Owner: supabase_auth_admin +-- Name: factor_type; Type: TYPE; Schema: auth; Owner: - -- CREATE TYPE auth.factor_type AS ENUM ( @@ -297,10 +271,8 @@ CREATE TYPE auth.factor_type AS ENUM ( ); -ALTER TYPE auth.factor_type OWNER TO supabase_auth_admin; - -- --- Name: oauth_authorization_status; Type: TYPE; Schema: auth; Owner: supabase_auth_admin +-- Name: oauth_authorization_status; Type: TYPE; Schema: auth; Owner: - -- CREATE TYPE auth.oauth_authorization_status AS ENUM ( @@ -311,10 +283,8 @@ CREATE TYPE auth.oauth_authorization_status AS ENUM ( ); -ALTER TYPE auth.oauth_authorization_status OWNER TO supabase_auth_admin; - -- --- Name: oauth_client_type; Type: TYPE; Schema: auth; Owner: supabase_auth_admin +-- Name: oauth_client_type; Type: TYPE; Schema: auth; Owner: - -- CREATE TYPE auth.oauth_client_type AS ENUM ( @@ -323,10 +293,8 @@ CREATE TYPE auth.oauth_client_type AS ENUM ( ); -ALTER TYPE auth.oauth_client_type OWNER TO supabase_auth_admin; - -- --- Name: oauth_registration_type; Type: TYPE; Schema: auth; Owner: supabase_auth_admin +-- Name: oauth_registration_type; Type: TYPE; Schema: auth; Owner: - -- CREATE TYPE auth.oauth_registration_type AS ENUM ( @@ -335,10 +303,8 @@ CREATE TYPE auth.oauth_registration_type AS ENUM ( ); -ALTER TYPE auth.oauth_registration_type OWNER TO supabase_auth_admin; - -- --- Name: oauth_response_type; Type: TYPE; Schema: auth; Owner: supabase_auth_admin +-- Name: oauth_response_type; Type: TYPE; Schema: auth; Owner: - -- CREATE TYPE auth.oauth_response_type AS ENUM ( @@ -346,10 +312,8 @@ CREATE TYPE auth.oauth_response_type AS ENUM ( ); -ALTER TYPE auth.oauth_response_type OWNER TO supabase_auth_admin; - -- --- Name: one_time_token_type; Type: TYPE; Schema: auth; Owner: supabase_auth_admin +-- Name: one_time_token_type; Type: TYPE; Schema: auth; Owner: - -- CREATE TYPE auth.one_time_token_type AS ENUM ( @@ -362,10 +326,8 @@ CREATE TYPE auth.one_time_token_type AS ENUM ( ); -ALTER TYPE auth.one_time_token_type OWNER TO supabase_auth_admin; - -- --- Name: commitment_log_source; Type: TYPE; Schema: public; Owner: supabase_admin +-- Name: commitment_log_source; Type: TYPE; Schema: public; Owner: - -- CREATE TYPE public.commitment_log_source AS ENUM ( @@ -374,10 +336,8 @@ CREATE TYPE public.commitment_log_source AS ENUM ( ); -ALTER TYPE public.commitment_log_source OWNER TO supabase_admin; - -- --- Name: determined_field_type; Type: TYPE; Schema: public; Owner: supabase_admin +-- Name: determined_field_type; Type: TYPE; Schema: public; Owner: - -- CREATE TYPE public.determined_field_type AS ENUM ( @@ -390,10 +350,8 @@ CREATE TYPE public.determined_field_type AS ENUM ( ); -ALTER TYPE public.determined_field_type OWNER TO supabase_admin; - -- --- Name: financial_record_type; Type: TYPE; Schema: public; Owner: supabase_admin +-- Name: financial_record_type; Type: TYPE; Schema: public; Owner: - -- CREATE TYPE public.financial_record_type AS ENUM ( @@ -402,10 +360,8 @@ CREATE TYPE public.financial_record_type AS ENUM ( ); -ALTER TYPE public.financial_record_type OWNER TO supabase_admin; - -- --- Name: recurrence_exception_type; Type: TYPE; Schema: public; Owner: supabase_admin +-- Name: recurrence_exception_type; Type: TYPE; Schema: public; Owner: - -- CREATE TYPE public.recurrence_exception_type AS ENUM ( @@ -417,10 +373,8 @@ CREATE TYPE public.recurrence_exception_type AS ENUM ( ); -ALTER TYPE public.recurrence_exception_type OWNER TO supabase_admin; - -- --- Name: recurrence_type; Type: TYPE; Schema: public; Owner: supabase_admin +-- Name: recurrence_type; Type: TYPE; Schema: public; Owner: - -- CREATE TYPE public.recurrence_type AS ENUM ( @@ -432,10 +386,8 @@ CREATE TYPE public.recurrence_type AS ENUM ( ); -ALTER TYPE public.recurrence_type OWNER TO supabase_admin; - -- --- Name: status_agenda_serie; Type: TYPE; Schema: public; Owner: supabase_admin +-- Name: status_agenda_serie; Type: TYPE; Schema: public; Owner: - -- CREATE TYPE public.status_agenda_serie AS ENUM ( @@ -445,10 +397,8 @@ CREATE TYPE public.status_agenda_serie AS ENUM ( ); -ALTER TYPE public.status_agenda_serie OWNER TO supabase_admin; - -- --- Name: status_evento_agenda; Type: TYPE; Schema: public; Owner: supabase_admin +-- Name: status_evento_agenda; Type: TYPE; Schema: public; Owner: - -- CREATE TYPE public.status_evento_agenda AS ENUM ( @@ -460,10 +410,8 @@ CREATE TYPE public.status_evento_agenda AS ENUM ( ); -ALTER TYPE public.status_evento_agenda OWNER TO supabase_admin; - -- --- Name: status_excecao_agenda; Type: TYPE; Schema: public; Owner: supabase_admin +-- Name: status_excecao_agenda; Type: TYPE; Schema: public; Owner: - -- CREATE TYPE public.status_excecao_agenda AS ENUM ( @@ -473,10 +421,8 @@ CREATE TYPE public.status_excecao_agenda AS ENUM ( ); -ALTER TYPE public.status_excecao_agenda OWNER TO supabase_admin; - -- --- Name: tipo_evento_agenda; Type: TYPE; Schema: public; Owner: supabase_admin +-- Name: tipo_evento_agenda; Type: TYPE; Schema: public; Owner: - -- CREATE TYPE public.tipo_evento_agenda AS ENUM ( @@ -485,10 +431,8 @@ CREATE TYPE public.tipo_evento_agenda AS ENUM ( ); -ALTER TYPE public.tipo_evento_agenda OWNER TO supabase_admin; - -- --- Name: tipo_excecao_agenda; Type: TYPE; Schema: public; Owner: supabase_admin +-- Name: tipo_excecao_agenda; Type: TYPE; Schema: public; Owner: - -- CREATE TYPE public.tipo_excecao_agenda AS ENUM ( @@ -497,10 +441,8 @@ CREATE TYPE public.tipo_excecao_agenda AS ENUM ( ); -ALTER TYPE public.tipo_excecao_agenda OWNER TO supabase_admin; - -- --- Name: action; Type: TYPE; Schema: realtime; Owner: supabase_admin +-- Name: action; Type: TYPE; Schema: realtime; Owner: - -- CREATE TYPE realtime.action AS ENUM ( @@ -512,10 +454,8 @@ CREATE TYPE realtime.action AS ENUM ( ); -ALTER TYPE realtime.action OWNER TO supabase_admin; - -- --- Name: equality_op; Type: TYPE; Schema: realtime; Owner: supabase_admin +-- Name: equality_op; Type: TYPE; Schema: realtime; Owner: - -- CREATE TYPE realtime.equality_op AS ENUM ( @@ -529,10 +469,8 @@ CREATE TYPE realtime.equality_op AS ENUM ( ); -ALTER TYPE realtime.equality_op OWNER TO supabase_admin; - -- --- Name: user_defined_filter; Type: TYPE; Schema: realtime; Owner: supabase_admin +-- Name: user_defined_filter; Type: TYPE; Schema: realtime; Owner: - -- CREATE TYPE realtime.user_defined_filter AS ( @@ -542,10 +480,8 @@ CREATE TYPE realtime.user_defined_filter AS ( ); -ALTER TYPE realtime.user_defined_filter OWNER TO supabase_admin; - -- --- Name: wal_column; Type: TYPE; Schema: realtime; Owner: supabase_admin +-- Name: wal_column; Type: TYPE; Schema: realtime; Owner: - -- CREATE TYPE realtime.wal_column AS ( @@ -558,10 +494,8 @@ CREATE TYPE realtime.wal_column AS ( ); -ALTER TYPE realtime.wal_column OWNER TO supabase_admin; - -- --- Name: wal_rls; Type: TYPE; Schema: realtime; Owner: supabase_admin +-- Name: wal_rls; Type: TYPE; Schema: realtime; Owner: - -- CREATE TYPE realtime.wal_rls AS ( @@ -572,10 +506,8 @@ CREATE TYPE realtime.wal_rls AS ( ); -ALTER TYPE realtime.wal_rls OWNER TO supabase_admin; - -- --- Name: buckettype; Type: TYPE; Schema: storage; Owner: supabase_storage_admin +-- Name: buckettype; Type: TYPE; Schema: storage; Owner: - -- CREATE TYPE storage.buckettype AS ENUM ( @@ -585,10 +517,8 @@ CREATE TYPE storage.buckettype AS ENUM ( ); -ALTER TYPE storage.buckettype OWNER TO supabase_storage_admin; - -- --- Name: email(); Type: FUNCTION; Schema: auth; Owner: supabase_auth_admin +-- Name: email(); Type: FUNCTION; Schema: auth; Owner: - -- CREATE FUNCTION auth.email() RETURNS text @@ -602,17 +532,15 @@ CREATE FUNCTION auth.email() RETURNS text $$; -ALTER FUNCTION auth.email() OWNER TO supabase_auth_admin; - -- --- Name: FUNCTION email(); Type: COMMENT; Schema: auth; Owner: supabase_auth_admin +-- Name: FUNCTION email(); Type: COMMENT; Schema: auth; Owner: - -- COMMENT ON FUNCTION auth.email() IS 'Deprecated. Use auth.jwt() -> ''email'' instead.'; -- --- Name: jwt(); Type: FUNCTION; Schema: auth; Owner: supabase_auth_admin +-- Name: jwt(); Type: FUNCTION; Schema: auth; Owner: - -- CREATE FUNCTION auth.jwt() RETURNS jsonb @@ -626,10 +554,8 @@ CREATE FUNCTION auth.jwt() RETURNS jsonb $$; -ALTER FUNCTION auth.jwt() OWNER TO supabase_auth_admin; - -- --- Name: role(); Type: FUNCTION; Schema: auth; Owner: supabase_auth_admin +-- Name: role(); Type: FUNCTION; Schema: auth; Owner: - -- CREATE FUNCTION auth.role() RETURNS text @@ -643,17 +569,15 @@ CREATE FUNCTION auth.role() RETURNS text $$; -ALTER FUNCTION auth.role() OWNER TO supabase_auth_admin; - -- --- Name: FUNCTION role(); Type: COMMENT; Schema: auth; Owner: supabase_auth_admin +-- Name: FUNCTION role(); Type: COMMENT; Schema: auth; Owner: - -- COMMENT ON FUNCTION auth.role() IS 'Deprecated. Use auth.jwt() -> ''role'' instead.'; -- --- Name: uid(); Type: FUNCTION; Schema: auth; Owner: supabase_auth_admin +-- Name: uid(); Type: FUNCTION; Schema: auth; Owner: - -- CREATE FUNCTION auth.uid() RETURNS uuid @@ -667,17 +591,15 @@ CREATE FUNCTION auth.uid() RETURNS uuid $$; -ALTER FUNCTION auth.uid() OWNER TO supabase_auth_admin; - -- --- Name: FUNCTION uid(); Type: COMMENT; Schema: auth; Owner: supabase_auth_admin +-- Name: FUNCTION uid(); Type: COMMENT; Schema: auth; Owner: - -- COMMENT ON FUNCTION auth.uid() IS 'Deprecated. Use auth.jwt() -> ''sub'' instead.'; -- --- Name: grant_pg_cron_access(); Type: FUNCTION; Schema: extensions; Owner: supabase_admin +-- Name: grant_pg_cron_access(); Type: FUNCTION; Schema: extensions; Owner: - -- CREATE FUNCTION extensions.grant_pg_cron_access() RETURNS event_trigger @@ -713,17 +635,15 @@ 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 +-- Name: FUNCTION grant_pg_cron_access(); Type: COMMENT; Schema: extensions; Owner: - -- 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 +-- Name: grant_pg_graphql_access(); Type: FUNCTION; Schema: extensions; Owner: - -- CREATE FUNCTION extensions.grant_pg_graphql_access() RETURNS event_trigger @@ -780,17 +700,15 @@ 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 +-- Name: FUNCTION grant_pg_graphql_access(); Type: COMMENT; Schema: extensions; Owner: - -- 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 +-- Name: grant_pg_net_access(); Type: FUNCTION; Schema: extensions; Owner: - -- CREATE FUNCTION extensions.grant_pg_net_access() RETURNS event_trigger @@ -823,17 +741,15 @@ 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 +-- Name: FUNCTION grant_pg_net_access(); Type: COMMENT; Schema: extensions; Owner: - -- 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 +-- Name: pgrst_ddl_watch(); Type: FUNCTION; Schema: extensions; Owner: - -- CREATE FUNCTION extensions.pgrst_ddl_watch() RETURNS event_trigger @@ -865,10 +781,8 @@ BEGIN END; $$; -ALTER FUNCTION extensions.pgrst_ddl_watch() OWNER TO supabase_admin; - -- --- Name: pgrst_drop_watch(); Type: FUNCTION; Schema: extensions; Owner: supabase_admin +-- Name: pgrst_drop_watch(); Type: FUNCTION; Schema: extensions; Owner: - -- CREATE FUNCTION extensions.pgrst_drop_watch() RETURNS event_trigger @@ -898,10 +812,8 @@ BEGIN END; $$; -ALTER FUNCTION extensions.pgrst_drop_watch() OWNER TO supabase_admin; - -- --- Name: set_graphql_placeholder(); Type: FUNCTION; Schema: extensions; Owner: supabase_admin +-- Name: set_graphql_placeholder(); Type: FUNCTION; Schema: extensions; Owner: - -- CREATE FUNCTION extensions.set_graphql_placeholder() RETURNS event_trigger @@ -957,17 +869,15 @@ CREATE FUNCTION extensions.set_graphql_placeholder() RETURNS event_trigger $_$; -ALTER FUNCTION extensions.set_graphql_placeholder() OWNER TO supabase_admin; - -- --- Name: FUNCTION set_graphql_placeholder(); Type: COMMENT; Schema: extensions; Owner: supabase_admin +-- Name: FUNCTION set_graphql_placeholder(); Type: COMMENT; Schema: extensions; Owner: - -- 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 +-- Name: get_auth(text); Type: FUNCTION; Schema: pgbouncer; Owner: - -- CREATE FUNCTION pgbouncer.get_auth(p_usename text) RETURNS TABLE(username text, password text) @@ -990,10 +900,8 @@ end; $_$; -ALTER FUNCTION pgbouncer.get_auth(p_usename text) OWNER TO supabase_admin; - -- --- Name: __rls_ping(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: __rls_ping(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.__rls_ping() RETURNS text @@ -1003,14 +911,12 @@ CREATE FUNCTION public.__rls_ping() RETURNS text $$; -ALTER FUNCTION public.__rls_ping() OWNER TO supabase_admin; - SET default_tablespace = ''; SET default_table_access_method = heap; -- --- Name: subscriptions; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: subscriptions; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.subscriptions ( @@ -1045,10 +951,8 @@ CREATE TABLE public.subscriptions ( ); -ALTER TABLE public.subscriptions OWNER TO supabase_admin; - -- --- Name: activate_subscription_from_intent(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: activate_subscription_from_intent(uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.activate_subscription_from_intent(p_intent_id uuid) RETURNS public.subscriptions @@ -1062,13 +966,13 @@ declare v_plan_id uuid; v_target text; begin - -- lê pela VIEW unificada + -- l?? pela VIEW unificada select * into v_intent from public.subscription_intents where id = p_intent_id; if not found then - raise exception 'Intent não encontrado: %', p_intent_id; + raise exception 'Intent n??o encontrado: %', p_intent_id; end if; if v_intent.status <> 'paid' then @@ -1083,14 +987,14 @@ begin limit 1; if v_plan_id is null then - raise exception 'Plano não encontrado em plans.key = %', v_intent.plan_key; + raise exception 'Plano n??o encontrado em plans.key = %', v_intent.plan_key; end if; v_target := lower(coalesce(v_target, '')); - -- ✅ supervisor adicionado + -- ??? supervisor adicionado if v_target not in ('clinic', 'therapist', 'supervisor') then - raise exception 'Target inválido em plans.target: %', v_target; + raise exception 'Target inv??lido em plans.target: %', v_target; end if; -- regra por target @@ -1107,7 +1011,7 @@ begin end if; if v_target in ('therapist', 'supervisor') and v_user_id is null then - raise exception 'Não foi possível determinar user_id para assinatura %.', v_target; + raise exception 'N??o foi poss??vel determinar user_id para assinatura %.', v_target; end if; -- cancela assinatura ativa anterior @@ -1129,7 +1033,7 @@ begin and tenant_id is null; end if; - -- duração do plano (30 dias para mensal) + -- dura????o do plano (30 dias para mensal) v_days := case when lower(coalesce(v_intent.interval, 'month')) = 'year' then 365 else 30 @@ -1168,7 +1072,7 @@ begin ) returning * into v_sub; - -- grava vínculo intent → subscription + -- grava v??nculo intent ??? subscription if v_target = 'clinic' then update public.subscription_intents_tenant set subscription_id = v_sub.id @@ -1184,13 +1088,11 @@ end; $$; -ALTER FUNCTION public.activate_subscription_from_intent(p_intent_id uuid) OWNER TO supabase_admin; - -- --- Name: admin_credit_addon(uuid, text, integer, uuid, text, text, integer); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: admin_credit_addon(uuid, text, integer, uuid, text, text, integer); Type: FUNCTION; Schema: public; Owner: - -- -CREATE FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid DEFAULT NULL::uuid, p_description text DEFAULT 'Crédito manual'::text, p_payment_method text DEFAULT 'manual'::text, p_price_cents integer DEFAULT 0) RETURNS jsonb +CREATE FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid DEFAULT NULL::uuid, p_description text DEFAULT 'Cr??dito manual'::text, p_payment_method text DEFAULT 'manual'::text, p_price_cents integer DEFAULT 0) RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$ @@ -1222,7 +1124,7 @@ BEGIN updated_at = now() WHERE id = v_credit.id; - -- Registra transação + -- Registra transa????o INSERT INTO addon_transactions ( tenant_id, addon_type, type, amount, balance_before, balance_after, @@ -1246,17 +1148,15 @@ END; $$; -ALTER FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer) OWNER TO supabase_admin; - -- --- Name: FUNCTION admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer); Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: FUNCTION admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer); Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer) IS 'Admin adiciona créditos de add-on a um tenant. Cria registro se não existir.'; +COMMENT ON FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer) IS 'Admin adiciona cr??ditos de add-on a um tenant. Cria registro se n??o existir.'; -- --- Name: admin_delete_email_template_global(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: admin_delete_email_template_global(uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.admin_delete_email_template_global(p_id uuid) RETURNS boolean @@ -1266,17 +1166,15 @@ CREATE FUNCTION public.admin_delete_email_template_global(p_id uuid) RETURNS boo BEGIN DELETE FROM public.email_templates_global WHERE id = p_id; IF NOT FOUND THEN - RAISE EXCEPTION 'Template com id % não encontrado', p_id; + RAISE EXCEPTION 'Template com id % n??o encontrado', p_id; END IF; RETURN true; END; $$; -ALTER FUNCTION public.admin_delete_email_template_global(p_id uuid) OWNER TO supabase_admin; - -- --- Name: admin_fix_plan_target(text, text); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: admin_fix_plan_target(text, text); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.admin_fix_plan_target(p_plan_key text, p_new_target text) RETURNS void @@ -1285,9 +1183,9 @@ CREATE FUNCTION public.admin_fix_plan_target(p_plan_key text, p_new_target text) declare v_plan_id uuid; begin - -- (opcional) restringe targets válidos + -- (opcional) restringe targets v??lidos if p_new_target not in ('clinic','therapist') then - raise exception 'Target inválido: %', p_new_target using errcode='P0001'; + raise exception 'Target inv??lido: %', p_new_target using errcode='P0001'; end if; -- trava o plano @@ -1297,15 +1195,15 @@ begin for update; if v_plan_id is null then - raise exception 'Plano não encontrado: %', p_plan_key using errcode='P0001'; + raise exception 'Plano n??o encontrado: %', p_plan_key using errcode='P0001'; end if; - -- segurança: não mexer se existe subscription + -- seguran??a: n??o mexer se existe subscription if exists (select 1 from public.subscriptions s where s.plan_id = v_plan_id) then - raise exception 'Plano % possui subscriptions. Migração bloqueada.', p_plan_key using errcode='P0001'; + raise exception 'Plano % possui subscriptions. Migra????o bloqueada.', p_plan_key using errcode='P0001'; end if; - -- liga bypass SOMENTE nesta transação + -- liga bypass SOMENTE nesta transa????o perform set_config('app.plan_migration_bypass', '1', true); update public.plans @@ -1316,10 +1214,8 @@ end $$; -ALTER FUNCTION public.admin_fix_plan_target(p_plan_key text, p_new_target text) OWNER TO supabase_admin; - -- --- Name: admin_upsert_email_template_global(uuid, text, text, text, text, text, text, boolean, jsonb); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: admin_upsert_email_template_global(uuid, text, text, text, text, text, text, boolean, jsonb); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.admin_upsert_email_template_global(p_id uuid DEFAULT NULL::uuid, p_key text DEFAULT NULL::text, p_domain text DEFAULT NULL::text, p_channel text DEFAULT 'email'::text, p_subject text DEFAULT NULL::text, p_body_html text DEFAULT NULL::text, p_body_text text DEFAULT NULL::text, p_is_active boolean DEFAULT true, p_variables jsonb DEFAULT '{}'::jsonb) RETURNS jsonb @@ -1344,7 +1240,7 @@ BEGIN RETURNING to_jsonb(email_templates_global.*) INTO v_result; IF v_result IS NULL THEN - RAISE EXCEPTION 'Template com id % não encontrado', p_id; + RAISE EXCEPTION 'Template com id % n??o encontrado', p_id; END IF; RETURN v_result; @@ -1352,7 +1248,7 @@ BEGIN -- INSERT novo IF p_key IS NULL OR p_domain IS NULL OR p_subject IS NULL OR p_body_html IS NULL THEN - RAISE EXCEPTION 'key, domain, subject e body_html são obrigatórios para novo template'; + RAISE EXCEPTION 'key, domain, subject e body_html s??o obrigat??rios para novo template'; END IF; INSERT INTO public.email_templates_global (key, domain, channel, subject, body_html, body_text, is_active, variables) @@ -1364,10 +1260,8 @@ END; $$; -ALTER FUNCTION public.admin_upsert_email_template_global(p_id uuid, p_key text, p_domain text, p_channel text, p_subject text, p_body_html text, p_body_text text, p_is_active boolean, p_variables jsonb) OWNER TO supabase_admin; - -- --- Name: agenda_cfg_sync(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: agenda_cfg_sync(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.agenda_cfg_sync() RETURNS trigger @@ -1387,10 +1281,8 @@ 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 +-- Name: agendador_dias_disponiveis(text, integer, integer); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.agendador_dias_disponiveis(p_slug text, p_ano integer, p_mes integer) RETURNS TABLE(data date, tem_slots boolean) @@ -1424,7 +1316,7 @@ BEGIN WHILE v_data <= v_data_fim LOOP v_db_dow := extract(dow from v_data::timestamp)::int; - -- ── Dia inteiro bloqueado? (agenda_bloqueios) ───────────────────────── + -- ?????? Dia inteiro bloqueado? (agenda_bloqueios) ??????????????????????????????????????????????????????????????????????????? SELECT EXISTS ( SELECT 1 FROM public.agenda_bloqueios b WHERE b.owner_id = v_owner_id @@ -1442,7 +1334,7 @@ BEGIN CONTINUE; END IF; - -- ── Tem slots disponíveis no dia? ───────────────────────────────────── + -- ?????? Tem slots dispon??veis no dia? ??????????????????????????????????????????????????????????????????????????????????????????????????????????????? SELECT EXISTS ( SELECT 1 FROM public.agenda_online_slots s WHERE s.owner_id = v_owner_id @@ -1465,10 +1357,8 @@ 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 +-- Name: agendador_gerar_slug(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.agendador_gerar_slug() RETURNS trigger @@ -1478,7 +1368,7 @@ DECLARE v_slug text; v_exists boolean; BEGIN - -- só gera se ativou e não tem slug ainda + -- 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)); @@ -1495,10 +1385,8 @@ END; $$; -ALTER FUNCTION public.agendador_gerar_slug() OWNER TO supabase_admin; - -- --- Name: agendador_slots_disponiveis(text, date); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: agendador_slots_disponiveis(text, date); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.agendador_slots_disponiveis(p_slug text, p_data date) RETURNS TABLE(hora time without time zone, disponivel boolean) @@ -1515,7 +1403,7 @@ DECLARE v_slot_fim time; v_slot_ts timestamptz; v_ocupado boolean; - -- loop de recorrências + -- loop de recorr??ncias v_rule RECORD; v_rule_start_dow int; v_first_occ date; @@ -1533,8 +1421,8 @@ BEGIN 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. + -- ?????? 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 @@ -1560,14 +1448,14 @@ BEGIN v_slot_fim := v_slot + (v_duracao || ' minutes')::interval; v_ocupado := false; - -- ── Antecedência mínima ────────────────────────────────────────────────── + -- ?????? 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) ─────────── + -- ?????? Bloqueio de hor??rio espec??fico (agenda_bloqueios com hora) ????????????????????????????????? IF NOT v_ocupado THEN SELECT EXISTS ( SELECT 1 FROM public.agenda_bloqueios b @@ -1584,7 +1472,7 @@ BEGIN ) INTO v_ocupado; END IF; - -- ── Eventos avulsos internos (agenda_eventos) ──────────────────────────── + -- ?????? Eventos avulsos internos (agenda_eventos) ???????????????????????????????????????????????????????????????????????????????????? IF NOT v_ocupado THEN SELECT EXISTS ( SELECT 1 FROM public.agenda_eventos e @@ -1596,7 +1484,7 @@ BEGIN ) INTO v_ocupado; END IF; - -- ── Recorrências ativas (recurrence_rules) ─────────────────────────────── + -- ?????? Recorr??ncias ativas (recurrence_rules) ????????????????????????????????????????????????????????????????????????????????????????????? IF NOT v_ocupado THEN FOR v_rule IN SELECT @@ -1640,7 +1528,7 @@ BEGIN END LOOP; END IF; - -- ── Recorrências remarcadas para este dia ──────────────────────────────── + -- ?????? Recorr??ncias remarcadas para este dia ???????????????????????????????????????????????????????????????????????????????????????????????? IF NOT v_ocupado THEN SELECT EXISTS ( SELECT 1 @@ -1655,7 +1543,7 @@ BEGIN ) INTO v_ocupado; END IF; - -- ── Solicitações públicas pendentes ────────────────────────────────────── + -- ?????? Solicita????es p??blicas pendentes ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????? IF NOT v_ocupado THEN SELECT EXISTS ( SELECT 1 FROM public.agendador_solicitacoes sol @@ -1675,10 +1563,8 @@ 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 +-- Name: auto_create_financial_record_from_session(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.auto_create_financial_record_from_session() RETURNS trigger @@ -1690,40 +1576,40 @@ DECLARE v_services_total NUMERIC(10,2); v_already_billed BOOLEAN; BEGIN - -- ── Guards de saída rápida ────────────────────────────────────────────── + -- ?????? Guards de sa??da r??pida ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? - -- Só processa quando o status muda PARA 'realizado' + -- 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 + -- 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.) + -- 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 + -- 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 + -- 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? + -- 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) + -- 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 @@ -1732,11 +1618,11 @@ BEGIN END IF; END IF; - -- ── Busca do preço ────────────────────────────────────────────────────── + -- ?????? Busca do pre??o ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? v_price := NULL; - -- Prioridade 1: soma dos serviços da regra de recorrência + -- 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 @@ -1747,7 +1633,7 @@ BEGIN v_price := v_services_total; END IF; - -- Prioridade 2: price direto da regra (fallback se sem serviços) + -- 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 @@ -1755,17 +1641,17 @@ BEGIN END IF; END IF; - -- Prioridade 3: price do próprio evento de agenda + -- 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) + -- 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 ───────────────────────────────────────── + -- ?????? Cria????o do financial_record ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? INSERT INTO public.financial_records ( owner_id, @@ -1780,7 +1666,7 @@ BEGIN clinic_fee_amount, status, due_date - -- payment_method: NULL até o momento do pagamento (mark_as_paid preenche) + -- payment_method: NULL at?? o momento do pagamento (mark_as_paid preenche) ) VALUES ( NEW.owner_id, NEW.tenant_id, @@ -1790,14 +1676,14 @@ BEGIN v_price, 0, v_price, - 0, -- clinic_fee_pct: sem campo de configuração global no schema atual. + 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 + (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 + -- ?????? 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; @@ -1814,17 +1700,15 @@ 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 +-- Name: FUNCTION auto_create_financial_record_from_session(); Type: COMMENT; Schema: public; Owner: - -- -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.'; +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 +-- Name: can_delete_patient(uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.can_delete_patient(p_patient_id uuid) RETURNS boolean @@ -1840,10 +1724,8 @@ CREATE FUNCTION public.can_delete_patient(p_patient_id uuid) RETURNS boolean $$; -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 +-- Name: cancel_notifications_on_opt_out(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.cancel_notifications_on_opt_out() RETURNS trigger @@ -1873,10 +1755,8 @@ 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 +-- Name: cancel_notifications_on_session_cancel(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.cancel_notifications_on_session_cancel() RETURNS trigger @@ -1895,10 +1775,8 @@ 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 +-- Name: cancel_patient_pending_notifications(uuid, text, uuid); Type: FUNCTION; Schema: public; Owner: - -- 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 @@ -1921,10 +1799,8 @@ 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 +-- Name: cancel_recurrence_from(uuid, date); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.cancel_recurrence_from(p_recurrence_id uuid, p_from_date date) RETURNS void @@ -1946,10 +1822,8 @@ 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 +-- Name: cancel_subscription(uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.cancel_subscription(p_subscription_id uuid) RETURNS public.subscriptions @@ -1968,7 +1842,7 @@ begin for update; if not found then - raise exception 'Subscription não encontrada'; + raise exception 'Subscription n??o encontrada'; end if; if v_sub.status = 'canceled' then @@ -2033,10 +1907,8 @@ end; $$; -ALTER FUNCTION public.cancel_subscription(p_subscription_id uuid) OWNER TO supabase_admin; - -- --- Name: cancelar_eventos_serie(uuid, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: cancelar_eventos_serie(uuid, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone DEFAULT now()) RETURNS integer @@ -2058,18 +1930,16 @@ 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 +-- Name: FUNCTION cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone); Type: COMMENT; Schema: public; Owner: - -- -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.'; +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 +-- Name: change_subscription_plan(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.change_subscription_plan(p_subscription_id uuid, p_new_plan_id uuid) RETURNS public.subscriptions @@ -2093,7 +1963,7 @@ begin for update; if not found then - raise exception 'Subscription não encontrada'; + raise exception 'Subscription n??o encontrada'; end if; v_old_plan := v_sub.plan_id; @@ -2108,7 +1978,7 @@ begin where id = p_new_plan_id; if v_new_key is null then - raise exception 'Plano não encontrado'; + raise exception 'Plano n??o encontrado'; end if; v_new_target := lower(coalesce(v_new_target, '')); @@ -2119,7 +1989,7 @@ begin end; if v_new_target <> v_sub_target then - raise exception 'Plano inválido para este tipo de assinatura. Assinatura é % e o plano é %.', + raise exception 'Plano inv??lido para este tipo de assinatura. Assinatura ?? % e o plano ?? %.', v_sub_target, v_new_target using errcode = 'P0001'; end if; @@ -2186,10 +2056,8 @@ end; $$; -ALTER FUNCTION public.change_subscription_plan(p_subscription_id uuid, p_new_plan_id uuid) OWNER TO supabase_admin; - -- --- Name: cleanup_notification_queue(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: cleanup_notification_queue(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.cleanup_notification_queue() RETURNS integer @@ -2208,10 +2076,8 @@ END; $$; -ALTER FUNCTION public.cleanup_notification_queue() OWNER TO supabase_admin; - -- --- Name: create_clinic_tenant(text); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: create_clinic_tenant(text); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.create_clinic_tenant(p_name text) RETURNS uuid @@ -2229,7 +2095,7 @@ begin v_name := nullif(trim(coalesce(p_name, '')), ''); if v_name is null then - v_name := 'Clínica'; + v_name := 'Cl??nica'; end if; insert into public.tenants (name, kind, created_at) @@ -2244,10 +2110,8 @@ end; $$; -ALTER FUNCTION public.create_clinic_tenant(p_name text) OWNER TO supabase_admin; - -- --- Name: financial_records; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: financial_records; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.financial_records ( @@ -2289,10 +2153,8 @@ CREATE TABLE public.financial_records ( ); -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 +-- Name: create_financial_record_for_session(uuid, uuid, uuid, uuid, numeric, date); Type: FUNCTION; Schema: public; Owner: - -- 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 @@ -2303,7 +2165,7 @@ DECLARE v_existing public.financial_records%ROWTYPE; v_new public.financial_records%ROWTYPE; BEGIN - -- Idempotência: retorna o registro existente se já foi criado + -- 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 @@ -2349,10 +2211,8 @@ 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 +-- Name: create_patient_intake_request(text, text, text, text, text, boolean); Type: FUNCTION; Schema: public; Owner: - -- 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 @@ -2374,7 +2234,7 @@ begin limit 1; if v_owner is null then - raise exception 'Token inválido'; + raise exception 'Token inv??lido'; end if; if v_active is not true then @@ -2390,7 +2250,7 @@ begin end if; if p_name is null or length(trim(p_name)) = 0 then - raise exception 'Nome é obrigatório'; + raise exception 'Nome ?? obrigat??rio'; end if; insert into public.patient_intake_requests @@ -2413,10 +2273,8 @@ 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 +-- Name: create_patient_intake_request_v2(text, jsonb); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.create_patient_intake_request_v2(p_token text, p_payload jsonb) RETURNS uuid @@ -2435,7 +2293,7 @@ begin where token = p_token; if v_owner_id is null then - raise exception 'Token inválido ou expirado'; + raise exception 'Token inv??lido ou expirado'; end if; v_birth_raw := nullif(trim(coalesce( @@ -2460,7 +2318,7 @@ begin email_principal, telefone, - avatar_url, -- 🔥 AQUI + avatar_url, -- ???? AQUI data_nascimento, cpf, @@ -2497,7 +2355,7 @@ begin nullif(trim(p_payload->>'email_principal'), ''), nullif(regexp_replace(coalesce(p_payload->>'telefone',''), '\D', '', 'g'), ''), - nullif(trim(p_payload->>'avatar_url'), ''), -- 🔥 AQUI + nullif(trim(p_payload->>'avatar_url'), ''), -- ???? AQUI v_birth, nullif(regexp_replace(coalesce(p_payload->>'cpf',''), '\D', '', 'g'), ''), @@ -2531,10 +2389,8 @@ 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 +-- Name: create_support_session(uuid, integer); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.create_support_session(p_tenant_id uuid, p_ttl_minutes integer DEFAULT 60) RETURNS json @@ -2548,10 +2404,10 @@ DECLARE v_expires timestamp with time zone; v_session support_sessions; BEGIN - -- Verifica autenticação + -- Verifica autentica????o v_admin_id := auth.uid(); IF v_admin_id IS NULL THEN - RAISE EXCEPTION 'Não autenticado.' USING ERRCODE = 'P0001'; + RAISE EXCEPTION 'N??o autenticado.' USING ERRCODE = 'P0001'; END IF; -- Verifica role saas_admin @@ -2560,27 +2416,27 @@ BEGIN WHERE id = v_admin_id; IF v_role <> 'saas_admin' THEN - RAISE EXCEPTION 'Acesso negado. Somente saas_admin pode criar sessões de suporte.' + 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.' + 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.' + RAISE EXCEPTION 'Tenant n??o encontrado.' USING ERRCODE = 'P0004'; END IF; - -- Gera token único (64 chars hex, sem pgcrypto) + -- 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 + -- 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; @@ -2594,10 +2450,8 @@ 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 +-- Name: therapist_payouts; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.therapist_payouts ( @@ -2623,10 +2477,8 @@ CREATE TABLE public.therapist_payouts ( ); -ALTER TABLE public.therapist_payouts OWNER TO supabase_admin; - -- --- Name: create_therapist_payout(uuid, uuid, date, date); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: create_therapist_payout(uuid, uuid, date, date); Type: FUNCTION; Schema: public; Owner: - -- 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 @@ -2640,13 +2492,13 @@ DECLARE 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 + -- ?????? 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.'; + RAISE EXCEPTION 'Sem permiss??o para criar repasse para este terapeuta.'; END IF; - -- ── Verifica se já existe repasse para o mesmo período ───────────────── + -- ?????? Verifica se j?? existe repasse para o mesmo per??odo ??????????????????????????????????????????????????? IF EXISTS ( SELECT 1 FROM public.therapist_payouts WHERE owner_id = p_therapist_id @@ -2656,13 +2508,13 @@ BEGIN AND status <> 'cancelled' ) THEN RAISE EXCEPTION - 'Já existe um repasse ativo para o período % a % deste terapeuta.', + '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. + -- ?????? 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, @@ -2682,14 +2534,14 @@ BEGIN WHERE tpr.financial_record_id = fr.id ); - -- Sem registros elegíveis → não criar payout vazio + -- 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 %.', + 'Nenhum registro financeiro eleg??vel encontrado para o per??odo % a %.', p_period_start, p_period_end; END IF; - -- ── Cria o repasse ───────────────────────────────────────────────────── + -- ?????? Cria o repasse ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? INSERT INTO public.therapist_payouts ( owner_id, tenant_id, @@ -2713,7 +2565,7 @@ BEGIN ) RETURNING * INTO v_payout; - -- ── Vincula os financial_records ao repasse ──────────────────────────── + -- ?????? 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 @@ -2733,17 +2585,15 @@ 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 +-- 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: - -- -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.'; +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 +-- Name: current_member_id(uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.current_member_id(p_tenant_id uuid) RETURNS uuid @@ -2757,10 +2607,8 @@ CREATE FUNCTION public.current_member_id(p_tenant_id uuid) RETURNS uuid $$; -ALTER FUNCTION public.current_member_id(p_tenant_id uuid) OWNER TO supabase_admin; - -- --- Name: current_member_role(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: current_member_role(uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.current_member_role(p_tenant_id uuid) RETURNS text @@ -2774,10 +2622,8 @@ CREATE FUNCTION public.current_member_role(p_tenant_id uuid) RETURNS text $$; -ALTER FUNCTION public.current_member_role(p_tenant_id uuid) OWNER TO supabase_admin; - -- --- Name: debit_addon_credit(uuid, text, uuid, text); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: debit_addon_credit(uuid, text, uuid, text); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid DEFAULT NULL::uuid, p_description text DEFAULT 'Consumo'::text) RETURNS jsonb @@ -2804,7 +2650,7 @@ BEGIN RETURN jsonb_build_object('success', false, 'reason', 'insufficient_balance', 'balance', 0); END IF; - -- Verifica rate limit diário + -- Verifica rate limit di??rio IF v_credit.daily_limit IS NOT NULL THEN -- Reset se passou do dia IF v_credit.daily_reset_at IS NULL OR v_credit.daily_reset_at < date_trunc('day', now()) THEN @@ -2817,7 +2663,7 @@ BEGIN END IF; END IF; - -- Verifica rate limit horário + -- Verifica rate limit hor??rio IF v_credit.hourly_limit IS NOT NULL THEN IF v_credit.hourly_reset_at IS NULL OR v_credit.hourly_reset_at < date_trunc('hour', now()) THEN UPDATE addon_credits SET hourly_used = 0, hourly_reset_at = date_trunc('hour', now()) + interval '1 hour' WHERE id = v_credit.id; @@ -2829,7 +2675,7 @@ BEGIN END IF; END IF; - -- Verifica expiração + -- Verifica expira????o IF v_credit.expires_at IS NOT NULL AND v_credit.expires_at < now() THEN RETURN jsonb_build_object('success', false, 'reason', 'credits_expired', 'balance', v_credit.balance); END IF; @@ -2846,7 +2692,7 @@ BEGIN updated_at = now() WHERE id = v_credit.id; - -- Registra transação + -- Registra transa????o INSERT INTO addon_transactions ( tenant_id, addon_type, type, amount, balance_before, balance_after, @@ -2866,17 +2712,15 @@ END; $$; -ALTER FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text) OWNER TO supabase_admin; - -- --- Name: FUNCTION debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text); Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: FUNCTION debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text); Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text) IS 'Debita 1 crédito de add-on. Verifica saldo, rate limits e expiração.'; +COMMENT ON FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text) IS 'Debita 1 cr??dito de add-on. Verifica saldo, rate limits e expira????o.'; -- --- Name: delete_commitment_full(uuid, uuid); Type: FUNCTION; Schema: public; Owner: postgres +-- Name: delete_commitment_full(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.delete_commitment_full(p_tenant_id uuid, p_commitment_id uuid) RETURNS jsonb @@ -2948,10 +2792,8 @@ 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 +-- Name: delete_determined_commitment(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.delete_determined_commitment(p_tenant_id uuid, p_commitment_id uuid) RETURNS jsonb @@ -3025,10 +2867,8 @@ 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 +-- Name: dev_list_auth_users(integer); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.dev_list_auth_users(p_limit integer DEFAULT 50) RETURNS TABLE(id uuid, email text, created_at timestamp with time zone) @@ -3036,7 +2876,7 @@ CREATE FUNCTION public.dev_list_auth_users(p_limit integer DEFAULT 50) RETURNS T SET search_path TO 'public', 'auth' AS $$ begin - -- só saas_admin pode ver + -- s?? saas_admin pode ver if not exists ( select 1 from public.profiles p @@ -3058,10 +2898,8 @@ end; $$; -ALTER FUNCTION public.dev_list_auth_users(p_limit integer) OWNER TO supabase_admin; - -- --- Name: dev_list_custom_users(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: dev_list_custom_users(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.dev_list_custom_users() RETURNS TABLE(user_id uuid, email text, created_at timestamp with time zone, global_role text, tenant_role text, tenant_id uuid, password_dev text, kind text) @@ -3112,10 +2950,8 @@ CREATE FUNCTION public.dev_list_custom_users() RETURNS TABLE(user_id uuid, email $$; -ALTER FUNCTION public.dev_list_custom_users() OWNER TO supabase_admin; - -- --- Name: dev_list_intent_leads(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: dev_list_intent_leads(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.dev_list_intent_leads() RETURNS TABLE(email text, last_intent_at timestamp with time zone, plan_key text, billing_interval text, status text, tenant_id uuid) @@ -3141,10 +2977,8 @@ CREATE FUNCTION public.dev_list_intent_leads() RETURNS TABLE(email text, last_in $$; -ALTER FUNCTION public.dev_list_intent_leads() OWNER TO supabase_admin; - -- --- Name: dev_public_debug_snapshot(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: dev_public_debug_snapshot(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.dev_public_debug_snapshot() RETURNS TABLE(users_total integer, tenants_total integer, intents_new_total integer, latest_intents jsonb) @@ -3183,10 +3017,8 @@ end; $_$; -ALTER FUNCTION public.dev_public_debug_snapshot() OWNER TO supabase_admin; - -- --- Name: ensure_personal_tenant(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: ensure_personal_tenant(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.ensure_personal_tenant() RETURNS uuid @@ -3219,10 +3051,8 @@ END; $$; -ALTER FUNCTION public.ensure_personal_tenant() OWNER TO supabase_admin; - -- --- Name: ensure_personal_tenant_for_user(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: ensure_personal_tenant_for_user(uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.ensure_personal_tenant_for_user(p_user_id uuid) RETURNS uuid @@ -3240,7 +3070,7 @@ begin raise exception 'Missing user id'; end if; - -- só considera tenant pessoal (kind='saas') + -- s?? considera tenant pessoal (kind='saas') select tm.tenant_id into v_existing from public.tenant_members tm @@ -3273,10 +3103,8 @@ end; $$; -ALTER FUNCTION public.ensure_personal_tenant_for_user(p_user_id uuid) OWNER TO supabase_admin; - -- --- Name: faq_votar(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: faq_votar(uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.faq_votar(faq_id uuid) RETURNS void @@ -3290,10 +3118,8 @@ CREATE FUNCTION public.faq_votar(faq_id uuid) RETURNS void $$; -ALTER FUNCTION public.faq_votar(faq_id uuid) OWNER TO supabase_admin; - -- --- Name: fix_all_subscription_mismatches(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: fix_all_subscription_mismatches(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.fix_all_subscription_mismatches() RETURNS void @@ -3314,10 +3140,8 @@ 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 +-- Name: fn_agenda_regras_semanais_no_overlap(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.fn_agenda_regras_semanais_no_overlap() RETURNS trigger @@ -3339,7 +3163,7 @@ begin 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.'; + raise exception 'Janela sobreposta: j?? existe uma regra ativa nesse intervalo.'; end if; return new; @@ -3347,10 +3171,81 @@ end; $$; -ALTER FUNCTION public.fn_agenda_regras_semanais_no_overlap() OWNER TO supabase_admin; +-- +-- Name: fn_document_signature_timeline(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.fn_document_signature_timeline() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_patient_id uuid; + v_tenant_id uuid; + v_doc_nome text; +BEGIN + IF NEW.status = 'assinado' AND (OLD.status IS NULL OR OLD.status <> 'assinado') THEN + SELECT d.patient_id, d.tenant_id, d.nome_original + INTO v_patient_id, v_tenant_id, v_doc_nome + FROM public.documents d + WHERE d.id = NEW.documento_id; + + IF v_patient_id IS NOT NULL THEN + INSERT INTO public.patient_timeline ( + patient_id, tenant_id, evento_tipo, + titulo, descricao, icone_cor, + link_ref_tipo, link_ref_id, + gerado_por, ocorrido_em + ) VALUES ( + v_patient_id, + v_tenant_id, + 'documento_assinado', + 'Documento assinado: ' || COALESCE(v_doc_nome, 'documento'), + 'Assinado por ' || COALESCE(NEW.signatario_nome, NEW.signatario_tipo), + 'green', + 'documento', + NEW.documento_id, + NEW.signatario_id, + NEW.assinado_em + ); + END IF; + END IF; + RETURN NEW; +END; +$$; + -- --- Name: get_financial_report(uuid, date, date, text); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: fn_documents_timeline_insert(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.fn_documents_timeline_insert() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + INSERT INTO public.patient_timeline ( + patient_id, tenant_id, evento_tipo, + titulo, descricao, icone_cor, + link_ref_tipo, link_ref_id, + gerado_por, ocorrido_em + ) VALUES ( + NEW.patient_id, + NEW.tenant_id, + 'documento_adicionado', + 'Documento adicionado: ' || COALESCE(NEW.nome_original, 'arquivo'), + 'Tipo: ' || COALESCE(NEW.tipo_documento, 'outro'), + 'blue', + 'documento', + NEW.id, + NEW.uploaded_by, + NEW.uploaded_at + ); + RETURN NEW; +END; +$$; + + +-- +-- Name: get_financial_report(uuid, date, date, text); Type: FUNCTION; Schema: public; Owner: - -- 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) @@ -3358,9 +3253,9 @@ CREATE FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, 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) + -- ?????? 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 @@ -3381,9 +3276,9 @@ CREATE FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, ) 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 + ELSE NULL -- group_by inv??lido ??? group_key NULL ??? retorno vazio END AS gkey, - -- Label legível (enriquecido via JOIN abaixo quando possível) + -- 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), @@ -3429,24 +3324,22 @@ CREATE FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, COUNT(*) AS count_records FROM base - WHERE gkey IS NOT NULL -- descarta p_group_by inválido + 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 +-- 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: - -- -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).'; +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 +-- Name: get_financial_summary(uuid, integer, integer); Type: FUNCTION; Schema: public; Owner: - -- 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) @@ -3454,12 +3347,12 @@ CREATE FUNCTION public.get_financial_summary(p_owner_id uuid, p_year integer, p_ SET search_path TO 'public' AS $$ SELECT - -- Receitas pagas no período + -- Receitas pagas no per??odo COALESCE(SUM(amount) FILTER ( WHERE type = 'receita' AND status = 'paid' ), 0) AS total_receitas, - -- Despesas pagas no período + -- Despesas pagas no per??odo COALESCE(SUM(amount) FILTER ( WHERE type = 'despesa' AND status = 'paid' ), 0) AS total_despesas, @@ -3469,7 +3362,7 @@ CREATE FUNCTION public.get_financial_summary(p_owner_id uuid, p_year integer, p_ WHERE status IN ('pending', 'overdue') ), 0) AS total_pendente, - -- Saldo líquido (receitas pagas − despesas pagas) + -- Saldo l??quido (receitas pagas ??? despesas pagas) COALESCE(SUM(amount) FILTER ( WHERE type = 'receita' AND status = 'paid' ), 0) @@ -3477,7 +3370,7 @@ CREATE FUNCTION public.get_financial_summary(p_owner_id uuid, p_year integer, p_ WHERE type = 'despesa' AND status = 'paid' ), 0) AS saldo_liquido, - -- Total repassado à clínica (apenas receitas pagas) + -- Total repassado ?? cl??nica (apenas receitas pagas) COALESCE(SUM(clinic_fee_amount) FILTER ( WHERE type = 'receita' AND status = 'paid' ), 0) AS total_repasse, @@ -3494,10 +3387,8 @@ CREATE FUNCTION public.get_financial_summary(p_owner_id uuid, p_year integer, p_ $$; -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 +-- Name: get_my_email(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.get_my_email() RETURNS text @@ -3510,10 +3401,8 @@ CREATE FUNCTION public.get_my_email() RETURNS text $$; -ALTER FUNCTION public.get_my_email() OWNER TO supabase_admin; - -- --- Name: guard_account_type_immutable(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: guard_account_type_immutable(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.guard_account_type_immutable() RETURNS trigger @@ -3521,7 +3410,7 @@ CREATE FUNCTION public.guard_account_type_immutable() RETURNS trigger AS $$ BEGIN IF OLD.account_type <> 'free' AND NEW.account_type IS DISTINCT FROM OLD.account_type THEN - RAISE EXCEPTION 'account_type é imutável após escolha (atual: "%" para tentativa: "%"). Para mudar de perfil, crie uma nova conta.', OLD.account_type, NEW.account_type + RAISE EXCEPTION 'account_type ?? imut??vel ap??s escolha (atual: "%" para tentativa: "%"). Para mudar de perfil, crie uma nova conta.', OLD.account_type, NEW.account_type USING ERRCODE = 'P0001'; END IF; RETURN NEW; @@ -3529,10 +3418,8 @@ END; $$; -ALTER FUNCTION public.guard_account_type_immutable() OWNER TO supabase_admin; - -- --- Name: guard_locked_commitment(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: guard_locked_commitment(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.guard_locked_commitment() RETURNS trigger @@ -3541,22 +3428,22 @@ CREATE FUNCTION public.guard_locked_commitment() RETURNS trigger begin if (old.is_locked = true) then if (tg_op = 'DELETE') then - raise exception 'Compromisso bloqueado não pode ser excluído.'; + 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.'; + raise exception 'Compromisso bloqueado n??o pode ser desativado.'; end if; - -- trava renomear (mantém o "Sessão" sempre igual) + -- 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.'; + raise exception 'Compromisso bloqueado n??o pode ser renomeado.'; end if; - -- se quiser travar descrição também, descomente: + -- 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.'; + -- raise exception 'Compromisso bloqueado n??o pode alterar descri????o.'; -- end if; end if; end if; @@ -3566,10 +3453,8 @@ 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 +-- Name: guard_no_change_core_plan_key(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.guard_no_change_core_plan_key() RETURNS trigger @@ -3578,7 +3463,7 @@ CREATE FUNCTION public.guard_no_change_core_plan_key() RETURNS trigger begin if old.key in ('clinic_free','clinic_pro','therapist_free','therapist_pro') and new.key is distinct from old.key then - raise exception 'Não é permitido alterar a key do plano padrão (%).', old.key + raise exception 'N??o ?? permitido alterar a key do plano padr??o (%).', old.key using errcode = 'P0001'; end if; @@ -3586,10 +3471,8 @@ begin end $$; -ALTER FUNCTION public.guard_no_change_core_plan_key() OWNER TO supabase_admin; - -- --- Name: guard_no_change_plan_target(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: guard_no_change_plan_target(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.guard_no_change_plan_target() RETURNS trigger @@ -3598,17 +3481,17 @@ CREATE FUNCTION public.guard_no_change_plan_target() RETURNS trigger declare v_bypass text; begin - -- bypass controlado por sessão/transação: - -- só passa se app.plan_migration_bypass = '1' + -- bypass controlado por sess??o/transa????o: + -- s?? passa se app.plan_migration_bypass = '1' v_bypass := current_setting('app.plan_migration_bypass', true); if v_bypass = '1' then return new; end if; - -- comportamento original (bloqueia qualquer mudança) + -- comportamento original (bloqueia qualquer mudan??a) if new.target is distinct from old.target then - raise exception 'Não é permitido alterar target do plano (%) de % para %.', + raise exception 'N??o ?? permitido alterar target do plano (%) de % para %.', old.key, old.target, new.target using errcode = 'P0001'; end if; @@ -3618,10 +3501,8 @@ end $$; -ALTER FUNCTION public.guard_no_change_plan_target() OWNER TO supabase_admin; - -- --- Name: guard_no_delete_core_plans(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: guard_no_delete_core_plans(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.guard_no_delete_core_plans() RETURNS trigger @@ -3629,7 +3510,7 @@ CREATE FUNCTION public.guard_no_delete_core_plans() RETURNS trigger AS $$ begin if old.key in ('clinic_free','clinic_pro','therapist_free','therapist_pro') then - raise exception 'Plano padrão (%) não pode ser removido.', old.key + raise exception 'Plano padr??o (%) n??o pode ser removido.', old.key using errcode = 'P0001'; end if; @@ -3637,10 +3518,8 @@ begin end $$; -ALTER FUNCTION public.guard_no_delete_core_plans() OWNER TO supabase_admin; - -- --- Name: guard_patient_cannot_own_tenant(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: guard_patient_cannot_own_tenant(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.guard_patient_cannot_own_tenant() RETURNS trigger @@ -3654,7 +3533,7 @@ BEGIN WHERE id = NEW.user_id; IF v_account_type = 'patient' AND NEW.role IN ('tenant_admin', 'therapist') THEN - RAISE EXCEPTION 'Usuário com perfil "patient" não pode ser proprietário ou terapeuta de um tenant. Se tornou profissional? Crie uma nova conta.' + RAISE EXCEPTION 'Usu??rio com perfil "patient" n??o pode ser propriet??rio ou terapeuta de um tenant. Se tornou profissional? Crie uma nova conta.' USING ERRCODE = 'P0001'; END IF; @@ -3663,10 +3542,8 @@ END; $$; -ALTER FUNCTION public.guard_patient_cannot_own_tenant() OWNER TO supabase_admin; - -- --- Name: guard_tenant_kind_immutable(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: guard_tenant_kind_immutable(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.guard_tenant_kind_immutable() RETURNS trigger @@ -3674,7 +3551,7 @@ CREATE FUNCTION public.guard_tenant_kind_immutable() RETURNS trigger AS $$ BEGIN IF NEW.kind IS DISTINCT FROM OLD.kind THEN - RAISE EXCEPTION 'tenants.kind é imutável após criação. Tentativa de alterar "%" para "%".', OLD.kind, NEW.kind + RAISE EXCEPTION 'tenants.kind ?? imut??vel ap??s cria????o. Tentativa de alterar "%" para "%".', OLD.kind, NEW.kind USING ERRCODE = 'P0001'; END IF; RETURN NEW; @@ -3682,10 +3559,8 @@ END; $$; -ALTER FUNCTION public.guard_tenant_kind_immutable() OWNER TO supabase_admin; - -- --- Name: handle_new_user(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: handle_new_user(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.handle_new_user() RETURNS trigger @@ -3701,10 +3576,8 @@ END; $$; -ALTER FUNCTION public.handle_new_user() OWNER TO supabase_admin; - -- --- Name: handle_new_user_create_personal_tenant(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: handle_new_user_create_personal_tenant(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.handle_new_user_create_personal_tenant() RETURNS trigger @@ -3717,10 +3590,8 @@ END; $$; -ALTER FUNCTION public.handle_new_user_create_personal_tenant() OWNER TO supabase_admin; - -- --- Name: has_feature(uuid, text); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: has_feature(uuid, text); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.has_feature(p_owner_id uuid, p_feature_key text) RETURNS boolean @@ -3735,10 +3606,8 @@ CREATE FUNCTION public.has_feature(p_owner_id uuid, p_feature_key text) RETURNS $$; -ALTER FUNCTION public.has_feature(p_owner_id uuid, p_feature_key text) OWNER TO supabase_admin; - -- --- Name: is_clinic_tenant(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: is_clinic_tenant(uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.is_clinic_tenant(_tenant_id uuid) RETURNS boolean @@ -3752,10 +3621,8 @@ CREATE FUNCTION public.is_clinic_tenant(_tenant_id uuid) RETURNS boolean $$; -ALTER FUNCTION public.is_clinic_tenant(_tenant_id uuid) OWNER TO supabase_admin; - -- --- Name: is_saas_admin(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: is_saas_admin(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.is_saas_admin() RETURNS boolean @@ -3768,10 +3635,8 @@ CREATE FUNCTION public.is_saas_admin() RETURNS boolean $$; -ALTER FUNCTION public.is_saas_admin() OWNER TO supabase_admin; - -- --- Name: is_tenant_admin(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: is_tenant_admin(uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.is_tenant_admin(p_tenant_id uuid) RETURNS boolean @@ -3790,10 +3655,8 @@ CREATE FUNCTION public.is_tenant_admin(p_tenant_id uuid) RETURNS boolean $$; -ALTER FUNCTION public.is_tenant_admin(p_tenant_id uuid) OWNER TO supabase_admin; - -- --- Name: is_tenant_member(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: is_tenant_member(uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.is_tenant_member(_tenant_id uuid) RETURNS boolean @@ -3809,10 +3672,8 @@ CREATE FUNCTION public.is_tenant_member(_tenant_id uuid) RETURNS boolean $$; -ALTER FUNCTION public.is_tenant_member(_tenant_id uuid) OWNER TO supabase_admin; - -- --- Name: is_therapist_tenant(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: is_therapist_tenant(uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.is_therapist_tenant(_tenant_id uuid) RETURNS boolean @@ -3825,10 +3686,8 @@ CREATE FUNCTION public.is_therapist_tenant(_tenant_id uuid) RETURNS boolean $$; -ALTER FUNCTION public.is_therapist_tenant(_tenant_id uuid) OWNER TO supabase_admin; - -- --- Name: jwt_email(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: jwt_email(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.jwt_email() RETURNS text @@ -3838,10 +3697,8 @@ CREATE FUNCTION public.jwt_email() RETURNS text $$; -ALTER FUNCTION public.jwt_email() OWNER TO supabase_admin; - -- --- Name: list_financial_records(uuid, integer, integer, text, text, uuid, integer, integer); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: list_financial_records(uuid, integer, integer, text, text, uuid, integer, integer); Type: FUNCTION; Schema: public; Owner: - -- 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 @@ -3863,10 +3720,8 @@ CREATE FUNCTION public.list_financial_records(p_owner_id uuid, p_year integer DE $$; -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 +-- Name: mark_as_paid(uuid, text); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.mark_as_paid(p_financial_record_id uuid, p_payment_method text) RETURNS SETOF public.financial_records @@ -3876,7 +3731,7 @@ CREATE FUNCTION public.mark_as_paid(p_financial_record_id uuid, p_payment_method DECLARE v_record public.financial_records%ROWTYPE; BEGIN - -- Garante que o registro pertence ao usuário autenticado (RLS não aplica em SECURITY DEFINER) + -- 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 @@ -3884,11 +3739,11 @@ BEGIN AND deleted_at IS NULL; IF NOT FOUND THEN - RAISE EXCEPTION 'Registro financeiro não encontrado ou sem permissão.'; + 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.'; + RAISE EXCEPTION 'Apenas cobran??as pendentes ou vencidas podem ser marcadas como pagas.'; END IF; UPDATE public.financial_records @@ -3904,10 +3759,8 @@ 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 +-- Name: mark_payout_as_paid(uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.mark_payout_as_paid(p_payout_id uuid) RETURNS public.therapist_payouts @@ -3923,18 +3776,18 @@ BEGIN WHERE id = p_payout_id; IF NOT FOUND THEN - RAISE EXCEPTION 'Repasse não encontrado: %', p_payout_id; + RAISE EXCEPTION 'Repasse n??o encontrado: %', p_payout_id; END IF; - -- Verifica permissão: apenas tenant_admin do tenant do repasse + -- 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.'; + 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.', + 'Repasse j?? est?? com status ''%''. Apenas repasses pendentes podem ser pagos.', v_payout.status; END IF; @@ -3952,17 +3805,15 @@ 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 +-- Name: FUNCTION mark_payout_as_paid(p_payout_id uuid); Type: COMMENT; Schema: public; Owner: - -- 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 +-- Name: my_tenants(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.my_tenants() RETURNS TABLE(tenant_id uuid, role text, status text, kind text) @@ -3979,10 +3830,8 @@ CREATE FUNCTION public.my_tenants() RETURNS TABLE(tenant_id uuid, role text, sta $$; -ALTER FUNCTION public.my_tenants() OWNER TO supabase_admin; - -- --- Name: notice_track_click(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: notice_track_click(uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.notice_track_click(p_notice_id uuid) RETURNS void @@ -3996,10 +3845,8 @@ 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 +-- Name: notice_track_view(uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.notice_track_view(p_notice_id uuid) RETURNS void @@ -4013,10 +3860,8 @@ 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 +-- Name: notify_on_intake(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.notify_on_intake() RETURNS trigger @@ -4051,10 +3896,8 @@ END; $$; -ALTER FUNCTION public.notify_on_intake() OWNER TO supabase_admin; - -- --- Name: notify_on_scheduling(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: notify_on_scheduling(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.notify_on_scheduling() RETURNS trigger @@ -4062,13 +3905,11 @@ CREATE FUNCTION public.notify_on_scheduling() RETURNS trigger 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; $$; + '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 +-- Name: notify_on_session_status(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.notify_on_session_status() RETURNS trigger @@ -4100,8 +3941,8 @@ BEGIN 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'), + '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)) ) @@ -4113,10 +3954,8 @@ 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 +-- Name: on_new_user_seed_patient_groups(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.on_new_user_seed_patient_groups() RETURNS trigger @@ -4130,10 +3969,8 @@ CREATE FUNCTION public.on_new_user_seed_patient_groups() RETURNS trigger $$; -ALTER FUNCTION public.on_new_user_seed_patient_groups() OWNER TO supabase_admin; - -- --- Name: patients_validate_member_consistency(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: patients_validate_member_consistency(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.patients_validate_member_consistency() RETURNS trigger @@ -4184,10 +4021,8 @@ 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 +-- Name: patients_validate_responsible_member_tenant(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.patients_validate_responsible_member_tenant() RETURNS trigger @@ -4217,10 +4052,8 @@ end; $$; -ALTER FUNCTION public.patients_validate_responsible_member_tenant() OWNER TO supabase_admin; - -- --- Name: populate_notification_queue(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: populate_notification_queue(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.populate_notification_queue() RETURNS void @@ -4247,7 +4080,7 @@ BEGIN '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') + 'titulo', COALESCE(ae.titulo, 'Sess??o') ), CASE ch.channel WHEN 'whatsapp' THEN COALESCE(p.telefone, '') @@ -4315,10 +4148,8 @@ END; $$; -ALTER FUNCTION public.populate_notification_queue() OWNER TO supabase_admin; - -- --- Name: prevent_promoting_to_system(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: prevent_promoting_to_system(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.prevent_promoting_to_system() RETURNS trigger @@ -4326,17 +4157,15 @@ CREATE FUNCTION public.prevent_promoting_to_system() RETURNS trigger AS $$ begin if new.is_system = true and old.is_system is distinct from true then - raise exception 'Não é permitido transformar um grupo comum em grupo do sistema.'; + raise exception 'N??o ?? permitido transformar um grupo comum em grupo do sistema.'; end if; return new; end; $$; -ALTER FUNCTION public.prevent_promoting_to_system() OWNER TO supabase_admin; - -- --- Name: prevent_saas_membership(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: prevent_saas_membership(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.prevent_saas_membership() RETURNS trigger @@ -4357,27 +4186,25 @@ END; $$; -ALTER FUNCTION public.prevent_saas_membership() OWNER TO supabase_admin; - -- --- Name: prevent_system_group_changes(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: prevent_system_group_changes(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.prevent_system_group_changes() RETURNS trigger LANGUAGE plpgsql AS $$ begin - -- Se for grupo do sistema, regras rígidas: + -- 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.'; + 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 + -- 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 @@ -4388,16 +4215,16 @@ begin 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.'; + 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 + -- chegou aqui: s?? tenant_id/updated_at mudaram -> ok return new; end if; end if; - -- não-system: deixa passar + -- n??o-system: deixa passar if tg_op = 'DELETE' then return old; end if; @@ -4407,10 +4234,8 @@ 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 +-- Name: provision_account_tenant(uuid, text, text); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text DEFAULT NULL::text) RETURNS uuid @@ -4422,7 +4247,7 @@ DECLARE v_name text; BEGIN IF p_kind NOT IN ('therapist', 'clinic_coworking', 'clinic_reception', 'clinic_full') THEN - RAISE EXCEPTION 'kind inválido: "%". Use: therapist, clinic_coworking, clinic_reception, clinic_full.', p_kind + RAISE EXCEPTION 'kind inv??lido: "%". Use: therapist, clinic_coworking, clinic_reception, clinic_full.', p_kind USING ERRCODE = 'P0001'; END IF; @@ -4437,7 +4262,7 @@ BEGIN AND tm.status = 'active' AND t.kind = p_kind ) THEN - RAISE EXCEPTION 'Usuário já possui um tenant do tipo "%".', p_kind + RAISE EXCEPTION 'Usu??rio j?? possui um tenant do tipo "%".', p_kind USING ERRCODE = 'P0001'; END IF; @@ -4470,17 +4295,15 @@ END; $$; -ALTER FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text) OWNER TO supabase_admin; - -- --- Name: FUNCTION provision_account_tenant(p_user_id uuid, p_kind text, p_name text); Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: FUNCTION provision_account_tenant(p_user_id uuid, p_kind text, p_name text); Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text) IS 'Cria o tenant do tipo correto e atualiza account_type no profile. Chamar no onboarding após escolha/pagamento de plano therapist ou clinic. p_kind: therapist | clinic_coworking | clinic_reception | clinic_full'; +COMMENT ON FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text) IS 'Cria o tenant do tipo correto e atualiza account_type no profile. Chamar no onboarding ap??s escolha/pagamento de plano therapist ou clinic. p_kind: therapist | clinic_coworking | clinic_reception | clinic_full'; -- --- Name: reactivate_subscription(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: reactivate_subscription(uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.reactivate_subscription(p_subscription_id uuid) RETURNS public.subscriptions @@ -4499,7 +4322,7 @@ begin for update; if not found then - raise exception 'Subscription não encontrada'; + raise exception 'Subscription n??o encontrada'; end if; if v_sub.status = 'active' then @@ -4546,7 +4369,7 @@ begin v_sub.plan_id, v_sub.plan_id, auth.uid(), - 'Reativação manual via admin', + 'Reativa????o manual via admin', 'admin_panel', jsonb_build_object('previous_status', 'canceled') ); @@ -4564,10 +4387,8 @@ end; $$; -ALTER FUNCTION public.reactivate_subscription(p_subscription_id uuid) OWNER TO supabase_admin; - -- --- Name: rebuild_owner_entitlements(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: rebuild_owner_entitlements(uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.rebuild_owner_entitlements(p_owner_id uuid) RETURNS void @@ -4589,7 +4410,7 @@ begin delete from public.owner_feature_entitlements e where e.owner_id = p_owner_id; - -- Se não tem assinatura ativa, acabou + -- Se n??o tem assinatura ativa, acabou if v_plan_id is null then return; end if; @@ -4609,10 +4430,8 @@ end; $$; -ALTER FUNCTION public.rebuild_owner_entitlements(p_owner_id uuid) OWNER TO supabase_admin; - -- --- Name: revoke_support_session(text); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: revoke_support_session(text); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.revoke_support_session(p_token text) RETURNS boolean @@ -4625,7 +4444,7 @@ DECLARE BEGIN v_admin_id := auth.uid(); IF v_admin_id IS NULL THEN - RAISE EXCEPTION 'Não autenticado.' USING ERRCODE = 'P0001'; + RAISE EXCEPTION 'N??o autenticado.' USING ERRCODE = 'P0001'; END IF; SELECT role INTO v_role FROM public.profiles WHERE id = v_admin_id; @@ -4642,10 +4461,8 @@ 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 +-- Name: rotate_patient_invite_token(text); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.rotate_patient_invite_token(p_new_token text) RETURNS uuid @@ -4656,13 +4473,13 @@ declare v_uid uuid; v_id uuid; begin - -- pega o usuário logado + -- pega o usu??rio logado v_uid := auth.uid(); if v_uid is null then - raise exception 'Usuário não autenticado'; + raise exception 'Usu??rio n??o autenticado'; end if; - -- desativa tokens antigos ativos do usuário + -- desativa tokens antigos ativos do usu??rio update public.patient_invites set active = false where owner_id = v_uid @@ -4678,10 +4495,8 @@ 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 +-- Name: saas_votar_doc(uuid, boolean); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.saas_votar_doc(p_doc_id uuid, p_util boolean) RETURNS jsonb @@ -4692,16 +4507,16 @@ declare v_voto_antigo boolean; begin if v_uid is null then - raise exception 'Não autenticado'; + raise exception 'N??o autenticado'; end if; - -- Verifica se já votou + -- 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) + -- 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; @@ -4743,22 +4558,20 @@ 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 +-- Name: safe_delete_patient(uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.safe_delete_patient(p_patient_id uuid) RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER AS $$ BEGIN - -- Bloqueia se houver histórico + -- 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.' + '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; @@ -4776,7 +4589,7 @@ BEGIN RETURN jsonb_build_object( 'ok', false, 'error', 'forbidden', - 'message', 'Sem permissão para excluir este paciente.' + 'message', 'Sem permiss??o para excluir este paciente.' ); END IF; @@ -4787,10 +4600,8 @@ 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 +-- Name: sanitize_phone_br(text); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.sanitize_phone_br(raw_phone text) RETURNS text @@ -4806,10 +4617,8 @@ CREATE FUNCTION public.sanitize_phone_br(raw_phone text) RETURNS text 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 +-- Name: seed_default_financial_categories(uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.seed_default_financial_categories(p_user_id uuid) RETURNS void @@ -4819,25 +4628,56 @@ CREATE FUNCTION public.seed_default_financial_categories(p_user_id uuid) RETURNS 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, '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, '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_default_patient_groups(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.seed_default_patient_groups(p_tenant_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_owner_id uuid; +BEGIN + -- busca o owner (tenant_admin) do tenant + SELECT user_id INTO v_owner_id + FROM public.tenant_members + WHERE tenant_id = p_tenant_id + AND role = 'tenant_admin' + AND status = 'active' + LIMIT 1; + + IF v_owner_id IS NULL THEN + RETURN; + END IF; + + INSERT INTO public.patient_groups (owner_id, nome, cor, is_system, tenant_id) + VALUES + (v_owner_id, 'Crian??as', '#60a5fa', true, p_tenant_id), + (v_owner_id, 'Adolescentes', '#a78bfa', true, p_tenant_id), + (v_owner_id, 'Idosos', '#34d399', true, p_tenant_id) + ON CONFLICT (owner_id, nome) DO NOTHING; +END; +$$; + -- --- Name: seed_determined_commitments(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: seed_determined_commitments(uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.seed_determined_commitments(p_tenant_id uuid) RETURNS void @@ -4846,7 +4686,7 @@ CREATE FUNCTION public.seed_determined_commitments(p_tenant_id uuid) RETURNS voi declare v_id uuid; begin - -- Sessão (locked + sempre ativa) + -- 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' @@ -4854,7 +4694,7 @@ begin 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'); + (p_tenant_id, true, 'session', true, true, 'Sess??o', 'Sess??o com paciente'); end if; -- Leitura @@ -4868,7 +4708,7 @@ begin (p_tenant_id, true, 'reading', false, true, 'Leitura', 'Praticar leitura'); end if; - -- Supervisão + -- 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' @@ -4876,10 +4716,10 @@ begin 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'); + (p_tenant_id, true, 'supervision', false, true, 'Supervis??o', 'Supervis??o'); end if; - -- Aula ✅ (corrigido) + -- 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' @@ -4890,7 +4730,7 @@ begin (p_tenant_id, true, 'class', false, false, 'Aula', 'Dar aula'); end if; - -- Análise pessoal + -- 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' @@ -4898,11 +4738,11 @@ begin 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'); + (p_tenant_id, true, 'analysis', false, true, 'An??lise Pessoal', 'Minha an??lise pessoal'); end if; -- ------------------------------------------------------- - -- Campos padrão (idempotentes por (commitment_id, key)) + -- Campos padr??o (idempotentes por (commitment_id, key)) -- ------------------------------------------------------- -- Leitura @@ -4924,11 +4764,11 @@ begin 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); + values (p_tenant_id, v_id, 'notes', 'Observa????o', 'textarea', false, 30); end if; end if; - -- Supervisão + -- 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' @@ -4947,7 +4787,7 @@ begin 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); + values (p_tenant_id, v_id, 'notes', 'Observa????o', 'textarea', false, 30); end if; end if; @@ -4970,11 +4810,11 @@ begin 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); + values (p_tenant_id, v_id, 'notes', 'Observa????o', 'textarea', false, 30); end if; end if; - -- Análise + -- 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' @@ -4993,17 +4833,15 @@ begin 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); + 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 +-- Name: set_insurance_plans_updated_at(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.set_insurance_plans_updated_at() RETURNS trigger @@ -5012,10 +4850,22 @@ CREATE FUNCTION public.set_insurance_plans_updated_at() RETURNS trigger BEGIN NEW.updated_at = now(); RETURN NEW; END; $$; -ALTER FUNCTION public.set_insurance_plans_updated_at() OWNER TO supabase_admin; +-- +-- Name: set_medicos_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_medicos_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + -- --- Name: set_owner_id(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: set_owner_id(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.set_owner_id() RETURNS trigger @@ -5030,10 +4880,8 @@ end; $$; -ALTER FUNCTION public.set_owner_id() OWNER TO supabase_admin; - -- --- Name: set_services_updated_at(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: set_services_updated_at(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.set_services_updated_at() RETURNS trigger @@ -5046,17 +4894,15 @@ 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 +-- Name: set_tenant_feature_exception(uuid, text, boolean, text); Type: FUNCTION; Schema: public; Owner: - -- 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 LANGUAGE plpgsql SECURITY DEFINER AS $$ begin - -- ✅ Só owner ou admin do tenant podem alterar features + -- ??? S?? owner ou admin do tenant podem alterar features if not exists ( select 1 from public.tenant_members where tenant_id = p_tenant_id @@ -5081,10 +4927,8 @@ end; $$; -ALTER FUNCTION public.set_tenant_feature_exception(p_tenant_id uuid, p_feature_key text, p_enabled boolean, p_reason text) OWNER TO supabase_admin; - -- --- Name: set_updated_at(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: set_updated_at(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.set_updated_at() RETURNS trigger @@ -5097,10 +4941,8 @@ END; $$; -ALTER FUNCTION public.set_updated_at() OWNER TO supabase_admin; - -- --- Name: set_updated_at_recurrence(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: set_updated_at_recurrence(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.set_updated_at_recurrence() RETURNS trigger @@ -5110,10 +4952,8 @@ 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 +-- Name: split_recurrence_at(uuid, date); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.split_recurrence_at(p_recurrence_id uuid, p_from_date date) RETURNS uuid @@ -5130,7 +4970,7 @@ BEGIN WHERE id = p_recurrence_id; IF NOT FOUND THEN - RAISE EXCEPTION 'recurrence_rule % não encontrada', p_recurrence_id; + RAISE EXCEPTION 'recurrence_rule % n??o encontrada', p_recurrence_id; END IF; -- encerra a regra antiga na data anterior @@ -5164,10 +5004,8 @@ 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 +-- Name: subscription_intents_view_insert(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.subscription_intents_view_insert() RETURNS trigger @@ -5182,12 +5020,12 @@ begin where p.key = new.plan_key; if v_plan_id is null then - raise exception 'Plano inválido: plan_key=%', new.plan_key; + raise exception 'Plano inv??lido: plan_key=%', new.plan_key; end if; if lower(v_target) = 'clinic' then if new.tenant_id is null then - raise exception 'Intenção clinic exige tenant_id.'; + raise exception 'Inten????o clinic exige tenant_id.'; end if; insert into public.subscription_intents_tenant ( @@ -5207,7 +5045,7 @@ begin return new; end if; - -- therapist ou supervisor → tabela personal + -- therapist ou supervisor ??? tabela personal if lower(v_target) in ('therapist', 'supervisor') then insert into public.subscription_intents_personal ( id, user_id, created_by_user_id, email, @@ -5226,15 +5064,13 @@ begin return new; end if; - raise exception 'Target de plano não suportado: %', v_target; + raise exception 'Target de plano n??o suportado: %', v_target; end; $$; -ALTER FUNCTION public.subscription_intents_view_insert() OWNER TO supabase_admin; - -- --- Name: subscriptions_validate_scope(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: subscriptions_validate_scope(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.subscriptions_validate_scope() RETURNS trigger @@ -5248,7 +5084,7 @@ BEGIN WHERE p.id = NEW.plan_id; IF v_target IS NULL THEN - RAISE EXCEPTION 'Plano inválido (target nulo).'; + RAISE EXCEPTION 'Plano inv??lido (target nulo).'; END IF; IF v_target = 'clinic' THEN @@ -5256,13 +5092,13 @@ BEGIN RAISE EXCEPTION 'Assinatura clinic exige tenant_id.'; END IF; IF NEW.user_id IS NOT NULL THEN - RAISE EXCEPTION 'Assinatura clinic não pode ter user_id (XOR).'; + RAISE EXCEPTION 'Assinatura clinic n??o pode ter user_id (XOR).'; END IF; ELSIF v_target IN ('therapist', 'supervisor') THEN - -- supervisor é pessoal como therapist + -- supervisor ?? pessoal como therapist IF NEW.tenant_id IS NOT NULL THEN - RAISE EXCEPTION 'Assinatura % não deve ter tenant_id.', v_target; + RAISE EXCEPTION 'Assinatura % n??o deve ter tenant_id.', v_target; END IF; IF NEW.user_id IS NULL THEN RAISE EXCEPTION 'Assinatura % exige user_id.', v_target; @@ -5270,14 +5106,14 @@ BEGIN ELSIF v_target = 'patient' THEN IF NEW.tenant_id IS NOT NULL THEN - RAISE EXCEPTION 'Assinatura patient não deve ter tenant_id.'; + RAISE EXCEPTION 'Assinatura patient n??o deve ter tenant_id.'; END IF; IF NEW.user_id IS NULL THEN RAISE EXCEPTION 'Assinatura patient exige user_id.'; END IF; ELSE - RAISE EXCEPTION 'Target de plano inválido: %', v_target; + RAISE EXCEPTION 'Target de plano inv??lido: %', v_target; END IF; RETURN NEW; @@ -5285,10 +5121,8 @@ END; $$; -ALTER FUNCTION public.subscriptions_validate_scope() OWNER TO supabase_admin; - -- --- Name: sync_busy_mirror_agenda_eventos(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: sync_busy_mirror_agenda_eventos(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.sync_busy_mirror_agenda_eventos() RETURNS trigger @@ -5300,7 +5134,7 @@ declare is_personal boolean; should_mirror boolean; begin - -- Anti-recursão: espelho não espelha + -- Anti-recurs??o: espelho n??o espelha if (tg_op <> 'DELETE') then if new.mirror_of_event_id is not null then return new; @@ -5311,7 +5145,7 @@ begin end if; end if; - -- Define se é pessoal e se deve espelhar + -- 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')); @@ -5320,7 +5154,7 @@ begin should_mirror := (new.visibility_scope in ('busy_only','private')); end if; - -- Se não é pessoal, não faz nada + -- Se n??o ?? pessoal, n??o faz nada if not is_personal then if (tg_op = 'DELETE') then return old; @@ -5338,7 +5172,7 @@ begin end if; -- INSERT/UPDATE: - -- Se não deve espelhar, remove espelhos e sai + -- 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 @@ -5347,7 +5181,7 @@ begin return new; end if; - -- Para cada clínica onde o usuário é therapist active, cria/atualiza o "Ocupado" + -- 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 @@ -5402,7 +5236,7 @@ begin updated_at = now(); end loop; - -- Limpa espelhos de clínicas onde o vínculo therapist active não existe mais + -- 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' @@ -5420,10 +5254,8 @@ 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 +-- Name: sync_overdue_financial_records(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.sync_overdue_financial_records() RETURNS integer @@ -5448,17 +5280,15 @@ 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 +-- Name: FUNCTION sync_overdue_financial_records(); Type: COMMENT; Schema: public; Owner: - -- 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 +-- Name: tenant_accept_invite(uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.tenant_accept_invite(p_token uuid) RETURNS jsonb @@ -5476,7 +5306,7 @@ begin raise exception 'not_authenticated' using errcode = 'P0001'; end if; - -- 2) pega email real do usuário logado sem depender do JWT claim + -- 2) pega email real do usu??rio logado sem depender do JWT claim select u.email into v_email from auth.users u @@ -5497,7 +5327,7 @@ begin raise exception 'invite_not_found' using errcode = 'P0001'; end if; - -- 4) validações de estado + -- 4) valida????es de estado if v_invite.revoked_at is not null then raise exception 'invite_revoked' using errcode = 'P0001'; end if; @@ -5529,7 +5359,7 @@ begin role = excluded.role, status = 'active'; - -- 8) retorno útil pro front (você já tenta ler tenant_id no AcceptInvitePage) + -- 8) retorno ??til pro front (voc?? j?? tenta ler tenant_id no AcceptInvitePage) return jsonb_build_object( 'ok', true, 'tenant_id', v_invite.tenant_id, @@ -5539,10 +5369,8 @@ end; $$; -ALTER FUNCTION public.tenant_accept_invite(p_token uuid) OWNER TO supabase_admin; - -- --- Name: tenant_members; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: tenant_members; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.tenant_members ( @@ -5555,10 +5383,8 @@ CREATE TABLE public.tenant_members ( ); -ALTER TABLE public.tenant_members OWNER TO supabase_admin; - -- --- Name: tenant_add_member_by_email(uuid, text, text); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: tenant_add_member_by_email(uuid, text, text); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.tenant_add_member_by_email(p_tenant_id uuid, p_email text, p_role text DEFAULT 'therapist'::text) RETURNS public.tenant_members @@ -5572,17 +5398,17 @@ declare v_email text; begin if p_tenant_id is null then - raise exception 'tenant_id é obrigatório'; + raise exception 'tenant_id ?? obrigat??rio'; end if; v_email := lower(trim(coalesce(p_email, ''))); if v_email = '' then - raise exception 'email é obrigatório'; + raise exception 'email ?? obrigat??rio'; end if; -- valida role permitida if p_role not in ('tenant_admin','therapist','secretary','patient') then - raise exception 'role inválida: %', p_role; + raise exception 'role inv??lida: %', p_role; end if; -- apenas admin do tenant (role real no banco) @@ -5596,10 +5422,10 @@ begin ) into v_is_admin; if not v_is_admin then - raise exception 'sem permissão: apenas admin da clínica pode adicionar membros'; + raise exception 'sem permiss??o: apenas admin da cl??nica pode adicionar membros'; end if; - -- acha usuário pelo e-mail no Supabase Auth + -- acha usu??rio pelo e-mail no Supabase Auth select u.id into v_target_uid from auth.users u @@ -5607,7 +5433,7 @@ begin limit 1; if v_target_uid is null then - raise exception 'nenhum usuário encontrado com este e-mail'; + raise exception 'nenhum usu??rio encontrado com este e-mail'; end if; -- cria ou reativa membro @@ -5624,10 +5450,8 @@ end; $$; -ALTER FUNCTION public.tenant_add_member_by_email(p_tenant_id uuid, p_email text, p_role text) OWNER TO supabase_admin; - -- --- Name: tenant_feature_allowed(uuid, text); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: tenant_feature_allowed(uuid, text); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.tenant_feature_allowed(p_tenant_id uuid, p_feature_key text) RETURNS boolean @@ -5643,10 +5467,8 @@ CREATE FUNCTION public.tenant_feature_allowed(p_tenant_id uuid, p_feature_key te $$; -ALTER FUNCTION public.tenant_feature_allowed(p_tenant_id uuid, p_feature_key text) OWNER TO supabase_admin; - -- --- Name: tenant_feature_enabled(uuid, text); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: tenant_feature_enabled(uuid, text); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.tenant_feature_enabled(p_tenant_id uuid, p_feature_key text) RETURNS boolean @@ -5661,10 +5483,8 @@ CREATE FUNCTION public.tenant_feature_enabled(p_tenant_id uuid, p_feature_key te $$; -ALTER FUNCTION public.tenant_feature_enabled(p_tenant_id uuid, p_feature_key text) OWNER TO supabase_admin; - -- --- Name: tenant_features_guard_with_plan(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: tenant_features_guard_with_plan(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.tenant_features_guard_with_plan() RETURNS trigger @@ -5673,7 +5493,7 @@ CREATE FUNCTION public.tenant_features_guard_with_plan() RETURNS trigger declare v_allowed boolean; begin - -- só valida quando está habilitando + -- s?? valida quando est?? habilitando if new.enabled is distinct from true then return new; end if; @@ -5689,7 +5509,7 @@ begin into v_allowed; if not v_allowed then - raise exception 'Feature % não permitida pelo plano atual do tenant %.', + raise exception 'Feature % n??o permitida pelo plano atual do tenant %.', new.feature_key, new.tenant_id using errcode = 'P0001'; end if; @@ -5699,10 +5519,8 @@ end; $$; -ALTER FUNCTION public.tenant_features_guard_with_plan() OWNER TO supabase_admin; - -- --- Name: tenant_has_feature(uuid, text); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: tenant_has_feature(uuid, text); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) RETURNS boolean @@ -5726,10 +5544,8 @@ CREATE FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) RETURN $$; -ALTER FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) OWNER TO supabase_admin; - -- --- Name: tenant_invite_member_by_email(uuid, text, text); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: tenant_invite_member_by_email(uuid, text, text); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) RETURNS uuid @@ -5742,9 +5558,9 @@ declare v_token uuid; v_updated int; begin - -- validações básicas + -- valida????es b??sicas if p_tenant_id is null then - raise exception 'tenant_id inválido' using errcode = 'P0001'; + raise exception 'tenant_id inv??lido' using errcode = 'P0001'; end if; v_email := lower(trim(coalesce(p_email, ''))); @@ -5754,16 +5570,16 @@ begin -- role permitido (ajuste se quiser) if p_role is null or p_role not in ('therapist', 'secretary') then - raise exception 'Role inválido (use therapist/secretary)' using errcode = 'P0001'; + raise exception 'Role inv??lido (use therapist/secretary)' using errcode = 'P0001'; end if; - -- ✅ bloqueio: auto-convite + -- ??? bloqueio: auto-convite v_my_email := public.get_my_email(); if v_my_email is not null and v_email = v_my_email then - raise exception 'Você não pode convidar o seu próprio email.' using errcode = 'P0001'; + raise exception 'Voc?? n??o pode convidar o seu pr??prio email.' using errcode = 'P0001'; end if; - -- ✅ bloqueio: já é membro ativo do tenant + -- ??? bloqueio: j?? ?? membro ativo do tenant if exists ( select 1 from tenant_members tm @@ -5772,10 +5588,10 @@ begin and tm.status = 'active' and lower(au.email) = v_email ) then - raise exception 'Este email já está vinculado a esta clínica.' using errcode = 'P0001'; + raise exception 'Este email j?? est?? vinculado a esta cl??nica.' using errcode = 'P0001'; end if; - -- ✅ permissão: só admin do tenant pode convidar + -- ??? permiss??o: s?? admin do tenant pode convidar if not exists ( select 1 from tenant_members me @@ -5784,10 +5600,10 @@ begin and me.status = 'active' and me.role in ('tenant_admin','clinic_admin') ) then - raise exception 'Sem permissão para convidar membros.' using errcode = 'P0001'; + raise exception 'Sem permiss??o para convidar membros.' using errcode = 'P0001'; end if; - -- Gera token (reenvio simples / regeneração) + -- Gera token (reenvio simples / regenera????o) v_token := gen_random_uuid(); -- 1) tenta "regerar" um convite pendente existente (mesmo email) @@ -5805,7 +5621,7 @@ begin get diagnostics v_updated = row_count; - -- 2) se não atualizou nada, cria convite novo + -- 2) se n??o atualizou nada, cria convite novo if v_updated = 0 then insert into tenant_invites (tenant_id, email, role, token, created_at, expires_at) values (p_tenant_id, v_email, p_role, v_token, now(), now() + interval '7 days'); @@ -5816,10 +5632,8 @@ end; $$; -ALTER FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) OWNER TO supabase_admin; - -- --- Name: tenant_reactivate_member(uuid, uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: tenant_reactivate_member(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid) RETURNS void @@ -5848,10 +5662,8 @@ end; $$; -ALTER FUNCTION public.tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid) OWNER TO supabase_admin; - -- --- Name: tenant_remove_member(uuid, uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: tenant_remove_member(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid) RETURNS void @@ -5874,7 +5686,7 @@ begin raise exception 'cannot_remove_self'; end if; - -- pega role atual do membro (se não existir, erro) + -- pega role atual do membro (se n??o existir, erro) select role into v_role from public.tenant_members where tenant_id = p_tenant_id @@ -5884,7 +5696,7 @@ begin raise exception 'membership_not_found'; end if; - -- trava: se for therapist, não pode remover com eventos futuros + -- trava: se for therapist, n??o pode remover com eventos futuros if v_role = 'therapist' then if exists ( select 1 @@ -5911,10 +5723,8 @@ end; $$; -ALTER FUNCTION public.tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid) OWNER TO supabase_admin; - -- --- Name: tenant_remove_member_soft(uuid, uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: tenant_remove_member_soft(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid) RETURNS void @@ -5947,10 +5757,8 @@ end; $$; -ALTER FUNCTION public.tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid) OWNER TO supabase_admin; - -- --- Name: tenant_revoke_invite(uuid, text, text); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: tenant_revoke_invite(uuid, text, text); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text) RETURNS void @@ -5987,10 +5795,8 @@ end; $$; -ALTER FUNCTION public.tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text) OWNER TO supabase_admin; - -- --- Name: tenant_set_member_status(uuid, uuid, text); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: tenant_set_member_status(uuid, uuid, text); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text) RETURNS void @@ -6029,10 +5835,8 @@ end; $$; -ALTER FUNCTION public.tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text) OWNER TO supabase_admin; - -- --- Name: tenant_update_member_role(uuid, uuid, text); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: tenant_update_member_role(uuid, uuid, text); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text) RETURNS void @@ -6056,7 +5860,7 @@ begin raise exception 'not_allowed'; end if; - -- evita o admin remover o próprio admin sem querer (opcional mas recomendado) + -- evita o admin remover o pr??prio admin sem querer (opcional mas recomendado) if p_member_user_id = auth.uid() and p_new_role <> 'tenant_admin' then raise exception 'cannot_demote_self'; end if; @@ -6073,10 +5877,8 @@ end; $$; -ALTER FUNCTION public.tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text) OWNER TO supabase_admin; - -- --- Name: toggle_plan(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: toggle_plan(uuid); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.toggle_plan(owner uuid) RETURNS void @@ -6107,10 +5909,8 @@ end; $$; -ALTER FUNCTION public.toggle_plan(owner uuid) OWNER TO supabase_admin; - -- --- Name: transition_subscription(uuid, text, text, jsonb); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: transition_subscription(uuid, text, text, jsonb); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.transition_subscription(p_subscription_id uuid, p_to_status text, p_reason text DEFAULT NULL::text, p_metadata jsonb DEFAULT NULL::jsonb) RETURNS public.subscriptions @@ -6129,11 +5929,11 @@ begin where id = p_subscription_id; if not found then - raise exception 'Assinatura não encontrada'; + raise exception 'Assinatura n??o encontrada'; end if; -- ===================================================== - -- 🔐 BLOCO DE AUTORIZAÇÃO + -- ???? BLOCO DE AUTORIZA????O -- ===================================================== -- 1) SaaS admin pode tudo @@ -6166,11 +5966,11 @@ begin end if; if not v_is_allowed then - raise exception 'Sem permissão para transicionar esta assinatura'; + raise exception 'Sem permiss??o para transicionar esta assinatura'; end if; -- ===================================================== - -- 🧠 TRANSIÇÃO + -- ???? TRANSI????O -- ===================================================== update public.subscriptions @@ -6185,7 +5985,7 @@ begin returning * into v_sub; -- ===================================================== - -- 🧾 EVENT LOG + -- ???? EVENT LOG -- ===================================================== insert into public.subscription_events ( @@ -6218,10 +6018,8 @@ end; $$; -ALTER FUNCTION public.transition_subscription(p_subscription_id uuid, p_to_status text, p_reason text, p_metadata jsonb) OWNER TO supabase_admin; - -- --- Name: trg_fn_financial_records_auto_overdue(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: trg_fn_financial_records_auto_overdue(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.trg_fn_financial_records_auto_overdue() RETURNS trigger @@ -6240,10 +6038,118 @@ END; $$; -ALTER FUNCTION public.trg_fn_financial_records_auto_overdue() OWNER TO supabase_admin; +-- +-- Name: trg_fn_patient_risco_timeline(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.trg_fn_patient_risco_timeline() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF OLD.risco_elevado IS DISTINCT FROM NEW.risco_elevado THEN + INSERT INTO public.patient_timeline ( + patient_id, tenant_id, + evento_tipo, titulo, descricao, icone_cor, + gerado_por, ocorrido_em + ) VALUES ( + NEW.id, NEW.tenant_id, + CASE WHEN NEW.risco_elevado THEN 'risco_sinalizado' ELSE 'risco_removido' END, + CASE WHEN NEW.risco_elevado THEN 'Risco elevado sinalizado' ELSE 'Sinalização de risco removida' END, + NEW.risco_nota, + CASE WHEN NEW.risco_elevado THEN 'red' ELSE 'green' END, + auth.uid(), + now() + ); + END IF; + RETURN NEW; +END; +$$; + -- --- Name: unstick_notification_queue(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: trg_fn_patient_status_history(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.trg_fn_patient_status_history() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF (TG_OP = 'INSERT') OR (OLD.status IS DISTINCT FROM NEW.status) THEN + INSERT INTO public.patient_status_history ( + patient_id, tenant_id, + status_anterior, status_novo, + motivo, encaminhado_para, data_saida, + alterado_por, alterado_em + ) VALUES ( + NEW.id, NEW.tenant_id, + CASE WHEN TG_OP = 'INSERT' THEN NULL ELSE OLD.status END, + NEW.status, + NEW.motivo_saida, + NEW.encaminhado_para, + NEW.data_saida, + auth.uid(), + now() + ); + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: trg_fn_patient_status_timeline(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.trg_fn_patient_status_timeline() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF (TG_OP = 'INSERT') OR (OLD.status IS DISTINCT FROM NEW.status) THEN + INSERT INTO public.patient_timeline ( + patient_id, tenant_id, + evento_tipo, titulo, descricao, icone_cor, + gerado_por, ocorrido_em + ) VALUES ( + NEW.id, NEW.tenant_id, + 'status_alterado', + 'Status alterado para ' || NEW.status, + CASE + WHEN TG_OP = 'INSERT' THEN 'Paciente cadastrado' + ELSE 'De ' || OLD.status || ' → ' || NEW.status || + CASE WHEN NEW.motivo_saida IS NOT NULL THEN ' · ' || NEW.motivo_saida ELSE '' END + END, + CASE NEW.status + WHEN 'Ativo' THEN 'green' + WHEN 'Alta' THEN 'blue' + WHEN 'Inativo' THEN 'gray' + WHEN 'Encaminhado' THEN 'amber' + WHEN 'Arquivado' THEN 'gray' + ELSE 'gray' + END, + auth.uid(), + now() + ); + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: trg_set_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.trg_set_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ BEGIN + NEW.updated_at = now(); + RETURN NEW; + END; + $$; + + +-- +-- Name: unstick_notification_queue(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.unstick_notification_queue() RETURNS integer @@ -6266,10 +6172,8 @@ END; $$; -ALTER FUNCTION public.unstick_notification_queue() OWNER TO supabase_admin; - -- --- Name: update_payment_settings_updated_at(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: update_payment_settings_updated_at(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.update_payment_settings_updated_at() RETURNS trigger @@ -6282,10 +6186,8 @@ 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 +-- Name: update_professional_pricing_updated_at(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.update_professional_pricing_updated_at() RETURNS trigger @@ -6298,10 +6200,8 @@ 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 +-- Name: user_has_feature(uuid, text); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.user_has_feature(_user_id uuid, _feature text) RETURNS boolean @@ -6317,10 +6217,8 @@ CREATE FUNCTION public.user_has_feature(_user_id uuid, _feature text) RETURNS bo $$; -ALTER FUNCTION public.user_has_feature(_user_id uuid, _feature text) OWNER TO supabase_admin; - -- --- Name: validate_support_session(text); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: validate_support_session(text); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.validate_support_session(p_token text) RETURNS json @@ -6352,10 +6250,8 @@ END; $$; -ALTER FUNCTION public.validate_support_session(p_token text) OWNER TO supabase_admin; - -- --- Name: whoami(); Type: FUNCTION; Schema: public; Owner: supabase_admin +-- Name: whoami(); Type: FUNCTION; Schema: public; Owner: - -- CREATE FUNCTION public.whoami() RETURNS TABLE(uid uuid, role text) @@ -6365,10 +6261,8 @@ CREATE FUNCTION public.whoami() RETURNS TABLE(uid uuid, role text) $$; -ALTER FUNCTION public.whoami() OWNER TO supabase_admin; - -- --- Name: apply_rls(jsonb, integer); Type: FUNCTION; Schema: realtime; Owner: supabase_admin +-- Name: apply_rls(jsonb, integer); Type: FUNCTION; Schema: realtime; Owner: - -- CREATE FUNCTION realtime.apply_rls(wal jsonb, max_record_bytes integer DEFAULT (1024 * 1024)) RETURNS SETOF realtime.wal_rls @@ -6672,10 +6566,8 @@ 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 +-- Name: broadcast_changes(text, text, text, text, text, record, record, text); Type: FUNCTION; Schema: realtime; Owner: - -- 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 @@ -6703,10 +6595,8 @@ 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 +-- Name: build_prepared_statement_sql(text, regclass, realtime.wal_column[]); Type: FUNCTION; Schema: realtime; Owner: - -- CREATE FUNCTION realtime.build_prepared_statement_sql(prepared_statement_name text, entity regclass, columns realtime.wal_column[]) RETURNS text @@ -6738,10 +6628,8 @@ CREATE FUNCTION realtime.build_prepared_statement_sql(prepared_statement_name te $$; -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 +-- Name: cast(text, regtype); Type: FUNCTION; Schema: realtime; Owner: - -- CREATE FUNCTION realtime."cast"(val text, type_ regtype) RETURNS jsonb @@ -6756,10 +6644,8 @@ CREATE FUNCTION realtime."cast"(val text, type_ regtype) RETURNS jsonb $$; -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 +-- Name: check_equality_op(realtime.equality_op, regtype, text, text); Type: FUNCTION; Schema: realtime; Owner: - -- CREATE FUNCTION realtime.check_equality_op(op realtime.equality_op, type_ regtype, val_1 text, val_2 text) RETURNS boolean @@ -6797,10 +6683,8 @@ CREATE FUNCTION realtime.check_equality_op(op realtime.equality_op, type_ regtyp $$; -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 +-- Name: is_visible_through_filters(realtime.wal_column[], realtime.user_defined_filter[]); Type: FUNCTION; Schema: realtime; Owner: - -- CREATE FUNCTION realtime.is_visible_through_filters(columns realtime.wal_column[], filters realtime.user_defined_filter[]) RETURNS boolean @@ -6835,10 +6719,8 @@ CREATE FUNCTION realtime.is_visible_through_filters(columns realtime.wal_column[ $_$; -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 +-- Name: list_changes(name, name, integer, integer); Type: FUNCTION; Schema: realtime; Owner: - -- CREATE FUNCTION realtime.list_changes(publication name, slot_name name, max_changes integer, max_record_bytes integer) RETURNS SETOF realtime.wal_rls @@ -6903,10 +6785,8 @@ CREATE FUNCTION realtime.list_changes(publication name, slot_name name, max_chan $$; -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 +-- Name: quote_wal2json(regclass); Type: FUNCTION; Schema: realtime; Owner: - -- CREATE FUNCTION realtime.quote_wal2json(entity regclass) RETURNS text @@ -6943,10 +6823,8 @@ CREATE FUNCTION realtime.quote_wal2json(entity regclass) RETURNS text $$; -ALTER FUNCTION realtime.quote_wal2json(entity regclass) OWNER TO supabase_admin; - -- --- Name: send(jsonb, text, text, boolean); Type: FUNCTION; Schema: realtime; Owner: supabase_admin +-- Name: send(jsonb, text, text, boolean); Type: FUNCTION; Schema: realtime; Owner: - -- CREATE FUNCTION realtime.send(payload jsonb, event text, topic text, private boolean DEFAULT true) RETURNS void @@ -6982,10 +6860,8 @@ 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 +-- Name: subscription_check_filters(); Type: FUNCTION; Schema: realtime; Owner: - -- CREATE FUNCTION realtime.subscription_check_filters() RETURNS trigger @@ -7058,10 +6934,8 @@ CREATE FUNCTION realtime.subscription_check_filters() RETURNS trigger $$; -ALTER FUNCTION realtime.subscription_check_filters() OWNER TO supabase_admin; - -- --- Name: to_regrole(text); Type: FUNCTION; Schema: realtime; Owner: supabase_admin +-- Name: to_regrole(text); Type: FUNCTION; Schema: realtime; Owner: - -- CREATE FUNCTION realtime.to_regrole(role_name text) RETURNS regrole @@ -7069,10 +6943,8 @@ CREATE FUNCTION realtime.to_regrole(role_name text) RETURNS regrole 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 +-- Name: topic(); Type: FUNCTION; Schema: realtime; Owner: - -- CREATE FUNCTION realtime.topic() RETURNS text @@ -7082,10 +6954,8 @@ 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 +-- Name: can_insert_object(text, text, uuid, jsonb); Type: FUNCTION; Schema: storage; Owner: - -- CREATE FUNCTION storage.can_insert_object(bucketid text, name text, owner uuid, metadata jsonb) RETURNS void @@ -7101,10 +6971,8 @@ 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 +-- Name: enforce_bucket_name_length(); Type: FUNCTION; Schema: storage; Owner: - -- CREATE FUNCTION storage.enforce_bucket_name_length() RETURNS trigger @@ -7119,10 +6987,8 @@ end; $$; -ALTER FUNCTION storage.enforce_bucket_name_length() OWNER TO supabase_storage_admin; - -- --- Name: extension(text); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin +-- Name: extension(text); Type: FUNCTION; Schema: storage; Owner: - -- CREATE FUNCTION storage.extension(name text) RETURNS text @@ -7140,10 +7006,8 @@ END $$; -ALTER FUNCTION storage.extension(name text) OWNER TO supabase_storage_admin; - -- --- Name: filename(text); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin +-- Name: filename(text); Type: FUNCTION; Schema: storage; Owner: - -- CREATE FUNCTION storage.filename(name text) RETURNS text @@ -7158,10 +7022,8 @@ END $$; -ALTER FUNCTION storage.filename(name text) OWNER TO supabase_storage_admin; - -- --- Name: foldername(text); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin +-- Name: foldername(text); Type: FUNCTION; Schema: storage; Owner: - -- CREATE FUNCTION storage.foldername(name text) RETURNS text[] @@ -7176,10 +7038,8 @@ 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 +-- Name: get_common_prefix(text, text, text); Type: FUNCTION; Schema: storage; Owner: - -- CREATE FUNCTION storage.get_common_prefix(p_key text, p_prefix text, p_delimiter text) RETURNS text @@ -7193,10 +7053,8 @@ 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 +-- Name: get_size_by_bucket(); Type: FUNCTION; Schema: storage; Owner: - -- CREATE FUNCTION storage.get_size_by_bucket() RETURNS TABLE(size bigint, bucket_id text) @@ -7211,10 +7069,8 @@ 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 +-- Name: list_multipart_uploads_with_delimiter(text, text, text, integer, text, text); Type: FUNCTION; Schema: storage; Owner: - -- 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) @@ -7259,10 +7115,8 @@ 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 +-- Name: list_objects_with_delimiter(text, text, text, integer, text, text, text); Type: FUNCTION; Schema: storage; Owner: - -- 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) @@ -7476,10 +7330,8 @@ 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 +-- Name: operation(); Type: FUNCTION; Schema: storage; Owner: - -- CREATE FUNCTION storage.operation() RETURNS text @@ -7491,10 +7343,8 @@ END; $$; -ALTER FUNCTION storage.operation() OWNER TO supabase_storage_admin; - -- --- Name: protect_delete(); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin +-- Name: protect_delete(); Type: FUNCTION; Schema: storage; Owner: - -- CREATE FUNCTION storage.protect_delete() RETURNS trigger @@ -7512,10 +7362,8 @@ 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 +-- Name: search(text, text, integer, integer, integer, text, text, text); Type: FUNCTION; Schema: storage; Owner: - -- 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) @@ -7768,10 +7616,8 @@ 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 +-- Name: search_by_timestamp(text, text, integer, integer, text, text, text, text); Type: FUNCTION; Schema: storage; Owner: - -- 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) @@ -7877,10 +7723,8 @@ 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 +-- Name: search_v2(text, text, integer, integer, text, text, text, text); Type: FUNCTION; Schema: storage; Owner: - -- 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) @@ -7939,10 +7783,8 @@ 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 +-- Name: update_updated_at_column(); Type: FUNCTION; Schema: storage; Owner: - -- CREATE FUNCTION storage.update_updated_at_column() RETURNS trigger @@ -7955,10 +7797,8 @@ 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 +-- Name: http_request(); Type: FUNCTION; Schema: supabase_functions; Owner: - -- CREATE FUNCTION supabase_functions.http_request() RETURNS trigger @@ -8038,10 +7878,8 @@ CREATE FUNCTION supabase_functions.http_request() RETURNS trigger $$; -ALTER FUNCTION supabase_functions.http_request() OWNER TO supabase_functions_admin; - -- --- Name: extensions; Type: TABLE; Schema: _realtime; Owner: supabase_admin +-- Name: extensions; Type: TABLE; Schema: _realtime; Owner: - -- CREATE TABLE _realtime.extensions ( @@ -8054,10 +7892,8 @@ CREATE TABLE _realtime.extensions ( ); -ALTER TABLE _realtime.extensions OWNER TO supabase_admin; - -- --- Name: schema_migrations; Type: TABLE; Schema: _realtime; Owner: supabase_admin +-- Name: schema_migrations; Type: TABLE; Schema: _realtime; Owner: - -- CREATE TABLE _realtime.schema_migrations ( @@ -8066,10 +7902,8 @@ CREATE TABLE _realtime.schema_migrations ( ); -ALTER TABLE _realtime.schema_migrations OWNER TO supabase_admin; - -- --- Name: tenants; Type: TABLE; Schema: _realtime; Owner: supabase_admin +-- Name: tenants; Type: TABLE; Schema: _realtime; Owner: - -- CREATE TABLE _realtime.tenants ( @@ -8097,10 +7931,8 @@ CREATE TABLE _realtime.tenants ( ); -ALTER TABLE _realtime.tenants OWNER TO supabase_admin; - -- --- Name: audit_log_entries; Type: TABLE; Schema: auth; Owner: supabase_auth_admin +-- Name: audit_log_entries; Type: TABLE; Schema: auth; Owner: - -- CREATE TABLE auth.audit_log_entries ( @@ -8112,17 +7944,15 @@ CREATE TABLE auth.audit_log_entries ( ); -ALTER TABLE auth.audit_log_entries OWNER TO supabase_auth_admin; - -- --- Name: TABLE audit_log_entries; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin +-- Name: TABLE audit_log_entries; Type: COMMENT; Schema: auth; Owner: - -- 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 +-- Name: flow_state; Type: TABLE; Schema: auth; Owner: - -- CREATE TABLE auth.flow_state ( @@ -8146,17 +7976,15 @@ CREATE TABLE auth.flow_state ( ); -ALTER TABLE auth.flow_state OWNER TO supabase_auth_admin; - -- --- Name: TABLE flow_state; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin +-- Name: TABLE flow_state; Type: COMMENT; Schema: auth; Owner: - -- 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 +-- Name: identities; Type: TABLE; Schema: auth; Owner: - -- CREATE TABLE auth.identities ( @@ -8172,24 +8000,22 @@ CREATE TABLE auth.identities ( ); -ALTER TABLE auth.identities OWNER TO supabase_auth_admin; - -- --- Name: TABLE identities; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin +-- Name: TABLE identities; Type: COMMENT; Schema: auth; Owner: - -- 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 +-- Name: COLUMN identities.email; Type: COMMENT; Schema: auth; Owner: - -- 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 +-- Name: instances; Type: TABLE; Schema: auth; Owner: - -- CREATE TABLE auth.instances ( @@ -8201,17 +8027,15 @@ CREATE TABLE auth.instances ( ); -ALTER TABLE auth.instances OWNER TO supabase_auth_admin; - -- --- Name: TABLE instances; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin +-- Name: TABLE instances; Type: COMMENT; Schema: auth; Owner: - -- COMMENT ON TABLE auth.instances IS 'Auth: Manages users across multiple sites.'; -- --- Name: mfa_amr_claims; Type: TABLE; Schema: auth; Owner: supabase_auth_admin +-- Name: mfa_amr_claims; Type: TABLE; Schema: auth; Owner: - -- CREATE TABLE auth.mfa_amr_claims ( @@ -8223,17 +8047,15 @@ CREATE TABLE auth.mfa_amr_claims ( ); -ALTER TABLE auth.mfa_amr_claims OWNER TO supabase_auth_admin; - -- --- Name: TABLE mfa_amr_claims; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin +-- Name: TABLE mfa_amr_claims; Type: COMMENT; Schema: auth; Owner: - -- 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 +-- Name: mfa_challenges; Type: TABLE; Schema: auth; Owner: - -- CREATE TABLE auth.mfa_challenges ( @@ -8247,17 +8069,15 @@ CREATE TABLE auth.mfa_challenges ( ); -ALTER TABLE auth.mfa_challenges OWNER TO supabase_auth_admin; - -- --- Name: TABLE mfa_challenges; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin +-- Name: TABLE mfa_challenges; Type: COMMENT; Schema: auth; Owner: - -- 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 +-- Name: mfa_factors; Type: TABLE; Schema: auth; Owner: - -- CREATE TABLE auth.mfa_factors ( @@ -8277,24 +8097,22 @@ CREATE TABLE auth.mfa_factors ( ); -ALTER TABLE auth.mfa_factors OWNER TO supabase_auth_admin; - -- --- Name: TABLE mfa_factors; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin +-- Name: TABLE mfa_factors; Type: COMMENT; Schema: auth; Owner: - -- 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 +-- Name: COLUMN mfa_factors.last_webauthn_challenge_data; Type: COMMENT; Schema: auth; Owner: - -- 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 +-- Name: oauth_authorizations; Type: TABLE; Schema: auth; Owner: - -- CREATE TABLE auth.oauth_authorizations ( @@ -8326,10 +8144,8 @@ CREATE TABLE auth.oauth_authorizations ( ); -ALTER TABLE auth.oauth_authorizations OWNER TO supabase_auth_admin; - -- --- Name: oauth_client_states; Type: TABLE; Schema: auth; Owner: supabase_auth_admin +-- Name: oauth_client_states; Type: TABLE; Schema: auth; Owner: - -- CREATE TABLE auth.oauth_client_states ( @@ -8340,17 +8156,15 @@ CREATE TABLE auth.oauth_client_states ( ); -ALTER TABLE auth.oauth_client_states OWNER TO supabase_auth_admin; - -- --- Name: TABLE oauth_client_states; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin +-- Name: TABLE oauth_client_states; Type: COMMENT; Schema: auth; Owner: - -- 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 +-- Name: oauth_clients; Type: TABLE; Schema: auth; Owner: - -- CREATE TABLE auth.oauth_clients ( @@ -8374,10 +8188,8 @@ CREATE TABLE auth.oauth_clients ( ); -ALTER TABLE auth.oauth_clients OWNER TO supabase_auth_admin; - -- --- Name: oauth_consents; Type: TABLE; Schema: auth; Owner: supabase_auth_admin +-- Name: oauth_consents; Type: TABLE; Schema: auth; Owner: - -- CREATE TABLE auth.oauth_consents ( @@ -8393,10 +8205,8 @@ CREATE TABLE auth.oauth_consents ( ); -ALTER TABLE auth.oauth_consents OWNER TO supabase_auth_admin; - -- --- Name: one_time_tokens; Type: TABLE; Schema: auth; Owner: supabase_auth_admin +-- Name: one_time_tokens; Type: TABLE; Schema: auth; Owner: - -- CREATE TABLE auth.one_time_tokens ( @@ -8411,10 +8221,8 @@ CREATE TABLE auth.one_time_tokens ( ); -ALTER TABLE auth.one_time_tokens OWNER TO supabase_auth_admin; - -- --- Name: refresh_tokens; Type: TABLE; Schema: auth; Owner: supabase_auth_admin +-- Name: refresh_tokens; Type: TABLE; Schema: auth; Owner: - -- CREATE TABLE auth.refresh_tokens ( @@ -8430,17 +8238,15 @@ CREATE TABLE auth.refresh_tokens ( ); -ALTER TABLE auth.refresh_tokens OWNER TO supabase_auth_admin; - -- --- Name: TABLE refresh_tokens; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin +-- Name: TABLE refresh_tokens; Type: COMMENT; Schema: auth; Owner: - -- 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 +-- Name: refresh_tokens_id_seq; Type: SEQUENCE; Schema: auth; Owner: - -- CREATE SEQUENCE auth.refresh_tokens_id_seq @@ -8451,17 +8257,15 @@ CREATE SEQUENCE auth.refresh_tokens_id_seq 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 +-- Name: refresh_tokens_id_seq; Type: SEQUENCE OWNED BY; Schema: auth; Owner: - -- ALTER SEQUENCE auth.refresh_tokens_id_seq OWNED BY auth.refresh_tokens.id; -- --- Name: saml_providers; Type: TABLE; Schema: auth; Owner: supabase_auth_admin +-- Name: saml_providers; Type: TABLE; Schema: auth; Owner: - -- CREATE TABLE auth.saml_providers ( @@ -8480,17 +8284,15 @@ CREATE TABLE auth.saml_providers ( ); -ALTER TABLE auth.saml_providers OWNER TO supabase_auth_admin; - -- --- Name: TABLE saml_providers; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin +-- Name: TABLE saml_providers; Type: COMMENT; Schema: auth; Owner: - -- 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 +-- Name: saml_relay_states; Type: TABLE; Schema: auth; Owner: - -- CREATE TABLE auth.saml_relay_states ( @@ -8506,17 +8308,15 @@ CREATE TABLE auth.saml_relay_states ( ); -ALTER TABLE auth.saml_relay_states OWNER TO supabase_auth_admin; - -- --- Name: TABLE saml_relay_states; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin +-- Name: TABLE saml_relay_states; Type: COMMENT; Schema: auth; Owner: - -- 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 +-- Name: schema_migrations; Type: TABLE; Schema: auth; Owner: - -- CREATE TABLE auth.schema_migrations ( @@ -8524,17 +8324,15 @@ CREATE TABLE auth.schema_migrations ( ); -ALTER TABLE auth.schema_migrations OWNER TO supabase_auth_admin; - -- --- Name: TABLE schema_migrations; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin +-- Name: TABLE schema_migrations; Type: COMMENT; Schema: auth; Owner: - -- COMMENT ON TABLE auth.schema_migrations IS 'Auth: Manages updates to the auth system.'; -- --- Name: sessions; Type: TABLE; Schema: auth; Owner: supabase_auth_admin +-- Name: sessions; Type: TABLE; Schema: auth; Owner: - -- CREATE TABLE auth.sessions ( @@ -8557,38 +8355,36 @@ CREATE TABLE auth.sessions ( ); -ALTER TABLE auth.sessions OWNER TO supabase_auth_admin; - -- --- Name: TABLE sessions; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin +-- Name: TABLE sessions; Type: COMMENT; Schema: auth; Owner: - -- 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 +-- Name: COLUMN sessions.not_after; Type: COMMENT; Schema: auth; Owner: - -- 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 +-- Name: COLUMN sessions.refresh_token_hmac_key; Type: COMMENT; Schema: auth; Owner: - -- 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 +-- Name: COLUMN sessions.refresh_token_counter; Type: COMMENT; Schema: auth; Owner: - -- 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 +-- Name: sso_domains; Type: TABLE; Schema: auth; Owner: - -- CREATE TABLE auth.sso_domains ( @@ -8601,17 +8397,15 @@ CREATE TABLE auth.sso_domains ( ); -ALTER TABLE auth.sso_domains OWNER TO supabase_auth_admin; - -- --- Name: TABLE sso_domains; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin +-- Name: TABLE sso_domains; Type: COMMENT; Schema: auth; Owner: - -- 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 +-- Name: sso_providers; Type: TABLE; Schema: auth; Owner: - -- CREATE TABLE auth.sso_providers ( @@ -8624,24 +8418,22 @@ CREATE TABLE auth.sso_providers ( ); -ALTER TABLE auth.sso_providers OWNER TO supabase_auth_admin; - -- --- Name: TABLE sso_providers; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin +-- Name: TABLE sso_providers; Type: COMMENT; Schema: auth; Owner: - -- 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 +-- Name: COLUMN sso_providers.resource_id; Type: COMMENT; Schema: auth; Owner: - -- 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 +-- Name: users; Type: TABLE; Schema: auth; Owner: - -- CREATE TABLE auth.users ( @@ -8684,24 +8476,55 @@ CREATE TABLE auth.users ( ); -ALTER TABLE auth.users OWNER TO supabase_auth_admin; - -- --- Name: TABLE users; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin +-- Name: TABLE users; Type: COMMENT; Schema: auth; Owner: - -- 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 +-- Name: COLUMN users.is_sso_user; Type: COMMENT; Schema: auth; Owner: - -- 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 +-- Name: _db_migrations; Type: TABLE; Schema: public; Owner: - +-- + +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 +); + + +-- +-- Name: _db_migrations_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public._db_migrations_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: _db_migrations_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public._db_migrations_id_seq OWNED BY public._db_migrations.id; + + +-- +-- Name: addon_credits; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.addon_credits ( @@ -8728,17 +8551,15 @@ CREATE TABLE public.addon_credits ( ); -ALTER TABLE public.addon_credits OWNER TO supabase_admin; - -- --- Name: TABLE addon_credits; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: TABLE addon_credits; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON TABLE public.addon_credits IS 'Saldo de créditos de add-ons por tenant. Um registro por (tenant_id, addon_type).'; +COMMENT ON TABLE public.addon_credits IS 'Saldo de cr??ditos de add-ons por tenant. Um registro por (tenant_id, addon_type).'; -- --- Name: addon_products; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: addon_products; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.addon_products ( @@ -8761,17 +8582,15 @@ CREATE TABLE public.addon_products ( ); -ALTER TABLE public.addon_products OWNER TO supabase_admin; - -- --- Name: TABLE addon_products; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: TABLE addon_products; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON TABLE public.addon_products IS 'Catálogo de recursos extras vendidos pela plataforma (SMS, servidor, domínio, etc.)'; +COMMENT ON TABLE public.addon_products IS 'Cat??logo de recursos extras vendidos pela plataforma (SMS, servidor, dom??nio, etc.)'; -- --- Name: addon_transactions; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: addon_transactions; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.addon_transactions ( @@ -8796,17 +8615,15 @@ CREATE TABLE public.addon_transactions ( ); -ALTER TABLE public.addon_transactions OWNER TO supabase_admin; - -- --- Name: TABLE addon_transactions; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: TABLE addon_transactions; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON TABLE public.addon_transactions IS 'Histórico de todas as transações de créditos: compras, consumo, ajustes, reembolsos.'; +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 +-- Name: agenda_bloqueios; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.agenda_bloqueios ( @@ -8828,10 +8645,8 @@ CREATE TABLE public.agenda_bloqueios ( ); -ALTER TABLE public.agenda_bloqueios OWNER TO supabase_admin; - -- --- Name: agenda_configuracoes; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: agenda_configuracoes; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.agenda_configuracoes ( @@ -8870,7 +8685,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])))), @@ -8890,10 +8707,15 @@ CREATE TABLE public.agenda_configuracoes ( ); -ALTER TABLE public.agenda_configuracoes OWNER TO supabase_admin; +-- +-- Name: COLUMN agenda_configuracoes.atendimento_mode; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.agenda_configuracoes.atendimento_mode IS 'Modo de atendimento: particular | convenio | ambos'; + -- --- Name: agenda_eventos; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.agenda_eventos ( @@ -8932,17 +8754,15 @@ CREATE TABLE public.agenda_eventos ( ); -ALTER TABLE public.agenda_eventos OWNER TO supabase_admin; - -- --- Name: COLUMN agenda_eventos.price; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN agenda_eventos.price; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON COLUMN public.agenda_eventos.price IS 'Valor da sessão em BRL. Preenchido automaticamente pela tabela professional_pricing do profissional.'; +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 +-- Name: agenda_excecoes; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.agenda_excecoes ( @@ -8964,10 +8784,8 @@ CREATE TABLE public.agenda_excecoes ( ); -ALTER TABLE public.agenda_excecoes OWNER TO supabase_admin; - -- --- Name: agenda_online_slots; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: agenda_online_slots; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.agenda_online_slots ( @@ -8983,10 +8801,8 @@ CREATE TABLE public.agenda_online_slots ( ); -ALTER TABLE public.agenda_online_slots OWNER TO supabase_admin; - -- --- Name: agenda_online_slots_id_seq; Type: SEQUENCE; Schema: public; Owner: supabase_admin +-- Name: agenda_online_slots_id_seq; Type: SEQUENCE; Schema: public; Owner: - -- CREATE SEQUENCE public.agenda_online_slots_id_seq @@ -8997,17 +8813,15 @@ CREATE SEQUENCE public.agenda_online_slots_id_seq 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 +-- Name: agenda_online_slots_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - -- 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 +-- Name: agenda_regras_semanais; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.agenda_regras_semanais ( @@ -9027,10 +8841,8 @@ CREATE TABLE public.agenda_regras_semanais ( ); -ALTER TABLE public.agenda_regras_semanais OWNER TO supabase_admin; - -- --- Name: agenda_slots_bloqueados_semanais; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: agenda_slots_bloqueados_semanais; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.agenda_slots_bloqueados_semanais ( @@ -9047,10 +8859,8 @@ CREATE TABLE public.agenda_slots_bloqueados_semanais ( ); -ALTER TABLE public.agenda_slots_bloqueados_semanais OWNER TO supabase_admin; - -- --- Name: agenda_slots_regras; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: agenda_slots_regras; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.agenda_slots_regras ( @@ -9075,10 +8885,8 @@ CREATE TABLE public.agenda_slots_regras ( ); -ALTER TABLE public.agenda_slots_regras OWNER TO supabase_admin; - -- --- Name: agendador_configuracoes; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: agendador_configuracoes; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.agendador_configuracoes ( @@ -9125,24 +8933,22 @@ CREATE TABLE public.agendador_configuracoes ( ); -ALTER TABLE public.agendador_configuracoes OWNER TO supabase_admin; - -- --- Name: COLUMN agendador_configuracoes.pagamento_modo; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN agendador_configuracoes.pagamento_modo; Type: COMMENT; Schema: public; Owner: - -- 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 +-- Name: COLUMN agendador_configuracoes.pagamento_metodos_visiveis; Type: COMMENT; Schema: public; Owner: - -- -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}'; +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 +-- Name: agendador_solicitacoes; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.agendador_solicitacoes ( @@ -9179,10 +8985,8 @@ CREATE TABLE public.agendador_solicitacoes ( ); -ALTER TABLE public.agendador_solicitacoes OWNER TO supabase_admin; - -- --- Name: billing_contracts; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: billing_contracts; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.billing_contracts ( @@ -9208,10 +9012,8 @@ CREATE TABLE public.billing_contracts ( ); -ALTER TABLE public.billing_contracts OWNER TO supabase_admin; - -- --- Name: commitment_services; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: commitment_services; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.commitment_services ( @@ -9231,10 +9033,8 @@ CREATE TABLE public.commitment_services ( ); -ALTER TABLE public.commitment_services OWNER TO supabase_admin; - -- --- Name: commitment_time_logs; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: commitment_time_logs; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.commitment_time_logs ( @@ -9251,10 +9051,8 @@ CREATE TABLE public.commitment_time_logs ( ); -ALTER TABLE public.commitment_time_logs OWNER TO supabase_admin; - -- --- Name: company_profiles; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: company_profiles; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.company_profiles ( @@ -9283,20 +9081,16 @@ CREATE TABLE public.company_profiles ( ); -ALTER TABLE public.company_profiles OWNER TO supabase_admin; - -- --- Name: current_tenant_id; Type: VIEW; Schema: public; Owner: supabase_admin +-- Name: current_tenant_id; Type: VIEW; Schema: public; Owner: - -- 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 +-- Name: determined_commitment_fields; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.determined_commitment_fields ( @@ -9313,10 +9107,8 @@ CREATE TABLE public.determined_commitment_fields ( ); -ALTER TABLE public.determined_commitment_fields OWNER TO supabase_admin; - -- --- Name: determined_commitments; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: determined_commitments; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.determined_commitments ( @@ -9336,10 +9128,8 @@ CREATE TABLE public.determined_commitments ( ); -ALTER TABLE public.determined_commitments OWNER TO supabase_admin; - -- --- Name: dev_user_credentials; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: dev_user_credentials; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.dev_user_credentials ( @@ -9353,10 +9143,474 @@ CREATE TABLE public.dev_user_credentials ( ); -ALTER TABLE public.dev_user_credentials OWNER TO supabase_admin; +-- +-- Name: document_access_logs; Type: TABLE; Schema: public; Owner: - +-- + +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]))) +); + -- --- Name: email_layout_config; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: TABLE document_access_logs; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.document_access_logs IS 'Log imutavel de acessos a documentos. Conformidade CFP e LGPD. Sem UPDATE/DELETE.'; + + +-- +-- Name: COLUMN document_access_logs.acao; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.document_access_logs.acao IS 'visualizou|baixou|imprimiu|compartilhou|assinou.'; + + +-- +-- Name: document_generated; Type: TABLE; Schema: public; Owner: - +-- + +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 +); + + +-- +-- Name: TABLE document_generated; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.document_generated IS 'Registro de cada documento PDF gerado a partir de um template.'; + + +-- +-- Name: COLUMN document_generated.template_id; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.document_generated.template_id IS 'Template usado para gerar o documento.'; + + +-- +-- Name: COLUMN document_generated.dados_preenchidos; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.document_generated.dados_preenchidos IS 'Snapshot JSON dos dados usados no preenchimento. Permite auditoria futura.'; + + +-- +-- Name: COLUMN document_generated.pdf_path; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.document_generated.pdf_path IS 'Caminho do PDF gerado no Supabase Storage bucket.'; + + +-- +-- Name: COLUMN document_generated.documento_id; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.document_generated.documento_id IS 'FK opcional para documents — se o PDF gerado tambem foi registrado como documento do paciente.'; + + +-- +-- Name: COLUMN document_generated.gerado_por; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.document_generated.gerado_por IS 'Usuario que gerou o documento (auth.uid()).'; + + +-- +-- Name: document_share_links; Type: TABLE; Schema: public; Owner: - +-- + +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 +); + + +-- +-- Name: TABLE document_share_links; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.document_share_links IS 'Links temporarios assinados para compartilhar documento com profissional externo.'; + + +-- +-- Name: COLUMN document_share_links.token; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.document_share_links.token IS 'Token unico gerado automaticamente (32 bytes hex).'; + + +-- +-- Name: COLUMN document_share_links.expira_em; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.document_share_links.expira_em IS 'Data/hora de expiracao do link.'; + + +-- +-- Name: COLUMN document_share_links.usos_max; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.document_share_links.usos_max IS 'Numero maximo de acessos permitidos.'; + + +-- +-- Name: COLUMN document_share_links.usos; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.document_share_links.usos IS 'Numero de vezes que o link ja foi acessado.'; + + +-- +-- Name: document_signatures; Type: TABLE; Schema: public; Owner: - +-- + +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]))) +); + + +-- +-- Name: TABLE document_signatures; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.document_signatures IS 'Assinaturas eletronicas de documentos. Cada signatario tem seu registro.'; + + +-- +-- Name: COLUMN document_signatures.signatario_tipo; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.document_signatures.signatario_tipo IS 'paciente|responsavel_legal|terapeuta.'; + + +-- +-- Name: COLUMN document_signatures.status; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.document_signatures.status IS 'pendente|enviado|assinado|recusado|expirado.'; + + +-- +-- Name: COLUMN document_signatures.ip; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.document_signatures.ip IS 'IP do signatario no momento da assinatura.'; + + +-- +-- Name: COLUMN document_signatures.hash_documento; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.document_signatures.hash_documento IS 'Hash SHA-256 do documento no momento da assinatura. Garante integridade.'; + + +-- +-- Name: document_templates; Type: TABLE; Schema: public; Owner: - +-- + +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]))) +); + + +-- +-- Name: TABLE document_templates; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.document_templates IS 'Templates de documentos para geracao automatica (declaracao, atestado, recibo etc.).'; + + +-- +-- Name: COLUMN document_templates.nome_template; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.document_templates.nome_template IS 'Nome do template. Ex: Declaracao de Comparecimento.'; + + +-- +-- Name: COLUMN document_templates.tipo; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.document_templates.tipo IS 'declaracao_comparecimento|atestado_psicologico|relatorio_acompanhamento|recibo_pagamento|termo_consentimento|encaminhamento|outro.'; + + +-- +-- Name: COLUMN document_templates.corpo_html; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.document_templates.corpo_html IS 'Corpo do template em HTML com variaveis {{nome_variavel}}.'; + + +-- +-- Name: COLUMN document_templates.cabecalho_html; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.document_templates.cabecalho_html IS 'HTML do cabecalho (logo, nome da clinica etc.).'; + + +-- +-- Name: COLUMN document_templates.rodape_html; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.document_templates.rodape_html IS 'HTML do rodape (CRP, endereco, contato etc.).'; + + +-- +-- Name: COLUMN document_templates.variaveis; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.document_templates.variaveis IS 'Array com nomes das variaveis usadas no template. Ex: {paciente_nome, data_sessao}.'; + + +-- +-- Name: COLUMN document_templates.logo_url; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.document_templates.logo_url IS 'URL do logo personalizado para o cabecalho do documento.'; + + +-- +-- Name: COLUMN document_templates.is_global; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.document_templates.is_global IS 'true = template padrao do sistema visivel para todos. false = template do tenant.'; + + +-- +-- Name: documents; Type: TABLE; Schema: public; Owner: - +-- + +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]))) +); + + +-- +-- Name: TABLE documents; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.documents IS 'Documentos e arquivos vinculados a pacientes. Armazenados no Supabase Storage.'; + + +-- +-- Name: COLUMN documents.owner_id; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.documents.owner_id IS 'Terapeuta dono do documento (auth.uid()).'; + + +-- +-- Name: COLUMN documents.tenant_id; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.documents.tenant_id IS 'Tenant do terapeuta.'; + + +-- +-- Name: COLUMN documents.patient_id; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.documents.patient_id IS 'Paciente ao qual o documento pertence.'; + + +-- +-- Name: COLUMN documents.bucket_path; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.documents.bucket_path IS 'Caminho do arquivo no Supabase Storage bucket.'; + + +-- +-- Name: COLUMN documents.storage_bucket; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.documents.storage_bucket IS 'Nome do bucket no Storage. Default: documents.'; + + +-- +-- Name: COLUMN documents.nome_original; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.documents.nome_original IS 'Nome original do arquivo enviado.'; + + +-- +-- Name: COLUMN documents.mime_type; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.documents.mime_type IS 'MIME type do arquivo. Ex: application/pdf, image/jpeg.'; + + +-- +-- Name: COLUMN documents.tamanho_bytes; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.documents.tamanho_bytes IS 'Tamanho do arquivo em bytes.'; + + +-- +-- Name: COLUMN documents.tipo_documento; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.documents.tipo_documento IS 'Tipo: laudo|receita|exame|termo_assinado|relatorio_externo|identidade|convenio|declaracao|atestado|recibo|outro.'; + + +-- +-- Name: COLUMN documents.categoria; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.documents.categoria IS 'Categoria livre para organizacao adicional.'; + + +-- +-- Name: COLUMN documents.tags; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.documents.tags IS 'Tags livres para busca e filtro. Array de text.'; + + +-- +-- Name: COLUMN documents.visibilidade; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.documents.visibilidade IS 'privado|compartilhado_supervisor|compartilhado_portal.'; + + +-- +-- Name: COLUMN documents.compartilhado_portal; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.documents.compartilhado_portal IS 'true = visivel para o paciente no portal.'; + + +-- +-- Name: COLUMN documents.compartilhado_supervisor; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.documents.compartilhado_supervisor IS 'true = visivel para o supervisor.'; + + +-- +-- Name: COLUMN documents.enviado_pelo_paciente; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.documents.enviado_pelo_paciente IS 'true = upload feito pelo paciente via portal.'; + + +-- +-- Name: COLUMN documents.status_revisao; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.documents.status_revisao IS 'pendente|aprovado|rejeitado — para uploads do paciente.'; + + +-- +-- Name: COLUMN documents.deleted_at; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.documents.deleted_at IS 'Soft delete: data da exclusao. NULL = ativo.'; + + +-- +-- Name: COLUMN documents.retencao_ate; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.documents.retencao_ate IS 'LGPD/CFP: arquivo retido ate esta data mesmo apos soft delete.'; + + +-- +-- Name: email_layout_config; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.email_layout_config ( @@ -9369,10 +9623,8 @@ CREATE TABLE public.email_layout_config ( ); -ALTER TABLE public.email_layout_config OWNER TO supabase_admin; - -- --- Name: email_templates_global; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: email_templates_global; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.email_templates_global ( @@ -9391,10 +9643,8 @@ CREATE TABLE public.email_templates_global ( ); -ALTER TABLE public.email_templates_global OWNER TO supabase_admin; - -- --- Name: email_templates_tenant; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: email_templates_tenant; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.email_templates_tenant ( @@ -9412,10 +9662,8 @@ CREATE TABLE public.email_templates_tenant ( ); -ALTER TABLE public.email_templates_tenant OWNER TO supabase_admin; - -- --- Name: entitlements_invalidation; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: entitlements_invalidation; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.entitlements_invalidation ( @@ -9424,10 +9672,8 @@ CREATE TABLE public.entitlements_invalidation ( ); -ALTER TABLE public.entitlements_invalidation OWNER TO supabase_admin; - -- --- Name: features; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: features; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.features ( @@ -9440,17 +9686,15 @@ CREATE TABLE public.features ( ); -ALTER TABLE public.features OWNER TO supabase_admin; - -- --- Name: COLUMN features.descricao; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN features.descricao; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON COLUMN public.features.descricao IS 'Descrição humana da feature (exibição no admin e documentação).'; +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 +-- Name: feriados; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.feriados ( @@ -9469,10 +9713,8 @@ CREATE TABLE public.feriados ( ); -ALTER TABLE public.feriados OWNER TO supabase_admin; - -- --- Name: financial_categories; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: financial_categories; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.financial_categories ( @@ -9487,10 +9729,8 @@ CREATE TABLE public.financial_categories ( ); -ALTER TABLE public.financial_categories OWNER TO supabase_admin; - -- --- Name: financial_exceptions; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: financial_exceptions; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.financial_exceptions ( @@ -9509,10 +9749,8 @@ CREATE TABLE public.financial_exceptions ( ); -ALTER TABLE public.financial_exceptions OWNER TO supabase_admin; - -- --- Name: global_notices; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: global_notices; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.global_notices ( @@ -9552,10 +9790,8 @@ CREATE TABLE public.global_notices ( ); -ALTER TABLE public.global_notices OWNER TO supabase_admin; - -- --- Name: insurance_plan_services; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: insurance_plan_services; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.insurance_plan_services ( @@ -9569,10 +9805,8 @@ CREATE TABLE public.insurance_plan_services ( ); -ALTER TABLE public.insurance_plan_services OWNER TO supabase_admin; - -- --- Name: insurance_plans; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: insurance_plans; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.insurance_plans ( @@ -9588,10 +9822,8 @@ CREATE TABLE public.insurance_plans ( ); -ALTER TABLE public.insurance_plans OWNER TO supabase_admin; - -- --- Name: login_carousel_slides; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: login_carousel_slides; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.login_carousel_slides ( @@ -9606,10 +9838,130 @@ CREATE TABLE public.login_carousel_slides ( ); -ALTER TABLE public.login_carousel_slides OWNER TO supabase_admin; +-- +-- Name: medicos; Type: TABLE; Schema: public; Owner: - +-- + +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() +); + -- --- Name: module_features; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: TABLE medicos; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.medicos IS 'Médicos e profissionais de referência cadastrados pelo terapeuta.'; + + +-- +-- Name: COLUMN medicos.owner_id; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.owner_id IS 'Terapeuta dono do cadastro (auth.uid()).'; + + +-- +-- Name: COLUMN medicos.tenant_id; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.tenant_id IS 'Tenant do terapeuta.'; + + +-- +-- Name: COLUMN medicos.nome; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.nome IS 'Nome completo do médico/profissional.'; + + +-- +-- Name: COLUMN medicos.crm; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.crm IS 'CRM com UF. Ex: 123456/SP. Único por owner_id.'; + + +-- +-- Name: COLUMN medicos.especialidade; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.especialidade IS 'Especialidade médica. Ex: Psiquiatria, Neurologia.'; + + +-- +-- Name: COLUMN medicos.telefone_profissional; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.telefone_profissional IS 'Telefone do consultório ou clínica.'; + + +-- +-- Name: COLUMN medicos.telefone_pessoal; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.telefone_pessoal IS 'Telefone pessoal / WhatsApp. Campo sensível.'; + + +-- +-- Name: COLUMN medicos.email; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.email IS 'E-mail profissional.'; + + +-- +-- Name: COLUMN medicos.clinica; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.clinica IS 'Nome da clínica ou hospital onde atua.'; + + +-- +-- Name: COLUMN medicos.cidade; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.cidade IS 'Cidade de atuação.'; + + +-- +-- Name: COLUMN medicos.estado; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.estado IS 'UF de atuação. Default SP.'; + + +-- +-- Name: COLUMN medicos.observacoes; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.observacoes IS 'Notas internas do terapeuta sobre o médico.'; + + +-- +-- Name: COLUMN medicos.ativo; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.ativo IS 'Soft delete: false oculta da listagem.'; + + +-- +-- Name: module_features; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.module_features ( @@ -9621,10 +9973,8 @@ CREATE TABLE public.module_features ( ); -ALTER TABLE public.module_features OWNER TO supabase_admin; - -- --- Name: modules; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: modules; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.modules ( @@ -9637,10 +9987,8 @@ CREATE TABLE public.modules ( ); -ALTER TABLE public.modules OWNER TO supabase_admin; - -- --- Name: notice_dismissals; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: notice_dismissals; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.notice_dismissals ( @@ -9652,10 +10000,8 @@ CREATE TABLE public.notice_dismissals ( ); -ALTER TABLE public.notice_dismissals OWNER TO supabase_admin; - -- --- Name: notification_channels; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: notification_channels; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.notification_channels ( @@ -9674,16 +10020,70 @@ 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: COLUMN notification_channels.twilio_subaccount_sid; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.twilio_subaccount_sid IS 'SID da subconta Twilio criada para este tenant'; + -- --- Name: notification_logs; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: COLUMN notification_channels.twilio_phone_number; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.twilio_phone_number IS 'Número WhatsApp provisionado (E.164, ex: +5511999990000)'; + + +-- +-- Name: COLUMN notification_channels.twilio_phone_sid; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.twilio_phone_sid IS 'SID do número de telefone na subconta Twilio'; + + +-- +-- Name: COLUMN notification_channels.webhook_url; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.webhook_url IS 'URL do webhook configurada na Twilio para receber callbacks de status'; + + +-- +-- Name: COLUMN notification_channels.cost_per_message_usd; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.cost_per_message_usd IS 'Custo real Twilio por mensagem WhatsApp (USD)'; + + +-- +-- Name: COLUMN notification_channels.price_per_message_brl; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.price_per_message_brl IS 'Valor cobrado do tenant por mensagem (BRL, inclui margem SaaS)'; + + +-- +-- Name: COLUMN notification_channels.provisioned_at; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.provisioned_at IS 'Timestamp do provisionamento da subconta'; + + +-- +-- Name: notification_logs; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.notification_logs ( @@ -9716,10 +10116,8 @@ CREATE TABLE public.notification_logs ( ); -ALTER TABLE public.notification_logs OWNER TO supabase_admin; - -- --- Name: notification_preferences; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: notification_preferences; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.notification_preferences ( @@ -9744,10 +10142,8 @@ CREATE TABLE public.notification_preferences ( ); -ALTER TABLE public.notification_preferences OWNER TO supabase_admin; - -- --- Name: notification_queue; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: notification_queue; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.notification_queue ( @@ -9777,10 +10173,8 @@ CREATE TABLE public.notification_queue ( ); -ALTER TABLE public.notification_queue OWNER TO supabase_admin; - -- --- Name: notification_schedules; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: notification_schedules; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.notification_schedules ( @@ -9808,10 +10202,8 @@ CREATE TABLE public.notification_schedules ( ); -ALTER TABLE public.notification_schedules OWNER TO supabase_admin; - -- --- Name: notification_templates; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: notification_templates; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.notification_templates ( @@ -9841,10 +10233,8 @@ CREATE TABLE public.notification_templates ( ); -ALTER TABLE public.notification_templates OWNER TO supabase_admin; - -- --- Name: notifications; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: notifications; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.notifications ( @@ -9862,10 +10252,8 @@ CREATE TABLE public.notifications ( ); -ALTER TABLE public.notifications OWNER TO supabase_admin; - -- --- Name: plan_features; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: plan_features; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.plan_features ( @@ -9877,10 +10265,8 @@ CREATE TABLE public.plan_features ( ); -ALTER TABLE public.plan_features OWNER TO supabase_admin; - -- --- Name: tenant_modules; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: tenant_modules; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.tenant_modules ( @@ -9896,10 +10282,8 @@ CREATE TABLE public.tenant_modules ( ); -ALTER TABLE public.tenant_modules OWNER TO supabase_admin; - -- --- Name: owner_feature_entitlements; Type: VIEW; Schema: public; Owner: supabase_admin +-- Name: owner_feature_entitlements; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.owner_feature_entitlements AS @@ -9931,10 +10315,8 @@ CREATE VIEW public.owner_feature_entitlements AS 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 +-- Name: owner_users; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.owner_users ( @@ -9945,10 +10327,53 @@ CREATE TABLE public.owner_users ( ); -ALTER TABLE public.owner_users OWNER TO supabase_admin; +-- +-- Name: patient_contacts; Type: TABLE; Schema: public; Owner: - +-- + +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]))) +); + -- --- Name: patient_discounts; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: TABLE patient_contacts; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.patient_contacts IS 'Contatos vinculados ao paciente: emergência, responsável legal, outros profissionais de saúde'; + + +-- +-- Name: COLUMN patient_contacts.tipo; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patient_contacts.tipo IS 'Categoria do contato: emergencia | responsavel_legal | profissional_saude | outro'; + + +-- +-- Name: COLUMN patient_contacts.is_primario; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patient_contacts.is_primario IS 'Contato de emergência principal — exibido em destaque no cadastro'; + + +-- +-- Name: patient_discounts; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.patient_discounts ( @@ -9966,10 +10391,8 @@ CREATE TABLE public.patient_discounts ( ); -ALTER TABLE public.patient_discounts OWNER TO supabase_admin; - -- --- Name: patient_group_patient; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: patient_group_patient; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.patient_group_patient ( @@ -9980,10 +10403,8 @@ CREATE TABLE public.patient_group_patient ( ); -ALTER TABLE public.patient_group_patient OWNER TO supabase_admin; - -- --- Name: patient_groups; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: patient_groups; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.patient_groups ( @@ -10001,10 +10422,8 @@ CREATE TABLE public.patient_groups ( ); -ALTER TABLE public.patient_groups OWNER TO supabase_admin; - -- --- Name: patient_intake_requests; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: patient_intake_requests; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.patient_intake_requests ( @@ -10049,10 +10468,8 @@ CREATE TABLE public.patient_intake_requests ( ); -ALTER TABLE public.patient_intake_requests OWNER TO supabase_admin; - -- --- Name: patient_invites; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: patient_invites; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.patient_invites ( @@ -10068,10 +10485,8 @@ CREATE TABLE public.patient_invites ( ); -ALTER TABLE public.patient_invites OWNER TO supabase_admin; - -- --- Name: patient_patient_tag; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: patient_patient_tag; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.patient_patient_tag ( @@ -10083,10 +10498,75 @@ CREATE TABLE public.patient_patient_tag ( ); -ALTER TABLE public.patient_patient_tag OWNER TO supabase_admin; +-- +-- Name: patient_status_history; Type: TABLE; Schema: public; Owner: - +-- + +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]))) +); + -- --- Name: patient_tags; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: TABLE patient_status_history; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.patient_status_history IS 'Histórico imutável de todas as mudanças de status do paciente — não editar, apenas inserir'; + + +-- +-- Name: patient_support_contacts; Type: TABLE; Schema: public; Owner: - +-- + +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() +); + + +-- +-- Name: TABLE patient_support_contacts; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.patient_support_contacts IS 'Rede de suporte do paciente. Exibida no card "Contatos & rede de suporte" do perfil.'; + + +-- +-- Name: COLUMN patient_support_contacts.tipo; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patient_support_contacts.tipo IS 'emergencia | familiar | profissional_saude | amigo | outro'; + + +-- +-- Name: COLUMN patient_support_contacts.is_primario; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patient_support_contacts.is_primario IS 'true = badge vermelho "emergência" no perfil do paciente.'; + + +-- +-- Name: patient_tags; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.patient_tags ( @@ -10101,10 +10581,50 @@ CREATE TABLE public.patient_tags ( ); -ALTER TABLE public.patient_tags OWNER TO supabase_admin; +-- +-- Name: patient_timeline; Type: TABLE; Schema: public; Owner: - +-- + +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]))) +); + -- --- Name: patients; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: TABLE patient_timeline; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.patient_timeline IS 'Feed cronológico de eventos do paciente — alimentado por triggers e inserções manuais'; + + +-- +-- Name: COLUMN patient_timeline.link_ref_tipo; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patient_timeline.link_ref_tipo IS 'Tipo da entidade referenciada (polimórfico): agenda_evento | financial_record | documento | escala'; + + +-- +-- Name: COLUMN patient_timeline.link_ref_id; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patient_timeline.link_ref_id IS 'ID da entidade referenciada — sem FK formal para suportar múltiplos tipos'; + + +-- +-- Name: patients; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.patients ( @@ -10155,25 +10675,185 @@ 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]))) ); -ALTER TABLE public.patients OWNER TO supabase_admin; - -- --- Name: COLUMN patients.avatar_url; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN patients.avatar_url; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON COLUMN public.patients.avatar_url IS 'URL pública da imagem de avatar armazenada no Supabase Storage'; +COMMENT ON COLUMN public.patients.avatar_url IS 'URL p??blica da imagem de avatar armazenada no Supabase Storage'; -- --- Name: payment_settings; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: COLUMN patients.nome_social; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.nome_social IS 'Nome social / como prefere ser chamado(a) no atendimento.'; + + +-- +-- Name: COLUMN patients.pronomes; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.pronomes IS 'Pronomes de tratamento. Ex: ela/dela, ele/dele. Exibido no header do perfil.'; + + +-- +-- Name: COLUMN patients.etnia; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.etnia IS 'Etnia / raça autodeclarada. Exibida no card "Dados pessoais".'; + + +-- +-- Name: COLUMN patients.religiao; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.religiao IS 'Religião ou espiritualidade (opcional, relevante clinicamente)'; + + +-- +-- Name: COLUMN patients.faixa_renda; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.faixa_renda IS 'Faixa de renda em salários mínimos — usado para precificação solidária'; + + +-- +-- Name: COLUMN patients.canal_preferido; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.canal_preferido IS 'Canal preferido de contato. Ex: WhatsApp, Telefone, E-mail.'; + + +-- +-- Name: COLUMN patients.horario_contato_inicio; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.horario_contato_inicio IS 'Início da janela de horário preferida para contato'; + + +-- +-- Name: COLUMN patients.horario_contato_fim; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.horario_contato_fim IS 'Fim da janela de horário preferida para contato'; + + +-- +-- Name: COLUMN patients.origem; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.origem IS 'Como o paciente chegou: indicacao, agendador, redes_sociais, encaminhamento, outro'; + + +-- +-- Name: COLUMN patients.metodo_pagamento_preferido; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.metodo_pagamento_preferido IS 'Método de pagamento preferido. Ex: PIX, Cartão crédito. Exibido no card Origem.'; + + +-- +-- Name: COLUMN patients.motivo_saida; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.motivo_saida IS 'Motivo de encerramento do acompanhamento. Exibido no card Origem quando preenchido.'; + + +-- +-- Name: COLUMN patients.data_saida; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.data_saida IS 'Data em que o paciente foi desligado/encaminhado'; + + +-- +-- Name: COLUMN patients.encaminhado_para; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.encaminhado_para IS 'Nome ou serviço para onde o paciente foi encaminhado'; + + +-- +-- Name: COLUMN patients.risco_elevado; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.risco_elevado IS 'Flag de atenção clínica — exibe alerta no topo do cadastro e prontuário'; + + +-- +-- Name: COLUMN patients.risco_nota; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.risco_nota IS 'Descrição do risco (obrigatória quando risco_elevado = true)'; + + +-- +-- Name: COLUMN patients.risco_sinalizado_em; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.risco_sinalizado_em IS 'Timestamp em que o risco foi sinalizado'; + + +-- +-- Name: COLUMN patients.risco_sinalizado_por; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.risco_sinalizado_por IS 'Usuário que sinalizou o risco'; + + +-- +-- Name: COLUMN patients.horario_contato; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.horario_contato IS 'Horário preferido para contato. Ex: 08h–18h.'; + + +-- +-- Name: COLUMN patients.convenio; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.convenio IS 'Nome do convênio para exibição (badge azul no header). Derivado de convenio_id.'; + + +-- +-- Name: COLUMN patients.convenio_id; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.convenio_id IS 'FK para insurance_plans.id. Vincula o paciente ao convênio cadastrado.'; + + +-- +-- Name: payment_settings; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.payment_settings ( @@ -10202,10 +10882,8 @@ CREATE TABLE public.payment_settings ( ); -ALTER TABLE public.payment_settings OWNER TO supabase_admin; - -- --- Name: plan_prices; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: plan_prices; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.plan_prices ( @@ -10226,17 +10904,15 @@ CREATE TABLE public.plan_prices ( ); -ALTER TABLE public.plan_prices OWNER TO supabase_admin; - -- --- Name: TABLE plan_prices; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: TABLE plan_prices; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON TABLE public.plan_prices IS 'Histórico de preços por plano (fonte: manual/gateway).'; +COMMENT ON TABLE public.plan_prices IS 'Hist??rico de pre??os por plano (fonte: manual/gateway).'; -- --- Name: plan_public; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: plan_public; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.plan_public ( @@ -10252,17 +10928,15 @@ CREATE TABLE public.plan_public ( ); -ALTER TABLE public.plan_public OWNER TO supabase_admin; - -- --- Name: TABLE plan_public; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: TABLE plan_public; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON TABLE public.plan_public IS 'Configuração de vitrine (página pública) dos planos.'; +COMMENT ON TABLE public.plan_public IS 'Configura????o de vitrine (p??gina p??blica) dos planos.'; -- --- Name: plan_public_bullets; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: plan_public_bullets; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.plan_public_bullets ( @@ -10275,10 +10949,8 @@ CREATE TABLE public.plan_public_bullets ( ); -ALTER TABLE public.plan_public_bullets OWNER TO supabase_admin; - -- --- Name: plans; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: plans; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.plans ( @@ -10297,31 +10969,29 @@ CREATE TABLE public.plans ( ); -ALTER TABLE public.plans OWNER TO supabase_admin; - -- --- Name: COLUMN plans.name; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN plans.name; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON COLUMN public.plans.name IS 'Nome interno do plano (admin). A key é técnica/imutável.'; +COMMENT ON COLUMN public.plans.name IS 'Nome interno do plano (admin). A key ?? t??cnica/imut??vel.'; -- --- Name: COLUMN plans.target; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN plans.target; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON COLUMN public.plans.target IS 'Público-alvo do plano: patient, therapist ou clinic.'; +COMMENT ON COLUMN public.plans.target IS 'P??blico-alvo do plano: patient, therapist ou clinic.'; -- --- Name: COLUMN plans.max_supervisees; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN plans.max_supervisees; Type: COMMENT; Schema: public; Owner: - -- COMMENT ON COLUMN public.plans.max_supervisees IS 'Limite de terapeutas que podem ser supervisionados. Apenas para planos target=supervisor. NULL = sem limite.'; -- --- Name: professional_pricing; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: professional_pricing; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.professional_pricing ( @@ -10336,17 +11006,15 @@ CREATE TABLE public.professional_pricing ( ); -ALTER TABLE public.professional_pricing OWNER TO supabase_admin; - -- --- Name: TABLE professional_pricing; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: TABLE professional_pricing; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON TABLE public.professional_pricing IS 'DEPRECATED: substituída por public.services. Manter até próxima release de limpeza.'; +COMMENT ON TABLE public.professional_pricing IS 'DEPRECATED: substitu??da por public.services. Manter at?? pr??xima release de limpeza.'; -- --- Name: profiles; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: profiles; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.profiles ( @@ -10374,106 +11042,105 @@ CREATE TABLE public.profiles ( 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]))) ); -ALTER TABLE public.profiles OWNER TO supabase_admin; - -- --- Name: COLUMN profiles.phone; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN profiles.phone; Type: COMMENT; Schema: public; Owner: - -- COMMENT ON COLUMN public.profiles.phone IS 'WhatsApp / telefone no formato (99) 99999-9999'; -- --- Name: COLUMN profiles.bio; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN profiles.bio; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON COLUMN public.profiles.bio IS 'Breve apresentação pública (máx 300 chars no front)'; +COMMENT ON COLUMN public.profiles.bio IS 'Breve apresenta????o p??blica (m??x 300 chars no front)'; -- --- Name: COLUMN profiles.account_type; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN profiles.account_type; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON COLUMN public.profiles.account_type IS 'Tipo de conta: free=sem perfil ainda, patient=paciente (imutável), therapist=terapeuta (imutável), clinic=clínica (imutável).'; +COMMENT ON COLUMN public.profiles.account_type IS 'Tipo de conta: free=sem perfil ainda, patient=paciente (imut??vel), therapist=terapeuta (imut??vel), clinic=cl??nica (imut??vel).'; -- --- Name: COLUMN profiles.platform_roles; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN profiles.platform_roles; Type: COMMENT; Schema: public; Owner: - -- COMMENT ON COLUMN public.profiles.platform_roles IS 'Papéis globais de plataforma, independentes de tenant. Ex: editor de microlearning. Atribuído pelo saas_admin.'; -- --- Name: COLUMN profiles.nickname; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN profiles.nickname; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON COLUMN public.profiles.nickname IS 'Apelido preferido para comunicação interna (Agência PSI)'; +COMMENT ON COLUMN public.profiles.nickname IS 'Apelido preferido para comunica????o interna (Ag??ncia PSI)'; -- --- Name: COLUMN profiles.work_description; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN profiles.work_description; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON COLUMN public.profiles.work_description IS 'Categoria de trabalho selecionada no perfil público'; +COMMENT ON COLUMN public.profiles.work_description IS 'Categoria de trabalho selecionada no perfil p??blico'; -- --- Name: COLUMN profiles.work_description_other; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN profiles.work_description_other; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON COLUMN public.profiles.work_description_other IS 'Descrição livre quando work_description = ''outro'''; +COMMENT ON COLUMN public.profiles.work_description_other IS 'Descri????o livre quando work_description = ''outro'''; -- --- Name: COLUMN profiles.site_url; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN profiles.site_url; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON COLUMN public.profiles.site_url IS 'Endereço do site pessoal ou profissional'; +COMMENT ON COLUMN public.profiles.site_url IS 'Endere??o do site pessoal ou profissional'; -- --- Name: COLUMN profiles.social_instagram; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN profiles.social_instagram; Type: COMMENT; Schema: public; Owner: - -- COMMENT ON COLUMN public.profiles.social_instagram IS 'Handle ou URL do Instagram'; -- --- Name: COLUMN profiles.social_youtube; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN profiles.social_youtube; Type: COMMENT; Schema: public; Owner: - -- COMMENT ON COLUMN public.profiles.social_youtube IS 'Handle ou URL do canal no YouTube'; -- --- Name: COLUMN profiles.social_facebook; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN profiles.social_facebook; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON COLUMN public.profiles.social_facebook IS 'Handle ou URL da página no Facebook'; +COMMENT ON COLUMN public.profiles.social_facebook IS 'Handle ou URL da p??gina no Facebook'; -- --- Name: COLUMN profiles.social_x; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN profiles.social_x; Type: COMMENT; Schema: public; Owner: - -- COMMENT ON COLUMN public.profiles.social_x IS 'Handle ou URL do perfil no X (Twitter)'; -- --- Name: COLUMN profiles.social_custom; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN profiles.social_custom; Type: COMMENT; Schema: public; Owner: - -- COMMENT ON COLUMN public.profiles.social_custom IS 'Array JSON com redes adicionais livres: [{name, url}]'; -- --- Name: recurrence_exceptions; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: recurrence_exceptions; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.recurrence_exceptions ( @@ -10495,10 +11162,8 @@ CREATE TABLE public.recurrence_exceptions ( ); -ALTER TABLE public.recurrence_exceptions OWNER TO supabase_admin; - -- --- Name: recurrence_rule_services; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: recurrence_rule_services; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.recurrence_rule_services ( @@ -10518,10 +11183,8 @@ CREATE TABLE public.recurrence_rule_services ( ); -ALTER TABLE public.recurrence_rule_services OWNER TO supabase_admin; - -- --- Name: recurrence_rules; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: recurrence_rules; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.recurrence_rules ( @@ -10561,10 +11224,8 @@ CREATE TABLE public.recurrence_rules ( ); -ALTER TABLE public.recurrence_rules OWNER TO supabase_admin; - -- --- Name: saas_admins; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: saas_admins; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.saas_admins ( @@ -10573,10 +11234,8 @@ CREATE TABLE public.saas_admins ( ); -ALTER TABLE public.saas_admins OWNER TO supabase_admin; - -- --- Name: saas_doc_votos; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: saas_doc_votos; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.saas_doc_votos ( @@ -10589,10 +11248,8 @@ CREATE TABLE public.saas_doc_votos ( ); -ALTER TABLE public.saas_doc_votos OWNER TO supabase_admin; - -- --- Name: saas_docs; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: saas_docs; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.saas_docs ( @@ -10615,24 +11272,22 @@ CREATE TABLE public.saas_docs ( ); -ALTER TABLE public.saas_docs OWNER TO supabase_admin; - -- --- Name: COLUMN saas_docs.categoria; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN saas_docs.categoria; Type: COMMENT; Schema: public; Owner: - -- 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 +-- Name: COLUMN saas_docs.exibir_no_faq; Type: COMMENT; Schema: public; Owner: - -- 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 +-- Name: saas_faq; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.saas_faq ( @@ -10655,10 +11310,8 @@ CREATE TABLE public.saas_faq ( ); -ALTER TABLE public.saas_faq OWNER TO supabase_admin; - -- --- Name: saas_faq_itens; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: saas_faq_itens; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.saas_faq_itens ( @@ -10673,17 +11326,15 @@ CREATE TABLE public.saas_faq_itens ( ); -ALTER TABLE public.saas_faq_itens OWNER TO supabase_admin; - -- --- Name: TABLE saas_faq_itens; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: TABLE saas_faq_itens; Type: COMMENT; Schema: public; Owner: - -- 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 +-- Name: services; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.services ( @@ -10700,10 +11351,8 @@ CREATE TABLE public.services ( ); -ALTER TABLE public.services OWNER TO supabase_admin; - -- --- Name: subscription_events; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: subscription_events; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.subscription_events ( @@ -10725,10 +11374,8 @@ CREATE TABLE public.subscription_events ( ); -ALTER TABLE public.subscription_events OWNER TO supabase_admin; - -- --- Name: subscription_intents_personal; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: subscription_intents_personal; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.subscription_intents_personal ( @@ -10752,10 +11399,8 @@ CREATE TABLE public.subscription_intents_personal ( ); -ALTER TABLE public.subscription_intents_personal OWNER TO supabase_admin; - -- --- Name: subscription_intents_tenant; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: subscription_intents_tenant; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.subscription_intents_tenant ( @@ -10780,10 +11425,8 @@ CREATE TABLE public.subscription_intents_tenant ( ); -ALTER TABLE public.subscription_intents_tenant OWNER TO supabase_admin; - -- --- Name: subscription_intents; Type: VIEW; Schema: public; Owner: supabase_admin +-- Name: subscription_intents; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.subscription_intents AS @@ -10826,10 +11469,8 @@ UNION ALL 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 +-- Name: subscription_intents_legacy; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.subscription_intents_legacy ( @@ -10852,10 +11493,8 @@ CREATE TABLE public.subscription_intents_legacy ( ); -ALTER TABLE public.subscription_intents_legacy OWNER TO supabase_admin; - -- --- Name: support_sessions; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: support_sessions; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.support_sessions ( @@ -10868,10 +11507,8 @@ CREATE TABLE public.support_sessions ( ); -ALTER TABLE public.support_sessions OWNER TO supabase_admin; - -- --- Name: tenant_feature_exceptions_log; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: tenant_feature_exceptions_log; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.tenant_feature_exceptions_log ( @@ -10885,10 +11522,8 @@ CREATE TABLE public.tenant_feature_exceptions_log ( ); -ALTER TABLE public.tenant_feature_exceptions_log OWNER TO supabase_admin; - -- --- Name: tenant_features; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: tenant_features; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.tenant_features ( @@ -10900,10 +11535,8 @@ CREATE TABLE public.tenant_features ( ); -ALTER TABLE public.tenant_features OWNER TO supabase_admin; - -- --- Name: tenant_invites; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: tenant_invites; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.tenant_invites ( @@ -10923,10 +11556,8 @@ CREATE TABLE public.tenant_invites ( ); -ALTER TABLE public.tenant_invites OWNER TO supabase_admin; - -- --- Name: tenants; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: tenants; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.tenants ( @@ -10934,21 +11565,27 @@ CREATE TABLE public.tenants ( 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]))) ); -ALTER TABLE public.tenants OWNER TO supabase_admin; - -- --- Name: COLUMN tenants.kind; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN tenants.kind; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON COLUMN public.tenants.kind IS 'Tipo do tenant. Imutável após criação. therapist=terapeuta solo. clinic_coworking/clinic_reception/clinic_full=clínicas. clinic e saas são legados.'; +COMMENT ON COLUMN public.tenants.kind IS 'Tipo do tenant. Imut??vel ap??s cria????o. therapist=terapeuta solo. clinic_coworking/clinic_reception/clinic_full=cl??nicas. clinic e saas s??o legados.'; -- --- Name: therapist_payout_records; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: COLUMN tenants.papel_timbrado; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.tenants.papel_timbrado IS 'Configuração do papel timbrado para geração de documentos (header, footer, margens)'; + + +-- +-- Name: therapist_payout_records; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.therapist_payout_records ( @@ -10957,10 +11594,40 @@ CREATE TABLE public.therapist_payout_records ( ); -ALTER TABLE public.therapist_payout_records OWNER TO supabase_admin; +-- +-- Name: twilio_subaccount_usage; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.twilio_subaccount_usage ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid 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, + CONSTRAINT twilio_subaccount_usage_period_check CHECK ((period_end >= period_start)) +); + -- --- Name: user_settings; Type: TABLE; Schema: public; Owner: supabase_admin +-- Name: TABLE twilio_subaccount_usage; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.twilio_subaccount_usage IS 'Consumo mensal de mensagens WhatsApp por subconta Twilio. Sincronizado via Edge Function.'; + + +-- +-- Name: user_settings; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.user_settings ( @@ -10979,66 +11646,64 @@ CREATE TABLE public.user_settings ( ); -ALTER TABLE public.user_settings OWNER TO supabase_admin; - -- --- Name: TABLE user_settings; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: TABLE user_settings; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON TABLE public.user_settings IS 'Preferências de aparência e layout por usuário'; +COMMENT ON TABLE public.user_settings IS 'Prefer??ncias de apar??ncia e layout por usu??rio'; -- --- Name: COLUMN user_settings.user_id; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN user_settings.user_id; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON COLUMN public.user_settings.user_id IS 'FK = auth.users.id — um registro por usuário'; +COMMENT ON COLUMN public.user_settings.user_id IS 'FK = auth.users.id ??? um registro por usu??rio'; -- --- Name: COLUMN user_settings.theme_mode; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN user_settings.theme_mode; Type: COMMENT; Schema: public; Owner: - -- COMMENT ON COLUMN public.user_settings.theme_mode IS 'light | dark'; -- --- Name: COLUMN user_settings.preset; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN user_settings.preset; Type: COMMENT; Schema: public; Owner: - -- COMMENT ON COLUMN public.user_settings.preset IS 'Preset PrimeVue: Aura | Lara | Nora'; -- --- Name: COLUMN user_settings.primary_color; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN user_settings.primary_color; Type: COMMENT; Schema: public; Owner: - -- -COMMENT ON COLUMN public.user_settings.primary_color IS 'Nome da cor primária (ex: blue, emerald, noir)'; +COMMENT ON COLUMN public.user_settings.primary_color IS 'Nome da cor prim??ria (ex: blue, emerald, noir)'; -- --- Name: COLUMN user_settings.surface_color; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN user_settings.surface_color; Type: COMMENT; Schema: public; Owner: - -- COMMENT ON COLUMN public.user_settings.surface_color IS 'Nome da surface (ex: slate, zinc, neutral)'; -- --- Name: COLUMN user_settings.menu_mode; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN user_settings.menu_mode; Type: COMMENT; Schema: public; Owner: - -- COMMENT ON COLUMN public.user_settings.menu_mode IS 'static | overlay'; -- --- Name: COLUMN user_settings.layout_variant; Type: COMMENT; Schema: public; Owner: supabase_admin +-- Name: COLUMN user_settings.layout_variant; Type: COMMENT; Schema: public; Owner: - -- COMMENT ON COLUMN public.user_settings.layout_variant IS 'classic (sidebar) | rail (mini rail + painel)'; -- --- Name: v_auth_users_public; Type: VIEW; Schema: public; Owner: supabase_admin +-- Name: v_auth_users_public; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.v_auth_users_public AS @@ -11049,10 +11714,8 @@ CREATE VIEW public.v_auth_users_public AS 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 +-- Name: v_cashflow_projection; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.v_cashflow_projection WITH (security_invoker='on') AS @@ -11072,17 +11735,15 @@ CREATE VIEW public.v_cashflow_projection WITH (security_invoker='on') AS 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 +-- Name: VIEW v_cashflow_projection; Type: COMMENT; Schema: public; Owner: - -- -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.'; +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 +-- Name: v_commitment_totals; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.v_commitment_totals AS @@ -11094,10 +11755,91 @@ CREATE VIEW public.v_commitment_totals AS GROUP BY c.tenant_id, c.id; -ALTER VIEW public.v_commitment_totals OWNER TO supabase_admin; +-- +-- Name: v_patient_engajamento; Type: VIEW; Schema: public; Owner: - +-- + +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)))); + -- --- Name: v_patient_groups_with_counts; Type: VIEW; Schema: public; Owner: supabase_admin +-- Name: VIEW v_patient_engajamento; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON VIEW public.v_patient_engajamento IS 'Score de engajamento e métricas consolidadas por paciente. Calculado em tempo real via RLS (security_invoker=on).'; + + +-- +-- Name: v_patient_groups_with_counts; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.v_patient_groups_with_counts AS @@ -11115,10 +11857,42 @@ CREATE VIEW public.v_patient_groups_with_counts AS 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_patients_risco; Type: VIEW; Schema: public; Owner: - +-- + +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))); + -- --- Name: v_plan_active_prices; Type: VIEW; Schema: public; Owner: supabase_admin +-- Name: VIEW v_patients_risco; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON VIEW public.v_patients_risco IS 'Pacientes ativos que precisam de atenção: risco clínico, sem sessão há 30+ dias, baixo comparecimento ou cobrança vencida'; + + +-- +-- Name: v_plan_active_prices; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.v_plan_active_prices AS @@ -11147,10 +11921,8 @@ CREATE VIEW public.v_plan_active_prices AS 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 +-- Name: v_public_pricing; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.v_public_pricing AS @@ -11177,10 +11949,8 @@ CREATE VIEW public.v_public_pricing AS 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 +-- Name: v_subscription_feature_mismatch; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.v_subscription_feature_mismatch AS @@ -11208,10 +11978,8 @@ CREATE VIEW public.v_subscription_feature_mismatch AS 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 +-- Name: v_subscription_health; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.v_subscription_health AS @@ -11239,10 +12007,8 @@ CREATE VIEW public.v_subscription_health AS 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 +-- Name: v_subscription_health_v2; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.v_subscription_health_v2 AS @@ -11270,10 +12036,8 @@ CREATE VIEW public.v_subscription_health_v2 AS 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 +-- Name: v_tag_patient_counts; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.v_tag_patient_counts AS @@ -11291,10 +12055,8 @@ CREATE VIEW public.v_tag_patient_counts AS 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 +-- Name: v_tenant_active_subscription; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.v_tenant_active_subscription AS @@ -11311,10 +12073,8 @@ CREATE VIEW public.v_tenant_active_subscription AS 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 +-- Name: v_tenant_entitlements; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.v_tenant_entitlements AS @@ -11326,10 +12086,8 @@ CREATE VIEW public.v_tenant_entitlements AS 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 +-- Name: v_tenant_entitlements_full; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.v_tenant_entitlements_full AS @@ -11345,10 +12103,8 @@ CREATE VIEW public.v_tenant_entitlements_full AS 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 +-- Name: v_tenant_entitlements_json; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.v_tenant_entitlements_json AS @@ -11359,10 +12115,8 @@ CREATE VIEW public.v_tenant_entitlements_json AS 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 +-- Name: v_tenant_feature_exceptions; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.v_tenant_feature_exceptions AS @@ -11376,10 +12130,8 @@ CREATE VIEW public.v_tenant_feature_exceptions AS 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 +-- Name: v_tenant_feature_mismatch; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.v_tenant_feature_mismatch AS @@ -11405,10 +12157,8 @@ CREATE VIEW public.v_tenant_feature_mismatch AS 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 +-- Name: v_tenant_members_with_profiles; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.v_tenant_members_with_profiles AS @@ -11425,10 +12175,8 @@ CREATE VIEW public.v_tenant_members_with_profiles AS 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 +-- Name: v_tenant_people; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.v_tenant_people AS @@ -11455,10 +12203,8 @@ UNION ALL 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 +-- Name: v_tenant_staff; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.v_tenant_staff AS @@ -11488,10 +12234,46 @@ UNION ALL 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_twilio_whatsapp_overview; Type: VIEW; Schema: public; Owner: - +-- + +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)); + -- --- Name: v_user_active_subscription; Type: VIEW; Schema: public; Owner: supabase_admin +-- Name: VIEW v_twilio_whatsapp_overview; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON VIEW public.v_twilio_whatsapp_overview IS 'Visão consolidada de subcontas Twilio WhatsApp com uso do mês corrente.'; + + +-- +-- Name: v_user_active_subscription; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.v_user_active_subscription AS @@ -11508,10 +12290,8 @@ CREATE VIEW public.v_user_active_subscription AS 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 +-- Name: v_user_entitlements; Type: VIEW; Schema: public; Owner: - -- CREATE VIEW public.v_user_entitlements AS @@ -11523,10 +12303,8 @@ CREATE VIEW public.v_user_entitlements AS 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 +-- Name: messages; Type: TABLE; Schema: realtime; Owner: - -- CREATE TABLE realtime.messages ( @@ -11542,13 +12320,11 @@ CREATE TABLE realtime.messages ( 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 +-- Name: messages_2026_04_14; Type: TABLE; Schema: realtime; Owner: - -- -CREATE TABLE realtime.messages_2026_03_20 ( +CREATE TABLE realtime.messages_2026_04_14 ( topic text NOT NULL, extension text NOT NULL, payload jsonb, @@ -11560,13 +12336,11 @@ CREATE TABLE realtime.messages_2026_03_20 ( ); -ALTER TABLE realtime.messages_2026_03_20 OWNER TO supabase_admin; - -- --- Name: messages_2026_03_21; Type: TABLE; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_15; Type: TABLE; Schema: realtime; Owner: - -- -CREATE TABLE realtime.messages_2026_03_21 ( +CREATE TABLE realtime.messages_2026_04_15 ( topic text NOT NULL, extension text NOT NULL, payload jsonb, @@ -11578,13 +12352,11 @@ CREATE TABLE realtime.messages_2026_03_21 ( ); -ALTER TABLE realtime.messages_2026_03_21 OWNER TO supabase_admin; - -- --- Name: messages_2026_03_22; Type: TABLE; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_16; Type: TABLE; Schema: realtime; Owner: - -- -CREATE TABLE realtime.messages_2026_03_22 ( +CREATE TABLE realtime.messages_2026_04_16 ( topic text NOT NULL, extension text NOT NULL, payload jsonb, @@ -11596,13 +12368,11 @@ CREATE TABLE realtime.messages_2026_03_22 ( ); -ALTER TABLE realtime.messages_2026_03_22 OWNER TO supabase_admin; - -- --- Name: messages_2026_03_23; Type: TABLE; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_17; Type: TABLE; Schema: realtime; Owner: - -- -CREATE TABLE realtime.messages_2026_03_23 ( +CREATE TABLE realtime.messages_2026_04_17 ( topic text NOT NULL, extension text NOT NULL, payload jsonb, @@ -11614,13 +12384,11 @@ CREATE TABLE realtime.messages_2026_03_23 ( ); -ALTER TABLE realtime.messages_2026_03_23 OWNER TO supabase_admin; - -- --- Name: messages_2026_03_24; Type: TABLE; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_18; Type: TABLE; Schema: realtime; Owner: - -- -CREATE TABLE realtime.messages_2026_03_24 ( +CREATE TABLE realtime.messages_2026_04_18 ( topic text NOT NULL, extension text NOT NULL, payload jsonb, @@ -11632,13 +12400,11 @@ CREATE TABLE realtime.messages_2026_03_24 ( ); -ALTER TABLE realtime.messages_2026_03_24 OWNER TO supabase_admin; - -- --- Name: messages_2026_03_25; Type: TABLE; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_19; Type: TABLE; Schema: realtime; Owner: - -- -CREATE TABLE realtime.messages_2026_03_25 ( +CREATE TABLE realtime.messages_2026_04_19 ( topic text NOT NULL, extension text NOT NULL, payload jsonb, @@ -11650,13 +12416,11 @@ CREATE TABLE realtime.messages_2026_03_25 ( ); -ALTER TABLE realtime.messages_2026_03_25 OWNER TO supabase_admin; - -- --- Name: messages_2026_03_26; Type: TABLE; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_20; Type: TABLE; Schema: realtime; Owner: - -- -CREATE TABLE realtime.messages_2026_03_26 ( +CREATE TABLE realtime.messages_2026_04_20 ( topic text NOT NULL, extension text NOT NULL, payload jsonb, @@ -11668,10 +12432,8 @@ CREATE TABLE realtime.messages_2026_03_26 ( ); -ALTER TABLE realtime.messages_2026_03_26 OWNER TO supabase_admin; - -- --- Name: schema_migrations; Type: TABLE; Schema: realtime; Owner: supabase_admin +-- Name: schema_migrations; Type: TABLE; Schema: realtime; Owner: - -- CREATE TABLE realtime.schema_migrations ( @@ -11680,10 +12442,8 @@ CREATE TABLE realtime.schema_migrations ( ); -ALTER TABLE realtime.schema_migrations OWNER TO supabase_admin; - -- --- Name: subscription; Type: TABLE; Schema: realtime; Owner: supabase_admin +-- Name: subscription; Type: TABLE; Schema: realtime; Owner: - -- CREATE TABLE realtime.subscription ( @@ -11697,10 +12457,8 @@ CREATE TABLE realtime.subscription ( ); -ALTER TABLE realtime.subscription OWNER TO supabase_admin; - -- --- Name: subscription_id_seq; Type: SEQUENCE; Schema: realtime; Owner: supabase_admin +-- Name: subscription_id_seq; Type: SEQUENCE; Schema: realtime; Owner: - -- ALTER TABLE realtime.subscription ALTER COLUMN id ADD GENERATED ALWAYS AS IDENTITY ( @@ -11714,7 +12472,7 @@ ALTER TABLE realtime.subscription ALTER COLUMN id ADD GENERATED ALWAYS AS IDENTI -- --- Name: buckets; Type: TABLE; Schema: storage; Owner: supabase_storage_admin +-- Name: buckets; Type: TABLE; Schema: storage; Owner: - -- CREATE TABLE storage.buckets ( @@ -11732,17 +12490,15 @@ CREATE TABLE storage.buckets ( ); -ALTER TABLE storage.buckets OWNER TO supabase_storage_admin; - -- --- Name: COLUMN buckets.owner; Type: COMMENT; Schema: storage; Owner: supabase_storage_admin +-- Name: COLUMN buckets.owner; Type: COMMENT; Schema: storage; Owner: - -- 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 +-- Name: buckets_analytics; Type: TABLE; Schema: storage; Owner: - -- CREATE TABLE storage.buckets_analytics ( @@ -11756,10 +12512,8 @@ CREATE TABLE storage.buckets_analytics ( ); -ALTER TABLE storage.buckets_analytics OWNER TO supabase_storage_admin; - -- --- Name: buckets_vectors; Type: TABLE; Schema: storage; Owner: supabase_storage_admin +-- Name: buckets_vectors; Type: TABLE; Schema: storage; Owner: - -- CREATE TABLE storage.buckets_vectors ( @@ -11770,10 +12524,8 @@ CREATE TABLE storage.buckets_vectors ( ); -ALTER TABLE storage.buckets_vectors OWNER TO supabase_storage_admin; - -- --- Name: iceberg_namespaces; Type: TABLE; Schema: storage; Owner: supabase_storage_admin +-- Name: iceberg_namespaces; Type: TABLE; Schema: storage; Owner: - -- CREATE TABLE storage.iceberg_namespaces ( @@ -11787,10 +12539,8 @@ CREATE TABLE storage.iceberg_namespaces ( ); -ALTER TABLE storage.iceberg_namespaces OWNER TO supabase_storage_admin; - -- --- Name: iceberg_tables; Type: TABLE; Schema: storage; Owner: supabase_storage_admin +-- Name: iceberg_tables; Type: TABLE; Schema: storage; Owner: - -- CREATE TABLE storage.iceberg_tables ( @@ -11808,10 +12558,8 @@ CREATE TABLE storage.iceberg_tables ( ); -ALTER TABLE storage.iceberg_tables OWNER TO supabase_storage_admin; - -- --- Name: migrations; Type: TABLE; Schema: storage; Owner: supabase_storage_admin +-- Name: migrations; Type: TABLE; Schema: storage; Owner: - -- CREATE TABLE storage.migrations ( @@ -11822,10 +12570,8 @@ CREATE TABLE storage.migrations ( ); -ALTER TABLE storage.migrations OWNER TO supabase_storage_admin; - -- --- Name: objects; Type: TABLE; Schema: storage; Owner: supabase_storage_admin +-- Name: objects; Type: TABLE; Schema: storage; Owner: - -- CREATE TABLE storage.objects ( @@ -11844,17 +12590,15 @@ CREATE TABLE storage.objects ( ); -ALTER TABLE storage.objects OWNER TO supabase_storage_admin; - -- --- Name: COLUMN objects.owner; Type: COMMENT; Schema: storage; Owner: supabase_storage_admin +-- Name: COLUMN objects.owner; Type: COMMENT; Schema: storage; Owner: - -- 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 +-- Name: s3_multipart_uploads; Type: TABLE; Schema: storage; Owner: - -- CREATE TABLE storage.s3_multipart_uploads ( @@ -11870,10 +12614,8 @@ CREATE TABLE storage.s3_multipart_uploads ( ); -ALTER TABLE storage.s3_multipart_uploads OWNER TO supabase_storage_admin; - -- --- Name: s3_multipart_uploads_parts; Type: TABLE; Schema: storage; Owner: supabase_storage_admin +-- Name: s3_multipart_uploads_parts; Type: TABLE; Schema: storage; Owner: - -- CREATE TABLE storage.s3_multipart_uploads_parts ( @@ -11890,10 +12632,8 @@ CREATE TABLE storage.s3_multipart_uploads_parts ( ); -ALTER TABLE storage.s3_multipart_uploads_parts OWNER TO supabase_storage_admin; - -- --- Name: vector_indexes; Type: TABLE; Schema: storage; Owner: supabase_storage_admin +-- Name: vector_indexes; Type: TABLE; Schema: storage; Owner: - -- CREATE TABLE storage.vector_indexes ( @@ -11909,10 +12649,8 @@ CREATE TABLE storage.vector_indexes ( ); -ALTER TABLE storage.vector_indexes OWNER TO supabase_storage_admin; - -- --- Name: hooks; Type: TABLE; Schema: supabase_functions; Owner: supabase_functions_admin +-- Name: hooks; Type: TABLE; Schema: supabase_functions; Owner: - -- CREATE TABLE supabase_functions.hooks ( @@ -11924,17 +12662,15 @@ CREATE TABLE supabase_functions.hooks ( ); -ALTER TABLE supabase_functions.hooks OWNER TO supabase_functions_admin; - -- --- Name: TABLE hooks; Type: COMMENT; Schema: supabase_functions; Owner: supabase_functions_admin +-- Name: TABLE hooks; Type: COMMENT; Schema: supabase_functions; Owner: - -- 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 +-- Name: hooks_id_seq; Type: SEQUENCE; Schema: supabase_functions; Owner: - -- CREATE SEQUENCE supabase_functions.hooks_id_seq @@ -11945,17 +12681,15 @@ CREATE SEQUENCE supabase_functions.hooks_id_seq 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 +-- Name: hooks_id_seq; Type: SEQUENCE OWNED BY; Schema: supabase_functions; Owner: - -- ALTER SEQUENCE supabase_functions.hooks_id_seq OWNED BY supabase_functions.hooks.id; -- --- Name: migrations; Type: TABLE; Schema: supabase_functions; Owner: supabase_functions_admin +-- Name: migrations; Type: TABLE; Schema: supabase_functions; Owner: - -- CREATE TABLE supabase_functions.migrations ( @@ -11964,80 +12698,85 @@ CREATE TABLE supabase_functions.migrations ( ); -ALTER TABLE supabase_functions.migrations OWNER TO supabase_functions_admin; - -- --- Name: messages_2026_03_20; Type: TABLE ATTACH; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_14; Type: TABLE ATTACH; Schema: realtime; Owner: - -- -ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_20 FOR VALUES FROM ('2026-03-20 00:00:00') TO ('2026-03-21 00:00:00'); +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_04_14 FOR VALUES FROM ('2026-04-14 00:00:00') TO ('2026-04-15 00:00:00'); -- --- Name: messages_2026_03_21; Type: TABLE ATTACH; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_15; Type: TABLE ATTACH; Schema: realtime; Owner: - -- -ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_21 FOR VALUES FROM ('2026-03-21 00:00:00') TO ('2026-03-22 00:00:00'); +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_04_15 FOR VALUES FROM ('2026-04-15 00:00:00') TO ('2026-04-16 00:00:00'); -- --- Name: messages_2026_03_22; Type: TABLE ATTACH; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_16; Type: TABLE ATTACH; Schema: realtime; Owner: - -- -ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_22 FOR VALUES FROM ('2026-03-22 00:00:00') TO ('2026-03-23 00:00:00'); +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_04_16 FOR VALUES FROM ('2026-04-16 00:00:00') TO ('2026-04-17 00:00:00'); -- --- Name: messages_2026_03_23; Type: TABLE ATTACH; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_17; Type: TABLE ATTACH; Schema: realtime; Owner: - -- -ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_23 FOR VALUES FROM ('2026-03-23 00:00:00') TO ('2026-03-24 00:00:00'); +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_04_17 FOR VALUES FROM ('2026-04-17 00:00:00') TO ('2026-04-18 00:00:00'); -- --- Name: messages_2026_03_24; Type: TABLE ATTACH; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_18; Type: TABLE ATTACH; Schema: realtime; Owner: - -- -ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_24 FOR VALUES FROM ('2026-03-24 00:00:00') TO ('2026-03-25 00:00:00'); +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_04_18 FOR VALUES FROM ('2026-04-18 00:00:00') TO ('2026-04-19 00:00:00'); -- --- Name: messages_2026_03_25; Type: TABLE ATTACH; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_19; Type: TABLE ATTACH; Schema: realtime; Owner: - -- -ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_25 FOR VALUES FROM ('2026-03-25 00:00:00') TO ('2026-03-26 00:00:00'); +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_04_19 FOR VALUES FROM ('2026-04-19 00:00:00') TO ('2026-04-20 00:00:00'); -- --- Name: messages_2026_03_26; Type: TABLE ATTACH; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_20; Type: TABLE ATTACH; Schema: realtime; Owner: - -- -ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_26 FOR VALUES FROM ('2026-03-26 00:00:00') TO ('2026-03-27 00:00:00'); +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_04_20 FOR VALUES FROM ('2026-04-20 00:00:00') TO ('2026-04-21 00:00:00'); -- --- Name: refresh_tokens id; Type: DEFAULT; Schema: auth; Owner: supabase_auth_admin +-- Name: refresh_tokens id; Type: DEFAULT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.refresh_tokens ALTER COLUMN id SET DEFAULT nextval('auth.refresh_tokens_id_seq'::regclass); -- --- Name: agenda_online_slots id; Type: DEFAULT; Schema: public; Owner: supabase_admin +-- Name: _db_migrations id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public._db_migrations ALTER COLUMN id SET DEFAULT nextval('public._db_migrations_id_seq'::regclass); + + +-- +-- Name: agenda_online_slots id; Type: DEFAULT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_online_slots ALTER COLUMN id SET DEFAULT nextval('public.agenda_online_slots_id_seq'::regclass); -- --- Name: hooks id; Type: DEFAULT; Schema: supabase_functions; Owner: supabase_functions_admin +-- Name: hooks id; Type: DEFAULT; Schema: supabase_functions; Owner: - -- ALTER TABLE ONLY supabase_functions.hooks ALTER COLUMN id SET DEFAULT nextval('supabase_functions.hooks_id_seq'::regclass); -- --- Name: extensions extensions_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: supabase_admin +-- Name: extensions extensions_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: - -- ALTER TABLE ONLY _realtime.extensions @@ -12045,7 +12784,7 @@ ALTER TABLE ONLY _realtime.extensions -- --- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: supabase_admin +-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: - -- ALTER TABLE ONLY _realtime.schema_migrations @@ -12053,7 +12792,7 @@ ALTER TABLE ONLY _realtime.schema_migrations -- --- Name: tenants tenants_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: supabase_admin +-- Name: tenants tenants_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: - -- ALTER TABLE ONLY _realtime.tenants @@ -12061,7 +12800,7 @@ ALTER TABLE ONLY _realtime.tenants -- --- Name: mfa_amr_claims amr_id_pk; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: mfa_amr_claims amr_id_pk; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.mfa_amr_claims @@ -12069,7 +12808,7 @@ ALTER TABLE ONLY auth.mfa_amr_claims -- --- Name: audit_log_entries audit_log_entries_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: audit_log_entries audit_log_entries_pkey; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.audit_log_entries @@ -12077,7 +12816,7 @@ ALTER TABLE ONLY auth.audit_log_entries -- --- Name: flow_state flow_state_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: flow_state flow_state_pkey; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.flow_state @@ -12085,7 +12824,7 @@ ALTER TABLE ONLY auth.flow_state -- --- Name: identities identities_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: identities identities_pkey; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.identities @@ -12093,7 +12832,7 @@ ALTER TABLE ONLY auth.identities -- --- Name: identities identities_provider_id_provider_unique; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: identities identities_provider_id_provider_unique; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.identities @@ -12101,7 +12840,7 @@ ALTER TABLE ONLY auth.identities -- --- Name: instances instances_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: instances instances_pkey; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.instances @@ -12109,7 +12848,7 @@ ALTER TABLE ONLY auth.instances -- --- Name: mfa_amr_claims mfa_amr_claims_session_id_authentication_method_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: mfa_amr_claims mfa_amr_claims_session_id_authentication_method_pkey; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.mfa_amr_claims @@ -12117,7 +12856,7 @@ ALTER TABLE ONLY auth.mfa_amr_claims -- --- Name: mfa_challenges mfa_challenges_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: mfa_challenges mfa_challenges_pkey; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.mfa_challenges @@ -12125,7 +12864,7 @@ ALTER TABLE ONLY auth.mfa_challenges -- --- Name: mfa_factors mfa_factors_last_challenged_at_key; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: mfa_factors mfa_factors_last_challenged_at_key; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.mfa_factors @@ -12133,7 +12872,7 @@ ALTER TABLE ONLY auth.mfa_factors -- --- Name: mfa_factors mfa_factors_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: mfa_factors mfa_factors_pkey; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.mfa_factors @@ -12141,7 +12880,7 @@ ALTER TABLE ONLY auth.mfa_factors -- --- Name: oauth_authorizations oauth_authorizations_authorization_code_key; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: oauth_authorizations oauth_authorizations_authorization_code_key; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.oauth_authorizations @@ -12149,7 +12888,7 @@ ALTER TABLE ONLY auth.oauth_authorizations -- --- Name: oauth_authorizations oauth_authorizations_authorization_id_key; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: oauth_authorizations oauth_authorizations_authorization_id_key; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.oauth_authorizations @@ -12157,7 +12896,7 @@ ALTER TABLE ONLY auth.oauth_authorizations -- --- Name: oauth_authorizations oauth_authorizations_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: oauth_authorizations oauth_authorizations_pkey; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.oauth_authorizations @@ -12165,7 +12904,7 @@ ALTER TABLE ONLY auth.oauth_authorizations -- --- Name: oauth_client_states oauth_client_states_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: oauth_client_states oauth_client_states_pkey; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.oauth_client_states @@ -12173,7 +12912,7 @@ ALTER TABLE ONLY auth.oauth_client_states -- --- Name: oauth_clients oauth_clients_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: oauth_clients oauth_clients_pkey; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.oauth_clients @@ -12181,7 +12920,7 @@ ALTER TABLE ONLY auth.oauth_clients -- --- Name: oauth_consents oauth_consents_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: oauth_consents oauth_consents_pkey; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.oauth_consents @@ -12189,7 +12928,7 @@ ALTER TABLE ONLY auth.oauth_consents -- --- Name: oauth_consents oauth_consents_user_client_unique; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: oauth_consents oauth_consents_user_client_unique; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.oauth_consents @@ -12197,7 +12936,7 @@ ALTER TABLE ONLY auth.oauth_consents -- --- Name: one_time_tokens one_time_tokens_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: one_time_tokens one_time_tokens_pkey; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.one_time_tokens @@ -12205,7 +12944,7 @@ ALTER TABLE ONLY auth.one_time_tokens -- --- Name: refresh_tokens refresh_tokens_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: refresh_tokens refresh_tokens_pkey; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.refresh_tokens @@ -12213,7 +12952,7 @@ ALTER TABLE ONLY auth.refresh_tokens -- --- Name: refresh_tokens refresh_tokens_token_unique; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: refresh_tokens refresh_tokens_token_unique; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.refresh_tokens @@ -12221,7 +12960,7 @@ ALTER TABLE ONLY auth.refresh_tokens -- --- Name: saml_providers saml_providers_entity_id_key; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: saml_providers saml_providers_entity_id_key; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.saml_providers @@ -12229,7 +12968,7 @@ ALTER TABLE ONLY auth.saml_providers -- --- Name: saml_providers saml_providers_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: saml_providers saml_providers_pkey; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.saml_providers @@ -12237,7 +12976,7 @@ ALTER TABLE ONLY auth.saml_providers -- --- Name: saml_relay_states saml_relay_states_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: saml_relay_states saml_relay_states_pkey; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.saml_relay_states @@ -12245,7 +12984,7 @@ ALTER TABLE ONLY auth.saml_relay_states -- --- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.schema_migrations @@ -12253,7 +12992,7 @@ ALTER TABLE ONLY auth.schema_migrations -- --- Name: sessions sessions_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: sessions sessions_pkey; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.sessions @@ -12261,7 +13000,7 @@ ALTER TABLE ONLY auth.sessions -- --- Name: sso_domains sso_domains_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: sso_domains sso_domains_pkey; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.sso_domains @@ -12269,7 +13008,7 @@ ALTER TABLE ONLY auth.sso_domains -- --- Name: sso_providers sso_providers_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: sso_providers sso_providers_pkey; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.sso_providers @@ -12277,7 +13016,7 @@ ALTER TABLE ONLY auth.sso_providers -- --- Name: users users_phone_key; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: users users_phone_key; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.users @@ -12285,7 +13024,7 @@ ALTER TABLE ONLY auth.users -- --- Name: users users_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: users users_pkey; Type: CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.users @@ -12293,7 +13032,23 @@ ALTER TABLE ONLY auth.users -- --- Name: addon_credits addon_credits_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: _db_migrations _db_migrations_filename_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public._db_migrations + ADD CONSTRAINT _db_migrations_filename_key UNIQUE (filename); + + +-- +-- Name: _db_migrations _db_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public._db_migrations + ADD CONSTRAINT _db_migrations_pkey PRIMARY KEY (id); + + +-- +-- Name: addon_credits addon_credits_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.addon_credits @@ -12301,7 +13056,7 @@ ALTER TABLE ONLY public.addon_credits -- --- Name: addon_products addon_products_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: addon_products addon_products_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.addon_products @@ -12309,7 +13064,7 @@ ALTER TABLE ONLY public.addon_products -- --- Name: addon_products addon_products_slug_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: addon_products addon_products_slug_key; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.addon_products @@ -12317,7 +13072,7 @@ ALTER TABLE ONLY public.addon_products -- --- Name: addon_transactions addon_transactions_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: addon_transactions addon_transactions_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.addon_transactions @@ -12325,7 +13080,7 @@ ALTER TABLE ONLY public.addon_transactions -- --- Name: agenda_bloqueios agenda_bloqueios_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_bloqueios agenda_bloqueios_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_bloqueios @@ -12333,7 +13088,7 @@ ALTER TABLE ONLY public.agenda_bloqueios -- --- Name: agenda_configuracoes agenda_configuracoes_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_configuracoes agenda_configuracoes_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_configuracoes @@ -12341,7 +13096,7 @@ ALTER TABLE ONLY public.agenda_configuracoes -- --- Name: agenda_eventos agenda_eventos_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos agenda_eventos_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_eventos @@ -12349,7 +13104,7 @@ ALTER TABLE ONLY public.agenda_eventos -- --- Name: agenda_eventos agenda_eventos_sem_sobreposicao; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos agenda_eventos_sem_sobreposicao; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_eventos @@ -12357,7 +13112,7 @@ ALTER TABLE ONLY public.agenda_eventos -- --- Name: agenda_excecoes agenda_excecoes_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_excecoes agenda_excecoes_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_excecoes @@ -12365,7 +13120,7 @@ ALTER TABLE ONLY public.agenda_excecoes -- --- Name: agenda_online_slots agenda_online_slots_owner_id_weekday_time_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_online_slots agenda_online_slots_owner_id_weekday_time_key; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_online_slots @@ -12373,7 +13128,7 @@ ALTER TABLE ONLY public.agenda_online_slots -- --- Name: agenda_online_slots agenda_online_slots_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_online_slots agenda_online_slots_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_online_slots @@ -12381,7 +13136,7 @@ ALTER TABLE ONLY public.agenda_online_slots -- --- Name: agenda_regras_semanais agenda_regras_semanais_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_regras_semanais agenda_regras_semanais_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_regras_semanais @@ -12389,7 +13144,7 @@ ALTER TABLE ONLY public.agenda_regras_semanais -- --- Name: agenda_regras_semanais agenda_regras_semanais_unique; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_regras_semanais agenda_regras_semanais_unique; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_regras_semanais @@ -12397,7 +13152,7 @@ ALTER TABLE ONLY public.agenda_regras_semanais -- --- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_seman_owner_id_dia_semana_hora_inic_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_seman_owner_id_dia_semana_hora_inic_key; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_slots_bloqueados_semanais @@ -12405,7 +13160,7 @@ ALTER TABLE ONLY public.agenda_slots_bloqueados_semanais -- --- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_slots_bloqueados_semanais @@ -12413,7 +13168,7 @@ ALTER TABLE ONLY public.agenda_slots_bloqueados_semanais -- --- Name: agenda_slots_regras agenda_slots_regras_owner_id_dia_semana_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_slots_regras agenda_slots_regras_owner_id_dia_semana_key; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_slots_regras @@ -12421,7 +13176,7 @@ ALTER TABLE ONLY public.agenda_slots_regras -- --- Name: agenda_slots_regras agenda_slots_regras_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_slots_regras agenda_slots_regras_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_slots_regras @@ -12429,7 +13184,7 @@ ALTER TABLE ONLY public.agenda_slots_regras -- --- Name: agendador_configuracoes agendador_configuracoes_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agendador_configuracoes agendador_configuracoes_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agendador_configuracoes @@ -12437,7 +13192,7 @@ ALTER TABLE ONLY public.agendador_configuracoes -- --- Name: agendador_solicitacoes agendador_solicitacoes_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agendador_solicitacoes agendador_solicitacoes_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agendador_solicitacoes @@ -12445,7 +13200,7 @@ ALTER TABLE ONLY public.agendador_solicitacoes -- --- Name: billing_contracts billing_contracts_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: billing_contracts billing_contracts_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.billing_contracts @@ -12453,7 +13208,7 @@ ALTER TABLE ONLY public.billing_contracts -- --- Name: commitment_services commitment_services_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: commitment_services commitment_services_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.commitment_services @@ -12461,7 +13216,7 @@ ALTER TABLE ONLY public.commitment_services -- --- Name: commitment_time_logs commitment_time_logs_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: commitment_time_logs commitment_time_logs_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.commitment_time_logs @@ -12469,7 +13224,7 @@ ALTER TABLE ONLY public.commitment_time_logs -- --- Name: company_profiles company_profiles_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: company_profiles company_profiles_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.company_profiles @@ -12477,7 +13232,7 @@ ALTER TABLE ONLY public.company_profiles -- --- Name: company_profiles company_profiles_tenant_id_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: company_profiles company_profiles_tenant_id_key; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.company_profiles @@ -12485,7 +13240,7 @@ ALTER TABLE ONLY public.company_profiles -- --- Name: determined_commitment_fields determined_commitment_fields_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: determined_commitment_fields determined_commitment_fields_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.determined_commitment_fields @@ -12493,7 +13248,7 @@ ALTER TABLE ONLY public.determined_commitment_fields -- --- Name: determined_commitments determined_commitments_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: determined_commitments determined_commitments_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.determined_commitments @@ -12501,7 +13256,7 @@ ALTER TABLE ONLY public.determined_commitments -- --- Name: determined_commitments determined_commitments_tenant_native_key_uq; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: determined_commitments determined_commitments_tenant_native_key_uq; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.determined_commitments @@ -12509,7 +13264,7 @@ ALTER TABLE ONLY public.determined_commitments -- --- Name: dev_user_credentials dev_user_credentials_email_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: dev_user_credentials dev_user_credentials_email_key; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.dev_user_credentials @@ -12517,7 +13272,7 @@ ALTER TABLE ONLY public.dev_user_credentials -- --- Name: dev_user_credentials dev_user_credentials_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: dev_user_credentials dev_user_credentials_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.dev_user_credentials @@ -12525,7 +13280,63 @@ ALTER TABLE ONLY public.dev_user_credentials -- --- Name: email_layout_config email_layout_config_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: document_access_logs document_access_logs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_access_logs + ADD CONSTRAINT document_access_logs_pkey PRIMARY KEY (id); + + +-- +-- Name: document_generated document_generated_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_generated + ADD CONSTRAINT document_generated_pkey PRIMARY KEY (id); + + +-- +-- Name: document_share_links document_share_links_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_share_links + ADD CONSTRAINT document_share_links_pkey PRIMARY KEY (id); + + +-- +-- Name: document_signatures document_signatures_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_signatures + ADD CONSTRAINT document_signatures_pkey PRIMARY KEY (id); + + +-- +-- Name: document_templates document_templates_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_templates + ADD CONSTRAINT document_templates_pkey PRIMARY KEY (id); + + +-- +-- Name: documents documents_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.documents + ADD CONSTRAINT documents_pkey PRIMARY KEY (id); + + +-- +-- Name: document_share_links dsl_token_unique; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_share_links + ADD CONSTRAINT dsl_token_unique UNIQUE (token); + + +-- +-- Name: email_layout_config email_layout_config_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.email_layout_config @@ -12533,7 +13344,7 @@ ALTER TABLE ONLY public.email_layout_config -- --- Name: email_layout_config email_layout_config_tenant_id_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: email_layout_config email_layout_config_tenant_id_key; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.email_layout_config @@ -12541,7 +13352,7 @@ ALTER TABLE ONLY public.email_layout_config -- --- Name: email_templates_global email_templates_global_key_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: email_templates_global email_templates_global_key_key; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.email_templates_global @@ -12549,7 +13360,7 @@ ALTER TABLE ONLY public.email_templates_global -- --- Name: email_templates_global email_templates_global_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: email_templates_global email_templates_global_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.email_templates_global @@ -12557,7 +13368,7 @@ ALTER TABLE ONLY public.email_templates_global -- --- Name: email_templates_tenant email_templates_tenant_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: email_templates_tenant email_templates_tenant_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.email_templates_tenant @@ -12565,7 +13376,7 @@ ALTER TABLE ONLY public.email_templates_tenant -- --- Name: email_templates_tenant email_templates_tenant_tenant_id_owner_id_template_key_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: email_templates_tenant email_templates_tenant_tenant_id_owner_id_template_key_key; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.email_templates_tenant @@ -12573,7 +13384,7 @@ ALTER TABLE ONLY public.email_templates_tenant -- --- Name: entitlements_invalidation entitlements_invalidation_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: entitlements_invalidation entitlements_invalidation_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.entitlements_invalidation @@ -12581,7 +13392,7 @@ ALTER TABLE ONLY public.entitlements_invalidation -- --- Name: features features_key_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: features features_key_key; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.features @@ -12589,7 +13400,7 @@ ALTER TABLE ONLY public.features -- --- Name: features features_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: features features_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.features @@ -12597,7 +13408,7 @@ ALTER TABLE ONLY public.features -- --- Name: feriados feriados_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: feriados feriados_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.feriados @@ -12605,7 +13416,7 @@ ALTER TABLE ONLY public.feriados -- --- Name: feriados feriados_tenant_id_data_nome_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: feriados feriados_tenant_id_data_nome_key; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.feriados @@ -12613,7 +13424,7 @@ ALTER TABLE ONLY public.feriados -- --- Name: financial_categories financial_categories_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: financial_categories financial_categories_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.financial_categories @@ -12621,7 +13432,7 @@ ALTER TABLE ONLY public.financial_categories -- --- Name: financial_exceptions financial_exceptions_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: financial_exceptions financial_exceptions_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.financial_exceptions @@ -12629,7 +13440,7 @@ ALTER TABLE ONLY public.financial_exceptions -- --- Name: financial_records financial_records_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: financial_records financial_records_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.financial_records @@ -12637,7 +13448,7 @@ ALTER TABLE ONLY public.financial_records -- --- Name: global_notices global_notices_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: global_notices global_notices_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.global_notices @@ -12645,7 +13456,7 @@ ALTER TABLE ONLY public.global_notices -- --- Name: insurance_plan_services insurance_plan_services_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: insurance_plan_services insurance_plan_services_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.insurance_plan_services @@ -12653,7 +13464,7 @@ ALTER TABLE ONLY public.insurance_plan_services -- --- Name: insurance_plans insurance_plans_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: insurance_plans insurance_plans_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.insurance_plans @@ -12661,7 +13472,7 @@ ALTER TABLE ONLY public.insurance_plans -- --- Name: login_carousel_slides login_carousel_slides_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: login_carousel_slides login_carousel_slides_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.login_carousel_slides @@ -12669,7 +13480,23 @@ ALTER TABLE ONLY public.login_carousel_slides -- --- Name: module_features module_features_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: medicos medicos_crm_owner_unique; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.medicos + ADD CONSTRAINT medicos_crm_owner_unique UNIQUE NULLS NOT DISTINCT (owner_id, crm); + + +-- +-- Name: medicos medicos_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.medicos + ADD CONSTRAINT medicos_pkey PRIMARY KEY (id); + + +-- +-- Name: module_features module_features_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.module_features @@ -12677,7 +13504,7 @@ ALTER TABLE ONLY public.module_features -- --- Name: modules modules_key_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: modules modules_key_key; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.modules @@ -12685,7 +13512,7 @@ ALTER TABLE ONLY public.modules -- --- Name: modules modules_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: modules modules_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.modules @@ -12693,7 +13520,7 @@ ALTER TABLE ONLY public.modules -- --- Name: notice_dismissals notice_dismissals_notice_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: notice_dismissals notice_dismissals_notice_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.notice_dismissals @@ -12701,7 +13528,7 @@ ALTER TABLE ONLY public.notice_dismissals -- --- Name: notice_dismissals notice_dismissals_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: notice_dismissals notice_dismissals_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.notice_dismissals @@ -12709,7 +13536,7 @@ ALTER TABLE ONLY public.notice_dismissals -- --- Name: notification_channels notification_channels_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: notification_channels notification_channels_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.notification_channels @@ -12717,7 +13544,7 @@ ALTER TABLE ONLY public.notification_channels -- --- Name: notification_logs notification_logs_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: notification_logs notification_logs_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.notification_logs @@ -12725,7 +13552,7 @@ ALTER TABLE ONLY public.notification_logs -- --- Name: notification_preferences notification_preferences_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: notification_preferences notification_preferences_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.notification_preferences @@ -12733,7 +13560,7 @@ ALTER TABLE ONLY public.notification_preferences -- --- Name: notification_queue notification_queue_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: notification_queue notification_queue_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.notification_queue @@ -12741,7 +13568,7 @@ ALTER TABLE ONLY public.notification_queue -- --- Name: notification_schedules notification_schedules_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: notification_schedules notification_schedules_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.notification_schedules @@ -12749,7 +13576,7 @@ ALTER TABLE ONLY public.notification_schedules -- --- Name: notification_templates notification_templates_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: notification_templates notification_templates_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.notification_templates @@ -12757,7 +13584,7 @@ ALTER TABLE ONLY public.notification_templates -- --- Name: notifications notifications_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: notifications notifications_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.notifications @@ -12765,7 +13592,7 @@ ALTER TABLE ONLY public.notifications -- --- Name: owner_users owner_users_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: owner_users owner_users_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.owner_users @@ -12773,7 +13600,15 @@ ALTER TABLE ONLY public.owner_users -- --- Name: patient_discounts patient_discounts_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_contacts patient_contacts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_contacts + ADD CONSTRAINT patient_contacts_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_discounts patient_discounts_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patient_discounts @@ -12781,7 +13616,7 @@ ALTER TABLE ONLY public.patient_discounts -- --- Name: patient_group_patient patient_group_patient_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_group_patient patient_group_patient_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patient_group_patient @@ -12789,7 +13624,7 @@ ALTER TABLE ONLY public.patient_group_patient -- --- Name: patient_groups patient_groups_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_groups patient_groups_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patient_groups @@ -12797,7 +13632,7 @@ ALTER TABLE ONLY public.patient_groups -- --- Name: patient_intake_requests patient_intake_requests_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_intake_requests patient_intake_requests_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patient_intake_requests @@ -12805,7 +13640,7 @@ ALTER TABLE ONLY public.patient_intake_requests -- --- Name: patient_invites patient_invites_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_invites patient_invites_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patient_invites @@ -12813,7 +13648,7 @@ ALTER TABLE ONLY public.patient_invites -- --- Name: patient_invites patient_invites_token_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_invites patient_invites_token_key; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patient_invites @@ -12821,7 +13656,7 @@ ALTER TABLE ONLY public.patient_invites -- --- Name: patient_patient_tag patient_patient_tag_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_patient_tag patient_patient_tag_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patient_patient_tag @@ -12829,7 +13664,23 @@ ALTER TABLE ONLY public.patient_patient_tag -- --- Name: patient_tags patient_tags_owner_name_uniq; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_status_history patient_status_history_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_status_history + ADD CONSTRAINT patient_status_history_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_support_contacts patient_support_contacts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_support_contacts + ADD CONSTRAINT patient_support_contacts_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_tags patient_tags_owner_name_uniq; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patient_tags @@ -12837,7 +13688,7 @@ ALTER TABLE ONLY public.patient_tags -- --- Name: patient_tags patient_tags_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_tags patient_tags_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patient_tags @@ -12845,7 +13696,15 @@ ALTER TABLE ONLY public.patient_tags -- --- Name: patients patients_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_timeline patient_timeline_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_timeline + ADD CONSTRAINT patient_timeline_pkey PRIMARY KEY (id); + + +-- +-- Name: patients patients_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patients @@ -12853,7 +13712,7 @@ ALTER TABLE ONLY public.patients -- --- Name: payment_settings payment_settings_owner_id_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: payment_settings payment_settings_owner_id_key; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.payment_settings @@ -12861,7 +13720,7 @@ ALTER TABLE ONLY public.payment_settings -- --- Name: payment_settings payment_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: payment_settings payment_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.payment_settings @@ -12869,7 +13728,7 @@ ALTER TABLE ONLY public.payment_settings -- --- Name: plan_features plan_features_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: plan_features plan_features_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.plan_features @@ -12877,7 +13736,7 @@ ALTER TABLE ONLY public.plan_features -- --- Name: plan_prices plan_prices_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: plan_prices plan_prices_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.plan_prices @@ -12885,7 +13744,7 @@ ALTER TABLE ONLY public.plan_prices -- --- Name: plan_public_bullets plan_public_bullets_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: plan_public_bullets plan_public_bullets_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.plan_public_bullets @@ -12893,7 +13752,7 @@ ALTER TABLE ONLY public.plan_public_bullets -- --- Name: plan_public plan_public_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: plan_public plan_public_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.plan_public @@ -12901,7 +13760,7 @@ ALTER TABLE ONLY public.plan_public -- --- Name: plans plans_key_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: plans plans_key_key; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.plans @@ -12909,7 +13768,7 @@ ALTER TABLE ONLY public.plans -- --- Name: plans plans_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: plans plans_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.plans @@ -12917,7 +13776,7 @@ ALTER TABLE ONLY public.plans -- --- Name: professional_pricing professional_pricing_owner_commitment_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: professional_pricing professional_pricing_owner_commitment_key; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.professional_pricing @@ -12925,7 +13784,7 @@ ALTER TABLE ONLY public.professional_pricing -- --- Name: professional_pricing professional_pricing_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: professional_pricing professional_pricing_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.professional_pricing @@ -12933,7 +13792,7 @@ ALTER TABLE ONLY public.professional_pricing -- --- Name: profiles profiles_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: profiles profiles_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.profiles @@ -12941,7 +13800,7 @@ ALTER TABLE ONLY public.profiles -- --- Name: recurrence_exceptions recurrence_exceptions_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: recurrence_exceptions recurrence_exceptions_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.recurrence_exceptions @@ -12949,7 +13808,7 @@ ALTER TABLE ONLY public.recurrence_exceptions -- --- Name: recurrence_exceptions recurrence_exceptions_unique; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: recurrence_exceptions recurrence_exceptions_unique; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.recurrence_exceptions @@ -12957,7 +13816,7 @@ ALTER TABLE ONLY public.recurrence_exceptions -- --- Name: recurrence_rule_services recurrence_rule_services_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: recurrence_rule_services recurrence_rule_services_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.recurrence_rule_services @@ -12965,7 +13824,7 @@ ALTER TABLE ONLY public.recurrence_rule_services -- --- Name: recurrence_rules recurrence_rules_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: recurrence_rules recurrence_rules_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.recurrence_rules @@ -12973,7 +13832,7 @@ ALTER TABLE ONLY public.recurrence_rules -- --- Name: saas_admins saas_admins_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: saas_admins saas_admins_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.saas_admins @@ -12981,7 +13840,7 @@ ALTER TABLE ONLY public.saas_admins -- --- Name: saas_doc_votos saas_doc_votos_doc_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: saas_doc_votos saas_doc_votos_doc_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.saas_doc_votos @@ -12989,7 +13848,7 @@ ALTER TABLE ONLY public.saas_doc_votos -- --- Name: saas_doc_votos saas_doc_votos_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: saas_doc_votos saas_doc_votos_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.saas_doc_votos @@ -12997,7 +13856,7 @@ ALTER TABLE ONLY public.saas_doc_votos -- --- Name: saas_docs saas_docs_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: saas_docs saas_docs_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.saas_docs @@ -13005,7 +13864,7 @@ ALTER TABLE ONLY public.saas_docs -- --- Name: saas_faq_itens saas_faq_itens_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: saas_faq_itens saas_faq_itens_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.saas_faq_itens @@ -13013,7 +13872,7 @@ ALTER TABLE ONLY public.saas_faq_itens -- --- Name: saas_faq saas_faq_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: saas_faq saas_faq_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.saas_faq @@ -13021,7 +13880,7 @@ ALTER TABLE ONLY public.saas_faq -- --- Name: services services_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: services services_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.services @@ -13029,7 +13888,7 @@ ALTER TABLE ONLY public.services -- --- Name: subscription_events subscription_events_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: subscription_events subscription_events_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.subscription_events @@ -13037,7 +13896,7 @@ ALTER TABLE ONLY public.subscription_events -- --- Name: subscription_intents_personal subscription_intents_personal_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: subscription_intents_personal subscription_intents_personal_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.subscription_intents_personal @@ -13045,7 +13904,7 @@ ALTER TABLE ONLY public.subscription_intents_personal -- --- Name: subscription_intents_legacy subscription_intents_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: subscription_intents_legacy subscription_intents_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.subscription_intents_legacy @@ -13053,7 +13912,7 @@ ALTER TABLE ONLY public.subscription_intents_legacy -- --- Name: subscription_intents_tenant subscription_intents_tenant_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: subscription_intents_tenant subscription_intents_tenant_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.subscription_intents_tenant @@ -13061,7 +13920,7 @@ ALTER TABLE ONLY public.subscription_intents_tenant -- --- Name: subscriptions subscriptions_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: subscriptions subscriptions_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.subscriptions @@ -13069,7 +13928,7 @@ ALTER TABLE ONLY public.subscriptions -- --- Name: support_sessions support_sessions_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: support_sessions support_sessions_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.support_sessions @@ -13077,7 +13936,7 @@ ALTER TABLE ONLY public.support_sessions -- --- Name: support_sessions support_sessions_token_unique; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: support_sessions support_sessions_token_unique; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.support_sessions @@ -13085,7 +13944,7 @@ ALTER TABLE ONLY public.support_sessions -- --- Name: tenant_feature_exceptions_log tenant_feature_exceptions_log_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: tenant_feature_exceptions_log tenant_feature_exceptions_log_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.tenant_feature_exceptions_log @@ -13093,7 +13952,7 @@ ALTER TABLE ONLY public.tenant_feature_exceptions_log -- --- Name: tenant_features tenant_features_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: tenant_features tenant_features_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.tenant_features @@ -13101,7 +13960,7 @@ ALTER TABLE ONLY public.tenant_features -- --- Name: tenant_invites tenant_invites_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: tenant_invites tenant_invites_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.tenant_invites @@ -13109,7 +13968,7 @@ ALTER TABLE ONLY public.tenant_invites -- --- Name: tenant_members tenant_members_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: tenant_members tenant_members_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.tenant_members @@ -13117,7 +13976,7 @@ ALTER TABLE ONLY public.tenant_members -- --- Name: tenant_members tenant_members_tenant_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: tenant_members tenant_members_tenant_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.tenant_members @@ -13125,7 +13984,7 @@ ALTER TABLE ONLY public.tenant_members -- --- Name: tenant_modules tenant_modules_owner_id_module_id_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: tenant_modules tenant_modules_owner_id_module_id_key; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.tenant_modules @@ -13133,7 +13992,7 @@ ALTER TABLE ONLY public.tenant_modules -- --- Name: tenant_modules tenant_modules_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: tenant_modules tenant_modules_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.tenant_modules @@ -13141,7 +14000,7 @@ ALTER TABLE ONLY public.tenant_modules -- --- Name: tenants tenants_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: tenants tenants_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.tenants @@ -13149,7 +14008,7 @@ ALTER TABLE ONLY public.tenants -- --- Name: therapist_payout_records therapist_payout_records_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: therapist_payout_records therapist_payout_records_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.therapist_payout_records @@ -13157,7 +14016,7 @@ ALTER TABLE ONLY public.therapist_payout_records -- --- Name: therapist_payouts therapist_payouts_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: therapist_payouts therapist_payouts_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.therapist_payouts @@ -13165,7 +14024,15 @@ ALTER TABLE ONLY public.therapist_payouts -- --- Name: addon_credits uq_addon_credits_tenant_type; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: twilio_subaccount_usage twilio_subaccount_usage_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.twilio_subaccount_usage + ADD CONSTRAINT twilio_subaccount_usage_pkey PRIMARY KEY (id); + + +-- +-- Name: addon_credits uq_addon_credits_tenant_type; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.addon_credits @@ -13173,7 +14040,7 @@ ALTER TABLE ONLY public.addon_credits -- --- Name: notification_channels uq_channel_per_owner; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: notification_channels uq_channel_per_owner; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.notification_channels @@ -13181,7 +14048,7 @@ ALTER TABLE ONLY public.notification_channels -- --- Name: notification_preferences uq_notif_prefs_patient_owner; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: notification_preferences uq_notif_prefs_patient_owner; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.notification_preferences @@ -13189,7 +14056,7 @@ ALTER TABLE ONLY public.notification_preferences -- --- Name: notification_queue uq_notif_queue_idempotency; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: notification_queue uq_notif_queue_idempotency; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.notification_queue @@ -13197,7 +14064,7 @@ ALTER TABLE ONLY public.notification_queue -- --- Name: notification_schedules uq_notif_schedule_owner; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: notification_schedules uq_notif_schedule_owner; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.notification_schedules @@ -13205,7 +14072,7 @@ ALTER TABLE ONLY public.notification_schedules -- --- Name: notification_templates uq_notif_template_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: notification_templates uq_notif_template_key; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.notification_templates @@ -13213,7 +14080,7 @@ ALTER TABLE ONLY public.notification_templates -- --- Name: user_settings user_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: user_settings user_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.user_settings @@ -13221,7 +14088,7 @@ ALTER TABLE ONLY public.user_settings -- --- Name: messages messages_pkey; Type: CONSTRAINT; Schema: realtime; Owner: supabase_realtime_admin +-- Name: messages messages_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - -- ALTER TABLE ONLY realtime.messages @@ -13229,63 +14096,63 @@ ALTER TABLE ONLY realtime.messages -- --- Name: messages_2026_03_20 messages_2026_03_20_pkey; Type: CONSTRAINT; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_14 messages_2026_04_14_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - -- -ALTER TABLE ONLY realtime.messages_2026_03_20 - ADD CONSTRAINT messages_2026_03_20_pkey PRIMARY KEY (id, inserted_at); +ALTER TABLE ONLY realtime.messages_2026_04_14 + ADD CONSTRAINT messages_2026_04_14_pkey PRIMARY KEY (id, inserted_at); -- --- Name: messages_2026_03_21 messages_2026_03_21_pkey; Type: CONSTRAINT; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_15 messages_2026_04_15_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - -- -ALTER TABLE ONLY realtime.messages_2026_03_21 - ADD CONSTRAINT messages_2026_03_21_pkey PRIMARY KEY (id, inserted_at); +ALTER TABLE ONLY realtime.messages_2026_04_15 + ADD CONSTRAINT messages_2026_04_15_pkey PRIMARY KEY (id, inserted_at); -- --- Name: messages_2026_03_22 messages_2026_03_22_pkey; Type: CONSTRAINT; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_16 messages_2026_04_16_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - -- -ALTER TABLE ONLY realtime.messages_2026_03_22 - ADD CONSTRAINT messages_2026_03_22_pkey PRIMARY KEY (id, inserted_at); +ALTER TABLE ONLY realtime.messages_2026_04_16 + ADD CONSTRAINT messages_2026_04_16_pkey PRIMARY KEY (id, inserted_at); -- --- Name: messages_2026_03_23 messages_2026_03_23_pkey; Type: CONSTRAINT; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_17 messages_2026_04_17_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - -- -ALTER TABLE ONLY realtime.messages_2026_03_23 - ADD CONSTRAINT messages_2026_03_23_pkey PRIMARY KEY (id, inserted_at); +ALTER TABLE ONLY realtime.messages_2026_04_17 + ADD CONSTRAINT messages_2026_04_17_pkey PRIMARY KEY (id, inserted_at); -- --- Name: messages_2026_03_24 messages_2026_03_24_pkey; Type: CONSTRAINT; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_18 messages_2026_04_18_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - -- -ALTER TABLE ONLY realtime.messages_2026_03_24 - ADD CONSTRAINT messages_2026_03_24_pkey PRIMARY KEY (id, inserted_at); +ALTER TABLE ONLY realtime.messages_2026_04_18 + ADD CONSTRAINT messages_2026_04_18_pkey PRIMARY KEY (id, inserted_at); -- --- Name: messages_2026_03_25 messages_2026_03_25_pkey; Type: CONSTRAINT; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_19 messages_2026_04_19_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - -- -ALTER TABLE ONLY realtime.messages_2026_03_25 - ADD CONSTRAINT messages_2026_03_25_pkey PRIMARY KEY (id, inserted_at); +ALTER TABLE ONLY realtime.messages_2026_04_19 + ADD CONSTRAINT messages_2026_04_19_pkey PRIMARY KEY (id, inserted_at); -- --- Name: messages_2026_03_26 messages_2026_03_26_pkey; Type: CONSTRAINT; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_20 messages_2026_04_20_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - -- -ALTER TABLE ONLY realtime.messages_2026_03_26 - ADD CONSTRAINT messages_2026_03_26_pkey PRIMARY KEY (id, inserted_at); +ALTER TABLE ONLY realtime.messages_2026_04_20 + ADD CONSTRAINT messages_2026_04_20_pkey PRIMARY KEY (id, inserted_at); -- --- Name: subscription pk_subscription; Type: CONSTRAINT; Schema: realtime; Owner: supabase_admin +-- Name: subscription pk_subscription; Type: CONSTRAINT; Schema: realtime; Owner: - -- ALTER TABLE ONLY realtime.subscription @@ -13293,7 +14160,7 @@ ALTER TABLE ONLY realtime.subscription -- --- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: realtime; Owner: supabase_admin +-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - -- ALTER TABLE ONLY realtime.schema_migrations @@ -13301,7 +14168,7 @@ ALTER TABLE ONLY realtime.schema_migrations -- --- Name: buckets_analytics buckets_analytics_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin +-- Name: buckets_analytics buckets_analytics_pkey; Type: CONSTRAINT; Schema: storage; Owner: - -- ALTER TABLE ONLY storage.buckets_analytics @@ -13309,7 +14176,7 @@ ALTER TABLE ONLY storage.buckets_analytics -- --- Name: buckets buckets_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin +-- Name: buckets buckets_pkey; Type: CONSTRAINT; Schema: storage; Owner: - -- ALTER TABLE ONLY storage.buckets @@ -13317,7 +14184,7 @@ ALTER TABLE ONLY storage.buckets -- --- Name: buckets_vectors buckets_vectors_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin +-- Name: buckets_vectors buckets_vectors_pkey; Type: CONSTRAINT; Schema: storage; Owner: - -- ALTER TABLE ONLY storage.buckets_vectors @@ -13325,7 +14192,7 @@ ALTER TABLE ONLY storage.buckets_vectors -- --- Name: iceberg_namespaces iceberg_namespaces_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin +-- Name: iceberg_namespaces iceberg_namespaces_pkey; Type: CONSTRAINT; Schema: storage; Owner: - -- ALTER TABLE ONLY storage.iceberg_namespaces @@ -13333,7 +14200,7 @@ ALTER TABLE ONLY storage.iceberg_namespaces -- --- Name: iceberg_tables iceberg_tables_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin +-- Name: iceberg_tables iceberg_tables_pkey; Type: CONSTRAINT; Schema: storage; Owner: - -- ALTER TABLE ONLY storage.iceberg_tables @@ -13341,7 +14208,7 @@ ALTER TABLE ONLY storage.iceberg_tables -- --- Name: migrations migrations_name_key; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin +-- Name: migrations migrations_name_key; Type: CONSTRAINT; Schema: storage; Owner: - -- ALTER TABLE ONLY storage.migrations @@ -13349,7 +14216,7 @@ ALTER TABLE ONLY storage.migrations -- --- Name: migrations migrations_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin +-- Name: migrations migrations_pkey; Type: CONSTRAINT; Schema: storage; Owner: - -- ALTER TABLE ONLY storage.migrations @@ -13357,7 +14224,7 @@ ALTER TABLE ONLY storage.migrations -- --- Name: objects objects_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin +-- Name: objects objects_pkey; Type: CONSTRAINT; Schema: storage; Owner: - -- ALTER TABLE ONLY storage.objects @@ -13365,7 +14232,7 @@ ALTER TABLE ONLY storage.objects -- --- Name: s3_multipart_uploads_parts s3_multipart_uploads_parts_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin +-- Name: s3_multipart_uploads_parts s3_multipart_uploads_parts_pkey; Type: CONSTRAINT; Schema: storage; Owner: - -- ALTER TABLE ONLY storage.s3_multipart_uploads_parts @@ -13373,7 +14240,7 @@ ALTER TABLE ONLY storage.s3_multipart_uploads_parts -- --- Name: s3_multipart_uploads s3_multipart_uploads_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin +-- Name: s3_multipart_uploads s3_multipart_uploads_pkey; Type: CONSTRAINT; Schema: storage; Owner: - -- ALTER TABLE ONLY storage.s3_multipart_uploads @@ -13381,7 +14248,7 @@ ALTER TABLE ONLY storage.s3_multipart_uploads -- --- Name: vector_indexes vector_indexes_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin +-- Name: vector_indexes vector_indexes_pkey; Type: CONSTRAINT; Schema: storage; Owner: - -- ALTER TABLE ONLY storage.vector_indexes @@ -13389,7 +14256,7 @@ ALTER TABLE ONLY storage.vector_indexes -- --- Name: hooks hooks_pkey; Type: CONSTRAINT; Schema: supabase_functions; Owner: supabase_functions_admin +-- Name: hooks hooks_pkey; Type: CONSTRAINT; Schema: supabase_functions; Owner: - -- ALTER TABLE ONLY supabase_functions.hooks @@ -13397,7 +14264,7 @@ ALTER TABLE ONLY supabase_functions.hooks -- --- Name: migrations migrations_pkey; Type: CONSTRAINT; Schema: supabase_functions; Owner: supabase_functions_admin +-- Name: migrations migrations_pkey; Type: CONSTRAINT; Schema: supabase_functions; Owner: - -- ALTER TABLE ONLY supabase_functions.migrations @@ -13405,2639 +14272,3073 @@ ALTER TABLE ONLY supabase_functions.migrations -- --- Name: extensions_tenant_external_id_index; Type: INDEX; Schema: _realtime; Owner: supabase_admin +-- Name: extensions_tenant_external_id_index; Type: INDEX; Schema: _realtime; Owner: - -- CREATE INDEX extensions_tenant_external_id_index ON _realtime.extensions USING btree (tenant_external_id); -- --- Name: extensions_tenant_external_id_type_index; Type: INDEX; Schema: _realtime; Owner: supabase_admin +-- Name: extensions_tenant_external_id_type_index; Type: INDEX; Schema: _realtime; Owner: - -- CREATE UNIQUE INDEX extensions_tenant_external_id_type_index ON _realtime.extensions USING btree (tenant_external_id, type); -- --- Name: tenants_external_id_index; Type: INDEX; Schema: _realtime; Owner: supabase_admin +-- Name: tenants_external_id_index; Type: INDEX; Schema: _realtime; Owner: - -- CREATE UNIQUE INDEX tenants_external_id_index ON _realtime.tenants USING btree (external_id); -- --- Name: audit_logs_instance_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: audit_logs_instance_id_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX audit_logs_instance_id_idx ON auth.audit_log_entries USING btree (instance_id); -- --- Name: confirmation_token_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: confirmation_token_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE UNIQUE INDEX confirmation_token_idx ON auth.users USING btree (confirmation_token) WHERE ((confirmation_token)::text !~ '^[0-9 ]*$'::text); -- --- Name: email_change_token_current_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: email_change_token_current_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE UNIQUE INDEX email_change_token_current_idx ON auth.users USING btree (email_change_token_current) WHERE ((email_change_token_current)::text !~ '^[0-9 ]*$'::text); -- --- Name: email_change_token_new_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: email_change_token_new_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE UNIQUE INDEX email_change_token_new_idx ON auth.users USING btree (email_change_token_new) WHERE ((email_change_token_new)::text !~ '^[0-9 ]*$'::text); -- --- Name: factor_id_created_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: factor_id_created_at_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX factor_id_created_at_idx ON auth.mfa_factors USING btree (user_id, created_at); -- --- Name: flow_state_created_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: flow_state_created_at_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX flow_state_created_at_idx ON auth.flow_state USING btree (created_at DESC); -- --- Name: identities_email_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: identities_email_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX identities_email_idx ON auth.identities USING btree (email text_pattern_ops); -- --- Name: INDEX identities_email_idx; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin +-- Name: INDEX identities_email_idx; Type: COMMENT; Schema: auth; Owner: - -- COMMENT ON INDEX auth.identities_email_idx IS 'Auth: Ensures indexed queries on the email column'; -- --- Name: identities_user_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: identities_user_id_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX identities_user_id_idx ON auth.identities USING btree (user_id); -- --- Name: idx_auth_code; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: idx_auth_code; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX idx_auth_code ON auth.flow_state USING btree (auth_code); -- --- Name: idx_oauth_client_states_created_at; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: idx_oauth_client_states_created_at; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX idx_oauth_client_states_created_at ON auth.oauth_client_states USING btree (created_at); -- --- Name: idx_user_id_auth_method; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: idx_user_id_auth_method; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX idx_user_id_auth_method ON auth.flow_state USING btree (user_id, authentication_method); -- --- Name: mfa_challenge_created_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: mfa_challenge_created_at_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX mfa_challenge_created_at_idx ON auth.mfa_challenges USING btree (created_at DESC); -- --- Name: mfa_factors_user_friendly_name_unique; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: mfa_factors_user_friendly_name_unique; Type: INDEX; Schema: auth; Owner: - -- CREATE UNIQUE INDEX mfa_factors_user_friendly_name_unique ON auth.mfa_factors USING btree (friendly_name, user_id) WHERE (TRIM(BOTH FROM friendly_name) <> ''::text); -- --- Name: mfa_factors_user_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: mfa_factors_user_id_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX mfa_factors_user_id_idx ON auth.mfa_factors USING btree (user_id); -- --- Name: oauth_auth_pending_exp_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: oauth_auth_pending_exp_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX oauth_auth_pending_exp_idx ON auth.oauth_authorizations USING btree (expires_at) WHERE (status = 'pending'::auth.oauth_authorization_status); -- --- Name: oauth_clients_deleted_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: oauth_clients_deleted_at_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX oauth_clients_deleted_at_idx ON auth.oauth_clients USING btree (deleted_at); -- --- Name: oauth_consents_active_client_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: oauth_consents_active_client_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX oauth_consents_active_client_idx ON auth.oauth_consents USING btree (client_id) WHERE (revoked_at IS NULL); -- --- Name: oauth_consents_active_user_client_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: oauth_consents_active_user_client_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX oauth_consents_active_user_client_idx ON auth.oauth_consents USING btree (user_id, client_id) WHERE (revoked_at IS NULL); -- --- Name: oauth_consents_user_order_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: oauth_consents_user_order_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX oauth_consents_user_order_idx ON auth.oauth_consents USING btree (user_id, granted_at DESC); -- --- Name: one_time_tokens_relates_to_hash_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: one_time_tokens_relates_to_hash_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX one_time_tokens_relates_to_hash_idx ON auth.one_time_tokens USING hash (relates_to); -- --- Name: one_time_tokens_token_hash_hash_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: one_time_tokens_token_hash_hash_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX one_time_tokens_token_hash_hash_idx ON auth.one_time_tokens USING hash (token_hash); -- --- Name: one_time_tokens_user_id_token_type_key; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: one_time_tokens_user_id_token_type_key; Type: INDEX; Schema: auth; Owner: - -- CREATE UNIQUE INDEX one_time_tokens_user_id_token_type_key ON auth.one_time_tokens USING btree (user_id, token_type); -- --- Name: reauthentication_token_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: reauthentication_token_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE UNIQUE INDEX reauthentication_token_idx ON auth.users USING btree (reauthentication_token) WHERE ((reauthentication_token)::text !~ '^[0-9 ]*$'::text); -- --- Name: recovery_token_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: recovery_token_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE UNIQUE INDEX recovery_token_idx ON auth.users USING btree (recovery_token) WHERE ((recovery_token)::text !~ '^[0-9 ]*$'::text); -- --- Name: refresh_tokens_instance_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: refresh_tokens_instance_id_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX refresh_tokens_instance_id_idx ON auth.refresh_tokens USING btree (instance_id); -- --- Name: refresh_tokens_instance_id_user_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: refresh_tokens_instance_id_user_id_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX refresh_tokens_instance_id_user_id_idx ON auth.refresh_tokens USING btree (instance_id, user_id); -- --- Name: refresh_tokens_parent_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: refresh_tokens_parent_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX refresh_tokens_parent_idx ON auth.refresh_tokens USING btree (parent); -- --- Name: refresh_tokens_session_id_revoked_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: refresh_tokens_session_id_revoked_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX refresh_tokens_session_id_revoked_idx ON auth.refresh_tokens USING btree (session_id, revoked); -- --- Name: refresh_tokens_updated_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: refresh_tokens_updated_at_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX refresh_tokens_updated_at_idx ON auth.refresh_tokens USING btree (updated_at DESC); -- --- Name: saml_providers_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: saml_providers_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX saml_providers_sso_provider_id_idx ON auth.saml_providers USING btree (sso_provider_id); -- --- Name: saml_relay_states_created_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: saml_relay_states_created_at_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX saml_relay_states_created_at_idx ON auth.saml_relay_states USING btree (created_at DESC); -- --- Name: saml_relay_states_for_email_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: saml_relay_states_for_email_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX saml_relay_states_for_email_idx ON auth.saml_relay_states USING btree (for_email); -- --- Name: saml_relay_states_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: saml_relay_states_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX saml_relay_states_sso_provider_id_idx ON auth.saml_relay_states USING btree (sso_provider_id); -- --- Name: sessions_not_after_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: sessions_not_after_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX sessions_not_after_idx ON auth.sessions USING btree (not_after DESC); -- --- Name: sessions_oauth_client_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: sessions_oauth_client_id_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX sessions_oauth_client_id_idx ON auth.sessions USING btree (oauth_client_id); -- --- Name: sessions_user_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: sessions_user_id_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX sessions_user_id_idx ON auth.sessions USING btree (user_id); -- --- Name: sso_domains_domain_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: sso_domains_domain_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE UNIQUE INDEX sso_domains_domain_idx ON auth.sso_domains USING btree (lower(domain)); -- --- Name: sso_domains_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: sso_domains_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX sso_domains_sso_provider_id_idx ON auth.sso_domains USING btree (sso_provider_id); -- --- Name: sso_providers_resource_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: sso_providers_resource_id_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE UNIQUE INDEX sso_providers_resource_id_idx ON auth.sso_providers USING btree (lower(resource_id)); -- --- Name: sso_providers_resource_id_pattern_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: sso_providers_resource_id_pattern_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX sso_providers_resource_id_pattern_idx ON auth.sso_providers USING btree (resource_id text_pattern_ops); -- --- Name: unique_phone_factor_per_user; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: unique_phone_factor_per_user; Type: INDEX; Schema: auth; Owner: - -- CREATE UNIQUE INDEX unique_phone_factor_per_user ON auth.mfa_factors USING btree (user_id, phone); -- --- Name: user_id_created_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: user_id_created_at_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX user_id_created_at_idx ON auth.sessions USING btree (user_id, created_at); -- --- Name: users_email_partial_key; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: users_email_partial_key; Type: INDEX; Schema: auth; Owner: - -- CREATE UNIQUE INDEX users_email_partial_key ON auth.users USING btree (email) WHERE (is_sso_user = false); -- --- Name: INDEX users_email_partial_key; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin +-- Name: INDEX users_email_partial_key; Type: COMMENT; Schema: auth; Owner: - -- COMMENT ON INDEX auth.users_email_partial_key IS 'Auth: A partial unique index that applies only when is_sso_user is false'; -- --- Name: users_instance_id_email_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: users_instance_id_email_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX users_instance_id_email_idx ON auth.users USING btree (instance_id, lower((email)::text)); -- --- Name: users_instance_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: users_instance_id_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX users_instance_id_idx ON auth.users USING btree (instance_id); -- --- Name: users_is_anonymous_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin +-- Name: users_is_anonymous_idx; Type: INDEX; Schema: auth; Owner: - -- CREATE INDEX users_is_anonymous_idx ON auth.users USING btree (is_anonymous); -- --- Name: agenda_bloqueios_owner_data_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_bloqueios_owner_data_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_bloqueios_owner_data_idx ON public.agenda_bloqueios USING btree (owner_id, data_inicio, data_fim); -- --- Name: agenda_bloqueios_owner_id_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_bloqueios_owner_id_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_bloqueios_owner_id_idx ON public.agenda_bloqueios USING btree (owner_id); -- --- Name: agenda_bloqueios_recorrente_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_bloqueios_recorrente_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_bloqueios_recorrente_idx ON public.agenda_bloqueios USING btree (owner_id, dia_semana) WHERE (recorrente = true); -- --- Name: agenda_bloqueios_tenant_id_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_bloqueios_tenant_id_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_bloqueios_tenant_id_idx ON public.agenda_bloqueios USING btree (tenant_id); -- --- Name: agenda_configuracoes_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_configuracoes_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_configuracoes_tenant_idx ON public.agenda_configuracoes USING btree (tenant_id); -- --- Name: agenda_configuracoes_tenant_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_configuracoes_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_configuracoes_tenant_owner_idx ON public.agenda_configuracoes USING btree (tenant_id, owner_id); -- --- Name: agenda_eventos_billing_contract_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos_billing_contract_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_eventos_billing_contract_idx ON public.agenda_eventos USING btree (billing_contract_id) WHERE (billing_contract_id IS NOT NULL); -- --- Name: agenda_eventos_insurance_plan_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos_insurance_plan_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_eventos_insurance_plan_idx ON public.agenda_eventos USING btree (insurance_plan_id); -- --- Name: agenda_eventos_owner_inicio_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos_owner_inicio_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_eventos_owner_inicio_idx ON public.agenda_eventos USING btree (owner_id, inicio_em); -- --- Name: agenda_eventos_owner_terapeuta_inicio_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos_owner_terapeuta_inicio_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_eventos_owner_terapeuta_inicio_idx ON public.agenda_eventos USING btree (owner_id, terapeuta_id, inicio_em); -- --- Name: agenda_eventos_recurrence_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos_recurrence_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_eventos_recurrence_idx ON public.agenda_eventos USING btree (recurrence_id) WHERE (recurrence_id IS NOT NULL); -- --- Name: agenda_eventos_tenant_inicio_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos_tenant_inicio_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_eventos_tenant_inicio_idx ON public.agenda_eventos USING btree (tenant_id, inicio_em); -- --- Name: agenda_eventos_tenant_owner_inicio_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos_tenant_owner_inicio_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_eventos_tenant_owner_inicio_idx ON public.agenda_eventos USING btree (tenant_id, owner_id, inicio_em); -- --- Name: agenda_excecoes_owner_data_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_excecoes_owner_data_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_excecoes_owner_data_idx ON public.agenda_excecoes USING btree (owner_id, data); -- --- Name: agenda_excecoes_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_excecoes_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_excecoes_tenant_idx ON public.agenda_excecoes USING btree (tenant_id); -- --- Name: agenda_excecoes_tenant_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_excecoes_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_excecoes_tenant_owner_idx ON public.agenda_excecoes USING btree (tenant_id, owner_id); -- --- Name: agenda_online_slots_owner_weekday_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_online_slots_owner_weekday_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_online_slots_owner_weekday_idx ON public.agenda_online_slots USING btree (owner_id, weekday); -- --- Name: agenda_online_slots_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_online_slots_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_online_slots_tenant_idx ON public.agenda_online_slots USING btree (tenant_id); -- --- Name: agenda_online_slots_tenant_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_online_slots_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_online_slots_tenant_owner_idx ON public.agenda_online_slots USING btree (tenant_id, owner_id); -- --- Name: agenda_regras_semanais_owner_dia_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_regras_semanais_owner_dia_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_regras_semanais_owner_dia_idx ON public.agenda_regras_semanais USING btree (owner_id, dia_semana); -- --- Name: agenda_regras_semanais_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_regras_semanais_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_regras_semanais_tenant_idx ON public.agenda_regras_semanais USING btree (tenant_id); -- --- Name: agenda_regras_semanais_tenant_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_regras_semanais_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_regras_semanais_tenant_owner_idx ON public.agenda_regras_semanais USING btree (tenant_id, owner_id); -- --- Name: agenda_slots_bloqueados_semanais_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_slots_bloqueados_semanais_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_slots_bloqueados_semanais_tenant_idx ON public.agenda_slots_bloqueados_semanais USING btree (tenant_id); -- --- Name: agenda_slots_bloqueados_semanais_tenant_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_slots_bloqueados_semanais_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_slots_bloqueados_semanais_tenant_owner_idx ON public.agenda_slots_bloqueados_semanais USING btree (tenant_id, owner_id); -- --- Name: agenda_slots_regras_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_slots_regras_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_slots_regras_tenant_idx ON public.agenda_slots_regras USING btree (tenant_id); -- --- Name: agenda_slots_regras_tenant_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agenda_slots_regras_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agenda_slots_regras_tenant_owner_idx ON public.agenda_slots_regras USING btree (tenant_id, owner_id); -- --- Name: agendador_cfg_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agendador_cfg_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agendador_cfg_tenant_idx ON public.agendador_configuracoes USING btree (tenant_id); -- --- Name: agendador_sol_data_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agendador_sol_data_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agendador_sol_data_idx ON public.agendador_solicitacoes USING btree (data_solicitada, hora_solicitada); -- --- Name: agendador_sol_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agendador_sol_owner_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agendador_sol_owner_idx ON public.agendador_solicitacoes USING btree (owner_id, status); -- --- Name: agendador_sol_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: agendador_sol_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX agendador_sol_tenant_idx ON public.agendador_solicitacoes USING btree (tenant_id); -- --- Name: billing_contracts_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: billing_contracts_owner_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX billing_contracts_owner_idx ON public.billing_contracts USING btree (owner_id); -- --- Name: billing_contracts_patient_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: billing_contracts_patient_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX billing_contracts_patient_idx ON public.billing_contracts USING btree (patient_id); -- --- Name: billing_contracts_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: billing_contracts_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX billing_contracts_tenant_idx ON public.billing_contracts USING btree (tenant_id); -- --- Name: commitment_services_commitment_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: commitment_services_commitment_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX commitment_services_commitment_idx ON public.commitment_services USING btree (commitment_id); -- --- Name: commitment_services_service_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: commitment_services_service_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX commitment_services_service_idx ON public.commitment_services USING btree (service_id); -- --- Name: commitment_time_logs_calendar_event_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: commitment_time_logs_calendar_event_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX commitment_time_logs_calendar_event_idx ON public.commitment_time_logs USING btree (calendar_event_id); -- --- Name: commitment_time_logs_commitment_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: commitment_time_logs_commitment_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX commitment_time_logs_commitment_idx ON public.commitment_time_logs USING btree (commitment_id, created_at DESC); -- --- Name: commitment_time_logs_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: commitment_time_logs_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX commitment_time_logs_tenant_idx ON public.commitment_time_logs USING btree (tenant_id, created_at DESC); -- --- Name: determined_commitment_fields_commitment_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: dal_documento_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX dal_documento_idx ON public.document_access_logs USING btree (documento_id, acessado_em DESC); + + +-- +-- Name: dal_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX dal_tenant_idx ON public.document_access_logs USING btree (tenant_id, acessado_em DESC); + + +-- +-- Name: dal_user_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX dal_user_idx ON public.document_access_logs USING btree (user_id, acessado_em DESC); + + +-- +-- Name: determined_commitment_fields_commitment_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX determined_commitment_fields_commitment_idx ON public.determined_commitment_fields USING btree (commitment_id, sort_order); -- --- Name: determined_commitment_fields_key_uniq; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: determined_commitment_fields_key_uniq; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX determined_commitment_fields_key_uniq ON public.determined_commitment_fields USING btree (commitment_id, key); -- --- Name: determined_commitment_fields_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: determined_commitment_fields_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX determined_commitment_fields_tenant_idx ON public.determined_commitment_fields USING btree (tenant_id); -- --- Name: determined_commitments_active_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: determined_commitments_active_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX determined_commitments_active_idx ON public.determined_commitments USING btree (tenant_id, active); -- --- Name: determined_commitments_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: determined_commitments_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX determined_commitments_tenant_idx ON public.determined_commitments USING btree (tenant_id); -- --- Name: determined_commitments_tenant_name_uniq; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: determined_commitments_tenant_name_uniq; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX determined_commitments_tenant_name_uniq ON public.determined_commitments USING btree (tenant_id, lower(name)); -- --- Name: feriados_global_unique; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: dg_gerado_por_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX dg_gerado_por_idx ON public.document_generated USING btree (gerado_por, gerado_em DESC); + + +-- +-- Name: dg_patient_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX dg_patient_idx ON public.document_generated USING btree (patient_id, gerado_em DESC); + + +-- +-- Name: dg_template_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX dg_template_idx ON public.document_generated USING btree (template_id); + + +-- +-- Name: dg_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX dg_tenant_idx ON public.document_generated USING btree (tenant_id, gerado_em DESC); + + +-- +-- Name: docs_active_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX docs_active_idx ON public.documents USING btree (patient_id, uploaded_at DESC) WHERE (deleted_at IS NULL); + + +-- +-- Name: docs_nome_trgm_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX docs_nome_trgm_idx ON public.documents USING gin (nome_original public.gin_trgm_ops); + + +-- +-- Name: docs_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX docs_owner_idx ON public.documents USING btree (owner_id); + + +-- +-- Name: docs_patient_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX docs_patient_idx ON public.documents USING btree (patient_id); + + +-- +-- Name: docs_tags_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX docs_tags_idx ON public.documents USING gin (tags); + + +-- +-- Name: docs_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX docs_tenant_idx ON public.documents USING btree (tenant_id); + + +-- +-- Name: docs_tipo_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX docs_tipo_idx ON public.documents USING btree (patient_id, tipo_documento); + + +-- +-- Name: docs_uploaded_at_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX docs_uploaded_at_idx ON public.documents USING btree (patient_id, uploaded_at DESC); + + +-- +-- Name: ds_documento_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ds_documento_idx ON public.document_signatures USING btree (documento_id, ordem); + + +-- +-- Name: ds_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ds_status_idx ON public.document_signatures USING btree (documento_id, status); + + +-- +-- Name: ds_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ds_tenant_idx ON public.document_signatures USING btree (tenant_id); + + +-- +-- Name: dsl_documento_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX dsl_documento_idx ON public.document_share_links USING btree (documento_id); + + +-- +-- Name: dsl_expira_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX dsl_expira_idx ON public.document_share_links USING btree (expira_em) WHERE (ativo = true); + + +-- +-- Name: dsl_token_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX dsl_token_idx ON public.document_share_links USING btree (token) WHERE (ativo = true); + + +-- +-- Name: dt_global_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX dt_global_idx ON public.document_templates USING btree (is_global) WHERE (is_global = true); + + +-- +-- Name: dt_nome_trgm_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX dt_nome_trgm_idx ON public.document_templates USING gin (nome_template public.gin_trgm_ops); + + +-- +-- Name: dt_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX dt_owner_idx ON public.document_templates USING btree (owner_id); + + +-- +-- Name: dt_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX dt_tenant_idx ON public.document_templates USING btree (tenant_id); + + +-- +-- Name: dt_tipo_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX dt_tipo_idx ON public.document_templates USING btree (tipo); + + +-- +-- Name: feriados_global_unique; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX feriados_global_unique ON public.feriados USING btree (data, nome) WHERE (tenant_id IS NULL); -- --- Name: financial_exceptions_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: financial_exceptions_owner_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX financial_exceptions_owner_idx ON public.financial_exceptions USING btree (owner_id); -- --- Name: financial_exceptions_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: financial_exceptions_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX financial_exceptions_tenant_idx ON public.financial_exceptions USING btree (tenant_id); -- --- Name: idx_addon_credits_tenant; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_addon_credits_tenant; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_addon_credits_tenant ON public.addon_credits USING btree (tenant_id) WHERE (is_active = true); -- --- Name: idx_addon_credits_type; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_addon_credits_type; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_addon_credits_type ON public.addon_credits USING btree (addon_type) WHERE (is_active = true); -- --- Name: idx_addon_products_active; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_addon_products_active; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_addon_products_active ON public.addon_products USING btree (is_active, is_visible) WHERE (deleted_at IS NULL); -- --- Name: idx_addon_products_type; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_addon_products_type; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_addon_products_type ON public.addon_products USING btree (addon_type) WHERE (deleted_at IS NULL); -- --- Name: idx_addon_tx_created; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_addon_tx_created; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_addon_tx_created ON public.addon_transactions USING btree (created_at DESC); -- --- Name: idx_addon_tx_queue; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_addon_tx_queue; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_addon_tx_queue ON public.addon_transactions USING btree (queue_id) WHERE (queue_id IS NOT NULL); -- --- Name: idx_addon_tx_tenant; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_addon_tx_tenant; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_addon_tx_tenant ON public.addon_transactions USING btree (tenant_id, addon_type); -- --- Name: idx_addon_tx_type; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_addon_tx_type; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_addon_tx_type ON public.addon_transactions USING btree (type); -- --- Name: idx_agenda_eventos_determined_commitment_id; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_agenda_eventos_determined_commitment_id; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_agenda_eventos_determined_commitment_id ON public.agenda_eventos USING btree (determined_commitment_id); -- --- Name: idx_agenda_excecoes_owner_data; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_agenda_excecoes_owner_data; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_agenda_excecoes_owner_data ON public.agenda_excecoes USING btree (owner_id, data); -- --- Name: idx_agenda_slots_regras_owner_dia; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_agenda_slots_regras_owner_dia; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_agenda_slots_regras_owner_dia ON public.agenda_slots_regras USING btree (owner_id, dia_semana); -- --- Name: idx_email_templates_global_domain; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_email_templates_global_domain; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_email_templates_global_domain ON public.email_templates_global USING btree (domain) WHERE (is_active = true); -- --- Name: idx_email_templates_global_key; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_email_templates_global_key; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_email_templates_global_key ON public.email_templates_global USING btree (key) WHERE (is_active = true); -- --- Name: idx_email_templates_tenant_lookup; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_email_templates_tenant_lookup; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_email_templates_tenant_lookup ON public.email_templates_tenant USING btree (tenant_id, template_key) WHERE (enabled = true); -- --- Name: idx_email_templates_tenant_owner; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_email_templates_tenant_owner; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_email_templates_tenant_owner ON public.email_templates_tenant USING btree (owner_id, template_key) WHERE ((enabled = true) AND (owner_id IS NOT NULL)); -- --- Name: idx_financial_categories_user_id; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_financial_categories_user_id; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_financial_categories_user_id ON public.financial_categories USING btree (user_id); -- --- Name: idx_financial_records_active; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_financial_records_active; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_financial_records_active ON public.financial_records USING btree (owner_id, paid_at DESC) WHERE (deleted_at IS NULL); -- --- Name: idx_financial_records_agenda_evento_id; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_financial_records_agenda_evento_id; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_financial_records_agenda_evento_id ON public.financial_records USING btree (agenda_evento_id) WHERE (agenda_evento_id IS NOT NULL); -- --- Name: idx_financial_records_category_id; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_financial_records_category_id; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_financial_records_category_id ON public.financial_records USING btree (category_id) WHERE (category_id IS NOT NULL); -- --- Name: idx_financial_records_due_date; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_financial_records_due_date; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_financial_records_due_date ON public.financial_records USING btree (due_date); -- --- Name: idx_financial_records_installment_group; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_financial_records_installment_group; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_financial_records_installment_group ON public.financial_records USING btree (installment_group) WHERE (installment_group IS NOT NULL); -- --- Name: idx_financial_records_owner_id; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_financial_records_owner_id; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_financial_records_owner_id ON public.financial_records USING btree (owner_id); -- --- Name: idx_financial_records_paid_at; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_financial_records_paid_at; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_financial_records_paid_at ON public.financial_records USING btree (paid_at DESC); -- --- Name: idx_financial_records_patient_id; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_financial_records_patient_id; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_financial_records_patient_id ON public.financial_records USING btree (patient_id); -- --- Name: idx_financial_records_status; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_financial_records_status; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_financial_records_status ON public.financial_records USING btree (status) WHERE (deleted_at IS NULL); -- --- Name: idx_financial_records_tenant_active; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_financial_records_tenant_active; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_financial_records_tenant_active ON public.financial_records USING btree (tenant_id, paid_at DESC) WHERE ((deleted_at IS NULL) AND (tenant_id IS NOT NULL)); -- --- Name: idx_financial_records_tenant_id; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_financial_records_tenant_id; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_financial_records_tenant_id ON public.financial_records USING btree (tenant_id); -- --- Name: idx_financial_records_type_status; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_financial_records_type_status; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_financial_records_type_status ON public.financial_records USING btree (type, status); -- --- Name: idx_global_notices_active_priority; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_global_notices_active_priority; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_global_notices_active_priority ON public.global_notices USING btree (is_active, priority DESC, starts_at, ends_at); -- --- Name: idx_intakes_converted_patient_id; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_intakes_converted_patient_id; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_intakes_converted_patient_id ON public.patient_intake_requests USING btree (converted_patient_id); -- --- Name: idx_intakes_owner_cpf; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_intakes_owner_cpf; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_intakes_owner_cpf ON public.patient_intake_requests USING btree (owner_id, cpf); -- --- Name: idx_intakes_owner_created; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_intakes_owner_created; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_intakes_owner_created ON public.patient_intake_requests USING btree (owner_id, created_at DESC); -- --- Name: idx_intakes_owner_status_created; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_intakes_owner_status_created; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_intakes_owner_status_created ON public.patient_intake_requests USING btree (owner_id, status, created_at DESC); -- --- Name: idx_intakes_status_created; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_intakes_status_created; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_intakes_status_created ON public.patient_intake_requests USING btree (status, created_at DESC); -- --- Name: idx_notice_dismissals_user; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_notice_dismissals_user; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_notice_dismissals_user ON public.notice_dismissals USING btree (user_id, notice_id); -- --- Name: idx_notif_channels_owner_active; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_notif_channels_owner_active; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_notif_channels_owner_active ON public.notification_channels USING btree (owner_id, channel) WHERE ((is_active = true) AND (deleted_at IS NULL)); -- --- Name: idx_notif_channels_tenant; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_notif_channels_tenant; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_notif_channels_tenant ON public.notification_channels USING btree (tenant_id) WHERE (deleted_at IS NULL); -- --- Name: idx_notif_logs_owner_date; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_notif_logs_owner_date; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_notif_logs_owner_date ON public.notification_logs USING btree (owner_id, created_at DESC); -- --- Name: idx_notif_logs_patient; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_notif_logs_patient; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_notif_logs_patient ON public.notification_logs USING btree (patient_id, created_at DESC); -- --- Name: idx_notif_logs_provider_msg; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_notif_logs_provider_msg; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_notif_logs_provider_msg ON public.notification_logs USING btree (provider_message_id) WHERE (provider_message_id IS NOT NULL); -- --- Name: idx_notif_logs_status; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_notif_logs_status; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_notif_logs_status ON public.notification_logs USING btree (status, created_at DESC); -- --- Name: idx_notif_logs_tenant_date; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_notif_logs_tenant_date; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_notif_logs_tenant_date ON public.notification_logs USING btree (tenant_id, created_at DESC); -- --- Name: idx_notif_prefs_owner; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_notif_prefs_owner; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_notif_prefs_owner ON public.notification_preferences USING btree (owner_id) WHERE (deleted_at IS NULL); -- --- Name: idx_notif_prefs_patient; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_notif_prefs_patient; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_notif_prefs_patient ON public.notification_preferences USING btree (patient_id) WHERE (deleted_at IS NULL); -- --- Name: idx_notif_prefs_whatsapp_active; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_notif_prefs_whatsapp_active; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_notif_prefs_whatsapp_active ON public.notification_preferences USING btree (owner_id, patient_id) WHERE ((whatsapp_opt_in = true) AND (deleted_at IS NULL)); -- --- Name: idx_notif_queue_evento; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_notif_queue_evento; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_notif_queue_evento ON public.notification_queue USING btree (agenda_evento_id) WHERE (status = ANY (ARRAY['pendente'::text, 'processando'::text])); -- --- Name: idx_notif_queue_patient; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_notif_queue_patient; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_notif_queue_patient ON public.notification_queue USING btree (patient_id, channel) WHERE (status = 'pendente'::text); -- --- Name: idx_notif_queue_pending; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_notif_queue_pending; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_notif_queue_pending ON public.notification_queue USING btree (scheduled_at) WHERE (status = 'pendente'::text); -- --- Name: idx_notif_queue_processing; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_notif_queue_processing; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_notif_queue_processing ON public.notification_queue USING btree (status, updated_at) WHERE (status = 'processando'::text); -- --- Name: idx_notif_queue_retry; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_notif_queue_retry; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_notif_queue_retry ON public.notification_queue USING btree (next_retry_at) WHERE ((status = 'pendente'::text) AND (attempts > 0)); -- --- Name: idx_notif_queue_tenant; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_notif_queue_tenant; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_notif_queue_tenant ON public.notification_queue USING btree (tenant_id, created_at DESC); -- --- Name: idx_notif_schedules_owner_active; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_notif_schedules_owner_active; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_notif_schedules_owner_active ON public.notification_schedules USING btree (owner_id, event_type) WHERE ((is_active = true) AND (deleted_at IS NULL)); -- --- Name: idx_notif_templates_default; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_notif_templates_default; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_notif_templates_default ON public.notification_templates USING btree (channel, event_type) WHERE ((is_default = true) AND (deleted_at IS NULL) AND (tenant_id IS NULL)); -- --- Name: idx_notif_templates_lookup; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_notif_templates_lookup; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_notif_templates_lookup ON public.notification_templates USING btree (channel, event_type, is_active) WHERE (deleted_at IS NULL); -- --- Name: idx_notif_templates_tenant; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_notif_templates_tenant; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_notif_templates_tenant ON public.notification_templates USING btree (tenant_id, channel, event_type) WHERE ((deleted_at IS NULL) AND (is_active = true)); -- --- Name: idx_patient_group_patient_group_id; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_notification_channels_twilio_subaccount_sid; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notification_channels_twilio_subaccount_sid ON public.notification_channels USING btree (twilio_subaccount_sid) WHERE (twilio_subaccount_sid IS NOT NULL); + + +-- +-- Name: idx_patient_contacts_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patient_contacts_patient ON public.patient_contacts USING btree (patient_id); + + +-- +-- Name: idx_patient_contacts_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patient_contacts_tenant ON public.patient_contacts USING btree (tenant_id); + + +-- +-- Name: idx_patient_group_patient_group_id; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_patient_group_patient_group_id ON public.patient_group_patient USING btree (patient_group_id); -- --- Name: idx_patient_groups_owner; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_patient_groups_owner; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_patient_groups_owner ON public.patient_groups USING btree (owner_id); -- --- Name: idx_patient_groups_owner_system_nome; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_patient_groups_owner_system_nome; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_patient_groups_owner_system_nome ON public.patient_groups USING btree (owner_id, is_system, nome); -- --- Name: idx_patient_tags_owner; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_patient_tags_owner; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_patient_tags_owner ON public.patient_tags USING btree (owner_id); -- --- Name: idx_patients_created_at; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_patients_created_at; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_patients_created_at ON public.patients USING btree (created_at DESC); -- --- Name: idx_patients_last_attended; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_patients_last_attended; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_patients_last_attended ON public.patients USING btree (last_attended_at DESC); -- --- Name: idx_patients_owner_email_principal; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_patients_origem; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_origem ON public.patients USING btree (tenant_id, origem) WHERE (origem IS NOT NULL); + + +-- +-- Name: idx_patients_owner_email_principal; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_patients_owner_email_principal ON public.patients USING btree (owner_id, email_principal); -- --- Name: idx_patients_owner_id; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_patients_owner_id; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_patients_owner_id ON public.patients USING btree (owner_id); -- --- Name: idx_patients_owner_nome; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_patients_owner_nome; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_patients_owner_nome ON public.patients USING btree (owner_id, nome_completo); -- --- Name: idx_patients_responsible_member; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_patients_responsible_member; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_patients_responsible_member ON public.patients USING btree (responsible_member_id); -- --- Name: idx_patients_status; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_patients_risco_elevado; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_risco_elevado ON public.patients USING btree (tenant_id, risco_elevado) WHERE (risco_elevado = true); + + +-- +-- Name: idx_patients_status; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_patients_status ON public.patients USING btree (status); -- --- Name: idx_patients_tenant; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_patients_status_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_status_tenant ON public.patients USING btree (tenant_id, status); + + +-- +-- Name: idx_patients_tenant; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_patients_tenant ON public.patients USING btree (tenant_id); -- --- Name: idx_patients_tenant_email_norm; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_patients_tenant_email_norm; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_patients_tenant_email_norm ON public.patients USING btree (tenant_id, lower(TRIM(BOTH FROM email_principal))); -- --- Name: idx_pgp_group; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_pgp_group; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_pgp_group ON public.patient_group_patient USING btree (patient_group_id); -- --- Name: idx_pgp_patient; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_pgp_patient; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_pgp_patient ON public.patient_group_patient USING btree (patient_id); -- --- Name: idx_ppt_patient; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_ppt_patient; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_ppt_patient ON public.patient_patient_tag USING btree (patient_id); -- --- Name: idx_ppt_tag; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_ppt_tag; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_ppt_tag ON public.patient_patient_tag USING btree (tag_id); -- --- Name: idx_slots_bloq_owner_dia; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_psh_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_psh_patient ON public.patient_status_history USING btree (patient_id, alterado_em DESC); + + +-- +-- Name: idx_psh_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_psh_tenant ON public.patient_status_history USING btree (tenant_id, alterado_em DESC); + + +-- +-- Name: idx_pt_evento_tipo; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_pt_evento_tipo ON public.patient_timeline USING btree (patient_id, evento_tipo); + + +-- +-- Name: idx_pt_patient_ocorrido; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_pt_patient_ocorrido ON public.patient_timeline USING btree (patient_id, ocorrido_em DESC); + + +-- +-- Name: idx_pt_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_pt_tenant ON public.patient_timeline USING btree (tenant_id, ocorrido_em DESC); + + +-- +-- Name: idx_slots_bloq_owner_dia; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_slots_bloq_owner_dia ON public.agenda_slots_bloqueados_semanais USING btree (owner_id, dia_semana); -- --- Name: idx_subscription_intents_plan_interval; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_subscription_intents_plan_interval; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_subscription_intents_plan_interval ON public.subscription_intents_legacy USING btree (plan_key, "interval"); -- --- Name: idx_subscription_intents_status; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_subscription_intents_status; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_subscription_intents_status ON public.subscription_intents_legacy USING btree (status); -- --- Name: idx_subscription_intents_user_id; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_subscription_intents_user_id; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_subscription_intents_user_id ON public.subscription_intents_legacy USING btree (user_id); -- --- Name: idx_tenant_features_tenant; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_tenant_features_tenant; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_tenant_features_tenant ON public.tenant_features USING btree (tenant_id); -- --- Name: idx_tenant_invites_tenant; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_tenant_invites_tenant; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_tenant_invites_tenant ON public.tenant_invites USING btree (tenant_id); -- --- Name: idx_tenant_invites_token; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_tenant_invites_token; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_tenant_invites_token ON public.tenant_invites USING btree (token); -- --- Name: idx_therapist_payout_records_financial_record_id; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_therapist_payout_records_financial_record_id; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_therapist_payout_records_financial_record_id ON public.therapist_payout_records USING btree (financial_record_id); -- --- Name: idx_therapist_payouts_owner_id; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_therapist_payouts_owner_id; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_therapist_payouts_owner_id ON public.therapist_payouts USING btree (owner_id); -- --- Name: idx_therapist_payouts_period; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_therapist_payouts_period; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_therapist_payouts_period ON public.therapist_payouts USING btree (period_start, period_end); -- --- Name: idx_therapist_payouts_status; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_therapist_payouts_status; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_therapist_payouts_status ON public.therapist_payouts USING btree (status) WHERE (status = 'pending'::text); -- --- Name: idx_therapist_payouts_tenant_id; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_therapist_payouts_tenant_id; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX idx_therapist_payouts_tenant_id ON public.therapist_payouts USING btree (tenant_id); -- --- Name: insurance_plans_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: idx_twilio_usage_channel; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_twilio_usage_channel ON public.twilio_subaccount_usage USING btree (channel_id, period_start DESC); + + +-- +-- Name: idx_twilio_usage_tenant_period; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_twilio_usage_tenant_period ON public.twilio_subaccount_usage USING btree (tenant_id, period_start DESC); + + +-- +-- Name: idx_twilio_usage_unique_period; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX idx_twilio_usage_unique_period ON public.twilio_subaccount_usage USING btree (channel_id, period_start, period_end); + + +-- +-- Name: insurance_plans_owner_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX insurance_plans_owner_idx ON public.insurance_plans USING btree (owner_id); -- --- Name: insurance_plans_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: insurance_plans_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX insurance_plans_tenant_idx ON public.insurance_plans USING btree (tenant_id); -- --- Name: ix_plan_prices_plan; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: ix_plan_prices_plan; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX ix_plan_prices_plan ON public.plan_prices USING btree (plan_id); -- --- Name: ix_plan_public_bullets_plan; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: ix_plan_public_bullets_plan; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX ix_plan_public_bullets_plan ON public.plan_public_bullets USING btree (plan_id); -- --- Name: ix_plan_public_sort; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: ix_plan_public_sort; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX ix_plan_public_sort ON public.plan_public USING btree (sort_order); -- --- Name: notifications_owner_created; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: medicos_especialidade_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX medicos_especialidade_idx ON public.medicos USING btree (especialidade); + + +-- +-- Name: medicos_nome_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX medicos_nome_idx ON public.medicos USING btree (nome); + + +-- +-- Name: medicos_nome_trgm_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX medicos_nome_trgm_idx ON public.medicos USING gin (nome public.gin_trgm_ops); + + +-- +-- Name: medicos_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX medicos_owner_idx ON public.medicos USING btree (owner_id); + + +-- +-- Name: medicos_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX medicos_tenant_idx ON public.medicos USING btree (tenant_id); + + +-- +-- Name: notifications_owner_created; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX notifications_owner_created ON public.notifications USING btree (owner_id, created_at DESC); -- --- Name: notifications_owner_unread; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: notifications_owner_unread; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX notifications_owner_unread ON public.notifications USING btree (owner_id, read_at) WHERE (read_at IS NULL); -- --- Name: patient_discounts_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: patient_discounts_owner_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX patient_discounts_owner_idx ON public.patient_discounts USING btree (owner_id); -- --- Name: patient_discounts_patient_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: patient_discounts_patient_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX patient_discounts_patient_idx ON public.patient_discounts USING btree (patient_id); -- --- Name: patient_discounts_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: patient_discounts_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX patient_discounts_tenant_idx ON public.patient_discounts USING btree (tenant_id); -- --- Name: patient_group_patient_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: patient_group_patient_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX patient_group_patient_tenant_idx ON public.patient_group_patient USING btree (tenant_id); -- --- Name: patient_groups_owner_nome_uniq; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: patient_groups_owner_nome_uniq; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX patient_groups_owner_nome_uniq ON public.patient_groups USING btree (owner_id, nome); -- --- Name: patient_groups_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: patient_groups_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX patient_groups_tenant_idx ON public.patient_groups USING btree (tenant_id); -- --- Name: patient_intake_owner_id_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: patient_intake_owner_id_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX patient_intake_owner_id_idx ON public.patient_intake_requests USING btree (owner_id); -- --- Name: patient_intake_requests_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: patient_intake_requests_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX patient_intake_requests_tenant_idx ON public.patient_intake_requests USING btree (tenant_id); -- --- Name: patient_intake_status_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: patient_intake_status_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX patient_intake_status_idx ON public.patient_intake_requests USING btree (status); -- --- Name: patient_intake_token_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: patient_intake_token_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX patient_intake_token_idx ON public.patient_intake_requests USING btree (token); -- --- Name: patient_invites_one_active_per_owner; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: patient_invites_one_active_per_owner; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX patient_invites_one_active_per_owner ON public.patient_invites USING btree (owner_id) WHERE (active = true); -- --- Name: patient_invites_owner_id_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: patient_invites_owner_id_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX patient_invites_owner_id_idx ON public.patient_invites USING btree (owner_id); -- --- Name: patient_invites_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: patient_invites_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX patient_invites_tenant_idx ON public.patient_invites USING btree (tenant_id); -- --- Name: patient_invites_token_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: patient_invites_token_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX patient_invites_token_idx ON public.patient_invites USING btree (token); -- --- Name: patient_patient_tag_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: patient_patient_tag_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX patient_patient_tag_tenant_idx ON public.patient_patient_tag USING btree (tenant_id); -- --- Name: patient_tags_owner_name_uq; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: patient_tags_owner_name_uq; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX patient_tags_owner_name_uq ON public.patient_tags USING btree (owner_id, lower(nome)); -- --- Name: patient_tags_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: patient_tags_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX patient_tags_tenant_idx ON public.patient_tags USING btree (tenant_id); -- --- Name: payment_settings_tenant_id_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: patients_convenio_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patients_convenio_id_idx ON public.patients USING btree (convenio_id); + + +-- +-- Name: patients_etnia_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patients_etnia_idx ON public.patients USING btree (etnia); + + +-- +-- Name: patients_pronomes_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patients_pronomes_idx ON public.patients USING btree (pronomes); + + +-- +-- Name: payment_settings_tenant_id_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX payment_settings_tenant_id_idx ON public.payment_settings USING btree (tenant_id); -- --- Name: ppt_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: ppt_owner_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX ppt_owner_idx ON public.patient_patient_tag USING btree (owner_id); -- --- Name: ppt_patient_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: ppt_patient_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX ppt_patient_idx ON public.patient_patient_tag USING btree (patient_id); -- --- Name: ppt_tag_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: ppt_tag_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX ppt_tag_idx ON public.patient_patient_tag USING btree (tag_id); -- --- Name: professional_pricing_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: professional_pricing_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX professional_pricing_tenant_idx ON public.professional_pricing USING btree (tenant_id); -- --- Name: profiles_work_description_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: profiles_work_description_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX profiles_work_description_idx ON public.profiles USING btree (work_description) WHERE (work_description IS NOT NULL); -- --- Name: recurrence_exceptions_rule_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: psc_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX psc_owner_idx ON public.patient_support_contacts USING btree (owner_id); + + +-- +-- Name: psc_patient_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX psc_patient_idx ON public.patient_support_contacts USING btree (patient_id); + + +-- +-- Name: recurrence_exceptions_rule_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX recurrence_exceptions_rule_idx ON public.recurrence_exceptions USING btree (recurrence_id); -- --- Name: recurrence_exceptions_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: recurrence_exceptions_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX recurrence_exceptions_tenant_idx ON public.recurrence_exceptions USING btree (tenant_id); -- --- Name: recurrence_rule_services_rule_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: recurrence_rule_services_rule_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX recurrence_rule_services_rule_idx ON public.recurrence_rule_services USING btree (rule_id); -- --- Name: recurrence_rule_services_service_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: recurrence_rule_services_service_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX recurrence_rule_services_service_idx ON public.recurrence_rule_services USING btree (service_id); -- --- Name: recurrence_rules_active_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: recurrence_rules_active_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX recurrence_rules_active_idx ON public.recurrence_rules USING btree (owner_id, status) WHERE (status = 'ativo'::text); -- --- Name: recurrence_rules_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: recurrence_rules_owner_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX recurrence_rules_owner_idx ON public.recurrence_rules USING btree (owner_id); -- --- Name: recurrence_rules_patient_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: recurrence_rules_patient_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX recurrence_rules_patient_idx ON public.recurrence_rules USING btree (patient_id); -- --- Name: recurrence_rules_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: recurrence_rules_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX recurrence_rules_tenant_idx ON public.recurrence_rules USING btree (tenant_id); -- --- Name: saas_doc_votos_doc_id_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: saas_doc_votos_doc_id_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX saas_doc_votos_doc_id_idx ON public.saas_doc_votos USING btree (doc_id); -- --- Name: saas_doc_votos_user_id_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: saas_doc_votos_user_id_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX saas_doc_votos_user_id_idx ON public.saas_doc_votos USING btree (user_id); -- --- Name: saas_docs_categoria_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: saas_docs_categoria_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX saas_docs_categoria_idx ON public.saas_docs USING btree (categoria); -- --- Name: saas_docs_exibir_no_faq_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: saas_docs_exibir_no_faq_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX saas_docs_exibir_no_faq_idx ON public.saas_docs USING btree (exibir_no_faq) WHERE (exibir_no_faq = true); -- --- Name: saas_docs_path_ativo_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: saas_docs_path_ativo_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX saas_docs_path_ativo_idx ON public.saas_docs USING btree (pagina_path, ativo); -- --- Name: saas_faq_ativo_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: saas_faq_ativo_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX saas_faq_ativo_idx ON public.saas_faq USING btree (ativo); -- --- Name: saas_faq_categoria_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: saas_faq_categoria_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX saas_faq_categoria_idx ON public.saas_faq USING btree (categoria); -- --- Name: saas_faq_fts_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: saas_faq_fts_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX saas_faq_fts_idx ON public.saas_faq USING gin (to_tsvector('portuguese'::regconfig, ((COALESCE(pergunta, ''::text) || ' '::text) || COALESCE(conteudo, ''::text)))); -- --- Name: saas_faq_itens_ativo_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: saas_faq_itens_ativo_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX saas_faq_itens_ativo_idx ON public.saas_faq_itens USING btree (ativo); -- --- Name: saas_faq_itens_doc_id_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: saas_faq_itens_doc_id_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX saas_faq_itens_doc_id_idx ON public.saas_faq_itens USING btree (doc_id); -- --- Name: saas_faq_pagina_path_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: saas_faq_pagina_path_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX saas_faq_pagina_path_idx ON public.saas_faq USING btree (pagina_path); -- --- Name: saas_faq_publico_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: saas_faq_publico_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX saas_faq_publico_idx ON public.saas_faq USING btree (publico); -- --- Name: saas_faq_votos_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: saas_faq_votos_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX saas_faq_votos_idx ON public.saas_faq USING btree (votos DESC); -- --- Name: services_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: services_owner_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX services_owner_idx ON public.services USING btree (owner_id); -- --- Name: services_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: services_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX services_tenant_idx ON public.services USING btree (tenant_id); -- --- Name: sint_personal_created_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: sint_personal_created_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX sint_personal_created_idx ON public.subscription_intents_personal USING btree (created_at DESC); -- --- Name: sint_personal_status_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: sint_personal_status_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX sint_personal_status_idx ON public.subscription_intents_personal USING btree (status); -- --- Name: sint_tenant_created_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: sint_tenant_created_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX sint_tenant_created_idx ON public.subscription_intents_tenant USING btree (created_at DESC); -- --- Name: sint_tenant_status_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: sint_tenant_status_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX sint_tenant_status_idx ON public.subscription_intents_tenant USING btree (status); -- --- Name: sint_tenant_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: sint_tenant_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX sint_tenant_tenant_idx ON public.subscription_intents_tenant USING btree (tenant_id); -- --- Name: subscription_events_created_at_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: subscription_events_created_at_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX subscription_events_created_at_idx ON public.subscription_events USING btree (created_at DESC); -- --- Name: subscription_events_owner_ref_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: subscription_events_owner_ref_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX subscription_events_owner_ref_idx ON public.subscription_events USING btree (owner_type, owner_ref); -- --- Name: subscription_events_sub_created_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: subscription_events_sub_created_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX subscription_events_sub_created_idx ON public.subscription_events USING btree (subscription_id, created_at DESC); -- --- Name: subscription_events_subscription_id_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: subscription_events_subscription_id_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX subscription_events_subscription_id_idx ON public.subscription_events USING btree (subscription_id); -- --- Name: subscriptions_one_active_per_tenant; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: subscriptions_one_active_per_tenant; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX subscriptions_one_active_per_tenant ON public.subscriptions USING btree (tenant_id) WHERE (status = 'active'::text); -- --- Name: subscriptions_one_active_per_user; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: subscriptions_one_active_per_user; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX subscriptions_one_active_per_user ON public.subscriptions USING btree (user_id) WHERE (status = 'active'::text); -- --- Name: subscriptions_one_active_per_user_personal; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: subscriptions_one_active_per_user_personal; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX subscriptions_one_active_per_user_personal ON public.subscriptions USING btree (user_id) WHERE ((tenant_id IS NULL) AND (status = 'active'::text)); -- --- Name: subscriptions_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: subscriptions_owner_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX subscriptions_owner_idx ON public.subscriptions USING btree (user_id); -- --- Name: subscriptions_plan_key_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: subscriptions_plan_key_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX subscriptions_plan_key_idx ON public.subscriptions USING btree (plan_key); -- --- Name: subscriptions_status_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: subscriptions_status_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX subscriptions_status_idx ON public.subscriptions USING btree (status); -- --- Name: subscriptions_tenant_id_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: subscriptions_tenant_id_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX subscriptions_tenant_id_idx ON public.subscriptions USING btree (tenant_id); -- --- Name: subscriptions_tenant_period_end_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: subscriptions_tenant_period_end_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX subscriptions_tenant_period_end_idx ON public.subscriptions USING btree (tenant_id, current_period_end); -- --- Name: subscriptions_tenant_status_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: subscriptions_tenant_status_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX subscriptions_tenant_status_idx ON public.subscriptions USING btree (tenant_id, status); -- --- Name: subscriptions_user_status_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: subscriptions_user_status_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX subscriptions_user_status_idx ON public.subscriptions USING btree (user_id, status, created_at DESC); -- --- Name: support_sessions_expires_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: support_sessions_expires_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX support_sessions_expires_idx ON public.support_sessions USING btree (expires_at); -- --- Name: support_sessions_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: support_sessions_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX support_sessions_tenant_idx ON public.support_sessions USING btree (tenant_id); -- --- Name: support_sessions_token_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: support_sessions_token_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX support_sessions_token_idx ON public.support_sessions USING btree (token); -- --- Name: tenant_members_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: tenant_members_tenant_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX tenant_members_tenant_idx ON public.tenant_members USING btree (tenant_id); -- --- Name: tenant_members_user_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: tenant_members_user_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX tenant_members_user_idx ON public.tenant_members USING btree (user_id); -- --- Name: tenant_modules_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: tenant_modules_owner_idx; Type: INDEX; Schema: public; Owner: - -- CREATE INDEX tenant_modules_owner_idx ON public.tenant_modules USING btree (owner_id); -- --- Name: unique_member_per_tenant; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: unique_member_per_tenant; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX unique_member_per_tenant ON public.tenant_members USING btree (tenant_id, user_id); -- --- Name: uq_patients_tenant_user; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: uq_patient_contacts_primario; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_patient_contacts_primario ON public.patient_contacts USING btree (patient_id) WHERE ((is_primario = true) AND (ativo = true)); + + +-- +-- Name: uq_patients_tenant_user; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX uq_patients_tenant_user ON public.patients USING btree (tenant_id, user_id) WHERE (user_id IS NOT NULL); -- --- Name: uq_plan_price_active; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: uq_plan_price_active; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX uq_plan_price_active ON public.plan_prices USING btree (plan_id, "interval", currency) WHERE ((is_active = true) AND (active_to IS NULL)); -- --- Name: uq_plan_prices_active; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: uq_plan_prices_active; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX uq_plan_prices_active ON public.plan_prices USING btree (plan_id, "interval") WHERE (is_active = true); -- --- Name: uq_subscriptions_active_by_tenant; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: uq_subscriptions_active_by_tenant; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX uq_subscriptions_active_by_tenant ON public.subscriptions USING btree (tenant_id) WHERE ((tenant_id IS NOT NULL) AND (status = 'active'::text)); -- --- Name: uq_subscriptions_active_personal_by_user; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: uq_subscriptions_active_personal_by_user; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX uq_subscriptions_active_personal_by_user ON public.subscriptions USING btree (user_id) WHERE ((tenant_id IS NULL) AND (status = 'active'::text)); -- --- Name: uq_tenant_invites_pending; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: uq_tenant_invites_pending; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX uq_tenant_invites_pending ON public.tenant_invites USING btree (tenant_id, lower(email), role) WHERE ((accepted_at IS NULL) AND (revoked_at IS NULL)); -- --- Name: uq_tenant_members_tenant_user; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: uq_tenant_members_tenant_user; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX uq_tenant_members_tenant_user ON public.tenant_members USING btree (tenant_id, user_id); -- --- Name: ux_subscriptions_active_per_personal_user; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: ux_subscriptions_active_per_personal_user; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX ux_subscriptions_active_per_personal_user ON public.subscriptions USING btree (user_id) WHERE ((status = 'active'::text) AND (tenant_id IS NULL)); -- --- Name: ux_subscriptions_active_per_tenant; Type: INDEX; Schema: public; Owner: supabase_admin +-- Name: ux_subscriptions_active_per_tenant; Type: INDEX; Schema: public; Owner: - -- CREATE UNIQUE INDEX ux_subscriptions_active_per_tenant ON public.subscriptions USING btree (tenant_id) WHERE ((status = 'active'::text) AND (tenant_id IS NOT NULL)); -- --- Name: ix_realtime_subscription_entity; Type: INDEX; Schema: realtime; Owner: supabase_admin +-- Name: ix_realtime_subscription_entity; Type: INDEX; Schema: realtime; Owner: - -- CREATE INDEX ix_realtime_subscription_entity ON realtime.subscription USING btree (entity); -- --- Name: messages_inserted_at_topic_index; Type: INDEX; Schema: realtime; Owner: supabase_realtime_admin +-- Name: messages_inserted_at_topic_index; Type: INDEX; Schema: realtime; Owner: - -- CREATE INDEX messages_inserted_at_topic_index ON ONLY realtime.messages USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); -- --- Name: messages_2026_03_20_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_14_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - -- -CREATE INDEX messages_2026_03_20_inserted_at_topic_idx ON realtime.messages_2026_03_20 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); +CREATE INDEX messages_2026_04_14_inserted_at_topic_idx ON realtime.messages_2026_04_14 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); -- --- Name: messages_2026_03_21_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_15_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - -- -CREATE INDEX messages_2026_03_21_inserted_at_topic_idx ON realtime.messages_2026_03_21 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); +CREATE INDEX messages_2026_04_15_inserted_at_topic_idx ON realtime.messages_2026_04_15 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); -- --- Name: messages_2026_03_22_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_16_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - -- -CREATE INDEX messages_2026_03_22_inserted_at_topic_idx ON realtime.messages_2026_03_22 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); +CREATE INDEX messages_2026_04_16_inserted_at_topic_idx ON realtime.messages_2026_04_16 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); -- --- Name: messages_2026_03_23_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_17_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - -- -CREATE INDEX messages_2026_03_23_inserted_at_topic_idx ON realtime.messages_2026_03_23 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); +CREATE INDEX messages_2026_04_17_inserted_at_topic_idx ON realtime.messages_2026_04_17 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); -- --- Name: messages_2026_03_24_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_18_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - -- -CREATE INDEX messages_2026_03_24_inserted_at_topic_idx ON realtime.messages_2026_03_24 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); +CREATE INDEX messages_2026_04_18_inserted_at_topic_idx ON realtime.messages_2026_04_18 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); -- --- Name: messages_2026_03_25_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_19_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - -- -CREATE INDEX messages_2026_03_25_inserted_at_topic_idx ON realtime.messages_2026_03_25 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); +CREATE INDEX messages_2026_04_19_inserted_at_topic_idx ON realtime.messages_2026_04_19 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); -- --- Name: messages_2026_03_26_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: supabase_admin +-- Name: messages_2026_04_20_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - -- -CREATE INDEX messages_2026_03_26_inserted_at_topic_idx ON realtime.messages_2026_03_26 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); +CREATE INDEX messages_2026_04_20_inserted_at_topic_idx ON realtime.messages_2026_04_20 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); -- --- Name: subscription_subscription_id_entity_filters_key; Type: INDEX; Schema: realtime; Owner: supabase_admin +-- Name: subscription_subscription_id_entity_filters_key; Type: INDEX; Schema: realtime; Owner: - -- CREATE UNIQUE INDEX subscription_subscription_id_entity_filters_key ON realtime.subscription USING btree (subscription_id, entity, filters); -- --- Name: bname; Type: INDEX; Schema: storage; Owner: supabase_storage_admin +-- Name: bname; Type: INDEX; Schema: storage; Owner: - -- CREATE UNIQUE INDEX bname ON storage.buckets USING btree (name); -- --- Name: bucketid_objname; Type: INDEX; Schema: storage; Owner: supabase_storage_admin +-- Name: bucketid_objname; Type: INDEX; Schema: storage; Owner: - -- CREATE UNIQUE INDEX bucketid_objname ON storage.objects USING btree (bucket_id, name); -- --- Name: buckets_analytics_unique_name_idx; Type: INDEX; Schema: storage; Owner: supabase_storage_admin +-- Name: buckets_analytics_unique_name_idx; Type: INDEX; Schema: storage; Owner: - -- CREATE UNIQUE INDEX buckets_analytics_unique_name_idx ON storage.buckets_analytics USING btree (name) WHERE (deleted_at IS NULL); -- --- Name: idx_iceberg_namespaces_bucket_id; Type: INDEX; Schema: storage; Owner: supabase_storage_admin +-- Name: idx_iceberg_namespaces_bucket_id; Type: INDEX; Schema: storage; Owner: - -- CREATE UNIQUE INDEX idx_iceberg_namespaces_bucket_id ON storage.iceberg_namespaces USING btree (catalog_id, name); -- --- Name: idx_iceberg_tables_location; Type: INDEX; Schema: storage; Owner: supabase_storage_admin +-- Name: idx_iceberg_tables_location; Type: INDEX; Schema: storage; Owner: - -- CREATE UNIQUE INDEX idx_iceberg_tables_location ON storage.iceberg_tables USING btree (location); -- --- Name: idx_iceberg_tables_namespace_id; Type: INDEX; Schema: storage; Owner: supabase_storage_admin +-- Name: idx_iceberg_tables_namespace_id; Type: INDEX; Schema: storage; Owner: - -- CREATE UNIQUE INDEX idx_iceberg_tables_namespace_id ON storage.iceberg_tables USING btree (catalog_id, namespace_id, name); -- --- Name: idx_multipart_uploads_list; Type: INDEX; Schema: storage; Owner: supabase_storage_admin +-- Name: idx_multipart_uploads_list; Type: INDEX; Schema: storage; Owner: - -- CREATE INDEX idx_multipart_uploads_list ON storage.s3_multipart_uploads USING btree (bucket_id, key, created_at); -- --- Name: idx_objects_bucket_id_name; Type: INDEX; Schema: storage; Owner: supabase_storage_admin +-- Name: idx_objects_bucket_id_name; Type: INDEX; Schema: storage; Owner: - -- CREATE INDEX idx_objects_bucket_id_name ON storage.objects USING btree (bucket_id, name COLLATE "C"); -- --- Name: idx_objects_bucket_id_name_lower; Type: INDEX; Schema: storage; Owner: supabase_storage_admin +-- Name: idx_objects_bucket_id_name_lower; Type: INDEX; Schema: storage; Owner: - -- CREATE INDEX idx_objects_bucket_id_name_lower ON storage.objects USING btree (bucket_id, lower(name) COLLATE "C"); -- --- Name: name_prefix_search; Type: INDEX; Schema: storage; Owner: supabase_storage_admin +-- Name: name_prefix_search; Type: INDEX; Schema: storage; Owner: - -- CREATE INDEX name_prefix_search ON storage.objects USING btree (name text_pattern_ops); -- --- Name: vector_indexes_name_bucket_id_idx; Type: INDEX; Schema: storage; Owner: supabase_storage_admin +-- Name: vector_indexes_name_bucket_id_idx; Type: INDEX; Schema: storage; Owner: - -- CREATE UNIQUE INDEX vector_indexes_name_bucket_id_idx ON storage.vector_indexes USING btree (name, bucket_id); -- --- Name: supabase_functions_hooks_h_table_id_h_name_idx; Type: INDEX; Schema: supabase_functions; Owner: supabase_functions_admin +-- Name: supabase_functions_hooks_h_table_id_h_name_idx; Type: INDEX; Schema: supabase_functions; Owner: - -- CREATE INDEX supabase_functions_hooks_h_table_id_h_name_idx ON supabase_functions.hooks USING btree (hook_table_id, hook_name); -- --- Name: supabase_functions_hooks_request_id_idx; Type: INDEX; Schema: supabase_functions; Owner: supabase_functions_admin +-- Name: supabase_functions_hooks_request_id_idx; Type: INDEX; Schema: supabase_functions; Owner: - -- CREATE INDEX supabase_functions_hooks_request_id_idx ON supabase_functions.hooks USING btree (request_id); -- --- Name: messages_2026_03_20_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: supabase_realtime_admin +-- Name: messages_2026_04_14_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - -- -ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_20_inserted_at_topic_idx; +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_04_14_inserted_at_topic_idx; -- --- Name: messages_2026_03_20_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: supabase_realtime_admin +-- Name: messages_2026_04_14_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - -- -ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_20_pkey; +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_04_14_pkey; -- --- Name: messages_2026_03_21_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: supabase_realtime_admin +-- Name: messages_2026_04_15_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - -- -ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_21_inserted_at_topic_idx; +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_04_15_inserted_at_topic_idx; -- --- Name: messages_2026_03_21_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: supabase_realtime_admin +-- Name: messages_2026_04_15_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - -- -ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_21_pkey; +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_04_15_pkey; -- --- Name: messages_2026_03_22_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: supabase_realtime_admin +-- Name: messages_2026_04_16_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - -- -ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_22_inserted_at_topic_idx; +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_04_16_inserted_at_topic_idx; -- --- Name: messages_2026_03_22_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: supabase_realtime_admin +-- Name: messages_2026_04_16_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - -- -ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_22_pkey; +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_04_16_pkey; -- --- Name: messages_2026_03_23_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: supabase_realtime_admin +-- Name: messages_2026_04_17_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - -- -ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_23_inserted_at_topic_idx; +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_04_17_inserted_at_topic_idx; -- --- Name: messages_2026_03_23_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: supabase_realtime_admin +-- Name: messages_2026_04_17_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - -- -ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_23_pkey; +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_04_17_pkey; -- --- Name: messages_2026_03_24_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: supabase_realtime_admin +-- Name: messages_2026_04_18_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - -- -ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_24_inserted_at_topic_idx; +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_04_18_inserted_at_topic_idx; -- --- Name: messages_2026_03_24_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: supabase_realtime_admin +-- Name: messages_2026_04_18_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - -- -ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_24_pkey; +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_04_18_pkey; -- --- Name: messages_2026_03_25_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: supabase_realtime_admin +-- Name: messages_2026_04_19_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - -- -ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_25_inserted_at_topic_idx; +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_04_19_inserted_at_topic_idx; -- --- Name: messages_2026_03_25_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: supabase_realtime_admin +-- Name: messages_2026_04_19_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - -- -ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_25_pkey; +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_04_19_pkey; -- --- Name: messages_2026_03_26_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: supabase_realtime_admin +-- Name: messages_2026_04_20_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - -- -ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_26_inserted_at_topic_idx; +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_04_20_inserted_at_topic_idx; -- --- Name: messages_2026_03_26_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: supabase_realtime_admin +-- Name: messages_2026_04_20_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - -- -ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_26_pkey; +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_04_20_pkey; -- --- Name: users on_auth_user_created; Type: TRIGGER; Schema: auth; Owner: supabase_auth_admin +-- Name: users on_auth_user_created; Type: TRIGGER; Schema: auth; Owner: - -- 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 +-- Name: users trg_seed_patient_groups; Type: TRIGGER; Schema: auth; Owner: - -- 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 +-- Name: agenda_bloqueios agenda_bloqueios_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: agendador_configuracoes agendador_slug_trigger; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: tenant_members prevent_saas_membership_trigger; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: insurance_plan_services set_insurance_plan_services_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: user_settings t_user_settings_set_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: agenda_configuracoes tg_agenda_configuracoes_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: agenda_eventos tg_agenda_eventos_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: agenda_excecoes tg_agenda_excecoes_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: agenda_regras_semanais tg_agenda_regras_semanais_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: recurrence_rules tg_recurrence_rules_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: plan_public tr_plan_public_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: profiles trg_account_type_immutable; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: agenda_configuracoes trg_agenda_cfg_sync; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: agenda_eventos trg_agenda_eventos_busy_mirror_del; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: agenda_eventos trg_agenda_eventos_busy_mirror_ins; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: agenda_eventos trg_agenda_eventos_busy_mirror_upd; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: agenda_regras_semanais trg_agenda_regras_semanais_no_overlap; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: agenda_eventos trg_auto_financial_from_session; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: notification_preferences trg_cancel_notifs_on_opt_out; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: agenda_eventos trg_cancel_notifs_on_session_cancel; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: company_profiles trg_company_profiles_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: determined_commitment_fields trg_determined_commitment_fields_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: determined_commitments trg_determined_commitments_updated_at; Type: TRIGGER; Schema: public; Owner: - -- CREATE TRIGGER trg_determined_commitments_updated_at BEFORE UPDATE ON public.determined_commitments FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); -- --- Name: email_layout_config trg_email_layout_config_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin +-- Name: documents trg_documents_timeline_insert; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_documents_timeline_insert AFTER INSERT ON public.documents FOR EACH ROW EXECUTE FUNCTION public.fn_documents_timeline_insert(); + + +-- +-- Name: documents trg_documents_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_documents_updated_at BEFORE UPDATE ON public.documents FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: document_signatures trg_ds_timeline; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_ds_timeline AFTER UPDATE ON public.document_signatures FOR EACH ROW EXECUTE FUNCTION public.fn_document_signature_timeline(); + + +-- +-- Name: document_signatures trg_ds_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_ds_updated_at BEFORE UPDATE ON public.document_signatures FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: document_templates trg_dt_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_dt_updated_at BEFORE UPDATE ON public.document_templates FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: email_layout_config trg_email_layout_config_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: email_templates_global trg_email_templates_global_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: email_templates_tenant trg_email_templates_tenant_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: financial_exceptions trg_financial_exceptions_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: financial_records trg_financial_records_auto_overdue; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: financial_records trg_financial_records_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: global_notices trg_global_notices_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: insurance_plans trg_insurance_plans_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: medicos trg_medicos_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_medicos_updated_at BEFORE UPDATE ON public.medicos FOR EACH ROW EXECUTE FUNCTION public.set_medicos_updated_at(); + + +-- +-- Name: plans trg_no_change_core_plan_key; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: plans trg_no_change_plan_target; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: plans trg_no_delete_core_plans; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: notification_channels trg_notification_channels_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: notification_logs trg_notification_logs_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: notification_preferences trg_notification_preferences_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: notification_queue trg_notification_queue_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: notification_schedules trg_notification_schedules_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: notification_templates trg_notification_templates_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: patient_intake_requests trg_notify_on_intake; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: agendador_solicitacoes trg_notify_on_scheduling; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: agenda_eventos trg_notify_on_session_status; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: tenant_members trg_patient_cannot_own_tenant; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: patient_contacts trg_patient_contacts_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_contacts_updated_at BEFORE UPDATE ON public.patient_contacts FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patient_groups trg_patient_groups_set_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: patient_intake_requests trg_patient_intake_requests_updated_at; Type: TRIGGER; Schema: public; Owner: - -- CREATE TRIGGER trg_patient_intake_requests_updated_at BEFORE UPDATE ON public.patient_intake_requests FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); -- --- Name: patient_tags trg_patient_tags_set_updated_at; Type: TRIGGER; Schema: public; Owner: supabase_admin +-- Name: patients trg_patient_risco_timeline; Type: TRIGGER; Schema: public; Owner: - +-- + +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: patients trg_patient_status_history; Type: TRIGGER; Schema: public; Owner: - +-- + +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(); + + +-- +-- Name: patients trg_patient_status_timeline; Type: TRIGGER; Schema: public; Owner: - +-- + +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(); + + +-- +-- Name: patient_tags trg_patient_tags_set_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: patients trg_patients_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: patients trg_patients_validate_members; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: payment_settings trg_payment_settings_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: patient_groups trg_prevent_promoting_to_system; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: patient_groups trg_prevent_system_group_changes; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: professional_pricing trg_professional_pricing_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: profiles trg_profiles_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: patient_support_contacts trg_psc_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_psc_updated_at BEFORE UPDATE ON public.patient_support_contacts FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: services trg_services_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: subscription_intents trg_subscription_intents_view_insert; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: subscriptions trg_subscriptions_validate_scope; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: tenant_features trg_tenant_features_guard_with_plan; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: tenant_features trg_tenant_features_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: tenants trg_tenant_kind_immutable; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: therapist_payouts trg_therapist_payouts_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: user_settings trg_user_settings_updated_at; Type: TRIGGER; Schema: public; Owner: - -- 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 +-- Name: subscription tr_check_filters; Type: TRIGGER; Schema: realtime; Owner: - -- 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 +-- Name: buckets enforce_bucket_name_length_trigger; Type: TRIGGER; Schema: storage; Owner: - -- 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 +-- Name: buckets protect_buckets_delete; Type: TRIGGER; Schema: storage; Owner: - -- 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 +-- Name: objects protect_objects_delete; Type: TRIGGER; Schema: storage; Owner: - -- 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 +-- Name: objects update_objects_updated_at; Type: TRIGGER; Schema: storage; Owner: - -- CREATE TRIGGER update_objects_updated_at BEFORE UPDATE ON storage.objects FOR EACH ROW EXECUTE FUNCTION storage.update_updated_at_column(); -- --- Name: extensions extensions_tenant_external_id_fkey; Type: FK CONSTRAINT; Schema: _realtime; Owner: supabase_admin +-- Name: extensions extensions_tenant_external_id_fkey; Type: FK CONSTRAINT; Schema: _realtime; Owner: - -- ALTER TABLE ONLY _realtime.extensions @@ -16045,7 +17346,7 @@ ALTER TABLE ONLY _realtime.extensions -- --- Name: identities identities_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: identities identities_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.identities @@ -16053,7 +17354,7 @@ ALTER TABLE ONLY auth.identities -- --- Name: mfa_amr_claims mfa_amr_claims_session_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: mfa_amr_claims mfa_amr_claims_session_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.mfa_amr_claims @@ -16061,7 +17362,7 @@ ALTER TABLE ONLY auth.mfa_amr_claims -- --- Name: mfa_challenges mfa_challenges_auth_factor_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: mfa_challenges mfa_challenges_auth_factor_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.mfa_challenges @@ -16069,7 +17370,7 @@ ALTER TABLE ONLY auth.mfa_challenges -- --- Name: mfa_factors mfa_factors_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: mfa_factors mfa_factors_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.mfa_factors @@ -16077,7 +17378,7 @@ ALTER TABLE ONLY auth.mfa_factors -- --- Name: oauth_authorizations oauth_authorizations_client_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: oauth_authorizations oauth_authorizations_client_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.oauth_authorizations @@ -16085,7 +17386,7 @@ ALTER TABLE ONLY auth.oauth_authorizations -- --- Name: oauth_authorizations oauth_authorizations_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: oauth_authorizations oauth_authorizations_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.oauth_authorizations @@ -16093,7 +17394,7 @@ ALTER TABLE ONLY auth.oauth_authorizations -- --- Name: oauth_consents oauth_consents_client_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: oauth_consents oauth_consents_client_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.oauth_consents @@ -16101,7 +17402,7 @@ ALTER TABLE ONLY auth.oauth_consents -- --- Name: oauth_consents oauth_consents_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: oauth_consents oauth_consents_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.oauth_consents @@ -16109,7 +17410,7 @@ ALTER TABLE ONLY auth.oauth_consents -- --- Name: one_time_tokens one_time_tokens_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: one_time_tokens one_time_tokens_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.one_time_tokens @@ -16117,7 +17418,7 @@ ALTER TABLE ONLY auth.one_time_tokens -- --- Name: refresh_tokens refresh_tokens_session_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: refresh_tokens refresh_tokens_session_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.refresh_tokens @@ -16125,7 +17426,7 @@ ALTER TABLE ONLY auth.refresh_tokens -- --- Name: saml_providers saml_providers_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: saml_providers saml_providers_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.saml_providers @@ -16133,7 +17434,7 @@ ALTER TABLE ONLY auth.saml_providers -- --- Name: saml_relay_states saml_relay_states_flow_state_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: saml_relay_states saml_relay_states_flow_state_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.saml_relay_states @@ -16141,7 +17442,7 @@ ALTER TABLE ONLY auth.saml_relay_states -- --- Name: saml_relay_states saml_relay_states_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: saml_relay_states saml_relay_states_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.saml_relay_states @@ -16149,7 +17450,7 @@ ALTER TABLE ONLY auth.saml_relay_states -- --- Name: sessions sessions_oauth_client_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: sessions sessions_oauth_client_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.sessions @@ -16157,7 +17458,7 @@ ALTER TABLE ONLY auth.sessions -- --- Name: sessions sessions_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: sessions sessions_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.sessions @@ -16165,7 +17466,7 @@ ALTER TABLE ONLY auth.sessions -- --- Name: sso_domains sso_domains_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin +-- Name: sso_domains sso_domains_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - -- ALTER TABLE ONLY auth.sso_domains @@ -16173,7 +17474,7 @@ ALTER TABLE ONLY auth.sso_domains -- --- Name: addon_credits addon_credits_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: addon_credits addon_credits_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.addon_credits @@ -16181,7 +17482,7 @@ ALTER TABLE ONLY public.addon_credits -- --- Name: addon_credits addon_credits_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: addon_credits addon_credits_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.addon_credits @@ -16189,7 +17490,7 @@ ALTER TABLE ONLY public.addon_credits -- --- Name: addon_transactions addon_transactions_admin_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: addon_transactions addon_transactions_admin_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.addon_transactions @@ -16197,7 +17498,7 @@ ALTER TABLE ONLY public.addon_transactions -- --- Name: addon_transactions addon_transactions_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: addon_transactions addon_transactions_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.addon_transactions @@ -16205,7 +17506,7 @@ ALTER TABLE ONLY public.addon_transactions -- --- Name: addon_transactions addon_transactions_product_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: addon_transactions addon_transactions_product_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.addon_transactions @@ -16213,7 +17514,7 @@ ALTER TABLE ONLY public.addon_transactions -- --- Name: addon_transactions addon_transactions_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: addon_transactions addon_transactions_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.addon_transactions @@ -16221,7 +17522,7 @@ ALTER TABLE ONLY public.addon_transactions -- --- Name: agenda_bloqueios agenda_bloqueios_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_bloqueios agenda_bloqueios_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_bloqueios @@ -16229,7 +17530,7 @@ ALTER TABLE ONLY public.agenda_bloqueios -- --- Name: agenda_bloqueios agenda_bloqueios_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_bloqueios agenda_bloqueios_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_bloqueios @@ -16237,7 +17538,7 @@ ALTER TABLE ONLY public.agenda_bloqueios -- --- Name: agenda_configuracoes agenda_configuracoes_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_configuracoes agenda_configuracoes_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_configuracoes @@ -16245,7 +17546,7 @@ ALTER TABLE ONLY public.agenda_configuracoes -- --- Name: agenda_eventos agenda_eventos_billing_contract_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos agenda_eventos_billing_contract_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_eventos @@ -16253,7 +17554,7 @@ ALTER TABLE ONLY public.agenda_eventos -- --- Name: agenda_eventos agenda_eventos_determined_commitment_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos agenda_eventos_determined_commitment_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_eventos @@ -16261,7 +17562,7 @@ ALTER TABLE ONLY public.agenda_eventos -- --- Name: agenda_eventos agenda_eventos_insurance_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos agenda_eventos_insurance_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_eventos @@ -16269,7 +17570,7 @@ ALTER TABLE ONLY public.agenda_eventos -- --- Name: agenda_eventos agenda_eventos_insurance_plan_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos agenda_eventos_insurance_plan_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_eventos @@ -16277,7 +17578,7 @@ ALTER TABLE ONLY public.agenda_eventos -- --- Name: agenda_eventos agenda_eventos_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos agenda_eventos_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_eventos @@ -16285,7 +17586,7 @@ ALTER TABLE ONLY public.agenda_eventos -- --- Name: agenda_eventos agenda_eventos_recurrence_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos agenda_eventos_recurrence_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_eventos @@ -16293,7 +17594,7 @@ ALTER TABLE ONLY public.agenda_eventos -- --- Name: agenda_eventos agenda_eventos_terapeuta_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos agenda_eventos_terapeuta_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_eventos @@ -16301,7 +17602,7 @@ ALTER TABLE ONLY public.agenda_eventos -- --- Name: agenda_excecoes agenda_excecoes_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_excecoes agenda_excecoes_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_excecoes @@ -16309,7 +17610,7 @@ ALTER TABLE ONLY public.agenda_excecoes -- --- Name: agenda_online_slots agenda_online_slots_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_online_slots agenda_online_slots_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_online_slots @@ -16317,7 +17618,7 @@ ALTER TABLE ONLY public.agenda_online_slots -- --- Name: agenda_online_slots agenda_online_slots_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_online_slots agenda_online_slots_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_online_slots @@ -16325,7 +17626,7 @@ ALTER TABLE ONLY public.agenda_online_slots -- --- Name: agenda_regras_semanais agenda_regras_semanais_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_regras_semanais agenda_regras_semanais_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_regras_semanais @@ -16333,7 +17634,7 @@ ALTER TABLE ONLY public.agenda_regras_semanais -- --- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_slots_bloqueados_semanais @@ -16341,7 +17642,7 @@ ALTER TABLE ONLY public.agenda_slots_bloqueados_semanais -- --- Name: agenda_slots_regras agenda_slots_regras_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agenda_slots_regras agenda_slots_regras_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agenda_slots_regras @@ -16349,7 +17650,7 @@ ALTER TABLE ONLY public.agenda_slots_regras -- --- Name: agendador_configuracoes agendador_configuracoes_owner_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agendador_configuracoes agendador_configuracoes_owner_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agendador_configuracoes @@ -16357,7 +17658,7 @@ ALTER TABLE ONLY public.agendador_configuracoes -- --- Name: agendador_configuracoes agendador_configuracoes_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agendador_configuracoes agendador_configuracoes_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agendador_configuracoes @@ -16365,7 +17666,7 @@ ALTER TABLE ONLY public.agendador_configuracoes -- --- Name: agendador_solicitacoes agendador_sol_owner_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agendador_solicitacoes agendador_sol_owner_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agendador_solicitacoes @@ -16373,7 +17674,7 @@ ALTER TABLE ONLY public.agendador_solicitacoes -- --- Name: agendador_solicitacoes agendador_sol_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: agendador_solicitacoes agendador_sol_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.agendador_solicitacoes @@ -16381,7 +17682,7 @@ ALTER TABLE ONLY public.agendador_solicitacoes -- --- Name: billing_contracts billing_contracts_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: billing_contracts billing_contracts_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.billing_contracts @@ -16389,7 +17690,7 @@ ALTER TABLE ONLY public.billing_contracts -- --- Name: billing_contracts billing_contracts_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: billing_contracts billing_contracts_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.billing_contracts @@ -16397,7 +17698,7 @@ ALTER TABLE ONLY public.billing_contracts -- --- Name: commitment_services commitment_services_commitment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: commitment_services commitment_services_commitment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.commitment_services @@ -16405,7 +17706,7 @@ ALTER TABLE ONLY public.commitment_services -- --- Name: commitment_services commitment_services_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: commitment_services commitment_services_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.commitment_services @@ -16413,7 +17714,7 @@ ALTER TABLE ONLY public.commitment_services -- --- Name: commitment_time_logs commitment_time_logs_calendar_event_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: commitment_time_logs commitment_time_logs_calendar_event_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.commitment_time_logs @@ -16421,7 +17722,7 @@ ALTER TABLE ONLY public.commitment_time_logs -- --- Name: commitment_time_logs commitment_time_logs_commitment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: commitment_time_logs commitment_time_logs_commitment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.commitment_time_logs @@ -16429,7 +17730,7 @@ ALTER TABLE ONLY public.commitment_time_logs -- --- Name: commitment_time_logs commitment_time_logs_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: commitment_time_logs commitment_time_logs_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.commitment_time_logs @@ -16437,15 +17738,15 @@ ALTER TABLE ONLY public.commitment_time_logs -- --- Name: company_profiles company_profiles_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: company_profiles company_profiles_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.company_profiles - ADD CONSTRAINT company_profiles_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES auth.users(id) ON DELETE CASCADE; + ADD CONSTRAINT company_profiles_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; -- --- Name: determined_commitment_fields determined_commitment_fields_commitment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: determined_commitment_fields determined_commitment_fields_commitment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.determined_commitment_fields @@ -16453,7 +17754,7 @@ ALTER TABLE ONLY public.determined_commitment_fields -- --- Name: determined_commitment_fields determined_commitment_fields_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: determined_commitment_fields determined_commitment_fields_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.determined_commitment_fields @@ -16461,7 +17762,7 @@ ALTER TABLE ONLY public.determined_commitment_fields -- --- Name: determined_commitments determined_commitments_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: determined_commitments determined_commitments_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.determined_commitments @@ -16469,7 +17770,71 @@ ALTER TABLE ONLY public.determined_commitments -- --- Name: email_layout_config email_layout_config_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: document_access_logs document_access_logs_documento_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_access_logs + ADD CONSTRAINT document_access_logs_documento_id_fkey FOREIGN KEY (documento_id) REFERENCES public.documents(id) ON DELETE CASCADE; + + +-- +-- Name: document_generated document_generated_documento_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_generated + ADD CONSTRAINT document_generated_documento_id_fkey FOREIGN KEY (documento_id) REFERENCES public.documents(id) ON DELETE SET NULL; + + +-- +-- Name: document_generated document_generated_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_generated + ADD CONSTRAINT document_generated_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: document_generated document_generated_template_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_generated + ADD CONSTRAINT document_generated_template_id_fkey FOREIGN KEY (template_id) REFERENCES public.document_templates(id) ON DELETE RESTRICT; + + +-- +-- Name: document_share_links document_share_links_documento_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_share_links + ADD CONSTRAINT document_share_links_documento_id_fkey FOREIGN KEY (documento_id) REFERENCES public.documents(id) ON DELETE CASCADE; + + +-- +-- Name: document_signatures document_signatures_documento_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_signatures + ADD CONSTRAINT document_signatures_documento_id_fkey FOREIGN KEY (documento_id) REFERENCES public.documents(id) ON DELETE CASCADE; + + +-- +-- Name: documents documents_agenda_evento_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.documents + ADD CONSTRAINT documents_agenda_evento_id_fkey FOREIGN KEY (agenda_evento_id) REFERENCES public.agenda_eventos(id) ON DELETE SET NULL; + + +-- +-- Name: documents documents_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.documents + ADD CONSTRAINT documents_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: email_layout_config email_layout_config_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.email_layout_config @@ -16477,7 +17842,7 @@ ALTER TABLE ONLY public.email_layout_config -- --- Name: email_templates_tenant email_templates_tenant_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: email_templates_tenant email_templates_tenant_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.email_templates_tenant @@ -16485,7 +17850,7 @@ ALTER TABLE ONLY public.email_templates_tenant -- --- Name: email_templates_tenant email_templates_tenant_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: email_templates_tenant email_templates_tenant_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.email_templates_tenant @@ -16493,7 +17858,7 @@ ALTER TABLE ONLY public.email_templates_tenant -- --- Name: feriados feriados_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: feriados feriados_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.feriados @@ -16501,7 +17866,7 @@ ALTER TABLE ONLY public.feriados -- --- Name: feriados feriados_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: feriados feriados_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.feriados @@ -16509,7 +17874,7 @@ ALTER TABLE ONLY public.feriados -- --- Name: financial_categories financial_categories_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: financial_categories financial_categories_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.financial_categories @@ -16517,7 +17882,7 @@ ALTER TABLE ONLY public.financial_categories -- --- Name: financial_exceptions financial_exceptions_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: financial_exceptions financial_exceptions_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.financial_exceptions @@ -16525,7 +17890,7 @@ ALTER TABLE ONLY public.financial_exceptions -- --- Name: financial_records financial_records_agenda_evento_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: financial_records financial_records_agenda_evento_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.financial_records @@ -16533,7 +17898,7 @@ ALTER TABLE ONLY public.financial_records -- --- Name: financial_records financial_records_category_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: financial_records financial_records_category_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.financial_records @@ -16541,7 +17906,7 @@ ALTER TABLE ONLY public.financial_records -- --- Name: financial_records financial_records_insurance_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: financial_records financial_records_insurance_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.financial_records @@ -16549,7 +17914,7 @@ ALTER TABLE ONLY public.financial_records -- --- Name: financial_records financial_records_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: financial_records financial_records_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.financial_records @@ -16557,7 +17922,7 @@ ALTER TABLE ONLY public.financial_records -- --- Name: financial_records financial_records_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: financial_records financial_records_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.financial_records @@ -16565,7 +17930,7 @@ ALTER TABLE ONLY public.financial_records -- --- Name: financial_records financial_records_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: financial_records financial_records_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.financial_records @@ -16573,7 +17938,7 @@ ALTER TABLE ONLY public.financial_records -- --- Name: global_notices global_notices_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: global_notices global_notices_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.global_notices @@ -16581,7 +17946,7 @@ ALTER TABLE ONLY public.global_notices -- --- Name: insurance_plan_services insurance_plan_services_plan_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: insurance_plan_services insurance_plan_services_plan_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.insurance_plan_services @@ -16589,7 +17954,7 @@ ALTER TABLE ONLY public.insurance_plan_services -- --- Name: insurance_plans insurance_plans_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: insurance_plans insurance_plans_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.insurance_plans @@ -16597,7 +17962,7 @@ ALTER TABLE ONLY public.insurance_plans -- --- Name: module_features module_features_feature_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: module_features module_features_feature_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.module_features @@ -16605,7 +17970,7 @@ ALTER TABLE ONLY public.module_features -- --- Name: module_features module_features_module_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: module_features module_features_module_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.module_features @@ -16613,7 +17978,7 @@ ALTER TABLE ONLY public.module_features -- --- Name: notice_dismissals notice_dismissals_notice_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: notice_dismissals notice_dismissals_notice_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.notice_dismissals @@ -16621,7 +17986,7 @@ ALTER TABLE ONLY public.notice_dismissals -- --- Name: notice_dismissals notice_dismissals_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: notice_dismissals notice_dismissals_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.notice_dismissals @@ -16629,7 +17994,7 @@ ALTER TABLE ONLY public.notice_dismissals -- --- Name: notification_channels notification_channels_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: notification_channels notification_channels_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.notification_channels @@ -16637,7 +18002,7 @@ ALTER TABLE ONLY public.notification_channels -- --- Name: notification_logs notification_logs_queue_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: notification_logs notification_logs_queue_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.notification_logs @@ -16645,7 +18010,7 @@ ALTER TABLE ONLY public.notification_logs -- --- Name: notification_schedules notification_schedules_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: notification_schedules notification_schedules_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.notification_schedules @@ -16653,7 +18018,7 @@ ALTER TABLE ONLY public.notification_schedules -- --- Name: notification_templates notification_templates_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: notification_templates notification_templates_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.notification_templates @@ -16661,7 +18026,7 @@ ALTER TABLE ONLY public.notification_templates -- --- Name: notifications notifications_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: notifications notifications_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.notifications @@ -16669,7 +18034,23 @@ ALTER TABLE ONLY public.notifications -- --- Name: patient_discounts patient_discounts_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_contacts patient_contacts_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_contacts + ADD CONSTRAINT patient_contacts_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_contacts patient_contacts_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_contacts + ADD CONSTRAINT patient_contacts_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_discounts patient_discounts_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patient_discounts @@ -16677,7 +18058,7 @@ ALTER TABLE ONLY public.patient_discounts -- --- Name: patient_discounts patient_discounts_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_discounts patient_discounts_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patient_discounts @@ -16685,7 +18066,7 @@ ALTER TABLE ONLY public.patient_discounts -- --- Name: patient_group_patient patient_group_patient_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_group_patient patient_group_patient_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patient_group_patient @@ -16693,7 +18074,7 @@ ALTER TABLE ONLY public.patient_group_patient -- --- Name: patient_group_patient patient_group_patient_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_group_patient patient_group_patient_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patient_group_patient @@ -16701,7 +18082,7 @@ ALTER TABLE ONLY public.patient_group_patient -- --- Name: patient_groups patient_groups_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_groups patient_groups_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patient_groups @@ -16709,7 +18090,7 @@ ALTER TABLE ONLY public.patient_groups -- --- Name: patient_intake_requests patient_intake_requests_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_intake_requests patient_intake_requests_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patient_intake_requests @@ -16717,7 +18098,7 @@ ALTER TABLE ONLY public.patient_intake_requests -- --- Name: patient_invites patient_invites_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_invites patient_invites_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patient_invites @@ -16725,7 +18106,7 @@ ALTER TABLE ONLY public.patient_invites -- --- Name: patient_patient_tag patient_patient_tag_tag_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_patient_tag patient_patient_tag_tag_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patient_patient_tag @@ -16733,7 +18114,7 @@ ALTER TABLE ONLY public.patient_patient_tag -- --- Name: patient_patient_tag patient_patient_tag_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_patient_tag patient_patient_tag_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patient_patient_tag @@ -16741,7 +18122,39 @@ ALTER TABLE ONLY public.patient_patient_tag -- --- Name: patient_tags patient_tags_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_status_history patient_status_history_alterado_por_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_status_history + ADD CONSTRAINT patient_status_history_alterado_por_fkey FOREIGN KEY (alterado_por) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: patient_status_history patient_status_history_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_status_history + ADD CONSTRAINT patient_status_history_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_status_history patient_status_history_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_status_history + ADD CONSTRAINT patient_status_history_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_support_contacts patient_support_contacts_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_support_contacts + ADD CONSTRAINT patient_support_contacts_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_tags patient_tags_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patient_tags @@ -16749,7 +18162,39 @@ ALTER TABLE ONLY public.patient_tags -- --- Name: patients patients_responsible_member_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_timeline patient_timeline_gerado_por_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_timeline + ADD CONSTRAINT patient_timeline_gerado_por_fkey FOREIGN KEY (gerado_por) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: patient_timeline patient_timeline_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_timeline + ADD CONSTRAINT patient_timeline_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_timeline patient_timeline_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_timeline + ADD CONSTRAINT patient_timeline_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patients patients_convenio_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_convenio_id_fkey FOREIGN KEY (convenio_id) REFERENCES public.insurance_plans(id) ON DELETE SET NULL; + + +-- +-- Name: patients patients_responsible_member_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patients @@ -16757,7 +18202,15 @@ ALTER TABLE ONLY public.patients -- --- Name: patients patients_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patients patients_risco_sinalizado_por_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_risco_sinalizado_por_fkey FOREIGN KEY (risco_sinalizado_por) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: patients patients_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patients @@ -16765,7 +18218,7 @@ ALTER TABLE ONLY public.patients -- --- Name: patients patients_therapist_member_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patients patients_therapist_member_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patients @@ -16773,7 +18226,7 @@ ALTER TABLE ONLY public.patients -- --- Name: patients patients_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patients patients_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patients @@ -16781,7 +18234,7 @@ ALTER TABLE ONLY public.patients -- --- Name: payment_settings payment_settings_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: payment_settings payment_settings_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.payment_settings @@ -16789,7 +18242,7 @@ ALTER TABLE ONLY public.payment_settings -- --- Name: payment_settings payment_settings_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: payment_settings payment_settings_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.payment_settings @@ -16797,7 +18250,7 @@ ALTER TABLE ONLY public.payment_settings -- --- Name: plan_features plan_features_feature_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: plan_features plan_features_feature_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.plan_features @@ -16805,7 +18258,7 @@ ALTER TABLE ONLY public.plan_features -- --- Name: plan_features plan_features_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: plan_features plan_features_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.plan_features @@ -16813,7 +18266,7 @@ ALTER TABLE ONLY public.plan_features -- --- Name: plan_prices plan_prices_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: plan_prices plan_prices_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.plan_prices @@ -16821,7 +18274,7 @@ ALTER TABLE ONLY public.plan_prices -- --- Name: plan_public_bullets plan_public_bullets_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: plan_public_bullets plan_public_bullets_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.plan_public_bullets @@ -16829,7 +18282,7 @@ ALTER TABLE ONLY public.plan_public_bullets -- --- Name: plan_public plan_public_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: plan_public plan_public_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.plan_public @@ -16837,7 +18290,7 @@ ALTER TABLE ONLY public.plan_public -- --- Name: patient_patient_tag ppt_patient_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_patient_tag ppt_patient_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patient_patient_tag @@ -16845,7 +18298,7 @@ ALTER TABLE ONLY public.patient_patient_tag -- --- Name: patient_patient_tag ppt_tag_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: patient_patient_tag ppt_tag_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.patient_patient_tag @@ -16853,7 +18306,7 @@ ALTER TABLE ONLY public.patient_patient_tag -- --- Name: professional_pricing professional_pricing_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: professional_pricing professional_pricing_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.professional_pricing @@ -16861,7 +18314,7 @@ ALTER TABLE ONLY public.professional_pricing -- --- Name: profiles profiles_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: profiles profiles_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.profiles @@ -16869,7 +18322,15 @@ ALTER TABLE ONLY public.profiles -- --- Name: recurrence_exceptions recurrence_exceptions_agenda_evento_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: profiles profiles_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.profiles + ADD CONSTRAINT profiles_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id); + + +-- +-- Name: recurrence_exceptions recurrence_exceptions_agenda_evento_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.recurrence_exceptions @@ -16877,7 +18338,7 @@ ALTER TABLE ONLY public.recurrence_exceptions -- --- Name: recurrence_exceptions recurrence_exceptions_recurrence_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: recurrence_exceptions recurrence_exceptions_recurrence_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.recurrence_exceptions @@ -16885,7 +18346,7 @@ ALTER TABLE ONLY public.recurrence_exceptions -- --- Name: recurrence_rule_services recurrence_rule_services_rule_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: recurrence_rule_services recurrence_rule_services_rule_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.recurrence_rule_services @@ -16893,7 +18354,7 @@ ALTER TABLE ONLY public.recurrence_rule_services -- --- Name: recurrence_rule_services recurrence_rule_services_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: recurrence_rule_services recurrence_rule_services_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.recurrence_rule_services @@ -16901,7 +18362,7 @@ ALTER TABLE ONLY public.recurrence_rule_services -- --- Name: recurrence_rules recurrence_rules_insurance_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: recurrence_rules recurrence_rules_insurance_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.recurrence_rules @@ -16909,7 +18370,7 @@ ALTER TABLE ONLY public.recurrence_rules -- --- Name: recurrence_rules recurrence_rules_insurance_plan_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: recurrence_rules recurrence_rules_insurance_plan_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.recurrence_rules @@ -16917,7 +18378,7 @@ ALTER TABLE ONLY public.recurrence_rules -- --- Name: saas_admins saas_admins_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: saas_admins saas_admins_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.saas_admins @@ -16925,7 +18386,7 @@ ALTER TABLE ONLY public.saas_admins -- --- Name: saas_doc_votos saas_doc_votos_doc_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: saas_doc_votos saas_doc_votos_doc_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.saas_doc_votos @@ -16933,7 +18394,7 @@ ALTER TABLE ONLY public.saas_doc_votos -- --- Name: saas_doc_votos saas_doc_votos_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: saas_doc_votos saas_doc_votos_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.saas_doc_votos @@ -16941,7 +18402,7 @@ ALTER TABLE ONLY public.saas_doc_votos -- --- Name: saas_faq_itens saas_faq_itens_doc_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: saas_faq_itens saas_faq_itens_doc_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.saas_faq_itens @@ -16949,7 +18410,7 @@ ALTER TABLE ONLY public.saas_faq_itens -- --- Name: services services_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: services services_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.services @@ -16957,7 +18418,7 @@ ALTER TABLE ONLY public.services -- --- Name: subscription_intents_personal sint_personal_subscription_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: subscription_intents_personal sint_personal_subscription_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.subscription_intents_personal @@ -16965,7 +18426,7 @@ ALTER TABLE ONLY public.subscription_intents_personal -- --- Name: subscription_intents_tenant sint_tenant_subscription_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: subscription_intents_tenant sint_tenant_subscription_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.subscription_intents_tenant @@ -16973,7 +18434,7 @@ ALTER TABLE ONLY public.subscription_intents_tenant -- --- Name: subscription_events subscription_events_subscription_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: subscription_events subscription_events_subscription_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.subscription_events @@ -16981,7 +18442,7 @@ ALTER TABLE ONLY public.subscription_events -- --- Name: subscription_intents_personal subscription_intents_personal_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: subscription_intents_personal subscription_intents_personal_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.subscription_intents_personal @@ -16989,7 +18450,7 @@ ALTER TABLE ONLY public.subscription_intents_personal -- --- Name: subscription_intents_tenant subscription_intents_tenant_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: subscription_intents_tenant subscription_intents_tenant_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.subscription_intents_tenant @@ -16997,7 +18458,7 @@ ALTER TABLE ONLY public.subscription_intents_tenant -- --- Name: subscription_intents_legacy subscription_intents_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: subscription_intents_legacy subscription_intents_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.subscription_intents_legacy @@ -17005,7 +18466,7 @@ ALTER TABLE ONLY public.subscription_intents_legacy -- --- Name: subscriptions subscriptions_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: subscriptions subscriptions_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.subscriptions @@ -17013,7 +18474,7 @@ ALTER TABLE ONLY public.subscriptions -- --- Name: subscriptions subscriptions_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: subscriptions subscriptions_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.subscriptions @@ -17021,7 +18482,7 @@ ALTER TABLE ONLY public.subscriptions -- --- Name: support_sessions support_sessions_admin_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: support_sessions support_sessions_admin_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.support_sessions @@ -17029,7 +18490,7 @@ ALTER TABLE ONLY public.support_sessions -- --- Name: support_sessions support_sessions_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: support_sessions support_sessions_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.support_sessions @@ -17037,7 +18498,7 @@ ALTER TABLE ONLY public.support_sessions -- --- Name: tenant_features tenant_features_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: tenant_features tenant_features_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.tenant_features @@ -17045,7 +18506,7 @@ ALTER TABLE ONLY public.tenant_features -- --- Name: tenant_invites tenant_invites_accepted_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: tenant_invites tenant_invites_accepted_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.tenant_invites @@ -17053,7 +18514,7 @@ ALTER TABLE ONLY public.tenant_invites -- --- Name: tenant_invites tenant_invites_invited_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: tenant_invites tenant_invites_invited_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.tenant_invites @@ -17061,7 +18522,7 @@ ALTER TABLE ONLY public.tenant_invites -- --- Name: tenant_invites tenant_invites_revoked_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: tenant_invites tenant_invites_revoked_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.tenant_invites @@ -17069,7 +18530,7 @@ ALTER TABLE ONLY public.tenant_invites -- --- Name: tenant_invites tenant_invites_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: tenant_invites tenant_invites_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.tenant_invites @@ -17077,7 +18538,7 @@ ALTER TABLE ONLY public.tenant_invites -- --- Name: tenant_members tenant_members_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: tenant_members tenant_members_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.tenant_members @@ -17085,7 +18546,7 @@ ALTER TABLE ONLY public.tenant_members -- --- Name: tenant_members tenant_members_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: tenant_members tenant_members_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.tenant_members @@ -17093,7 +18554,7 @@ ALTER TABLE ONLY public.tenant_members -- --- Name: tenant_modules tenant_modules_module_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: tenant_modules tenant_modules_module_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.tenant_modules @@ -17101,7 +18562,7 @@ ALTER TABLE ONLY public.tenant_modules -- --- Name: tenant_modules tenant_modules_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: tenant_modules tenant_modules_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.tenant_modules @@ -17109,7 +18570,7 @@ ALTER TABLE ONLY public.tenant_modules -- --- Name: therapist_payout_records therapist_payout_records_financial_record_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: therapist_payout_records therapist_payout_records_financial_record_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.therapist_payout_records @@ -17117,7 +18578,7 @@ ALTER TABLE ONLY public.therapist_payout_records -- --- Name: therapist_payout_records therapist_payout_records_payout_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: therapist_payout_records therapist_payout_records_payout_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.therapist_payout_records @@ -17125,7 +18586,7 @@ ALTER TABLE ONLY public.therapist_payout_records -- --- Name: therapist_payouts therapist_payouts_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: therapist_payouts therapist_payouts_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.therapist_payouts @@ -17133,7 +18594,7 @@ ALTER TABLE ONLY public.therapist_payouts -- --- Name: therapist_payouts therapist_payouts_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: therapist_payouts therapist_payouts_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.therapist_payouts @@ -17141,7 +18602,15 @@ ALTER TABLE ONLY public.therapist_payouts -- --- Name: user_settings user_settings_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin +-- Name: twilio_subaccount_usage twilio_subaccount_usage_channel_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.twilio_subaccount_usage + ADD CONSTRAINT twilio_subaccount_usage_channel_fk FOREIGN KEY (channel_id) REFERENCES public.notification_channels(id) ON DELETE CASCADE; + + +-- +-- Name: user_settings user_settings_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.user_settings @@ -17149,7 +18618,7 @@ ALTER TABLE ONLY public.user_settings -- --- Name: iceberg_namespaces iceberg_namespaces_catalog_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: supabase_storage_admin +-- Name: iceberg_namespaces iceberg_namespaces_catalog_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - -- ALTER TABLE ONLY storage.iceberg_namespaces @@ -17157,7 +18626,7 @@ ALTER TABLE ONLY storage.iceberg_namespaces -- --- Name: iceberg_tables iceberg_tables_catalog_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: supabase_storage_admin +-- Name: iceberg_tables iceberg_tables_catalog_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - -- ALTER TABLE ONLY storage.iceberg_tables @@ -17165,7 +18634,7 @@ ALTER TABLE ONLY storage.iceberg_tables -- --- Name: iceberg_tables iceberg_tables_namespace_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: supabase_storage_admin +-- Name: iceberg_tables iceberg_tables_namespace_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - -- ALTER TABLE ONLY storage.iceberg_tables @@ -17173,7 +18642,7 @@ ALTER TABLE ONLY storage.iceberg_tables -- --- Name: objects objects_bucketId_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: supabase_storage_admin +-- Name: objects objects_bucketId_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - -- ALTER TABLE ONLY storage.objects @@ -17181,7 +18650,7 @@ ALTER TABLE ONLY storage.objects -- --- Name: s3_multipart_uploads s3_multipart_uploads_bucket_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: supabase_storage_admin +-- Name: s3_multipart_uploads s3_multipart_uploads_bucket_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - -- ALTER TABLE ONLY storage.s3_multipart_uploads @@ -17189,7 +18658,7 @@ ALTER TABLE ONLY storage.s3_multipart_uploads -- --- Name: s3_multipart_uploads_parts s3_multipart_uploads_parts_bucket_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: supabase_storage_admin +-- Name: s3_multipart_uploads_parts s3_multipart_uploads_parts_bucket_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - -- ALTER TABLE ONLY storage.s3_multipart_uploads_parts @@ -17197,7 +18666,7 @@ ALTER TABLE ONLY storage.s3_multipart_uploads_parts -- --- Name: s3_multipart_uploads_parts s3_multipart_uploads_parts_upload_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: supabase_storage_admin +-- Name: s3_multipart_uploads_parts s3_multipart_uploads_parts_upload_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - -- ALTER TABLE ONLY storage.s3_multipart_uploads_parts @@ -17205,7 +18674,7 @@ ALTER TABLE ONLY storage.s3_multipart_uploads_parts -- --- Name: vector_indexes vector_indexes_bucket_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: supabase_storage_admin +-- Name: vector_indexes vector_indexes_bucket_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - -- ALTER TABLE ONLY storage.vector_indexes @@ -17213,109 +18682,109 @@ ALTER TABLE ONLY storage.vector_indexes -- --- Name: audit_log_entries; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin +-- Name: audit_log_entries; Type: ROW SECURITY; Schema: auth; Owner: - -- ALTER TABLE auth.audit_log_entries ENABLE ROW LEVEL SECURITY; -- --- Name: flow_state; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin +-- Name: flow_state; Type: ROW SECURITY; Schema: auth; Owner: - -- ALTER TABLE auth.flow_state ENABLE ROW LEVEL SECURITY; -- --- Name: identities; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin +-- Name: identities; Type: ROW SECURITY; Schema: auth; Owner: - -- ALTER TABLE auth.identities ENABLE ROW LEVEL SECURITY; -- --- Name: instances; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin +-- Name: instances; Type: ROW SECURITY; Schema: auth; Owner: - -- ALTER TABLE auth.instances ENABLE ROW LEVEL SECURITY; -- --- Name: mfa_amr_claims; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin +-- Name: mfa_amr_claims; Type: ROW SECURITY; Schema: auth; Owner: - -- ALTER TABLE auth.mfa_amr_claims ENABLE ROW LEVEL SECURITY; -- --- Name: mfa_challenges; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin +-- Name: mfa_challenges; Type: ROW SECURITY; Schema: auth; Owner: - -- ALTER TABLE auth.mfa_challenges ENABLE ROW LEVEL SECURITY; -- --- Name: mfa_factors; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin +-- Name: mfa_factors; Type: ROW SECURITY; Schema: auth; Owner: - -- ALTER TABLE auth.mfa_factors ENABLE ROW LEVEL SECURITY; -- --- Name: one_time_tokens; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin +-- Name: one_time_tokens; Type: ROW SECURITY; Schema: auth; Owner: - -- ALTER TABLE auth.one_time_tokens ENABLE ROW LEVEL SECURITY; -- --- Name: refresh_tokens; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin +-- Name: refresh_tokens; Type: ROW SECURITY; Schema: auth; Owner: - -- ALTER TABLE auth.refresh_tokens ENABLE ROW LEVEL SECURITY; -- --- Name: saml_providers; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin +-- Name: saml_providers; Type: ROW SECURITY; Schema: auth; Owner: - -- ALTER TABLE auth.saml_providers ENABLE ROW LEVEL SECURITY; -- --- Name: saml_relay_states; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin +-- Name: saml_relay_states; Type: ROW SECURITY; Schema: auth; Owner: - -- ALTER TABLE auth.saml_relay_states ENABLE ROW LEVEL SECURITY; -- --- Name: schema_migrations; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin +-- Name: schema_migrations; Type: ROW SECURITY; Schema: auth; Owner: - -- ALTER TABLE auth.schema_migrations ENABLE ROW LEVEL SECURITY; -- --- Name: sessions; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin +-- Name: sessions; Type: ROW SECURITY; Schema: auth; Owner: - -- ALTER TABLE auth.sessions ENABLE ROW LEVEL SECURITY; -- --- Name: sso_domains; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin +-- Name: sso_domains; Type: ROW SECURITY; Schema: auth; Owner: - -- ALTER TABLE auth.sso_domains ENABLE ROW LEVEL SECURITY; -- --- Name: sso_providers; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin +-- Name: sso_providers; Type: ROW SECURITY; Schema: auth; Owner: - -- ALTER TABLE auth.sso_providers ENABLE ROW LEVEL SECURITY; -- --- Name: users; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin +-- Name: users; Type: ROW SECURITY; Schema: auth; Owner: - -- ALTER TABLE auth.users ENABLE ROW LEVEL SECURITY; -- --- Name: addon_credits; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: addon_credits; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.addon_credits ENABLE ROW LEVEL SECURITY; -- --- Name: addon_credits addon_credits_admin_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: addon_credits addon_credits_admin_select; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY addon_credits_admin_select ON public.addon_credits FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 @@ -17324,7 +18793,7 @@ CREATE POLICY addon_credits_admin_select ON public.addon_credits FOR SELECT TO a -- --- Name: addon_credits addon_credits_admin_write; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: addon_credits addon_credits_admin_write; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY addon_credits_admin_write ON public.addon_credits TO authenticated USING ((EXISTS ( SELECT 1 @@ -17335,20 +18804,20 @@ CREATE POLICY addon_credits_admin_write ON public.addon_credits TO authenticated -- --- Name: addon_credits addon_credits_select_own; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: addon_credits addon_credits_select_own; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY addon_credits_select_own ON public.addon_credits FOR SELECT TO authenticated USING ((public.is_tenant_member(tenant_id) OR (owner_id = auth.uid()))); -- --- Name: addon_products; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: addon_products; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.addon_products ENABLE ROW LEVEL SECURITY; -- --- Name: addon_products addon_products_admin_all; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: addon_products addon_products_admin_all; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY addon_products_admin_all ON public.addon_products TO authenticated USING ((EXISTS ( SELECT 1 @@ -17359,20 +18828,20 @@ CREATE POLICY addon_products_admin_all ON public.addon_products TO authenticated -- --- Name: addon_products addon_products_select_authenticated; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: addon_products addon_products_select_authenticated; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY addon_products_select_authenticated ON public.addon_products FOR SELECT TO authenticated USING (((deleted_at IS NULL) AND (is_active = true) AND (is_visible = true))); -- --- Name: addon_transactions; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: addon_transactions; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.addon_transactions ENABLE ROW LEVEL SECURITY; -- --- Name: addon_transactions addon_transactions_admin_insert; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: addon_transactions addon_transactions_admin_insert; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY addon_transactions_admin_insert ON public.addon_transactions FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 @@ -17381,7 +18850,7 @@ CREATE POLICY addon_transactions_admin_insert ON public.addon_transactions FOR I -- --- Name: addon_transactions addon_transactions_admin_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: addon_transactions addon_transactions_admin_select; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY addon_transactions_admin_select ON public.addon_transactions FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 @@ -17390,297 +18859,297 @@ CREATE POLICY addon_transactions_admin_select ON public.addon_transactions FOR S -- --- Name: addon_transactions addon_transactions_select_own; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: addon_transactions addon_transactions_select_own; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY addon_transactions_select_own ON public.addon_transactions FOR SELECT TO authenticated USING ((public.is_tenant_member(tenant_id) OR (owner_id = auth.uid()))); -- --- Name: agenda_bloqueios; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: agenda_bloqueios; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.agenda_bloqueios ENABLE ROW LEVEL SECURITY; -- --- Name: agenda_configuracoes; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: agenda_configuracoes; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.agenda_configuracoes ENABLE ROW LEVEL SECURITY; -- --- Name: agenda_configuracoes agenda_configuracoes_clinic_read; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_configuracoes agenda_configuracoes_clinic_read; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agenda_configuracoes_clinic_read ON public.agenda_configuracoes FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); -- --- Name: agenda_configuracoes agenda_configuracoes_clinic_write; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_configuracoes agenda_configuracoes_clinic_write; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agenda_configuracoes_clinic_write ON public.agenda_configuracoes USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); -- --- Name: agenda_configuracoes agenda_configuracoes_owner; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_configuracoes agenda_configuracoes_owner; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agenda_configuracoes_owner ON public.agenda_configuracoes USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); -- --- Name: agenda_eventos; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.agenda_eventos ENABLE ROW LEVEL SECURITY; -- --- Name: agenda_eventos agenda_eventos_delete; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos agenda_eventos_delete; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agenda_eventos_delete ON public.agenda_eventos FOR DELETE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.delete'::text))); -- --- Name: agenda_eventos agenda_eventos_insert; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos agenda_eventos_insert; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agenda_eventos_insert ON public.agenda_eventos FOR INSERT WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.create'::text))); -- --- Name: agenda_eventos agenda_eventos_owner_all; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos agenda_eventos_owner_all; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agenda_eventos_owner_all ON public.agenda_eventos TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); -- --- Name: agenda_eventos agenda_eventos_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos agenda_eventos_select; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agenda_eventos_select ON public.agenda_eventos FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); -- --- Name: agenda_eventos agenda_eventos_update; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_eventos agenda_eventos_update; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agenda_eventos_update ON public.agenda_eventos FOR UPDATE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); -- --- Name: agenda_excecoes; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: agenda_excecoes; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.agenda_excecoes ENABLE ROW LEVEL SECURITY; -- --- Name: agenda_excecoes agenda_excecoes_owner; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_excecoes agenda_excecoes_owner; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agenda_excecoes_owner ON public.agenda_excecoes USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); -- --- Name: agenda_excecoes agenda_excecoes_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_excecoes agenda_excecoes_select; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agenda_excecoes_select ON public.agenda_excecoes FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); -- --- Name: agenda_excecoes agenda_excecoes_write; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_excecoes agenda_excecoes_write; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agenda_excecoes_write ON public.agenda_excecoes USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); -- --- Name: agenda_online_slots; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: agenda_online_slots; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.agenda_online_slots ENABLE ROW LEVEL SECURITY; -- --- Name: agenda_online_slots agenda_online_slots_owner; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_online_slots agenda_online_slots_owner; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agenda_online_slots_owner ON public.agenda_online_slots USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); -- --- Name: agenda_online_slots agenda_online_slots_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_online_slots agenda_online_slots_select; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agenda_online_slots_select ON public.agenda_online_slots FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); -- --- Name: agenda_online_slots agenda_online_slots_write; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_online_slots agenda_online_slots_write; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agenda_online_slots_write ON public.agenda_online_slots USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); -- --- Name: agenda_regras_semanais; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: agenda_regras_semanais; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.agenda_regras_semanais ENABLE ROW LEVEL SECURITY; -- --- Name: agenda_regras_semanais agenda_regras_semanais_owner; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_regras_semanais agenda_regras_semanais_owner; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agenda_regras_semanais_owner ON public.agenda_regras_semanais USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); -- --- Name: agenda_regras_semanais agenda_regras_semanais_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_regras_semanais agenda_regras_semanais_select; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agenda_regras_semanais_select ON public.agenda_regras_semanais FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); -- --- Name: agenda_regras_semanais agenda_regras_semanais_write; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_regras_semanais agenda_regras_semanais_write; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agenda_regras_semanais_write ON public.agenda_regras_semanais USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); -- --- Name: agenda_slots_bloqueados_semanais; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: agenda_slots_bloqueados_semanais; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.agenda_slots_bloqueados_semanais ENABLE ROW LEVEL SECURITY; -- --- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_select; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agenda_slots_bloqueados_semanais_select ON public.agenda_slots_bloqueados_semanais FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); -- --- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_write; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_write; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agenda_slots_bloqueados_semanais_write ON public.agenda_slots_bloqueados_semanais USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); -- --- Name: agenda_slots_regras; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: agenda_slots_regras; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.agenda_slots_regras ENABLE ROW LEVEL SECURITY; -- --- Name: agenda_slots_regras agenda_slots_regras_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_slots_regras agenda_slots_regras_select; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agenda_slots_regras_select ON public.agenda_slots_regras FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); -- --- Name: agenda_slots_regras agenda_slots_regras_write; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_slots_regras agenda_slots_regras_write; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agenda_slots_regras_write ON public.agenda_slots_regras USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); -- --- Name: agendador_configuracoes agendador_cfg_public_read; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agendador_configuracoes agendador_cfg_public_read; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agendador_cfg_public_read ON public.agendador_configuracoes FOR SELECT TO anon USING (((ativo = true) AND (link_slug IS NOT NULL))); -- --- Name: agendador_configuracoes agendador_cfg_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agendador_configuracoes agendador_cfg_select; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agendador_cfg_select ON public.agendador_configuracoes FOR SELECT USING ((auth.uid() = owner_id)); -- --- Name: agendador_configuracoes agendador_cfg_write; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agendador_configuracoes agendador_cfg_write; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agendador_cfg_write ON public.agendador_configuracoes USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); -- --- Name: agendador_configuracoes; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: agendador_configuracoes; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.agendador_configuracoes ENABLE ROW LEVEL SECURITY; -- --- Name: agendador_solicitacoes agendador_sol_owner_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agendador_solicitacoes agendador_sol_owner_select; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agendador_sol_owner_select ON public.agendador_solicitacoes FOR SELECT USING ((auth.uid() = owner_id)); -- --- Name: agendador_solicitacoes agendador_sol_owner_write; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agendador_solicitacoes agendador_sol_owner_write; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agendador_sol_owner_write ON public.agendador_solicitacoes USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); -- --- Name: agendador_solicitacoes agendador_sol_patient_read; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agendador_solicitacoes agendador_sol_patient_read; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agendador_sol_patient_read ON public.agendador_solicitacoes FOR SELECT TO authenticated USING (((auth.uid() = user_id) OR (auth.uid() = owner_id))); -- --- Name: agendador_solicitacoes agendador_sol_public_insert; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agendador_solicitacoes agendador_sol_public_insert; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY agendador_sol_public_insert ON public.agendador_solicitacoes FOR INSERT TO anon WITH CHECK (true); -- --- Name: agendador_solicitacoes; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: agendador_solicitacoes; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.agendador_solicitacoes ENABLE ROW LEVEL SECURITY; -- --- Name: billing_contracts; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: billing_contracts; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.billing_contracts ENABLE ROW LEVEL SECURITY; -- --- Name: billing_contracts billing_contracts: owner full access; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: billing_contracts billing_contracts: owner full access; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "billing_contracts: owner full access" ON public.billing_contracts USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); -- --- Name: agenda_bloqueios bloqueios_delete; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_bloqueios bloqueios_delete; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY bloqueios_delete ON public.agenda_bloqueios FOR DELETE TO authenticated USING ((owner_id = auth.uid())); -- --- Name: agenda_bloqueios bloqueios_insert; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_bloqueios bloqueios_insert; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY bloqueios_insert ON public.agenda_bloqueios FOR INSERT TO authenticated WITH CHECK ((owner_id = auth.uid())); -- --- Name: agenda_bloqueios bloqueios_select_clinic; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_bloqueios bloqueios_select_clinic; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY bloqueios_select_clinic ON public.agenda_bloqueios FOR SELECT TO authenticated USING ((tenant_id IN ( SELECT tenant_members.tenant_id @@ -17689,21 +19158,21 @@ CREATE POLICY bloqueios_select_clinic ON public.agenda_bloqueios FOR SELECT TO a -- --- Name: agenda_bloqueios bloqueios_select_own; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_bloqueios bloqueios_select_own; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY bloqueios_select_own ON public.agenda_bloqueios FOR SELECT TO authenticated USING ((owner_id = auth.uid())); -- --- Name: agenda_bloqueios bloqueios_update; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_bloqueios bloqueios_update; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY bloqueios_update ON public.agenda_bloqueios FOR UPDATE TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); -- --- Name: saas_docs clinic_admin_read_all_docs; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: saas_docs clinic_admin_read_all_docs; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY clinic_admin_read_all_docs ON public.saas_docs FOR SELECT TO authenticated USING (((ativo = true) AND (EXISTS ( SELECT 1 @@ -17712,13 +19181,13 @@ CREATE POLICY clinic_admin_read_all_docs ON public.saas_docs FOR SELECT TO authe -- --- Name: commitment_services; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: commitment_services; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.commitment_services ENABLE ROW LEVEL SECURITY; -- --- Name: commitment_services commitment_services: owner full access; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: commitment_services commitment_services: owner full access; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "commitment_services: owner full access" ON public.commitment_services USING ((EXISTS ( SELECT 1 @@ -17729,47 +19198,57 @@ CREATE POLICY "commitment_services: owner full access" ON public.commitment_serv -- --- Name: commitment_time_logs; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: commitment_time_logs; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.commitment_time_logs ENABLE ROW LEVEL SECURITY; -- --- Name: company_profiles; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: company_profiles; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.company_profiles ENABLE ROW LEVEL SECURITY; -- --- Name: company_profiles company_profiles_delete; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: company_profiles company_profiles_delete; Type: POLICY; Schema: public; Owner: - -- -CREATE POLICY company_profiles_delete ON public.company_profiles FOR DELETE USING ((tenant_id = auth.uid())); +CREATE POLICY company_profiles_delete ON public.company_profiles FOR DELETE USING ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); -- --- Name: company_profiles company_profiles_insert; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: company_profiles company_profiles_insert; Type: POLICY; Schema: public; Owner: - -- -CREATE POLICY company_profiles_insert ON public.company_profiles FOR INSERT WITH CHECK ((tenant_id = auth.uid())); +CREATE POLICY company_profiles_insert ON public.company_profiles FOR INSERT WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); -- --- Name: company_profiles company_profiles_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: company_profiles company_profiles_select; Type: POLICY; Schema: public; Owner: - -- -CREATE POLICY company_profiles_select ON public.company_profiles FOR SELECT USING ((tenant_id = auth.uid())); +CREATE POLICY company_profiles_select ON public.company_profiles FOR SELECT USING ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); -- --- Name: company_profiles company_profiles_update; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: company_profiles company_profiles_update; Type: POLICY; Schema: public; Owner: - -- -CREATE POLICY company_profiles_update ON public.company_profiles FOR UPDATE USING ((tenant_id = auth.uid())); +CREATE POLICY company_profiles_update ON public.company_profiles FOR UPDATE USING ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); -- --- Name: commitment_time_logs ctl_delete_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: commitment_time_logs ctl_delete_for_active_member; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY ctl_delete_for_active_member ON public.commitment_time_logs FOR DELETE TO authenticated USING ((EXISTS ( SELECT 1 @@ -17778,7 +19257,7 @@ CREATE POLICY ctl_delete_for_active_member ON public.commitment_time_logs FOR DE -- --- Name: commitment_time_logs ctl_insert_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: commitment_time_logs ctl_insert_for_active_member; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY ctl_insert_for_active_member ON public.commitment_time_logs FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 @@ -17787,7 +19266,7 @@ CREATE POLICY ctl_insert_for_active_member ON public.commitment_time_logs FOR IN -- --- Name: commitment_time_logs ctl_select_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: commitment_time_logs ctl_select_for_active_member; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY ctl_select_for_active_member ON public.commitment_time_logs FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 @@ -17796,7 +19275,7 @@ CREATE POLICY ctl_select_for_active_member ON public.commitment_time_logs FOR SE -- --- Name: commitment_time_logs ctl_update_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: commitment_time_logs ctl_update_for_active_member; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY ctl_update_for_active_member ON public.commitment_time_logs FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 @@ -17807,7 +19286,23 @@ CREATE POLICY ctl_update_for_active_member ON public.commitment_time_logs FOR UP -- --- Name: determined_commitments dc_delete_custom_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: document_access_logs dal: tenant members can insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "dal: tenant members can insert" ON public.document_access_logs FOR INSERT WITH CHECK (true); + + +-- +-- Name: document_access_logs dal: tenant members can select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "dal: tenant members can select" ON public.document_access_logs FOR SELECT USING ((tenant_id IN ( SELECT tm.tenant_id + FROM public.tenant_members tm + WHERE ((tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitments dc_delete_custom_for_active_member; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY dc_delete_custom_for_active_member ON public.determined_commitments FOR DELETE TO authenticated USING (((is_native = false) AND (EXISTS ( SELECT 1 @@ -17816,7 +19311,7 @@ CREATE POLICY dc_delete_custom_for_active_member ON public.determined_commitment -- --- Name: determined_commitments dc_insert_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: determined_commitments dc_insert_for_active_member; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY dc_insert_for_active_member ON public.determined_commitments FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 @@ -17825,7 +19320,7 @@ CREATE POLICY dc_insert_for_active_member ON public.determined_commitments FOR I -- --- Name: determined_commitments dc_select_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: determined_commitments dc_select_for_active_member; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY dc_select_for_active_member ON public.determined_commitments FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 @@ -17834,7 +19329,7 @@ CREATE POLICY dc_select_for_active_member ON public.determined_commitments FOR S -- --- Name: determined_commitments dc_update_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: determined_commitments dc_update_for_active_member; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY dc_update_for_active_member ON public.determined_commitments FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 @@ -17845,7 +19340,7 @@ CREATE POLICY dc_update_for_active_member ON public.determined_commitments FOR U -- --- Name: determined_commitment_fields dcf_delete_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: determined_commitment_fields dcf_delete_for_active_member; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY dcf_delete_for_active_member ON public.determined_commitment_fields FOR DELETE TO authenticated USING ((EXISTS ( SELECT 1 @@ -17854,7 +19349,7 @@ CREATE POLICY dcf_delete_for_active_member ON public.determined_commitment_field -- --- Name: determined_commitment_fields dcf_insert_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: determined_commitment_fields dcf_insert_for_active_member; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY dcf_insert_for_active_member ON public.determined_commitment_fields FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 @@ -17863,7 +19358,7 @@ CREATE POLICY dcf_insert_for_active_member ON public.determined_commitment_field -- --- Name: determined_commitment_fields dcf_select_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: determined_commitment_fields dcf_select_for_active_member; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY dcf_select_for_active_member ON public.determined_commitment_fields FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 @@ -17872,7 +19367,7 @@ CREATE POLICY dcf_select_for_active_member ON public.determined_commitment_field -- --- Name: determined_commitment_fields dcf_update_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: determined_commitment_fields dcf_update_for_active_member; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY dcf_update_for_active_member ON public.determined_commitment_fields FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 @@ -17883,26 +19378,26 @@ CREATE POLICY dcf_update_for_active_member ON public.determined_commitment_field -- --- Name: agenda_bloqueios delete own; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_bloqueios delete own; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "delete own" ON public.agenda_bloqueios FOR DELETE USING ((owner_id = auth.uid())); -- --- Name: determined_commitment_fields; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: determined_commitment_fields; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.determined_commitment_fields ENABLE ROW LEVEL SECURITY; -- --- Name: determined_commitments; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: determined_commitments; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.determined_commitments ENABLE ROW LEVEL SECURITY; -- --- Name: dev_user_credentials dev_creds_select_saas_admin; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: dev_user_credentials dev_creds_select_saas_admin; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY dev_creds_select_saas_admin ON public.dev_user_credentials FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 @@ -17911,7 +19406,7 @@ CREATE POLICY dev_creds_select_saas_admin ON public.dev_user_credentials FOR SEL -- --- Name: dev_user_credentials dev_creds_write_saas_admin; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: dev_user_credentials dev_creds_write_saas_admin; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY dev_creds_write_saas_admin ON public.dev_user_credentials TO authenticated USING ((EXISTS ( SELECT 1 @@ -17922,58 +19417,200 @@ CREATE POLICY dev_creds_write_saas_admin ON public.dev_user_credentials TO authe -- --- Name: dev_user_credentials; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: dev_user_credentials; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.dev_user_credentials ENABLE ROW LEVEL SECURITY; -- --- Name: email_layout_config; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: document_generated dg: generator full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "dg: generator full access" ON public.document_generated USING ((gerado_por = auth.uid())) WITH CHECK ((gerado_por = auth.uid())); + + +-- +-- Name: document_generated dg: tenant members can select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "dg: tenant members can select" ON public.document_generated FOR SELECT USING ((tenant_id IN ( SELECT tm.tenant_id + FROM public.tenant_members tm + WHERE ((tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: document_access_logs; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.document_access_logs ENABLE ROW LEVEL SECURITY; + +-- +-- Name: document_generated; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.document_generated ENABLE ROW LEVEL SECURITY; + +-- +-- Name: document_share_links; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.document_share_links ENABLE ROW LEVEL SECURITY; + +-- +-- Name: document_signatures; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.document_signatures ENABLE ROW LEVEL SECURITY; + +-- +-- Name: document_templates; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.document_templates ENABLE ROW LEVEL SECURITY; + +-- +-- Name: documents; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.documents ENABLE ROW LEVEL SECURITY; + +-- +-- Name: documents documents: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "documents: owner full access" ON public.documents USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: document_signatures ds: tenant members access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "ds: tenant members access" ON public.document_signatures USING ((tenant_id IN ( SELECT tm.tenant_id + FROM public.tenant_members tm + WHERE ((tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))) WITH CHECK ((tenant_id IN ( SELECT tm.tenant_id + FROM public.tenant_members tm + WHERE ((tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: document_share_links dsl: creator full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "dsl: creator full access" ON public.document_share_links USING ((criado_por = auth.uid())) WITH CHECK ((criado_por = auth.uid())); + + +-- +-- Name: document_share_links dsl: public read by token; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "dsl: public read by token" ON public.document_share_links FOR SELECT USING (((ativo = true) AND (expira_em > now()) AND (usos < usos_max))); + + +-- +-- Name: document_templates dt: global templates readable by all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "dt: global templates readable by all" ON public.document_templates FOR SELECT USING ((is_global = true)); + + +-- +-- Name: document_templates dt: owner can delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "dt: owner can delete" ON public.document_templates FOR DELETE USING (((owner_id = auth.uid()) AND (is_global = false))); + + +-- +-- Name: document_templates dt: owner can insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "dt: owner can insert" ON public.document_templates FOR INSERT WITH CHECK (((owner_id = auth.uid()) AND (is_global = false))); + + +-- +-- Name: document_templates dt: owner can update; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "dt: owner can update" ON public.document_templates FOR UPDATE USING (((owner_id = auth.uid()) AND (is_global = false))) WITH CHECK (((owner_id = auth.uid()) AND (is_global = false))); + + +-- +-- Name: document_templates dt: saas admin can delete global; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "dt: saas admin can delete global" ON public.document_templates FOR DELETE USING (((is_global = true) AND public.is_saas_admin())); + + +-- +-- Name: document_templates dt: saas admin can insert global; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "dt: saas admin can insert global" ON public.document_templates FOR INSERT WITH CHECK (((is_global = true) AND public.is_saas_admin())); + + +-- +-- Name: document_templates dt: saas admin can update global; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "dt: saas admin can update global" ON public.document_templates FOR UPDATE USING (((is_global = true) AND public.is_saas_admin())) WITH CHECK (((is_global = true) AND public.is_saas_admin())); + + +-- +-- Name: document_templates dt: tenant members can select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "dt: tenant members can select" ON public.document_templates FOR SELECT USING (((is_global = false) AND (tenant_id IN ( SELECT tm.tenant_id + FROM public.tenant_members tm + WHERE ((tm.user_id = auth.uid()) AND (tm.status = 'active'::text)))))); + + +-- +-- Name: email_layout_config; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.email_layout_config ENABLE ROW LEVEL SECURITY; -- --- Name: email_templates_global; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: email_templates_global; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.email_templates_global ENABLE ROW LEVEL SECURITY; -- --- Name: email_templates_tenant; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: email_templates_tenant; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.email_templates_tenant ENABLE ROW LEVEL SECURITY; -- --- Name: entitlements_invalidation ent_inv_select_own; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: entitlements_invalidation ent_inv_select_own; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY ent_inv_select_own ON public.entitlements_invalidation FOR SELECT USING (((owner_id = auth.uid()) OR public.is_saas_admin())); -- --- Name: entitlements_invalidation ent_inv_update_saas; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: entitlements_invalidation ent_inv_update_saas; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY ent_inv_update_saas ON public.entitlements_invalidation FOR UPDATE USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); -- --- Name: entitlements_invalidation ent_inv_write_saas; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: entitlements_invalidation ent_inv_write_saas; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY ent_inv_write_saas ON public.entitlements_invalidation FOR INSERT WITH CHECK (public.is_saas_admin()); -- --- Name: entitlements_invalidation; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: entitlements_invalidation; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.entitlements_invalidation ENABLE ROW LEVEL SECURITY; -- --- Name: saas_faq faq_admin_write; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: saas_faq faq_admin_write; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY faq_admin_write ON public.saas_faq TO authenticated USING ((EXISTS ( SELECT 1 @@ -17982,14 +19619,14 @@ CREATE POLICY faq_admin_write ON public.saas_faq TO authenticated USING ((EXISTS -- --- Name: saas_faq faq_auth_read; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: saas_faq faq_auth_read; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY faq_auth_read ON public.saas_faq FOR SELECT TO authenticated USING ((ativo = true)); -- --- Name: saas_faq_itens faq_itens_admin_write; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: saas_faq_itens faq_itens_admin_write; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY faq_itens_admin_write ON public.saas_faq_itens TO authenticated USING ((EXISTS ( SELECT 1 @@ -17998,7 +19635,7 @@ CREATE POLICY faq_itens_admin_write ON public.saas_faq_itens TO authenticated US -- --- Name: saas_faq_itens faq_itens_auth_read; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: saas_faq_itens faq_itens_auth_read; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY faq_itens_auth_read ON public.saas_faq_itens FOR SELECT TO authenticated USING (((ativo = true) AND (EXISTS ( SELECT 1 @@ -18007,54 +19644,54 @@ CREATE POLICY faq_itens_auth_read ON public.saas_faq_itens FOR SELECT TO authent -- --- Name: saas_faq faq_public_read; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: saas_faq faq_public_read; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY faq_public_read ON public.saas_faq FOR SELECT USING (((publico = true) AND (ativo = true))); -- --- Name: features; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: features; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.features ENABLE ROW LEVEL SECURITY; -- --- Name: features features_read_authenticated; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: features features_read_authenticated; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY features_read_authenticated ON public.features FOR SELECT TO authenticated USING (true); -- --- Name: features features_write_saas_admin; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: features features_write_saas_admin; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY features_write_saas_admin ON public.features TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); -- --- Name: feriados; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: feriados; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.feriados ENABLE ROW LEVEL SECURITY; -- --- Name: feriados feriados_delete; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: feriados feriados_delete; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY feriados_delete ON public.feriados FOR DELETE USING ((owner_id = auth.uid())); -- --- Name: feriados feriados_global_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: feriados feriados_global_select; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY feriados_global_select ON public.feriados FOR SELECT USING ((tenant_id IS NULL)); -- --- Name: feriados feriados_insert; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: feriados feriados_insert; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY feriados_insert ON public.feriados FOR INSERT WITH CHECK ((tenant_id IN ( SELECT tenant_members.tenant_id @@ -18063,7 +19700,7 @@ CREATE POLICY feriados_insert ON public.feriados FOR INSERT WITH CHECK ((tenant_ -- --- Name: feriados feriados_saas_delete; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: feriados feriados_saas_delete; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY feriados_saas_delete ON public.feriados FOR DELETE USING ((EXISTS ( SELECT 1 @@ -18072,7 +19709,7 @@ CREATE POLICY feriados_saas_delete ON public.feriados FOR DELETE USING ((EXISTS -- --- Name: feriados feriados_saas_insert; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: feriados feriados_saas_insert; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY feriados_saas_insert ON public.feriados FOR INSERT WITH CHECK ((EXISTS ( SELECT 1 @@ -18081,7 +19718,7 @@ CREATE POLICY feriados_saas_insert ON public.feriados FOR INSERT WITH CHECK ((EX -- --- Name: feriados feriados_saas_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: feriados feriados_saas_select; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY feriados_saas_select ON public.feriados FOR SELECT USING ((EXISTS ( SELECT 1 @@ -18090,7 +19727,7 @@ CREATE POLICY feriados_saas_select ON public.feriados FOR SELECT USING ((EXISTS -- --- Name: feriados feriados_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: feriados feriados_select; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY feriados_select ON public.feriados FOR SELECT USING ((tenant_id IN ( SELECT tenant_members.tenant_id @@ -18099,33 +19736,33 @@ CREATE POLICY feriados_select ON public.feriados FOR SELECT USING ((tenant_id IN -- --- Name: financial_categories; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: financial_categories; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.financial_categories ENABLE ROW LEVEL SECURITY; -- --- Name: financial_categories financial_categories_self; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: financial_categories financial_categories_self; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY financial_categories_self ON public.financial_categories USING ((auth.uid() = user_id)) WITH CHECK ((auth.uid() = user_id)); -- --- Name: financial_exceptions; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: financial_exceptions; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.financial_exceptions ENABLE ROW LEVEL SECURITY; -- --- Name: financial_exceptions financial_exceptions: owner full access; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: financial_exceptions financial_exceptions: owner full access; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "financial_exceptions: owner full access" ON public.financial_exceptions USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); -- --- Name: financial_exceptions financial_exceptions: tenant members read clinic rules; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: financial_exceptions financial_exceptions: tenant members read clinic rules; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "financial_exceptions: tenant members read clinic rules" ON public.financial_exceptions FOR SELECT USING (((owner_id IS NULL) AND (EXISTS ( SELECT 1 @@ -18134,27 +19771,27 @@ CREATE POLICY "financial_exceptions: tenant members read clinic rules" ON public -- --- Name: financial_records; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: financial_records; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.financial_records ENABLE ROW LEVEL SECURITY; -- --- Name: financial_records financial_records_self; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: financial_records financial_records_self; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY financial_records_self ON public.financial_records USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); -- --- Name: financial_records financial_records_tenant_admin; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: financial_records financial_records_tenant_admin; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY financial_records_tenant_admin ON public.financial_records FOR SELECT USING (((tenant_id IS NOT NULL) AND public.is_tenant_admin(tenant_id))); -- --- Name: financial_records financial_records_tenant_member_read; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: financial_records financial_records_tenant_member_read; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY financial_records_tenant_member_read ON public.financial_records FOR SELECT USING (((tenant_id IS NOT NULL) AND (EXISTS ( SELECT 1 @@ -18163,20 +19800,20 @@ CREATE POLICY financial_records_tenant_member_read ON public.financial_records F -- --- Name: email_templates_global global templates readable by authenticated; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: email_templates_global global templates readable by authenticated; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "global templates readable by authenticated" ON public.email_templates_global FOR SELECT USING ((auth.role() = 'authenticated'::text)); -- --- Name: global_notices; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: global_notices; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.global_notices ENABLE ROW LEVEL SECURITY; -- --- Name: global_notices global_notices_saas_all; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: global_notices global_notices_saas_all; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY global_notices_saas_all ON public.global_notices TO authenticated USING ((EXISTS ( SELECT 1 @@ -18185,27 +19822,27 @@ CREATE POLICY global_notices_saas_all ON public.global_notices TO authenticated -- --- Name: global_notices global_notices_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: global_notices global_notices_select; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY global_notices_select ON public.global_notices FOR SELECT TO authenticated USING ((is_active = true)); -- --- Name: agenda_bloqueios insert own; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_bloqueios insert own; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "insert own" ON public.agenda_bloqueios FOR INSERT WITH CHECK ((owner_id = auth.uid())); -- --- Name: insurance_plan_services; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: insurance_plan_services; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.insurance_plan_services ENABLE ROW LEVEL SECURITY; -- --- Name: insurance_plan_services insurance_plan_services_owner; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: insurance_plan_services insurance_plan_services_owner; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY insurance_plan_services_owner ON public.insurance_plan_services USING ((EXISTS ( SELECT 1 @@ -18216,107 +19853,120 @@ CREATE POLICY insurance_plan_services_owner ON public.insurance_plan_services US -- --- Name: insurance_plans; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: insurance_plans; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.insurance_plans ENABLE ROW LEVEL SECURITY; -- --- Name: insurance_plans insurance_plans: owner full access; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: insurance_plans insurance_plans: owner full access; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "insurance_plans: owner full access" ON public.insurance_plans USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); -- --- Name: login_carousel_slides; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: login_carousel_slides; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.login_carousel_slides ENABLE ROW LEVEL SECURITY; -- --- Name: module_features; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: medicos; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.medicos ENABLE ROW LEVEL SECURITY; + +-- +-- Name: medicos medicos: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "medicos: owner full access" ON public.medicos USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: module_features; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.module_features ENABLE ROW LEVEL SECURITY; -- --- Name: module_features module_features_read_authenticated; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: module_features module_features_read_authenticated; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY module_features_read_authenticated ON public.module_features FOR SELECT TO authenticated USING (true); -- --- Name: module_features module_features_write_saas_admin; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: module_features module_features_write_saas_admin; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY module_features_write_saas_admin ON public.module_features TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); -- --- Name: modules; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: modules; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.modules ENABLE ROW LEVEL SECURITY; -- --- Name: modules modules_read_authenticated; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: modules modules_read_authenticated; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY modules_read_authenticated ON public.modules FOR SELECT TO authenticated USING (true); -- --- Name: modules modules_write_saas_admin; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: modules modules_write_saas_admin; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY modules_write_saas_admin ON public.modules TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); -- --- Name: notice_dismissals; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: notice_dismissals; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.notice_dismissals ENABLE ROW LEVEL SECURITY; -- --- Name: notice_dismissals notice_dismissals_own; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: notice_dismissals notice_dismissals_own; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY notice_dismissals_own ON public.notice_dismissals TO authenticated USING ((user_id = auth.uid())) WITH CHECK ((user_id = auth.uid())); -- --- Name: notification_logs notif_logs_owner; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: notification_logs notif_logs_owner; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY notif_logs_owner ON public.notification_logs FOR SELECT USING ((owner_id = auth.uid())); -- --- Name: notification_preferences notif_prefs_owner; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: notification_preferences notif_prefs_owner; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY notif_prefs_owner ON public.notification_preferences USING (((owner_id = auth.uid()) AND (deleted_at IS NULL))) WITH CHECK ((owner_id = auth.uid())); -- --- Name: notification_queue notif_queue_owner; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: notification_queue notif_queue_owner; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY notif_queue_owner ON public.notification_queue FOR SELECT USING ((owner_id = auth.uid())); -- --- Name: notification_schedules notif_schedules_owner; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: notification_schedules notif_schedules_owner; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY notif_schedules_owner ON public.notification_schedules USING (((owner_id = auth.uid()) AND (deleted_at IS NULL))) WITH CHECK ((owner_id = auth.uid())); -- --- Name: notification_templates notif_templates_admin_all; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: notification_templates notif_templates_admin_all; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY notif_templates_admin_all ON public.notification_templates TO authenticated USING ((EXISTS ( SELECT 1 @@ -18327,109 +19977,129 @@ CREATE POLICY notif_templates_admin_all ON public.notification_templates TO auth -- --- Name: notification_templates notif_templates_read_global; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: notification_templates notif_templates_read_global; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY notif_templates_read_global ON public.notification_templates FOR SELECT TO authenticated USING (((deleted_at IS NULL) AND (((tenant_id IS NULL) AND (is_default = true)) OR (owner_id = auth.uid()) OR public.is_tenant_member(tenant_id)))); -- --- Name: notification_templates notif_templates_write_owner; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: notification_templates notif_templates_write_owner; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY notif_templates_write_owner ON public.notification_templates TO authenticated USING (((owner_id = auth.uid()) OR public.is_tenant_member(tenant_id))) WITH CHECK (((owner_id = auth.uid()) OR public.is_tenant_member(tenant_id))); -- --- Name: notification_channels; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: notification_channels; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.notification_channels ENABLE ROW LEVEL SECURITY; -- --- Name: notification_channels notification_channels_owner; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: notification_channels notification_channels_owner; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY notification_channels_owner ON public.notification_channels USING (((owner_id = auth.uid()) AND (deleted_at IS NULL))) WITH CHECK ((owner_id = auth.uid())); -- --- Name: notification_logs; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: notification_logs; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.notification_logs ENABLE ROW LEVEL SECURITY; -- --- Name: notification_preferences; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: notification_preferences; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.notification_preferences ENABLE ROW LEVEL SECURITY; -- --- Name: notification_queue; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: notification_queue; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.notification_queue ENABLE ROW LEVEL SECURITY; -- --- Name: notification_schedules; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: notification_schedules; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.notification_schedules ENABLE ROW LEVEL SECURITY; -- --- Name: notification_templates; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: notification_templates; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.notification_templates ENABLE ROW LEVEL SECURITY; -- --- Name: notifications; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: notifications; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.notifications ENABLE ROW LEVEL SECURITY; -- --- Name: notifications owner only; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: notifications owner only; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "owner only" ON public.notifications USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); -- --- Name: owner_users; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: owner_users; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.owner_users ENABLE ROW LEVEL SECURITY; -- --- Name: owner_users owner_users: user can read own links; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: owner_users owner_users: user can read own links; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "owner_users: user can read own links" ON public.owner_users FOR SELECT TO authenticated USING ((user_id = auth.uid())); -- --- Name: patient_discounts; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: patient_contacts; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_contacts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_contacts patient_contacts_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_contacts_select ON public.patient_contacts FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_contacts patient_contacts_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_contacts_write ON public.patient_contacts USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_discounts; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.patient_discounts ENABLE ROW LEVEL SECURITY; -- --- Name: patient_discounts patient_discounts: owner full access; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patient_discounts patient_discounts: owner full access; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "patient_discounts: owner full access" ON public.patient_discounts USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); -- --- Name: patient_group_patient; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: patient_group_patient; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.patient_group_patient ENABLE ROW LEVEL SECURITY; -- --- Name: patient_group_patient patient_group_patient_owner_all; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patient_group_patient patient_group_patient_owner_all; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patient_group_patient_owner_all ON public.patient_group_patient TO authenticated USING ((EXISTS ( SELECT 1 @@ -18440,331 +20110,384 @@ CREATE POLICY patient_group_patient_owner_all ON public.patient_group_patient TO -- --- Name: patient_group_patient patient_group_patient_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patient_group_patient patient_group_patient_select; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patient_group_patient_select ON public.patient_group_patient FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); -- --- Name: patient_group_patient patient_group_patient_write; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patient_group_patient patient_group_patient_write; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patient_group_patient_write ON public.patient_group_patient USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); -- --- Name: patient_groups; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: patient_groups; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.patient_groups ENABLE ROW LEVEL SECURITY; -- --- Name: patient_groups patient_groups_owner_all; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patient_groups patient_groups_owner_all; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patient_groups_owner_all ON public.patient_groups TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); -- --- Name: patient_groups patient_groups_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patient_groups patient_groups_select; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patient_groups_select ON public.patient_groups FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); -- --- Name: patient_groups patient_groups_write; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patient_groups patient_groups_write; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patient_groups_write ON public.patient_groups USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); -- --- Name: patient_intake_requests; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: patient_intake_requests; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.patient_intake_requests ENABLE ROW LEVEL SECURITY; -- --- Name: patient_intake_requests patient_intake_requests_owner_all; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patient_intake_requests patient_intake_requests_owner_all; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patient_intake_requests_owner_all ON public.patient_intake_requests TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); -- --- Name: patient_intake_requests patient_intake_requests_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patient_intake_requests patient_intake_requests_select; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patient_intake_requests_select ON public.patient_intake_requests FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); -- --- Name: patient_intake_requests patient_intake_requests_write; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patient_intake_requests patient_intake_requests_write; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patient_intake_requests_write ON public.patient_intake_requests USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); -- --- Name: patient_invites; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: patient_invites; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.patient_invites ENABLE ROW LEVEL SECURITY; -- --- Name: patient_invites patient_invites_owner_all; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patient_invites patient_invites_owner_all; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patient_invites_owner_all ON public.patient_invites TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); -- --- Name: patient_invites patient_invites_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patient_invites patient_invites_select; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patient_invites_select ON public.patient_invites FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); -- --- Name: patient_invites patient_invites_write; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patient_invites patient_invites_write; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patient_invites_write ON public.patient_invites USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); -- --- Name: patient_patient_tag; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: patient_patient_tag; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.patient_patient_tag ENABLE ROW LEVEL SECURITY; -- --- Name: patient_patient_tag patient_patient_tag_owner_all; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patient_patient_tag patient_patient_tag_owner_all; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patient_patient_tag_owner_all ON public.patient_patient_tag TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); -- --- Name: patient_patient_tag patient_patient_tag_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patient_patient_tag patient_patient_tag_select; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patient_patient_tag_select ON public.patient_patient_tag FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); -- --- Name: patient_patient_tag patient_patient_tag_write; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patient_patient_tag patient_patient_tag_write; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patient_patient_tag_write ON public.patient_patient_tag USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); -- --- Name: patient_tags; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: patient_status_history; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_status_history ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_support_contacts; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_support_contacts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_tags; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.patient_tags ENABLE ROW LEVEL SECURITY; -- --- Name: patient_tags patient_tags_owner_all; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patient_tags patient_tags_owner_all; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patient_tags_owner_all ON public.patient_tags TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); -- --- Name: patient_tags patient_tags_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patient_tags patient_tags_select; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patient_tags_select ON public.patient_tags FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); -- --- Name: patient_tags patient_tags_write; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patient_tags patient_tags_write; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patient_tags_write ON public.patient_tags USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); -- --- Name: patients; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: patient_timeline; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_timeline ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patients; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.patients ENABLE ROW LEVEL SECURITY; -- --- Name: patients patients_delete; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patients patients_delete; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patients_delete ON public.patients FOR DELETE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.delete'::text))); -- --- Name: patients patients_insert; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patients patients_insert; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patients_insert ON public.patients FOR INSERT WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.create'::text))); -- --- Name: patients patients_owner_all; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patients patients_owner_all; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patients_owner_all ON public.patients TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); -- --- Name: patients patients_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patients patients_select; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patients_select ON public.patients FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); -- --- Name: patients patients_update; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patients patients_update; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY patients_update ON public.patients FOR UPDATE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); -- --- Name: payment_settings; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: payment_settings; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.payment_settings ENABLE ROW LEVEL SECURITY; -- --- Name: payment_settings payment_settings: owner full access; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: payment_settings payment_settings: owner full access; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "payment_settings: owner full access" ON public.payment_settings USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); -- --- Name: plan_features; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: plan_features; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.plan_features ENABLE ROW LEVEL SECURITY; -- --- Name: plan_features plan_features_read_authenticated; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: plan_features plan_features_read_authenticated; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY plan_features_read_authenticated ON public.plan_features FOR SELECT TO authenticated USING (true); -- --- Name: plan_features plan_features_write_saas_admin; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: plan_features plan_features_write_saas_admin; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY plan_features_write_saas_admin ON public.plan_features TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); -- --- Name: plans; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: plans; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.plans ENABLE ROW LEVEL SECURITY; -- --- Name: plans plans_read_authenticated; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: plans plans_read_authenticated; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY plans_read_authenticated ON public.plans FOR SELECT TO authenticated USING (true); -- --- Name: plans plans_write_saas_admin; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: plans plans_write_saas_admin; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY plans_write_saas_admin ON public.plans TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); -- --- Name: professional_pricing; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: professional_pricing; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.professional_pricing ENABLE ROW LEVEL SECURITY; -- --- Name: professional_pricing professional_pricing: owner full access; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: professional_pricing professional_pricing: owner full access; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "professional_pricing: owner full access" ON public.professional_pricing USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); -- --- Name: profiles; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: profiles; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY; -- --- Name: profiles profiles_insert_own; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: profiles profiles_insert_own; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY profiles_insert_own ON public.profiles FOR INSERT WITH CHECK ((id = auth.uid())); -- --- Name: profiles profiles_read_saas_admin; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: profiles profiles_read_saas_admin; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY profiles_read_saas_admin ON public.profiles FOR SELECT USING (public.is_saas_admin()); -- --- Name: profiles profiles_select_own; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: profiles profiles_select_own; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY profiles_select_own ON public.profiles FOR SELECT USING ((id = auth.uid())); -- --- Name: profiles profiles_update_own; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: profiles profiles_update_own; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY profiles_update_own ON public.profiles FOR UPDATE USING ((id = auth.uid())) WITH CHECK ((id = auth.uid())); -- --- Name: login_carousel_slides public_read; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: patient_support_contacts psc: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "psc: owner full access" ON public.patient_support_contacts USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_status_history psh_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY psh_insert ON public.patient_status_history FOR INSERT WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_status_history psh_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY psh_select ON public.patient_status_history FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_timeline pt_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY pt_insert ON public.patient_timeline FOR INSERT WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_timeline pt_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY pt_select ON public.patient_timeline FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: login_carousel_slides public_read; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY public_read ON public.login_carousel_slides FOR SELECT USING ((ativo = true)); -- --- Name: features read features (auth); Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: features read features (auth); Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "read features (auth)" ON public.features FOR SELECT TO authenticated USING (true); -- --- Name: plan_features read plan_features (auth); Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: plan_features read plan_features (auth); Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "read plan_features (auth)" ON public.plan_features FOR SELECT TO authenticated USING (true); -- --- Name: plans read plans (auth); Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: plans read plans (auth); Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "read plans (auth)" ON public.plans FOR SELECT TO authenticated USING (true); -- --- Name: recurrence_exceptions; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: recurrence_exceptions; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.recurrence_exceptions ENABLE ROW LEVEL SECURITY; -- --- Name: recurrence_exceptions recurrence_exceptions_tenant; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: recurrence_exceptions recurrence_exceptions_tenant; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY recurrence_exceptions_tenant ON public.recurrence_exceptions TO authenticated USING ((tenant_id IN ( SELECT tenant_members.tenant_id @@ -18775,13 +20498,13 @@ CREATE POLICY recurrence_exceptions_tenant ON public.recurrence_exceptions TO au -- --- Name: recurrence_rule_services; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: recurrence_rule_services; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.recurrence_rule_services ENABLE ROW LEVEL SECURITY; -- --- Name: recurrence_rule_services recurrence_rule_services: clinic read; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: recurrence_rule_services recurrence_rule_services: clinic read; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "recurrence_rule_services: clinic read" ON public.recurrence_rule_services FOR SELECT USING ((EXISTS ( SELECT 1 @@ -18790,7 +20513,7 @@ CREATE POLICY "recurrence_rule_services: clinic read" ON public.recurrence_rule_ -- --- Name: recurrence_rule_services recurrence_rule_services: clinic write; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: recurrence_rule_services recurrence_rule_services: clinic write; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "recurrence_rule_services: clinic write" ON public.recurrence_rule_services USING ((EXISTS ( SELECT 1 @@ -18801,7 +20524,7 @@ CREATE POLICY "recurrence_rule_services: clinic write" ON public.recurrence_rule -- --- Name: recurrence_rule_services recurrence_rule_services: owner full access; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: recurrence_rule_services recurrence_rule_services: owner full access; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "recurrence_rule_services: owner full access" ON public.recurrence_rule_services TO authenticated USING ((EXISTS ( SELECT 1 @@ -18812,34 +20535,34 @@ CREATE POLICY "recurrence_rule_services: owner full access" ON public.recurrence -- --- Name: recurrence_rules; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: recurrence_rules; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.recurrence_rules ENABLE ROW LEVEL SECURITY; -- --- Name: recurrence_rules recurrence_rules_clinic_read; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: recurrence_rules recurrence_rules_clinic_read; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY recurrence_rules_clinic_read ON public.recurrence_rules FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); -- --- Name: recurrence_rules recurrence_rules_clinic_write; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: recurrence_rules recurrence_rules_clinic_write; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY recurrence_rules_clinic_write ON public.recurrence_rules USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); -- --- Name: recurrence_rules recurrence_rules_owner; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: recurrence_rules recurrence_rules_owner; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY recurrence_rules_owner ON public.recurrence_rules TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); -- --- Name: subscription_intents_legacy saas_admin can read subscription_intents; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: subscription_intents_legacy saas_admin can read subscription_intents; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "saas_admin can read subscription_intents" ON public.subscription_intents_legacy FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 @@ -18848,7 +20571,7 @@ CREATE POLICY "saas_admin can read subscription_intents" ON public.subscription_ -- --- Name: subscription_intents_legacy saas_admin can update subscription_intents; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: subscription_intents_legacy saas_admin can update subscription_intents; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "saas_admin can update subscription_intents" ON public.subscription_intents_legacy FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 @@ -18859,7 +20582,7 @@ CREATE POLICY "saas_admin can update subscription_intents" ON public.subscriptio -- --- Name: login_carousel_slides saas_admin_full; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: login_carousel_slides saas_admin_full; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY saas_admin_full ON public.login_carousel_slides USING ((EXISTS ( SELECT 1 @@ -18868,7 +20591,7 @@ CREATE POLICY saas_admin_full ON public.login_carousel_slides USING ((EXISTS ( S -- --- Name: saas_docs saas_admin_full_access; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: saas_docs saas_admin_full_access; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY saas_admin_full_access ON public.saas_docs TO authenticated USING ((EXISTS ( SELECT 1 @@ -18879,117 +20602,124 @@ CREATE POLICY saas_admin_full_access ON public.saas_docs TO authenticated USING -- --- Name: saas_admins; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: saas_admins; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.saas_admins ENABLE ROW LEVEL SECURITY; -- --- Name: saas_admins saas_admins_select_self; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: saas_admins saas_admins_select_self; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY saas_admins_select_self ON public.saas_admins FOR SELECT TO authenticated USING ((user_id = auth.uid())); -- --- Name: saas_doc_votos; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: saas_doc_votos; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.saas_doc_votos ENABLE ROW LEVEL SECURITY; -- --- Name: saas_docs; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: saas_docs; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.saas_docs ENABLE ROW LEVEL SECURITY; -- --- Name: saas_faq; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: saas_faq; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.saas_faq ENABLE ROW LEVEL SECURITY; -- --- Name: saas_faq_itens; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: saas_faq_itens; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.saas_faq_itens ENABLE ROW LEVEL SECURITY; -- --- Name: agenda_bloqueios select own; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: agenda_bloqueios select own; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "select own" ON public.agenda_bloqueios FOR SELECT USING ((owner_id = auth.uid())); -- --- Name: services; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: twilio_subaccount_usage service_role_manage_usage; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY service_role_manage_usage ON public.twilio_subaccount_usage USING ((auth.role() = 'service_role'::text)); + + +-- +-- Name: services; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.services ENABLE ROW LEVEL SECURITY; -- --- Name: services services: owner full access; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: services services: owner full access; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "services: owner full access" ON public.services USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); -- --- Name: subscription_events; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: subscription_events; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.subscription_events ENABLE ROW LEVEL SECURITY; -- --- Name: subscription_events subscription_events_read_saas; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: subscription_events subscription_events_read_saas; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY subscription_events_read_saas ON public.subscription_events FOR SELECT USING (public.is_saas_admin()); -- --- Name: subscription_events subscription_events_write_saas; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: subscription_events subscription_events_write_saas; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY subscription_events_write_saas ON public.subscription_events FOR INSERT WITH CHECK (public.is_saas_admin()); -- --- Name: subscription_intents_legacy subscription_intents_insert_own; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: subscription_intents_legacy subscription_intents_insert_own; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY subscription_intents_insert_own ON public.subscription_intents_legacy FOR INSERT TO authenticated WITH CHECK ((user_id = auth.uid())); -- --- Name: subscription_intents_legacy; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: subscription_intents_legacy; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.subscription_intents_legacy ENABLE ROW LEVEL SECURITY; -- --- Name: subscription_intents_legacy subscription_intents_select_own; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: subscription_intents_legacy subscription_intents_select_own; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY subscription_intents_select_own ON public.subscription_intents_legacy FOR SELECT TO authenticated USING ((user_id = auth.uid())); -- --- Name: subscriptions; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: subscriptions; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.subscriptions ENABLE ROW LEVEL SECURITY; -- --- Name: subscriptions subscriptions read own; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: subscriptions subscriptions read own; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "subscriptions read own" ON public.subscriptions FOR SELECT TO authenticated USING ((user_id = auth.uid())); -- --- Name: subscriptions subscriptions: read if linked owner_users; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: subscriptions subscriptions: read if linked owner_users; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "subscriptions: read if linked owner_users" ON public.subscriptions FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 @@ -18998,28 +20728,28 @@ CREATE POLICY "subscriptions: read if linked owner_users" ON public.subscription -- --- Name: subscriptions subscriptions_insert_own_personal; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: subscriptions subscriptions_insert_own_personal; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY subscriptions_insert_own_personal ON public.subscriptions FOR INSERT TO authenticated WITH CHECK (((user_id = auth.uid()) AND (tenant_id IS NULL))); -- --- Name: subscriptions subscriptions_no_direct_update; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: subscriptions subscriptions_no_direct_update; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY subscriptions_no_direct_update ON public.subscriptions FOR UPDATE TO authenticated USING (false) WITH CHECK (false); -- --- Name: subscriptions subscriptions_read_own; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: subscriptions subscriptions_read_own; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY subscriptions_read_own ON public.subscriptions FOR SELECT TO authenticated USING (((user_id = auth.uid()) OR public.is_saas_admin())); -- --- Name: subscriptions subscriptions_select_for_tenant_members; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: subscriptions subscriptions_select_for_tenant_members; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY subscriptions_select_for_tenant_members ON public.subscriptions FOR SELECT TO authenticated USING (((tenant_id IS NOT NULL) AND (EXISTS ( SELECT 1 @@ -19028,27 +20758,27 @@ CREATE POLICY subscriptions_select_for_tenant_members ON public.subscriptions FO -- --- Name: subscriptions subscriptions_select_own_personal; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: subscriptions subscriptions_select_own_personal; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY subscriptions_select_own_personal ON public.subscriptions FOR SELECT TO authenticated USING (((user_id = auth.uid()) AND (tenant_id IS NULL))); -- --- Name: subscriptions subscriptions_update_only_saas_admin; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: subscriptions subscriptions_update_only_saas_admin; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY subscriptions_update_only_saas_admin ON public.subscriptions FOR UPDATE TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); -- --- Name: support_sessions; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: support_sessions; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.support_sessions ENABLE ROW LEVEL SECURITY; -- --- Name: support_sessions support_sessions_saas_delete; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: support_sessions support_sessions_saas_delete; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY support_sessions_saas_delete ON public.support_sessions FOR DELETE USING (((auth.uid() = admin_id) AND (EXISTS ( SELECT 1 @@ -19057,7 +20787,7 @@ CREATE POLICY support_sessions_saas_delete ON public.support_sessions FOR DELETE -- --- Name: support_sessions support_sessions_saas_insert; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: support_sessions support_sessions_saas_insert; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY support_sessions_saas_insert ON public.support_sessions FOR INSERT WITH CHECK (((auth.uid() = admin_id) AND (EXISTS ( SELECT 1 @@ -19066,7 +20796,7 @@ CREATE POLICY support_sessions_saas_insert ON public.support_sessions FOR INSERT -- --- Name: support_sessions support_sessions_saas_select; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: support_sessions support_sessions_saas_select; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY support_sessions_saas_select ON public.support_sessions FOR SELECT USING (((auth.uid() = admin_id) AND (EXISTS ( SELECT 1 @@ -19075,60 +20805,69 @@ CREATE POLICY support_sessions_saas_select ON public.support_sessions FOR SELECT -- --- Name: email_templates_tenant tenant manages own overrides; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: email_templates_tenant tenant manages own overrides; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "tenant manages own overrides" ON public.email_templates_tenant USING ((tenant_id = auth.uid())) WITH CHECK ((tenant_id = auth.uid())); -- --- Name: email_layout_config tenant owns email layout config; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: email_layout_config tenant owns email layout config; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "tenant owns email layout config" ON public.email_layout_config USING ((tenant_id = auth.uid())) WITH CHECK ((tenant_id = auth.uid())); -- --- Name: tenant_members; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: tenant_members; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.tenant_members ENABLE ROW LEVEL SECURITY; -- --- Name: tenant_members tenant_members_write_saas; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: tenant_members tenant_members_write_saas; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY tenant_members_write_saas ON public.tenant_members TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); -- --- Name: tenant_modules; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: tenant_modules; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.tenant_modules ENABLE ROW LEVEL SECURITY; -- --- Name: tenant_modules tenant_modules_read_own; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: tenant_modules tenant_modules_read_own; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY tenant_modules_read_own ON public.tenant_modules FOR SELECT TO authenticated USING (((owner_id = auth.uid()) OR public.is_saas_admin())); -- --- Name: tenant_modules tenant_modules_write_saas; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: tenant_modules tenant_modules_write_saas; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY tenant_modules_write_saas ON public.tenant_modules TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); -- --- Name: tenants; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: twilio_subaccount_usage tenant_select_own_usage; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenant_select_own_usage ON public.twilio_subaccount_usage FOR SELECT USING ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE (tenant_members.user_id = auth.uid())))); + + +-- +-- Name: tenants; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.tenants ENABLE ROW LEVEL SECURITY; -- --- Name: tenants tenants_read_members; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: tenants tenants_read_members; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY tenants_read_members ON public.tenants FOR SELECT TO authenticated USING ((public.is_saas_admin() OR (EXISTS ( SELECT 1 @@ -19137,20 +20876,20 @@ CREATE POLICY tenants_read_members ON public.tenants FOR SELECT TO authenticated -- --- Name: tenants tenants_write_saas; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: tenants tenants_write_saas; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY tenants_write_saas ON public.tenants TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); -- --- Name: therapist_payout_records; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: therapist_payout_records; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.therapist_payout_records ENABLE ROW LEVEL SECURITY; -- --- Name: therapist_payout_records therapist_payout_records_self; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: therapist_payout_records therapist_payout_records_self; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY therapist_payout_records_self ON public.therapist_payout_records USING ((EXISTS ( SELECT 1 @@ -19161,7 +20900,7 @@ CREATE POLICY therapist_payout_records_self ON public.therapist_payout_records U -- --- Name: therapist_payout_records therapist_payout_records_tenant_admin; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: therapist_payout_records therapist_payout_records_tenant_admin; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY therapist_payout_records_tenant_admin ON public.therapist_payout_records FOR SELECT USING ((EXISTS ( SELECT 1 @@ -19170,303 +20909,372 @@ CREATE POLICY therapist_payout_records_tenant_admin ON public.therapist_payout_r -- --- Name: therapist_payouts; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: therapist_payouts; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.therapist_payouts ENABLE ROW LEVEL SECURITY; -- --- Name: therapist_payouts therapist_payouts_self; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: therapist_payouts therapist_payouts_self; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY therapist_payouts_self ON public.therapist_payouts USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); -- --- Name: therapist_payouts therapist_payouts_tenant_admin; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: therapist_payouts therapist_payouts_tenant_admin; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY therapist_payouts_tenant_admin ON public.therapist_payouts FOR SELECT USING (((tenant_id IS NOT NULL) AND public.is_tenant_admin(tenant_id))); -- --- Name: tenant_members tm_select_admin_all_members; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: tenant_members tm_select_admin_all_members; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY tm_select_admin_all_members ON public.tenant_members FOR SELECT TO authenticated USING (public.is_tenant_admin(tenant_id)); -- --- Name: tenant_members tm_select_own_membership; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: tenant_members tm_select_own_membership; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY tm_select_own_membership ON public.tenant_members FOR SELECT TO authenticated USING ((user_id = auth.uid())); -- --- Name: agenda_bloqueios update own; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: twilio_subaccount_usage; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.twilio_subaccount_usage ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_bloqueios update own; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY "update own" ON public.agenda_bloqueios FOR UPDATE USING ((owner_id = auth.uid())); -- --- Name: user_settings; Type: ROW SECURITY; Schema: public; Owner: supabase_admin +-- Name: user_settings; Type: ROW SECURITY; Schema: public; Owner: - -- ALTER TABLE public.user_settings ENABLE ROW LEVEL SECURITY; -- --- Name: user_settings user_settings_insert_own; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: user_settings user_settings_insert_own; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY user_settings_insert_own ON public.user_settings FOR INSERT WITH CHECK ((user_id = auth.uid())); -- --- Name: user_settings user_settings_select_own; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: user_settings user_settings_select_own; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY user_settings_select_own ON public.user_settings FOR SELECT USING ((user_id = auth.uid())); -- --- Name: user_settings user_settings_update_own; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: user_settings user_settings_update_own; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY user_settings_update_own ON public.user_settings FOR UPDATE USING ((user_id = auth.uid())) WITH CHECK ((user_id = auth.uid())); -- --- Name: saas_docs users_read_usuario_docs; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: saas_docs users_read_usuario_docs; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY users_read_usuario_docs ON public.saas_docs FOR SELECT TO authenticated USING (((ativo = true) AND (tipo_acesso = 'usuario'::text))); -- --- Name: saas_doc_votos votos_select_own; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: saas_doc_votos votos_select_own; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY votos_select_own ON public.saas_doc_votos FOR SELECT TO authenticated USING ((user_id = auth.uid())); -- --- Name: saas_doc_votos votos_upsert_own; Type: POLICY; Schema: public; Owner: supabase_admin +-- Name: saas_doc_votos votos_upsert_own; Type: POLICY; Schema: public; Owner: - -- CREATE POLICY votos_upsert_own ON public.saas_doc_votos TO authenticated USING ((user_id = auth.uid())) WITH CHECK ((user_id = auth.uid())); -- --- Name: messages; Type: ROW SECURITY; Schema: realtime; Owner: supabase_realtime_admin +-- Name: messages; Type: ROW SECURITY; Schema: realtime; Owner: - -- ALTER TABLE realtime.messages ENABLE ROW LEVEL SECURITY; -- --- Name: objects agendador_storage_owner_delete; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects Allow authenticated updates; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "Allow authenticated updates" ON storage.objects FOR UPDATE TO authenticated USING ((bucket_id = ANY (ARRAY['avatars'::text, 'logos'::text]))); + + +-- +-- Name: objects Allow authenticated uploads; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "Allow authenticated uploads" ON storage.objects FOR INSERT TO authenticated WITH CHECK ((bucket_id = ANY (ARRAY['avatars'::text, 'logos'::text]))); + + +-- +-- Name: objects Public read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "Public read" ON storage.objects FOR SELECT USING ((bucket_id = ANY (ARRAY['avatars'::text, 'logos'::text]))); + + +-- +-- Name: objects agendador_storage_owner_delete; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY agendador_storage_owner_delete ON storage.objects FOR DELETE TO authenticated USING (((bucket_id = 'agendador'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); -- --- Name: objects agendador_storage_owner_insert; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects agendador_storage_owner_insert; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY agendador_storage_owner_insert ON storage.objects FOR INSERT TO authenticated WITH CHECK (((bucket_id = 'agendador'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); -- --- Name: objects agendador_storage_owner_update; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects agendador_storage_owner_update; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY agendador_storage_owner_update ON storage.objects FOR UPDATE TO authenticated USING (((bucket_id = 'agendador'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); -- --- Name: objects agendador_storage_public_read; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects agendador_storage_public_read; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY agendador_storage_public_read ON storage.objects FOR SELECT USING ((bucket_id = 'agendador'::text)); -- --- Name: objects avatars authenticated upload; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects avatars authenticated upload; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY "avatars authenticated upload" ON storage.objects FOR INSERT WITH CHECK (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); -- --- Name: objects avatars owner delete; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects avatars owner delete; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY "avatars owner delete" ON storage.objects FOR DELETE USING (((bucket_id = 'avatars'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); -- --- Name: objects avatars owner update; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects avatars owner update; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY "avatars owner update" ON storage.objects FOR UPDATE USING (((bucket_id = 'avatars'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); -- --- Name: objects avatars public read; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects avatars public read; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY "avatars public read" ON storage.objects FOR SELECT USING ((bucket_id = 'avatars'::text)); -- --- Name: objects avatars_delete_own; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects avatars_delete_own; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY avatars_delete_own ON storage.objects FOR DELETE TO authenticated USING (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); -- --- Name: objects avatars_delete_own_folder; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects avatars_delete_own_folder; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY avatars_delete_own_folder ON storage.objects FOR DELETE USING (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))); -- --- Name: objects avatars_insert_own; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects avatars_insert_own; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY avatars_insert_own ON storage.objects FOR INSERT TO authenticated WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); -- --- Name: objects avatars_insert_own_folder; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects avatars_insert_own_folder; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY avatars_insert_own_folder ON storage.objects FOR INSERT WITH CHECK (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))); -- --- Name: objects avatars_read; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects avatars_read; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY avatars_read ON storage.objects FOR SELECT USING ((bucket_id = 'avatars'::text)); -- --- Name: objects avatars_select_own; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects avatars_select_own; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY avatars_select_own ON storage.objects FOR SELECT TO authenticated USING (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); -- --- Name: objects avatars_update_own; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects avatars_update_own; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY avatars_update_own ON storage.objects FOR UPDATE TO authenticated USING (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))) WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); -- --- Name: objects avatars_update_own_folder; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects avatars_update_own_folder; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY avatars_update_own_folder ON storage.objects FOR UPDATE USING (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))) WITH CHECK (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))); -- --- Name: buckets; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin +-- Name: buckets; Type: ROW SECURITY; Schema: storage; Owner: - -- ALTER TABLE storage.buckets ENABLE ROW LEVEL SECURITY; -- --- Name: buckets_analytics; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin +-- Name: buckets_analytics; Type: ROW SECURITY; Schema: storage; Owner: - -- ALTER TABLE storage.buckets_analytics ENABLE ROW LEVEL SECURITY; -- --- Name: buckets_vectors; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin +-- Name: buckets_vectors; Type: ROW SECURITY; Schema: storage; Owner: - -- ALTER TABLE storage.buckets_vectors ENABLE ROW LEVEL SECURITY; -- --- Name: iceberg_namespaces; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects documents: authenticated delete; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "documents: authenticated delete" ON storage.objects FOR DELETE TO authenticated USING ((bucket_id = 'documents'::text)); + + +-- +-- Name: objects documents: authenticated read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "documents: authenticated read" ON storage.objects FOR SELECT TO authenticated USING ((bucket_id = 'documents'::text)); + + +-- +-- Name: objects documents: authenticated upload; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "documents: authenticated upload" ON storage.objects FOR INSERT TO authenticated WITH CHECK ((bucket_id = 'documents'::text)); + + +-- +-- Name: objects generated-docs: authenticated delete; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "generated-docs: authenticated delete" ON storage.objects FOR DELETE TO authenticated USING ((bucket_id = 'generated-docs'::text)); + + +-- +-- Name: objects generated-docs: authenticated read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "generated-docs: authenticated read" ON storage.objects FOR SELECT TO authenticated USING ((bucket_id = 'generated-docs'::text)); + + +-- +-- Name: objects generated-docs: authenticated upload; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "generated-docs: authenticated upload" ON storage.objects FOR INSERT TO authenticated WITH CHECK ((bucket_id = 'generated-docs'::text)); + + +-- +-- Name: iceberg_namespaces; Type: ROW SECURITY; Schema: storage; Owner: - -- ALTER TABLE storage.iceberg_namespaces ENABLE ROW LEVEL SECURITY; -- --- Name: iceberg_tables; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin +-- Name: iceberg_tables; Type: ROW SECURITY; Schema: storage; Owner: - -- ALTER TABLE storage.iceberg_tables ENABLE ROW LEVEL SECURITY; -- --- Name: objects intake_read_anon; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects intake_read_anon; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY intake_read_anon ON storage.objects FOR SELECT TO anon USING (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); -- --- Name: objects intake_read_public; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects intake_read_public; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY intake_read_public ON storage.objects FOR SELECT USING (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); -- --- Name: objects intake_upload_anon; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects intake_upload_anon; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY intake_upload_anon ON storage.objects FOR INSERT TO anon WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); -- --- Name: objects intake_upload_public; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects intake_upload_public; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY intake_upload_public ON storage.objects FOR INSERT WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); -- --- Name: migrations; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin +-- Name: migrations; Type: ROW SECURITY; Schema: storage; Owner: - -- ALTER TABLE storage.migrations ENABLE ROW LEVEL SECURITY; -- --- Name: objects; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects; Type: ROW SECURITY; Schema: storage; Owner: - -- ALTER TABLE storage.objects ENABLE ROW LEVEL SECURITY; -- --- Name: objects public_read; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects public_read; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY public_read ON storage.objects FOR SELECT USING ((bucket_id = 'saas-docs'::text)); -- --- Name: s3_multipart_uploads; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin +-- Name: s3_multipart_uploads; Type: ROW SECURITY; Schema: storage; Owner: - -- ALTER TABLE storage.s3_multipart_uploads ENABLE ROW LEVEL SECURITY; -- --- Name: s3_multipart_uploads_parts; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin +-- Name: s3_multipart_uploads_parts; Type: ROW SECURITY; Schema: storage; Owner: - -- ALTER TABLE storage.s3_multipart_uploads_parts ENABLE ROW LEVEL SECURITY; -- --- Name: objects saas_admin_delete; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects saas_admin_delete; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY saas_admin_delete ON storage.objects FOR DELETE TO authenticated USING (((bucket_id = 'saas-docs'::text) AND (EXISTS ( SELECT 1 @@ -19475,7 +21283,7 @@ CREATE POLICY saas_admin_delete ON storage.objects FOR DELETE TO authenticated U -- --- Name: objects saas_admin_upload; Type: POLICY; Schema: storage; Owner: supabase_storage_admin +-- Name: objects saas_admin_upload; Type: POLICY; Schema: storage; Owner: - -- CREATE POLICY saas_admin_upload ON storage.objects FOR INSERT TO authenticated WITH CHECK (((bucket_id = 'saas-docs'::text) AND (EXISTS ( SELECT 1 @@ -19484,6509 +21292,41 @@ CREATE POLICY saas_admin_upload ON storage.objects FOR INSERT TO authenticated W -- --- Name: vector_indexes; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin +-- Name: vector_indexes; Type: ROW SECURITY; Schema: storage; Owner: - -- ALTER TABLE storage.vector_indexes ENABLE ROW LEVEL SECURITY; -- --- Name: supabase_realtime; Type: PUBLICATION; Schema: -; Owner: postgres +-- Name: supabase_realtime; Type: PUBLICATION; Schema: -; Owner: - -- CREATE PUBLICATION supabase_realtime WITH (publish = 'insert, update, delete, truncate'); -ALTER PUBLICATION supabase_realtime OWNER TO postgres; - -- --- Name: supabase_realtime_messages_publication; Type: PUBLICATION; Schema: -; Owner: supabase_admin +-- Name: supabase_realtime_messages_publication; Type: PUBLICATION; Schema: -; Owner: - -- CREATE PUBLICATION supabase_realtime_messages_publication WITH (publish = 'insert, update, delete, truncate'); -ALTER PUBLICATION supabase_realtime_messages_publication OWNER TO supabase_admin; - -- --- Name: supabase_realtime notifications; Type: PUBLICATION TABLE; Schema: public; Owner: postgres +-- Name: supabase_realtime notifications; Type: PUBLICATION TABLE; Schema: public; Owner: - -- ALTER PUBLICATION supabase_realtime ADD TABLE ONLY public.notifications; -- --- Name: supabase_realtime_messages_publication messages; Type: PUBLICATION TABLE; Schema: realtime; Owner: supabase_admin +-- Name: supabase_realtime_messages_publication messages; Type: PUBLICATION TABLE; Schema: realtime; Owner: - -- ALTER PUBLICATION supabase_realtime_messages_publication ADD TABLE ONLY realtime.messages; -- --- Name: SCHEMA auth; Type: ACL; Schema: -; Owner: supabase_admin --- - -GRANT USAGE ON SCHEMA auth TO anon; -GRANT USAGE ON SCHEMA auth TO authenticated; -GRANT USAGE ON SCHEMA auth TO service_role; -GRANT ALL ON SCHEMA auth TO supabase_auth_admin; -GRANT ALL ON SCHEMA auth TO dashboard_user; -GRANT USAGE ON SCHEMA auth TO postgres; - - --- --- Name: SCHEMA cron; Type: ACL; Schema: -; Owner: supabase_admin --- - -GRANT USAGE ON SCHEMA cron TO postgres WITH GRANT OPTION; - - --- --- Name: SCHEMA extensions; Type: ACL; Schema: -; Owner: postgres --- - -GRANT USAGE ON SCHEMA extensions TO anon; -GRANT USAGE ON SCHEMA extensions TO authenticated; -GRANT USAGE ON SCHEMA extensions TO service_role; -GRANT ALL ON SCHEMA extensions TO dashboard_user; - - --- --- Name: SCHEMA net; Type: ACL; Schema: -; Owner: supabase_admin --- - -GRANT USAGE ON SCHEMA net TO supabase_functions_admin; -GRANT USAGE ON SCHEMA net TO postgres; -GRANT USAGE ON SCHEMA net TO anon; -GRANT USAGE ON SCHEMA net TO authenticated; -GRANT USAGE ON SCHEMA net TO service_role; - - --- --- Name: SCHEMA public; Type: ACL; Schema: -; Owner: pg_database_owner --- - -GRANT USAGE ON SCHEMA public TO postgres; -GRANT USAGE ON SCHEMA public TO anon; -GRANT USAGE ON SCHEMA public TO authenticated; -GRANT USAGE ON SCHEMA public TO service_role; - - --- --- Name: SCHEMA realtime; Type: ACL; Schema: -; Owner: supabase_admin --- - -GRANT USAGE ON SCHEMA realtime TO postgres; -GRANT USAGE ON SCHEMA realtime TO anon; -GRANT USAGE ON SCHEMA realtime TO authenticated; -GRANT USAGE ON SCHEMA realtime TO service_role; -GRANT ALL ON SCHEMA realtime TO supabase_realtime_admin; - - --- --- Name: SCHEMA storage; Type: ACL; Schema: -; Owner: supabase_admin --- - -GRANT USAGE ON SCHEMA storage TO postgres WITH GRANT OPTION; -GRANT USAGE ON SCHEMA storage TO anon; -GRANT USAGE ON SCHEMA storage TO authenticated; -GRANT USAGE ON SCHEMA storage TO service_role; -GRANT ALL ON SCHEMA storage TO supabase_storage_admin WITH GRANT OPTION; -GRANT ALL ON SCHEMA storage TO dashboard_user; - - --- --- Name: SCHEMA supabase_functions; Type: ACL; Schema: -; Owner: supabase_admin --- - -GRANT USAGE ON SCHEMA supabase_functions TO postgres; -GRANT USAGE ON SCHEMA supabase_functions TO anon; -GRANT USAGE ON SCHEMA supabase_functions TO authenticated; -GRANT USAGE ON SCHEMA supabase_functions TO service_role; -GRANT ALL ON SCHEMA supabase_functions TO supabase_functions_admin; - - --- --- Name: SCHEMA vault; Type: ACL; Schema: -; Owner: supabase_admin --- - -GRANT USAGE ON SCHEMA vault TO postgres WITH GRANT OPTION; -GRANT USAGE ON SCHEMA vault TO service_role; - - --- --- Name: FUNCTION citextin(cstring); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citextin(cstring) TO postgres; -GRANT ALL ON FUNCTION public.citextin(cstring) TO anon; -GRANT ALL ON FUNCTION public.citextin(cstring) TO authenticated; -GRANT ALL ON FUNCTION public.citextin(cstring) TO service_role; - - --- --- Name: FUNCTION citextout(public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citextout(public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citextout(public.citext) TO anon; -GRANT ALL ON FUNCTION public.citextout(public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citextout(public.citext) TO service_role; - - --- --- Name: FUNCTION citextrecv(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citextrecv(internal) TO postgres; -GRANT ALL ON FUNCTION public.citextrecv(internal) TO anon; -GRANT ALL ON FUNCTION public.citextrecv(internal) TO authenticated; -GRANT ALL ON FUNCTION public.citextrecv(internal) TO service_role; - - --- --- Name: FUNCTION citextsend(public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citextsend(public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citextsend(public.citext) TO anon; -GRANT ALL ON FUNCTION public.citextsend(public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citextsend(public.citext) TO service_role; - - --- --- Name: FUNCTION gbtreekey16_in(cstring); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey16_in(cstring) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey16_in(cstring) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey16_in(cstring) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey16_in(cstring) TO service_role; - - --- --- Name: FUNCTION gbtreekey16_out(public.gbtreekey16); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey16_out(public.gbtreekey16) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey16_out(public.gbtreekey16) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey16_out(public.gbtreekey16) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey16_out(public.gbtreekey16) TO service_role; - - --- --- Name: FUNCTION gbtreekey2_in(cstring); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey2_in(cstring) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey2_in(cstring) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey2_in(cstring) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey2_in(cstring) TO service_role; - - --- --- Name: FUNCTION gbtreekey2_out(public.gbtreekey2); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey2_out(public.gbtreekey2) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey2_out(public.gbtreekey2) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey2_out(public.gbtreekey2) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey2_out(public.gbtreekey2) TO service_role; - - --- --- Name: FUNCTION gbtreekey32_in(cstring); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey32_in(cstring) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey32_in(cstring) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey32_in(cstring) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey32_in(cstring) TO service_role; - - --- --- Name: FUNCTION gbtreekey32_out(public.gbtreekey32); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey32_out(public.gbtreekey32) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey32_out(public.gbtreekey32) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey32_out(public.gbtreekey32) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey32_out(public.gbtreekey32) TO service_role; - - --- --- Name: FUNCTION gbtreekey4_in(cstring); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey4_in(cstring) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey4_in(cstring) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey4_in(cstring) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey4_in(cstring) TO service_role; - - --- --- Name: FUNCTION gbtreekey4_out(public.gbtreekey4); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey4_out(public.gbtreekey4) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey4_out(public.gbtreekey4) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey4_out(public.gbtreekey4) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey4_out(public.gbtreekey4) TO service_role; - - --- --- Name: FUNCTION gbtreekey8_in(cstring); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey8_in(cstring) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey8_in(cstring) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey8_in(cstring) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey8_in(cstring) TO service_role; - - --- --- Name: FUNCTION gbtreekey8_out(public.gbtreekey8); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey8_out(public.gbtreekey8) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey8_out(public.gbtreekey8) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey8_out(public.gbtreekey8) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey8_out(public.gbtreekey8) TO service_role; - - --- --- Name: FUNCTION gbtreekey_var_in(cstring); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey_var_in(cstring) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey_var_in(cstring) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey_var_in(cstring) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey_var_in(cstring) TO service_role; - - --- --- Name: FUNCTION gbtreekey_var_out(public.gbtreekey_var); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey_var_out(public.gbtreekey_var) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey_var_out(public.gbtreekey_var) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey_var_out(public.gbtreekey_var) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey_var_out(public.gbtreekey_var) TO service_role; - - --- --- Name: FUNCTION gtrgm_in(cstring); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gtrgm_in(cstring) TO postgres; -GRANT ALL ON FUNCTION public.gtrgm_in(cstring) TO anon; -GRANT ALL ON FUNCTION public.gtrgm_in(cstring) TO authenticated; -GRANT ALL ON FUNCTION public.gtrgm_in(cstring) TO service_role; - - --- --- Name: FUNCTION gtrgm_out(public.gtrgm); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gtrgm_out(public.gtrgm) TO postgres; -GRANT ALL ON FUNCTION public.gtrgm_out(public.gtrgm) TO anon; -GRANT ALL ON FUNCTION public.gtrgm_out(public.gtrgm) TO authenticated; -GRANT ALL ON FUNCTION public.gtrgm_out(public.gtrgm) TO service_role; - - --- --- Name: FUNCTION citext(boolean); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext(boolean) TO postgres; -GRANT ALL ON FUNCTION public.citext(boolean) TO anon; -GRANT ALL ON FUNCTION public.citext(boolean) TO authenticated; -GRANT ALL ON FUNCTION public.citext(boolean) TO service_role; - - --- --- Name: FUNCTION citext(character); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext(character) TO postgres; -GRANT ALL ON FUNCTION public.citext(character) TO anon; -GRANT ALL ON FUNCTION public.citext(character) TO authenticated; -GRANT ALL ON FUNCTION public.citext(character) TO service_role; - - --- --- Name: FUNCTION citext(inet); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext(inet) TO postgres; -GRANT ALL ON FUNCTION public.citext(inet) TO anon; -GRANT ALL ON FUNCTION public.citext(inet) TO authenticated; -GRANT ALL ON FUNCTION public.citext(inet) TO service_role; - - --- --- Name: FUNCTION email(); Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON FUNCTION auth.email() TO dashboard_user; - - --- --- Name: FUNCTION jwt(); Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON FUNCTION auth.jwt() TO postgres; -GRANT ALL ON FUNCTION auth.jwt() TO dashboard_user; - - --- --- Name: FUNCTION role(); Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON FUNCTION auth.role() TO dashboard_user; - - --- --- Name: FUNCTION uid(); Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON FUNCTION auth.uid() TO dashboard_user; - - --- --- Name: FUNCTION alter_job(job_id bigint, schedule text, command text, database text, username text, active boolean); Type: ACL; Schema: cron; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION cron.alter_job(job_id bigint, schedule text, command text, database text, username text, active boolean) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION job_cache_invalidate(); Type: ACL; Schema: cron; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION cron.job_cache_invalidate() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION schedule(schedule text, command text); Type: ACL; Schema: cron; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION cron.schedule(schedule text, command text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION schedule(job_name text, schedule text, command text); Type: ACL; Schema: cron; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION cron.schedule(job_name text, schedule text, command text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION schedule_in_database(job_name text, schedule text, command text, database text, username text, active boolean); Type: ACL; Schema: cron; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION cron.schedule_in_database(job_name text, schedule text, command text, database text, username text, active boolean) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION unschedule(job_id bigint); Type: ACL; Schema: cron; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION cron.unschedule(job_id bigint) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION unschedule(job_name text); Type: ACL; Schema: cron; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION cron.unschedule(job_name text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION armor(bytea); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.armor(bytea) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.armor(bytea) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION armor(bytea, text[], text[]); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.armor(bytea, text[], text[]) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.armor(bytea, text[], text[]) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION crypt(text, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.crypt(text, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.crypt(text, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION dearmor(text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.dearmor(text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.dearmor(text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION decrypt(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.decrypt(bytea, bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.decrypt(bytea, bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION decrypt_iv(bytea, bytea, bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.decrypt_iv(bytea, bytea, bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.decrypt_iv(bytea, bytea, bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION digest(bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.digest(bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.digest(bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION digest(text, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.digest(text, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.digest(text, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION encrypt(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.encrypt(bytea, bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.encrypt(bytea, bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION encrypt_iv(bytea, bytea, bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.encrypt_iv(bytea, bytea, bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.encrypt_iv(bytea, bytea, bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION gen_random_bytes(integer); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.gen_random_bytes(integer) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.gen_random_bytes(integer) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION gen_random_uuid(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.gen_random_uuid() TO dashboard_user; -GRANT ALL ON FUNCTION extensions.gen_random_uuid() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION gen_salt(text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.gen_salt(text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.gen_salt(text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION gen_salt(text, integer); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.gen_salt(text, integer) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.gen_salt(text, integer) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION grant_pg_cron_access(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION extensions.grant_pg_cron_access() FROM supabase_admin; -GRANT ALL ON FUNCTION extensions.grant_pg_cron_access() TO supabase_admin WITH GRANT OPTION; -GRANT ALL ON FUNCTION extensions.grant_pg_cron_access() TO dashboard_user; - - --- --- Name: FUNCTION grant_pg_graphql_access(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.grant_pg_graphql_access() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION grant_pg_net_access(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION extensions.grant_pg_net_access() FROM supabase_admin; -GRANT ALL ON FUNCTION extensions.grant_pg_net_access() TO supabase_admin WITH GRANT OPTION; -GRANT ALL ON FUNCTION extensions.grant_pg_net_access() TO dashboard_user; - - --- --- Name: FUNCTION hmac(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.hmac(bytea, bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.hmac(bytea, bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION hmac(text, text, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.hmac(text, text, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.hmac(text, text, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pg_stat_statements(showtext boolean, OUT userid oid, OUT dbid oid, OUT toplevel boolean, OUT queryid bigint, OUT query text, OUT plans bigint, OUT total_plan_time double precision, OUT min_plan_time double precision, OUT max_plan_time double precision, OUT mean_plan_time double precision, OUT stddev_plan_time double precision, OUT calls bigint, OUT total_exec_time double precision, OUT min_exec_time double precision, OUT max_exec_time double precision, OUT mean_exec_time double precision, OUT stddev_exec_time double precision, OUT rows bigint, OUT shared_blks_hit bigint, OUT shared_blks_read bigint, OUT shared_blks_dirtied bigint, OUT shared_blks_written bigint, OUT local_blks_hit bigint, OUT local_blks_read bigint, OUT local_blks_dirtied bigint, OUT local_blks_written bigint, OUT temp_blks_read bigint, OUT temp_blks_written bigint, OUT shared_blk_read_time double precision, OUT shared_blk_write_time double precision, OUT local_blk_read_time double precision, OUT local_blk_write_time double precision, OUT temp_blk_read_time double precision, OUT temp_blk_write_time double precision, OUT wal_records bigint, OUT wal_fpi bigint, OUT wal_bytes numeric, OUT jit_functions bigint, OUT jit_generation_time double precision, OUT jit_inlining_count bigint, OUT jit_inlining_time double precision, OUT jit_optimization_count bigint, OUT jit_optimization_time double precision, OUT jit_emission_count bigint, OUT jit_emission_time double precision, OUT jit_deform_count bigint, OUT jit_deform_time double precision, OUT stats_since timestamp with time zone, OUT minmax_stats_since timestamp with time zone); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pg_stat_statements(showtext boolean, OUT userid oid, OUT dbid oid, OUT toplevel boolean, OUT queryid bigint, OUT query text, OUT plans bigint, OUT total_plan_time double precision, OUT min_plan_time double precision, OUT max_plan_time double precision, OUT mean_plan_time double precision, OUT stddev_plan_time double precision, OUT calls bigint, OUT total_exec_time double precision, OUT min_exec_time double precision, OUT max_exec_time double precision, OUT mean_exec_time double precision, OUT stddev_exec_time double precision, OUT rows bigint, OUT shared_blks_hit bigint, OUT shared_blks_read bigint, OUT shared_blks_dirtied bigint, OUT shared_blks_written bigint, OUT local_blks_hit bigint, OUT local_blks_read bigint, OUT local_blks_dirtied bigint, OUT local_blks_written bigint, OUT temp_blks_read bigint, OUT temp_blks_written bigint, OUT shared_blk_read_time double precision, OUT shared_blk_write_time double precision, OUT local_blk_read_time double precision, OUT local_blk_write_time double precision, OUT temp_blk_read_time double precision, OUT temp_blk_write_time double precision, OUT wal_records bigint, OUT wal_fpi bigint, OUT wal_bytes numeric, OUT jit_functions bigint, OUT jit_generation_time double precision, OUT jit_inlining_count bigint, OUT jit_inlining_time double precision, OUT jit_optimization_count bigint, OUT jit_optimization_time double precision, OUT jit_emission_count bigint, OUT jit_emission_time double precision, OUT jit_deform_count bigint, OUT jit_deform_time double precision, OUT stats_since timestamp with time zone, OUT minmax_stats_since timestamp with time zone) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pg_stat_statements_info(OUT dealloc bigint, OUT stats_reset timestamp with time zone); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pg_stat_statements_info(OUT dealloc bigint, OUT stats_reset timestamp with time zone) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pg_stat_statements_reset(userid oid, dbid oid, queryid bigint, minmax_only boolean); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pg_stat_statements_reset(userid oid, dbid oid, queryid bigint, minmax_only boolean) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_armor_headers(text, OUT key text, OUT value text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_armor_headers(text, OUT key text, OUT value text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_armor_headers(text, OUT key text, OUT value text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_key_id(bytea); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_key_id(bytea) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_key_id(bytea) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_pub_decrypt(bytea, bytea); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt(bytea, bytea) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt(bytea, bytea) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_pub_decrypt(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt(bytea, bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt(bytea, bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_pub_decrypt(bytea, bytea, text, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt(bytea, bytea, text, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt(bytea, bytea, text, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_pub_decrypt_bytea(bytea, bytea); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt_bytea(bytea, bytea) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt_bytea(bytea, bytea) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_pub_decrypt_bytea(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt_bytea(bytea, bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt_bytea(bytea, bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_pub_decrypt_bytea(bytea, bytea, text, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt_bytea(bytea, bytea, text, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt_bytea(bytea, bytea, text, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_pub_encrypt(text, bytea); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt(text, bytea) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt(text, bytea) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_pub_encrypt(text, bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt(text, bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt(text, bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_pub_encrypt_bytea(bytea, bytea); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt_bytea(bytea, bytea) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt_bytea(bytea, bytea) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_pub_encrypt_bytea(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt_bytea(bytea, bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt_bytea(bytea, bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_sym_decrypt(bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt(bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt(bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_sym_decrypt(bytea, text, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt(bytea, text, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt(bytea, text, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_sym_decrypt_bytea(bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt_bytea(bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt_bytea(bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_sym_decrypt_bytea(bytea, text, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt_bytea(bytea, text, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt_bytea(bytea, text, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_sym_encrypt(text, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt(text, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt(text, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_sym_encrypt(text, text, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt(text, text, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt(text, text, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_sym_encrypt_bytea(bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt_bytea(bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt_bytea(bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_sym_encrypt_bytea(bytea, text, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt_bytea(bytea, text, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt_bytea(bytea, text, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgrst_ddl_watch(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgrst_ddl_watch() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgrst_drop_watch(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgrst_drop_watch() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION set_graphql_placeholder(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.set_graphql_placeholder() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION uuid_generate_v1(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.uuid_generate_v1() TO dashboard_user; -GRANT ALL ON FUNCTION extensions.uuid_generate_v1() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION uuid_generate_v1mc(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.uuid_generate_v1mc() TO dashboard_user; -GRANT ALL ON FUNCTION extensions.uuid_generate_v1mc() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION uuid_generate_v3(namespace uuid, name text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.uuid_generate_v3(namespace uuid, name text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.uuid_generate_v3(namespace uuid, name text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION uuid_generate_v4(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.uuid_generate_v4() TO dashboard_user; -GRANT ALL ON FUNCTION extensions.uuid_generate_v4() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION uuid_generate_v5(namespace uuid, name text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.uuid_generate_v5(namespace uuid, name text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.uuid_generate_v5(namespace uuid, name text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION uuid_nil(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.uuid_nil() TO dashboard_user; -GRANT ALL ON FUNCTION extensions.uuid_nil() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION uuid_ns_dns(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.uuid_ns_dns() TO dashboard_user; -GRANT ALL ON FUNCTION extensions.uuid_ns_dns() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION uuid_ns_oid(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.uuid_ns_oid() TO dashboard_user; -GRANT ALL ON FUNCTION extensions.uuid_ns_oid() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION uuid_ns_url(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.uuid_ns_url() TO dashboard_user; -GRANT ALL ON FUNCTION extensions.uuid_ns_url() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION uuid_ns_x500(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.uuid_ns_x500() TO dashboard_user; -GRANT ALL ON FUNCTION extensions.uuid_ns_x500() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION graphql("operationName" text, query text, variables jsonb, extensions jsonb); Type: ACL; Schema: graphql_public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION graphql_public.graphql("operationName" text, query text, variables jsonb, extensions jsonb) TO postgres; -GRANT ALL ON FUNCTION graphql_public.graphql("operationName" text, query text, variables jsonb, extensions jsonb) TO anon; -GRANT ALL ON FUNCTION graphql_public.graphql("operationName" text, query text, variables jsonb, extensions jsonb) TO authenticated; -GRANT ALL ON FUNCTION graphql_public.graphql("operationName" text, query text, variables jsonb, extensions jsonb) TO service_role; - - --- --- Name: FUNCTION http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer); Type: ACL; Schema: net; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; -GRANT ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin; -GRANT ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO postgres; -GRANT ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO anon; -GRANT ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO authenticated; -GRANT ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO service_role; - - --- --- Name: FUNCTION http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer); Type: ACL; Schema: net; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; -GRANT ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin; -GRANT ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO postgres; -GRANT ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO anon; -GRANT ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO authenticated; -GRANT ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO service_role; - - --- --- Name: FUNCTION pg_reload_conf(); Type: ACL; Schema: pg_catalog; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION pg_catalog.pg_reload_conf() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION get_auth(p_usename text); Type: ACL; Schema: pgbouncer; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION pgbouncer.get_auth(p_usename text) FROM PUBLIC; -GRANT ALL ON FUNCTION pgbouncer.get_auth(p_usename text) TO pgbouncer; - - --- --- Name: FUNCTION __rls_ping(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.__rls_ping() TO postgres; -GRANT ALL ON FUNCTION public.__rls_ping() TO anon; -GRANT ALL ON FUNCTION public.__rls_ping() TO authenticated; -GRANT ALL ON FUNCTION public.__rls_ping() TO service_role; - - --- --- Name: TABLE subscriptions; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.subscriptions TO postgres; -GRANT ALL ON TABLE public.subscriptions TO anon; -GRANT ALL ON TABLE public.subscriptions TO authenticated; -GRANT ALL ON TABLE public.subscriptions TO service_role; - - --- --- Name: FUNCTION activate_subscription_from_intent(p_intent_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.activate_subscription_from_intent(p_intent_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.activate_subscription_from_intent(p_intent_id uuid) TO anon; -GRANT ALL ON FUNCTION public.activate_subscription_from_intent(p_intent_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.activate_subscription_from_intent(p_intent_id uuid) TO service_role; - - --- --- Name: FUNCTION admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer) TO postgres; -GRANT ALL ON FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer) TO anon; -GRANT ALL ON FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer) TO authenticated; -GRANT ALL ON FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer) TO service_role; - - --- --- Name: FUNCTION admin_delete_email_template_global(p_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.admin_delete_email_template_global(p_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.admin_delete_email_template_global(p_id uuid) TO anon; -GRANT ALL ON FUNCTION public.admin_delete_email_template_global(p_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.admin_delete_email_template_global(p_id uuid) TO service_role; - - --- --- Name: FUNCTION admin_fix_plan_target(p_plan_key text, p_new_target text); Type: ACL; Schema: public; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION public.admin_fix_plan_target(p_plan_key text, p_new_target text) FROM PUBLIC; -GRANT ALL ON FUNCTION public.admin_fix_plan_target(p_plan_key text, p_new_target text) TO postgres; -GRANT ALL ON FUNCTION public.admin_fix_plan_target(p_plan_key text, p_new_target text) TO anon; -GRANT ALL ON FUNCTION public.admin_fix_plan_target(p_plan_key text, p_new_target text) TO authenticated; -GRANT ALL ON FUNCTION public.admin_fix_plan_target(p_plan_key text, p_new_target text) TO service_role; - - --- --- Name: FUNCTION admin_upsert_email_template_global(p_id uuid, p_key text, p_domain text, p_channel text, p_subject text, p_body_html text, p_body_text text, p_is_active boolean, p_variables jsonb); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.admin_upsert_email_template_global(p_id uuid, p_key text, p_domain text, p_channel text, p_subject text, p_body_html text, p_body_text text, p_is_active boolean, p_variables jsonb) TO postgres; -GRANT ALL ON FUNCTION public.admin_upsert_email_template_global(p_id uuid, p_key text, p_domain text, p_channel text, p_subject text, p_body_html text, p_body_text text, p_is_active boolean, p_variables jsonb) TO anon; -GRANT ALL ON FUNCTION public.admin_upsert_email_template_global(p_id uuid, p_key text, p_domain text, p_channel text, p_subject text, p_body_html text, p_body_text text, p_is_active boolean, p_variables jsonb) TO authenticated; -GRANT ALL ON FUNCTION public.admin_upsert_email_template_global(p_id uuid, p_key text, p_domain text, p_channel text, p_subject text, p_body_html text, p_body_text text, p_is_active boolean, p_variables jsonb) TO service_role; - - --- --- Name: FUNCTION agenda_cfg_sync(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.agenda_cfg_sync() TO postgres; -GRANT ALL ON FUNCTION public.agenda_cfg_sync() TO anon; -GRANT ALL ON FUNCTION public.agenda_cfg_sync() TO authenticated; -GRANT ALL ON FUNCTION public.agenda_cfg_sync() TO service_role; - - --- --- Name: FUNCTION agendador_dias_disponiveis(p_slug text, p_ano integer, p_mes integer); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.agendador_dias_disponiveis(p_slug text, p_ano integer, p_mes integer) TO postgres; -GRANT ALL ON FUNCTION public.agendador_dias_disponiveis(p_slug text, p_ano integer, p_mes integer) TO anon; -GRANT ALL ON FUNCTION public.agendador_dias_disponiveis(p_slug text, p_ano integer, p_mes integer) TO authenticated; -GRANT ALL ON FUNCTION public.agendador_dias_disponiveis(p_slug text, p_ano integer, p_mes integer) TO service_role; - - --- --- Name: FUNCTION agendador_gerar_slug(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.agendador_gerar_slug() TO postgres; -GRANT ALL ON FUNCTION public.agendador_gerar_slug() TO anon; -GRANT ALL ON FUNCTION public.agendador_gerar_slug() TO authenticated; -GRANT ALL ON FUNCTION public.agendador_gerar_slug() TO service_role; - - --- --- Name: FUNCTION agendador_slots_disponiveis(p_slug text, p_data date); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.agendador_slots_disponiveis(p_slug text, p_data date) TO postgres; -GRANT ALL ON FUNCTION public.agendador_slots_disponiveis(p_slug text, p_data date) TO anon; -GRANT ALL ON FUNCTION public.agendador_slots_disponiveis(p_slug text, p_data date) TO authenticated; -GRANT ALL ON FUNCTION public.agendador_slots_disponiveis(p_slug text, p_data date) TO service_role; - - --- --- Name: FUNCTION auto_create_financial_record_from_session(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.auto_create_financial_record_from_session() TO postgres; -GRANT ALL ON FUNCTION public.auto_create_financial_record_from_session() TO anon; -GRANT ALL ON FUNCTION public.auto_create_financial_record_from_session() TO authenticated; -GRANT ALL ON FUNCTION public.auto_create_financial_record_from_session() TO service_role; - - --- --- Name: FUNCTION can_delete_patient(p_patient_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.can_delete_patient(p_patient_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.can_delete_patient(p_patient_id uuid) TO anon; -GRANT ALL ON FUNCTION public.can_delete_patient(p_patient_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.can_delete_patient(p_patient_id uuid) TO service_role; - - --- --- Name: FUNCTION cancel_notifications_on_opt_out(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.cancel_notifications_on_opt_out() TO postgres; -GRANT ALL ON FUNCTION public.cancel_notifications_on_opt_out() TO anon; -GRANT ALL ON FUNCTION public.cancel_notifications_on_opt_out() TO authenticated; -GRANT ALL ON FUNCTION public.cancel_notifications_on_opt_out() TO service_role; - - --- --- Name: FUNCTION cancel_notifications_on_session_cancel(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.cancel_notifications_on_session_cancel() TO postgres; -GRANT ALL ON FUNCTION public.cancel_notifications_on_session_cancel() TO anon; -GRANT ALL ON FUNCTION public.cancel_notifications_on_session_cancel() TO authenticated; -GRANT ALL ON FUNCTION public.cancel_notifications_on_session_cancel() TO service_role; - - --- --- Name: FUNCTION cancel_patient_pending_notifications(p_patient_id uuid, p_channel text, p_evento_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.cancel_patient_pending_notifications(p_patient_id uuid, p_channel text, p_evento_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.cancel_patient_pending_notifications(p_patient_id uuid, p_channel text, p_evento_id uuid) TO anon; -GRANT ALL ON FUNCTION public.cancel_patient_pending_notifications(p_patient_id uuid, p_channel text, p_evento_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.cancel_patient_pending_notifications(p_patient_id uuid, p_channel text, p_evento_id uuid) TO service_role; - - --- --- Name: FUNCTION cancel_recurrence_from(p_recurrence_id uuid, p_from_date date); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.cancel_recurrence_from(p_recurrence_id uuid, p_from_date date) TO postgres; -GRANT ALL ON FUNCTION public.cancel_recurrence_from(p_recurrence_id uuid, p_from_date date) TO anon; -GRANT ALL ON FUNCTION public.cancel_recurrence_from(p_recurrence_id uuid, p_from_date date) TO authenticated; -GRANT ALL ON FUNCTION public.cancel_recurrence_from(p_recurrence_id uuid, p_from_date date) TO service_role; - - --- --- Name: FUNCTION cancel_subscription(p_subscription_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.cancel_subscription(p_subscription_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.cancel_subscription(p_subscription_id uuid) TO anon; -GRANT ALL ON FUNCTION public.cancel_subscription(p_subscription_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.cancel_subscription(p_subscription_id uuid) TO service_role; - - --- --- Name: FUNCTION cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone) TO postgres; -GRANT ALL ON FUNCTION public.cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone) TO anon; -GRANT ALL ON FUNCTION public.cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone) TO authenticated; -GRANT ALL ON FUNCTION public.cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone) TO service_role; - - --- --- Name: FUNCTION cash_dist(money, money); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.cash_dist(money, money) TO postgres; -GRANT ALL ON FUNCTION public.cash_dist(money, money) TO anon; -GRANT ALL ON FUNCTION public.cash_dist(money, money) TO authenticated; -GRANT ALL ON FUNCTION public.cash_dist(money, money) TO service_role; - - --- --- Name: FUNCTION change_subscription_plan(p_subscription_id uuid, p_new_plan_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.change_subscription_plan(p_subscription_id uuid, p_new_plan_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.change_subscription_plan(p_subscription_id uuid, p_new_plan_id uuid) TO anon; -GRANT ALL ON FUNCTION public.change_subscription_plan(p_subscription_id uuid, p_new_plan_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.change_subscription_plan(p_subscription_id uuid, p_new_plan_id uuid) TO service_role; - - --- --- Name: FUNCTION citext_cmp(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_cmp(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_cmp(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_cmp(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_cmp(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_eq(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_eq(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_eq(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_eq(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_eq(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_ge(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_ge(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_ge(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_ge(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_ge(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_gt(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_gt(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_gt(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_gt(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_gt(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_hash(public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_hash(public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_hash(public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_hash(public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_hash(public.citext) TO service_role; - - --- --- Name: FUNCTION citext_hash_extended(public.citext, bigint); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_hash_extended(public.citext, bigint) TO postgres; -GRANT ALL ON FUNCTION public.citext_hash_extended(public.citext, bigint) TO anon; -GRANT ALL ON FUNCTION public.citext_hash_extended(public.citext, bigint) TO authenticated; -GRANT ALL ON FUNCTION public.citext_hash_extended(public.citext, bigint) TO service_role; - - --- --- Name: FUNCTION citext_larger(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_larger(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_larger(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_larger(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_larger(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_le(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_le(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_le(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_le(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_le(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_lt(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_lt(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_lt(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_lt(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_lt(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_ne(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_ne(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_ne(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_ne(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_ne(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_pattern_cmp(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_pattern_cmp(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_pattern_cmp(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_pattern_cmp(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_pattern_cmp(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_pattern_ge(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_pattern_ge(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_pattern_ge(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_pattern_ge(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_pattern_ge(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_pattern_gt(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_pattern_gt(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_pattern_gt(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_pattern_gt(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_pattern_gt(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_pattern_le(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_pattern_le(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_pattern_le(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_pattern_le(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_pattern_le(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_pattern_lt(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_pattern_lt(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_pattern_lt(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_pattern_lt(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_pattern_lt(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_smaller(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_smaller(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_smaller(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_smaller(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_smaller(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION cleanup_notification_queue(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.cleanup_notification_queue() TO postgres; -GRANT ALL ON FUNCTION public.cleanup_notification_queue() TO anon; -GRANT ALL ON FUNCTION public.cleanup_notification_queue() TO authenticated; -GRANT ALL ON FUNCTION public.cleanup_notification_queue() TO service_role; - - --- --- Name: FUNCTION create_clinic_tenant(p_name text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.create_clinic_tenant(p_name text) TO postgres; -GRANT ALL ON FUNCTION public.create_clinic_tenant(p_name text) TO anon; -GRANT ALL ON FUNCTION public.create_clinic_tenant(p_name text) TO authenticated; -GRANT ALL ON FUNCTION public.create_clinic_tenant(p_name text) TO service_role; - - --- --- Name: TABLE financial_records; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.financial_records TO postgres; -GRANT ALL ON TABLE public.financial_records TO anon; -GRANT ALL ON TABLE public.financial_records TO authenticated; -GRANT ALL ON TABLE public.financial_records TO service_role; - - --- --- Name: FUNCTION 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); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON 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) TO postgres; -GRANT ALL ON 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) TO anon; -GRANT ALL ON 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) TO authenticated; -GRANT ALL ON 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) TO service_role; - - --- --- Name: FUNCTION create_patient_intake_request(p_token text, p_name text, p_email text, p_phone text, p_notes text, p_consent boolean); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.create_patient_intake_request(p_token text, p_name text, p_email text, p_phone text, p_notes text, p_consent boolean) TO postgres; -GRANT ALL ON FUNCTION public.create_patient_intake_request(p_token text, p_name text, p_email text, p_phone text, p_notes text, p_consent boolean) TO anon; -GRANT ALL ON FUNCTION public.create_patient_intake_request(p_token text, p_name text, p_email text, p_phone text, p_notes text, p_consent boolean) TO authenticated; -GRANT ALL ON FUNCTION public.create_patient_intake_request(p_token text, p_name text, p_email text, p_phone text, p_notes text, p_consent boolean) TO service_role; - - --- --- Name: FUNCTION create_patient_intake_request_v2(p_token text, p_payload jsonb); Type: ACL; Schema: public; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION public.create_patient_intake_request_v2(p_token text, p_payload jsonb) FROM PUBLIC; -GRANT ALL ON FUNCTION public.create_patient_intake_request_v2(p_token text, p_payload jsonb) TO postgres; -GRANT ALL ON FUNCTION public.create_patient_intake_request_v2(p_token text, p_payload jsonb) TO anon; -GRANT ALL ON FUNCTION public.create_patient_intake_request_v2(p_token text, p_payload jsonb) TO authenticated; -GRANT ALL ON FUNCTION public.create_patient_intake_request_v2(p_token text, p_payload jsonb) TO service_role; - - --- --- Name: FUNCTION create_support_session(p_tenant_id uuid, p_ttl_minutes integer); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.create_support_session(p_tenant_id uuid, p_ttl_minutes integer) TO postgres; -GRANT ALL ON FUNCTION public.create_support_session(p_tenant_id uuid, p_ttl_minutes integer) TO anon; -GRANT ALL ON FUNCTION public.create_support_session(p_tenant_id uuid, p_ttl_minutes integer) TO authenticated; -GRANT ALL ON FUNCTION public.create_support_session(p_tenant_id uuid, p_ttl_minutes integer) TO service_role; - - --- --- Name: TABLE therapist_payouts; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.therapist_payouts TO postgres; -GRANT ALL ON TABLE public.therapist_payouts TO anon; -GRANT ALL ON TABLE public.therapist_payouts TO authenticated; -GRANT ALL ON TABLE public.therapist_payouts TO service_role; - - --- --- Name: FUNCTION create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date) TO postgres; -GRANT ALL ON FUNCTION public.create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date) TO anon; -GRANT ALL ON FUNCTION public.create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date) TO authenticated; -GRANT ALL ON FUNCTION public.create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date) TO service_role; - - --- --- Name: FUNCTION current_member_id(p_tenant_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.current_member_id(p_tenant_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.current_member_id(p_tenant_id uuid) TO anon; -GRANT ALL ON FUNCTION public.current_member_id(p_tenant_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.current_member_id(p_tenant_id uuid) TO service_role; - - --- --- Name: FUNCTION current_member_role(p_tenant_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.current_member_role(p_tenant_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.current_member_role(p_tenant_id uuid) TO anon; -GRANT ALL ON FUNCTION public.current_member_role(p_tenant_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.current_member_role(p_tenant_id uuid) TO service_role; - - --- --- Name: FUNCTION date_dist(date, date); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.date_dist(date, date) TO postgres; -GRANT ALL ON FUNCTION public.date_dist(date, date) TO anon; -GRANT ALL ON FUNCTION public.date_dist(date, date) TO authenticated; -GRANT ALL ON FUNCTION public.date_dist(date, date) TO service_role; - - --- --- Name: FUNCTION debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text) TO postgres; -GRANT ALL ON FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text) TO anon; -GRANT ALL ON FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text) TO authenticated; -GRANT ALL ON FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text) TO service_role; - - --- --- Name: FUNCTION delete_commitment_full(p_tenant_id uuid, p_commitment_id uuid); Type: ACL; Schema: public; Owner: postgres --- - -GRANT ALL ON FUNCTION public.delete_commitment_full(p_tenant_id uuid, p_commitment_id uuid) TO anon; -GRANT ALL ON FUNCTION public.delete_commitment_full(p_tenant_id uuid, p_commitment_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.delete_commitment_full(p_tenant_id uuid, p_commitment_id uuid) TO service_role; - - --- --- Name: FUNCTION delete_determined_commitment(p_tenant_id uuid, p_commitment_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.delete_determined_commitment(p_tenant_id uuid, p_commitment_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.delete_determined_commitment(p_tenant_id uuid, p_commitment_id uuid) TO anon; -GRANT ALL ON FUNCTION public.delete_determined_commitment(p_tenant_id uuid, p_commitment_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.delete_determined_commitment(p_tenant_id uuid, p_commitment_id uuid) TO service_role; - - --- --- Name: FUNCTION dev_list_auth_users(p_limit integer); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.dev_list_auth_users(p_limit integer) TO postgres; -GRANT ALL ON FUNCTION public.dev_list_auth_users(p_limit integer) TO anon; -GRANT ALL ON FUNCTION public.dev_list_auth_users(p_limit integer) TO authenticated; -GRANT ALL ON FUNCTION public.dev_list_auth_users(p_limit integer) TO service_role; - - --- --- Name: FUNCTION dev_list_custom_users(); Type: ACL; Schema: public; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION public.dev_list_custom_users() FROM PUBLIC; -GRANT ALL ON FUNCTION public.dev_list_custom_users() TO postgres; -GRANT ALL ON FUNCTION public.dev_list_custom_users() TO anon; -GRANT ALL ON FUNCTION public.dev_list_custom_users() TO authenticated; -GRANT ALL ON FUNCTION public.dev_list_custom_users() TO service_role; - - --- --- Name: FUNCTION dev_list_intent_leads(); Type: ACL; Schema: public; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION public.dev_list_intent_leads() FROM PUBLIC; -GRANT ALL ON FUNCTION public.dev_list_intent_leads() TO postgres; -GRANT ALL ON FUNCTION public.dev_list_intent_leads() TO anon; -GRANT ALL ON FUNCTION public.dev_list_intent_leads() TO authenticated; -GRANT ALL ON FUNCTION public.dev_list_intent_leads() TO service_role; - - --- --- Name: FUNCTION dev_public_debug_snapshot(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.dev_public_debug_snapshot() TO postgres; -GRANT ALL ON FUNCTION public.dev_public_debug_snapshot() TO anon; -GRANT ALL ON FUNCTION public.dev_public_debug_snapshot() TO authenticated; -GRANT ALL ON FUNCTION public.dev_public_debug_snapshot() TO service_role; - - --- --- Name: FUNCTION ensure_personal_tenant(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.ensure_personal_tenant() TO postgres; -GRANT ALL ON FUNCTION public.ensure_personal_tenant() TO anon; -GRANT ALL ON FUNCTION public.ensure_personal_tenant() TO authenticated; -GRANT ALL ON FUNCTION public.ensure_personal_tenant() TO service_role; - - --- --- Name: FUNCTION ensure_personal_tenant_for_user(p_user_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.ensure_personal_tenant_for_user(p_user_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.ensure_personal_tenant_for_user(p_user_id uuid) TO anon; -GRANT ALL ON FUNCTION public.ensure_personal_tenant_for_user(p_user_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.ensure_personal_tenant_for_user(p_user_id uuid) TO service_role; - - --- --- Name: FUNCTION faq_votar(faq_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.faq_votar(faq_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.faq_votar(faq_id uuid) TO anon; -GRANT ALL ON FUNCTION public.faq_votar(faq_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.faq_votar(faq_id uuid) TO service_role; - - --- --- Name: FUNCTION fix_all_subscription_mismatches(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.fix_all_subscription_mismatches() TO postgres; -GRANT ALL ON FUNCTION public.fix_all_subscription_mismatches() TO anon; -GRANT ALL ON FUNCTION public.fix_all_subscription_mismatches() TO authenticated; -GRANT ALL ON FUNCTION public.fix_all_subscription_mismatches() TO service_role; - - --- --- Name: FUNCTION float4_dist(real, real); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.float4_dist(real, real) TO postgres; -GRANT ALL ON FUNCTION public.float4_dist(real, real) TO anon; -GRANT ALL ON FUNCTION public.float4_dist(real, real) TO authenticated; -GRANT ALL ON FUNCTION public.float4_dist(real, real) TO service_role; - - --- --- Name: FUNCTION float8_dist(double precision, double precision); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.float8_dist(double precision, double precision) TO postgres; -GRANT ALL ON FUNCTION public.float8_dist(double precision, double precision) TO anon; -GRANT ALL ON FUNCTION public.float8_dist(double precision, double precision) TO authenticated; -GRANT ALL ON FUNCTION public.float8_dist(double precision, double precision) TO service_role; - - --- --- Name: FUNCTION fn_agenda_regras_semanais_no_overlap(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.fn_agenda_regras_semanais_no_overlap() TO postgres; -GRANT ALL ON FUNCTION public.fn_agenda_regras_semanais_no_overlap() TO anon; -GRANT ALL ON FUNCTION public.fn_agenda_regras_semanais_no_overlap() TO authenticated; -GRANT ALL ON FUNCTION public.fn_agenda_regras_semanais_no_overlap() TO service_role; - - --- --- Name: FUNCTION gbt_bit_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bit_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bit_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bit_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bit_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_bit_consistent(internal, bit, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bit_consistent(internal, bit, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bit_consistent(internal, bit, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bit_consistent(internal, bit, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bit_consistent(internal, bit, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bit_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bit_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bit_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bit_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bit_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bit_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bit_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bit_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bit_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bit_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bit_same(public.gbtreekey_var, public.gbtreekey_var, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bit_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bit_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bit_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bit_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bit_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bit_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bit_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bit_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bit_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bool_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bool_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bool_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bool_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bool_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_bool_consistent(internal, boolean, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bool_consistent(internal, boolean, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bool_consistent(internal, boolean, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bool_consistent(internal, boolean, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bool_consistent(internal, boolean, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bool_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bool_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bool_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bool_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bool_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_bool_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bool_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bool_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bool_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bool_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bool_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bool_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bool_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bool_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bool_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bool_same(public.gbtreekey2, public.gbtreekey2, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bool_same(public.gbtreekey2, public.gbtreekey2, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bool_same(public.gbtreekey2, public.gbtreekey2, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bool_same(public.gbtreekey2, public.gbtreekey2, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bool_same(public.gbtreekey2, public.gbtreekey2, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bool_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bool_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bool_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bool_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bool_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bpchar_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bpchar_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bpchar_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bpchar_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bpchar_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_bpchar_consistent(internal, character, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bpchar_consistent(internal, character, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bpchar_consistent(internal, character, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bpchar_consistent(internal, character, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bpchar_consistent(internal, character, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bytea_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bytea_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bytea_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bytea_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bytea_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_bytea_consistent(internal, bytea, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bytea_consistent(internal, bytea, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bytea_consistent(internal, bytea, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bytea_consistent(internal, bytea, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bytea_consistent(internal, bytea, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bytea_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bytea_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bytea_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bytea_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bytea_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bytea_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bytea_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bytea_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bytea_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bytea_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bytea_same(public.gbtreekey_var, public.gbtreekey_var, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bytea_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bytea_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bytea_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bytea_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bytea_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bytea_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bytea_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bytea_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bytea_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_cash_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_cash_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_cash_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_cash_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_cash_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_cash_consistent(internal, money, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_cash_consistent(internal, money, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_cash_consistent(internal, money, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_cash_consistent(internal, money, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_cash_consistent(internal, money, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_cash_distance(internal, money, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_cash_distance(internal, money, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_cash_distance(internal, money, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_cash_distance(internal, money, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_cash_distance(internal, money, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_cash_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_cash_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_cash_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_cash_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_cash_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_cash_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_cash_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_cash_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_cash_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_cash_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_cash_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_cash_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_cash_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_cash_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_cash_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_cash_same(public.gbtreekey16, public.gbtreekey16, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_cash_same(public.gbtreekey16, public.gbtreekey16, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_cash_same(public.gbtreekey16, public.gbtreekey16, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_cash_same(public.gbtreekey16, public.gbtreekey16, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_cash_same(public.gbtreekey16, public.gbtreekey16, internal) TO service_role; - - --- --- Name: FUNCTION gbt_cash_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_cash_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_cash_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_cash_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_cash_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_date_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_date_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_date_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_date_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_date_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_date_consistent(internal, date, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_date_consistent(internal, date, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_date_consistent(internal, date, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_date_consistent(internal, date, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_date_consistent(internal, date, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_date_distance(internal, date, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_date_distance(internal, date, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_date_distance(internal, date, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_date_distance(internal, date, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_date_distance(internal, date, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_date_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_date_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_date_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_date_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_date_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_date_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_date_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_date_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_date_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_date_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_date_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_date_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_date_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_date_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_date_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_date_same(public.gbtreekey8, public.gbtreekey8, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_date_same(public.gbtreekey8, public.gbtreekey8, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_date_same(public.gbtreekey8, public.gbtreekey8, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_date_same(public.gbtreekey8, public.gbtreekey8, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_date_same(public.gbtreekey8, public.gbtreekey8, internal) TO service_role; - - --- --- Name: FUNCTION gbt_date_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_date_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_date_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_date_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_date_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_decompress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_decompress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_decompress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_decompress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_decompress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_enum_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_enum_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_enum_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_enum_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_enum_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_enum_consistent(internal, anyenum, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_enum_consistent(internal, anyenum, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_enum_consistent(internal, anyenum, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_enum_consistent(internal, anyenum, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_enum_consistent(internal, anyenum, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_enum_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_enum_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_enum_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_enum_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_enum_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_enum_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_enum_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_enum_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_enum_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_enum_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_enum_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_enum_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_enum_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_enum_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_enum_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_enum_same(public.gbtreekey8, public.gbtreekey8, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_enum_same(public.gbtreekey8, public.gbtreekey8, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_enum_same(public.gbtreekey8, public.gbtreekey8, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_enum_same(public.gbtreekey8, public.gbtreekey8, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_enum_same(public.gbtreekey8, public.gbtreekey8, internal) TO service_role; - - --- --- Name: FUNCTION gbt_enum_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_enum_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_enum_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_enum_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_enum_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float4_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float4_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float4_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float4_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float4_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_float4_consistent(internal, real, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float4_consistent(internal, real, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float4_consistent(internal, real, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float4_consistent(internal, real, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float4_consistent(internal, real, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float4_distance(internal, real, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float4_distance(internal, real, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float4_distance(internal, real, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float4_distance(internal, real, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float4_distance(internal, real, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float4_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float4_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float4_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float4_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float4_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_float4_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float4_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float4_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float4_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float4_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float4_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float4_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float4_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float4_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float4_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float4_same(public.gbtreekey8, public.gbtreekey8, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float4_same(public.gbtreekey8, public.gbtreekey8, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float4_same(public.gbtreekey8, public.gbtreekey8, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float4_same(public.gbtreekey8, public.gbtreekey8, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float4_same(public.gbtreekey8, public.gbtreekey8, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float4_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float4_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float4_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float4_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float4_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float8_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float8_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float8_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float8_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float8_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_float8_consistent(internal, double precision, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float8_consistent(internal, double precision, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float8_consistent(internal, double precision, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float8_consistent(internal, double precision, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float8_consistent(internal, double precision, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float8_distance(internal, double precision, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float8_distance(internal, double precision, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float8_distance(internal, double precision, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float8_distance(internal, double precision, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float8_distance(internal, double precision, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float8_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float8_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float8_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float8_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float8_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_float8_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float8_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float8_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float8_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float8_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float8_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float8_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float8_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float8_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float8_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float8_same(public.gbtreekey16, public.gbtreekey16, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float8_same(public.gbtreekey16, public.gbtreekey16, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float8_same(public.gbtreekey16, public.gbtreekey16, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float8_same(public.gbtreekey16, public.gbtreekey16, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float8_same(public.gbtreekey16, public.gbtreekey16, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float8_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float8_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float8_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float8_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float8_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_inet_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_inet_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_inet_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_inet_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_inet_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_inet_consistent(internal, inet, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_inet_consistent(internal, inet, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_inet_consistent(internal, inet, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_inet_consistent(internal, inet, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_inet_consistent(internal, inet, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_inet_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_inet_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_inet_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_inet_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_inet_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_inet_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_inet_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_inet_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_inet_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_inet_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_inet_same(public.gbtreekey16, public.gbtreekey16, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_inet_same(public.gbtreekey16, public.gbtreekey16, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_inet_same(public.gbtreekey16, public.gbtreekey16, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_inet_same(public.gbtreekey16, public.gbtreekey16, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_inet_same(public.gbtreekey16, public.gbtreekey16, internal) TO service_role; - - --- --- Name: FUNCTION gbt_inet_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_inet_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_inet_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_inet_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_inet_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int2_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int2_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int2_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int2_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int2_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_int2_consistent(internal, smallint, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int2_consistent(internal, smallint, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int2_consistent(internal, smallint, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int2_consistent(internal, smallint, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int2_consistent(internal, smallint, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int2_distance(internal, smallint, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int2_distance(internal, smallint, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int2_distance(internal, smallint, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int2_distance(internal, smallint, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int2_distance(internal, smallint, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int2_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int2_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int2_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int2_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int2_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_int2_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int2_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int2_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int2_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int2_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int2_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int2_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int2_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int2_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int2_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int2_same(public.gbtreekey4, public.gbtreekey4, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int2_same(public.gbtreekey4, public.gbtreekey4, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int2_same(public.gbtreekey4, public.gbtreekey4, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int2_same(public.gbtreekey4, public.gbtreekey4, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int2_same(public.gbtreekey4, public.gbtreekey4, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int2_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int2_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int2_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int2_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int2_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int4_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int4_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int4_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int4_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int4_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_int4_consistent(internal, integer, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int4_consistent(internal, integer, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int4_consistent(internal, integer, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int4_consistent(internal, integer, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int4_consistent(internal, integer, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int4_distance(internal, integer, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int4_distance(internal, integer, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int4_distance(internal, integer, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int4_distance(internal, integer, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int4_distance(internal, integer, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int4_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int4_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int4_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int4_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int4_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_int4_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int4_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int4_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int4_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int4_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int4_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int4_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int4_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int4_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int4_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int4_same(public.gbtreekey8, public.gbtreekey8, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int4_same(public.gbtreekey8, public.gbtreekey8, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int4_same(public.gbtreekey8, public.gbtreekey8, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int4_same(public.gbtreekey8, public.gbtreekey8, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int4_same(public.gbtreekey8, public.gbtreekey8, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int4_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int4_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int4_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int4_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int4_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int8_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int8_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int8_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int8_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int8_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_int8_consistent(internal, bigint, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int8_consistent(internal, bigint, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int8_consistent(internal, bigint, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int8_consistent(internal, bigint, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int8_consistent(internal, bigint, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int8_distance(internal, bigint, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int8_distance(internal, bigint, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int8_distance(internal, bigint, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int8_distance(internal, bigint, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int8_distance(internal, bigint, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int8_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int8_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int8_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int8_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int8_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_int8_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int8_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int8_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int8_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int8_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int8_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int8_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int8_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int8_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int8_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int8_same(public.gbtreekey16, public.gbtreekey16, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int8_same(public.gbtreekey16, public.gbtreekey16, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int8_same(public.gbtreekey16, public.gbtreekey16, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int8_same(public.gbtreekey16, public.gbtreekey16, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int8_same(public.gbtreekey16, public.gbtreekey16, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int8_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int8_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int8_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int8_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int8_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_intv_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_intv_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_intv_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_intv_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_intv_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_intv_consistent(internal, interval, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_intv_consistent(internal, interval, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_intv_consistent(internal, interval, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_intv_consistent(internal, interval, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_intv_consistent(internal, interval, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_intv_decompress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_intv_decompress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_intv_decompress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_intv_decompress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_intv_decompress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_intv_distance(internal, interval, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_intv_distance(internal, interval, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_intv_distance(internal, interval, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_intv_distance(internal, interval, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_intv_distance(internal, interval, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_intv_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_intv_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_intv_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_intv_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_intv_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_intv_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_intv_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_intv_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_intv_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_intv_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_intv_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_intv_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_intv_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_intv_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_intv_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_intv_same(public.gbtreekey32, public.gbtreekey32, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_intv_same(public.gbtreekey32, public.gbtreekey32, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_intv_same(public.gbtreekey32, public.gbtreekey32, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_intv_same(public.gbtreekey32, public.gbtreekey32, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_intv_same(public.gbtreekey32, public.gbtreekey32, internal) TO service_role; - - --- --- Name: FUNCTION gbt_intv_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_intv_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_intv_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_intv_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_intv_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad8_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad8_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad8_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad8_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad8_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad8_consistent(internal, macaddr8, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad8_consistent(internal, macaddr8, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad8_consistent(internal, macaddr8, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad8_consistent(internal, macaddr8, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad8_consistent(internal, macaddr8, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad8_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad8_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad8_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad8_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad8_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad8_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad8_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad8_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad8_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad8_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad8_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad8_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad8_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad8_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad8_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad8_same(public.gbtreekey16, public.gbtreekey16, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad8_same(public.gbtreekey16, public.gbtreekey16, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad8_same(public.gbtreekey16, public.gbtreekey16, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad8_same(public.gbtreekey16, public.gbtreekey16, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad8_same(public.gbtreekey16, public.gbtreekey16, internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad8_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad8_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad8_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad8_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad8_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad_consistent(internal, macaddr, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad_consistent(internal, macaddr, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad_consistent(internal, macaddr, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad_consistent(internal, macaddr, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad_consistent(internal, macaddr, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad_same(public.gbtreekey16, public.gbtreekey16, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad_same(public.gbtreekey16, public.gbtreekey16, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad_same(public.gbtreekey16, public.gbtreekey16, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad_same(public.gbtreekey16, public.gbtreekey16, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad_same(public.gbtreekey16, public.gbtreekey16, internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_numeric_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_numeric_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_numeric_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_numeric_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_numeric_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_numeric_consistent(internal, numeric, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_numeric_consistent(internal, numeric, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_numeric_consistent(internal, numeric, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_numeric_consistent(internal, numeric, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_numeric_consistent(internal, numeric, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_numeric_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_numeric_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_numeric_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_numeric_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_numeric_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_numeric_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_numeric_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_numeric_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_numeric_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_numeric_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_numeric_same(public.gbtreekey_var, public.gbtreekey_var, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_numeric_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_numeric_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_numeric_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_numeric_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO service_role; - - --- --- Name: FUNCTION gbt_numeric_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_numeric_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_numeric_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_numeric_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_numeric_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_oid_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_oid_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_oid_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_oid_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_oid_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_oid_consistent(internal, oid, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_oid_consistent(internal, oid, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_oid_consistent(internal, oid, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_oid_consistent(internal, oid, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_oid_consistent(internal, oid, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_oid_distance(internal, oid, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_oid_distance(internal, oid, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_oid_distance(internal, oid, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_oid_distance(internal, oid, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_oid_distance(internal, oid, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_oid_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_oid_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_oid_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_oid_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_oid_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_oid_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_oid_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_oid_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_oid_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_oid_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_oid_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_oid_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_oid_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_oid_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_oid_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_oid_same(public.gbtreekey8, public.gbtreekey8, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_oid_same(public.gbtreekey8, public.gbtreekey8, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_oid_same(public.gbtreekey8, public.gbtreekey8, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_oid_same(public.gbtreekey8, public.gbtreekey8, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_oid_same(public.gbtreekey8, public.gbtreekey8, internal) TO service_role; - - --- --- Name: FUNCTION gbt_oid_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_oid_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_oid_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_oid_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_oid_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_text_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_text_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_text_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_text_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_text_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_text_consistent(internal, text, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_text_consistent(internal, text, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_text_consistent(internal, text, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_text_consistent(internal, text, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_text_consistent(internal, text, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_text_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_text_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_text_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_text_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_text_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_text_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_text_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_text_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_text_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_text_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_text_same(public.gbtreekey_var, public.gbtreekey_var, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_text_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_text_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_text_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_text_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO service_role; - - --- --- Name: FUNCTION gbt_text_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_text_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_text_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_text_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_text_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_time_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_time_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_time_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_time_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_time_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_time_consistent(internal, time without time zone, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_time_consistent(internal, time without time zone, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_time_consistent(internal, time without time zone, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_time_consistent(internal, time without time zone, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_time_consistent(internal, time without time zone, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_time_distance(internal, time without time zone, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_time_distance(internal, time without time zone, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_time_distance(internal, time without time zone, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_time_distance(internal, time without time zone, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_time_distance(internal, time without time zone, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_time_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_time_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_time_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_time_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_time_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_time_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_time_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_time_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_time_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_time_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_time_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_time_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_time_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_time_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_time_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_time_same(public.gbtreekey16, public.gbtreekey16, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_time_same(public.gbtreekey16, public.gbtreekey16, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_time_same(public.gbtreekey16, public.gbtreekey16, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_time_same(public.gbtreekey16, public.gbtreekey16, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_time_same(public.gbtreekey16, public.gbtreekey16, internal) TO service_role; - - --- --- Name: FUNCTION gbt_time_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_time_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_time_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_time_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_time_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_timetz_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_timetz_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_timetz_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_timetz_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_timetz_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_timetz_consistent(internal, time with time zone, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_timetz_consistent(internal, time with time zone, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_timetz_consistent(internal, time with time zone, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_timetz_consistent(internal, time with time zone, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_timetz_consistent(internal, time with time zone, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_ts_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_ts_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_ts_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_ts_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_ts_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_ts_consistent(internal, timestamp without time zone, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_ts_consistent(internal, timestamp without time zone, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_ts_consistent(internal, timestamp without time zone, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_ts_consistent(internal, timestamp without time zone, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_ts_consistent(internal, timestamp without time zone, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_ts_distance(internal, timestamp without time zone, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_ts_distance(internal, timestamp without time zone, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_ts_distance(internal, timestamp without time zone, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_ts_distance(internal, timestamp without time zone, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_ts_distance(internal, timestamp without time zone, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_ts_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_ts_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_ts_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_ts_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_ts_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_ts_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_ts_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_ts_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_ts_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_ts_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_ts_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_ts_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_ts_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_ts_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_ts_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_ts_same(public.gbtreekey16, public.gbtreekey16, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_ts_same(public.gbtreekey16, public.gbtreekey16, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_ts_same(public.gbtreekey16, public.gbtreekey16, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_ts_same(public.gbtreekey16, public.gbtreekey16, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_ts_same(public.gbtreekey16, public.gbtreekey16, internal) TO service_role; - - --- --- Name: FUNCTION gbt_ts_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_ts_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_ts_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_ts_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_ts_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_tstz_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_tstz_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_tstz_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_tstz_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_tstz_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_tstz_consistent(internal, timestamp with time zone, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_tstz_consistent(internal, timestamp with time zone, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_tstz_consistent(internal, timestamp with time zone, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_tstz_consistent(internal, timestamp with time zone, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_tstz_consistent(internal, timestamp with time zone, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_tstz_distance(internal, timestamp with time zone, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_tstz_distance(internal, timestamp with time zone, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_tstz_distance(internal, timestamp with time zone, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_tstz_distance(internal, timestamp with time zone, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_tstz_distance(internal, timestamp with time zone, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_uuid_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_uuid_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_uuid_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_uuid_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_uuid_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_uuid_consistent(internal, uuid, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_uuid_consistent(internal, uuid, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_uuid_consistent(internal, uuid, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_uuid_consistent(internal, uuid, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_uuid_consistent(internal, uuid, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_uuid_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_uuid_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_uuid_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_uuid_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_uuid_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_uuid_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_uuid_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_uuid_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_uuid_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_uuid_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_uuid_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_uuid_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_uuid_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_uuid_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_uuid_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_uuid_same(public.gbtreekey32, public.gbtreekey32, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_uuid_same(public.gbtreekey32, public.gbtreekey32, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_uuid_same(public.gbtreekey32, public.gbtreekey32, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_uuid_same(public.gbtreekey32, public.gbtreekey32, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_uuid_same(public.gbtreekey32, public.gbtreekey32, internal) TO service_role; - - --- --- Name: FUNCTION gbt_uuid_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_uuid_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_uuid_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_uuid_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_uuid_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_var_decompress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_var_decompress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_var_decompress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_var_decompress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_var_decompress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_var_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_var_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_var_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_var_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_var_fetch(internal) TO service_role; - - --- --- Name: FUNCTION get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text) TO postgres; -GRANT ALL ON FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text) TO anon; -GRANT ALL ON FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text) TO authenticated; -GRANT ALL ON FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text) TO service_role; - - --- --- Name: FUNCTION get_financial_summary(p_owner_id uuid, p_year integer, p_month integer); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.get_financial_summary(p_owner_id uuid, p_year integer, p_month integer) TO postgres; -GRANT ALL ON FUNCTION public.get_financial_summary(p_owner_id uuid, p_year integer, p_month integer) TO anon; -GRANT ALL ON FUNCTION public.get_financial_summary(p_owner_id uuid, p_year integer, p_month integer) TO authenticated; -GRANT ALL ON FUNCTION public.get_financial_summary(p_owner_id uuid, p_year integer, p_month integer) TO service_role; - - --- --- Name: FUNCTION get_my_email(); Type: ACL; Schema: public; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION public.get_my_email() FROM PUBLIC; -GRANT ALL ON FUNCTION public.get_my_email() TO postgres; -GRANT ALL ON FUNCTION public.get_my_email() TO anon; -GRANT ALL ON FUNCTION public.get_my_email() TO authenticated; -GRANT ALL ON FUNCTION public.get_my_email() TO service_role; - - --- --- Name: FUNCTION gin_extract_query_trgm(text, internal, smallint, internal, internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gin_extract_query_trgm(text, internal, smallint, internal, internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gin_extract_query_trgm(text, internal, smallint, internal, internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gin_extract_query_trgm(text, internal, smallint, internal, internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gin_extract_query_trgm(text, internal, smallint, internal, internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gin_extract_value_trgm(text, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gin_extract_value_trgm(text, internal) TO postgres; -GRANT ALL ON FUNCTION public.gin_extract_value_trgm(text, internal) TO anon; -GRANT ALL ON FUNCTION public.gin_extract_value_trgm(text, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gin_extract_value_trgm(text, internal) TO service_role; - - --- --- Name: FUNCTION gin_trgm_consistent(internal, smallint, text, integer, internal, internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gin_trgm_consistent(internal, smallint, text, integer, internal, internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gin_trgm_consistent(internal, smallint, text, integer, internal, internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gin_trgm_consistent(internal, smallint, text, integer, internal, internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gin_trgm_consistent(internal, smallint, text, integer, internal, internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gin_trgm_triconsistent(internal, smallint, text, integer, internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gin_trgm_triconsistent(internal, smallint, text, integer, internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gin_trgm_triconsistent(internal, smallint, text, integer, internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gin_trgm_triconsistent(internal, smallint, text, integer, internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gin_trgm_triconsistent(internal, smallint, text, integer, internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gtrgm_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gtrgm_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gtrgm_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gtrgm_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gtrgm_compress(internal) TO service_role; - - --- --- Name: FUNCTION gtrgm_consistent(internal, text, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gtrgm_consistent(internal, text, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gtrgm_consistent(internal, text, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gtrgm_consistent(internal, text, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gtrgm_consistent(internal, text, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gtrgm_decompress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gtrgm_decompress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gtrgm_decompress(internal) TO anon; -GRANT ALL ON FUNCTION public.gtrgm_decompress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gtrgm_decompress(internal) TO service_role; - - --- --- Name: FUNCTION gtrgm_distance(internal, text, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gtrgm_distance(internal, text, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gtrgm_distance(internal, text, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gtrgm_distance(internal, text, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gtrgm_distance(internal, text, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gtrgm_options(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gtrgm_options(internal) TO postgres; -GRANT ALL ON FUNCTION public.gtrgm_options(internal) TO anon; -GRANT ALL ON FUNCTION public.gtrgm_options(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gtrgm_options(internal) TO service_role; - - --- --- Name: FUNCTION gtrgm_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gtrgm_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gtrgm_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gtrgm_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gtrgm_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gtrgm_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gtrgm_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gtrgm_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gtrgm_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gtrgm_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gtrgm_same(public.gtrgm, public.gtrgm, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gtrgm_same(public.gtrgm, public.gtrgm, internal) TO postgres; -GRANT ALL ON FUNCTION public.gtrgm_same(public.gtrgm, public.gtrgm, internal) TO anon; -GRANT ALL ON FUNCTION public.gtrgm_same(public.gtrgm, public.gtrgm, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gtrgm_same(public.gtrgm, public.gtrgm, internal) TO service_role; - - --- --- Name: FUNCTION gtrgm_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gtrgm_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gtrgm_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gtrgm_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gtrgm_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION guard_account_type_immutable(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.guard_account_type_immutable() TO postgres; -GRANT ALL ON FUNCTION public.guard_account_type_immutable() TO anon; -GRANT ALL ON FUNCTION public.guard_account_type_immutable() TO authenticated; -GRANT ALL ON FUNCTION public.guard_account_type_immutable() TO service_role; - - --- --- Name: FUNCTION guard_locked_commitment(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.guard_locked_commitment() TO postgres; -GRANT ALL ON FUNCTION public.guard_locked_commitment() TO anon; -GRANT ALL ON FUNCTION public.guard_locked_commitment() TO authenticated; -GRANT ALL ON FUNCTION public.guard_locked_commitment() TO service_role; - - --- --- Name: FUNCTION guard_no_change_core_plan_key(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.guard_no_change_core_plan_key() TO postgres; -GRANT ALL ON FUNCTION public.guard_no_change_core_plan_key() TO anon; -GRANT ALL ON FUNCTION public.guard_no_change_core_plan_key() TO authenticated; -GRANT ALL ON FUNCTION public.guard_no_change_core_plan_key() TO service_role; - - --- --- Name: FUNCTION guard_no_change_plan_target(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.guard_no_change_plan_target() TO postgres; -GRANT ALL ON FUNCTION public.guard_no_change_plan_target() TO anon; -GRANT ALL ON FUNCTION public.guard_no_change_plan_target() TO authenticated; -GRANT ALL ON FUNCTION public.guard_no_change_plan_target() TO service_role; - - --- --- Name: FUNCTION guard_no_delete_core_plans(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.guard_no_delete_core_plans() TO postgres; -GRANT ALL ON FUNCTION public.guard_no_delete_core_plans() TO anon; -GRANT ALL ON FUNCTION public.guard_no_delete_core_plans() TO authenticated; -GRANT ALL ON FUNCTION public.guard_no_delete_core_plans() TO service_role; - - --- --- Name: FUNCTION guard_patient_cannot_own_tenant(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.guard_patient_cannot_own_tenant() TO postgres; -GRANT ALL ON FUNCTION public.guard_patient_cannot_own_tenant() TO anon; -GRANT ALL ON FUNCTION public.guard_patient_cannot_own_tenant() TO authenticated; -GRANT ALL ON FUNCTION public.guard_patient_cannot_own_tenant() TO service_role; - - --- --- Name: FUNCTION guard_tenant_kind_immutable(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.guard_tenant_kind_immutable() TO postgres; -GRANT ALL ON FUNCTION public.guard_tenant_kind_immutable() TO anon; -GRANT ALL ON FUNCTION public.guard_tenant_kind_immutable() TO authenticated; -GRANT ALL ON FUNCTION public.guard_tenant_kind_immutable() TO service_role; - - --- --- Name: FUNCTION handle_new_user(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.handle_new_user() TO postgres; -GRANT ALL ON FUNCTION public.handle_new_user() TO anon; -GRANT ALL ON FUNCTION public.handle_new_user() TO authenticated; -GRANT ALL ON FUNCTION public.handle_new_user() TO service_role; - - --- --- Name: FUNCTION handle_new_user_create_personal_tenant(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.handle_new_user_create_personal_tenant() TO postgres; -GRANT ALL ON FUNCTION public.handle_new_user_create_personal_tenant() TO anon; -GRANT ALL ON FUNCTION public.handle_new_user_create_personal_tenant() TO authenticated; -GRANT ALL ON FUNCTION public.handle_new_user_create_personal_tenant() TO service_role; - - --- --- Name: FUNCTION has_feature(p_owner_id uuid, p_feature_key text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.has_feature(p_owner_id uuid, p_feature_key text) TO postgres; -GRANT ALL ON FUNCTION public.has_feature(p_owner_id uuid, p_feature_key text) TO anon; -GRANT ALL ON FUNCTION public.has_feature(p_owner_id uuid, p_feature_key text) TO authenticated; -GRANT ALL ON FUNCTION public.has_feature(p_owner_id uuid, p_feature_key text) TO service_role; - - --- --- Name: FUNCTION int2_dist(smallint, smallint); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.int2_dist(smallint, smallint) TO postgres; -GRANT ALL ON FUNCTION public.int2_dist(smallint, smallint) TO anon; -GRANT ALL ON FUNCTION public.int2_dist(smallint, smallint) TO authenticated; -GRANT ALL ON FUNCTION public.int2_dist(smallint, smallint) TO service_role; - - --- --- Name: FUNCTION int4_dist(integer, integer); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.int4_dist(integer, integer) TO postgres; -GRANT ALL ON FUNCTION public.int4_dist(integer, integer) TO anon; -GRANT ALL ON FUNCTION public.int4_dist(integer, integer) TO authenticated; -GRANT ALL ON FUNCTION public.int4_dist(integer, integer) TO service_role; - - --- --- Name: FUNCTION int8_dist(bigint, bigint); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.int8_dist(bigint, bigint) TO postgres; -GRANT ALL ON FUNCTION public.int8_dist(bigint, bigint) TO anon; -GRANT ALL ON FUNCTION public.int8_dist(bigint, bigint) TO authenticated; -GRANT ALL ON FUNCTION public.int8_dist(bigint, bigint) TO service_role; - - --- --- Name: FUNCTION interval_dist(interval, interval); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.interval_dist(interval, interval) TO postgres; -GRANT ALL ON FUNCTION public.interval_dist(interval, interval) TO anon; -GRANT ALL ON FUNCTION public.interval_dist(interval, interval) TO authenticated; -GRANT ALL ON FUNCTION public.interval_dist(interval, interval) TO service_role; - - --- --- Name: FUNCTION is_clinic_tenant(_tenant_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.is_clinic_tenant(_tenant_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.is_clinic_tenant(_tenant_id uuid) TO anon; -GRANT ALL ON FUNCTION public.is_clinic_tenant(_tenant_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.is_clinic_tenant(_tenant_id uuid) TO service_role; - - --- --- Name: FUNCTION is_saas_admin(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.is_saas_admin() TO postgres; -GRANT ALL ON FUNCTION public.is_saas_admin() TO anon; -GRANT ALL ON FUNCTION public.is_saas_admin() TO authenticated; -GRANT ALL ON FUNCTION public.is_saas_admin() TO service_role; - - --- --- Name: FUNCTION is_tenant_admin(p_tenant_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.is_tenant_admin(p_tenant_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.is_tenant_admin(p_tenant_id uuid) TO anon; -GRANT ALL ON FUNCTION public.is_tenant_admin(p_tenant_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.is_tenant_admin(p_tenant_id uuid) TO service_role; - - --- --- Name: FUNCTION is_tenant_member(_tenant_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.is_tenant_member(_tenant_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.is_tenant_member(_tenant_id uuid) TO anon; -GRANT ALL ON FUNCTION public.is_tenant_member(_tenant_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.is_tenant_member(_tenant_id uuid) TO service_role; - - --- --- Name: FUNCTION is_therapist_tenant(_tenant_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.is_therapist_tenant(_tenant_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.is_therapist_tenant(_tenant_id uuid) TO anon; -GRANT ALL ON FUNCTION public.is_therapist_tenant(_tenant_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.is_therapist_tenant(_tenant_id uuid) TO service_role; - - --- --- Name: FUNCTION jwt_email(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.jwt_email() TO postgres; -GRANT ALL ON FUNCTION public.jwt_email() TO anon; -GRANT ALL ON FUNCTION public.jwt_email() TO authenticated; -GRANT ALL ON FUNCTION public.jwt_email() TO service_role; - - --- --- Name: FUNCTION 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); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON 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) TO postgres; -GRANT ALL ON 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) TO anon; -GRANT ALL ON 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) TO authenticated; -GRANT ALL ON 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) TO service_role; - - --- --- Name: FUNCTION mark_as_paid(p_financial_record_id uuid, p_payment_method text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.mark_as_paid(p_financial_record_id uuid, p_payment_method text) TO postgres; -GRANT ALL ON FUNCTION public.mark_as_paid(p_financial_record_id uuid, p_payment_method text) TO anon; -GRANT ALL ON FUNCTION public.mark_as_paid(p_financial_record_id uuid, p_payment_method text) TO authenticated; -GRANT ALL ON FUNCTION public.mark_as_paid(p_financial_record_id uuid, p_payment_method text) TO service_role; - - --- --- Name: FUNCTION mark_payout_as_paid(p_payout_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.mark_payout_as_paid(p_payout_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.mark_payout_as_paid(p_payout_id uuid) TO anon; -GRANT ALL ON FUNCTION public.mark_payout_as_paid(p_payout_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.mark_payout_as_paid(p_payout_id uuid) TO service_role; - - --- --- Name: FUNCTION my_tenants(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.my_tenants() TO postgres; -GRANT ALL ON FUNCTION public.my_tenants() TO anon; -GRANT ALL ON FUNCTION public.my_tenants() TO authenticated; -GRANT ALL ON FUNCTION public.my_tenants() TO service_role; - - --- --- Name: FUNCTION notice_track_click(p_notice_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.notice_track_click(p_notice_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.notice_track_click(p_notice_id uuid) TO anon; -GRANT ALL ON FUNCTION public.notice_track_click(p_notice_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.notice_track_click(p_notice_id uuid) TO service_role; - - --- --- Name: FUNCTION notice_track_view(p_notice_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.notice_track_view(p_notice_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.notice_track_view(p_notice_id uuid) TO anon; -GRANT ALL ON FUNCTION public.notice_track_view(p_notice_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.notice_track_view(p_notice_id uuid) TO service_role; - - --- --- Name: FUNCTION notify_on_intake(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.notify_on_intake() TO postgres; -GRANT ALL ON FUNCTION public.notify_on_intake() TO anon; -GRANT ALL ON FUNCTION public.notify_on_intake() TO authenticated; -GRANT ALL ON FUNCTION public.notify_on_intake() TO service_role; - - --- --- Name: FUNCTION notify_on_scheduling(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.notify_on_scheduling() TO postgres; -GRANT ALL ON FUNCTION public.notify_on_scheduling() TO anon; -GRANT ALL ON FUNCTION public.notify_on_scheduling() TO authenticated; -GRANT ALL ON FUNCTION public.notify_on_scheduling() TO service_role; - - --- --- Name: FUNCTION notify_on_session_status(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.notify_on_session_status() TO postgres; -GRANT ALL ON FUNCTION public.notify_on_session_status() TO anon; -GRANT ALL ON FUNCTION public.notify_on_session_status() TO authenticated; -GRANT ALL ON FUNCTION public.notify_on_session_status() TO service_role; - - --- --- Name: FUNCTION oid_dist(oid, oid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.oid_dist(oid, oid) TO postgres; -GRANT ALL ON FUNCTION public.oid_dist(oid, oid) TO anon; -GRANT ALL ON FUNCTION public.oid_dist(oid, oid) TO authenticated; -GRANT ALL ON FUNCTION public.oid_dist(oid, oid) TO service_role; - - --- --- Name: FUNCTION on_new_user_seed_patient_groups(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.on_new_user_seed_patient_groups() TO postgres; -GRANT ALL ON FUNCTION public.on_new_user_seed_patient_groups() TO anon; -GRANT ALL ON FUNCTION public.on_new_user_seed_patient_groups() TO authenticated; -GRANT ALL ON FUNCTION public.on_new_user_seed_patient_groups() TO service_role; - - --- --- Name: FUNCTION patients_validate_member_consistency(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.patients_validate_member_consistency() TO postgres; -GRANT ALL ON FUNCTION public.patients_validate_member_consistency() TO anon; -GRANT ALL ON FUNCTION public.patients_validate_member_consistency() TO authenticated; -GRANT ALL ON FUNCTION public.patients_validate_member_consistency() TO service_role; - - --- --- Name: FUNCTION patients_validate_responsible_member_tenant(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.patients_validate_responsible_member_tenant() TO postgres; -GRANT ALL ON FUNCTION public.patients_validate_responsible_member_tenant() TO anon; -GRANT ALL ON FUNCTION public.patients_validate_responsible_member_tenant() TO authenticated; -GRANT ALL ON FUNCTION public.patients_validate_responsible_member_tenant() TO service_role; - - --- --- Name: FUNCTION populate_notification_queue(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.populate_notification_queue() TO postgres; -GRANT ALL ON FUNCTION public.populate_notification_queue() TO anon; -GRANT ALL ON FUNCTION public.populate_notification_queue() TO authenticated; -GRANT ALL ON FUNCTION public.populate_notification_queue() TO service_role; - - --- --- Name: FUNCTION prevent_promoting_to_system(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.prevent_promoting_to_system() TO postgres; -GRANT ALL ON FUNCTION public.prevent_promoting_to_system() TO anon; -GRANT ALL ON FUNCTION public.prevent_promoting_to_system() TO authenticated; -GRANT ALL ON FUNCTION public.prevent_promoting_to_system() TO service_role; - - --- --- Name: FUNCTION prevent_saas_membership(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.prevent_saas_membership() TO postgres; -GRANT ALL ON FUNCTION public.prevent_saas_membership() TO anon; -GRANT ALL ON FUNCTION public.prevent_saas_membership() TO authenticated; -GRANT ALL ON FUNCTION public.prevent_saas_membership() TO service_role; - - --- --- Name: FUNCTION prevent_system_group_changes(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.prevent_system_group_changes() TO postgres; -GRANT ALL ON FUNCTION public.prevent_system_group_changes() TO anon; -GRANT ALL ON FUNCTION public.prevent_system_group_changes() TO authenticated; -GRANT ALL ON FUNCTION public.prevent_system_group_changes() TO service_role; - - --- --- Name: FUNCTION provision_account_tenant(p_user_id uuid, p_kind text, p_name text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text) TO postgres; -GRANT ALL ON FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text) TO anon; -GRANT ALL ON FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text) TO authenticated; -GRANT ALL ON FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text) TO service_role; - - --- --- Name: FUNCTION reactivate_subscription(p_subscription_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.reactivate_subscription(p_subscription_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.reactivate_subscription(p_subscription_id uuid) TO anon; -GRANT ALL ON FUNCTION public.reactivate_subscription(p_subscription_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.reactivate_subscription(p_subscription_id uuid) TO service_role; - - --- --- Name: FUNCTION rebuild_owner_entitlements(p_owner_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.rebuild_owner_entitlements(p_owner_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.rebuild_owner_entitlements(p_owner_id uuid) TO anon; -GRANT ALL ON FUNCTION public.rebuild_owner_entitlements(p_owner_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.rebuild_owner_entitlements(p_owner_id uuid) TO service_role; - - --- --- Name: FUNCTION regexp_match(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.regexp_match(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.regexp_match(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.regexp_match(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.regexp_match(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION regexp_match(public.citext, public.citext, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.regexp_match(public.citext, public.citext, text) TO postgres; -GRANT ALL ON FUNCTION public.regexp_match(public.citext, public.citext, text) TO anon; -GRANT ALL ON FUNCTION public.regexp_match(public.citext, public.citext, text) TO authenticated; -GRANT ALL ON FUNCTION public.regexp_match(public.citext, public.citext, text) TO service_role; - - --- --- Name: FUNCTION regexp_matches(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.regexp_matches(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.regexp_matches(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.regexp_matches(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.regexp_matches(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION regexp_matches(public.citext, public.citext, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.regexp_matches(public.citext, public.citext, text) TO postgres; -GRANT ALL ON FUNCTION public.regexp_matches(public.citext, public.citext, text) TO anon; -GRANT ALL ON FUNCTION public.regexp_matches(public.citext, public.citext, text) TO authenticated; -GRANT ALL ON FUNCTION public.regexp_matches(public.citext, public.citext, text) TO service_role; - - --- --- Name: FUNCTION regexp_replace(public.citext, public.citext, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.regexp_replace(public.citext, public.citext, text) TO postgres; -GRANT ALL ON FUNCTION public.regexp_replace(public.citext, public.citext, text) TO anon; -GRANT ALL ON FUNCTION public.regexp_replace(public.citext, public.citext, text) TO authenticated; -GRANT ALL ON FUNCTION public.regexp_replace(public.citext, public.citext, text) TO service_role; - - --- --- Name: FUNCTION regexp_replace(public.citext, public.citext, text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.regexp_replace(public.citext, public.citext, text, text) TO postgres; -GRANT ALL ON FUNCTION public.regexp_replace(public.citext, public.citext, text, text) TO anon; -GRANT ALL ON FUNCTION public.regexp_replace(public.citext, public.citext, text, text) TO authenticated; -GRANT ALL ON FUNCTION public.regexp_replace(public.citext, public.citext, text, text) TO service_role; - - --- --- Name: FUNCTION regexp_split_to_array(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.regexp_split_to_array(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.regexp_split_to_array(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.regexp_split_to_array(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.regexp_split_to_array(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION regexp_split_to_array(public.citext, public.citext, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.regexp_split_to_array(public.citext, public.citext, text) TO postgres; -GRANT ALL ON FUNCTION public.regexp_split_to_array(public.citext, public.citext, text) TO anon; -GRANT ALL ON FUNCTION public.regexp_split_to_array(public.citext, public.citext, text) TO authenticated; -GRANT ALL ON FUNCTION public.regexp_split_to_array(public.citext, public.citext, text) TO service_role; - - --- --- Name: FUNCTION regexp_split_to_table(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.regexp_split_to_table(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.regexp_split_to_table(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.regexp_split_to_table(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.regexp_split_to_table(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION regexp_split_to_table(public.citext, public.citext, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.regexp_split_to_table(public.citext, public.citext, text) TO postgres; -GRANT ALL ON FUNCTION public.regexp_split_to_table(public.citext, public.citext, text) TO anon; -GRANT ALL ON FUNCTION public.regexp_split_to_table(public.citext, public.citext, text) TO authenticated; -GRANT ALL ON FUNCTION public.regexp_split_to_table(public.citext, public.citext, text) TO service_role; - - --- --- Name: FUNCTION replace(public.citext, public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.replace(public.citext, public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.replace(public.citext, public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.replace(public.citext, public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.replace(public.citext, public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION revoke_support_session(p_token text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.revoke_support_session(p_token text) TO postgres; -GRANT ALL ON FUNCTION public.revoke_support_session(p_token text) TO anon; -GRANT ALL ON FUNCTION public.revoke_support_session(p_token text) TO authenticated; -GRANT ALL ON FUNCTION public.revoke_support_session(p_token text) TO service_role; - - --- --- Name: FUNCTION rotate_patient_invite_token(p_new_token text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.rotate_patient_invite_token(p_new_token text) TO postgres; -GRANT ALL ON FUNCTION public.rotate_patient_invite_token(p_new_token text) TO anon; -GRANT ALL ON FUNCTION public.rotate_patient_invite_token(p_new_token text) TO authenticated; -GRANT ALL ON FUNCTION public.rotate_patient_invite_token(p_new_token text) TO service_role; - - --- --- Name: FUNCTION saas_votar_doc(p_doc_id uuid, p_util boolean); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.saas_votar_doc(p_doc_id uuid, p_util boolean) TO postgres; -GRANT ALL ON FUNCTION public.saas_votar_doc(p_doc_id uuid, p_util boolean) TO anon; -GRANT ALL ON FUNCTION public.saas_votar_doc(p_doc_id uuid, p_util boolean) TO authenticated; -GRANT ALL ON FUNCTION public.saas_votar_doc(p_doc_id uuid, p_util boolean) TO service_role; - - --- --- Name: FUNCTION safe_delete_patient(p_patient_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.safe_delete_patient(p_patient_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.safe_delete_patient(p_patient_id uuid) TO anon; -GRANT ALL ON FUNCTION public.safe_delete_patient(p_patient_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.safe_delete_patient(p_patient_id uuid) TO service_role; - - --- --- Name: FUNCTION sanitize_phone_br(raw_phone text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.sanitize_phone_br(raw_phone text) TO postgres; -GRANT ALL ON FUNCTION public.sanitize_phone_br(raw_phone text) TO anon; -GRANT ALL ON FUNCTION public.sanitize_phone_br(raw_phone text) TO authenticated; -GRANT ALL ON FUNCTION public.sanitize_phone_br(raw_phone text) TO service_role; - - --- --- Name: FUNCTION seed_default_financial_categories(p_user_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.seed_default_financial_categories(p_user_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.seed_default_financial_categories(p_user_id uuid) TO anon; -GRANT ALL ON FUNCTION public.seed_default_financial_categories(p_user_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.seed_default_financial_categories(p_user_id uuid) TO service_role; - - --- --- Name: FUNCTION seed_determined_commitments(p_tenant_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.seed_determined_commitments(p_tenant_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.seed_determined_commitments(p_tenant_id uuid) TO anon; -GRANT ALL ON FUNCTION public.seed_determined_commitments(p_tenant_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.seed_determined_commitments(p_tenant_id uuid) TO service_role; - - --- --- Name: FUNCTION set_insurance_plans_updated_at(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.set_insurance_plans_updated_at() TO postgres; -GRANT ALL ON FUNCTION public.set_insurance_plans_updated_at() TO anon; -GRANT ALL ON FUNCTION public.set_insurance_plans_updated_at() TO authenticated; -GRANT ALL ON FUNCTION public.set_insurance_plans_updated_at() TO service_role; - - --- --- Name: FUNCTION set_limit(real); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.set_limit(real) TO postgres; -GRANT ALL ON FUNCTION public.set_limit(real) TO anon; -GRANT ALL ON FUNCTION public.set_limit(real) TO authenticated; -GRANT ALL ON FUNCTION public.set_limit(real) TO service_role; - - --- --- Name: FUNCTION set_owner_id(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.set_owner_id() TO postgres; -GRANT ALL ON FUNCTION public.set_owner_id() TO anon; -GRANT ALL ON FUNCTION public.set_owner_id() TO authenticated; -GRANT ALL ON FUNCTION public.set_owner_id() TO service_role; - - --- --- Name: FUNCTION set_services_updated_at(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.set_services_updated_at() TO postgres; -GRANT ALL ON FUNCTION public.set_services_updated_at() TO anon; -GRANT ALL ON FUNCTION public.set_services_updated_at() TO authenticated; -GRANT ALL ON FUNCTION public.set_services_updated_at() TO service_role; - - --- --- Name: FUNCTION set_tenant_feature_exception(p_tenant_id uuid, p_feature_key text, p_enabled boolean, p_reason text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.set_tenant_feature_exception(p_tenant_id uuid, p_feature_key text, p_enabled boolean, p_reason text) TO postgres; -GRANT ALL ON FUNCTION public.set_tenant_feature_exception(p_tenant_id uuid, p_feature_key text, p_enabled boolean, p_reason text) TO anon; -GRANT ALL ON FUNCTION public.set_tenant_feature_exception(p_tenant_id uuid, p_feature_key text, p_enabled boolean, p_reason text) TO authenticated; -GRANT ALL ON FUNCTION public.set_tenant_feature_exception(p_tenant_id uuid, p_feature_key text, p_enabled boolean, p_reason text) TO service_role; - - --- --- Name: FUNCTION set_updated_at(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.set_updated_at() TO postgres; -GRANT ALL ON FUNCTION public.set_updated_at() TO anon; -GRANT ALL ON FUNCTION public.set_updated_at() TO authenticated; -GRANT ALL ON FUNCTION public.set_updated_at() TO service_role; - - --- --- Name: FUNCTION set_updated_at_recurrence(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.set_updated_at_recurrence() TO postgres; -GRANT ALL ON FUNCTION public.set_updated_at_recurrence() TO anon; -GRANT ALL ON FUNCTION public.set_updated_at_recurrence() TO authenticated; -GRANT ALL ON FUNCTION public.set_updated_at_recurrence() TO service_role; - - --- --- Name: FUNCTION show_limit(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.show_limit() TO postgres; -GRANT ALL ON FUNCTION public.show_limit() TO anon; -GRANT ALL ON FUNCTION public.show_limit() TO authenticated; -GRANT ALL ON FUNCTION public.show_limit() TO service_role; - - --- --- Name: FUNCTION show_trgm(text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.show_trgm(text) TO postgres; -GRANT ALL ON FUNCTION public.show_trgm(text) TO anon; -GRANT ALL ON FUNCTION public.show_trgm(text) TO authenticated; -GRANT ALL ON FUNCTION public.show_trgm(text) TO service_role; - - --- --- Name: FUNCTION similarity(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.similarity(text, text) TO postgres; -GRANT ALL ON FUNCTION public.similarity(text, text) TO anon; -GRANT ALL ON FUNCTION public.similarity(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.similarity(text, text) TO service_role; - - --- --- Name: FUNCTION similarity_dist(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.similarity_dist(text, text) TO postgres; -GRANT ALL ON FUNCTION public.similarity_dist(text, text) TO anon; -GRANT ALL ON FUNCTION public.similarity_dist(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.similarity_dist(text, text) TO service_role; - - --- --- Name: FUNCTION similarity_op(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.similarity_op(text, text) TO postgres; -GRANT ALL ON FUNCTION public.similarity_op(text, text) TO anon; -GRANT ALL ON FUNCTION public.similarity_op(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.similarity_op(text, text) TO service_role; - - --- --- Name: FUNCTION split_part(public.citext, public.citext, integer); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.split_part(public.citext, public.citext, integer) TO postgres; -GRANT ALL ON FUNCTION public.split_part(public.citext, public.citext, integer) TO anon; -GRANT ALL ON FUNCTION public.split_part(public.citext, public.citext, integer) TO authenticated; -GRANT ALL ON FUNCTION public.split_part(public.citext, public.citext, integer) TO service_role; - - --- --- Name: FUNCTION split_recurrence_at(p_recurrence_id uuid, p_from_date date); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.split_recurrence_at(p_recurrence_id uuid, p_from_date date) TO postgres; -GRANT ALL ON FUNCTION public.split_recurrence_at(p_recurrence_id uuid, p_from_date date) TO anon; -GRANT ALL ON FUNCTION public.split_recurrence_at(p_recurrence_id uuid, p_from_date date) TO authenticated; -GRANT ALL ON FUNCTION public.split_recurrence_at(p_recurrence_id uuid, p_from_date date) TO service_role; - - --- --- Name: FUNCTION strict_word_similarity(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.strict_word_similarity(text, text) TO postgres; -GRANT ALL ON FUNCTION public.strict_word_similarity(text, text) TO anon; -GRANT ALL ON FUNCTION public.strict_word_similarity(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.strict_word_similarity(text, text) TO service_role; - - --- --- Name: FUNCTION strict_word_similarity_commutator_op(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.strict_word_similarity_commutator_op(text, text) TO postgres; -GRANT ALL ON FUNCTION public.strict_word_similarity_commutator_op(text, text) TO anon; -GRANT ALL ON FUNCTION public.strict_word_similarity_commutator_op(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.strict_word_similarity_commutator_op(text, text) TO service_role; - - --- --- Name: FUNCTION strict_word_similarity_dist_commutator_op(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.strict_word_similarity_dist_commutator_op(text, text) TO postgres; -GRANT ALL ON FUNCTION public.strict_word_similarity_dist_commutator_op(text, text) TO anon; -GRANT ALL ON FUNCTION public.strict_word_similarity_dist_commutator_op(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.strict_word_similarity_dist_commutator_op(text, text) TO service_role; - - --- --- Name: FUNCTION strict_word_similarity_dist_op(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.strict_word_similarity_dist_op(text, text) TO postgres; -GRANT ALL ON FUNCTION public.strict_word_similarity_dist_op(text, text) TO anon; -GRANT ALL ON FUNCTION public.strict_word_similarity_dist_op(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.strict_word_similarity_dist_op(text, text) TO service_role; - - --- --- Name: FUNCTION strict_word_similarity_op(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.strict_word_similarity_op(text, text) TO postgres; -GRANT ALL ON FUNCTION public.strict_word_similarity_op(text, text) TO anon; -GRANT ALL ON FUNCTION public.strict_word_similarity_op(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.strict_word_similarity_op(text, text) TO service_role; - - --- --- Name: FUNCTION strpos(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.strpos(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.strpos(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.strpos(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.strpos(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION subscription_intents_view_insert(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.subscription_intents_view_insert() TO postgres; -GRANT ALL ON FUNCTION public.subscription_intents_view_insert() TO anon; -GRANT ALL ON FUNCTION public.subscription_intents_view_insert() TO authenticated; -GRANT ALL ON FUNCTION public.subscription_intents_view_insert() TO service_role; - - --- --- Name: FUNCTION subscriptions_validate_scope(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.subscriptions_validate_scope() TO postgres; -GRANT ALL ON FUNCTION public.subscriptions_validate_scope() TO anon; -GRANT ALL ON FUNCTION public.subscriptions_validate_scope() TO authenticated; -GRANT ALL ON FUNCTION public.subscriptions_validate_scope() TO service_role; - - --- --- Name: FUNCTION sync_busy_mirror_agenda_eventos(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.sync_busy_mirror_agenda_eventos() TO postgres; -GRANT ALL ON FUNCTION public.sync_busy_mirror_agenda_eventos() TO anon; -GRANT ALL ON FUNCTION public.sync_busy_mirror_agenda_eventos() TO authenticated; -GRANT ALL ON FUNCTION public.sync_busy_mirror_agenda_eventos() TO service_role; - - --- --- Name: FUNCTION sync_overdue_financial_records(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.sync_overdue_financial_records() TO postgres; -GRANT ALL ON FUNCTION public.sync_overdue_financial_records() TO anon; -GRANT ALL ON FUNCTION public.sync_overdue_financial_records() TO authenticated; -GRANT ALL ON FUNCTION public.sync_overdue_financial_records() TO service_role; - - --- --- Name: FUNCTION tenant_accept_invite(p_token uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_accept_invite(p_token uuid) TO postgres; -GRANT ALL ON FUNCTION public.tenant_accept_invite(p_token uuid) TO anon; -GRANT ALL ON FUNCTION public.tenant_accept_invite(p_token uuid) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_accept_invite(p_token uuid) TO service_role; - - --- --- Name: TABLE tenant_members; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.tenant_members TO postgres; -GRANT SELECT,REFERENCES,TRIGGER,TRUNCATE,MAINTAIN ON TABLE public.tenant_members TO authenticated; -GRANT ALL ON TABLE public.tenant_members TO service_role; - - --- --- Name: FUNCTION tenant_add_member_by_email(p_tenant_id uuid, p_email text, p_role text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_add_member_by_email(p_tenant_id uuid, p_email text, p_role text) TO postgres; -GRANT ALL ON FUNCTION public.tenant_add_member_by_email(p_tenant_id uuid, p_email text, p_role text) TO anon; -GRANT ALL ON FUNCTION public.tenant_add_member_by_email(p_tenant_id uuid, p_email text, p_role text) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_add_member_by_email(p_tenant_id uuid, p_email text, p_role text) TO service_role; - - --- --- Name: FUNCTION tenant_feature_allowed(p_tenant_id uuid, p_feature_key text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_feature_allowed(p_tenant_id uuid, p_feature_key text) TO postgres; -GRANT ALL ON FUNCTION public.tenant_feature_allowed(p_tenant_id uuid, p_feature_key text) TO anon; -GRANT ALL ON FUNCTION public.tenant_feature_allowed(p_tenant_id uuid, p_feature_key text) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_feature_allowed(p_tenant_id uuid, p_feature_key text) TO service_role; - - --- --- Name: FUNCTION tenant_feature_enabled(p_tenant_id uuid, p_feature_key text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_feature_enabled(p_tenant_id uuid, p_feature_key text) TO postgres; -GRANT ALL ON FUNCTION public.tenant_feature_enabled(p_tenant_id uuid, p_feature_key text) TO anon; -GRANT ALL ON FUNCTION public.tenant_feature_enabled(p_tenant_id uuid, p_feature_key text) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_feature_enabled(p_tenant_id uuid, p_feature_key text) TO service_role; - - --- --- Name: FUNCTION tenant_features_guard_with_plan(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_features_guard_with_plan() TO postgres; -GRANT ALL ON FUNCTION public.tenant_features_guard_with_plan() TO anon; -GRANT ALL ON FUNCTION public.tenant_features_guard_with_plan() TO authenticated; -GRANT ALL ON FUNCTION public.tenant_features_guard_with_plan() TO service_role; - - --- --- Name: FUNCTION tenant_has_feature(_tenant_id uuid, _feature text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) TO postgres; -GRANT ALL ON FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) TO anon; -GRANT ALL ON FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) TO service_role; - - --- --- Name: FUNCTION tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text); Type: ACL; Schema: public; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) FROM PUBLIC; -GRANT ALL ON FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) TO postgres; -GRANT ALL ON FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) TO anon; -GRANT ALL ON FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) TO service_role; - - --- --- Name: FUNCTION tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid) TO anon; -GRANT ALL ON FUNCTION public.tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid) TO service_role; - - --- --- Name: FUNCTION tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid) TO anon; -GRANT ALL ON FUNCTION public.tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid) TO service_role; - - --- --- Name: FUNCTION tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid) TO anon; -GRANT ALL ON FUNCTION public.tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid) TO service_role; - - --- --- Name: FUNCTION tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text) TO postgres; -GRANT ALL ON FUNCTION public.tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text) TO anon; -GRANT ALL ON FUNCTION public.tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text) TO service_role; - - --- --- Name: FUNCTION tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text) TO postgres; -GRANT ALL ON FUNCTION public.tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text) TO anon; -GRANT ALL ON FUNCTION public.tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text) TO service_role; - - --- --- Name: FUNCTION tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text) TO postgres; -GRANT ALL ON FUNCTION public.tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text) TO anon; -GRANT ALL ON FUNCTION public.tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text) TO service_role; - - --- --- Name: FUNCTION texticlike(public.citext, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.texticlike(public.citext, text) TO postgres; -GRANT ALL ON FUNCTION public.texticlike(public.citext, text) TO anon; -GRANT ALL ON FUNCTION public.texticlike(public.citext, text) TO authenticated; -GRANT ALL ON FUNCTION public.texticlike(public.citext, text) TO service_role; - - --- --- Name: FUNCTION texticlike(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.texticlike(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.texticlike(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.texticlike(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.texticlike(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION texticnlike(public.citext, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.texticnlike(public.citext, text) TO postgres; -GRANT ALL ON FUNCTION public.texticnlike(public.citext, text) TO anon; -GRANT ALL ON FUNCTION public.texticnlike(public.citext, text) TO authenticated; -GRANT ALL ON FUNCTION public.texticnlike(public.citext, text) TO service_role; - - --- --- Name: FUNCTION texticnlike(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.texticnlike(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.texticnlike(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.texticnlike(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.texticnlike(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION texticregexeq(public.citext, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.texticregexeq(public.citext, text) TO postgres; -GRANT ALL ON FUNCTION public.texticregexeq(public.citext, text) TO anon; -GRANT ALL ON FUNCTION public.texticregexeq(public.citext, text) TO authenticated; -GRANT ALL ON FUNCTION public.texticregexeq(public.citext, text) TO service_role; - - --- --- Name: FUNCTION texticregexeq(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.texticregexeq(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.texticregexeq(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.texticregexeq(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.texticregexeq(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION texticregexne(public.citext, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.texticregexne(public.citext, text) TO postgres; -GRANT ALL ON FUNCTION public.texticregexne(public.citext, text) TO anon; -GRANT ALL ON FUNCTION public.texticregexne(public.citext, text) TO authenticated; -GRANT ALL ON FUNCTION public.texticregexne(public.citext, text) TO service_role; - - --- --- Name: FUNCTION texticregexne(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.texticregexne(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.texticregexne(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.texticregexne(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.texticregexne(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION time_dist(time without time zone, time without time zone); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.time_dist(time without time zone, time without time zone) TO postgres; -GRANT ALL ON FUNCTION public.time_dist(time without time zone, time without time zone) TO anon; -GRANT ALL ON FUNCTION public.time_dist(time without time zone, time without time zone) TO authenticated; -GRANT ALL ON FUNCTION public.time_dist(time without time zone, time without time zone) TO service_role; - - --- --- Name: FUNCTION toggle_plan(owner uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.toggle_plan(owner uuid) TO postgres; -GRANT ALL ON FUNCTION public.toggle_plan(owner uuid) TO anon; -GRANT ALL ON FUNCTION public.toggle_plan(owner uuid) TO authenticated; -GRANT ALL ON FUNCTION public.toggle_plan(owner uuid) TO service_role; - - --- --- Name: FUNCTION transition_subscription(p_subscription_id uuid, p_to_status text, p_reason text, p_metadata jsonb); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.transition_subscription(p_subscription_id uuid, p_to_status text, p_reason text, p_metadata jsonb) TO postgres; -GRANT ALL ON FUNCTION public.transition_subscription(p_subscription_id uuid, p_to_status text, p_reason text, p_metadata jsonb) TO anon; -GRANT ALL ON FUNCTION public.transition_subscription(p_subscription_id uuid, p_to_status text, p_reason text, p_metadata jsonb) TO authenticated; -GRANT ALL ON FUNCTION public.transition_subscription(p_subscription_id uuid, p_to_status text, p_reason text, p_metadata jsonb) TO service_role; - - --- --- Name: FUNCTION translate(public.citext, public.citext, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.translate(public.citext, public.citext, text) TO postgres; -GRANT ALL ON FUNCTION public.translate(public.citext, public.citext, text) TO anon; -GRANT ALL ON FUNCTION public.translate(public.citext, public.citext, text) TO authenticated; -GRANT ALL ON FUNCTION public.translate(public.citext, public.citext, text) TO service_role; - - --- --- Name: FUNCTION trg_fn_financial_records_auto_overdue(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.trg_fn_financial_records_auto_overdue() TO postgres; -GRANT ALL ON FUNCTION public.trg_fn_financial_records_auto_overdue() TO anon; -GRANT ALL ON FUNCTION public.trg_fn_financial_records_auto_overdue() TO authenticated; -GRANT ALL ON FUNCTION public.trg_fn_financial_records_auto_overdue() TO service_role; - - --- --- Name: FUNCTION ts_dist(timestamp without time zone, timestamp without time zone); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.ts_dist(timestamp without time zone, timestamp without time zone) TO postgres; -GRANT ALL ON FUNCTION public.ts_dist(timestamp without time zone, timestamp without time zone) TO anon; -GRANT ALL ON FUNCTION public.ts_dist(timestamp without time zone, timestamp without time zone) TO authenticated; -GRANT ALL ON FUNCTION public.ts_dist(timestamp without time zone, timestamp without time zone) TO service_role; - - --- --- Name: FUNCTION tstz_dist(timestamp with time zone, timestamp with time zone); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tstz_dist(timestamp with time zone, timestamp with time zone) TO postgres; -GRANT ALL ON FUNCTION public.tstz_dist(timestamp with time zone, timestamp with time zone) TO anon; -GRANT ALL ON FUNCTION public.tstz_dist(timestamp with time zone, timestamp with time zone) TO authenticated; -GRANT ALL ON FUNCTION public.tstz_dist(timestamp with time zone, timestamp with time zone) TO service_role; - - --- --- Name: FUNCTION unstick_notification_queue(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.unstick_notification_queue() TO postgres; -GRANT ALL ON FUNCTION public.unstick_notification_queue() TO anon; -GRANT ALL ON FUNCTION public.unstick_notification_queue() TO authenticated; -GRANT ALL ON FUNCTION public.unstick_notification_queue() TO service_role; - - --- --- Name: FUNCTION update_payment_settings_updated_at(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.update_payment_settings_updated_at() TO postgres; -GRANT ALL ON FUNCTION public.update_payment_settings_updated_at() TO anon; -GRANT ALL ON FUNCTION public.update_payment_settings_updated_at() TO authenticated; -GRANT ALL ON FUNCTION public.update_payment_settings_updated_at() TO service_role; - - --- --- Name: FUNCTION update_professional_pricing_updated_at(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.update_professional_pricing_updated_at() TO postgres; -GRANT ALL ON FUNCTION public.update_professional_pricing_updated_at() TO anon; -GRANT ALL ON FUNCTION public.update_professional_pricing_updated_at() TO authenticated; -GRANT ALL ON FUNCTION public.update_professional_pricing_updated_at() TO service_role; - - --- --- Name: FUNCTION user_has_feature(_user_id uuid, _feature text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.user_has_feature(_user_id uuid, _feature text) TO postgres; -GRANT ALL ON FUNCTION public.user_has_feature(_user_id uuid, _feature text) TO anon; -GRANT ALL ON FUNCTION public.user_has_feature(_user_id uuid, _feature text) TO authenticated; -GRANT ALL ON FUNCTION public.user_has_feature(_user_id uuid, _feature text) TO service_role; - - --- --- Name: FUNCTION validate_support_session(p_token text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.validate_support_session(p_token text) TO postgres; -GRANT ALL ON FUNCTION public.validate_support_session(p_token text) TO anon; -GRANT ALL ON FUNCTION public.validate_support_session(p_token text) TO authenticated; -GRANT ALL ON FUNCTION public.validate_support_session(p_token text) TO service_role; - - --- --- Name: FUNCTION whoami(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.whoami() TO postgres; -GRANT ALL ON FUNCTION public.whoami() TO anon; -GRANT ALL ON FUNCTION public.whoami() TO authenticated; -GRANT ALL ON FUNCTION public.whoami() TO service_role; - - --- --- Name: FUNCTION word_similarity(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.word_similarity(text, text) TO postgres; -GRANT ALL ON FUNCTION public.word_similarity(text, text) TO anon; -GRANT ALL ON FUNCTION public.word_similarity(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.word_similarity(text, text) TO service_role; - - --- --- Name: FUNCTION word_similarity_commutator_op(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.word_similarity_commutator_op(text, text) TO postgres; -GRANT ALL ON FUNCTION public.word_similarity_commutator_op(text, text) TO anon; -GRANT ALL ON FUNCTION public.word_similarity_commutator_op(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.word_similarity_commutator_op(text, text) TO service_role; - - --- --- Name: FUNCTION word_similarity_dist_commutator_op(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.word_similarity_dist_commutator_op(text, text) TO postgres; -GRANT ALL ON FUNCTION public.word_similarity_dist_commutator_op(text, text) TO anon; -GRANT ALL ON FUNCTION public.word_similarity_dist_commutator_op(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.word_similarity_dist_commutator_op(text, text) TO service_role; - - --- --- Name: FUNCTION word_similarity_dist_op(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.word_similarity_dist_op(text, text) TO postgres; -GRANT ALL ON FUNCTION public.word_similarity_dist_op(text, text) TO anon; -GRANT ALL ON FUNCTION public.word_similarity_dist_op(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.word_similarity_dist_op(text, text) TO service_role; - - --- --- Name: FUNCTION word_similarity_op(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.word_similarity_op(text, text) TO postgres; -GRANT ALL ON FUNCTION public.word_similarity_op(text, text) TO anon; -GRANT ALL ON FUNCTION public.word_similarity_op(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.word_similarity_op(text, text) TO service_role; - - --- --- Name: FUNCTION apply_rls(wal jsonb, max_record_bytes integer); Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION realtime.apply_rls(wal jsonb, max_record_bytes integer) TO postgres; -GRANT ALL ON FUNCTION realtime.apply_rls(wal jsonb, max_record_bytes integer) TO dashboard_user; -GRANT ALL ON FUNCTION realtime.apply_rls(wal jsonb, max_record_bytes integer) TO anon; -GRANT ALL ON FUNCTION realtime.apply_rls(wal jsonb, max_record_bytes integer) TO authenticated; -GRANT ALL ON FUNCTION realtime.apply_rls(wal jsonb, max_record_bytes integer) TO service_role; -GRANT ALL ON FUNCTION realtime.apply_rls(wal jsonb, max_record_bytes integer) TO supabase_realtime_admin; - - --- --- Name: FUNCTION broadcast_changes(topic_name text, event_name text, operation text, table_name text, table_schema text, new record, old record, level text); Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION realtime.broadcast_changes(topic_name text, event_name text, operation text, table_name text, table_schema text, new record, old record, level text) TO postgres; -GRANT ALL ON FUNCTION realtime.broadcast_changes(topic_name text, event_name text, operation text, table_name text, table_schema text, new record, old record, level text) TO dashboard_user; - - --- --- Name: FUNCTION build_prepared_statement_sql(prepared_statement_name text, entity regclass, columns realtime.wal_column[]); Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION realtime.build_prepared_statement_sql(prepared_statement_name text, entity regclass, columns realtime.wal_column[]) TO postgres; -GRANT ALL ON FUNCTION realtime.build_prepared_statement_sql(prepared_statement_name text, entity regclass, columns realtime.wal_column[]) TO dashboard_user; -GRANT ALL ON FUNCTION realtime.build_prepared_statement_sql(prepared_statement_name text, entity regclass, columns realtime.wal_column[]) TO anon; -GRANT ALL ON FUNCTION realtime.build_prepared_statement_sql(prepared_statement_name text, entity regclass, columns realtime.wal_column[]) TO authenticated; -GRANT ALL ON FUNCTION realtime.build_prepared_statement_sql(prepared_statement_name text, entity regclass, columns realtime.wal_column[]) TO service_role; -GRANT ALL ON FUNCTION realtime.build_prepared_statement_sql(prepared_statement_name text, entity regclass, columns realtime.wal_column[]) TO supabase_realtime_admin; - - --- --- Name: FUNCTION "cast"(val text, type_ regtype); Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION realtime."cast"(val text, type_ regtype) TO postgres; -GRANT ALL ON FUNCTION realtime."cast"(val text, type_ regtype) TO dashboard_user; -GRANT ALL ON FUNCTION realtime."cast"(val text, type_ regtype) TO anon; -GRANT ALL ON FUNCTION realtime."cast"(val text, type_ regtype) TO authenticated; -GRANT ALL ON FUNCTION realtime."cast"(val text, type_ regtype) TO service_role; -GRANT ALL ON FUNCTION realtime."cast"(val text, type_ regtype) TO supabase_realtime_admin; - - --- --- Name: FUNCTION check_equality_op(op realtime.equality_op, type_ regtype, val_1 text, val_2 text); Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION realtime.check_equality_op(op realtime.equality_op, type_ regtype, val_1 text, val_2 text) TO postgres; -GRANT ALL ON FUNCTION realtime.check_equality_op(op realtime.equality_op, type_ regtype, val_1 text, val_2 text) TO dashboard_user; -GRANT ALL ON FUNCTION realtime.check_equality_op(op realtime.equality_op, type_ regtype, val_1 text, val_2 text) TO anon; -GRANT ALL ON FUNCTION realtime.check_equality_op(op realtime.equality_op, type_ regtype, val_1 text, val_2 text) TO authenticated; -GRANT ALL ON FUNCTION realtime.check_equality_op(op realtime.equality_op, type_ regtype, val_1 text, val_2 text) TO service_role; -GRANT ALL ON FUNCTION realtime.check_equality_op(op realtime.equality_op, type_ regtype, val_1 text, val_2 text) TO supabase_realtime_admin; - - --- --- Name: FUNCTION is_visible_through_filters(columns realtime.wal_column[], filters realtime.user_defined_filter[]); Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION realtime.is_visible_through_filters(columns realtime.wal_column[], filters realtime.user_defined_filter[]) TO postgres; -GRANT ALL ON FUNCTION realtime.is_visible_through_filters(columns realtime.wal_column[], filters realtime.user_defined_filter[]) TO dashboard_user; -GRANT ALL ON FUNCTION realtime.is_visible_through_filters(columns realtime.wal_column[], filters realtime.user_defined_filter[]) TO anon; -GRANT ALL ON FUNCTION realtime.is_visible_through_filters(columns realtime.wal_column[], filters realtime.user_defined_filter[]) TO authenticated; -GRANT ALL ON FUNCTION realtime.is_visible_through_filters(columns realtime.wal_column[], filters realtime.user_defined_filter[]) TO service_role; -GRANT ALL ON FUNCTION realtime.is_visible_through_filters(columns realtime.wal_column[], filters realtime.user_defined_filter[]) TO supabase_realtime_admin; - - --- --- Name: FUNCTION list_changes(publication name, slot_name name, max_changes integer, max_record_bytes integer); Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION realtime.list_changes(publication name, slot_name name, max_changes integer, max_record_bytes integer) TO postgres; -GRANT ALL ON FUNCTION realtime.list_changes(publication name, slot_name name, max_changes integer, max_record_bytes integer) TO dashboard_user; -GRANT ALL ON FUNCTION realtime.list_changes(publication name, slot_name name, max_changes integer, max_record_bytes integer) TO anon; -GRANT ALL ON FUNCTION realtime.list_changes(publication name, slot_name name, max_changes integer, max_record_bytes integer) TO authenticated; -GRANT ALL ON FUNCTION realtime.list_changes(publication name, slot_name name, max_changes integer, max_record_bytes integer) TO service_role; -GRANT ALL ON FUNCTION realtime.list_changes(publication name, slot_name name, max_changes integer, max_record_bytes integer) TO supabase_realtime_admin; - - --- --- Name: FUNCTION quote_wal2json(entity regclass); Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION realtime.quote_wal2json(entity regclass) TO postgres; -GRANT ALL ON FUNCTION realtime.quote_wal2json(entity regclass) TO dashboard_user; -GRANT ALL ON FUNCTION realtime.quote_wal2json(entity regclass) TO anon; -GRANT ALL ON FUNCTION realtime.quote_wal2json(entity regclass) TO authenticated; -GRANT ALL ON FUNCTION realtime.quote_wal2json(entity regclass) TO service_role; -GRANT ALL ON FUNCTION realtime.quote_wal2json(entity regclass) TO supabase_realtime_admin; - - --- --- Name: FUNCTION send(payload jsonb, event text, topic text, private boolean); Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION realtime.send(payload jsonb, event text, topic text, private boolean) TO postgres; -GRANT ALL ON FUNCTION realtime.send(payload jsonb, event text, topic text, private boolean) TO dashboard_user; - - --- --- Name: FUNCTION subscription_check_filters(); Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION realtime.subscription_check_filters() TO postgres; -GRANT ALL ON FUNCTION realtime.subscription_check_filters() TO dashboard_user; -GRANT ALL ON FUNCTION realtime.subscription_check_filters() TO anon; -GRANT ALL ON FUNCTION realtime.subscription_check_filters() TO authenticated; -GRANT ALL ON FUNCTION realtime.subscription_check_filters() TO service_role; -GRANT ALL ON FUNCTION realtime.subscription_check_filters() TO supabase_realtime_admin; - - --- --- Name: FUNCTION to_regrole(role_name text); Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION realtime.to_regrole(role_name text) TO postgres; -GRANT ALL ON FUNCTION realtime.to_regrole(role_name text) TO dashboard_user; -GRANT ALL ON FUNCTION realtime.to_regrole(role_name text) TO anon; -GRANT ALL ON FUNCTION realtime.to_regrole(role_name text) TO authenticated; -GRANT ALL ON FUNCTION realtime.to_regrole(role_name text) TO service_role; -GRANT ALL ON FUNCTION realtime.to_regrole(role_name text) TO supabase_realtime_admin; - - --- --- Name: FUNCTION topic(); Type: ACL; Schema: realtime; Owner: supabase_realtime_admin --- - -GRANT ALL ON FUNCTION realtime.topic() TO postgres; -GRANT ALL ON FUNCTION realtime.topic() TO dashboard_user; - - --- --- Name: FUNCTION http_request(); Type: ACL; Schema: supabase_functions; Owner: supabase_functions_admin --- - -REVOKE ALL ON FUNCTION supabase_functions.http_request() FROM PUBLIC; -GRANT ALL ON FUNCTION supabase_functions.http_request() TO postgres; -GRANT ALL ON FUNCTION supabase_functions.http_request() TO anon; -GRANT ALL ON FUNCTION supabase_functions.http_request() TO authenticated; -GRANT ALL ON FUNCTION supabase_functions.http_request() TO service_role; - - --- --- Name: FUNCTION _crypto_aead_det_decrypt(message bytea, additional bytea, key_id bigint, context bytea, nonce bytea); Type: ACL; Schema: vault; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION vault._crypto_aead_det_decrypt(message bytea, additional bytea, key_id bigint, context bytea, nonce bytea) TO postgres WITH GRANT OPTION; -GRANT ALL ON FUNCTION vault._crypto_aead_det_decrypt(message bytea, additional bytea, key_id bigint, context bytea, nonce bytea) TO service_role; - - --- --- Name: FUNCTION create_secret(new_secret text, new_name text, new_description text, new_key_id uuid); Type: ACL; Schema: vault; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION vault.create_secret(new_secret text, new_name text, new_description text, new_key_id uuid) TO postgres WITH GRANT OPTION; -GRANT ALL ON FUNCTION vault.create_secret(new_secret text, new_name text, new_description text, new_key_id uuid) TO service_role; - - --- --- Name: FUNCTION update_secret(secret_id uuid, new_secret text, new_name text, new_description text, new_key_id uuid); Type: ACL; Schema: vault; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION vault.update_secret(secret_id uuid, new_secret text, new_name text, new_description text, new_key_id uuid) TO postgres WITH GRANT OPTION; -GRANT ALL ON FUNCTION vault.update_secret(secret_id uuid, new_secret text, new_name text, new_description text, new_key_id uuid) TO service_role; - - --- --- Name: FUNCTION max(public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.max(public.citext) TO postgres; -GRANT ALL ON FUNCTION public.max(public.citext) TO anon; -GRANT ALL ON FUNCTION public.max(public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.max(public.citext) TO service_role; - - --- --- Name: FUNCTION min(public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.min(public.citext) TO postgres; -GRANT ALL ON FUNCTION public.min(public.citext) TO anon; -GRANT ALL ON FUNCTION public.min(public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.min(public.citext) TO service_role; - - --- --- Name: TABLE audit_log_entries; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.audit_log_entries TO dashboard_user; -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.audit_log_entries TO postgres; -GRANT SELECT ON TABLE auth.audit_log_entries TO postgres WITH GRANT OPTION; - - --- --- Name: TABLE flow_state; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.flow_state TO postgres; -GRANT SELECT ON TABLE auth.flow_state TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE auth.flow_state TO dashboard_user; - - --- --- Name: TABLE identities; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.identities TO postgres; -GRANT SELECT ON TABLE auth.identities TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE auth.identities TO dashboard_user; - - --- --- Name: TABLE instances; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.instances TO dashboard_user; -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.instances TO postgres; -GRANT SELECT ON TABLE auth.instances TO postgres WITH GRANT OPTION; - - --- --- Name: TABLE mfa_amr_claims; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.mfa_amr_claims TO postgres; -GRANT SELECT ON TABLE auth.mfa_amr_claims TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE auth.mfa_amr_claims TO dashboard_user; - - --- --- Name: TABLE mfa_challenges; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.mfa_challenges TO postgres; -GRANT SELECT ON TABLE auth.mfa_challenges TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE auth.mfa_challenges TO dashboard_user; - - --- --- Name: TABLE mfa_factors; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.mfa_factors TO postgres; -GRANT SELECT ON TABLE auth.mfa_factors TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE auth.mfa_factors TO dashboard_user; - - --- --- Name: TABLE oauth_authorizations; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.oauth_authorizations TO postgres; -GRANT ALL ON TABLE auth.oauth_authorizations TO dashboard_user; - - --- --- Name: TABLE oauth_client_states; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.oauth_client_states TO postgres; -GRANT ALL ON TABLE auth.oauth_client_states TO dashboard_user; - - --- --- Name: TABLE oauth_clients; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.oauth_clients TO postgres; -GRANT ALL ON TABLE auth.oauth_clients TO dashboard_user; - - --- --- Name: TABLE oauth_consents; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.oauth_consents TO postgres; -GRANT ALL ON TABLE auth.oauth_consents TO dashboard_user; - - --- --- Name: TABLE one_time_tokens; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.one_time_tokens TO postgres; -GRANT SELECT ON TABLE auth.one_time_tokens TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE auth.one_time_tokens TO dashboard_user; - - --- --- Name: TABLE refresh_tokens; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.refresh_tokens TO dashboard_user; -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.refresh_tokens TO postgres; -GRANT SELECT ON TABLE auth.refresh_tokens TO postgres WITH GRANT OPTION; - - --- --- Name: SEQUENCE refresh_tokens_id_seq; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON SEQUENCE auth.refresh_tokens_id_seq TO dashboard_user; -GRANT ALL ON SEQUENCE auth.refresh_tokens_id_seq TO postgres; - - --- --- Name: TABLE saml_providers; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.saml_providers TO postgres; -GRANT SELECT ON TABLE auth.saml_providers TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE auth.saml_providers TO dashboard_user; - - --- --- Name: TABLE saml_relay_states; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.saml_relay_states TO postgres; -GRANT SELECT ON TABLE auth.saml_relay_states TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE auth.saml_relay_states TO dashboard_user; - - --- --- Name: TABLE schema_migrations; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT SELECT ON TABLE auth.schema_migrations TO postgres WITH GRANT OPTION; - - --- --- Name: TABLE sessions; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.sessions TO postgres; -GRANT SELECT ON TABLE auth.sessions TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE auth.sessions TO dashboard_user; - - --- --- Name: TABLE sso_domains; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.sso_domains TO postgres; -GRANT SELECT ON TABLE auth.sso_domains TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE auth.sso_domains TO dashboard_user; - - --- --- Name: TABLE sso_providers; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.sso_providers TO postgres; -GRANT SELECT ON TABLE auth.sso_providers TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE auth.sso_providers TO dashboard_user; - - --- --- Name: TABLE users; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.users TO dashboard_user; -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.users TO postgres; -GRANT SELECT ON TABLE auth.users TO postgres WITH GRANT OPTION; - - --- --- Name: TABLE job; Type: ACL; Schema: cron; Owner: supabase_admin --- - -GRANT SELECT ON TABLE cron.job TO postgres WITH GRANT OPTION; - - --- --- Name: TABLE job_run_details; Type: ACL; Schema: cron; Owner: supabase_admin --- - -GRANT ALL ON TABLE cron.job_run_details TO postgres WITH GRANT OPTION; - - --- --- Name: TABLE pg_stat_statements; Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON TABLE extensions.pg_stat_statements TO postgres WITH GRANT OPTION; - - --- --- Name: TABLE pg_stat_statements_info; Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON TABLE extensions.pg_stat_statements_info TO postgres WITH GRANT OPTION; - - --- --- Name: TABLE addon_credits; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.addon_credits TO postgres; -GRANT ALL ON TABLE public.addon_credits TO anon; -GRANT ALL ON TABLE public.addon_credits TO authenticated; -GRANT ALL ON TABLE public.addon_credits TO service_role; - - --- --- Name: TABLE addon_products; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.addon_products TO postgres; -GRANT ALL ON TABLE public.addon_products TO anon; -GRANT ALL ON TABLE public.addon_products TO authenticated; -GRANT ALL ON TABLE public.addon_products TO service_role; - - --- --- Name: TABLE addon_transactions; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.addon_transactions TO postgres; -GRANT ALL ON TABLE public.addon_transactions TO anon; -GRANT ALL ON TABLE public.addon_transactions TO authenticated; -GRANT ALL ON TABLE public.addon_transactions TO service_role; - - --- --- Name: TABLE agenda_bloqueios; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.agenda_bloqueios TO postgres; -GRANT ALL ON TABLE public.agenda_bloqueios TO anon; -GRANT ALL ON TABLE public.agenda_bloqueios TO authenticated; -GRANT ALL ON TABLE public.agenda_bloqueios TO service_role; - - --- --- Name: TABLE agenda_configuracoes; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.agenda_configuracoes TO postgres; -GRANT ALL ON TABLE public.agenda_configuracoes TO anon; -GRANT ALL ON TABLE public.agenda_configuracoes TO authenticated; -GRANT ALL ON TABLE public.agenda_configuracoes TO service_role; - - --- --- Name: TABLE agenda_eventos; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.agenda_eventos TO postgres; -GRANT ALL ON TABLE public.agenda_eventos TO anon; -GRANT ALL ON TABLE public.agenda_eventos TO authenticated; -GRANT ALL ON TABLE public.agenda_eventos TO service_role; - - --- --- Name: TABLE agenda_excecoes; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.agenda_excecoes TO postgres; -GRANT ALL ON TABLE public.agenda_excecoes TO anon; -GRANT ALL ON TABLE public.agenda_excecoes TO authenticated; -GRANT ALL ON TABLE public.agenda_excecoes TO service_role; - - --- --- Name: TABLE agenda_online_slots; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.agenda_online_slots TO postgres; -GRANT ALL ON TABLE public.agenda_online_slots TO anon; -GRANT ALL ON TABLE public.agenda_online_slots TO authenticated; -GRANT ALL ON TABLE public.agenda_online_slots TO service_role; - - --- --- Name: SEQUENCE agenda_online_slots_id_seq; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON SEQUENCE public.agenda_online_slots_id_seq TO postgres; -GRANT ALL ON SEQUENCE public.agenda_online_slots_id_seq TO anon; -GRANT ALL ON SEQUENCE public.agenda_online_slots_id_seq TO authenticated; -GRANT ALL ON SEQUENCE public.agenda_online_slots_id_seq TO service_role; - - --- --- Name: TABLE agenda_regras_semanais; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.agenda_regras_semanais TO postgres; -GRANT ALL ON TABLE public.agenda_regras_semanais TO anon; -GRANT ALL ON TABLE public.agenda_regras_semanais TO authenticated; -GRANT ALL ON TABLE public.agenda_regras_semanais TO service_role; - - --- --- Name: TABLE agenda_slots_bloqueados_semanais; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.agenda_slots_bloqueados_semanais TO postgres; -GRANT ALL ON TABLE public.agenda_slots_bloqueados_semanais TO anon; -GRANT ALL ON TABLE public.agenda_slots_bloqueados_semanais TO authenticated; -GRANT ALL ON TABLE public.agenda_slots_bloqueados_semanais TO service_role; - - --- --- Name: TABLE agenda_slots_regras; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.agenda_slots_regras TO postgres; -GRANT ALL ON TABLE public.agenda_slots_regras TO anon; -GRANT ALL ON TABLE public.agenda_slots_regras TO authenticated; -GRANT ALL ON TABLE public.agenda_slots_regras TO service_role; - - --- --- Name: TABLE agendador_configuracoes; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.agendador_configuracoes TO postgres; -GRANT ALL ON TABLE public.agendador_configuracoes TO anon; -GRANT ALL ON TABLE public.agendador_configuracoes TO authenticated; -GRANT ALL ON TABLE public.agendador_configuracoes TO service_role; - - --- --- Name: TABLE agendador_solicitacoes; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.agendador_solicitacoes TO postgres; -GRANT ALL ON TABLE public.agendador_solicitacoes TO anon; -GRANT ALL ON TABLE public.agendador_solicitacoes TO authenticated; -GRANT ALL ON TABLE public.agendador_solicitacoes TO service_role; - - --- --- Name: TABLE billing_contracts; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.billing_contracts TO postgres; -GRANT ALL ON TABLE public.billing_contracts TO anon; -GRANT ALL ON TABLE public.billing_contracts TO authenticated; -GRANT ALL ON TABLE public.billing_contracts TO service_role; - - --- --- Name: TABLE commitment_services; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.commitment_services TO postgres; -GRANT ALL ON TABLE public.commitment_services TO anon; -GRANT ALL ON TABLE public.commitment_services TO authenticated; -GRANT ALL ON TABLE public.commitment_services TO service_role; - - --- --- Name: TABLE commitment_time_logs; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.commitment_time_logs TO postgres; -GRANT ALL ON TABLE public.commitment_time_logs TO anon; -GRANT ALL ON TABLE public.commitment_time_logs TO authenticated; -GRANT ALL ON TABLE public.commitment_time_logs TO service_role; - - --- --- Name: TABLE company_profiles; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.company_profiles TO postgres; -GRANT ALL ON TABLE public.company_profiles TO anon; -GRANT ALL ON TABLE public.company_profiles TO authenticated; -GRANT ALL ON TABLE public.company_profiles TO service_role; - - --- --- Name: TABLE current_tenant_id; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.current_tenant_id TO postgres; -GRANT ALL ON TABLE public.current_tenant_id TO anon; -GRANT ALL ON TABLE public.current_tenant_id TO authenticated; -GRANT ALL ON TABLE public.current_tenant_id TO service_role; - - --- --- Name: TABLE determined_commitment_fields; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.determined_commitment_fields TO postgres; -GRANT ALL ON TABLE public.determined_commitment_fields TO anon; -GRANT ALL ON TABLE public.determined_commitment_fields TO authenticated; -GRANT ALL ON TABLE public.determined_commitment_fields TO service_role; - - --- --- Name: TABLE determined_commitments; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.determined_commitments TO postgres; -GRANT ALL ON TABLE public.determined_commitments TO anon; -GRANT ALL ON TABLE public.determined_commitments TO authenticated; -GRANT ALL ON TABLE public.determined_commitments TO service_role; - - --- --- Name: TABLE dev_user_credentials; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.dev_user_credentials TO postgres; -GRANT ALL ON TABLE public.dev_user_credentials TO anon; -GRANT ALL ON TABLE public.dev_user_credentials TO authenticated; -GRANT ALL ON TABLE public.dev_user_credentials TO service_role; - - --- --- Name: TABLE email_layout_config; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.email_layout_config TO postgres; -GRANT ALL ON TABLE public.email_layout_config TO anon; -GRANT ALL ON TABLE public.email_layout_config TO authenticated; -GRANT ALL ON TABLE public.email_layout_config TO service_role; - - --- --- Name: TABLE email_templates_global; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.email_templates_global TO postgres; -GRANT ALL ON TABLE public.email_templates_global TO anon; -GRANT ALL ON TABLE public.email_templates_global TO authenticated; -GRANT ALL ON TABLE public.email_templates_global TO service_role; - - --- --- Name: TABLE email_templates_tenant; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.email_templates_tenant TO postgres; -GRANT ALL ON TABLE public.email_templates_tenant TO anon; -GRANT ALL ON TABLE public.email_templates_tenant TO authenticated; -GRANT ALL ON TABLE public.email_templates_tenant TO service_role; - - --- --- Name: TABLE entitlements_invalidation; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.entitlements_invalidation TO postgres; -GRANT ALL ON TABLE public.entitlements_invalidation TO anon; -GRANT ALL ON TABLE public.entitlements_invalidation TO authenticated; -GRANT ALL ON TABLE public.entitlements_invalidation TO service_role; - - --- --- Name: TABLE features; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.features TO postgres; -GRANT ALL ON TABLE public.features TO anon; -GRANT ALL ON TABLE public.features TO authenticated; -GRANT ALL ON TABLE public.features TO service_role; - - --- --- Name: TABLE feriados; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.feriados TO postgres; -GRANT ALL ON TABLE public.feriados TO anon; -GRANT ALL ON TABLE public.feriados TO authenticated; -GRANT ALL ON TABLE public.feriados TO service_role; - - --- --- Name: TABLE financial_categories; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.financial_categories TO postgres; -GRANT ALL ON TABLE public.financial_categories TO anon; -GRANT ALL ON TABLE public.financial_categories TO authenticated; -GRANT ALL ON TABLE public.financial_categories TO service_role; - - --- --- Name: TABLE financial_exceptions; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.financial_exceptions TO postgres; -GRANT ALL ON TABLE public.financial_exceptions TO anon; -GRANT ALL ON TABLE public.financial_exceptions TO authenticated; -GRANT ALL ON TABLE public.financial_exceptions TO service_role; - - --- --- Name: TABLE global_notices; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.global_notices TO postgres; -GRANT ALL ON TABLE public.global_notices TO anon; -GRANT ALL ON TABLE public.global_notices TO authenticated; -GRANT ALL ON TABLE public.global_notices TO service_role; - - --- --- Name: TABLE insurance_plan_services; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.insurance_plan_services TO postgres; -GRANT ALL ON TABLE public.insurance_plan_services TO anon; -GRANT ALL ON TABLE public.insurance_plan_services TO authenticated; -GRANT ALL ON TABLE public.insurance_plan_services TO service_role; - - --- --- Name: TABLE insurance_plans; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.insurance_plans TO postgres; -GRANT ALL ON TABLE public.insurance_plans TO anon; -GRANT ALL ON TABLE public.insurance_plans TO authenticated; -GRANT ALL ON TABLE public.insurance_plans TO service_role; - - --- --- Name: TABLE login_carousel_slides; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.login_carousel_slides TO postgres; -GRANT ALL ON TABLE public.login_carousel_slides TO anon; -GRANT ALL ON TABLE public.login_carousel_slides TO authenticated; -GRANT ALL ON TABLE public.login_carousel_slides TO service_role; - - --- --- Name: TABLE module_features; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.module_features TO postgres; -GRANT ALL ON TABLE public.module_features TO anon; -GRANT ALL ON TABLE public.module_features TO authenticated; -GRANT ALL ON TABLE public.module_features TO service_role; - - --- --- Name: TABLE modules; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.modules TO postgres; -GRANT ALL ON TABLE public.modules TO anon; -GRANT ALL ON TABLE public.modules TO authenticated; -GRANT ALL ON TABLE public.modules TO service_role; - - --- --- Name: TABLE notice_dismissals; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.notice_dismissals TO postgres; -GRANT ALL ON TABLE public.notice_dismissals TO anon; -GRANT ALL ON TABLE public.notice_dismissals TO authenticated; -GRANT ALL ON TABLE public.notice_dismissals TO service_role; - - --- --- Name: TABLE notification_channels; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.notification_channels TO postgres; -GRANT ALL ON TABLE public.notification_channels TO anon; -GRANT ALL ON TABLE public.notification_channels TO authenticated; -GRANT ALL ON TABLE public.notification_channels TO service_role; - - --- --- Name: TABLE notification_logs; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.notification_logs TO postgres; -GRANT ALL ON TABLE public.notification_logs TO anon; -GRANT ALL ON TABLE public.notification_logs TO authenticated; -GRANT ALL ON TABLE public.notification_logs TO service_role; - - --- --- Name: TABLE notification_preferences; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.notification_preferences TO postgres; -GRANT ALL ON TABLE public.notification_preferences TO anon; -GRANT ALL ON TABLE public.notification_preferences TO authenticated; -GRANT ALL ON TABLE public.notification_preferences TO service_role; - - --- --- Name: TABLE notification_queue; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.notification_queue TO postgres; -GRANT ALL ON TABLE public.notification_queue TO anon; -GRANT ALL ON TABLE public.notification_queue TO authenticated; -GRANT ALL ON TABLE public.notification_queue TO service_role; - - --- --- Name: TABLE notification_schedules; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.notification_schedules TO postgres; -GRANT ALL ON TABLE public.notification_schedules TO anon; -GRANT ALL ON TABLE public.notification_schedules TO authenticated; -GRANT ALL ON TABLE public.notification_schedules TO service_role; - - --- --- Name: TABLE notification_templates; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.notification_templates TO postgres; -GRANT ALL ON TABLE public.notification_templates TO anon; -GRANT ALL ON TABLE public.notification_templates TO authenticated; -GRANT ALL ON TABLE public.notification_templates TO service_role; - - --- --- Name: TABLE notifications; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.notifications TO postgres; -GRANT ALL ON TABLE public.notifications TO anon; -GRANT ALL ON TABLE public.notifications TO authenticated; -GRANT ALL ON TABLE public.notifications TO service_role; - - --- --- Name: TABLE plan_features; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.plan_features TO postgres; -GRANT ALL ON TABLE public.plan_features TO anon; -GRANT ALL ON TABLE public.plan_features TO authenticated; -GRANT ALL ON TABLE public.plan_features TO service_role; - - --- --- Name: TABLE tenant_modules; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.tenant_modules TO postgres; -GRANT ALL ON TABLE public.tenant_modules TO anon; -GRANT ALL ON TABLE public.tenant_modules TO authenticated; -GRANT ALL ON TABLE public.tenant_modules TO service_role; - - --- --- Name: TABLE owner_feature_entitlements; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.owner_feature_entitlements TO postgres; -GRANT ALL ON TABLE public.owner_feature_entitlements TO anon; -GRANT ALL ON TABLE public.owner_feature_entitlements TO authenticated; -GRANT ALL ON TABLE public.owner_feature_entitlements TO service_role; - - --- --- Name: TABLE owner_users; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.owner_users TO postgres; -GRANT ALL ON TABLE public.owner_users TO anon; -GRANT ALL ON TABLE public.owner_users TO authenticated; -GRANT ALL ON TABLE public.owner_users TO service_role; - - --- --- Name: TABLE patient_discounts; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.patient_discounts TO postgres; -GRANT ALL ON TABLE public.patient_discounts TO anon; -GRANT ALL ON TABLE public.patient_discounts TO authenticated; -GRANT ALL ON TABLE public.patient_discounts TO service_role; - - --- --- Name: TABLE patient_group_patient; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.patient_group_patient TO postgres; -GRANT ALL ON TABLE public.patient_group_patient TO anon; -GRANT ALL ON TABLE public.patient_group_patient TO authenticated; -GRANT ALL ON TABLE public.patient_group_patient TO service_role; - - --- --- Name: TABLE patient_groups; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.patient_groups TO postgres; -GRANT ALL ON TABLE public.patient_groups TO anon; -GRANT ALL ON TABLE public.patient_groups TO authenticated; -GRANT ALL ON TABLE public.patient_groups TO service_role; - - --- --- Name: TABLE patient_intake_requests; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.patient_intake_requests TO postgres; -GRANT ALL ON TABLE public.patient_intake_requests TO authenticated; -GRANT ALL ON TABLE public.patient_intake_requests TO service_role; - - --- --- Name: TABLE patient_invites; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.patient_invites TO postgres; -GRANT ALL ON TABLE public.patient_invites TO authenticated; -GRANT ALL ON TABLE public.patient_invites TO service_role; - - --- --- Name: TABLE patient_patient_tag; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.patient_patient_tag TO postgres; -GRANT ALL ON TABLE public.patient_patient_tag TO anon; -GRANT ALL ON TABLE public.patient_patient_tag TO authenticated; -GRANT ALL ON TABLE public.patient_patient_tag TO service_role; - - --- --- Name: TABLE patient_tags; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.patient_tags TO postgres; -GRANT ALL ON TABLE public.patient_tags TO anon; -GRANT ALL ON TABLE public.patient_tags TO authenticated; -GRANT ALL ON TABLE public.patient_tags TO service_role; - - --- --- Name: TABLE patients; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.patients TO postgres; -GRANT ALL ON TABLE public.patients TO anon; -GRANT ALL ON TABLE public.patients TO authenticated; -GRANT ALL ON TABLE public.patients TO service_role; - - --- --- Name: TABLE payment_settings; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.payment_settings TO postgres; -GRANT ALL ON TABLE public.payment_settings TO anon; -GRANT ALL ON TABLE public.payment_settings TO authenticated; -GRANT ALL ON TABLE public.payment_settings TO service_role; - - --- --- Name: TABLE plan_prices; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.plan_prices TO postgres; -GRANT ALL ON TABLE public.plan_prices TO anon; -GRANT ALL ON TABLE public.plan_prices TO authenticated; -GRANT ALL ON TABLE public.plan_prices TO service_role; - - --- --- Name: TABLE plan_public; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.plan_public TO postgres; -GRANT ALL ON TABLE public.plan_public TO anon; -GRANT ALL ON TABLE public.plan_public TO authenticated; -GRANT ALL ON TABLE public.plan_public TO service_role; - - --- --- Name: TABLE plan_public_bullets; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.plan_public_bullets TO postgres; -GRANT ALL ON TABLE public.plan_public_bullets TO anon; -GRANT ALL ON TABLE public.plan_public_bullets TO authenticated; -GRANT ALL ON TABLE public.plan_public_bullets TO service_role; - - --- --- Name: TABLE plans; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.plans TO postgres; -GRANT ALL ON TABLE public.plans TO anon; -GRANT ALL ON TABLE public.plans TO authenticated; -GRANT ALL ON TABLE public.plans TO service_role; - - --- --- Name: TABLE professional_pricing; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.professional_pricing TO postgres; -GRANT ALL ON TABLE public.professional_pricing TO anon; -GRANT ALL ON TABLE public.professional_pricing TO authenticated; -GRANT ALL ON TABLE public.professional_pricing TO service_role; - - --- --- Name: TABLE profiles; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.profiles TO postgres; -GRANT ALL ON TABLE public.profiles TO anon; -GRANT ALL ON TABLE public.profiles TO authenticated; -GRANT ALL ON TABLE public.profiles TO service_role; - - --- --- Name: TABLE recurrence_exceptions; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.recurrence_exceptions TO postgres; -GRANT ALL ON TABLE public.recurrence_exceptions TO anon; -GRANT ALL ON TABLE public.recurrence_exceptions TO authenticated; -GRANT ALL ON TABLE public.recurrence_exceptions TO service_role; - - --- --- Name: TABLE recurrence_rule_services; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.recurrence_rule_services TO postgres; -GRANT ALL ON TABLE public.recurrence_rule_services TO anon; -GRANT ALL ON TABLE public.recurrence_rule_services TO authenticated; -GRANT ALL ON TABLE public.recurrence_rule_services TO service_role; - - --- --- Name: TABLE recurrence_rules; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.recurrence_rules TO postgres; -GRANT ALL ON TABLE public.recurrence_rules TO anon; -GRANT ALL ON TABLE public.recurrence_rules TO authenticated; -GRANT ALL ON TABLE public.recurrence_rules TO service_role; - - --- --- Name: TABLE saas_admins; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.saas_admins TO postgres; -GRANT ALL ON TABLE public.saas_admins TO anon; -GRANT ALL ON TABLE public.saas_admins TO authenticated; -GRANT ALL ON TABLE public.saas_admins TO service_role; - - --- --- Name: TABLE saas_doc_votos; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.saas_doc_votos TO postgres; -GRANT ALL ON TABLE public.saas_doc_votos TO anon; -GRANT ALL ON TABLE public.saas_doc_votos TO authenticated; -GRANT ALL ON TABLE public.saas_doc_votos TO service_role; - - --- --- Name: TABLE saas_docs; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.saas_docs TO postgres; -GRANT ALL ON TABLE public.saas_docs TO anon; -GRANT ALL ON TABLE public.saas_docs TO authenticated; -GRANT ALL ON TABLE public.saas_docs TO service_role; - - --- --- Name: TABLE saas_faq; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.saas_faq TO postgres; -GRANT ALL ON TABLE public.saas_faq TO anon; -GRANT ALL ON TABLE public.saas_faq TO authenticated; -GRANT ALL ON TABLE public.saas_faq TO service_role; - - --- --- Name: TABLE saas_faq_itens; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.saas_faq_itens TO postgres; -GRANT ALL ON TABLE public.saas_faq_itens TO anon; -GRANT ALL ON TABLE public.saas_faq_itens TO authenticated; -GRANT ALL ON TABLE public.saas_faq_itens TO service_role; - - --- --- Name: TABLE services; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.services TO postgres; -GRANT ALL ON TABLE public.services TO anon; -GRANT ALL ON TABLE public.services TO authenticated; -GRANT ALL ON TABLE public.services TO service_role; - - --- --- Name: TABLE subscription_events; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.subscription_events TO postgres; -GRANT ALL ON TABLE public.subscription_events TO anon; -GRANT ALL ON TABLE public.subscription_events TO authenticated; -GRANT ALL ON TABLE public.subscription_events TO service_role; - - --- --- Name: TABLE subscription_intents_personal; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.subscription_intents_personal TO postgres; -GRANT ALL ON TABLE public.subscription_intents_personal TO anon; -GRANT ALL ON TABLE public.subscription_intents_personal TO authenticated; -GRANT ALL ON TABLE public.subscription_intents_personal TO service_role; - - --- --- Name: TABLE subscription_intents_tenant; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.subscription_intents_tenant TO postgres; -GRANT ALL ON TABLE public.subscription_intents_tenant TO anon; -GRANT ALL ON TABLE public.subscription_intents_tenant TO authenticated; -GRANT ALL ON TABLE public.subscription_intents_tenant TO service_role; - - --- --- Name: TABLE subscription_intents; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.subscription_intents TO postgres; -GRANT ALL ON TABLE public.subscription_intents TO anon; -GRANT ALL ON TABLE public.subscription_intents TO authenticated; -GRANT ALL ON TABLE public.subscription_intents TO service_role; - - --- --- Name: TABLE subscription_intents_legacy; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.subscription_intents_legacy TO postgres; -GRANT ALL ON TABLE public.subscription_intents_legacy TO anon; -GRANT ALL ON TABLE public.subscription_intents_legacy TO authenticated; -GRANT ALL ON TABLE public.subscription_intents_legacy TO service_role; - - --- --- Name: TABLE support_sessions; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.support_sessions TO postgres; -GRANT ALL ON TABLE public.support_sessions TO anon; -GRANT ALL ON TABLE public.support_sessions TO authenticated; -GRANT ALL ON TABLE public.support_sessions TO service_role; - - --- --- Name: TABLE tenant_feature_exceptions_log; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.tenant_feature_exceptions_log TO postgres; -GRANT ALL ON TABLE public.tenant_feature_exceptions_log TO anon; -GRANT ALL ON TABLE public.tenant_feature_exceptions_log TO authenticated; -GRANT ALL ON TABLE public.tenant_feature_exceptions_log TO service_role; - - --- --- Name: TABLE tenant_features; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.tenant_features TO postgres; -GRANT ALL ON TABLE public.tenant_features TO anon; -GRANT ALL ON TABLE public.tenant_features TO authenticated; -GRANT ALL ON TABLE public.tenant_features TO service_role; - - --- --- Name: TABLE tenant_invites; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.tenant_invites TO postgres; -GRANT ALL ON TABLE public.tenant_invites TO authenticated; -GRANT ALL ON TABLE public.tenant_invites TO service_role; - - --- --- Name: TABLE tenants; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.tenants TO postgres; -GRANT ALL ON TABLE public.tenants TO anon; -GRANT ALL ON TABLE public.tenants TO authenticated; -GRANT ALL ON TABLE public.tenants TO service_role; - - --- --- Name: TABLE therapist_payout_records; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.therapist_payout_records TO postgres; -GRANT ALL ON TABLE public.therapist_payout_records TO anon; -GRANT ALL ON TABLE public.therapist_payout_records TO authenticated; -GRANT ALL ON TABLE public.therapist_payout_records TO service_role; - - --- --- Name: TABLE user_settings; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.user_settings TO postgres; -GRANT ALL ON TABLE public.user_settings TO anon; -GRANT ALL ON TABLE public.user_settings TO authenticated; -GRANT ALL ON TABLE public.user_settings TO service_role; - - --- --- Name: TABLE v_auth_users_public; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_auth_users_public TO postgres; -GRANT ALL ON TABLE public.v_auth_users_public TO anon; -GRANT ALL ON TABLE public.v_auth_users_public TO authenticated; -GRANT ALL ON TABLE public.v_auth_users_public TO service_role; - - --- --- Name: TABLE v_cashflow_projection; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_cashflow_projection TO postgres; -GRANT ALL ON TABLE public.v_cashflow_projection TO anon; -GRANT ALL ON TABLE public.v_cashflow_projection TO authenticated; -GRANT ALL ON TABLE public.v_cashflow_projection TO service_role; - - --- --- Name: TABLE v_commitment_totals; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_commitment_totals TO postgres; -GRANT ALL ON TABLE public.v_commitment_totals TO anon; -GRANT ALL ON TABLE public.v_commitment_totals TO authenticated; -GRANT ALL ON TABLE public.v_commitment_totals TO service_role; - - --- --- Name: TABLE v_patient_groups_with_counts; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_patient_groups_with_counts TO postgres; -GRANT ALL ON TABLE public.v_patient_groups_with_counts TO anon; -GRANT ALL ON TABLE public.v_patient_groups_with_counts TO authenticated; -GRANT ALL ON TABLE public.v_patient_groups_with_counts TO service_role; - - --- --- Name: TABLE v_plan_active_prices; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_plan_active_prices TO postgres; -GRANT ALL ON TABLE public.v_plan_active_prices TO anon; -GRANT ALL ON TABLE public.v_plan_active_prices TO authenticated; -GRANT ALL ON TABLE public.v_plan_active_prices TO service_role; - - --- --- Name: TABLE v_public_pricing; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_public_pricing TO postgres; -GRANT ALL ON TABLE public.v_public_pricing TO anon; -GRANT ALL ON TABLE public.v_public_pricing TO authenticated; -GRANT ALL ON TABLE public.v_public_pricing TO service_role; - - --- --- Name: TABLE v_subscription_feature_mismatch; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_subscription_feature_mismatch TO postgres; -GRANT ALL ON TABLE public.v_subscription_feature_mismatch TO anon; -GRANT ALL ON TABLE public.v_subscription_feature_mismatch TO authenticated; -GRANT ALL ON TABLE public.v_subscription_feature_mismatch TO service_role; - - --- --- Name: TABLE v_subscription_health; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_subscription_health TO postgres; -GRANT ALL ON TABLE public.v_subscription_health TO anon; -GRANT ALL ON TABLE public.v_subscription_health TO authenticated; -GRANT ALL ON TABLE public.v_subscription_health TO service_role; - - --- --- Name: TABLE v_subscription_health_v2; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_subscription_health_v2 TO postgres; -GRANT ALL ON TABLE public.v_subscription_health_v2 TO anon; -GRANT ALL ON TABLE public.v_subscription_health_v2 TO authenticated; -GRANT ALL ON TABLE public.v_subscription_health_v2 TO service_role; - - --- --- Name: TABLE v_tag_patient_counts; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_tag_patient_counts TO postgres; -GRANT ALL ON TABLE public.v_tag_patient_counts TO anon; -GRANT ALL ON TABLE public.v_tag_patient_counts TO authenticated; -GRANT ALL ON TABLE public.v_tag_patient_counts TO service_role; - - --- --- Name: TABLE v_tenant_active_subscription; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_tenant_active_subscription TO postgres; -GRANT ALL ON TABLE public.v_tenant_active_subscription TO anon; -GRANT ALL ON TABLE public.v_tenant_active_subscription TO authenticated; -GRANT ALL ON TABLE public.v_tenant_active_subscription TO service_role; - - --- --- Name: TABLE v_tenant_entitlements; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_tenant_entitlements TO postgres; -GRANT ALL ON TABLE public.v_tenant_entitlements TO anon; -GRANT ALL ON TABLE public.v_tenant_entitlements TO authenticated; -GRANT ALL ON TABLE public.v_tenant_entitlements TO service_role; - - --- --- Name: TABLE v_tenant_entitlements_full; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_tenant_entitlements_full TO postgres; -GRANT ALL ON TABLE public.v_tenant_entitlements_full TO anon; -GRANT ALL ON TABLE public.v_tenant_entitlements_full TO authenticated; -GRANT ALL ON TABLE public.v_tenant_entitlements_full TO service_role; - - --- --- Name: TABLE v_tenant_entitlements_json; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_tenant_entitlements_json TO postgres; -GRANT ALL ON TABLE public.v_tenant_entitlements_json TO anon; -GRANT ALL ON TABLE public.v_tenant_entitlements_json TO authenticated; -GRANT ALL ON TABLE public.v_tenant_entitlements_json TO service_role; - - --- --- Name: TABLE v_tenant_feature_exceptions; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_tenant_feature_exceptions TO postgres; -GRANT ALL ON TABLE public.v_tenant_feature_exceptions TO anon; -GRANT ALL ON TABLE public.v_tenant_feature_exceptions TO authenticated; -GRANT ALL ON TABLE public.v_tenant_feature_exceptions TO service_role; - - --- --- Name: TABLE v_tenant_feature_mismatch; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_tenant_feature_mismatch TO postgres; -GRANT ALL ON TABLE public.v_tenant_feature_mismatch TO anon; -GRANT ALL ON TABLE public.v_tenant_feature_mismatch TO authenticated; -GRANT ALL ON TABLE public.v_tenant_feature_mismatch TO service_role; - - --- --- Name: TABLE v_tenant_members_with_profiles; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_tenant_members_with_profiles TO postgres; -GRANT ALL ON TABLE public.v_tenant_members_with_profiles TO anon; -GRANT ALL ON TABLE public.v_tenant_members_with_profiles TO authenticated; -GRANT ALL ON TABLE public.v_tenant_members_with_profiles TO service_role; - - --- --- Name: TABLE v_tenant_people; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_tenant_people TO postgres; -GRANT ALL ON TABLE public.v_tenant_people TO anon; -GRANT ALL ON TABLE public.v_tenant_people TO authenticated; -GRANT ALL ON TABLE public.v_tenant_people TO service_role; - - --- --- Name: TABLE v_tenant_staff; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_tenant_staff TO postgres; -GRANT ALL ON TABLE public.v_tenant_staff TO anon; -GRANT ALL ON TABLE public.v_tenant_staff TO authenticated; -GRANT ALL ON TABLE public.v_tenant_staff TO service_role; - - --- --- Name: TABLE v_user_active_subscription; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_user_active_subscription TO postgres; -GRANT ALL ON TABLE public.v_user_active_subscription TO anon; -GRANT ALL ON TABLE public.v_user_active_subscription TO authenticated; -GRANT ALL ON TABLE public.v_user_active_subscription TO service_role; - - --- --- Name: TABLE v_user_entitlements; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_user_entitlements TO postgres; -GRANT ALL ON TABLE public.v_user_entitlements TO anon; -GRANT ALL ON TABLE public.v_user_entitlements TO authenticated; -GRANT ALL ON TABLE public.v_user_entitlements TO service_role; - - --- --- Name: TABLE messages; Type: ACL; Schema: realtime; Owner: supabase_realtime_admin --- - -GRANT ALL ON TABLE realtime.messages TO postgres; -GRANT ALL ON TABLE realtime.messages TO dashboard_user; -GRANT SELECT,INSERT,UPDATE ON TABLE realtime.messages TO anon; -GRANT SELECT,INSERT,UPDATE ON TABLE realtime.messages TO authenticated; -GRANT SELECT,INSERT,UPDATE ON TABLE realtime.messages TO service_role; - - --- --- Name: TABLE messages_2026_03_20; Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON TABLE realtime.messages_2026_03_20 TO postgres; -GRANT ALL ON TABLE realtime.messages_2026_03_20 TO dashboard_user; - - --- --- Name: TABLE messages_2026_03_21; Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON TABLE realtime.messages_2026_03_21 TO postgres; -GRANT ALL ON TABLE realtime.messages_2026_03_21 TO dashboard_user; - - --- --- Name: TABLE messages_2026_03_22; Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON TABLE realtime.messages_2026_03_22 TO postgres; -GRANT ALL ON TABLE realtime.messages_2026_03_22 TO dashboard_user; - - --- --- Name: TABLE messages_2026_03_23; Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON TABLE realtime.messages_2026_03_23 TO postgres; -GRANT ALL ON TABLE realtime.messages_2026_03_23 TO dashboard_user; - - --- --- Name: TABLE messages_2026_03_24; Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON TABLE realtime.messages_2026_03_24 TO postgres; -GRANT ALL ON TABLE realtime.messages_2026_03_24 TO dashboard_user; - - --- --- Name: TABLE messages_2026_03_25; Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON TABLE realtime.messages_2026_03_25 TO postgres; -GRANT ALL ON TABLE realtime.messages_2026_03_25 TO dashboard_user; - - --- --- Name: TABLE messages_2026_03_26; Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON TABLE realtime.messages_2026_03_26 TO postgres; -GRANT ALL ON TABLE realtime.messages_2026_03_26 TO dashboard_user; - - --- --- Name: TABLE schema_migrations; Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON TABLE realtime.schema_migrations TO postgres; -GRANT ALL ON TABLE realtime.schema_migrations TO dashboard_user; -GRANT SELECT ON TABLE realtime.schema_migrations TO anon; -GRANT SELECT ON TABLE realtime.schema_migrations TO authenticated; -GRANT SELECT ON TABLE realtime.schema_migrations TO service_role; -GRANT ALL ON TABLE realtime.schema_migrations TO supabase_realtime_admin; - - --- --- Name: TABLE subscription; Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON TABLE realtime.subscription TO postgres; -GRANT ALL ON TABLE realtime.subscription TO dashboard_user; -GRANT SELECT ON TABLE realtime.subscription TO anon; -GRANT SELECT ON TABLE realtime.subscription TO authenticated; -GRANT SELECT ON TABLE realtime.subscription TO service_role; -GRANT ALL ON TABLE realtime.subscription TO supabase_realtime_admin; - - --- --- Name: SEQUENCE subscription_id_seq; Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON SEQUENCE realtime.subscription_id_seq TO postgres; -GRANT ALL ON SEQUENCE realtime.subscription_id_seq TO dashboard_user; -GRANT USAGE ON SEQUENCE realtime.subscription_id_seq TO anon; -GRANT USAGE ON SEQUENCE realtime.subscription_id_seq TO authenticated; -GRANT USAGE ON SEQUENCE realtime.subscription_id_seq TO service_role; -GRANT ALL ON SEQUENCE realtime.subscription_id_seq TO supabase_realtime_admin; - - --- --- Name: TABLE buckets; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON TABLE storage.buckets TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE storage.buckets TO service_role; -GRANT ALL ON TABLE storage.buckets TO authenticated; -GRANT ALL ON TABLE storage.buckets TO anon; - - --- --- Name: TABLE buckets_analytics; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON TABLE storage.buckets_analytics TO service_role; -GRANT ALL ON TABLE storage.buckets_analytics TO authenticated; -GRANT ALL ON TABLE storage.buckets_analytics TO anon; - - --- --- Name: TABLE buckets_vectors; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT SELECT ON TABLE storage.buckets_vectors TO service_role; -GRANT SELECT ON TABLE storage.buckets_vectors TO authenticated; -GRANT SELECT ON TABLE storage.buckets_vectors TO anon; - - --- --- Name: TABLE iceberg_namespaces; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON TABLE storage.iceberg_namespaces TO service_role; -GRANT SELECT ON TABLE storage.iceberg_namespaces TO authenticated; -GRANT SELECT ON TABLE storage.iceberg_namespaces TO anon; - - --- --- Name: TABLE iceberg_tables; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON TABLE storage.iceberg_tables TO service_role; -GRANT SELECT ON TABLE storage.iceberg_tables TO authenticated; -GRANT SELECT ON TABLE storage.iceberg_tables TO anon; - - --- --- Name: TABLE objects; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON TABLE storage.objects TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE storage.objects TO service_role; -GRANT ALL ON TABLE storage.objects TO authenticated; -GRANT ALL ON TABLE storage.objects TO anon; - - --- --- Name: TABLE s3_multipart_uploads; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON TABLE storage.s3_multipart_uploads TO service_role; -GRANT SELECT ON TABLE storage.s3_multipart_uploads TO authenticated; -GRANT SELECT ON TABLE storage.s3_multipart_uploads TO anon; - - --- --- Name: TABLE s3_multipart_uploads_parts; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON TABLE storage.s3_multipart_uploads_parts TO service_role; -GRANT SELECT ON TABLE storage.s3_multipart_uploads_parts TO authenticated; -GRANT SELECT ON TABLE storage.s3_multipart_uploads_parts TO anon; - - --- --- Name: TABLE vector_indexes; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT SELECT ON TABLE storage.vector_indexes TO service_role; -GRANT SELECT ON TABLE storage.vector_indexes TO authenticated; -GRANT SELECT ON TABLE storage.vector_indexes TO anon; - - --- --- Name: TABLE hooks; Type: ACL; Schema: supabase_functions; Owner: supabase_functions_admin --- - -GRANT ALL ON TABLE supabase_functions.hooks TO postgres; -GRANT ALL ON TABLE supabase_functions.hooks TO anon; -GRANT ALL ON TABLE supabase_functions.hooks TO authenticated; -GRANT ALL ON TABLE supabase_functions.hooks TO service_role; - - --- --- Name: SEQUENCE hooks_id_seq; Type: ACL; Schema: supabase_functions; Owner: supabase_functions_admin --- - -GRANT ALL ON SEQUENCE supabase_functions.hooks_id_seq TO postgres; -GRANT ALL ON SEQUENCE supabase_functions.hooks_id_seq TO anon; -GRANT ALL ON SEQUENCE supabase_functions.hooks_id_seq TO authenticated; -GRANT ALL ON SEQUENCE supabase_functions.hooks_id_seq TO service_role; - - --- --- Name: TABLE migrations; Type: ACL; Schema: supabase_functions; Owner: supabase_functions_admin --- - -GRANT ALL ON TABLE supabase_functions.migrations TO postgres; -GRANT ALL ON TABLE supabase_functions.migrations TO anon; -GRANT ALL ON TABLE supabase_functions.migrations TO authenticated; -GRANT ALL ON TABLE supabase_functions.migrations TO service_role; - - --- --- Name: TABLE secrets; Type: ACL; Schema: vault; Owner: supabase_admin --- - -GRANT SELECT,REFERENCES,DELETE,TRUNCATE ON TABLE vault.secrets TO postgres WITH GRANT OPTION; -GRANT SELECT,DELETE ON TABLE vault.secrets TO service_role; - - --- --- Name: TABLE decrypted_secrets; Type: ACL; Schema: vault; Owner: supabase_admin --- - -GRANT SELECT,REFERENCES,DELETE,TRUNCATE ON TABLE vault.decrypted_secrets TO postgres WITH GRANT OPTION; -GRANT SELECT,DELETE ON TABLE vault.decrypted_secrets TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: auth; Owner: supabase_auth_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON SEQUENCES TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: auth; Owner: supabase_auth_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON FUNCTIONS TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: auth; Owner: supabase_auth_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON TABLES TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: cron; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA cron GRANT ALL ON SEQUENCES TO postgres WITH GRANT OPTION; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: cron; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA cron GRANT ALL ON FUNCTIONS TO postgres WITH GRANT OPTION; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: cron; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA cron GRANT ALL ON TABLES TO postgres WITH GRANT OPTION; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: extensions; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA extensions GRANT ALL ON SEQUENCES TO postgres WITH GRANT OPTION; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: extensions; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA extensions GRANT ALL ON FUNCTIONS TO postgres WITH GRANT OPTION; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: extensions; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA extensions GRANT ALL ON TABLES TO postgres WITH GRANT OPTION; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: graphql; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: graphql; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: graphql; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON TABLES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: graphql_public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: graphql_public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: graphql_public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON TABLES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: public; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: public; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: public; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: realtime; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON SEQUENCES TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: realtime; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON FUNCTIONS TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: realtime; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON TABLES TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: storage; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: storage; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: storage; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON TABLES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: supabase_functions; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: supabase_functions; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: supabase_functions; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON TABLES TO service_role; - - --- --- Name: issue_graphql_placeholder; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin +-- Name: issue_graphql_placeholder; Type: EVENT TRIGGER; Schema: -; Owner: - -- CREATE EVENT TRIGGER issue_graphql_placeholder ON sql_drop @@ -25994,10 +21334,8 @@ CREATE EVENT TRIGGER issue_graphql_placeholder ON sql_drop EXECUTE FUNCTION extensions.set_graphql_placeholder(); -ALTER EVENT TRIGGER issue_graphql_placeholder OWNER TO supabase_admin; - -- --- Name: issue_pg_cron_access; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin +-- Name: issue_pg_cron_access; Type: EVENT TRIGGER; Schema: -; Owner: - -- CREATE EVENT TRIGGER issue_pg_cron_access ON ddl_command_end @@ -26005,10 +21343,8 @@ CREATE EVENT TRIGGER issue_pg_cron_access ON ddl_command_end EXECUTE FUNCTION extensions.grant_pg_cron_access(); -ALTER EVENT TRIGGER issue_pg_cron_access OWNER TO supabase_admin; - -- --- Name: issue_pg_graphql_access; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin +-- Name: issue_pg_graphql_access; Type: EVENT TRIGGER; Schema: -; Owner: - -- CREATE EVENT TRIGGER issue_pg_graphql_access ON ddl_command_end @@ -26016,10 +21352,8 @@ CREATE EVENT TRIGGER issue_pg_graphql_access ON ddl_command_end EXECUTE FUNCTION extensions.grant_pg_graphql_access(); -ALTER EVENT TRIGGER issue_pg_graphql_access OWNER TO supabase_admin; - -- --- Name: issue_pg_net_access; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin +-- Name: issue_pg_net_access; Type: EVENT TRIGGER; Schema: -; Owner: - -- CREATE EVENT TRIGGER issue_pg_net_access ON ddl_command_end @@ -26027,31 +21361,25 @@ CREATE EVENT TRIGGER issue_pg_net_access ON ddl_command_end EXECUTE FUNCTION extensions.grant_pg_net_access(); -ALTER EVENT TRIGGER issue_pg_net_access OWNER TO supabase_admin; - -- --- Name: pgrst_ddl_watch; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin +-- Name: pgrst_ddl_watch; Type: EVENT TRIGGER; Schema: -; Owner: - -- CREATE EVENT TRIGGER pgrst_ddl_watch ON ddl_command_end EXECUTE FUNCTION extensions.pgrst_ddl_watch(); -ALTER EVENT TRIGGER pgrst_ddl_watch OWNER TO supabase_admin; - -- --- Name: pgrst_drop_watch; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin +-- Name: pgrst_drop_watch; Type: EVENT TRIGGER; Schema: -; Owner: - -- CREATE EVENT TRIGGER pgrst_drop_watch ON sql_drop EXECUTE FUNCTION extensions.pgrst_drop_watch(); -ALTER EVENT TRIGGER pgrst_drop_watch OWNER TO supabase_admin; - -- -- PostgreSQL database dump complete -- -\unrestrict ABfzP9IZJ8pAzvgt6E9jKpFn1phQ3b3Lgk09BZZTle5el6ODr77nIXlXnCf1PS1 +\unrestrict t3AqgfsPfrxKQ6xDOUZgLcwOmqGEQFr9QmWZe441K296WP9E1M31r17ZcywnfKW diff --git a/database-novo/schema/01_extensions/extensions.sql b/database-novo/schema/01_extensions/extensions.sql index 0816a7d..3274258 100644 --- a/database-novo/schema/01_extensions/extensions.sql +++ b/database-novo/schema/01_extensions/extensions.sql @@ -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)'; - - diff --git a/database-novo/schema/02_types/auth_types.sql b/database-novo/schema/02_types/auth_types.sql deleted file mode 100644 index 2650239..0000000 --- a/database-novo/schema/02_types/auth_types.sql +++ /dev/null @@ -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 --- - diff --git a/database-novo/schema/02_types/infra_types.sql b/database-novo/schema/02_types/infra_types.sql deleted file mode 100644 index 6239c17..0000000 --- a/database-novo/schema/02_types/infra_types.sql +++ /dev/null @@ -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; - diff --git a/database-novo/schema/02_types/public_types.sql b/database-novo/schema/02_types/public_types.sql deleted file mode 100644 index 62acd16..0000000 --- a/database-novo/schema/02_types/public_types.sql +++ /dev/null @@ -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' -); - diff --git a/database-novo/schema/03_functions/_all.sql b/database-novo/schema/03_functions/_all.sql new file mode 100644 index 0000000..5a465d8 --- /dev/null +++ b/database-novo/schema/03_functions/_all.sql @@ -0,0 +1,6302 @@ +-- All Functions +-- Gerado automaticamente em 2026-04-17T12:23:05.224Z +-- Total: 165 + +CREATE FUNCTION auth.email() RETURNS text + LANGUAGE sql STABLE + AS $$ + select + coalesce( + nullif(current_setting('request.jwt.claim.email', true), ''), + (nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'email') + )::text +$$; + +CREATE FUNCTION auth.jwt() RETURNS jsonb + LANGUAGE sql STABLE + AS $$ + select + coalesce( + nullif(current_setting('request.jwt.claim', true), ''), + nullif(current_setting('request.jwt.claims', true), '') + )::jsonb +$$; + +CREATE FUNCTION auth.role() RETURNS text + LANGUAGE sql STABLE + AS $$ + select + coalesce( + nullif(current_setting('request.jwt.claim.role', true), ''), + (nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'role') + )::text +$$; + +CREATE FUNCTION auth.uid() RETURNS uuid + LANGUAGE sql STABLE + AS $$ + select + coalesce( + nullif(current_setting('request.jwt.claim.sub', true), ''), + (nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'sub') + )::uuid +$$; + +CREATE FUNCTION extensions.grant_pg_cron_access() RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF EXISTS ( + SELECT + FROM pg_event_trigger_ddl_commands() AS ev + JOIN pg_extension AS ext + ON ev.objid = ext.oid + WHERE ext.extname = 'pg_cron' + ) + THEN + grant usage on schema cron to postgres with grant option; + + alter default privileges in schema cron grant all on tables to postgres with grant option; + alter default privileges in schema cron grant all on functions to postgres with grant option; + alter default privileges in schema cron grant all on sequences to postgres with grant option; + + alter default privileges for user supabase_admin in schema cron grant all + on sequences to postgres with grant option; + alter default privileges for user supabase_admin in schema cron grant all + on tables to postgres with grant option; + alter default privileges for user supabase_admin in schema cron grant all + on functions to postgres with grant option; + + grant all privileges on all tables in schema cron to postgres with grant option; + revoke all on table cron.job from postgres; + grant select on table cron.job to postgres with grant option; + END IF; +END; +$$; + +CREATE FUNCTION extensions.grant_pg_graphql_access() RETURNS event_trigger + LANGUAGE plpgsql + AS $_$ +DECLARE + func_is_graphql_resolve bool; +BEGIN + func_is_graphql_resolve = ( + SELECT n.proname = 'resolve' + FROM pg_event_trigger_ddl_commands() AS ev + LEFT JOIN pg_catalog.pg_proc AS n + ON ev.objid = n.oid + ); + + IF func_is_graphql_resolve + THEN + -- Update public wrapper to pass all arguments through to the pg_graphql resolve func + DROP FUNCTION IF EXISTS graphql_public.graphql; + create or replace function graphql_public.graphql( + "operationName" text default null, + query text default null, + variables jsonb default null, + extensions jsonb default null + ) + returns jsonb + language sql + as $$ + select graphql.resolve( + query := query, + variables := coalesce(variables, '{}'), + "operationName" := "operationName", + extensions := extensions + ); + $$; + + -- This hook executes when `graphql.resolve` is created. That is not necessarily the last + -- function in the extension so we need to grant permissions on existing entities AND + -- update default permissions to any others that are created after `graphql.resolve` + grant usage on schema graphql to postgres, anon, authenticated, service_role; + grant select on all tables in schema graphql to postgres, anon, authenticated, service_role; + grant execute on all functions in schema graphql to postgres, anon, authenticated, service_role; + grant all on all sequences in schema graphql to postgres, anon, authenticated, service_role; + alter default privileges in schema graphql grant all on tables to postgres, anon, authenticated, service_role; + alter default privileges in schema graphql grant all on functions to postgres, anon, authenticated, service_role; + alter default privileges in schema graphql grant all on sequences to postgres, anon, authenticated, service_role; + + -- Allow postgres role to allow granting usage on graphql and graphql_public schemas to custom roles + grant usage on schema graphql_public to postgres with grant option; + grant usage on schema graphql to postgres with grant option; + END IF; + +END; +$_$; + +CREATE FUNCTION extensions.grant_pg_net_access() RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_event_trigger_ddl_commands() AS ev + JOIN pg_extension AS ext + ON ev.objid = ext.oid + WHERE ext.extname = 'pg_net' + ) + THEN + GRANT USAGE ON SCHEMA net TO supabase_functions_admin, postgres, anon, authenticated, service_role; + + ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; + ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; + + ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; + ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; + + REVOKE ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; + REVOKE ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; + + GRANT EXECUTE ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; + GRANT EXECUTE ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; + END IF; +END; +$$; + +CREATE FUNCTION extensions.pgrst_ddl_watch() RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +DECLARE + cmd record; +BEGIN + FOR cmd IN SELECT * FROM pg_event_trigger_ddl_commands() + LOOP + IF cmd.command_tag IN ( + 'CREATE SCHEMA', 'ALTER SCHEMA' + , 'CREATE TABLE', 'CREATE TABLE AS', 'SELECT INTO', 'ALTER TABLE' + , 'CREATE FOREIGN TABLE', 'ALTER FOREIGN TABLE' + , 'CREATE VIEW', 'ALTER VIEW' + , 'CREATE MATERIALIZED VIEW', 'ALTER MATERIALIZED VIEW' + , 'CREATE FUNCTION', 'ALTER FUNCTION' + , 'CREATE TRIGGER' + , 'CREATE TYPE', 'ALTER TYPE' + , 'CREATE RULE' + , 'COMMENT' + ) + -- don't notify in case of CREATE TEMP table or other objects created on pg_temp + AND cmd.schema_name is distinct from 'pg_temp' + THEN + NOTIFY pgrst, 'reload schema'; + END IF; + END LOOP; +END; $$; + +CREATE FUNCTION extensions.pgrst_drop_watch() RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +DECLARE + obj record; +BEGIN + FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects() + LOOP + IF obj.object_type IN ( + 'schema' + , 'table' + , 'foreign table' + , 'view' + , 'materialized view' + , 'function' + , 'trigger' + , 'type' + , 'rule' + ) + AND obj.is_temporary IS false -- no pg_temp objects + THEN + NOTIFY pgrst, 'reload schema'; + END IF; + END LOOP; +END; $$; + +CREATE FUNCTION extensions.set_graphql_placeholder() RETURNS event_trigger + LANGUAGE plpgsql + AS $_$ + DECLARE + graphql_is_dropped bool; + BEGIN + graphql_is_dropped = ( + SELECT ev.schema_name = 'graphql_public' + FROM pg_event_trigger_dropped_objects() AS ev + WHERE ev.schema_name = 'graphql_public' + ); + + IF graphql_is_dropped + THEN + create or replace function graphql_public.graphql( + "operationName" text default null, + query text default null, + variables jsonb default null, + extensions jsonb default null + ) + returns jsonb + language plpgsql + as $$ + DECLARE + server_version float; + BEGIN + server_version = (SELECT (SPLIT_PART((select version()), ' ', 2))::float); + + IF server_version >= 14 THEN + RETURN jsonb_build_object( + 'errors', jsonb_build_array( + jsonb_build_object( + 'message', 'pg_graphql extension is not enabled.' + ) + ) + ); + ELSE + RETURN jsonb_build_object( + 'errors', jsonb_build_array( + jsonb_build_object( + 'message', 'pg_graphql is only available on projects running Postgres 14 onwards.' + ) + ) + ); + END IF; + END; + $$; + END IF; + + END; +$_$; + +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; +$_$; + +CREATE FUNCTION public.__rls_ping() RETURNS text + LANGUAGE sql STABLE + AS $$ + select 'ok'::text; +$$; + +CREATE FUNCTION public.activate_subscription_from_intent(p_intent_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_intent record; + v_sub public.subscriptions; + v_days int; + v_user_id uuid; + v_plan_id uuid; + v_target text; +begin + -- l?? pela VIEW unificada + select * into v_intent + from public.subscription_intents + where id = p_intent_id; + + if not found then + raise exception 'Intent n??o encontrado: %', p_intent_id; + end if; + + if v_intent.status <> 'paid' then + raise exception 'Intent precisa estar paid para ativar assinatura'; + end if; + + -- resolve target e plan_id via plans.key + select p.id, p.target + into v_plan_id, v_target + from public.plans p + where p.key = v_intent.plan_key + limit 1; + + if v_plan_id is null then + raise exception 'Plano n??o encontrado em plans.key = %', v_intent.plan_key; + end if; + + v_target := lower(coalesce(v_target, '')); + + -- ??? supervisor adicionado + if v_target not in ('clinic', 'therapist', 'supervisor') then + raise exception 'Target inv??lido em plans.target: %', v_target; + end if; + + -- regra por target + if v_target = 'clinic' then + if v_intent.tenant_id is null then + raise exception 'Intent sem tenant_id'; + end if; + else + -- therapist ou supervisor: vinculado ao user + v_user_id := v_intent.user_id; + if v_user_id is null then + v_user_id := v_intent.created_by_user_id; + end if; + end if; + + if v_target in ('therapist', 'supervisor') and v_user_id is null then + raise exception 'N??o foi poss??vel determinar user_id para assinatura %.', v_target; + end if; + + -- cancela assinatura ativa anterior + if v_target = 'clinic' then + update public.subscriptions + set status = 'cancelled', + cancelled_at = now() + where tenant_id = v_intent.tenant_id + and plan_id = v_plan_id + and status = 'active'; + else + -- therapist ou supervisor + update public.subscriptions + set status = 'cancelled', + cancelled_at = now() + where user_id = v_user_id + and plan_id = v_plan_id + and status = 'active' + and tenant_id is null; + end if; + + -- dura????o do plano (30 dias para mensal) + v_days := case + when lower(coalesce(v_intent.interval, 'month')) = 'year' then 365 + else 30 + end; + + -- cria nova assinatura + insert into public.subscriptions ( + user_id, + plan_id, + status, + started_at, + expires_at, + cancelled_at, + activated_at, + tenant_id, + plan_key, + interval, + source, + created_at, + updated_at + ) + values ( + case when v_target = 'clinic' then null else v_user_id end, + v_plan_id, + 'active', + now(), + now() + make_interval(days => v_days), + null, + now(), + case when v_target = 'clinic' then v_intent.tenant_id else null end, + v_intent.plan_key, + v_intent.interval, + 'manual', + now(), + now() + ) + returning * into v_sub; + + -- grava v??nculo intent ??? subscription + if v_target = 'clinic' then + update public.subscription_intents_tenant + set subscription_id = v_sub.id + where id = p_intent_id; + else + update public.subscription_intents_personal + set subscription_id = v_sub.id + where id = p_intent_id; + end if; + + return v_sub; +end; +$$; + +CREATE FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid DEFAULT NULL::uuid, p_description text DEFAULT 'Cr??dito manual'::text, p_payment_method text DEFAULT 'manual'::text, p_price_cents integer DEFAULT 0) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_credit addon_credits%ROWTYPE; + v_balance_before INTEGER; + v_balance_after INTEGER; + v_tx_id UUID; +BEGIN + -- Upsert addon_credits + INSERT INTO addon_credits (tenant_id, addon_type, balance, total_purchased) + VALUES (p_tenant_id, p_addon_type, 0, 0) + ON CONFLICT (tenant_id, addon_type) DO NOTHING; + + -- Lock e leitura + SELECT * INTO v_credit + FROM addon_credits + WHERE tenant_id = p_tenant_id AND addon_type = p_addon_type + FOR UPDATE; + + v_balance_before := v_credit.balance; + v_balance_after := v_credit.balance + p_amount; + + -- Atualiza saldo + UPDATE addon_credits + SET balance = v_balance_after, + total_purchased = total_purchased + p_amount, + low_balance_notified = CASE WHEN v_balance_after > COALESCE(low_balance_threshold, 10) THEN false ELSE low_balance_notified END, + updated_at = now() + WHERE id = v_credit.id; + + -- Registra transa????o + INSERT INTO addon_transactions ( + tenant_id, addon_type, type, amount, + balance_before, balance_after, + product_id, description, + admin_user_id, payment_method, price_cents + ) VALUES ( + p_tenant_id, p_addon_type, 'purchase', p_amount, + v_balance_before, v_balance_after, + p_product_id, p_description, + auth.uid(), p_payment_method, p_price_cents + ) + RETURNING id INTO v_tx_id; + + RETURN jsonb_build_object( + 'success', true, + 'transaction_id', v_tx_id, + 'balance_before', v_balance_before, + 'balance_after', v_balance_after + ); +END; +$$; + +CREATE FUNCTION public.admin_delete_email_template_global(p_id uuid) RETURNS boolean + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + DELETE FROM public.email_templates_global WHERE id = p_id; + IF NOT FOUND THEN + RAISE EXCEPTION 'Template com id % n??o encontrado', p_id; + END IF; + RETURN true; +END; +$$; + +CREATE FUNCTION public.admin_fix_plan_target(p_plan_key text, p_new_target text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_plan_id uuid; +begin + -- (opcional) restringe targets v??lidos + if p_new_target not in ('clinic','therapist') then + raise exception 'Target inv??lido: %', p_new_target using errcode='P0001'; + end if; + + -- trava o plano + select id into v_plan_id + from public.plans + where key = p_plan_key + for update; + + if v_plan_id is null then + raise exception 'Plano n??o encontrado: %', p_plan_key using errcode='P0001'; + end if; + + -- seguran??a: n??o mexer se existe subscription + if exists (select 1 from public.subscriptions s where s.plan_id = v_plan_id) then + raise exception 'Plano % possui subscriptions. Migra????o bloqueada.', p_plan_key using errcode='P0001'; + end if; + + -- liga bypass SOMENTE nesta transa????o + perform set_config('app.plan_migration_bypass', '1', true); + + update public.plans + set target = p_new_target + where id = v_plan_id; + +end +$$; + +CREATE FUNCTION public.admin_upsert_email_template_global(p_id uuid DEFAULT NULL::uuid, p_key text DEFAULT NULL::text, p_domain text DEFAULT NULL::text, p_channel text DEFAULT 'email'::text, p_subject text DEFAULT NULL::text, p_body_html text DEFAULT NULL::text, p_body_text text DEFAULT NULL::text, p_is_active boolean DEFAULT true, p_variables jsonb DEFAULT '{}'::jsonb) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_result jsonb; + v_id uuid; +BEGIN + -- UPDATE existente + IF p_id IS NOT NULL THEN + UPDATE public.email_templates_global + SET + subject = COALESCE(p_subject, subject), + body_html = COALESCE(p_body_html, body_html), + body_text = p_body_text, + is_active = p_is_active, + variables = COALESCE(p_variables, variables), + version = version + 1 + WHERE id = p_id + RETURNING to_jsonb(email_templates_global.*) INTO v_result; + + IF v_result IS NULL THEN + RAISE EXCEPTION 'Template com id % n??o encontrado', p_id; + END IF; + + RETURN v_result; + END IF; + + -- INSERT novo + IF p_key IS NULL OR p_domain IS NULL OR p_subject IS NULL OR p_body_html IS NULL THEN + RAISE EXCEPTION 'key, domain, subject e body_html s??o obrigat??rios para novo template'; + END IF; + + INSERT INTO public.email_templates_global (key, domain, channel, subject, body_html, body_text, is_active, variables) + VALUES (p_key, p_domain, p_channel, p_subject, p_body_html, p_body_text, p_is_active, p_variables) + RETURNING to_jsonb(email_templates_global.*) INTO v_result; + + RETURN v_result; +END; +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +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 + ); +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +CREATE FUNCTION public.cancel_subscription(p_subscription_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_owner_type text; + v_owner_ref uuid; +begin + + select * + into v_sub + from public.subscriptions + where id = p_subscription_id + for update; + + if not found then + raise exception 'Subscription n??o encontrada'; + end if; + + if v_sub.status = 'canceled' then + return v_sub; + end if; + + if v_sub.tenant_id is not null then + v_owner_type := 'clinic'; + v_owner_ref := v_sub.tenant_id; + elsif v_sub.user_id is not null then + v_owner_type := 'therapist'; + v_owner_ref := v_sub.user_id; + else + v_owner_type := null; + v_owner_ref := null; + end if; + + update public.subscriptions + set status = 'canceled', + cancel_at_period_end = false, + updated_at = now() + where id = p_subscription_id + returning * into v_sub; + + insert into public.subscription_events( + subscription_id, + owner_id, + owner_type, + owner_ref, + event_type, + old_plan_id, + new_plan_id, + created_by, + reason, + source, + metadata + ) + values ( + v_sub.id, + v_owner_ref, + v_owner_type, + v_owner_ref, + 'canceled', + v_sub.plan_id, + v_sub.plan_id, + auth.uid(), + 'Cancelamento manual via admin', + 'admin_panel', + jsonb_build_object('previous_status', 'active') + ); + + if v_owner_ref is not null then + insert into public.entitlements_invalidation(owner_id, changed_at) + values (v_owner_ref, now()) + on conflict (owner_id) + do update set changed_at = excluded.changed_at; + end if; + + return v_sub; + +end; +$$; + +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; +$$; + +CREATE FUNCTION public.change_subscription_plan(p_subscription_id uuid, p_new_plan_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_old_plan uuid; + v_new_key text; + + v_owner_type text; + v_owner_ref uuid; + + v_new_target text; + v_sub_target text; +begin + select * + into v_sub + from public.subscriptions + where id = p_subscription_id + for update; + + if not found then + raise exception 'Subscription n??o encontrada'; + end if; + + v_old_plan := v_sub.plan_id; + + if v_old_plan = p_new_plan_id then + return v_sub; + end if; + + select key, target + into v_new_key, v_new_target + from public.plans + where id = p_new_plan_id; + + if v_new_key is null then + raise exception 'Plano n??o encontrado'; + end if; + + v_new_target := lower(coalesce(v_new_target, '')); + + v_sub_target := case + when v_sub.tenant_id is not null then 'clinic' + else 'therapist' + end; + + if v_new_target <> v_sub_target then + raise exception 'Plano inv??lido para este tipo de assinatura. Assinatura ?? % e o plano ?? %.', + v_sub_target, v_new_target + using errcode = 'P0001'; + end if; + + if v_sub.tenant_id is not null then + v_owner_type := 'clinic'; + v_owner_ref := v_sub.tenant_id; + elsif v_sub.user_id is not null then + v_owner_type := 'therapist'; + v_owner_ref := v_sub.user_id; + else + v_owner_type := null; + v_owner_ref := null; + end if; + + update public.subscriptions + set plan_id = p_new_plan_id, + plan_key = v_new_key, + updated_at = now() + where id = p_subscription_id + returning * into v_sub; + + insert into public.subscription_events( + subscription_id, + owner_id, + owner_type, + owner_ref, + event_type, + old_plan_id, + new_plan_id, + created_by, + reason, + source, + metadata + ) + values ( + v_sub.id, + v_owner_ref, + v_owner_type, + v_owner_ref, + 'plan_changed', + v_old_plan, + p_new_plan_id, + auth.uid(), + 'Plan change via DEV menu', + 'dev_menu', + jsonb_build_object( + 'previous_plan', v_old_plan, + 'new_plan', p_new_plan_id, + 'new_plan_key', v_new_key, + 'new_plan_target', v_new_target + ) + ); + + if v_owner_ref is not null then + insert into public.entitlements_invalidation (owner_id, changed_at) + values (v_owner_ref, now()) + on conflict (owner_id) + do update set changed_at = excluded.changed_at; + end if; + + return v_sub; +end; +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +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; +$_$; + +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; +$$; + +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; +$$; + +CREATE FUNCTION public.current_member_id(p_tenant_id uuid) RETURNS uuid + LANGUAGE sql STABLE + AS $$ + select tm.id + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + limit 1 +$$; + +CREATE FUNCTION public.current_member_role(p_tenant_id uuid) RETURNS text + LANGUAGE sql STABLE + AS $$ + select tm.role + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + limit 1 +$$; + +CREATE FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid DEFAULT NULL::uuid, p_description text DEFAULT 'Consumo'::text) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_credit addon_credits%ROWTYPE; + v_balance_before INTEGER; + v_balance_after INTEGER; +BEGIN + -- Lock e leitura + SELECT * INTO v_credit + FROM addon_credits + WHERE tenant_id = p_tenant_id AND addon_type = p_addon_type AND is_active = true + FOR UPDATE; + + IF NOT FOUND THEN + RETURN jsonb_build_object('success', false, 'reason', 'no_credits', 'balance', 0); + END IF; + + -- Verifica saldo + IF v_credit.balance <= 0 THEN + RETURN jsonb_build_object('success', false, 'reason', 'insufficient_balance', 'balance', 0); + END IF; + + -- Verifica rate limit di??rio + IF v_credit.daily_limit IS NOT NULL THEN + -- Reset se passou do dia + IF v_credit.daily_reset_at IS NULL OR v_credit.daily_reset_at < date_trunc('day', now()) THEN + UPDATE addon_credits SET daily_used = 0, daily_reset_at = date_trunc('day', now()) + interval '1 day' WHERE id = v_credit.id; + v_credit.daily_used := 0; + END IF; + + IF v_credit.daily_used >= v_credit.daily_limit THEN + RETURN jsonb_build_object('success', false, 'reason', 'daily_limit_reached', 'balance', v_credit.balance); + END IF; + END IF; + + -- Verifica rate limit hor??rio + IF v_credit.hourly_limit IS NOT NULL THEN + IF v_credit.hourly_reset_at IS NULL OR v_credit.hourly_reset_at < date_trunc('hour', now()) THEN + UPDATE addon_credits SET hourly_used = 0, hourly_reset_at = date_trunc('hour', now()) + interval '1 hour' WHERE id = v_credit.id; + v_credit.hourly_used := 0; + END IF; + + IF v_credit.hourly_used >= v_credit.hourly_limit THEN + RETURN jsonb_build_object('success', false, 'reason', 'hourly_limit_reached', 'balance', v_credit.balance); + END IF; + END IF; + + -- Verifica expira????o + IF v_credit.expires_at IS NOT NULL AND v_credit.expires_at < now() THEN + RETURN jsonb_build_object('success', false, 'reason', 'credits_expired', 'balance', v_credit.balance); + END IF; + + v_balance_before := v_credit.balance; + v_balance_after := v_credit.balance - 1; + + -- Debita + UPDATE addon_credits + SET balance = v_balance_after, + total_consumed = total_consumed + 1, + daily_used = COALESCE(daily_used, 0) + 1, + hourly_used = COALESCE(hourly_used, 0) + 1, + updated_at = now() + WHERE id = v_credit.id; + + -- Registra transa????o + INSERT INTO addon_transactions ( + tenant_id, addon_type, type, amount, + balance_before, balance_after, + queue_id, description + ) VALUES ( + p_tenant_id, p_addon_type, 'consume', -1, + v_balance_before, v_balance_after, + p_queue_id, p_description + ); + + RETURN jsonb_build_object( + 'success', true, + 'balance_before', v_balance_before, + 'balance_after', v_balance_after + ); +END; +$$; + +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; +$$; + +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; +$$; + +CREATE FUNCTION public.dev_list_auth_users(p_limit integer DEFAULT 50) RETURNS TABLE(id uuid, email text, created_at timestamp with time zone) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +begin + -- s?? saas_admin pode ver + if not exists ( + select 1 + from public.profiles p + where p.id = auth.uid() + and p.role = 'saas_admin' + ) then + return; + end if; + + return query + select + u.id, + u.email, + u.created_at + from auth.users u + order by u.created_at desc + limit greatest(1, least(coalesce(p_limit, 50), 500)); +end; +$$; + +CREATE FUNCTION public.dev_list_custom_users() RETURNS TABLE(user_id uuid, email text, created_at timestamp with time zone, global_role text, tenant_role text, tenant_id uuid, password_dev text, kind text) + LANGUAGE sql SECURITY DEFINER + SET search_path TO 'public' + AS $$ + with base as ( + select + u.id as user_id, + lower(u.email) as email, + u.created_at + from auth.users u + where lower(u.email) not in ( + 'clinic@agenciapsi.com.br', + 'therapist@agenciapsi.com.br', + 'patient@agenciapsi.com.br', + 'saas@agenciapsi.com.br' + ) + ), + prof as ( + select p.id, p.role as global_role + from public.profiles p + ), + last_membership as ( + select distinct on (tm.user_id) + tm.user_id, + tm.tenant_id, + tm.role as tenant_role, + tm.created_at + from public.tenant_members tm + where tm.status = 'active' + order by tm.user_id, tm.created_at desc + ) + select + b.user_id, + b.email, + b.created_at, + pr.global_role, + lm.tenant_role, + lm.tenant_id, + dc.password_dev, + dc.kind + from base b + left join prof pr on pr.id = b.user_id + left join last_membership lm on lm.user_id = b.user_id + left join public.dev_user_credentials dc on lower(dc.email) = b.email + order by b.created_at desc; +$$; + +CREATE FUNCTION public.dev_list_intent_leads() RETURNS TABLE(email text, last_intent_at timestamp with time zone, plan_key text, billing_interval text, status text, tenant_id uuid) + LANGUAGE sql SECURITY DEFINER + SET search_path TO 'public' + AS $$ + select + lower(si.email) as email, + max(si.created_at) as last_intent_at, + (array_agg(si.plan_key order by si.created_at desc))[1] as plan_key, + (array_agg(si.interval order by si.created_at desc))[1] as billing_interval, + (array_agg(si.status order by si.created_at desc))[1] as status, + (array_agg(si.tenant_id order by si.created_at desc))[1] as tenant_id + from public.subscription_intents si + where si.email is not null + and not exists ( + select 1 + from auth.users au + where lower(au.email) = lower(si.email) + ) + group by lower(si.email) + order by max(si.created_at) desc; +$$; + +CREATE FUNCTION public.dev_public_debug_snapshot() RETURNS TABLE(users_total integer, tenants_total integer, intents_new_total integer, latest_intents jsonb) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $_$ +declare + v_latest jsonb; +begin + select jsonb_agg( + jsonb_build_object( + 'created_at', si.created_at, + 'email_masked', + regexp_replace(lower(si.email), '(^.).*(@.*$)', '\1***\2'), + 'plan_key', si.plan_key, + 'status', si.status + ) + order by si.created_at desc + ) + into v_latest + from ( + select si.* + from public.subscription_intents si + where si.email is not null + order by si.created_at desc + limit 5 + ) si; + + return query + select + (select count(*)::int from auth.users) as users_total, + (select count(*)::int from public.tenants) as tenants_total, + (select count(*)::int from public.subscription_intents where status = 'new') as intents_new_total, + coalesce(v_latest, '[]'::jsonb) as latest_intents; +end; +$_$; + +CREATE FUNCTION public.ensure_personal_tenant() RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_uid uuid; + v_existing uuid; +BEGIN + v_uid := auth.uid(); + IF v_uid IS NULL THEN + RAISE EXCEPTION 'Not authenticated'; + END IF; + + SELECT tm.tenant_id INTO v_existing + FROM public.tenant_members tm + JOIN public.tenants t ON t.id = tm.tenant_id + WHERE tm.user_id = v_uid + AND tm.status = 'active' + AND t.kind IN ('therapist', 'saas') + ORDER BY tm.created_at DESC + LIMIT 1; + + IF v_existing IS NOT NULL THEN + RETURN v_existing; + END IF; + + RETURN public.provision_account_tenant(v_uid, 'therapist'); +END; +$$; + +CREATE FUNCTION public.ensure_personal_tenant_for_user(p_user_id uuid) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_uid uuid; + v_existing uuid; + v_tenant uuid; + v_email text; + v_name text; +begin + v_uid := p_user_id; + if v_uid is null then + raise exception 'Missing user id'; + end if; + + -- s?? considera tenant pessoal (kind='saas') + select tm.tenant_id + into v_existing + from public.tenant_members tm + join public.tenants t on t.id = tm.tenant_id + where tm.user_id = v_uid + and tm.status = 'active' + and t.kind = 'saas' + order by tm.created_at desc + limit 1; + + if v_existing is not null then + return v_existing; + end if; + + select email into v_email + from auth.users + where id = v_uid; + + v_name := coalesce(split_part(v_email, '@', 1), 'Conta'); + + insert into public.tenants (name, kind, created_at) + values (v_name || ' (Pessoal)', 'saas', 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; +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +CREATE FUNCTION public.fn_document_signature_timeline() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_patient_id uuid; + v_tenant_id uuid; + v_doc_nome text; +BEGIN + IF NEW.status = 'assinado' AND (OLD.status IS NULL OR OLD.status <> 'assinado') THEN + SELECT d.patient_id, d.tenant_id, d.nome_original + INTO v_patient_id, v_tenant_id, v_doc_nome + FROM public.documents d + WHERE d.id = NEW.documento_id; + + IF v_patient_id IS NOT NULL THEN + INSERT INTO public.patient_timeline ( + patient_id, tenant_id, evento_tipo, + titulo, descricao, icone_cor, + link_ref_tipo, link_ref_id, + gerado_por, ocorrido_em + ) VALUES ( + v_patient_id, + v_tenant_id, + 'documento_assinado', + 'Documento assinado: ' || COALESCE(v_doc_nome, 'documento'), + 'Assinado por ' || COALESCE(NEW.signatario_nome, NEW.signatario_tipo), + 'green', + 'documento', + NEW.documento_id, + NEW.signatario_id, + NEW.assinado_em + ); + END IF; + END IF; + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.fn_documents_timeline_insert() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + INSERT INTO public.patient_timeline ( + patient_id, tenant_id, evento_tipo, + titulo, descricao, icone_cor, + link_ref_tipo, link_ref_id, + gerado_por, ocorrido_em + ) VALUES ( + NEW.patient_id, + NEW.tenant_id, + 'documento_adicionado', + 'Documento adicionado: ' || COALESCE(NEW.nome_original, 'arquivo'), + 'Tipo: ' || COALESCE(NEW.tipo_documento, 'outro'), + 'blue', + 'documento', + NEW.id, + NEW.uploaded_by, + NEW.uploaded_at + ); + RETURN NEW; +END; +$$; + +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; + +$$; + +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; +$$; + +CREATE FUNCTION public.get_my_email() RETURNS text + LANGUAGE sql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ + select lower(email) + from auth.users + where id = auth.uid(); +$$; + +CREATE FUNCTION public.guard_account_type_immutable() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF OLD.account_type <> 'free' AND NEW.account_type IS DISTINCT FROM OLD.account_type THEN + RAISE EXCEPTION 'account_type ?? imut??vel ap??s escolha (atual: "%" para tentativa: "%"). Para mudar de perfil, crie uma nova conta.', OLD.account_type, NEW.account_type + USING ERRCODE = 'P0001'; + END IF; + RETURN NEW; +END; +$$; + +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; +$$; + +CREATE FUNCTION public.guard_no_change_core_plan_key() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if old.key in ('clinic_free','clinic_pro','therapist_free','therapist_pro') + and new.key is distinct from old.key then + raise exception 'N??o ?? permitido alterar a key do plano padr??o (%).', old.key + using errcode = 'P0001'; + end if; + + return new; +end $$; + +CREATE FUNCTION public.guard_no_change_plan_target() RETURNS trigger + LANGUAGE plpgsql + AS $$ +declare + v_bypass text; +begin + -- bypass controlado por sess??o/transa????o: + -- s?? passa se app.plan_migration_bypass = '1' + v_bypass := current_setting('app.plan_migration_bypass', true); + + if v_bypass = '1' then + return new; + end if; + + -- comportamento original (bloqueia qualquer mudan??a) + if new.target is distinct from old.target then + raise exception 'N??o ?? permitido alterar target do plano (%) de % para %.', + old.key, old.target, new.target + using errcode = 'P0001'; + end if; + + return new; +end +$$; + +CREATE FUNCTION public.guard_no_delete_core_plans() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if old.key in ('clinic_free','clinic_pro','therapist_free','therapist_pro') then + raise exception 'Plano padr??o (%) n??o pode ser removido.', old.key + using errcode = 'P0001'; + end if; + + return old; +end $$; + +CREATE FUNCTION public.guard_patient_cannot_own_tenant() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE + v_account_type text; +BEGIN + SELECT account_type INTO v_account_type + FROM public.profiles + WHERE id = NEW.user_id; + + IF v_account_type = 'patient' AND NEW.role IN ('tenant_admin', 'therapist') THEN + RAISE EXCEPTION 'Usu??rio com perfil "patient" n??o pode ser propriet??rio ou terapeuta de um tenant. Se tornou profissional? Crie uma nova conta.' + USING ERRCODE = 'P0001'; + END IF; + + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.guard_tenant_kind_immutable() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF NEW.kind IS DISTINCT FROM OLD.kind THEN + RAISE EXCEPTION 'tenants.kind ?? imut??vel ap??s cria????o. Tentativa de alterar "%" para "%".', OLD.kind, NEW.kind + USING ERRCODE = 'P0001'; + END IF; + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.handle_new_user() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + INSERT INTO public.profiles (id, role, account_type) + VALUES (NEW.id, 'portal_user', 'free') + ON CONFLICT (id) DO NOTHING; + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.handle_new_user_create_personal_tenant() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + -- Desabilitado. Tenant criado no onboarding via provision_account_tenant(). + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.has_feature(p_owner_id uuid, p_feature_key text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.owner_feature_entitlements e + where e.owner_id = p_owner_id + and e.feature_key = p_feature_key + ); +$$; + +CREATE FUNCTION public.is_clinic_tenant(_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + SELECT EXISTS ( + SELECT 1 FROM public.tenants t + WHERE t.id = _tenant_id + AND t.kind IN ('clinic', 'clinic_coworking', 'clinic_reception', 'clinic_full') + ); +$$; + +CREATE FUNCTION public.is_saas_admin() RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 from public.saas_admins sa + where sa.user_id = auth.uid() + ); +$$; + +CREATE FUNCTION public.is_tenant_admin(p_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ + select exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + and tm.role = 'tenant_admin' + and tm.status = 'active' + ); +$$; + +CREATE FUNCTION public.is_tenant_member(_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.tenant_members m + where m.tenant_id = _tenant_id + and m.user_id = auth.uid() + and m.status = 'active' + ); +$$; + +CREATE FUNCTION public.is_therapist_tenant(_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + SELECT EXISTS ( + SELECT 1 FROM public.tenants t + WHERE t.id = _tenant_id AND t.kind = 'therapist' + ); +$$; + +CREATE FUNCTION public.jwt_email() RETURNS text + LANGUAGE sql STABLE + AS $$ + select nullif(lower(current_setting('request.jwt.claim.email', true)), ''); +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +CREATE FUNCTION public.my_tenants() RETURNS TABLE(tenant_id uuid, role text, status text, kind text) + LANGUAGE sql STABLE + AS $$ + select + tm.tenant_id, + tm.role, + tm.status, + t.kind + from public.tenant_members tm + join public.tenants t on t.id = tm.tenant_id + where tm.user_id = auth.uid(); +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +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; $$; + +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; +$$; + +CREATE FUNCTION public.on_new_user_seed_patient_groups() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ + BEGIN + PERFORM public.seed_default_patient_groups(NEW.id); + RETURN NEW; + END; + $$; + +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; +$$; + +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; +$$; + +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; +$$; + +CREATE FUNCTION public.prevent_promoting_to_system() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if new.is_system = true and old.is_system is distinct from true then + raise exception 'N??o ?? permitido transformar um grupo comum em grupo do sistema.'; + end if; + return new; +end; +$$; + +CREATE FUNCTION public.prevent_saas_membership() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM public.profiles + WHERE id = NEW.user_id + AND role = 'saas_admin' + ) THEN + RAISE EXCEPTION 'SaaS admin cannot belong to tenant'; + END IF; + + RETURN NEW; +END; +$$; + +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; +$$; + +CREATE FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text DEFAULT NULL::text) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_tenant_id uuid; + v_account_type text; + v_name text; +BEGIN + IF p_kind NOT IN ('therapist', 'clinic_coworking', 'clinic_reception', 'clinic_full') THEN + RAISE EXCEPTION 'kind inv??lido: "%". Use: therapist, clinic_coworking, clinic_reception, clinic_full.', p_kind + USING ERRCODE = 'P0001'; + END IF; + + v_account_type := CASE WHEN p_kind = 'therapist' THEN 'therapist' ELSE 'clinic' END; + + IF EXISTS ( + SELECT 1 + FROM public.tenant_members tm + JOIN public.tenants t ON t.id = tm.tenant_id + WHERE tm.user_id = p_user_id + AND tm.role = 'tenant_admin' + AND tm.status = 'active' + AND t.kind = p_kind + ) THEN + RAISE EXCEPTION 'Usu??rio j?? possui um tenant do tipo "%".', p_kind + USING ERRCODE = 'P0001'; + END IF; + + v_name := COALESCE( + NULLIF(TRIM(p_name), ''), + ( + SELECT COALESCE(NULLIF(TRIM(pr.full_name), ''), SPLIT_PART(au.email, '@', 1)) + FROM public.profiles pr + JOIN auth.users au ON au.id = pr.id + WHERE pr.id = p_user_id + ), + 'Conta' + ); + + INSERT INTO public.tenants (name, kind, created_at) + VALUES (v_name, p_kind, now()) + RETURNING id INTO v_tenant_id; + + INSERT INTO public.tenant_members (tenant_id, user_id, role, status, created_at) + VALUES (v_tenant_id, p_user_id, 'tenant_admin', 'active', now()); + + UPDATE public.profiles + SET account_type = v_account_type + WHERE id = p_user_id; + + PERFORM public.seed_determined_commitments(v_tenant_id); + + RETURN v_tenant_id; +END; +$$; + +CREATE FUNCTION public.reactivate_subscription(p_subscription_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_owner_type text; + v_owner_ref uuid; +begin + + select * + into v_sub + from public.subscriptions + where id = p_subscription_id + for update; + + if not found then + raise exception 'Subscription n??o encontrada'; + end if; + + if v_sub.status = 'active' then + return v_sub; + end if; + + if v_sub.tenant_id is not null then + v_owner_type := 'clinic'; + v_owner_ref := v_sub.tenant_id; + elsif v_sub.user_id is not null then + v_owner_type := 'therapist'; + v_owner_ref := v_sub.user_id; + else + v_owner_type := null; + v_owner_ref := null; + end if; + + update public.subscriptions + set status = 'active', + cancel_at_period_end = false, + updated_at = now() + where id = p_subscription_id + returning * into v_sub; + + insert into public.subscription_events( + subscription_id, + owner_id, + owner_type, + owner_ref, + event_type, + old_plan_id, + new_plan_id, + created_by, + reason, + source, + metadata + ) + values ( + v_sub.id, + v_owner_ref, + v_owner_type, + v_owner_ref, + 'reactivated', + v_sub.plan_id, + v_sub.plan_id, + auth.uid(), + 'Reativa????o manual via admin', + 'admin_panel', + jsonb_build_object('previous_status', 'canceled') + ); + + if v_owner_ref is not null then + insert into public.entitlements_invalidation(owner_id, changed_at) + values (v_owner_ref, now()) + on conflict (owner_id) + do update set changed_at = excluded.changed_at; + end if; + + return v_sub; + +end; +$$; + +CREATE FUNCTION public.rebuild_owner_entitlements(p_owner_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_plan_id uuid; +begin + -- Plano ativo do owner (owner = subscriptions.user_id) + select s.plan_id + into v_plan_id + from public.subscriptions s + where s.user_id = p_owner_id + and s.status = 'active' + order by s.created_at desc + limit 1; + + -- Sempre zera entitlements do owner (rebuild) + delete from public.owner_feature_entitlements e + where e.owner_id = p_owner_id; + + -- Se n??o tem assinatura ativa, acabou + if v_plan_id is null then + return; + end if; + + -- Recria entitlements esperados pelo plano + insert into public.owner_feature_entitlements (owner_id, feature_key, sources, limits_list) + select + p_owner_id as owner_id, + f.key as feature_key, + array['plan'::text] as sources, + '{}'::jsonb as limits_list + from public.plan_features pf + join public.features f on f.id = pf.feature_id + where pf.plan_id = v_plan_id; + +end; +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +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; $$; + +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; +$$; + +CREATE FUNCTION public.seed_default_patient_groups(p_tenant_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_owner_id uuid; +BEGIN + -- busca o owner (tenant_admin) do tenant + SELECT user_id INTO v_owner_id + FROM public.tenant_members + WHERE tenant_id = p_tenant_id + AND role = 'tenant_admin' + AND status = 'active' + LIMIT 1; + + IF v_owner_id IS NULL THEN + RETURN; + END IF; + + INSERT INTO public.patient_groups (owner_id, nome, cor, is_system, tenant_id) + VALUES + (v_owner_id, 'Crian??as', '#60a5fa', true, p_tenant_id), + (v_owner_id, 'Adolescentes', '#a78bfa', true, p_tenant_id), + (v_owner_id, 'Idosos', '#34d399', true, p_tenant_id) + ON CONFLICT (owner_id, nome) DO NOTHING; +END; +$$; + +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; +$$; + +CREATE FUNCTION public.set_insurance_plans_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN NEW.updated_at = now(); RETURN NEW; END; $$; + +CREATE FUNCTION public.set_medicos_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + +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; +$$; + +CREATE FUNCTION public.set_services_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + +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 + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +begin + -- ??? S?? owner ou admin do tenant podem alterar features + if not exists ( + select 1 from public.tenant_members + where tenant_id = p_tenant_id + and user_id = auth.uid() + and role in ('owner', 'admin') + and status = 'active' + ) then + raise exception 'Acesso negado: apenas owner/admin pode alterar features do tenant.'; + end if; + + insert into public.tenant_features (tenant_id, feature_key, enabled) + values (p_tenant_id, p_feature_key, p_enabled) + on conflict (tenant_id, feature_key) + do update set enabled = excluded.enabled; + + insert into public.tenant_feature_exceptions_log ( + tenant_id, feature_key, enabled, reason, created_by + ) values ( + p_tenant_id, p_feature_key, p_enabled, p_reason, auth.uid() + ); +end; +$$; + +CREATE FUNCTION public.set_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.set_updated_at_recurrence() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN NEW.updated_at = now(); RETURN NEW; END; +$$; + +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; +$$; + +CREATE FUNCTION public.subscription_intents_view_insert() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_target text; + v_plan_id uuid; +begin + select p.id, p.target into v_plan_id, v_target + from public.plans p + where p.key = new.plan_key; + + if v_plan_id is null then + raise exception 'Plano inv??lido: plan_key=%', new.plan_key; + end if; + + if lower(v_target) = 'clinic' then + if new.tenant_id is null then + raise exception 'Inten????o clinic exige tenant_id.'; + end if; + + insert into public.subscription_intents_tenant ( + id, tenant_id, created_by_user_id, email, + plan_id, plan_key, interval, amount_cents, currency, + status, source, notes, created_at, paid_at + ) values ( + coalesce(new.id, gen_random_uuid()), + new.tenant_id, new.created_by_user_id, new.email, + v_plan_id, new.plan_key, coalesce(new.interval,'month'), + new.amount_cents, coalesce(new.currency,'BRL'), + coalesce(new.status,'pending'), coalesce(new.source,'manual'), + new.notes, coalesce(new.created_at, now()), new.paid_at + ); + + new.plan_target := 'clinic'; + return new; + end if; + + -- therapist ou supervisor ??? tabela personal + if lower(v_target) in ('therapist', 'supervisor') then + insert into public.subscription_intents_personal ( + id, user_id, created_by_user_id, email, + plan_id, plan_key, interval, amount_cents, currency, + status, source, notes, created_at, paid_at + ) values ( + coalesce(new.id, gen_random_uuid()), + new.user_id, new.created_by_user_id, new.email, + v_plan_id, new.plan_key, coalesce(new.interval,'month'), + new.amount_cents, coalesce(new.currency,'BRL'), + coalesce(new.status,'pending'), coalesce(new.source,'manual'), + new.notes, coalesce(new.created_at, now()), new.paid_at + ); + + new.plan_target := lower(v_target); -- 'therapist' ou 'supervisor' + return new; + end if; + + raise exception 'Target de plano n??o suportado: %', v_target; +end; +$$; + +CREATE FUNCTION public.subscriptions_validate_scope() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE + v_target text; +BEGIN + SELECT lower(p.target) INTO v_target + FROM public.plans p + WHERE p.id = NEW.plan_id; + + IF v_target IS NULL THEN + RAISE EXCEPTION 'Plano inv??lido (target nulo).'; + END IF; + + IF v_target = 'clinic' THEN + IF NEW.tenant_id IS NULL THEN + RAISE EXCEPTION 'Assinatura clinic exige tenant_id.'; + END IF; + IF NEW.user_id IS NOT NULL THEN + RAISE EXCEPTION 'Assinatura clinic n??o pode ter user_id (XOR).'; + END IF; + + ELSIF v_target IN ('therapist', 'supervisor') THEN + -- supervisor ?? pessoal como therapist + IF NEW.tenant_id IS NOT NULL THEN + RAISE EXCEPTION 'Assinatura % n??o deve ter tenant_id.', v_target; + END IF; + IF NEW.user_id IS NULL THEN + RAISE EXCEPTION 'Assinatura % exige user_id.', v_target; + END IF; + + ELSIF v_target = 'patient' THEN + IF NEW.tenant_id IS NOT NULL THEN + RAISE EXCEPTION 'Assinatura patient n??o deve ter tenant_id.'; + END IF; + IF NEW.user_id IS NULL THEN + RAISE EXCEPTION 'Assinatura patient exige user_id.'; + END IF; + + ELSE + RAISE EXCEPTION 'Target de plano inv??lido: %', v_target; + END IF; + + RETURN NEW; +END; +$$; + +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; +$$; + +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; +$$; + +CREATE FUNCTION public.tenant_accept_invite(p_token uuid) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +declare + v_uid uuid; + v_email text; + v_invite public.tenant_invites%rowtype; +begin + -- 1) precisa estar autenticado + v_uid := auth.uid(); + if v_uid is null then + raise exception 'not_authenticated' using errcode = 'P0001'; + end if; + + -- 2) pega email real do usu??rio logado sem depender do JWT claim + select u.email + into v_email + from auth.users u + where u.id = v_uid; + + if v_email is null or length(trim(v_email)) = 0 then + raise exception 'missing_user_email' using errcode = 'P0001'; + end if; + + -- 3) carrega o invite e trava linha (evita 2 aceites concorrentes) + select * + into v_invite + from public.tenant_invites i + where i.token = p_token + for update; + + if not found then + raise exception 'invite_not_found' using errcode = 'P0001'; + end if; + + -- 4) valida????es de estado + if v_invite.revoked_at is not null then + raise exception 'invite_revoked' using errcode = 'P0001'; + end if; + + if v_invite.accepted_at is not null then + raise exception 'invite_already_accepted' using errcode = 'P0001'; + end if; + + if v_invite.expires_at is not null and v_invite.expires_at <= now() then + raise exception 'invite_expired' using errcode = 'P0001'; + end if; + + -- 5) valida email (case-insensitive) + if lower(trim(v_invite.email)) <> lower(trim(v_email)) then + raise exception 'email_mismatch' using errcode = 'P0001'; + end if; + + -- 6) consome o invite + update public.tenant_invites + set accepted_at = now(), + accepted_by = v_uid + where id = v_invite.id; + + -- 7) cria ou reativa o membership + insert into public.tenant_members (tenant_id, user_id, role, status, created_at) + values (v_invite.tenant_id, v_uid, v_invite.role, 'active', now()) + on conflict (tenant_id, user_id) + do update set + role = excluded.role, + status = 'active'; + + -- 8) retorno ??til pro front (voc?? j?? tenta ler tenant_id no AcceptInvitePage) + return jsonb_build_object( + 'ok', true, + 'tenant_id', v_invite.tenant_id, + 'role', v_invite.role + ); +end; +$$; + +CREATE FUNCTION public.tenant_add_member_by_email(p_tenant_id uuid, p_email text, p_role text DEFAULT 'therapist'::text) RETURNS public.tenant_members + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +declare + v_target_uid uuid; + v_member public.tenant_members%rowtype; + v_is_admin boolean; + v_email text; +begin + if p_tenant_id is null then + raise exception 'tenant_id ?? obrigat??rio'; + end if; + + v_email := lower(trim(coalesce(p_email, ''))); + if v_email = '' then + raise exception 'email ?? obrigat??rio'; + end if; + + -- valida role permitida + if p_role not in ('tenant_admin','therapist','secretary','patient') then + raise exception 'role inv??lida: %', p_role; + end if; + + -- apenas admin do tenant (role real no banco) + select exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + and tm.role = 'tenant_admin' + and coalesce(tm.status,'active') = 'active' + ) into v_is_admin; + + if not v_is_admin then + raise exception 'sem permiss??o: apenas admin da cl??nica pode adicionar membros'; + end if; + + -- acha usu??rio pelo e-mail no Supabase Auth + select u.id + into v_target_uid + from auth.users u + where lower(u.email) = v_email + limit 1; + + if v_target_uid is null then + raise exception 'nenhum usu??rio encontrado com este e-mail'; + end if; + + -- cria ou reativa membro + insert into public.tenant_members (tenant_id, user_id, role, status) + values (p_tenant_id, v_target_uid, p_role, 'active') + on conflict (tenant_id, user_id) + do update set + role = excluded.role, + status = 'active' + returning * into v_member; + + return v_member; +end; +$$; + +CREATE FUNCTION public.tenant_feature_allowed(p_tenant_id uuid, p_feature_key text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.v_tenant_entitlements v + where v.tenant_id = p_tenant_id + and v.feature_key = p_feature_key + and coalesce(v.allowed, false) = true + ); +$$; + +CREATE FUNCTION public.tenant_feature_enabled(p_tenant_id uuid, p_feature_key text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select coalesce( + (select tf.enabled + from public.tenant_features tf + where tf.tenant_id = p_tenant_id and tf.feature_key = p_feature_key), + false + ); +$$; + +CREATE FUNCTION public.tenant_features_guard_with_plan() RETURNS trigger + LANGUAGE plpgsql + AS $$ +declare + v_allowed boolean; +begin + -- s?? valida quando est?? habilitando + if new.enabled is distinct from true then + return new; + end if; + + -- permitido pelo plano do tenant? + select exists ( + select 1 + from public.v_tenant_entitlements_full v + where v.tenant_id = new.tenant_id + and v.feature_key = new.feature_key + and v.allowed = true + ) + into v_allowed; + + if not v_allowed then + raise exception 'Feature % n??o permitida pelo plano atual do tenant %.', + new.feature_key, new.tenant_id + using errcode = 'P0001'; + end if; + + return new; +end; +$$; + +CREATE FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select + exists ( + select 1 + from public.v_tenant_entitlements e + where e.tenant_id = _tenant_id + and e.feature_key = _feature + and e.allowed = true + ) + or exists ( + select 1 + from public.tenant_features tf + where tf.tenant_id = _tenant_id + and tf.feature_key = _feature + and tf.enabled = true + ); +$$; + +CREATE FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +declare + v_email text; + v_my_email text; + v_token uuid; + v_updated int; +begin + -- valida????es b??sicas + if p_tenant_id is null then + raise exception 'tenant_id inv??lido' using errcode = 'P0001'; + end if; + + v_email := lower(trim(coalesce(p_email, ''))); + if v_email = '' then + raise exception 'Informe um email' using errcode = 'P0001'; + end if; + + -- role permitido (ajuste se quiser) + if p_role is null or p_role not in ('therapist', 'secretary') then + raise exception 'Role inv??lido (use therapist/secretary)' using errcode = 'P0001'; + end if; + + -- ??? bloqueio: auto-convite + v_my_email := public.get_my_email(); + if v_my_email is not null and v_email = v_my_email then + raise exception 'Voc?? n??o pode convidar o seu pr??prio email.' using errcode = 'P0001'; + end if; + + -- ??? bloqueio: j?? ?? membro ativo do tenant + if exists ( + select 1 + from tenant_members tm + join auth.users au on au.id = tm.user_id + where tm.tenant_id = p_tenant_id + and tm.status = 'active' + and lower(au.email) = v_email + ) then + raise exception 'Este email j?? est?? vinculado a esta cl??nica.' using errcode = 'P0001'; + end if; + + -- ??? permiss??o: s?? admin do tenant pode convidar + if not exists ( + select 1 + from tenant_members me + where me.tenant_id = p_tenant_id + and me.user_id = auth.uid() + and me.status = 'active' + and me.role in ('tenant_admin','clinic_admin') + ) then + raise exception 'Sem permiss??o para convidar membros.' using errcode = 'P0001'; + end if; + + -- Gera token (reenvio simples / regenera????o) + v_token := gen_random_uuid(); + + -- 1) tenta "regerar" um convite pendente existente (mesmo email) + update tenant_invites + set token = v_token, + role = p_role, + created_at = now(), + expires_at = now() + interval '7 days', + accepted_at = null, + revoked_at = null + where tenant_id = p_tenant_id + and lower(email) = v_email + and accepted_at is null + and revoked_at is null; + + get diagnostics v_updated = row_count; + + -- 2) se n??o atualizou nada, cria convite novo + if v_updated = 0 then + insert into tenant_invites (tenant_id, email, role, token, created_at, expires_at) + values (p_tenant_id, v_email, p_role, v_token, now(), now() + interval '7 days'); + end if; + + return v_token; +end; +$$; + +CREATE FUNCTION public.tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + update public.tenant_members + set status = 'active' + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + +CREATE FUNCTION public.tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +declare + v_role text; +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + if p_member_user_id = auth.uid() then + raise exception 'cannot_remove_self'; + end if; + + -- pega role atual do membro (se n??o existir, erro) + select role into v_role + from public.tenant_members + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if v_role is null then + raise exception 'membership_not_found'; + end if; + + -- trava: se for therapist, n??o pode remover com eventos futuros + if v_role = 'therapist' then + if exists ( + select 1 + from public.agenda_eventos e + where e.owner_id = p_tenant_id + and e.terapeuta_id = p_member_user_id + and e.inicio_em >= now() + and e.status::text not in ('cancelado','cancelled','canceled') + limit 1 + ) then + raise exception 'cannot_remove_therapist_with_future_events'; + end if; + end if; + + update public.tenant_members + set status = 'inactive' + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + +CREATE FUNCTION public.tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + if p_member_user_id = auth.uid() then + raise exception 'cannot_remove_self'; + end if; + + update public.tenant_members + set status = 'inactive' + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + +CREATE FUNCTION public.tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +declare + v_email text; +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + v_email := lower(trim(p_email)); + + update public.tenant_invites + set revoked_at = now(), + revoked_by = auth.uid() + where tenant_id = p_tenant_id + and lower(email) = v_email + and role = p_role + and accepted_at is null + and revoked_at is null; + + if not found then + raise exception 'invite_not_found'; + end if; +end; +$$; + +CREATE FUNCTION public.tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + -- valida status (adapte aos seus valores reais) + if p_new_status not in ('active','inactive','suspended','invited') then + raise exception 'invalid_status: %', p_new_status; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + -- evita desativar a si mesmo (opcional) + if p_member_user_id = auth.uid() and p_new_status <> 'active' then + raise exception 'cannot_disable_self'; + end if; + + update public.tenant_members + set status = p_new_status + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + +CREATE FUNCTION public.tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + -- exige auth + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + -- valida role + if p_new_role not in ('tenant_admin','therapist','secretary','patient') then + raise exception 'invalid_role: %', p_new_role; + end if; + + -- somente tenant_admin ativo pode alterar role + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + -- evita o admin remover o pr??prio admin sem querer (opcional mas recomendado) + if p_member_user_id = auth.uid() and p_new_role <> 'tenant_admin' then + raise exception 'cannot_demote_self'; + end if; + + update public.tenant_members + set role = p_new_role + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + +CREATE FUNCTION public.toggle_plan(owner uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + current_key text; + new_key text; +begin + select p.key into current_key + from subscriptions s + join plans p on p.id = s.plan_id + where s.owner_id = owner + and s.status = 'active'; + + new_key := case + when current_key = 'pro' then 'free' + else 'pro' + end; + + update subscriptions s + set plan_id = p.id + from plans p + where p.key = new_key + and s.owner_id = owner + and s.status = 'active'; +end; +$$; + +CREATE FUNCTION public.transition_subscription(p_subscription_id uuid, p_to_status text, p_reason text DEFAULT NULL::text, p_metadata jsonb DEFAULT NULL::jsonb) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_uid uuid; + v_is_allowed boolean := false; +begin + v_uid := auth.uid(); + + select * + into v_sub + from public.subscriptions + where id = p_subscription_id; + + if not found then + raise exception 'Assinatura n??o encontrada'; + end if; + + -- ===================================================== + -- ???? BLOCO DE AUTORIZA????O + -- ===================================================== + + -- 1) SaaS admin pode tudo + if is_saas_admin() then + v_is_allowed := true; + end if; + + -- 2) Assinatura pessoal (therapist) + if not v_is_allowed + and v_sub.tenant_id is null + and v_sub.user_id = v_uid then + v_is_allowed := true; + end if; + + -- 3) Assinatura de clinic (tenant) + if not v_is_allowed + and v_sub.tenant_id is not null then + + if exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = v_sub.tenant_id + and tm.user_id = v_uid + and tm.status = 'active' + and tm.role = 'tenant_admin' + ) then + v_is_allowed := true; + end if; + + end if; + + if not v_is_allowed then + raise exception 'Sem permiss??o para transicionar esta assinatura'; + end if; + + -- ===================================================== + -- ???? TRANSI????O + -- ===================================================== + + update public.subscriptions + set status = p_to_status, + updated_at = now(), + cancelled_at = case when p_to_status = 'cancelled' then now() else cancelled_at end, + suspended_at = case when p_to_status = 'suspended' then now() else suspended_at end, + past_due_since = case when p_to_status = 'past_due' then now() else past_due_since end, + expired_at = case when p_to_status = 'expired' then now() else expired_at end, + activated_at = case when p_to_status = 'active' then now() else activated_at end + where id = p_subscription_id + returning * into v_sub; + + -- ===================================================== + -- ???? EVENT LOG + -- ===================================================== + + insert into public.subscription_events ( + subscription_id, + owner_id, + event_type, + created_at, + created_by, + source, + reason, + metadata, + owner_type, + owner_ref + ) + values ( + v_sub.id, + coalesce(v_sub.tenant_id, v_sub.user_id), + 'status_changed', + now(), + v_uid, + 'manual_transition', + p_reason, + p_metadata, + case when v_sub.tenant_id is not null then 'tenant' else 'personal' end, + coalesce(v_sub.tenant_id, v_sub.user_id) + ); + + return v_sub; +end; +$$; + +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; +$$; + +CREATE FUNCTION public.trg_fn_patient_risco_timeline() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF OLD.risco_elevado IS DISTINCT FROM NEW.risco_elevado THEN + INSERT INTO public.patient_timeline ( + patient_id, tenant_id, + evento_tipo, titulo, descricao, icone_cor, + gerado_por, ocorrido_em + ) VALUES ( + NEW.id, NEW.tenant_id, + CASE WHEN NEW.risco_elevado THEN 'risco_sinalizado' ELSE 'risco_removido' END, + CASE WHEN NEW.risco_elevado THEN 'Risco elevado sinalizado' ELSE 'Sinalização de risco removida' END, + NEW.risco_nota, + CASE WHEN NEW.risco_elevado THEN 'red' ELSE 'green' END, + auth.uid(), + now() + ); + END IF; + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.trg_fn_patient_status_history() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF (TG_OP = 'INSERT') OR (OLD.status IS DISTINCT FROM NEW.status) THEN + INSERT INTO public.patient_status_history ( + patient_id, tenant_id, + status_anterior, status_novo, + motivo, encaminhado_para, data_saida, + alterado_por, alterado_em + ) VALUES ( + NEW.id, NEW.tenant_id, + CASE WHEN TG_OP = 'INSERT' THEN NULL ELSE OLD.status END, + NEW.status, + NEW.motivo_saida, + NEW.encaminhado_para, + NEW.data_saida, + auth.uid(), + now() + ); + END IF; + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.trg_fn_patient_status_timeline() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF (TG_OP = 'INSERT') OR (OLD.status IS DISTINCT FROM NEW.status) THEN + INSERT INTO public.patient_timeline ( + patient_id, tenant_id, + evento_tipo, titulo, descricao, icone_cor, + gerado_por, ocorrido_em + ) VALUES ( + NEW.id, NEW.tenant_id, + 'status_alterado', + 'Status alterado para ' || NEW.status, + CASE + WHEN TG_OP = 'INSERT' THEN 'Paciente cadastrado' + ELSE 'De ' || OLD.status || ' → ' || NEW.status || + CASE WHEN NEW.motivo_saida IS NOT NULL THEN ' · ' || NEW.motivo_saida ELSE '' END + END, + CASE NEW.status + WHEN 'Ativo' THEN 'green' + WHEN 'Alta' THEN 'blue' + WHEN 'Inativo' THEN 'gray' + WHEN 'Encaminhado' THEN 'amber' + WHEN 'Arquivado' THEN 'gray' + ELSE 'gray' + END, + auth.uid(), + now() + ); + END IF; + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.trg_set_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ BEGIN + NEW.updated_at = now(); + RETURN NEW; + END; + $$; + +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; +$$; + +CREATE FUNCTION public.update_payment_settings_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.update_professional_pricing_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.user_has_feature(_user_id uuid, _feature text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.v_user_entitlements e + where e.user_id = _user_id + and e.feature_key = _feature + and e.allowed = true + ); +$$; + +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; +$$; + +CREATE FUNCTION public.whoami() RETURNS TABLE(uid uuid, role text) + LANGUAGE sql STABLE + AS $$ + select auth.uid() as uid, auth.role() as role; +$$; + +CREATE FUNCTION realtime.apply_rls(wal jsonb, max_record_bytes integer DEFAULT (1024 * 1024)) RETURNS SETOF realtime.wal_rls + LANGUAGE plpgsql + AS $$ +declare +-- Regclass of the table e.g. public.notes +entity_ regclass = (quote_ident(wal ->> 'schema') || '.' || quote_ident(wal ->> 'table'))::regclass; + +-- I, U, D, T: insert, update ... +action realtime.action = ( + case wal ->> 'action' + when 'I' then 'INSERT' + when 'U' then 'UPDATE' + when 'D' then 'DELETE' + else 'ERROR' + end +); + +-- Is row level security enabled for the table +is_rls_enabled bool = relrowsecurity from pg_class where oid = entity_; + +subscriptions realtime.subscription[] = array_agg(subs) + from + realtime.subscription subs + where + subs.entity = entity_; + +-- Subscription vars +roles regrole[] = array_agg(distinct us.claims_role::text) + from + unnest(subscriptions) us; + +working_role regrole; +claimed_role regrole; +claims jsonb; + +subscription_id uuid; +subscription_has_access bool; +visible_to_subscription_ids uuid[] = '{}'; + +-- structured info for wal's columns +columns realtime.wal_column[]; +-- previous identity values for update/delete +old_columns realtime.wal_column[]; + +error_record_exceeds_max_size boolean = octet_length(wal::text) > max_record_bytes; + +-- Primary jsonb output for record +output jsonb; + +begin +perform set_config('role', null, true); + +columns = + array_agg( + ( + x->>'name', + x->>'type', + x->>'typeoid', + realtime.cast( + (x->'value') #>> '{}', + coalesce( + (x->>'typeoid')::regtype, -- null when wal2json version <= 2.4 + (x->>'type')::regtype + ) + ), + (pks ->> 'name') is not null, + true + )::realtime.wal_column + ) + from + jsonb_array_elements(wal -> 'columns') x + left join jsonb_array_elements(wal -> 'pk') pks + on (x ->> 'name') = (pks ->> 'name'); + +old_columns = + array_agg( + ( + x->>'name', + x->>'type', + x->>'typeoid', + realtime.cast( + (x->'value') #>> '{}', + coalesce( + (x->>'typeoid')::regtype, -- null when wal2json version <= 2.4 + (x->>'type')::regtype + ) + ), + (pks ->> 'name') is not null, + true + )::realtime.wal_column + ) + from + jsonb_array_elements(wal -> 'identity') x + left join jsonb_array_elements(wal -> 'pk') pks + on (x ->> 'name') = (pks ->> 'name'); + +for working_role in select * from unnest(roles) loop + + -- Update `is_selectable` for columns and old_columns + columns = + array_agg( + ( + c.name, + c.type_name, + c.type_oid, + c.value, + c.is_pkey, + pg_catalog.has_column_privilege(working_role, entity_, c.name, 'SELECT') + )::realtime.wal_column + ) + from + unnest(columns) c; + + old_columns = + array_agg( + ( + c.name, + c.type_name, + c.type_oid, + c.value, + c.is_pkey, + pg_catalog.has_column_privilege(working_role, entity_, c.name, 'SELECT') + )::realtime.wal_column + ) + from + unnest(old_columns) c; + + if action <> 'DELETE' and count(1) = 0 from unnest(columns) c where c.is_pkey then + return next ( + jsonb_build_object( + 'schema', wal ->> 'schema', + 'table', wal ->> 'table', + 'type', action + ), + is_rls_enabled, + -- subscriptions is already filtered by entity + (select array_agg(s.subscription_id) from unnest(subscriptions) as s where claims_role = working_role), + array['Error 400: Bad Request, no primary key'] + )::realtime.wal_rls; + + -- The claims role does not have SELECT permission to the primary key of entity + elsif action <> 'DELETE' and sum(c.is_selectable::int) <> count(1) from unnest(columns) c where c.is_pkey then + return next ( + jsonb_build_object( + 'schema', wal ->> 'schema', + 'table', wal ->> 'table', + 'type', action + ), + is_rls_enabled, + (select array_agg(s.subscription_id) from unnest(subscriptions) as s where claims_role = working_role), + array['Error 401: Unauthorized'] + )::realtime.wal_rls; + + else + output = jsonb_build_object( + 'schema', wal ->> 'schema', + 'table', wal ->> 'table', + 'type', action, + 'commit_timestamp', to_char( + ((wal ->> 'timestamp')::timestamptz at time zone 'utc'), + 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"' + ), + 'columns', ( + select + jsonb_agg( + jsonb_build_object( + 'name', pa.attname, + 'type', pt.typname + ) + order by pa.attnum asc + ) + from + pg_attribute pa + join pg_type pt + on pa.atttypid = pt.oid + where + attrelid = entity_ + and attnum > 0 + and pg_catalog.has_column_privilege(working_role, entity_, pa.attname, 'SELECT') + ) + ) + -- Add "record" key for insert and update + || case + when action in ('INSERT', 'UPDATE') then + jsonb_build_object( + 'record', + ( + select + jsonb_object_agg( + -- if unchanged toast, get column name and value from old record + coalesce((c).name, (oc).name), + case + when (c).name is null then (oc).value + else (c).value + end + ) + from + unnest(columns) c + full outer join unnest(old_columns) oc + on (c).name = (oc).name + where + coalesce((c).is_selectable, (oc).is_selectable) + and ( not error_record_exceeds_max_size or (octet_length((c).value::text) <= 64)) + ) + ) + else '{}'::jsonb + end + -- Add "old_record" key for update and delete + || case + when action = 'UPDATE' then + jsonb_build_object( + 'old_record', + ( + select jsonb_object_agg((c).name, (c).value) + from unnest(old_columns) c + where + (c).is_selectable + and ( not error_record_exceeds_max_size or (octet_length((c).value::text) <= 64)) + ) + ) + when action = 'DELETE' then + jsonb_build_object( + 'old_record', + ( + select jsonb_object_agg((c).name, (c).value) + from unnest(old_columns) c + where + (c).is_selectable + and ( not error_record_exceeds_max_size or (octet_length((c).value::text) <= 64)) + and ( not is_rls_enabled or (c).is_pkey ) -- if RLS enabled, we can't secure deletes so filter to pkey + ) + ) + else '{}'::jsonb + end; + + -- Create the prepared statement + if is_rls_enabled and action <> 'DELETE' then + if (select 1 from pg_prepared_statements where name = 'walrus_rls_stmt' limit 1) > 0 then + deallocate walrus_rls_stmt; + end if; + execute realtime.build_prepared_statement_sql('walrus_rls_stmt', entity_, columns); + end if; + + visible_to_subscription_ids = '{}'; + + for subscription_id, claims in ( + select + subs.subscription_id, + subs.claims + from + unnest(subscriptions) subs + where + subs.entity = entity_ + and subs.claims_role = working_role + and ( + realtime.is_visible_through_filters(columns, subs.filters) + or ( + action = 'DELETE' + and realtime.is_visible_through_filters(old_columns, subs.filters) + ) + ) + ) loop + + if not is_rls_enabled or action = 'DELETE' then + visible_to_subscription_ids = visible_to_subscription_ids || subscription_id; + else + -- Check if RLS allows the role to see the record + perform + -- Trim leading and trailing quotes from working_role because set_config + -- doesn't recognize the role as valid if they are included + set_config('role', trim(both '"' from working_role::text), true), + set_config('request.jwt.claims', claims::text, true); + + execute 'execute walrus_rls_stmt' into subscription_has_access; + + if subscription_has_access then + visible_to_subscription_ids = visible_to_subscription_ids || subscription_id; + end if; + end if; + end loop; + + perform set_config('role', null, true); + + return next ( + output, + is_rls_enabled, + visible_to_subscription_ids, + case + when error_record_exceeds_max_size then array['Error 413: Payload Too Large'] + else '{}' + end + )::realtime.wal_rls; + + end if; +end loop; + +perform set_config('role', null, true); +end; +$$; + +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 $$ +DECLARE + -- Declare a variable to hold the JSONB representation of the row + row_data jsonb := '{}'::jsonb; +BEGIN + IF level = 'STATEMENT' THEN + RAISE EXCEPTION 'function can only be triggered for each row, not for each statement'; + END IF; + -- Check the operation type and handle accordingly + IF operation = 'INSERT' OR operation = 'UPDATE' OR operation = 'DELETE' THEN + row_data := jsonb_build_object('old_record', OLD, 'record', NEW, 'operation', operation, 'table', table_name, 'schema', table_schema); + PERFORM realtime.send (row_data, event_name, topic_name); + ELSE + RAISE EXCEPTION 'Unexpected operation type: %', operation; + END IF; +EXCEPTION + WHEN OTHERS THEN + RAISE EXCEPTION 'Failed to process the row: %', SQLERRM; +END; + +$$; + +CREATE FUNCTION realtime.build_prepared_statement_sql(prepared_statement_name text, entity regclass, columns realtime.wal_column[]) RETURNS text + LANGUAGE sql + AS $$ + /* + Builds a sql string that, if executed, creates a prepared statement to + tests retrive a row from *entity* by its primary key columns. + Example + select realtime.build_prepared_statement_sql('public.notes', '{"id"}'::text[], '{"bigint"}'::text[]) + */ + select + 'prepare ' || prepared_statement_name || ' as + select + exists( + select + 1 + from + ' || entity || ' + where + ' || string_agg(quote_ident(pkc.name) || '=' || quote_nullable(pkc.value #>> '{}') , ' and ') || ' + )' + from + unnest(columns) pkc + where + pkc.is_pkey + group by + entity + $$; + +CREATE FUNCTION realtime."cast"(val text, type_ regtype) RETURNS jsonb + LANGUAGE plpgsql IMMUTABLE + AS $$ + declare + res jsonb; + begin + execute format('select to_jsonb(%L::'|| type_::text || ')', val) into res; + return res; + end + $$; + +CREATE FUNCTION realtime.check_equality_op(op realtime.equality_op, type_ regtype, val_1 text, val_2 text) RETURNS boolean + LANGUAGE plpgsql IMMUTABLE + AS $$ + /* + Casts *val_1* and *val_2* as type *type_* and check the *op* condition for truthiness + */ + declare + op_symbol text = ( + case + when op = 'eq' then '=' + when op = 'neq' then '!=' + when op = 'lt' then '<' + when op = 'lte' then '<=' + when op = 'gt' then '>' + when op = 'gte' then '>=' + when op = 'in' then '= any' + else 'UNKNOWN OP' + end + ); + res boolean; + begin + execute format( + 'select %L::'|| type_::text || ' ' || op_symbol + || ' ( %L::' + || ( + case + when op = 'in' then type_::text || '[]' + else type_::text end + ) + || ')', val_1, val_2) into res; + return res; + end; + $$; + +CREATE FUNCTION realtime.is_visible_through_filters(columns realtime.wal_column[], filters realtime.user_defined_filter[]) RETURNS boolean + LANGUAGE sql IMMUTABLE + AS $_$ + /* + Should the record be visible (true) or filtered out (false) after *filters* are applied + */ + select + -- Default to allowed when no filters present + $2 is null -- no filters. this should not happen because subscriptions has a default + or array_length($2, 1) is null -- array length of an empty array is null + or bool_and( + coalesce( + realtime.check_equality_op( + op:=f.op, + type_:=coalesce( + col.type_oid::regtype, -- null when wal2json version <= 2.4 + col.type_name::regtype + ), + -- cast jsonb to text + val_1:=col.value #>> '{}', + val_2:=f.value + ), + false -- if null, filter does not match + ) + ) + from + unnest(filters) f + join unnest(columns) col + on f.column_name = col.name; + $_$; + +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' + AS $$ + with pub as ( + select + concat_ws( + ',', + case when bool_or(pubinsert) then 'insert' else null end, + case when bool_or(pubupdate) then 'update' else null end, + case when bool_or(pubdelete) then 'delete' else null end + ) as w2j_actions, + coalesce( + string_agg( + realtime.quote_wal2json(format('%I.%I', schemaname, tablename)::regclass), + ',' + ) filter (where ppt.tablename is not null and ppt.tablename not like '% %'), + '' + ) w2j_add_tables + from + pg_publication pp + left join pg_publication_tables ppt + on pp.pubname = ppt.pubname + where + pp.pubname = publication + group by + pp.pubname + limit 1 + ), + w2j as ( + select + x.*, pub.w2j_add_tables + from + pub, + pg_logical_slot_get_changes( + slot_name, null, max_changes, + 'include-pk', 'true', + 'include-transaction', 'false', + 'include-timestamp', 'true', + 'include-type-oids', 'true', + 'format-version', '2', + 'actions', pub.w2j_actions, + 'add-tables', pub.w2j_add_tables + ) x + ) + select + xyz.wal, + xyz.is_rls_enabled, + xyz.subscription_ids, + xyz.errors + from + w2j, + realtime.apply_rls( + wal := w2j.data::jsonb, + max_record_bytes := max_record_bytes + ) xyz(wal, is_rls_enabled, subscription_ids, errors) + where + w2j.w2j_add_tables <> '' + and xyz.subscription_ids[1] is not null + $$; + +CREATE FUNCTION realtime.quote_wal2json(entity regclass) RETURNS text + LANGUAGE sql IMMUTABLE STRICT + AS $$ + select + ( + select string_agg('' || ch,'') + from unnest(string_to_array(nsp.nspname::text, null)) with ordinality x(ch, idx) + where + not (x.idx = 1 and x.ch = '"') + and not ( + x.idx = array_length(string_to_array(nsp.nspname::text, null), 1) + and x.ch = '"' + ) + ) + || '.' + || ( + select string_agg('' || ch,'') + from unnest(string_to_array(pc.relname::text, null)) with ordinality x(ch, idx) + where + not (x.idx = 1 and x.ch = '"') + and not ( + x.idx = array_length(string_to_array(nsp.nspname::text, null), 1) + and x.ch = '"' + ) + ) + from + pg_class pc + join pg_namespace nsp + on pc.relnamespace = nsp.oid + where + pc.oid = entity + $$; + +CREATE FUNCTION realtime.send(payload jsonb, event text, topic text, private boolean DEFAULT true) RETURNS void + LANGUAGE plpgsql + AS $$ +DECLARE + generated_id uuid; + final_payload jsonb; +BEGIN + BEGIN + -- Generate a new UUID for the id + generated_id := gen_random_uuid(); + + -- Check if payload has an 'id' key, if not, add the generated UUID + IF payload ? 'id' THEN + final_payload := payload; + ELSE + final_payload := jsonb_set(payload, '{id}', to_jsonb(generated_id)); + END IF; + + -- Set the topic configuration + EXECUTE format('SET LOCAL realtime.topic TO %L', topic); + + -- Attempt to insert the message + INSERT INTO realtime.messages (id, payload, event, topic, private, extension) + VALUES (generated_id, final_payload, event, topic, private, 'broadcast'); + EXCEPTION + WHEN OTHERS THEN + -- Capture and notify the error + RAISE WARNING 'ErrorSendingBroadcastMessage: %', SQLERRM; + END; +END; +$$; + +CREATE FUNCTION realtime.subscription_check_filters() RETURNS trigger + LANGUAGE plpgsql + AS $$ + /* + Validates that the user defined filters for a subscription: + - refer to valid columns that the claimed role may access + - values are coercable to the correct column type + */ + declare + col_names text[] = coalesce( + array_agg(c.column_name order by c.ordinal_position), + '{}'::text[] + ) + from + information_schema.columns c + where + format('%I.%I', c.table_schema, c.table_name)::regclass = new.entity + and pg_catalog.has_column_privilege( + (new.claims ->> 'role'), + format('%I.%I', c.table_schema, c.table_name)::regclass, + c.column_name, + 'SELECT' + ); + filter realtime.user_defined_filter; + col_type regtype; + + in_val jsonb; + begin + for filter in select * from unnest(new.filters) loop + -- Filtered column is valid + if not filter.column_name = any(col_names) then + raise exception 'invalid column for filter %', filter.column_name; + end if; + + -- Type is sanitized and safe for string interpolation + col_type = ( + select atttypid::regtype + from pg_catalog.pg_attribute + where attrelid = new.entity + and attname = filter.column_name + ); + if col_type is null then + raise exception 'failed to lookup type for column %', filter.column_name; + end if; + + -- Set maximum number of entries for in filter + if filter.op = 'in'::realtime.equality_op then + in_val = realtime.cast(filter.value, (col_type::text || '[]')::regtype); + if coalesce(jsonb_array_length(in_val), 0) > 100 then + raise exception 'too many values for `in` filter. Maximum 100'; + end if; + else + -- raises an exception if value is not coercable to type + perform realtime.cast(filter.value, col_type); + end if; + + end loop; + + -- Apply consistent order to filters so the unique constraint on + -- (subscription_id, entity, filters) can't be tricked by a different filter order + new.filters = coalesce( + array_agg(f order by f.column_name, f.op, f.value), + '{}' + ) from unnest(new.filters) f; + + return new; + end; + $$; + +CREATE FUNCTION realtime.to_regrole(role_name text) RETURNS regrole + LANGUAGE sql IMMUTABLE + AS $$ select role_name::regrole $$; + +CREATE FUNCTION realtime.topic() RETURNS text + LANGUAGE sql STABLE + AS $$ +select nullif(current_setting('realtime.topic', true), '')::text; +$$; + +CREATE FUNCTION storage.can_insert_object(bucketid text, name text, owner uuid, metadata jsonb) RETURNS void + LANGUAGE plpgsql + AS $$ +BEGIN + INSERT INTO "storage"."objects" ("bucket_id", "name", "owner", "metadata") VALUES (bucketid, name, owner, metadata); + -- hack to rollback the successful insert + RAISE sqlstate 'PT200' using + message = 'ROLLBACK', + detail = 'rollback successful insert'; +END +$$; + +CREATE FUNCTION storage.enforce_bucket_name_length() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if length(new.name) > 100 then + raise exception 'bucket name "%" is too long (% characters). Max is 100.', new.name, length(new.name); + end if; + return new; +end; +$$; + +CREATE FUNCTION storage.extension(name text) RETURNS text + LANGUAGE plpgsql + AS $$ +DECLARE +_parts text[]; +_filename text; +BEGIN + select string_to_array(name, '/') into _parts; + select _parts[array_length(_parts,1)] into _filename; + -- @todo return the last part instead of 2 + return reverse(split_part(reverse(_filename), '.', 1)); +END +$$; + +CREATE FUNCTION storage.filename(name text) RETURNS text + LANGUAGE plpgsql + AS $$ +DECLARE +_parts text[]; +BEGIN + select string_to_array(name, '/') into _parts; + return _parts[array_length(_parts,1)]; +END +$$; + +CREATE FUNCTION storage.foldername(name text) RETURNS text[] + LANGUAGE plpgsql + AS $$ +DECLARE +_parts text[]; +BEGIN + select string_to_array(name, '/') into _parts; + return _parts[1:array_length(_parts,1)-1]; +END +$$; + +CREATE FUNCTION storage.get_common_prefix(p_key text, p_prefix text, p_delimiter text) RETURNS text + LANGUAGE sql IMMUTABLE + AS $$ +SELECT CASE + WHEN position(p_delimiter IN substring(p_key FROM length(p_prefix) + 1)) > 0 + THEN left(p_key, length(p_prefix) + position(p_delimiter IN substring(p_key FROM length(p_prefix) + 1))) + ELSE NULL +END; +$$; + +CREATE FUNCTION storage.get_size_by_bucket() RETURNS TABLE(size bigint, bucket_id text) + LANGUAGE plpgsql + AS $$ +BEGIN + return query + select sum((metadata->>'size')::int) as size, obj.bucket_id + from "storage".objects as obj + group by obj.bucket_id; +END +$$; + +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 $_$ +BEGIN + RETURN QUERY EXECUTE + 'SELECT DISTINCT ON(key COLLATE "C") * from ( + SELECT + CASE + WHEN position($2 IN substring(key from length($1) + 1)) > 0 THEN + substring(key from 1 for length($1) + position($2 IN substring(key from length($1) + 1))) + ELSE + key + END AS key, id, created_at + FROM + storage.s3_multipart_uploads + WHERE + bucket_id = $5 AND + key ILIKE $1 || ''%'' AND + CASE + WHEN $4 != '''' AND $6 = '''' THEN + CASE + WHEN position($2 IN substring(key from length($1) + 1)) > 0 THEN + substring(key from 1 for length($1) + position($2 IN substring(key from length($1) + 1))) COLLATE "C" > $4 + ELSE + key COLLATE "C" > $4 + END + ELSE + true + END AND + CASE + WHEN $6 != '''' THEN + id COLLATE "C" > $6 + ELSE + true + END + ORDER BY + key COLLATE "C" ASC, created_at ASC) as e order by key COLLATE "C" LIMIT $3' + USING prefix_param, delimiter_param, max_keys, next_key_token, bucket_id, next_upload_token; +END; +$_$; + +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 $_$ +DECLARE + v_peek_name TEXT; + v_current RECORD; + v_common_prefix TEXT; + + -- Configuration + v_is_asc BOOLEAN; + v_prefix TEXT; + v_start TEXT; + v_upper_bound TEXT; + v_file_batch_size INT; + + -- Seek state + v_next_seek TEXT; + v_count INT := 0; + + -- Dynamic SQL for batch query only + v_batch_query TEXT; + +BEGIN + -- ======================================================================== + -- INITIALIZATION + -- ======================================================================== + v_is_asc := lower(coalesce(sort_order, 'asc')) = 'asc'; + v_prefix := coalesce(prefix_param, ''); + v_start := CASE WHEN coalesce(next_token, '') <> '' THEN next_token ELSE coalesce(start_after, '') END; + v_file_batch_size := LEAST(GREATEST(max_keys * 2, 100), 1000); + + -- Calculate upper bound for prefix filtering (bytewise, using COLLATE "C") + IF v_prefix = '' THEN + v_upper_bound := NULL; + ELSIF right(v_prefix, 1) = delimiter_param THEN + v_upper_bound := left(v_prefix, -1) || chr(ascii(delimiter_param) + 1); + ELSE + v_upper_bound := left(v_prefix, -1) || chr(ascii(right(v_prefix, 1)) + 1); + END IF; + + -- Build batch query (dynamic SQL - called infrequently, amortized over many rows) + IF v_is_asc THEN + IF v_upper_bound IS NOT NULL THEN + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND o.name COLLATE "C" >= $2 ' || + 'AND o.name COLLATE "C" < $3 ORDER BY o.name COLLATE "C" ASC LIMIT $4'; + ELSE + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND o.name COLLATE "C" >= $2 ' || + 'ORDER BY o.name COLLATE "C" ASC LIMIT $4'; + END IF; + ELSE + IF v_upper_bound IS NOT NULL THEN + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND o.name COLLATE "C" < $2 ' || + 'AND o.name COLLATE "C" >= $3 ORDER BY o.name COLLATE "C" DESC LIMIT $4'; + ELSE + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND o.name COLLATE "C" < $2 ' || + 'ORDER BY o.name COLLATE "C" DESC LIMIT $4'; + END IF; + END IF; + + -- ======================================================================== + -- SEEK INITIALIZATION: Determine starting position + -- ======================================================================== + IF v_start = '' THEN + IF v_is_asc THEN + v_next_seek := v_prefix; + ELSE + -- DESC without cursor: find the last item in range + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_next_seek FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" >= v_prefix AND o.name COLLATE "C" < v_upper_bound + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + ELSIF v_prefix <> '' THEN + SELECT o.name INTO v_next_seek FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" >= v_prefix + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + ELSE + SELECT o.name INTO v_next_seek FROM storage.objects o + WHERE o.bucket_id = _bucket_id + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + END IF; + + IF v_next_seek IS NOT NULL THEN + v_next_seek := v_next_seek || delimiter_param; + ELSE + RETURN; + END IF; + END IF; + ELSE + -- Cursor provided: determine if it refers to a folder or leaf + IF EXISTS ( + SELECT 1 FROM storage.objects o + WHERE o.bucket_id = _bucket_id + AND o.name COLLATE "C" LIKE v_start || delimiter_param || '%' + LIMIT 1 + ) THEN + -- Cursor refers to a folder + IF v_is_asc THEN + v_next_seek := v_start || chr(ascii(delimiter_param) + 1); + ELSE + v_next_seek := v_start || delimiter_param; + END IF; + ELSE + -- Cursor refers to a leaf object + IF v_is_asc THEN + v_next_seek := v_start || delimiter_param; + ELSE + v_next_seek := v_start; + END IF; + END IF; + END IF; + + -- ======================================================================== + -- MAIN LOOP: Hybrid peek-then-batch algorithm + -- Uses STATIC SQL for peek (hot path) and DYNAMIC SQL for batch + -- ======================================================================== + LOOP + EXIT WHEN v_count >= max_keys; + + -- STEP 1: PEEK using STATIC SQL (plan cached, very fast) + IF v_is_asc THEN + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" >= v_next_seek AND o.name COLLATE "C" < v_upper_bound + ORDER BY o.name COLLATE "C" ASC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" >= v_next_seek + ORDER BY o.name COLLATE "C" ASC LIMIT 1; + END IF; + ELSE + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" < v_next_seek AND o.name COLLATE "C" >= v_prefix + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + ELSIF v_prefix <> '' THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" < v_next_seek AND o.name COLLATE "C" >= v_prefix + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" < v_next_seek + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + END IF; + END IF; + + EXIT WHEN v_peek_name IS NULL; + + -- STEP 2: Check if this is a FOLDER or FILE + v_common_prefix := storage.get_common_prefix(v_peek_name, v_prefix, delimiter_param); + + IF v_common_prefix IS NOT NULL THEN + -- FOLDER: Emit and skip to next folder (no heap access needed) + name := rtrim(v_common_prefix, delimiter_param); + id := NULL; + updated_at := NULL; + created_at := NULL; + last_accessed_at := NULL; + metadata := NULL; + RETURN NEXT; + v_count := v_count + 1; + + -- Advance seek past the folder range + IF v_is_asc THEN + v_next_seek := left(v_common_prefix, -1) || chr(ascii(delimiter_param) + 1); + ELSE + v_next_seek := v_common_prefix; + END IF; + ELSE + -- FILE: Batch fetch using DYNAMIC SQL (overhead amortized over many rows) + -- For ASC: upper_bound is the exclusive upper limit (< condition) + -- For DESC: prefix is the inclusive lower limit (>= condition) + FOR v_current IN EXECUTE v_batch_query USING _bucket_id, v_next_seek, + CASE WHEN v_is_asc THEN COALESCE(v_upper_bound, v_prefix) ELSE v_prefix END, v_file_batch_size + LOOP + v_common_prefix := storage.get_common_prefix(v_current.name, v_prefix, delimiter_param); + + IF v_common_prefix IS NOT NULL THEN + -- Hit a folder: exit batch, let peek handle it + v_next_seek := v_current.name; + EXIT; + END IF; + + -- Emit file + name := v_current.name; + id := v_current.id; + updated_at := v_current.updated_at; + created_at := v_current.created_at; + last_accessed_at := v_current.last_accessed_at; + metadata := v_current.metadata; + RETURN NEXT; + v_count := v_count + 1; + + -- Advance seek past this file + IF v_is_asc THEN + v_next_seek := v_current.name || delimiter_param; + ELSE + v_next_seek := v_current.name; + END IF; + + EXIT WHEN v_count >= max_keys; + END LOOP; + END IF; + END LOOP; +END; +$_$; + +CREATE FUNCTION storage.operation() RETURNS text + LANGUAGE plpgsql STABLE + AS $$ +BEGIN + RETURN current_setting('storage.operation', true); +END; +$$; + +CREATE FUNCTION storage.protect_delete() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + -- Check if storage.allow_delete_query is set to 'true' + IF COALESCE(current_setting('storage.allow_delete_query', true), 'false') != 'true' THEN + RAISE EXCEPTION 'Direct deletion from storage tables is not allowed. Use the Storage API instead.' + USING HINT = 'This prevents accidental data loss from orphaned objects.', + ERRCODE = '42501'; + END IF; + RETURN NULL; +END; +$$; + +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 $_$ +DECLARE + v_peek_name TEXT; + v_current RECORD; + v_common_prefix TEXT; + v_delimiter CONSTANT TEXT := '/'; + + -- Configuration + v_limit INT; + v_prefix TEXT; + v_prefix_lower TEXT; + v_is_asc BOOLEAN; + v_order_by TEXT; + v_sort_order TEXT; + v_upper_bound TEXT; + v_file_batch_size INT; + + -- Dynamic SQL for batch query only + v_batch_query TEXT; + + -- Seek state + v_next_seek TEXT; + v_count INT := 0; + v_skipped INT := 0; +BEGIN + -- ======================================================================== + -- INITIALIZATION + -- ======================================================================== + v_limit := LEAST(coalesce(limits, 100), 1500); + v_prefix := coalesce(prefix, '') || coalesce(search, ''); + v_prefix_lower := lower(v_prefix); + v_is_asc := lower(coalesce(sortorder, 'asc')) = 'asc'; + v_file_batch_size := LEAST(GREATEST(v_limit * 2, 100), 1000); + + -- Validate sort column + CASE lower(coalesce(sortcolumn, 'name')) + WHEN 'name' THEN v_order_by := 'name'; + WHEN 'updated_at' THEN v_order_by := 'updated_at'; + WHEN 'created_at' THEN v_order_by := 'created_at'; + WHEN 'last_accessed_at' THEN v_order_by := 'last_accessed_at'; + ELSE v_order_by := 'name'; + END CASE; + + v_sort_order := CASE WHEN v_is_asc THEN 'asc' ELSE 'desc' END; + + -- ======================================================================== + -- NON-NAME SORTING: Use path_tokens approach (unchanged) + -- ======================================================================== + IF v_order_by != 'name' THEN + RETURN QUERY EXECUTE format( + $sql$ + WITH folders AS ( + SELECT path_tokens[$1] AS folder + FROM storage.objects + WHERE objects.name ILIKE $2 || '%%' + AND bucket_id = $3 + AND array_length(objects.path_tokens, 1) <> $1 + GROUP BY folder + ORDER BY folder %s + ) + (SELECT folder AS "name", + NULL::uuid AS id, + NULL::timestamptz AS updated_at, + NULL::timestamptz AS created_at, + NULL::timestamptz AS last_accessed_at, + NULL::jsonb AS metadata FROM folders) + UNION ALL + (SELECT path_tokens[$1] AS "name", + id, updated_at, created_at, last_accessed_at, metadata + FROM storage.objects + WHERE objects.name ILIKE $2 || '%%' + AND bucket_id = $3 + AND array_length(objects.path_tokens, 1) = $1 + ORDER BY %I %s) + LIMIT $4 OFFSET $5 + $sql$, v_sort_order, v_order_by, v_sort_order + ) USING levels, v_prefix, bucketname, v_limit, offsets; + RETURN; + END IF; + + -- ======================================================================== + -- NAME SORTING: Hybrid skip-scan with batch optimization + -- ======================================================================== + + -- Calculate upper bound for prefix filtering + IF v_prefix_lower = '' THEN + v_upper_bound := NULL; + ELSIF right(v_prefix_lower, 1) = v_delimiter THEN + v_upper_bound := left(v_prefix_lower, -1) || chr(ascii(v_delimiter) + 1); + ELSE + v_upper_bound := left(v_prefix_lower, -1) || chr(ascii(right(v_prefix_lower, 1)) + 1); + END IF; + + -- Build batch query (dynamic SQL - called infrequently, amortized over many rows) + IF v_is_asc THEN + IF v_upper_bound IS NOT NULL THEN + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND lower(o.name) COLLATE "C" >= $2 ' || + 'AND lower(o.name) COLLATE "C" < $3 ORDER BY lower(o.name) COLLATE "C" ASC LIMIT $4'; + ELSE + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND lower(o.name) COLLATE "C" >= $2 ' || + 'ORDER BY lower(o.name) COLLATE "C" ASC LIMIT $4'; + END IF; + ELSE + IF v_upper_bound IS NOT NULL THEN + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND lower(o.name) COLLATE "C" < $2 ' || + 'AND lower(o.name) COLLATE "C" >= $3 ORDER BY lower(o.name) COLLATE "C" DESC LIMIT $4'; + ELSE + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND lower(o.name) COLLATE "C" < $2 ' || + 'ORDER BY lower(o.name) COLLATE "C" DESC LIMIT $4'; + END IF; + END IF; + + -- Initialize seek position + IF v_is_asc THEN + v_next_seek := v_prefix_lower; + ELSE + -- DESC: find the last item in range first (static SQL) + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" >= v_prefix_lower AND lower(o.name) COLLATE "C" < v_upper_bound + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + ELSIF v_prefix_lower <> '' THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" >= v_prefix_lower + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + END IF; + + IF v_peek_name IS NOT NULL THEN + v_next_seek := lower(v_peek_name) || v_delimiter; + ELSE + RETURN; + END IF; + END IF; + + -- ======================================================================== + -- MAIN LOOP: Hybrid peek-then-batch algorithm + -- Uses STATIC SQL for peek (hot path) and DYNAMIC SQL for batch + -- ======================================================================== + LOOP + EXIT WHEN v_count >= v_limit; + + -- STEP 1: PEEK using STATIC SQL (plan cached, very fast) + IF v_is_asc THEN + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" >= v_next_seek AND lower(o.name) COLLATE "C" < v_upper_bound + ORDER BY lower(o.name) COLLATE "C" ASC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" >= v_next_seek + ORDER BY lower(o.name) COLLATE "C" ASC LIMIT 1; + END IF; + ELSE + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" < v_next_seek AND lower(o.name) COLLATE "C" >= v_prefix_lower + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + ELSIF v_prefix_lower <> '' THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" < v_next_seek AND lower(o.name) COLLATE "C" >= v_prefix_lower + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" < v_next_seek + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + END IF; + END IF; + + EXIT WHEN v_peek_name IS NULL; + + -- STEP 2: Check if this is a FOLDER or FILE + v_common_prefix := storage.get_common_prefix(lower(v_peek_name), v_prefix_lower, v_delimiter); + + IF v_common_prefix IS NOT NULL THEN + -- FOLDER: Handle offset, emit if needed, skip to next folder + IF v_skipped < offsets THEN + v_skipped := v_skipped + 1; + ELSE + name := split_part(rtrim(v_common_prefix, v_delimiter), v_delimiter, levels); + id := NULL; + updated_at := NULL; + created_at := NULL; + last_accessed_at := NULL; + metadata := NULL; + RETURN NEXT; + v_count := v_count + 1; + END IF; + + -- Advance seek past the folder range + IF v_is_asc THEN + v_next_seek := lower(left(v_common_prefix, -1)) || chr(ascii(v_delimiter) + 1); + ELSE + v_next_seek := lower(v_common_prefix); + END IF; + ELSE + -- FILE: Batch fetch using DYNAMIC SQL (overhead amortized over many rows) + -- For ASC: upper_bound is the exclusive upper limit (< condition) + -- For DESC: prefix_lower is the inclusive lower limit (>= condition) + FOR v_current IN EXECUTE v_batch_query + USING bucketname, v_next_seek, + CASE WHEN v_is_asc THEN COALESCE(v_upper_bound, v_prefix_lower) ELSE v_prefix_lower END, v_file_batch_size + LOOP + v_common_prefix := storage.get_common_prefix(lower(v_current.name), v_prefix_lower, v_delimiter); + + IF v_common_prefix IS NOT NULL THEN + -- Hit a folder: exit batch, let peek handle it + v_next_seek := lower(v_current.name); + EXIT; + END IF; + + -- Handle offset skipping + IF v_skipped < offsets THEN + v_skipped := v_skipped + 1; + ELSE + -- Emit file + name := split_part(v_current.name, v_delimiter, levels); + id := v_current.id; + updated_at := v_current.updated_at; + created_at := v_current.created_at; + last_accessed_at := v_current.last_accessed_at; + metadata := v_current.metadata; + RETURN NEXT; + v_count := v_count + 1; + END IF; + + -- Advance seek past this file + IF v_is_asc THEN + v_next_seek := lower(v_current.name) || v_delimiter; + ELSE + v_next_seek := lower(v_current.name); + END IF; + + EXIT WHEN v_count >= v_limit; + END LOOP; + END IF; + END LOOP; +END; +$_$; + +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 $_$ +DECLARE + v_cursor_op text; + v_query text; + v_prefix text; +BEGIN + v_prefix := coalesce(p_prefix, ''); + + IF p_sort_order = 'asc' THEN + v_cursor_op := '>'; + ELSE + v_cursor_op := '<'; + END IF; + + v_query := format($sql$ + WITH raw_objects AS ( + SELECT + o.name AS obj_name, + o.id AS obj_id, + o.updated_at AS obj_updated_at, + o.created_at AS obj_created_at, + o.last_accessed_at AS obj_last_accessed_at, + o.metadata AS obj_metadata, + storage.get_common_prefix(o.name, $1, '/') AS common_prefix + FROM storage.objects o + WHERE o.bucket_id = $2 + AND o.name COLLATE "C" LIKE $1 || '%%' + ), + -- Aggregate common prefixes (folders) + -- Both created_at and updated_at use MIN(obj_created_at) to match the old prefixes table behavior + aggregated_prefixes AS ( + SELECT + rtrim(common_prefix, '/') AS name, + NULL::uuid AS id, + MIN(obj_created_at) AS updated_at, + MIN(obj_created_at) AS created_at, + NULL::timestamptz AS last_accessed_at, + NULL::jsonb AS metadata, + TRUE AS is_prefix + FROM raw_objects + WHERE common_prefix IS NOT NULL + GROUP BY common_prefix + ), + leaf_objects AS ( + SELECT + obj_name AS name, + obj_id AS id, + obj_updated_at AS updated_at, + obj_created_at AS created_at, + obj_last_accessed_at AS last_accessed_at, + obj_metadata AS metadata, + FALSE AS is_prefix + FROM raw_objects + WHERE common_prefix IS NULL + ), + combined AS ( + SELECT * FROM aggregated_prefixes + UNION ALL + SELECT * FROM leaf_objects + ), + filtered AS ( + SELECT * + FROM combined + WHERE ( + $5 = '' + OR ROW( + date_trunc('milliseconds', %I), + name COLLATE "C" + ) %s ROW( + COALESCE(NULLIF($6, '')::timestamptz, 'epoch'::timestamptz), + $5 + ) + ) + ) + SELECT + split_part(name, '/', $3) AS key, + name, + id, + updated_at, + created_at, + last_accessed_at, + metadata + FROM filtered + ORDER BY + COALESCE(date_trunc('milliseconds', %I), 'epoch'::timestamptz) %s, + name COLLATE "C" %s + LIMIT $4 + $sql$, + p_sort_column, + v_cursor_op, + p_sort_column, + p_sort_order, + p_sort_order + ); + + RETURN QUERY EXECUTE v_query + USING v_prefix, p_bucket_id, p_level, p_limit, p_start_after, p_sort_column_after; +END; +$_$; + +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 $$ +DECLARE + v_sort_col text; + v_sort_ord text; + v_limit int; +BEGIN + -- Cap limit to maximum of 1500 records + v_limit := LEAST(coalesce(limits, 100), 1500); + + -- Validate and normalize sort_order + v_sort_ord := lower(coalesce(sort_order, 'asc')); + IF v_sort_ord NOT IN ('asc', 'desc') THEN + v_sort_ord := 'asc'; + END IF; + + -- Validate and normalize sort_column + v_sort_col := lower(coalesce(sort_column, 'name')); + IF v_sort_col NOT IN ('name', 'updated_at', 'created_at') THEN + v_sort_col := 'name'; + END IF; + + -- Route to appropriate implementation + IF v_sort_col = 'name' THEN + -- Use list_objects_with_delimiter for name sorting (most efficient: O(k * log n)) + RETURN QUERY + SELECT + split_part(l.name, '/', levels) AS key, + l.name AS name, + l.id, + l.updated_at, + l.created_at, + l.last_accessed_at, + l.metadata + FROM storage.list_objects_with_delimiter( + bucket_name, + coalesce(prefix, ''), + '/', + v_limit, + start_after, + '', + v_sort_ord + ) l; + ELSE + -- Use aggregation approach for timestamp sorting + -- Not efficient for large datasets but supports correct pagination + RETURN QUERY SELECT * FROM storage.search_by_timestamp( + prefix, bucket_name, v_limit, levels, start_after, + v_sort_ord, v_sort_col, sort_column_after + ); + END IF; +END; +$$; + +CREATE FUNCTION storage.update_updated_at_column() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + +CREATE FUNCTION supabase_functions.http_request() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'supabase_functions' + AS $$ + DECLARE + request_id bigint; + payload jsonb; + url text := TG_ARGV[0]::text; + method text := TG_ARGV[1]::text; + headers jsonb DEFAULT '{}'::jsonb; + params jsonb DEFAULT '{}'::jsonb; + timeout_ms integer DEFAULT 1000; + BEGIN + IF url IS NULL OR url = 'null' THEN + RAISE EXCEPTION 'url argument is missing'; + END IF; + + IF method IS NULL OR method = 'null' THEN + RAISE EXCEPTION 'method argument is missing'; + END IF; + + IF TG_ARGV[2] IS NULL OR TG_ARGV[2] = 'null' THEN + headers = '{"Content-Type": "application/json"}'::jsonb; + ELSE + headers = TG_ARGV[2]::jsonb; + END IF; + + IF TG_ARGV[3] IS NULL OR TG_ARGV[3] = 'null' THEN + params = '{}'::jsonb; + ELSE + params = TG_ARGV[3]::jsonb; + END IF; + + IF TG_ARGV[4] IS NULL OR TG_ARGV[4] = 'null' THEN + timeout_ms = 1000; + ELSE + timeout_ms = TG_ARGV[4]::integer; + END IF; + + CASE + WHEN method = 'GET' THEN + SELECT http_get INTO request_id FROM net.http_get( + url, + params, + headers, + timeout_ms + ); + WHEN method = 'POST' THEN + payload = jsonb_build_object( + 'old_record', OLD, + 'record', NEW, + 'type', TG_OP, + 'table', TG_TABLE_NAME, + 'schema', TG_TABLE_SCHEMA + ); + + SELECT http_post INTO request_id FROM net.http_post( + url, + payload, + params, + headers, + timeout_ms + ); + ELSE + RAISE EXCEPTION 'method argument % is invalid', method; + END CASE; + + INSERT INTO supabase_functions.hooks + (hook_table_id, hook_name, request_id) + VALUES + (TG_RELID, TG_NAME, request_id); + + RETURN NEW; + END +$$; diff --git a/database-novo/schema/03_functions/agenda.sql b/database-novo/schema/03_functions/agenda.sql deleted file mode 100644 index 15e55c3..0000000 --- a/database-novo/schema/03_functions/agenda.sql +++ /dev/null @@ -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 diff --git a/database-novo/schema/03_functions/auth.sql b/database-novo/schema/03_functions/auth.sql index a8b366d..c646c30 100644 --- a/database-novo/schema/03_functions/auth.sql +++ b/database-novo/schema/03_functions/auth.sql @@ -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.'; - diff --git a/database-novo/schema/03_functions/billing.sql b/database-novo/schema/03_functions/billing.sql deleted file mode 100644 index 20de4e3..0000000 --- a/database-novo/schema/03_functions/billing.sql +++ /dev/null @@ -1,1283 +0,0 @@ --- ============================================================================= --- AgenciaPsi — Functions — Plans, Subscriptions, Billing --- ============================================================================= --- activate_subscription_from_intent, cancel_subscription, --- change_subscription_plan, toggle_plan, transition_subscription, --- reactivate_subscription, rebuild_owner_entitlements, --- fix_all_subscription_mismatches, subscription_intents_view_insert, --- subscriptions_validate_scope, admin_fix_plan_target, --- set_tenant_feature_exception, guard_no_change_core_plan_key, etc. --- ============================================================================= - -CREATE FUNCTION public.activate_subscription_from_intent(p_intent_id uuid) RETURNS public.subscriptions - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -declare - v_intent record; - v_sub public.subscriptions; - v_days int; - v_user_id uuid; - v_plan_id uuid; - v_target text; -begin - -- lê pela VIEW unificada - select * into v_intent - from public.subscription_intents - where id = p_intent_id; - - if not found then - raise exception 'Intent não encontrado: %', p_intent_id; - end if; - - if v_intent.status <> 'paid' then - raise exception 'Intent precisa estar paid para ativar assinatura'; - end if; - - -- resolve target e plan_id via plans.key - select p.id, p.target - into v_plan_id, v_target - from public.plans p - where p.key = v_intent.plan_key - limit 1; - - if v_plan_id is null then - raise exception 'Plano não encontrado em plans.key = %', v_intent.plan_key; - end if; - - v_target := lower(coalesce(v_target, '')); - - -- ✅ supervisor adicionado - if v_target not in ('clinic', 'therapist', 'supervisor') then - raise exception 'Target inválido em plans.target: %', v_target; - end if; - - -- regra por target - if v_target = 'clinic' then - if v_intent.tenant_id is null then - raise exception 'Intent sem tenant_id'; - end if; - else - -- therapist ou supervisor: vinculado ao user - v_user_id := v_intent.user_id; - if v_user_id is null then - v_user_id := v_intent.created_by_user_id; - end if; - end if; - - if v_target in ('therapist', 'supervisor') and v_user_id is null then - raise exception 'Não foi possível determinar user_id para assinatura %.', v_target; - end if; - - -- cancela assinatura ativa anterior - if v_target = 'clinic' then - update public.subscriptions - set status = 'cancelled', - cancelled_at = now() - where tenant_id = v_intent.tenant_id - and plan_id = v_plan_id - and status = 'active'; - else - -- therapist ou supervisor - update public.subscriptions - set status = 'cancelled', - cancelled_at = now() - where user_id = v_user_id - and plan_id = v_plan_id - and status = 'active' - and tenant_id is null; - end if; - - -- duração do plano (30 dias para mensal) - v_days := case - when lower(coalesce(v_intent.interval, 'month')) = 'year' then 365 - else 30 - end; - - -- cria nova assinatura - insert into public.subscriptions ( - user_id, - plan_id, - status, - started_at, - expires_at, - cancelled_at, - activated_at, - tenant_id, - plan_key, - interval, - source, - created_at, - updated_at - ) - values ( - case when v_target = 'clinic' then null else v_user_id end, - v_plan_id, - 'active', - now(), - now() + make_interval(days => v_days), - null, - now(), - case when v_target = 'clinic' then v_intent.tenant_id else null end, - v_intent.plan_key, - v_intent.interval, - 'manual', - now(), - now() - ) - returning * into v_sub; - - -- grava vínculo intent → subscription - if v_target = 'clinic' then - update public.subscription_intents_tenant - set subscription_id = v_sub.id - where id = p_intent_id; - else - update public.subscription_intents_personal - set subscription_id = v_sub.id - where id = p_intent_id; - end if; - - return v_sub; -end; -$$; - - -ALTER FUNCTION public.activate_subscription_from_intent(p_intent_id uuid) OWNER TO supabase_admin; - --- --- Name: admin_credit_addon(uuid, text, integer, uuid, text, text, integer); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid DEFAULT NULL::uuid, p_description text DEFAULT 'Crédito manual'::text, p_payment_method text DEFAULT 'manual'::text, p_price_cents integer DEFAULT 0) RETURNS jsonb -CREATE FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid DEFAULT NULL::uuid, p_description text DEFAULT 'Crédito manual'::text, p_payment_method text DEFAULT 'manual'::text, p_price_cents integer DEFAULT 0) RETURNS jsonb - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public' - AS $$ -DECLARE - v_credit addon_credits%ROWTYPE; - v_balance_before INTEGER; - v_balance_after INTEGER; - v_tx_id UUID; -BEGIN - -- Upsert addon_credits - INSERT INTO addon_credits (tenant_id, addon_type, balance, total_purchased) - VALUES (p_tenant_id, p_addon_type, 0, 0) - ON CONFLICT (tenant_id, addon_type) DO NOTHING; - - -- Lock e leitura - SELECT * INTO v_credit - FROM addon_credits - WHERE tenant_id = p_tenant_id AND addon_type = p_addon_type - FOR UPDATE; - - v_balance_before := v_credit.balance; - v_balance_after := v_credit.balance + p_amount; - - -- Atualiza saldo - UPDATE addon_credits - SET balance = v_balance_after, - total_purchased = total_purchased + p_amount, - low_balance_notified = CASE WHEN v_balance_after > COALESCE(low_balance_threshold, 10) THEN false ELSE low_balance_notified END, - updated_at = now() - WHERE id = v_credit.id; - - -- Registra transação - INSERT INTO addon_transactions ( - tenant_id, addon_type, type, amount, - balance_before, balance_after, - product_id, description, - admin_user_id, payment_method, price_cents - ) VALUES ( - p_tenant_id, p_addon_type, 'purchase', p_amount, - v_balance_before, v_balance_after, - p_product_id, p_description, - auth.uid(), p_payment_method, p_price_cents - ) - RETURNING id INTO v_tx_id; - - RETURN jsonb_build_object( - 'success', true, - 'transaction_id', v_tx_id, - 'balance_before', v_balance_before, - 'balance_after', v_balance_after - ); -END; -$$; - - -ALTER FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer) OWNER TO supabase_admin; - --- --- Name: FUNCTION admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer); Type: COMMENT; Schema: public; Owner: supabase_admin --- - -COMMENT ON FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer) IS 'Admin adiciona créditos de add-on a um tenant. Cria registro se não existir.'; - - --- --- Name: admin_delete_email_template_global(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.admin_delete_email_template_global(p_id uuid) RETURNS boolean -CREATE FUNCTION public.admin_delete_email_template_global(p_id uuid) RETURNS boolean - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public' - AS $$ -BEGIN - DELETE FROM public.email_templates_global WHERE id = p_id; - IF NOT FOUND THEN - RAISE EXCEPTION 'Template com id % não encontrado', p_id; - END IF; - RETURN true; -END; -$$; - - -ALTER FUNCTION public.admin_delete_email_template_global(p_id uuid) OWNER TO supabase_admin; - --- --- Name: admin_fix_plan_target(text, text); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.admin_fix_plan_target(p_plan_key text, p_new_target text) RETURNS void -CREATE FUNCTION public.admin_fix_plan_target(p_plan_key text, p_new_target text) RETURNS void - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -declare - v_plan_id uuid; -begin - -- (opcional) restringe targets válidos - if p_new_target not in ('clinic','therapist') then - raise exception 'Target inválido: %', p_new_target using errcode='P0001'; - end if; - - -- trava o plano - select id into v_plan_id - from public.plans - where key = p_plan_key - for update; - - if v_plan_id is null then - raise exception 'Plano não encontrado: %', p_plan_key using errcode='P0001'; - end if; - - -- segurança: não mexer se existe subscription - if exists (select 1 from public.subscriptions s where s.plan_id = v_plan_id) then - raise exception 'Plano % possui subscriptions. Migração bloqueada.', p_plan_key using errcode='P0001'; - end if; - - -- liga bypass SOMENTE nesta transação - perform set_config('app.plan_migration_bypass', '1', true); - - update public.plans - set target = p_new_target - where id = v_plan_id; - -end -$$; - - -ALTER FUNCTION public.admin_fix_plan_target(p_plan_key text, p_new_target text) OWNER TO supabase_admin; - --- --- Name: admin_upsert_email_template_global(uuid, text, text, text, text, text, text, boolean, jsonb); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.admin_upsert_email_template_global(p_id uuid DEFAULT NULL::uuid, p_key text DEFAULT NULL::text, p_domain text DEFAULT NULL::text, p_channel text DEFAULT 'email'::text, p_subject text DEFAULT NULL::text, p_body_html text DEFAULT NULL::text, p_body_text text DEFAULT NULL::text, p_is_active boolean DEFAULT true, p_variables jsonb DEFAULT '{}'::jsonb) RETURNS jsonb -CREATE FUNCTION public.admin_upsert_email_template_global(p_id uuid DEFAULT NULL::uuid, p_key text DEFAULT NULL::text, p_domain text DEFAULT NULL::text, p_channel text DEFAULT 'email'::text, p_subject text DEFAULT NULL::text, p_body_html text DEFAULT NULL::text, p_body_text text DEFAULT NULL::text, p_is_active boolean DEFAULT true, p_variables jsonb DEFAULT '{}'::jsonb) RETURNS jsonb - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public' - AS $$ -DECLARE - v_result jsonb; - v_id uuid; -BEGIN - -- UPDATE existente - IF p_id IS NOT NULL THEN - UPDATE public.email_templates_global - SET - subject = COALESCE(p_subject, subject), - body_html = COALESCE(p_body_html, body_html), - body_text = p_body_text, - is_active = p_is_active, - variables = COALESCE(p_variables, variables), - version = version + 1 - WHERE id = p_id - RETURNING to_jsonb(email_templates_global.*) INTO v_result; - - IF v_result IS NULL THEN - RAISE EXCEPTION 'Template com id % não encontrado', p_id; - END IF; - - RETURN v_result; - END IF; - - -- INSERT novo - IF p_key IS NULL OR p_domain IS NULL OR p_subject IS NULL OR p_body_html IS NULL THEN - RAISE EXCEPTION 'key, domain, subject e body_html são obrigatórios para novo template'; - END IF; - - INSERT INTO public.email_templates_global (key, domain, channel, subject, body_html, body_text, is_active, variables) - VALUES (p_key, p_domain, p_channel, p_subject, p_body_html, p_body_text, p_is_active, p_variables) - RETURNING to_jsonb(email_templates_global.*) INTO v_result; - - RETURN v_result; -END; -$$; - - -ALTER FUNCTION public.admin_upsert_email_template_global(p_id uuid, p_key text, p_domain text, p_channel text, p_subject text, p_body_html text, p_body_text text, p_is_active boolean, p_variables jsonb) OWNER TO supabase_admin; - --- --- Name: agenda_cfg_sync(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.agenda_cfg_sync() RETURNS trigger -CREATE FUNCTION public.cancel_subscription(p_subscription_id uuid) RETURNS public.subscriptions - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -declare - v_sub public.subscriptions; - v_owner_type text; - v_owner_ref uuid; -begin - - select * - into v_sub - from public.subscriptions - where id = p_subscription_id - for update; - - if not found then - raise exception 'Subscription não encontrada'; - end if; - - if v_sub.status = 'canceled' then - return v_sub; - end if; - - if v_sub.tenant_id is not null then - v_owner_type := 'clinic'; - v_owner_ref := v_sub.tenant_id; - elsif v_sub.user_id is not null then - v_owner_type := 'therapist'; - v_owner_ref := v_sub.user_id; - else - v_owner_type := null; - v_owner_ref := null; - end if; - - update public.subscriptions - set status = 'canceled', - cancel_at_period_end = false, - updated_at = now() - where id = p_subscription_id - returning * into v_sub; - - insert into public.subscription_events( - subscription_id, - owner_id, - owner_type, - owner_ref, - event_type, - old_plan_id, - new_plan_id, - created_by, - reason, - source, - metadata - ) - values ( - v_sub.id, - v_owner_ref, - v_owner_type, - v_owner_ref, - 'canceled', - v_sub.plan_id, - v_sub.plan_id, - auth.uid(), - 'Cancelamento manual via admin', - 'admin_panel', - jsonb_build_object('previous_status', 'active') - ); - - if v_owner_ref is not null then - insert into public.entitlements_invalidation(owner_id, changed_at) - values (v_owner_ref, now()) - on conflict (owner_id) - do update set changed_at = excluded.changed_at; - end if; - - return v_sub; - -end; -$$; - - -ALTER FUNCTION public.cancel_subscription(p_subscription_id uuid) OWNER TO supabase_admin; - --- --- Name: cancelar_eventos_serie(uuid, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone DEFAULT now()) RETURNS integer -CREATE FUNCTION public.change_subscription_plan(p_subscription_id uuid, p_new_plan_id uuid) RETURNS public.subscriptions - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -declare - v_sub public.subscriptions; - v_old_plan uuid; - v_new_key text; - - v_owner_type text; - v_owner_ref uuid; - - v_new_target text; - v_sub_target text; -begin - select * - into v_sub - from public.subscriptions - where id = p_subscription_id - for update; - - if not found then - raise exception 'Subscription não encontrada'; - end if; - - v_old_plan := v_sub.plan_id; - - if v_old_plan = p_new_plan_id then - return v_sub; - end if; - - select key, target - into v_new_key, v_new_target - from public.plans - where id = p_new_plan_id; - - if v_new_key is null then - raise exception 'Plano não encontrado'; - end if; - - v_new_target := lower(coalesce(v_new_target, '')); - - v_sub_target := case - when v_sub.tenant_id is not null then 'clinic' - else 'therapist' - end; - - if v_new_target <> v_sub_target then - raise exception 'Plano inválido para este tipo de assinatura. Assinatura é % e o plano é %.', - v_sub_target, v_new_target - using errcode = 'P0001'; - end if; - - if v_sub.tenant_id is not null then - v_owner_type := 'clinic'; - v_owner_ref := v_sub.tenant_id; - elsif v_sub.user_id is not null then - v_owner_type := 'therapist'; - v_owner_ref := v_sub.user_id; - else - v_owner_type := null; - v_owner_ref := null; - end if; - - update public.subscriptions - set plan_id = p_new_plan_id, - plan_key = v_new_key, - updated_at = now() - where id = p_subscription_id - returning * into v_sub; - - insert into public.subscription_events( - subscription_id, - owner_id, - owner_type, - owner_ref, - event_type, - old_plan_id, - new_plan_id, - created_by, - reason, - source, - metadata - ) - values ( - v_sub.id, - v_owner_ref, - v_owner_type, - v_owner_ref, - 'plan_changed', - v_old_plan, - p_new_plan_id, - auth.uid(), - 'Plan change via DEV menu', - 'dev_menu', - jsonb_build_object( - 'previous_plan', v_old_plan, - 'new_plan', p_new_plan_id, - 'new_plan_key', v_new_key, - 'new_plan_target', v_new_target - ) - ); - - if v_owner_ref is not null then - insert into public.entitlements_invalidation (owner_id, changed_at) - values (v_owner_ref, now()) - on conflict (owner_id) - do update set changed_at = excluded.changed_at; - end if; - - return v_sub; -end; -$$; - - -ALTER FUNCTION public.change_subscription_plan(p_subscription_id uuid, p_new_plan_id uuid) OWNER TO supabase_admin; - --- --- Name: cleanup_notification_queue(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.cleanup_notification_queue() RETURNS integer -CREATE FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid DEFAULT NULL::uuid, p_description text DEFAULT 'Consumo'::text) RETURNS jsonb - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public' - AS $$ -DECLARE - v_credit addon_credits%ROWTYPE; - v_balance_before INTEGER; - v_balance_after INTEGER; -BEGIN - -- Lock e leitura - SELECT * INTO v_credit - FROM addon_credits - WHERE tenant_id = p_tenant_id AND addon_type = p_addon_type AND is_active = true - FOR UPDATE; - - IF NOT FOUND THEN - RETURN jsonb_build_object('success', false, 'reason', 'no_credits', 'balance', 0); - END IF; - - -- Verifica saldo - IF v_credit.balance <= 0 THEN - RETURN jsonb_build_object('success', false, 'reason', 'insufficient_balance', 'balance', 0); - END IF; - - -- Verifica rate limit diário - IF v_credit.daily_limit IS NOT NULL THEN - -- Reset se passou do dia - IF v_credit.daily_reset_at IS NULL OR v_credit.daily_reset_at < date_trunc('day', now()) THEN - UPDATE addon_credits SET daily_used = 0, daily_reset_at = date_trunc('day', now()) + interval '1 day' WHERE id = v_credit.id; - v_credit.daily_used := 0; - END IF; - - IF v_credit.daily_used >= v_credit.daily_limit THEN - RETURN jsonb_build_object('success', false, 'reason', 'daily_limit_reached', 'balance', v_credit.balance); - END IF; - END IF; - - -- Verifica rate limit horário - IF v_credit.hourly_limit IS NOT NULL THEN - IF v_credit.hourly_reset_at IS NULL OR v_credit.hourly_reset_at < date_trunc('hour', now()) THEN - UPDATE addon_credits SET hourly_used = 0, hourly_reset_at = date_trunc('hour', now()) + interval '1 hour' WHERE id = v_credit.id; - v_credit.hourly_used := 0; - END IF; - - IF v_credit.hourly_used >= v_credit.hourly_limit THEN - RETURN jsonb_build_object('success', false, 'reason', 'hourly_limit_reached', 'balance', v_credit.balance); - END IF; - END IF; - - -- Verifica expiração - IF v_credit.expires_at IS NOT NULL AND v_credit.expires_at < now() THEN - RETURN jsonb_build_object('success', false, 'reason', 'credits_expired', 'balance', v_credit.balance); - END IF; - - v_balance_before := v_credit.balance; - v_balance_after := v_credit.balance - 1; - - -- Debita - UPDATE addon_credits - SET balance = v_balance_after, - total_consumed = total_consumed + 1, - daily_used = COALESCE(daily_used, 0) + 1, - hourly_used = COALESCE(hourly_used, 0) + 1, - updated_at = now() - WHERE id = v_credit.id; - - -- Registra transação - INSERT INTO addon_transactions ( - tenant_id, addon_type, type, amount, - balance_before, balance_after, - queue_id, description - ) VALUES ( - p_tenant_id, p_addon_type, 'consume', -1, - v_balance_before, v_balance_after, - p_queue_id, p_description - ); - - RETURN jsonb_build_object( - 'success', true, - 'balance_before', v_balance_before, - 'balance_after', v_balance_after - ); -END; -$$; - - -ALTER FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text) OWNER TO supabase_admin; - --- --- Name: FUNCTION debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text); Type: COMMENT; Schema: public; Owner: supabase_admin --- - -COMMENT ON FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text) IS 'Debita 1 crédito de add-on. Verifica saldo, rate limits e expiração.'; - - --- --- Name: delete_commitment_full(uuid, uuid); Type: FUNCTION; Schema: public; Owner: postgres --- - -CREATE FUNCTION public.delete_commitment_full(p_tenant_id uuid, p_commitment_id uuid) RETURNS jsonb -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 - 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.guard_no_change_core_plan_key() RETURNS trigger - LANGUAGE plpgsql - AS $$ -begin - if old.key in ('clinic_free','clinic_pro','therapist_free','therapist_pro') - and new.key is distinct from old.key then - raise exception 'Não é permitido alterar a key do plano padrão (%).', old.key - using errcode = 'P0001'; - end if; - - return new; -end $$; - - -ALTER FUNCTION public.guard_no_change_core_plan_key() OWNER TO supabase_admin; - --- --- Name: guard_no_change_plan_target(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.guard_no_change_plan_target() RETURNS trigger - LANGUAGE plpgsql - AS $$ -declare - v_bypass text; -begin - -- bypass controlado por sessão/transação: - -- só passa se app.plan_migration_bypass = '1' - v_bypass := current_setting('app.plan_migration_bypass', true); - - if v_bypass = '1' then - return new; - end if; - - -- comportamento original (bloqueia qualquer mudança) - if new.target is distinct from old.target then - raise exception 'Não é permitido alterar target do plano (%) de % para %.', - old.key, old.target, new.target - using errcode = 'P0001'; - end if; - - return new; -end -$$; - - -ALTER FUNCTION public.guard_no_change_plan_target() OWNER TO supabase_admin; - --- --- Name: guard_no_delete_core_plans(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.guard_no_delete_core_plans() RETURNS trigger -CREATE FUNCTION public.guard_no_change_plan_target() RETURNS trigger - LANGUAGE plpgsql - AS $$ -declare - v_bypass text; -begin - -- bypass controlado por sessão/transação: - -- só passa se app.plan_migration_bypass = '1' - v_bypass := current_setting('app.plan_migration_bypass', true); - - if v_bypass = '1' then - return new; - end if; - - -- comportamento original (bloqueia qualquer mudança) - if new.target is distinct from old.target then - raise exception 'Não é permitido alterar target do plano (%) de % para %.', - old.key, old.target, new.target - using errcode = 'P0001'; - end if; - - return new; -end -$$; - - -ALTER FUNCTION public.guard_no_change_plan_target() OWNER TO supabase_admin; - --- --- Name: guard_no_delete_core_plans(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.guard_no_delete_core_plans() RETURNS trigger - LANGUAGE plpgsql - AS $$ -begin - if old.key in ('clinic_free','clinic_pro','therapist_free','therapist_pro') then - raise exception 'Plano padrão (%) não pode ser removido.', old.key - using errcode = 'P0001'; - end if; - - return old; -end $$; - - -ALTER FUNCTION public.guard_no_delete_core_plans() OWNER TO supabase_admin; - --- --- Name: guard_patient_cannot_own_tenant(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.guard_patient_cannot_own_tenant() RETURNS trigger -CREATE FUNCTION public.guard_no_delete_core_plans() RETURNS trigger - LANGUAGE plpgsql - AS $$ -begin - if old.key in ('clinic_free','clinic_pro','therapist_free','therapist_pro') then - raise exception 'Plano padrão (%) não pode ser removido.', old.key - using errcode = 'P0001'; - end if; - - return old; -end $$; - - -ALTER FUNCTION public.guard_no_delete_core_plans() OWNER TO supabase_admin; - --- --- Name: guard_patient_cannot_own_tenant(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.guard_patient_cannot_own_tenant() RETURNS trigger -CREATE FUNCTION public.reactivate_subscription(p_subscription_id uuid) RETURNS public.subscriptions - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -declare - v_sub public.subscriptions; - v_owner_type text; - v_owner_ref uuid; -begin - - select * - into v_sub - from public.subscriptions - where id = p_subscription_id - for update; - - if not found then - raise exception 'Subscription não encontrada'; - end if; - - if v_sub.status = 'active' then - return v_sub; - end if; - - if v_sub.tenant_id is not null then - v_owner_type := 'clinic'; - v_owner_ref := v_sub.tenant_id; - elsif v_sub.user_id is not null then - v_owner_type := 'therapist'; - v_owner_ref := v_sub.user_id; - else - v_owner_type := null; - v_owner_ref := null; - end if; - - update public.subscriptions - set status = 'active', - cancel_at_period_end = false, - updated_at = now() - where id = p_subscription_id - returning * into v_sub; - - insert into public.subscription_events( - subscription_id, - owner_id, - owner_type, - owner_ref, - event_type, - old_plan_id, - new_plan_id, - created_by, - reason, - source, - metadata - ) - values ( - v_sub.id, - v_owner_ref, - v_owner_type, - v_owner_ref, - 'reactivated', - v_sub.plan_id, - v_sub.plan_id, - auth.uid(), - 'Reativação manual via admin', - 'admin_panel', - jsonb_build_object('previous_status', 'canceled') - ); - - if v_owner_ref is not null then - insert into public.entitlements_invalidation(owner_id, changed_at) - values (v_owner_ref, now()) - on conflict (owner_id) - do update set changed_at = excluded.changed_at; - end if; - - return v_sub; - -end; -$$; - - -ALTER FUNCTION public.reactivate_subscription(p_subscription_id uuid) OWNER TO supabase_admin; - --- --- Name: rebuild_owner_entitlements(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.rebuild_owner_entitlements(p_owner_id uuid) RETURNS void -CREATE FUNCTION public.rebuild_owner_entitlements(p_owner_id uuid) RETURNS void - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -declare - v_plan_id uuid; -begin - -- Plano ativo do owner (owner = subscriptions.user_id) - select s.plan_id - into v_plan_id - from public.subscriptions s - where s.user_id = p_owner_id - and s.status = 'active' - order by s.created_at desc - limit 1; - - -- Sempre zera entitlements do owner (rebuild) - delete from public.owner_feature_entitlements e - where e.owner_id = p_owner_id; - - -- Se não tem assinatura ativa, acabou - if v_plan_id is null then - return; - end if; - - -- Recria entitlements esperados pelo plano - insert into public.owner_feature_entitlements (owner_id, feature_key, sources, limits_list) - select - p_owner_id as owner_id, - f.key as feature_key, - array['plan'::text] as sources, - '{}'::jsonb as limits_list - from public.plan_features pf - join public.features f on f.id = pf.feature_id - where pf.plan_id = v_plan_id; - -end; -$$; - - -ALTER FUNCTION public.rebuild_owner_entitlements(p_owner_id uuid) OWNER TO supabase_admin; - --- --- Name: revoke_support_session(text); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.revoke_support_session(p_token text) RETURNS boolean -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 - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -begin - -- ✅ Só owner ou admin do tenant podem alterar features - if not exists ( - select 1 from public.tenant_members - where tenant_id = p_tenant_id - and user_id = auth.uid() - and role in ('owner', 'admin') - and status = 'active' - ) then - raise exception 'Acesso negado: apenas owner/admin pode alterar features do tenant.'; - end if; - - insert into public.tenant_features (tenant_id, feature_key, enabled) - values (p_tenant_id, p_feature_key, p_enabled) - on conflict (tenant_id, feature_key) - do update set enabled = excluded.enabled; - - insert into public.tenant_feature_exceptions_log ( - tenant_id, feature_key, enabled, reason, created_by - ) values ( - p_tenant_id, p_feature_key, p_enabled, p_reason, auth.uid() - ); -end; -$$; - - -ALTER FUNCTION public.set_tenant_feature_exception(p_tenant_id uuid, p_feature_key text, p_enabled boolean, p_reason text) OWNER TO supabase_admin; - --- --- Name: set_updated_at(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -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.subscription_intents_view_insert() RETURNS trigger - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -declare - v_target text; - v_plan_id uuid; -begin - select p.id, p.target into v_plan_id, v_target - from public.plans p - where p.key = new.plan_key; - - if v_plan_id is null then - raise exception 'Plano inválido: plan_key=%', new.plan_key; - end if; - - if lower(v_target) = 'clinic' then - if new.tenant_id is null then - raise exception 'Intenção clinic exige tenant_id.'; - end if; - - insert into public.subscription_intents_tenant ( - id, tenant_id, created_by_user_id, email, - plan_id, plan_key, interval, amount_cents, currency, - status, source, notes, created_at, paid_at - ) values ( - coalesce(new.id, gen_random_uuid()), - new.tenant_id, new.created_by_user_id, new.email, - v_plan_id, new.plan_key, coalesce(new.interval,'month'), - new.amount_cents, coalesce(new.currency,'BRL'), - coalesce(new.status,'pending'), coalesce(new.source,'manual'), - new.notes, coalesce(new.created_at, now()), new.paid_at - ); - - new.plan_target := 'clinic'; - return new; - end if; - - -- therapist ou supervisor → tabela personal - if lower(v_target) in ('therapist', 'supervisor') then - insert into public.subscription_intents_personal ( - id, user_id, created_by_user_id, email, - plan_id, plan_key, interval, amount_cents, currency, - status, source, notes, created_at, paid_at - ) values ( - coalesce(new.id, gen_random_uuid()), - new.user_id, new.created_by_user_id, new.email, - v_plan_id, new.plan_key, coalesce(new.interval,'month'), - new.amount_cents, coalesce(new.currency,'BRL'), - coalesce(new.status,'pending'), coalesce(new.source,'manual'), - new.notes, coalesce(new.created_at, now()), new.paid_at - ); - - new.plan_target := lower(v_target); -- 'therapist' ou 'supervisor' - return new; - end if; - - raise exception 'Target de plano não suportado: %', v_target; -end; -$$; - - -ALTER FUNCTION public.subscription_intents_view_insert() OWNER TO supabase_admin; - --- --- Name: subscriptions_validate_scope(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.subscriptions_validate_scope() RETURNS trigger -CREATE FUNCTION public.subscriptions_validate_scope() RETURNS trigger - LANGUAGE plpgsql - AS $$ -DECLARE - v_target text; -BEGIN - SELECT lower(p.target) INTO v_target - FROM public.plans p - WHERE p.id = NEW.plan_id; - - IF v_target IS NULL THEN - RAISE EXCEPTION 'Plano inválido (target nulo).'; - END IF; - - IF v_target = 'clinic' THEN - IF NEW.tenant_id IS NULL THEN - RAISE EXCEPTION 'Assinatura clinic exige tenant_id.'; - END IF; - IF NEW.user_id IS NOT NULL THEN - RAISE EXCEPTION 'Assinatura clinic não pode ter user_id (XOR).'; - END IF; - - ELSIF v_target IN ('therapist', 'supervisor') THEN - -- supervisor é pessoal como therapist - IF NEW.tenant_id IS NOT NULL THEN - RAISE EXCEPTION 'Assinatura % não deve ter tenant_id.', v_target; - END IF; - IF NEW.user_id IS NULL THEN - RAISE EXCEPTION 'Assinatura % exige user_id.', v_target; - END IF; - - ELSIF v_target = 'patient' THEN - IF NEW.tenant_id IS NOT NULL THEN - RAISE EXCEPTION 'Assinatura patient não deve ter tenant_id.'; - END IF; - IF NEW.user_id IS NULL THEN - RAISE EXCEPTION 'Assinatura patient exige user_id.'; - END IF; - - ELSE - RAISE EXCEPTION 'Target de plano inválido: %', v_target; - END IF; - - RETURN NEW; -END; -$$; - - -ALTER FUNCTION public.subscriptions_validate_scope() OWNER TO supabase_admin; - --- --- Name: sync_busy_mirror_agenda_eventos(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.sync_busy_mirror_agenda_eventos() RETURNS trigger -CREATE FUNCTION public.toggle_plan(owner uuid) RETURNS void - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -declare - current_key text; - new_key text; -begin - select p.key into current_key - from subscriptions s - join plans p on p.id = s.plan_id - where s.owner_id = owner - and s.status = 'active'; - - new_key := case - when current_key = 'pro' then 'free' - else 'pro' - end; - - update subscriptions s - set plan_id = p.id - from plans p - where p.key = new_key - and s.owner_id = owner - and s.status = 'active'; -end; -$$; - - -ALTER FUNCTION public.toggle_plan(owner uuid) OWNER TO supabase_admin; - --- --- Name: transition_subscription(uuid, text, text, jsonb); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.transition_subscription(p_subscription_id uuid, p_to_status text, p_reason text DEFAULT NULL::text, p_metadata jsonb DEFAULT NULL::jsonb) RETURNS public.subscriptions -CREATE FUNCTION public.transition_subscription(p_subscription_id uuid, p_to_status text, p_reason text DEFAULT NULL::text, p_metadata jsonb DEFAULT NULL::jsonb) RETURNS public.subscriptions - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -declare - v_sub public.subscriptions; - v_uid uuid; - v_is_allowed boolean := false; -begin - v_uid := auth.uid(); - - select * - into v_sub - from public.subscriptions - where id = p_subscription_id; - - if not found then - raise exception 'Assinatura não encontrada'; - end if; - - -- ===================================================== - -- 🔐 BLOCO DE AUTORIZAÇÃO - -- ===================================================== - - -- 1) SaaS admin pode tudo - if is_saas_admin() then - v_is_allowed := true; - end if; - - -- 2) Assinatura pessoal (therapist) - if not v_is_allowed - and v_sub.tenant_id is null - and v_sub.user_id = v_uid then - v_is_allowed := true; - end if; - - -- 3) Assinatura de clinic (tenant) - if not v_is_allowed - and v_sub.tenant_id is not null then - - if exists ( - select 1 - from public.tenant_members tm - where tm.tenant_id = v_sub.tenant_id - and tm.user_id = v_uid - and tm.status = 'active' - and tm.role = 'tenant_admin' - ) then - v_is_allowed := true; - end if; - - end if; - - if not v_is_allowed then - raise exception 'Sem permissão para transicionar esta assinatura'; - end if; - - -- ===================================================== - -- 🧠 TRANSIÇÃO - -- ===================================================== - - update public.subscriptions - set status = p_to_status, - updated_at = now(), - cancelled_at = case when p_to_status = 'cancelled' then now() else cancelled_at end, - suspended_at = case when p_to_status = 'suspended' then now() else suspended_at end, - past_due_since = case when p_to_status = 'past_due' then now() else past_due_since end, - expired_at = case when p_to_status = 'expired' then now() else expired_at end, - activated_at = case when p_to_status = 'active' then now() else activated_at end - where id = p_subscription_id - returning * into v_sub; - - -- ===================================================== - -- 🧾 EVENT LOG - -- ===================================================== - - insert into public.subscription_events ( - subscription_id, - owner_id, - event_type, - created_at, - created_by, - source, - reason, - metadata, - owner_type, - owner_ref - ) - values ( - v_sub.id, - coalesce(v_sub.tenant_id, v_sub.user_id), - 'status_changed', - now(), - v_uid, - 'manual_transition', - p_reason, - p_metadata, - case when v_sub.tenant_id is not null then 'tenant' else 'personal' end, - coalesce(v_sub.tenant_id, v_sub.user_id) - ); - - return v_sub; -end; -$$; - - -ALTER FUNCTION public.transition_subscription(p_subscription_id uuid, p_to_status text, p_reason text, p_metadata jsonb) OWNER TO supabase_admin; - --- --- Name: trg_fn_financial_records_auto_overdue(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.trg_fn_financial_records_auto_overdue() RETURNS trigger diff --git a/database-novo/schema/03_functions/core.sql b/database-novo/schema/03_functions/core.sql deleted file mode 100644 index 68924cb..0000000 --- a/database-novo/schema/03_functions/core.sql +++ /dev/null @@ -1,2350 +0,0 @@ --- ============================================================================= --- AgenciaPsi — Functions — Core (profiles, tenants, membros, auth) --- ============================================================================= --- handle_new_user, ensure_personal_tenant, whoami, my_tenants, --- tenant_add_member, tenant_remove_member, tenant_invite, guards, etc. --- ============================================================================= - -CREATE FUNCTION public.__rls_ping() RETURNS text - LANGUAGE sql STABLE - AS $$ - select 'ok'::text; -$$; - - -ALTER FUNCTION public.__rls_ping() OWNER TO supabase_admin; - -SET default_tablespace = ''; - -SET default_table_access_method = heap; - --- --- Name: subscriptions; Type: TABLE; Schema: public; Owner: supabase_admin --- - - -CREATE FUNCTION public.ensure_personal_tenant() RETURNS uuid - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - v_uid uuid; - v_existing uuid; -BEGIN - v_uid := auth.uid(); - IF v_uid IS NULL THEN - RAISE EXCEPTION 'Not authenticated'; - END IF; - - SELECT tm.tenant_id INTO v_existing - FROM public.tenant_members tm - JOIN public.tenants t ON t.id = tm.tenant_id - WHERE tm.user_id = v_uid - AND tm.status = 'active' - AND t.kind IN ('therapist', 'saas') - ORDER BY tm.created_at DESC - LIMIT 1; - - IF v_existing IS NOT NULL THEN - RETURN v_existing; - END IF; - - RETURN public.provision_account_tenant(v_uid, 'therapist'); -END; -$$; - - -ALTER FUNCTION public.ensure_personal_tenant() OWNER TO supabase_admin; - --- --- Name: ensure_personal_tenant_for_user(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.ensure_personal_tenant_for_user(p_user_id uuid) RETURNS uuid - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -declare - v_uid uuid; - v_existing uuid; - v_tenant uuid; - v_email text; - v_name text; -begin - v_uid := p_user_id; - if v_uid is null then - raise exception 'Missing user id'; - end if; - - -- só considera tenant pessoal (kind='saas') - select tm.tenant_id - into v_existing - from public.tenant_members tm - join public.tenants t on t.id = tm.tenant_id - where tm.user_id = v_uid - and tm.status = 'active' - and t.kind = 'saas' - order by tm.created_at desc - limit 1; - - if v_existing is not null then - return v_existing; - end if; - - select email into v_email - from auth.users - where id = v_uid; - - v_name := coalesce(split_part(v_email, '@', 1), 'Conta'); - - insert into public.tenants (name, kind, created_at) - values (v_name || ' (Pessoal)', 'saas', 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.ensure_personal_tenant_for_user(p_user_id uuid) OWNER TO supabase_admin; - --- --- Name: faq_votar(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.faq_votar(faq_id uuid) RETURNS void -CREATE FUNCTION public.ensure_personal_tenant_for_user(p_user_id uuid) RETURNS uuid - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -declare - v_uid uuid; - v_existing uuid; - v_tenant uuid; - v_email text; - v_name text; -begin - v_uid := p_user_id; - if v_uid is null then - raise exception 'Missing user id'; - end if; - - -- só considera tenant pessoal (kind='saas') - select tm.tenant_id - into v_existing - from public.tenant_members tm - join public.tenants t on t.id = tm.tenant_id - where tm.user_id = v_uid - and tm.status = 'active' - and t.kind = 'saas' - order by tm.created_at desc - limit 1; - - if v_existing is not null then - return v_existing; - end if; - - select email into v_email - from auth.users - where id = v_uid; - - v_name := coalesce(split_part(v_email, '@', 1), 'Conta'); - - insert into public.tenants (name, kind, created_at) - values (v_name || ' (Pessoal)', 'saas', 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.ensure_personal_tenant_for_user(p_user_id uuid) OWNER TO supabase_admin; - --- --- Name: faq_votar(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.faq_votar(faq_id uuid) RETURNS void -CREATE FUNCTION public.get_my_email() RETURNS text - LANGUAGE sql SECURITY DEFINER - SET search_path TO 'public', 'auth' - AS $$ - select lower(email) - from auth.users - where id = auth.uid(); -$$; - - -ALTER FUNCTION public.get_my_email() OWNER TO supabase_admin; - --- --- Name: guard_account_type_immutable(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.guard_account_type_immutable() RETURNS trigger -CREATE FUNCTION public.guard_account_type_immutable() RETURNS trigger - LANGUAGE plpgsql - AS $$ -BEGIN - IF OLD.account_type <> 'free' AND NEW.account_type IS DISTINCT FROM OLD.account_type THEN - RAISE EXCEPTION 'account_type é imutável após escolha (atual: "%" para tentativa: "%"). Para mudar de perfil, crie uma nova conta.', OLD.account_type, NEW.account_type - USING ERRCODE = 'P0001'; - END IF; - RETURN NEW; -END; -$$; - - -ALTER FUNCTION public.guard_account_type_immutable() OWNER TO supabase_admin; - --- --- Name: guard_locked_commitment(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -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.guard_patient_cannot_own_tenant() RETURNS trigger - LANGUAGE plpgsql - AS $$ -DECLARE - v_account_type text; -BEGIN - SELECT account_type INTO v_account_type - FROM public.profiles - WHERE id = NEW.user_id; - - IF v_account_type = 'patient' AND NEW.role IN ('tenant_admin', 'therapist') THEN - RAISE EXCEPTION 'Usuário com perfil "patient" não pode ser proprietário ou terapeuta de um tenant. Se tornou profissional? Crie uma nova conta.' - USING ERRCODE = 'P0001'; - END IF; - - RETURN NEW; -END; -$$; - - -ALTER FUNCTION public.guard_patient_cannot_own_tenant() OWNER TO supabase_admin; - --- --- Name: guard_tenant_kind_immutable(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.guard_tenant_kind_immutable() RETURNS trigger - LANGUAGE plpgsql - AS $$ -BEGIN - IF NEW.kind IS DISTINCT FROM OLD.kind THEN - RAISE EXCEPTION 'tenants.kind é imutável após criação. Tentativa de alterar "%" para "%".', OLD.kind, NEW.kind - USING ERRCODE = 'P0001'; - END IF; - RETURN NEW; -END; -$$; - - -ALTER FUNCTION public.guard_tenant_kind_immutable() OWNER TO supabase_admin; - --- --- Name: handle_new_user(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.handle_new_user() RETURNS trigger -CREATE FUNCTION public.guard_tenant_kind_immutable() RETURNS trigger - LANGUAGE plpgsql - AS $$ -BEGIN - IF NEW.kind IS DISTINCT FROM OLD.kind THEN - RAISE EXCEPTION 'tenants.kind é imutável após criação. Tentativa de alterar "%" para "%".', OLD.kind, NEW.kind - USING ERRCODE = 'P0001'; - END IF; - RETURN NEW; -END; -$$; - - -ALTER FUNCTION public.guard_tenant_kind_immutable() OWNER TO supabase_admin; - --- --- Name: handle_new_user(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.handle_new_user() RETURNS trigger - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public' - AS $$ -BEGIN - INSERT INTO public.profiles (id, role, account_type) - VALUES (NEW.id, 'portal_user', 'free') - ON CONFLICT (id) DO NOTHING; - RETURN NEW; -END; -$$; - - -ALTER FUNCTION public.handle_new_user() OWNER TO supabase_admin; - --- --- Name: handle_new_user_create_personal_tenant(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.handle_new_user_create_personal_tenant() RETURNS trigger -CREATE FUNCTION public.handle_new_user() RETURNS trigger - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public' - AS $$ -BEGIN - INSERT INTO public.profiles (id, role, account_type) - VALUES (NEW.id, 'portal_user', 'free') - ON CONFLICT (id) DO NOTHING; - RETURN NEW; -END; -$$; - - -ALTER FUNCTION public.handle_new_user() OWNER TO supabase_admin; - --- --- Name: handle_new_user_create_personal_tenant(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.handle_new_user_create_personal_tenant() RETURNS trigger - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -BEGIN - -- Desabilitado. Tenant criado no onboarding via provision_account_tenant(). - RETURN NEW; -END; -$$; - - -ALTER FUNCTION public.handle_new_user_create_personal_tenant() OWNER TO supabase_admin; - --- --- Name: has_feature(uuid, text); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.has_feature(p_owner_id uuid, p_feature_key text) RETURNS boolean -CREATE FUNCTION public.handle_new_user_create_personal_tenant() RETURNS trigger - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -BEGIN - -- Desabilitado. Tenant criado no onboarding via provision_account_tenant(). - RETURN NEW; -END; -$$; - - -ALTER FUNCTION public.handle_new_user_create_personal_tenant() OWNER TO supabase_admin; - --- --- Name: has_feature(uuid, text); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.has_feature(p_owner_id uuid, p_feature_key text) RETURNS boolean - LANGUAGE sql STABLE - AS $$ - select exists ( - select 1 - from public.owner_feature_entitlements e - where e.owner_id = p_owner_id - and e.feature_key = p_feature_key - ); -$$; - - -ALTER FUNCTION public.has_feature(p_owner_id uuid, p_feature_key text) OWNER TO supabase_admin; - --- --- Name: is_clinic_tenant(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.is_clinic_tenant(_tenant_id uuid) RETURNS boolean -CREATE FUNCTION public.has_feature(p_owner_id uuid, p_feature_key text) RETURNS boolean - LANGUAGE sql STABLE - AS $$ - select exists ( - select 1 - from public.owner_feature_entitlements e - where e.owner_id = p_owner_id - and e.feature_key = p_feature_key - ); -$$; - - -ALTER FUNCTION public.has_feature(p_owner_id uuid, p_feature_key text) OWNER TO supabase_admin; - --- --- Name: is_clinic_tenant(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.is_clinic_tenant(_tenant_id uuid) RETURNS boolean - LANGUAGE sql STABLE - AS $$ - SELECT EXISTS ( - SELECT 1 FROM public.tenants t - WHERE t.id = _tenant_id - AND t.kind IN ('clinic', 'clinic_coworking', 'clinic_reception', 'clinic_full') - ); -$$; - - -ALTER FUNCTION public.is_clinic_tenant(_tenant_id uuid) OWNER TO supabase_admin; - --- --- Name: is_saas_admin(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.is_saas_admin() RETURNS boolean -CREATE FUNCTION public.is_clinic_tenant(_tenant_id uuid) RETURNS boolean - LANGUAGE sql STABLE - AS $$ - SELECT EXISTS ( - SELECT 1 FROM public.tenants t - WHERE t.id = _tenant_id - AND t.kind IN ('clinic', 'clinic_coworking', 'clinic_reception', 'clinic_full') - ); -$$; - - -ALTER FUNCTION public.is_clinic_tenant(_tenant_id uuid) OWNER TO supabase_admin; - --- --- Name: is_saas_admin(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.is_saas_admin() RETURNS boolean - LANGUAGE sql STABLE - AS $$ - select exists ( - select 1 from public.saas_admins sa - where sa.user_id = auth.uid() - ); -$$; - - -ALTER FUNCTION public.is_saas_admin() OWNER TO supabase_admin; - --- --- Name: is_tenant_admin(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.is_tenant_admin(p_tenant_id uuid) RETURNS boolean -CREATE FUNCTION public.is_saas_admin() RETURNS boolean - LANGUAGE sql STABLE - AS $$ - select exists ( - select 1 from public.saas_admins sa - where sa.user_id = auth.uid() - ); -$$; - - -ALTER FUNCTION public.is_saas_admin() OWNER TO supabase_admin; - --- --- Name: is_tenant_admin(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.is_tenant_admin(p_tenant_id uuid) RETURNS boolean - LANGUAGE sql STABLE SECURITY DEFINER - SET search_path TO 'public' - SET row_security TO 'off' - AS $$ - select exists ( - select 1 - from public.tenant_members tm - where tm.tenant_id = p_tenant_id - and tm.user_id = auth.uid() - and tm.role = 'tenant_admin' - and tm.status = 'active' - ); -$$; - - -ALTER FUNCTION public.is_tenant_admin(p_tenant_id uuid) OWNER TO supabase_admin; - --- --- Name: is_tenant_member(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.is_tenant_member(_tenant_id uuid) RETURNS boolean -CREATE FUNCTION public.is_tenant_admin(p_tenant_id uuid) RETURNS boolean - LANGUAGE sql STABLE SECURITY DEFINER - SET search_path TO 'public' - SET row_security TO 'off' - AS $$ - select exists ( - select 1 - from public.tenant_members tm - where tm.tenant_id = p_tenant_id - and tm.user_id = auth.uid() - and tm.role = 'tenant_admin' - and tm.status = 'active' - ); -$$; - - -ALTER FUNCTION public.is_tenant_admin(p_tenant_id uuid) OWNER TO supabase_admin; - --- --- Name: is_tenant_member(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.is_tenant_member(_tenant_id uuid) RETURNS boolean - LANGUAGE sql STABLE - AS $$ - select exists ( - select 1 - from public.tenant_members m - where m.tenant_id = _tenant_id - and m.user_id = auth.uid() - and m.status = 'active' - ); -$$; - - -ALTER FUNCTION public.is_tenant_member(_tenant_id uuid) OWNER TO supabase_admin; - --- --- Name: is_therapist_tenant(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.is_therapist_tenant(_tenant_id uuid) RETURNS boolean -CREATE FUNCTION public.is_tenant_member(_tenant_id uuid) RETURNS boolean - LANGUAGE sql STABLE - AS $$ - select exists ( - select 1 - from public.tenant_members m - where m.tenant_id = _tenant_id - and m.user_id = auth.uid() - and m.status = 'active' - ); -$$; - - -ALTER FUNCTION public.is_tenant_member(_tenant_id uuid) OWNER TO supabase_admin; - --- --- Name: is_therapist_tenant(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.is_therapist_tenant(_tenant_id uuid) RETURNS boolean - LANGUAGE sql STABLE - AS $$ - SELECT EXISTS ( - SELECT 1 FROM public.tenants t - WHERE t.id = _tenant_id AND t.kind = 'therapist' - ); -$$; - - -ALTER FUNCTION public.is_therapist_tenant(_tenant_id uuid) OWNER TO supabase_admin; - --- --- Name: jwt_email(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.jwt_email() RETURNS text -CREATE FUNCTION public.is_therapist_tenant(_tenant_id uuid) RETURNS boolean - LANGUAGE sql STABLE - AS $$ - SELECT EXISTS ( - SELECT 1 FROM public.tenants t - WHERE t.id = _tenant_id AND t.kind = 'therapist' - ); -$$; - - -ALTER FUNCTION public.is_therapist_tenant(_tenant_id uuid) OWNER TO supabase_admin; - --- --- Name: jwt_email(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.jwt_email() RETURNS text - LANGUAGE sql STABLE - AS $$ - select nullif(lower(current_setting('request.jwt.claim.email', true)), ''); -$$; - - -ALTER FUNCTION public.jwt_email() OWNER TO supabase_admin; - --- --- Name: list_financial_records(uuid, integer, integer, text, text, uuid, integer, integer); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -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 -CREATE FUNCTION public.jwt_email() RETURNS text - LANGUAGE sql STABLE - AS $$ - select nullif(lower(current_setting('request.jwt.claim.email', true)), ''); -$$; - - -ALTER FUNCTION public.jwt_email() OWNER TO supabase_admin; - --- --- Name: list_financial_records(uuid, integer, integer, text, text, uuid, integer, integer); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -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 -CREATE FUNCTION public.my_tenants() RETURNS TABLE(tenant_id uuid, role text, status text, kind text) - LANGUAGE sql STABLE - AS $$ - select - tm.tenant_id, - tm.role, - tm.status, - t.kind - from public.tenant_members tm - join public.tenants t on t.id = tm.tenant_id - where tm.user_id = auth.uid(); -$$; - - -ALTER FUNCTION public.my_tenants() OWNER TO supabase_admin; - --- --- Name: notice_track_click(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -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 -CREATE FUNCTION public.on_new_user_seed_patient_groups() RETURNS trigger - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public' - AS $$ - BEGIN - PERFORM public.seed_default_patient_groups(NEW.id); - RETURN NEW; - END; - $$; - - -ALTER FUNCTION public.on_new_user_seed_patient_groups() OWNER TO supabase_admin; - --- --- Name: patients_validate_member_consistency(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -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 -CREATE FUNCTION public.prevent_promoting_to_system() RETURNS trigger - LANGUAGE plpgsql - AS $$ -begin - if new.is_system = true and old.is_system is distinct from true then - raise exception 'Não é permitido transformar um grupo comum em grupo do sistema.'; - end if; - return new; -end; -$$; - - -ALTER FUNCTION public.prevent_promoting_to_system() OWNER TO supabase_admin; - --- --- Name: prevent_saas_membership(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.prevent_saas_membership() RETURNS trigger - LANGUAGE plpgsql - AS $$ -BEGIN - IF EXISTS ( - SELECT 1 - FROM public.profiles - WHERE id = NEW.user_id - AND role = 'saas_admin' - ) THEN - RAISE EXCEPTION 'SaaS admin cannot belong to tenant'; - END IF; - - RETURN NEW; -END; -$$; - - -ALTER FUNCTION public.prevent_saas_membership() OWNER TO supabase_admin; - --- --- Name: prevent_system_group_changes(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.prevent_system_group_changes() RETURNS trigger -CREATE FUNCTION public.prevent_saas_membership() RETURNS trigger - LANGUAGE plpgsql - AS $$ -BEGIN - IF EXISTS ( - SELECT 1 - FROM public.profiles - WHERE id = NEW.user_id - AND role = 'saas_admin' - ) THEN - RAISE EXCEPTION 'SaaS admin cannot belong to tenant'; - END IF; - - RETURN NEW; -END; -$$; - - -ALTER FUNCTION public.prevent_saas_membership() OWNER TO supabase_admin; - --- --- Name: prevent_system_group_changes(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -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.provision_account_tenant(p_user_id uuid, p_kind text, p_name text DEFAULT NULL::text) RETURNS uuid - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - v_tenant_id uuid; - v_account_type text; - v_name text; -BEGIN - IF p_kind NOT IN ('therapist', 'clinic_coworking', 'clinic_reception', 'clinic_full') THEN - RAISE EXCEPTION 'kind inválido: "%". Use: therapist, clinic_coworking, clinic_reception, clinic_full.', p_kind - USING ERRCODE = 'P0001'; - END IF; - - v_account_type := CASE WHEN p_kind = 'therapist' THEN 'therapist' ELSE 'clinic' END; - - IF EXISTS ( - SELECT 1 - FROM public.tenant_members tm - JOIN public.tenants t ON t.id = tm.tenant_id - WHERE tm.user_id = p_user_id - AND tm.role = 'tenant_admin' - AND tm.status = 'active' - AND t.kind = p_kind - ) THEN - RAISE EXCEPTION 'Usuário já possui um tenant do tipo "%".', p_kind - USING ERRCODE = 'P0001'; - END IF; - - v_name := COALESCE( - NULLIF(TRIM(p_name), ''), - ( - SELECT COALESCE(NULLIF(TRIM(pr.full_name), ''), SPLIT_PART(au.email, '@', 1)) - FROM public.profiles pr - JOIN auth.users au ON au.id = pr.id - WHERE pr.id = p_user_id - ), - 'Conta' - ); - - INSERT INTO public.tenants (name, kind, created_at) - VALUES (v_name, p_kind, now()) - RETURNING id INTO v_tenant_id; - - INSERT INTO public.tenant_members (tenant_id, user_id, role, status, created_at) - VALUES (v_tenant_id, p_user_id, 'tenant_admin', 'active', now()); - - UPDATE public.profiles - SET account_type = v_account_type - WHERE id = p_user_id; - - PERFORM public.seed_determined_commitments(v_tenant_id); - - RETURN v_tenant_id; -END; -$$; - - -ALTER FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text) OWNER TO supabase_admin; - --- --- Name: FUNCTION provision_account_tenant(p_user_id uuid, p_kind text, p_name text); Type: COMMENT; Schema: public; Owner: supabase_admin --- - -COMMENT ON FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text) IS 'Cria o tenant do tipo correto e atualiza account_type no profile. Chamar no onboarding após escolha/pagamento de plano therapist ou clinic. p_kind: therapist | clinic_coworking | clinic_reception | clinic_full'; - - --- --- Name: reactivate_subscription(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.reactivate_subscription(p_subscription_id uuid) RETURNS public.subscriptions -CREATE FUNCTION public.tenant_accept_invite(p_token uuid) RETURNS jsonb - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public', 'auth' - AS $$ -declare - v_uid uuid; - v_email text; - v_invite public.tenant_invites%rowtype; -begin - -- 1) precisa estar autenticado - v_uid := auth.uid(); - if v_uid is null then - raise exception 'not_authenticated' using errcode = 'P0001'; - end if; - - -- 2) pega email real do usuário logado sem depender do JWT claim - select u.email - into v_email - from auth.users u - where u.id = v_uid; - - if v_email is null or length(trim(v_email)) = 0 then - raise exception 'missing_user_email' using errcode = 'P0001'; - end if; - - -- 3) carrega o invite e trava linha (evita 2 aceites concorrentes) - select * - into v_invite - from public.tenant_invites i - where i.token = p_token - for update; - - if not found then - raise exception 'invite_not_found' using errcode = 'P0001'; - end if; - - -- 4) validações de estado - if v_invite.revoked_at is not null then - raise exception 'invite_revoked' using errcode = 'P0001'; - end if; - - if v_invite.accepted_at is not null then - raise exception 'invite_already_accepted' using errcode = 'P0001'; - end if; - - if v_invite.expires_at is not null and v_invite.expires_at <= now() then - raise exception 'invite_expired' using errcode = 'P0001'; - end if; - - -- 5) valida email (case-insensitive) - if lower(trim(v_invite.email)) <> lower(trim(v_email)) then - raise exception 'email_mismatch' using errcode = 'P0001'; - end if; - - -- 6) consome o invite - update public.tenant_invites - set accepted_at = now(), - accepted_by = v_uid - where id = v_invite.id; - - -- 7) cria ou reativa o membership - insert into public.tenant_members (tenant_id, user_id, role, status, created_at) - values (v_invite.tenant_id, v_uid, v_invite.role, 'active', now()) - on conflict (tenant_id, user_id) - do update set - role = excluded.role, - status = 'active'; - - -- 8) retorno útil pro front (você já tenta ler tenant_id no AcceptInvitePage) - return jsonb_build_object( - 'ok', true, - 'tenant_id', v_invite.tenant_id, - 'role', v_invite.role - ); -end; -$$; - - -ALTER FUNCTION public.tenant_accept_invite(p_token uuid) OWNER TO supabase_admin; - --- --- Name: tenant_members; Type: TABLE; Schema: public; Owner: supabase_admin --- - -CREATE TABLE public.tenant_members ( - id uuid DEFAULT gen_random_uuid() NOT NULL, - tenant_id uuid NOT NULL, - user_id uuid NOT NULL, - role text NOT NULL, - status text DEFAULT 'active'::text NOT NULL, - created_at timestamp with time zone DEFAULT now() NOT NULL -); - - -ALTER TABLE public.tenant_members OWNER TO supabase_admin; - --- --- Name: tenant_add_member_by_email(uuid, text, text); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_add_member_by_email(p_tenant_id uuid, p_email text, p_role text DEFAULT 'therapist'::text) RETURNS public.tenant_members - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public', 'auth' - AS $$ -declare - v_target_uid uuid; - v_member public.tenant_members%rowtype; - v_is_admin boolean; - v_email text; -begin - if p_tenant_id is null then - raise exception 'tenant_id é obrigatório'; - end if; - - v_email := lower(trim(coalesce(p_email, ''))); - if v_email = '' then - raise exception 'email é obrigatório'; - end if; - - -- valida role permitida - if p_role not in ('tenant_admin','therapist','secretary','patient') then - raise exception 'role inválida: %', p_role; - end if; - - -- apenas admin do tenant (role real no banco) - select exists ( - select 1 - from public.tenant_members tm - where tm.tenant_id = p_tenant_id - and tm.user_id = auth.uid() - and tm.role = 'tenant_admin' - and coalesce(tm.status,'active') = 'active' - ) into v_is_admin; - - if not v_is_admin then - raise exception 'sem permissão: apenas admin da clínica pode adicionar membros'; - end if; - - -- acha usuário pelo e-mail no Supabase Auth - select u.id - into v_target_uid - from auth.users u - where lower(u.email) = v_email - limit 1; - - if v_target_uid is null then - raise exception 'nenhum usuário encontrado com este e-mail'; - end if; - - -- cria ou reativa membro - insert into public.tenant_members (tenant_id, user_id, role, status) - values (p_tenant_id, v_target_uid, p_role, 'active') - on conflict (tenant_id, user_id) - do update set - role = excluded.role, - status = 'active' - returning * into v_member; - - return v_member; -end; -$$; - - -ALTER FUNCTION public.tenant_add_member_by_email(p_tenant_id uuid, p_email text, p_role text) OWNER TO supabase_admin; - --- --- Name: tenant_feature_allowed(uuid, text); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_feature_allowed(p_tenant_id uuid, p_feature_key text) RETURNS boolean -CREATE FUNCTION public.tenant_add_member_by_email(p_tenant_id uuid, p_email text, p_role text DEFAULT 'therapist'::text) RETURNS public.tenant_members - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public', 'auth' - AS $$ -declare - v_target_uid uuid; - v_member public.tenant_members%rowtype; - v_is_admin boolean; - v_email text; -begin - if p_tenant_id is null then - raise exception 'tenant_id é obrigatório'; - end if; - - v_email := lower(trim(coalesce(p_email, ''))); - if v_email = '' then - raise exception 'email é obrigatório'; - end if; - - -- valida role permitida - if p_role not in ('tenant_admin','therapist','secretary','patient') then - raise exception 'role inválida: %', p_role; - end if; - - -- apenas admin do tenant (role real no banco) - select exists ( - select 1 - from public.tenant_members tm - where tm.tenant_id = p_tenant_id - and tm.user_id = auth.uid() - and tm.role = 'tenant_admin' - and coalesce(tm.status,'active') = 'active' - ) into v_is_admin; - - if not v_is_admin then - raise exception 'sem permissão: apenas admin da clínica pode adicionar membros'; - end if; - - -- acha usuário pelo e-mail no Supabase Auth - select u.id - into v_target_uid - from auth.users u - where lower(u.email) = v_email - limit 1; - - if v_target_uid is null then - raise exception 'nenhum usuário encontrado com este e-mail'; - end if; - - -- cria ou reativa membro - insert into public.tenant_members (tenant_id, user_id, role, status) - values (p_tenant_id, v_target_uid, p_role, 'active') - on conflict (tenant_id, user_id) - do update set - role = excluded.role, - status = 'active' - returning * into v_member; - - return v_member; -end; -$$; - - -ALTER FUNCTION public.tenant_add_member_by_email(p_tenant_id uuid, p_email text, p_role text) OWNER TO supabase_admin; - --- --- Name: tenant_feature_allowed(uuid, text); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_feature_allowed(p_tenant_id uuid, p_feature_key text) RETURNS boolean -CREATE FUNCTION public.tenant_feature_allowed(p_tenant_id uuid, p_feature_key text) RETURNS boolean - LANGUAGE sql STABLE - AS $$ - select exists ( - select 1 - from public.v_tenant_entitlements v - where v.tenant_id = p_tenant_id - and v.feature_key = p_feature_key - and coalesce(v.allowed, false) = true - ); -$$; - - -ALTER FUNCTION public.tenant_feature_allowed(p_tenant_id uuid, p_feature_key text) OWNER TO supabase_admin; - --- --- Name: tenant_feature_enabled(uuid, text); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_feature_enabled(p_tenant_id uuid, p_feature_key text) RETURNS boolean - LANGUAGE sql STABLE - AS $$ - select coalesce( - (select tf.enabled - from public.tenant_features tf - where tf.tenant_id = p_tenant_id and tf.feature_key = p_feature_key), - false - ); -$$; - - -ALTER FUNCTION public.tenant_feature_enabled(p_tenant_id uuid, p_feature_key text) OWNER TO supabase_admin; - --- --- Name: tenant_features_guard_with_plan(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_features_guard_with_plan() RETURNS trigger -CREATE FUNCTION public.tenant_feature_enabled(p_tenant_id uuid, p_feature_key text) RETURNS boolean - LANGUAGE sql STABLE - AS $$ - select coalesce( - (select tf.enabled - from public.tenant_features tf - where tf.tenant_id = p_tenant_id and tf.feature_key = p_feature_key), - false - ); -$$; - - -ALTER FUNCTION public.tenant_feature_enabled(p_tenant_id uuid, p_feature_key text) OWNER TO supabase_admin; - --- --- Name: tenant_features_guard_with_plan(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_features_guard_with_plan() RETURNS trigger - LANGUAGE plpgsql - AS $$ -declare - v_allowed boolean; -begin - -- só valida quando está habilitando - if new.enabled is distinct from true then - return new; - end if; - - -- permitido pelo plano do tenant? - select exists ( - select 1 - from public.v_tenant_entitlements_full v - where v.tenant_id = new.tenant_id - and v.feature_key = new.feature_key - and v.allowed = true - ) - into v_allowed; - - if not v_allowed then - raise exception 'Feature % não permitida pelo plano atual do tenant %.', - new.feature_key, new.tenant_id - using errcode = 'P0001'; - end if; - - return new; -end; -$$; - - -ALTER FUNCTION public.tenant_features_guard_with_plan() OWNER TO supabase_admin; - --- --- Name: tenant_has_feature(uuid, text); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) RETURNS boolean - LANGUAGE sql STABLE - AS $$ - select - exists ( - select 1 - from public.v_tenant_entitlements e - where e.tenant_id = _tenant_id - and e.feature_key = _feature - and e.allowed = true - ) - or exists ( - select 1 - from public.tenant_features tf - where tf.tenant_id = _tenant_id - and tf.feature_key = _feature - and tf.enabled = true - ); -$$; - - -ALTER FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) OWNER TO supabase_admin; - --- --- Name: tenant_invite_member_by_email(uuid, text, text); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) RETURNS uuid -CREATE FUNCTION public.tenant_features_guard_with_plan() RETURNS trigger - LANGUAGE plpgsql - AS $$ -declare - v_allowed boolean; -begin - -- só valida quando está habilitando - if new.enabled is distinct from true then - return new; - end if; - - -- permitido pelo plano do tenant? - select exists ( - select 1 - from public.v_tenant_entitlements_full v - where v.tenant_id = new.tenant_id - and v.feature_key = new.feature_key - and v.allowed = true - ) - into v_allowed; - - if not v_allowed then - raise exception 'Feature % não permitida pelo plano atual do tenant %.', - new.feature_key, new.tenant_id - using errcode = 'P0001'; - end if; - - return new; -end; -$$; - - -ALTER FUNCTION public.tenant_features_guard_with_plan() OWNER TO supabase_admin; - --- --- Name: tenant_has_feature(uuid, text); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) RETURNS boolean - LANGUAGE sql STABLE - AS $$ - select - exists ( - select 1 - from public.v_tenant_entitlements e - where e.tenant_id = _tenant_id - and e.feature_key = _feature - and e.allowed = true - ) - or exists ( - select 1 - from public.tenant_features tf - where tf.tenant_id = _tenant_id - and tf.feature_key = _feature - and tf.enabled = true - ); -$$; - - -ALTER FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) OWNER TO supabase_admin; - --- --- Name: tenant_invite_member_by_email(uuid, text, text); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) RETURNS uuid -CREATE FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) RETURNS boolean - LANGUAGE sql STABLE - AS $$ - select - exists ( - select 1 - from public.v_tenant_entitlements e - where e.tenant_id = _tenant_id - and e.feature_key = _feature - and e.allowed = true - ) - or exists ( - select 1 - from public.tenant_features tf - where tf.tenant_id = _tenant_id - and tf.feature_key = _feature - and tf.enabled = true - ); -$$; - - -ALTER FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) OWNER TO supabase_admin; - --- --- Name: tenant_invite_member_by_email(uuid, text, text); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) RETURNS uuid - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public', 'auth' - AS $$ -declare - v_email text; - v_my_email text; - v_token uuid; - v_updated int; -begin - -- validações básicas - if p_tenant_id is null then - raise exception 'tenant_id inválido' using errcode = 'P0001'; - end if; - - v_email := lower(trim(coalesce(p_email, ''))); - if v_email = '' then - raise exception 'Informe um email' using errcode = 'P0001'; - end if; - - -- role permitido (ajuste se quiser) - if p_role is null or p_role not in ('therapist', 'secretary') then - raise exception 'Role inválido (use therapist/secretary)' using errcode = 'P0001'; - end if; - - -- ✅ bloqueio: auto-convite - v_my_email := public.get_my_email(); - if v_my_email is not null and v_email = v_my_email then - raise exception 'Você não pode convidar o seu próprio email.' using errcode = 'P0001'; - end if; - - -- ✅ bloqueio: já é membro ativo do tenant - if exists ( - select 1 - from tenant_members tm - join auth.users au on au.id = tm.user_id - where tm.tenant_id = p_tenant_id - and tm.status = 'active' - and lower(au.email) = v_email - ) then - raise exception 'Este email já está vinculado a esta clínica.' using errcode = 'P0001'; - end if; - - -- ✅ permissão: só admin do tenant pode convidar - if not exists ( - select 1 - from tenant_members me - where me.tenant_id = p_tenant_id - and me.user_id = auth.uid() - and me.status = 'active' - and me.role in ('tenant_admin','clinic_admin') - ) then - raise exception 'Sem permissão para convidar membros.' using errcode = 'P0001'; - end if; - - -- Gera token (reenvio simples / regeneração) - v_token := gen_random_uuid(); - - -- 1) tenta "regerar" um convite pendente existente (mesmo email) - update tenant_invites - set token = v_token, - role = p_role, - created_at = now(), - expires_at = now() + interval '7 days', - accepted_at = null, - revoked_at = null - where tenant_id = p_tenant_id - and lower(email) = v_email - and accepted_at is null - and revoked_at is null; - - get diagnostics v_updated = row_count; - - -- 2) se não atualizou nada, cria convite novo - if v_updated = 0 then - insert into tenant_invites (tenant_id, email, role, token, created_at, expires_at) - values (p_tenant_id, v_email, p_role, v_token, now(), now() + interval '7 days'); - end if; - - return v_token; -end; -$$; - - -ALTER FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) OWNER TO supabase_admin; - --- --- Name: tenant_reactivate_member(uuid, uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid) RETURNS void -CREATE FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) RETURNS uuid - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public', 'auth' - AS $$ -declare - v_email text; - v_my_email text; - v_token uuid; - v_updated int; -begin - -- validações básicas - if p_tenant_id is null then - raise exception 'tenant_id inválido' using errcode = 'P0001'; - end if; - - v_email := lower(trim(coalesce(p_email, ''))); - if v_email = '' then - raise exception 'Informe um email' using errcode = 'P0001'; - end if; - - -- role permitido (ajuste se quiser) - if p_role is null or p_role not in ('therapist', 'secretary') then - raise exception 'Role inválido (use therapist/secretary)' using errcode = 'P0001'; - end if; - - -- ✅ bloqueio: auto-convite - v_my_email := public.get_my_email(); - if v_my_email is not null and v_email = v_my_email then - raise exception 'Você não pode convidar o seu próprio email.' using errcode = 'P0001'; - end if; - - -- ✅ bloqueio: já é membro ativo do tenant - if exists ( - select 1 - from tenant_members tm - join auth.users au on au.id = tm.user_id - where tm.tenant_id = p_tenant_id - and tm.status = 'active' - and lower(au.email) = v_email - ) then - raise exception 'Este email já está vinculado a esta clínica.' using errcode = 'P0001'; - end if; - - -- ✅ permissão: só admin do tenant pode convidar - if not exists ( - select 1 - from tenant_members me - where me.tenant_id = p_tenant_id - and me.user_id = auth.uid() - and me.status = 'active' - and me.role in ('tenant_admin','clinic_admin') - ) then - raise exception 'Sem permissão para convidar membros.' using errcode = 'P0001'; - end if; - - -- Gera token (reenvio simples / regeneração) - v_token := gen_random_uuid(); - - -- 1) tenta "regerar" um convite pendente existente (mesmo email) - update tenant_invites - set token = v_token, - role = p_role, - created_at = now(), - expires_at = now() + interval '7 days', - accepted_at = null, - revoked_at = null - where tenant_id = p_tenant_id - and lower(email) = v_email - and accepted_at is null - and revoked_at is null; - - get diagnostics v_updated = row_count; - - -- 2) se não atualizou nada, cria convite novo - if v_updated = 0 then - insert into tenant_invites (tenant_id, email, role, token, created_at, expires_at) - values (p_tenant_id, v_email, p_role, v_token, now(), now() + interval '7 days'); - end if; - - return v_token; -end; -$$; - - -ALTER FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) OWNER TO supabase_admin; - --- --- Name: tenant_reactivate_member(uuid, uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid) RETURNS void - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public' - SET row_security TO 'off' - AS $$ -begin - if auth.uid() is null then - raise exception 'not_authenticated'; - end if; - - if not public.is_tenant_admin(p_tenant_id) then - raise exception 'not_allowed'; - end if; - - update public.tenant_members - set status = 'active' - where tenant_id = p_tenant_id - and user_id = p_member_user_id; - - if not found then - raise exception 'membership_not_found'; - end if; -end; -$$; - - -ALTER FUNCTION public.tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid) OWNER TO supabase_admin; - --- --- Name: tenant_remove_member(uuid, uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid) RETURNS void -CREATE FUNCTION public.tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid) RETURNS void - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public' - SET row_security TO 'off' - AS $$ -begin - if auth.uid() is null then - raise exception 'not_authenticated'; - end if; - - if not public.is_tenant_admin(p_tenant_id) then - raise exception 'not_allowed'; - end if; - - update public.tenant_members - set status = 'active' - where tenant_id = p_tenant_id - and user_id = p_member_user_id; - - if not found then - raise exception 'membership_not_found'; - end if; -end; -$$; - - -ALTER FUNCTION public.tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid) OWNER TO supabase_admin; - --- --- Name: tenant_remove_member(uuid, uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid) RETURNS void - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public' - SET row_security TO 'off' - AS $$ -declare - v_role text; -begin - if auth.uid() is null then - raise exception 'not_authenticated'; - end if; - - if not public.is_tenant_admin(p_tenant_id) then - raise exception 'not_allowed'; - end if; - - if p_member_user_id = auth.uid() then - raise exception 'cannot_remove_self'; - end if; - - -- pega role atual do membro (se não existir, erro) - select role into v_role - from public.tenant_members - where tenant_id = p_tenant_id - and user_id = p_member_user_id; - - if v_role is null then - raise exception 'membership_not_found'; - end if; - - -- trava: se for therapist, não pode remover com eventos futuros - if v_role = 'therapist' then - if exists ( - select 1 - from public.agenda_eventos e - where e.owner_id = p_tenant_id - and e.terapeuta_id = p_member_user_id - and e.inicio_em >= now() - and e.status::text not in ('cancelado','cancelled','canceled') - limit 1 - ) then - raise exception 'cannot_remove_therapist_with_future_events'; - end if; - end if; - - update public.tenant_members - set status = 'inactive' - where tenant_id = p_tenant_id - and user_id = p_member_user_id; - - if not found then - raise exception 'membership_not_found'; - end if; -end; -$$; - - -ALTER FUNCTION public.tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid) OWNER TO supabase_admin; - --- --- Name: tenant_remove_member_soft(uuid, uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid) RETURNS void -CREATE FUNCTION public.tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid) RETURNS void - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public' - SET row_security TO 'off' - AS $$ -declare - v_role text; -begin - if auth.uid() is null then - raise exception 'not_authenticated'; - end if; - - if not public.is_tenant_admin(p_tenant_id) then - raise exception 'not_allowed'; - end if; - - if p_member_user_id = auth.uid() then - raise exception 'cannot_remove_self'; - end if; - - -- pega role atual do membro (se não existir, erro) - select role into v_role - from public.tenant_members - where tenant_id = p_tenant_id - and user_id = p_member_user_id; - - if v_role is null then - raise exception 'membership_not_found'; - end if; - - -- trava: se for therapist, não pode remover com eventos futuros - if v_role = 'therapist' then - if exists ( - select 1 - from public.agenda_eventos e - where e.owner_id = p_tenant_id - and e.terapeuta_id = p_member_user_id - and e.inicio_em >= now() - and e.status::text not in ('cancelado','cancelled','canceled') - limit 1 - ) then - raise exception 'cannot_remove_therapist_with_future_events'; - end if; - end if; - - update public.tenant_members - set status = 'inactive' - where tenant_id = p_tenant_id - and user_id = p_member_user_id; - - if not found then - raise exception 'membership_not_found'; - end if; -end; -$$; - - -ALTER FUNCTION public.tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid) OWNER TO supabase_admin; - --- --- Name: tenant_remove_member_soft(uuid, uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid) RETURNS void - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public' - SET row_security TO 'off' - AS $$ -begin - if auth.uid() is null then - raise exception 'not_authenticated'; - end if; - - if not public.is_tenant_admin(p_tenant_id) then - raise exception 'not_allowed'; - end if; - - if p_member_user_id = auth.uid() then - raise exception 'cannot_remove_self'; - end if; - - update public.tenant_members - set status = 'inactive' - where tenant_id = p_tenant_id - and user_id = p_member_user_id; - - if not found then - raise exception 'membership_not_found'; - end if; -end; -$$; - - -ALTER FUNCTION public.tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid) OWNER TO supabase_admin; - --- --- Name: tenant_revoke_invite(uuid, text, text); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text) RETURNS void -CREATE FUNCTION public.tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid) RETURNS void - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public' - SET row_security TO 'off' - AS $$ -begin - if auth.uid() is null then - raise exception 'not_authenticated'; - end if; - - if not public.is_tenant_admin(p_tenant_id) then - raise exception 'not_allowed'; - end if; - - if p_member_user_id = auth.uid() then - raise exception 'cannot_remove_self'; - end if; - - update public.tenant_members - set status = 'inactive' - where tenant_id = p_tenant_id - and user_id = p_member_user_id; - - if not found then - raise exception 'membership_not_found'; - end if; -end; -$$; - - -ALTER FUNCTION public.tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid) OWNER TO supabase_admin; - --- --- Name: tenant_revoke_invite(uuid, text, text); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text) RETURNS void - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public' - SET row_security TO 'off' - AS $$ -declare - v_email text; -begin - if auth.uid() is null then - raise exception 'not_authenticated'; - end if; - - if not public.is_tenant_admin(p_tenant_id) then - raise exception 'not_allowed'; - end if; - - v_email := lower(trim(p_email)); - - update public.tenant_invites - set revoked_at = now(), - revoked_by = auth.uid() - where tenant_id = p_tenant_id - and lower(email) = v_email - and role = p_role - and accepted_at is null - and revoked_at is null; - - if not found then - raise exception 'invite_not_found'; - end if; -end; -$$; - - -ALTER FUNCTION public.tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text) OWNER TO supabase_admin; - --- --- Name: tenant_set_member_status(uuid, uuid, text); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text) RETURNS void -CREATE FUNCTION public.tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text) RETURNS void - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public' - SET row_security TO 'off' - AS $$ -declare - v_email text; -begin - if auth.uid() is null then - raise exception 'not_authenticated'; - end if; - - if not public.is_tenant_admin(p_tenant_id) then - raise exception 'not_allowed'; - end if; - - v_email := lower(trim(p_email)); - - update public.tenant_invites - set revoked_at = now(), - revoked_by = auth.uid() - where tenant_id = p_tenant_id - and lower(email) = v_email - and role = p_role - and accepted_at is null - and revoked_at is null; - - if not found then - raise exception 'invite_not_found'; - end if; -end; -$$; - - -ALTER FUNCTION public.tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text) OWNER TO supabase_admin; - --- --- Name: tenant_set_member_status(uuid, uuid, text); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text) RETURNS void - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public' - SET row_security TO 'off' - AS $$ -begin - if auth.uid() is null then - raise exception 'not_authenticated'; - end if; - - -- valida status (adapte aos seus valores reais) - if p_new_status not in ('active','inactive','suspended','invited') then - raise exception 'invalid_status: %', p_new_status; - end if; - - if not public.is_tenant_admin(p_tenant_id) then - raise exception 'not_allowed'; - end if; - - -- evita desativar a si mesmo (opcional) - if p_member_user_id = auth.uid() and p_new_status <> 'active' then - raise exception 'cannot_disable_self'; - end if; - - update public.tenant_members - set status = p_new_status - where tenant_id = p_tenant_id - and user_id = p_member_user_id; - - if not found then - raise exception 'membership_not_found'; - end if; -end; -$$; - - -ALTER FUNCTION public.tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text) OWNER TO supabase_admin; - --- --- Name: tenant_update_member_role(uuid, uuid, text); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text) RETURNS void -CREATE FUNCTION public.tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text) RETURNS void - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public' - SET row_security TO 'off' - AS $$ -begin - if auth.uid() is null then - raise exception 'not_authenticated'; - end if; - - -- valida status (adapte aos seus valores reais) - if p_new_status not in ('active','inactive','suspended','invited') then - raise exception 'invalid_status: %', p_new_status; - end if; - - if not public.is_tenant_admin(p_tenant_id) then - raise exception 'not_allowed'; - end if; - - -- evita desativar a si mesmo (opcional) - if p_member_user_id = auth.uid() and p_new_status <> 'active' then - raise exception 'cannot_disable_self'; - end if; - - update public.tenant_members - set status = p_new_status - where tenant_id = p_tenant_id - and user_id = p_member_user_id; - - if not found then - raise exception 'membership_not_found'; - end if; -end; -$$; - - -ALTER FUNCTION public.tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text) OWNER TO supabase_admin; - --- --- Name: tenant_update_member_role(uuid, uuid, text); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text) RETURNS void - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public' - SET row_security TO 'off' - AS $$ -begin - -- exige auth - if auth.uid() is null then - raise exception 'not_authenticated'; - end if; - - -- valida role - if p_new_role not in ('tenant_admin','therapist','secretary','patient') then - raise exception 'invalid_role: %', p_new_role; - end if; - - -- somente tenant_admin ativo pode alterar role - if not public.is_tenant_admin(p_tenant_id) then - raise exception 'not_allowed'; - end if; - - -- evita o admin remover o próprio admin sem querer (opcional mas recomendado) - if p_member_user_id = auth.uid() and p_new_role <> 'tenant_admin' then - raise exception 'cannot_demote_self'; - end if; - - update public.tenant_members - set role = p_new_role - where tenant_id = p_tenant_id - and user_id = p_member_user_id; - - if not found then - raise exception 'membership_not_found'; - end if; -end; -$$; - - -ALTER FUNCTION public.tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text) OWNER TO supabase_admin; - --- --- Name: toggle_plan(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.toggle_plan(owner uuid) RETURNS void -CREATE FUNCTION public.tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text) RETURNS void - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public' - SET row_security TO 'off' - AS $$ -begin - -- exige auth - if auth.uid() is null then - raise exception 'not_authenticated'; - end if; - - -- valida role - if p_new_role not in ('tenant_admin','therapist','secretary','patient') then - raise exception 'invalid_role: %', p_new_role; - end if; - - -- somente tenant_admin ativo pode alterar role - if not public.is_tenant_admin(p_tenant_id) then - raise exception 'not_allowed'; - end if; - - -- evita o admin remover o próprio admin sem querer (opcional mas recomendado) - if p_member_user_id = auth.uid() and p_new_role <> 'tenant_admin' then - raise exception 'cannot_demote_self'; - end if; - - update public.tenant_members - set role = p_new_role - where tenant_id = p_tenant_id - and user_id = p_member_user_id; - - if not found then - raise exception 'membership_not_found'; - end if; -end; -$$; - - -ALTER FUNCTION public.tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text) OWNER TO supabase_admin; - --- --- Name: toggle_plan(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.toggle_plan(owner uuid) RETURNS void -CREATE FUNCTION public.user_has_feature(_user_id uuid, _feature text) RETURNS boolean - LANGUAGE sql STABLE - AS $$ - select exists ( - select 1 - from public.v_user_entitlements e - where e.user_id = _user_id - and e.feature_key = _feature - and e.allowed = true - ); -$$; - - -ALTER FUNCTION public.user_has_feature(_user_id uuid, _feature text) OWNER TO supabase_admin; - --- --- Name: validate_support_session(text); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -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) - LANGUAGE sql STABLE - AS $$ - select auth.uid() as uid, auth.role() as role; -$$; - - -ALTER FUNCTION public.whoami() OWNER TO supabase_admin; - --- --- Name: apply_rls(jsonb, integer); Type: FUNCTION; Schema: realtime; Owner: supabase_admin --- - -CREATE FUNCTION realtime.apply_rls(wal jsonb, max_record_bytes integer DEFAULT (1024 * 1024)) RETURNS SETOF realtime.wal_rls -CREATE FUNCTION public.current_member_id(p_tenant_id uuid) RETURNS uuid - LANGUAGE sql STABLE - AS $$ - select tm.id - from public.tenant_members tm - where tm.tenant_id = p_tenant_id - and tm.user_id = auth.uid() - limit 1 -$$; - - -ALTER FUNCTION public.current_member_id(p_tenant_id uuid) OWNER TO supabase_admin; - --- --- Name: current_member_role(uuid); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.current_member_role(p_tenant_id uuid) RETURNS text - LANGUAGE sql STABLE - AS $$ - select tm.role - from public.tenant_members tm - where tm.tenant_id = p_tenant_id - and tm.user_id = auth.uid() - limit 1 -$$; - - -ALTER FUNCTION public.current_member_role(p_tenant_id uuid) OWNER TO supabase_admin; - --- --- Name: debit_addon_credit(uuid, text, uuid, text); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid DEFAULT NULL::uuid, p_description text DEFAULT 'Consumo'::text) RETURNS jsonb -CREATE FUNCTION public.dev_list_auth_users(p_limit integer DEFAULT 50) RETURNS TABLE(id uuid, email text, created_at timestamp with time zone) - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public', 'auth' - AS $$ -begin - -- só saas_admin pode ver - if not exists ( - select 1 - from public.profiles p - where p.id = auth.uid() - and p.role = 'saas_admin' - ) then - return; - end if; - - return query - select - u.id, - u.email, - u.created_at - from auth.users u - order by u.created_at desc - limit greatest(1, least(coalesce(p_limit, 50), 500)); -end; -$$; - - -ALTER FUNCTION public.dev_list_auth_users(p_limit integer) OWNER TO supabase_admin; - --- --- Name: dev_list_custom_users(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.dev_list_custom_users() RETURNS TABLE(user_id uuid, email text, created_at timestamp with time zone, global_role text, tenant_role text, tenant_id uuid, password_dev text, kind text) - LANGUAGE sql SECURITY DEFINER - SET search_path TO 'public' - AS $$ - with base as ( - select - u.id as user_id, - lower(u.email) as email, - u.created_at - from auth.users u - where lower(u.email) not in ( - 'clinic@agenciapsi.com.br', - 'therapist@agenciapsi.com.br', - 'patient@agenciapsi.com.br', - 'saas@agenciapsi.com.br' - ) - ), - prof as ( - select p.id, p.role as global_role - from public.profiles p - ), - last_membership as ( - select distinct on (tm.user_id) - tm.user_id, - tm.tenant_id, - tm.role as tenant_role, - tm.created_at - from public.tenant_members tm - where tm.status = 'active' - order by tm.user_id, tm.created_at desc - ) - select - b.user_id, - b.email, - b.created_at, - pr.global_role, - lm.tenant_role, - lm.tenant_id, - dc.password_dev, - dc.kind - from base b - left join prof pr on pr.id = b.user_id - left join last_membership lm on lm.user_id = b.user_id - left join public.dev_user_credentials dc on lower(dc.email) = b.email - order by b.created_at desc; -$$; - - -ALTER FUNCTION public.dev_list_custom_users() OWNER TO supabase_admin; - --- --- Name: dev_list_intent_leads(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.dev_list_intent_leads() RETURNS TABLE(email text, last_intent_at timestamp with time zone, plan_key text, billing_interval text, status text, tenant_id uuid) - LANGUAGE sql SECURITY DEFINER - SET search_path TO 'public' - AS $$ - select - lower(si.email) as email, - max(si.created_at) as last_intent_at, - (array_agg(si.plan_key order by si.created_at desc))[1] as plan_key, - (array_agg(si.interval order by si.created_at desc))[1] as billing_interval, - (array_agg(si.status order by si.created_at desc))[1] as status, - (array_agg(si.tenant_id order by si.created_at desc))[1] as tenant_id - from public.subscription_intents si - where si.email is not null - and not exists ( - select 1 - from auth.users au - where lower(au.email) = lower(si.email) - ) - group by lower(si.email) - order by max(si.created_at) desc; -$$; - - -ALTER FUNCTION public.dev_list_intent_leads() OWNER TO supabase_admin; - --- --- Name: dev_public_debug_snapshot(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.dev_public_debug_snapshot() RETURNS TABLE(users_total integer, tenants_total integer, intents_new_total integer, latest_intents jsonb) - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'public' - AS $_$ -declare - v_latest jsonb; -begin - select jsonb_agg( - jsonb_build_object( - 'created_at', si.created_at, - 'email_masked', - regexp_replace(lower(si.email), '(^.).*(@.*$)', '\1***\2'), - 'plan_key', si.plan_key, - 'status', si.status - ) - order by si.created_at desc - ) - into v_latest - from ( - select si.* - from public.subscription_intents si - where si.email is not null - order by si.created_at desc - limit 5 - ) si; - - return query - select - (select count(*)::int from auth.users) as users_total, - (select count(*)::int from public.tenants) as tenants_total, - (select count(*)::int from public.subscription_intents where status = 'new') as intents_new_total, - coalesce(v_latest, '[]'::jsonb) as latest_intents; -end; -$_$; - - -ALTER FUNCTION public.dev_public_debug_snapshot() OWNER TO supabase_admin; - --- --- Name: ensure_personal_tenant(); Type: FUNCTION; Schema: public; Owner: supabase_admin --- - -CREATE FUNCTION public.ensure_personal_tenant() RETURNS uuid diff --git a/database-novo/schema/03_functions/infra.sql b/database-novo/schema/03_functions/extensions.sql similarity index 75% rename from database-novo/schema/03_functions/infra.sql rename to database-novo/schema/03_functions/extensions.sql index 8b7b663..935536b 100644 --- a/database-novo/schema/03_functions/infra.sql +++ b/database-novo/schema/03_functions/extensions.sql @@ -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; diff --git a/database-novo/schema/03_functions/financial.sql b/database-novo/schema/03_functions/financial.sql deleted file mode 100644 index eabe61e..0000000 --- a/database-novo/schema/03_functions/financial.sql +++ /dev/null @@ -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 diff --git a/database-novo/schema/03_functions/misc.sql b/database-novo/schema/03_functions/misc.sql deleted file mode 100644 index dcf22f4..0000000 --- a/database-novo/schema/03_functions/misc.sql +++ /dev/null @@ -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) diff --git a/database-novo/schema/03_functions/notifications.sql b/database-novo/schema/03_functions/notifications.sql deleted file mode 100644 index 161377e..0000000 --- a/database-novo/schema/03_functions/notifications.sql +++ /dev/null @@ -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 diff --git a/database-novo/schema/03_functions/patients.sql b/database-novo/schema/03_functions/patients.sql deleted file mode 100644 index 8d9d9f0..0000000 --- a/database-novo/schema/03_functions/patients.sql +++ /dev/null @@ -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 diff --git a/database-novo/schema/03_functions/pgbouncer.sql b/database-novo/schema/03_functions/pgbouncer.sql new file mode 100644 index 0000000..2cb77aa --- /dev/null +++ b/database-novo/schema/03_functions/pgbouncer.sql @@ -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; +$_$; diff --git a/database-novo/schema/03_functions/public.sql b/database-novo/schema/03_functions/public.sql new file mode 100644 index 0000000..ab9658f --- /dev/null +++ b/database-novo/schema/03_functions/public.sql @@ -0,0 +1,4546 @@ +-- Functions: public +-- Gerado automaticamente em 2026-04-17T12:23:05.222Z +-- Total: 126 + +CREATE FUNCTION public.__rls_ping() RETURNS text + LANGUAGE sql STABLE + AS $$ + select 'ok'::text; +$$; + +CREATE FUNCTION public.activate_subscription_from_intent(p_intent_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_intent record; + v_sub public.subscriptions; + v_days int; + v_user_id uuid; + v_plan_id uuid; + v_target text; +begin + -- l?? pela VIEW unificada + select * into v_intent + from public.subscription_intents + where id = p_intent_id; + + if not found then + raise exception 'Intent n??o encontrado: %', p_intent_id; + end if; + + if v_intent.status <> 'paid' then + raise exception 'Intent precisa estar paid para ativar assinatura'; + end if; + + -- resolve target e plan_id via plans.key + select p.id, p.target + into v_plan_id, v_target + from public.plans p + where p.key = v_intent.plan_key + limit 1; + + if v_plan_id is null then + raise exception 'Plano n??o encontrado em plans.key = %', v_intent.plan_key; + end if; + + v_target := lower(coalesce(v_target, '')); + + -- ??? supervisor adicionado + if v_target not in ('clinic', 'therapist', 'supervisor') then + raise exception 'Target inv??lido em plans.target: %', v_target; + end if; + + -- regra por target + if v_target = 'clinic' then + if v_intent.tenant_id is null then + raise exception 'Intent sem tenant_id'; + end if; + else + -- therapist ou supervisor: vinculado ao user + v_user_id := v_intent.user_id; + if v_user_id is null then + v_user_id := v_intent.created_by_user_id; + end if; + end if; + + if v_target in ('therapist', 'supervisor') and v_user_id is null then + raise exception 'N??o foi poss??vel determinar user_id para assinatura %.', v_target; + end if; + + -- cancela assinatura ativa anterior + if v_target = 'clinic' then + update public.subscriptions + set status = 'cancelled', + cancelled_at = now() + where tenant_id = v_intent.tenant_id + and plan_id = v_plan_id + and status = 'active'; + else + -- therapist ou supervisor + update public.subscriptions + set status = 'cancelled', + cancelled_at = now() + where user_id = v_user_id + and plan_id = v_plan_id + and status = 'active' + and tenant_id is null; + end if; + + -- dura????o do plano (30 dias para mensal) + v_days := case + when lower(coalesce(v_intent.interval, 'month')) = 'year' then 365 + else 30 + end; + + -- cria nova assinatura + insert into public.subscriptions ( + user_id, + plan_id, + status, + started_at, + expires_at, + cancelled_at, + activated_at, + tenant_id, + plan_key, + interval, + source, + created_at, + updated_at + ) + values ( + case when v_target = 'clinic' then null else v_user_id end, + v_plan_id, + 'active', + now(), + now() + make_interval(days => v_days), + null, + now(), + case when v_target = 'clinic' then v_intent.tenant_id else null end, + v_intent.plan_key, + v_intent.interval, + 'manual', + now(), + now() + ) + returning * into v_sub; + + -- grava v??nculo intent ??? subscription + if v_target = 'clinic' then + update public.subscription_intents_tenant + set subscription_id = v_sub.id + where id = p_intent_id; + else + update public.subscription_intents_personal + set subscription_id = v_sub.id + where id = p_intent_id; + end if; + + return v_sub; +end; +$$; + +CREATE FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid DEFAULT NULL::uuid, p_description text DEFAULT 'Cr??dito manual'::text, p_payment_method text DEFAULT 'manual'::text, p_price_cents integer DEFAULT 0) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_credit addon_credits%ROWTYPE; + v_balance_before INTEGER; + v_balance_after INTEGER; + v_tx_id UUID; +BEGIN + -- Upsert addon_credits + INSERT INTO addon_credits (tenant_id, addon_type, balance, total_purchased) + VALUES (p_tenant_id, p_addon_type, 0, 0) + ON CONFLICT (tenant_id, addon_type) DO NOTHING; + + -- Lock e leitura + SELECT * INTO v_credit + FROM addon_credits + WHERE tenant_id = p_tenant_id AND addon_type = p_addon_type + FOR UPDATE; + + v_balance_before := v_credit.balance; + v_balance_after := v_credit.balance + p_amount; + + -- Atualiza saldo + UPDATE addon_credits + SET balance = v_balance_after, + total_purchased = total_purchased + p_amount, + low_balance_notified = CASE WHEN v_balance_after > COALESCE(low_balance_threshold, 10) THEN false ELSE low_balance_notified END, + updated_at = now() + WHERE id = v_credit.id; + + -- Registra transa????o + INSERT INTO addon_transactions ( + tenant_id, addon_type, type, amount, + balance_before, balance_after, + product_id, description, + admin_user_id, payment_method, price_cents + ) VALUES ( + p_tenant_id, p_addon_type, 'purchase', p_amount, + v_balance_before, v_balance_after, + p_product_id, p_description, + auth.uid(), p_payment_method, p_price_cents + ) + RETURNING id INTO v_tx_id; + + RETURN jsonb_build_object( + 'success', true, + 'transaction_id', v_tx_id, + 'balance_before', v_balance_before, + 'balance_after', v_balance_after + ); +END; +$$; + +CREATE FUNCTION public.admin_delete_email_template_global(p_id uuid) RETURNS boolean + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + DELETE FROM public.email_templates_global WHERE id = p_id; + IF NOT FOUND THEN + RAISE EXCEPTION 'Template com id % n??o encontrado', p_id; + END IF; + RETURN true; +END; +$$; + +CREATE FUNCTION public.admin_fix_plan_target(p_plan_key text, p_new_target text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_plan_id uuid; +begin + -- (opcional) restringe targets v??lidos + if p_new_target not in ('clinic','therapist') then + raise exception 'Target inv??lido: %', p_new_target using errcode='P0001'; + end if; + + -- trava o plano + select id into v_plan_id + from public.plans + where key = p_plan_key + for update; + + if v_plan_id is null then + raise exception 'Plano n??o encontrado: %', p_plan_key using errcode='P0001'; + end if; + + -- seguran??a: n??o mexer se existe subscription + if exists (select 1 from public.subscriptions s where s.plan_id = v_plan_id) then + raise exception 'Plano % possui subscriptions. Migra????o bloqueada.', p_plan_key using errcode='P0001'; + end if; + + -- liga bypass SOMENTE nesta transa????o + perform set_config('app.plan_migration_bypass', '1', true); + + update public.plans + set target = p_new_target + where id = v_plan_id; + +end +$$; + +CREATE FUNCTION public.admin_upsert_email_template_global(p_id uuid DEFAULT NULL::uuid, p_key text DEFAULT NULL::text, p_domain text DEFAULT NULL::text, p_channel text DEFAULT 'email'::text, p_subject text DEFAULT NULL::text, p_body_html text DEFAULT NULL::text, p_body_text text DEFAULT NULL::text, p_is_active boolean DEFAULT true, p_variables jsonb DEFAULT '{}'::jsonb) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_result jsonb; + v_id uuid; +BEGIN + -- UPDATE existente + IF p_id IS NOT NULL THEN + UPDATE public.email_templates_global + SET + subject = COALESCE(p_subject, subject), + body_html = COALESCE(p_body_html, body_html), + body_text = p_body_text, + is_active = p_is_active, + variables = COALESCE(p_variables, variables), + version = version + 1 + WHERE id = p_id + RETURNING to_jsonb(email_templates_global.*) INTO v_result; + + IF v_result IS NULL THEN + RAISE EXCEPTION 'Template com id % n??o encontrado', p_id; + END IF; + + RETURN v_result; + END IF; + + -- INSERT novo + IF p_key IS NULL OR p_domain IS NULL OR p_subject IS NULL OR p_body_html IS NULL THEN + RAISE EXCEPTION 'key, domain, subject e body_html s??o obrigat??rios para novo template'; + END IF; + + INSERT INTO public.email_templates_global (key, domain, channel, subject, body_html, body_text, is_active, variables) + VALUES (p_key, p_domain, p_channel, p_subject, p_body_html, p_body_text, p_is_active, p_variables) + RETURNING to_jsonb(email_templates_global.*) INTO v_result; + + RETURN v_result; +END; +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +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 + ); +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +CREATE FUNCTION public.cancel_subscription(p_subscription_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_owner_type text; + v_owner_ref uuid; +begin + + select * + into v_sub + from public.subscriptions + where id = p_subscription_id + for update; + + if not found then + raise exception 'Subscription n??o encontrada'; + end if; + + if v_sub.status = 'canceled' then + return v_sub; + end if; + + if v_sub.tenant_id is not null then + v_owner_type := 'clinic'; + v_owner_ref := v_sub.tenant_id; + elsif v_sub.user_id is not null then + v_owner_type := 'therapist'; + v_owner_ref := v_sub.user_id; + else + v_owner_type := null; + v_owner_ref := null; + end if; + + update public.subscriptions + set status = 'canceled', + cancel_at_period_end = false, + updated_at = now() + where id = p_subscription_id + returning * into v_sub; + + insert into public.subscription_events( + subscription_id, + owner_id, + owner_type, + owner_ref, + event_type, + old_plan_id, + new_plan_id, + created_by, + reason, + source, + metadata + ) + values ( + v_sub.id, + v_owner_ref, + v_owner_type, + v_owner_ref, + 'canceled', + v_sub.plan_id, + v_sub.plan_id, + auth.uid(), + 'Cancelamento manual via admin', + 'admin_panel', + jsonb_build_object('previous_status', 'active') + ); + + if v_owner_ref is not null then + insert into public.entitlements_invalidation(owner_id, changed_at) + values (v_owner_ref, now()) + on conflict (owner_id) + do update set changed_at = excluded.changed_at; + end if; + + return v_sub; + +end; +$$; + +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; +$$; + +CREATE FUNCTION public.change_subscription_plan(p_subscription_id uuid, p_new_plan_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_old_plan uuid; + v_new_key text; + + v_owner_type text; + v_owner_ref uuid; + + v_new_target text; + v_sub_target text; +begin + select * + into v_sub + from public.subscriptions + where id = p_subscription_id + for update; + + if not found then + raise exception 'Subscription n??o encontrada'; + end if; + + v_old_plan := v_sub.plan_id; + + if v_old_plan = p_new_plan_id then + return v_sub; + end if; + + select key, target + into v_new_key, v_new_target + from public.plans + where id = p_new_plan_id; + + if v_new_key is null then + raise exception 'Plano n??o encontrado'; + end if; + + v_new_target := lower(coalesce(v_new_target, '')); + + v_sub_target := case + when v_sub.tenant_id is not null then 'clinic' + else 'therapist' + end; + + if v_new_target <> v_sub_target then + raise exception 'Plano inv??lido para este tipo de assinatura. Assinatura ?? % e o plano ?? %.', + v_sub_target, v_new_target + using errcode = 'P0001'; + end if; + + if v_sub.tenant_id is not null then + v_owner_type := 'clinic'; + v_owner_ref := v_sub.tenant_id; + elsif v_sub.user_id is not null then + v_owner_type := 'therapist'; + v_owner_ref := v_sub.user_id; + else + v_owner_type := null; + v_owner_ref := null; + end if; + + update public.subscriptions + set plan_id = p_new_plan_id, + plan_key = v_new_key, + updated_at = now() + where id = p_subscription_id + returning * into v_sub; + + insert into public.subscription_events( + subscription_id, + owner_id, + owner_type, + owner_ref, + event_type, + old_plan_id, + new_plan_id, + created_by, + reason, + source, + metadata + ) + values ( + v_sub.id, + v_owner_ref, + v_owner_type, + v_owner_ref, + 'plan_changed', + v_old_plan, + p_new_plan_id, + auth.uid(), + 'Plan change via DEV menu', + 'dev_menu', + jsonb_build_object( + 'previous_plan', v_old_plan, + 'new_plan', p_new_plan_id, + 'new_plan_key', v_new_key, + 'new_plan_target', v_new_target + ) + ); + + if v_owner_ref is not null then + insert into public.entitlements_invalidation (owner_id, changed_at) + values (v_owner_ref, now()) + on conflict (owner_id) + do update set changed_at = excluded.changed_at; + end if; + + return v_sub; +end; +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +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; +$_$; + +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; +$$; + +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; +$$; + +CREATE FUNCTION public.current_member_id(p_tenant_id uuid) RETURNS uuid + LANGUAGE sql STABLE + AS $$ + select tm.id + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + limit 1 +$$; + +CREATE FUNCTION public.current_member_role(p_tenant_id uuid) RETURNS text + LANGUAGE sql STABLE + AS $$ + select tm.role + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + limit 1 +$$; + +CREATE FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid DEFAULT NULL::uuid, p_description text DEFAULT 'Consumo'::text) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_credit addon_credits%ROWTYPE; + v_balance_before INTEGER; + v_balance_after INTEGER; +BEGIN + -- Lock e leitura + SELECT * INTO v_credit + FROM addon_credits + WHERE tenant_id = p_tenant_id AND addon_type = p_addon_type AND is_active = true + FOR UPDATE; + + IF NOT FOUND THEN + RETURN jsonb_build_object('success', false, 'reason', 'no_credits', 'balance', 0); + END IF; + + -- Verifica saldo + IF v_credit.balance <= 0 THEN + RETURN jsonb_build_object('success', false, 'reason', 'insufficient_balance', 'balance', 0); + END IF; + + -- Verifica rate limit di??rio + IF v_credit.daily_limit IS NOT NULL THEN + -- Reset se passou do dia + IF v_credit.daily_reset_at IS NULL OR v_credit.daily_reset_at < date_trunc('day', now()) THEN + UPDATE addon_credits SET daily_used = 0, daily_reset_at = date_trunc('day', now()) + interval '1 day' WHERE id = v_credit.id; + v_credit.daily_used := 0; + END IF; + + IF v_credit.daily_used >= v_credit.daily_limit THEN + RETURN jsonb_build_object('success', false, 'reason', 'daily_limit_reached', 'balance', v_credit.balance); + END IF; + END IF; + + -- Verifica rate limit hor??rio + IF v_credit.hourly_limit IS NOT NULL THEN + IF v_credit.hourly_reset_at IS NULL OR v_credit.hourly_reset_at < date_trunc('hour', now()) THEN + UPDATE addon_credits SET hourly_used = 0, hourly_reset_at = date_trunc('hour', now()) + interval '1 hour' WHERE id = v_credit.id; + v_credit.hourly_used := 0; + END IF; + + IF v_credit.hourly_used >= v_credit.hourly_limit THEN + RETURN jsonb_build_object('success', false, 'reason', 'hourly_limit_reached', 'balance', v_credit.balance); + END IF; + END IF; + + -- Verifica expira????o + IF v_credit.expires_at IS NOT NULL AND v_credit.expires_at < now() THEN + RETURN jsonb_build_object('success', false, 'reason', 'credits_expired', 'balance', v_credit.balance); + END IF; + + v_balance_before := v_credit.balance; + v_balance_after := v_credit.balance - 1; + + -- Debita + UPDATE addon_credits + SET balance = v_balance_after, + total_consumed = total_consumed + 1, + daily_used = COALESCE(daily_used, 0) + 1, + hourly_used = COALESCE(hourly_used, 0) + 1, + updated_at = now() + WHERE id = v_credit.id; + + -- Registra transa????o + INSERT INTO addon_transactions ( + tenant_id, addon_type, type, amount, + balance_before, balance_after, + queue_id, description + ) VALUES ( + p_tenant_id, p_addon_type, 'consume', -1, + v_balance_before, v_balance_after, + p_queue_id, p_description + ); + + RETURN jsonb_build_object( + 'success', true, + 'balance_before', v_balance_before, + 'balance_after', v_balance_after + ); +END; +$$; + +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; +$$; + +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; +$$; + +CREATE FUNCTION public.dev_list_auth_users(p_limit integer DEFAULT 50) RETURNS TABLE(id uuid, email text, created_at timestamp with time zone) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +begin + -- s?? saas_admin pode ver + if not exists ( + select 1 + from public.profiles p + where p.id = auth.uid() + and p.role = 'saas_admin' + ) then + return; + end if; + + return query + select + u.id, + u.email, + u.created_at + from auth.users u + order by u.created_at desc + limit greatest(1, least(coalesce(p_limit, 50), 500)); +end; +$$; + +CREATE FUNCTION public.dev_list_custom_users() RETURNS TABLE(user_id uuid, email text, created_at timestamp with time zone, global_role text, tenant_role text, tenant_id uuid, password_dev text, kind text) + LANGUAGE sql SECURITY DEFINER + SET search_path TO 'public' + AS $$ + with base as ( + select + u.id as user_id, + lower(u.email) as email, + u.created_at + from auth.users u + where lower(u.email) not in ( + 'clinic@agenciapsi.com.br', + 'therapist@agenciapsi.com.br', + 'patient@agenciapsi.com.br', + 'saas@agenciapsi.com.br' + ) + ), + prof as ( + select p.id, p.role as global_role + from public.profiles p + ), + last_membership as ( + select distinct on (tm.user_id) + tm.user_id, + tm.tenant_id, + tm.role as tenant_role, + tm.created_at + from public.tenant_members tm + where tm.status = 'active' + order by tm.user_id, tm.created_at desc + ) + select + b.user_id, + b.email, + b.created_at, + pr.global_role, + lm.tenant_role, + lm.tenant_id, + dc.password_dev, + dc.kind + from base b + left join prof pr on pr.id = b.user_id + left join last_membership lm on lm.user_id = b.user_id + left join public.dev_user_credentials dc on lower(dc.email) = b.email + order by b.created_at desc; +$$; + +CREATE FUNCTION public.dev_list_intent_leads() RETURNS TABLE(email text, last_intent_at timestamp with time zone, plan_key text, billing_interval text, status text, tenant_id uuid) + LANGUAGE sql SECURITY DEFINER + SET search_path TO 'public' + AS $$ + select + lower(si.email) as email, + max(si.created_at) as last_intent_at, + (array_agg(si.plan_key order by si.created_at desc))[1] as plan_key, + (array_agg(si.interval order by si.created_at desc))[1] as billing_interval, + (array_agg(si.status order by si.created_at desc))[1] as status, + (array_agg(si.tenant_id order by si.created_at desc))[1] as tenant_id + from public.subscription_intents si + where si.email is not null + and not exists ( + select 1 + from auth.users au + where lower(au.email) = lower(si.email) + ) + group by lower(si.email) + order by max(si.created_at) desc; +$$; + +CREATE FUNCTION public.dev_public_debug_snapshot() RETURNS TABLE(users_total integer, tenants_total integer, intents_new_total integer, latest_intents jsonb) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $_$ +declare + v_latest jsonb; +begin + select jsonb_agg( + jsonb_build_object( + 'created_at', si.created_at, + 'email_masked', + regexp_replace(lower(si.email), '(^.).*(@.*$)', '\1***\2'), + 'plan_key', si.plan_key, + 'status', si.status + ) + order by si.created_at desc + ) + into v_latest + from ( + select si.* + from public.subscription_intents si + where si.email is not null + order by si.created_at desc + limit 5 + ) si; + + return query + select + (select count(*)::int from auth.users) as users_total, + (select count(*)::int from public.tenants) as tenants_total, + (select count(*)::int from public.subscription_intents where status = 'new') as intents_new_total, + coalesce(v_latest, '[]'::jsonb) as latest_intents; +end; +$_$; + +CREATE FUNCTION public.ensure_personal_tenant() RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_uid uuid; + v_existing uuid; +BEGIN + v_uid := auth.uid(); + IF v_uid IS NULL THEN + RAISE EXCEPTION 'Not authenticated'; + END IF; + + SELECT tm.tenant_id INTO v_existing + FROM public.tenant_members tm + JOIN public.tenants t ON t.id = tm.tenant_id + WHERE tm.user_id = v_uid + AND tm.status = 'active' + AND t.kind IN ('therapist', 'saas') + ORDER BY tm.created_at DESC + LIMIT 1; + + IF v_existing IS NOT NULL THEN + RETURN v_existing; + END IF; + + RETURN public.provision_account_tenant(v_uid, 'therapist'); +END; +$$; + +CREATE FUNCTION public.ensure_personal_tenant_for_user(p_user_id uuid) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_uid uuid; + v_existing uuid; + v_tenant uuid; + v_email text; + v_name text; +begin + v_uid := p_user_id; + if v_uid is null then + raise exception 'Missing user id'; + end if; + + -- s?? considera tenant pessoal (kind='saas') + select tm.tenant_id + into v_existing + from public.tenant_members tm + join public.tenants t on t.id = tm.tenant_id + where tm.user_id = v_uid + and tm.status = 'active' + and t.kind = 'saas' + order by tm.created_at desc + limit 1; + + if v_existing is not null then + return v_existing; + end if; + + select email into v_email + from auth.users + where id = v_uid; + + v_name := coalesce(split_part(v_email, '@', 1), 'Conta'); + + insert into public.tenants (name, kind, created_at) + values (v_name || ' (Pessoal)', 'saas', 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; +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +CREATE FUNCTION public.fn_document_signature_timeline() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_patient_id uuid; + v_tenant_id uuid; + v_doc_nome text; +BEGIN + IF NEW.status = 'assinado' AND (OLD.status IS NULL OR OLD.status <> 'assinado') THEN + SELECT d.patient_id, d.tenant_id, d.nome_original + INTO v_patient_id, v_tenant_id, v_doc_nome + FROM public.documents d + WHERE d.id = NEW.documento_id; + + IF v_patient_id IS NOT NULL THEN + INSERT INTO public.patient_timeline ( + patient_id, tenant_id, evento_tipo, + titulo, descricao, icone_cor, + link_ref_tipo, link_ref_id, + gerado_por, ocorrido_em + ) VALUES ( + v_patient_id, + v_tenant_id, + 'documento_assinado', + 'Documento assinado: ' || COALESCE(v_doc_nome, 'documento'), + 'Assinado por ' || COALESCE(NEW.signatario_nome, NEW.signatario_tipo), + 'green', + 'documento', + NEW.documento_id, + NEW.signatario_id, + NEW.assinado_em + ); + END IF; + END IF; + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.fn_documents_timeline_insert() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + INSERT INTO public.patient_timeline ( + patient_id, tenant_id, evento_tipo, + titulo, descricao, icone_cor, + link_ref_tipo, link_ref_id, + gerado_por, ocorrido_em + ) VALUES ( + NEW.patient_id, + NEW.tenant_id, + 'documento_adicionado', + 'Documento adicionado: ' || COALESCE(NEW.nome_original, 'arquivo'), + 'Tipo: ' || COALESCE(NEW.tipo_documento, 'outro'), + 'blue', + 'documento', + NEW.id, + NEW.uploaded_by, + NEW.uploaded_at + ); + RETURN NEW; +END; +$$; + +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; + +$$; + +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; +$$; + +CREATE FUNCTION public.get_my_email() RETURNS text + LANGUAGE sql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ + select lower(email) + from auth.users + where id = auth.uid(); +$$; + +CREATE FUNCTION public.guard_account_type_immutable() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF OLD.account_type <> 'free' AND NEW.account_type IS DISTINCT FROM OLD.account_type THEN + RAISE EXCEPTION 'account_type ?? imut??vel ap??s escolha (atual: "%" para tentativa: "%"). Para mudar de perfil, crie uma nova conta.', OLD.account_type, NEW.account_type + USING ERRCODE = 'P0001'; + END IF; + RETURN NEW; +END; +$$; + +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; +$$; + +CREATE FUNCTION public.guard_no_change_core_plan_key() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if old.key in ('clinic_free','clinic_pro','therapist_free','therapist_pro') + and new.key is distinct from old.key then + raise exception 'N??o ?? permitido alterar a key do plano padr??o (%).', old.key + using errcode = 'P0001'; + end if; + + return new; +end $$; + +CREATE FUNCTION public.guard_no_change_plan_target() RETURNS trigger + LANGUAGE plpgsql + AS $$ +declare + v_bypass text; +begin + -- bypass controlado por sess??o/transa????o: + -- s?? passa se app.plan_migration_bypass = '1' + v_bypass := current_setting('app.plan_migration_bypass', true); + + if v_bypass = '1' then + return new; + end if; + + -- comportamento original (bloqueia qualquer mudan??a) + if new.target is distinct from old.target then + raise exception 'N??o ?? permitido alterar target do plano (%) de % para %.', + old.key, old.target, new.target + using errcode = 'P0001'; + end if; + + return new; +end +$$; + +CREATE FUNCTION public.guard_no_delete_core_plans() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if old.key in ('clinic_free','clinic_pro','therapist_free','therapist_pro') then + raise exception 'Plano padr??o (%) n??o pode ser removido.', old.key + using errcode = 'P0001'; + end if; + + return old; +end $$; + +CREATE FUNCTION public.guard_patient_cannot_own_tenant() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE + v_account_type text; +BEGIN + SELECT account_type INTO v_account_type + FROM public.profiles + WHERE id = NEW.user_id; + + IF v_account_type = 'patient' AND NEW.role IN ('tenant_admin', 'therapist') THEN + RAISE EXCEPTION 'Usu??rio com perfil "patient" n??o pode ser propriet??rio ou terapeuta de um tenant. Se tornou profissional? Crie uma nova conta.' + USING ERRCODE = 'P0001'; + END IF; + + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.guard_tenant_kind_immutable() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF NEW.kind IS DISTINCT FROM OLD.kind THEN + RAISE EXCEPTION 'tenants.kind ?? imut??vel ap??s cria????o. Tentativa de alterar "%" para "%".', OLD.kind, NEW.kind + USING ERRCODE = 'P0001'; + END IF; + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.handle_new_user() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + INSERT INTO public.profiles (id, role, account_type) + VALUES (NEW.id, 'portal_user', 'free') + ON CONFLICT (id) DO NOTHING; + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.handle_new_user_create_personal_tenant() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + -- Desabilitado. Tenant criado no onboarding via provision_account_tenant(). + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.has_feature(p_owner_id uuid, p_feature_key text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.owner_feature_entitlements e + where e.owner_id = p_owner_id + and e.feature_key = p_feature_key + ); +$$; + +CREATE FUNCTION public.is_clinic_tenant(_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + SELECT EXISTS ( + SELECT 1 FROM public.tenants t + WHERE t.id = _tenant_id + AND t.kind IN ('clinic', 'clinic_coworking', 'clinic_reception', 'clinic_full') + ); +$$; + +CREATE FUNCTION public.is_saas_admin() RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 from public.saas_admins sa + where sa.user_id = auth.uid() + ); +$$; + +CREATE FUNCTION public.is_tenant_admin(p_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ + select exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + and tm.role = 'tenant_admin' + and tm.status = 'active' + ); +$$; + +CREATE FUNCTION public.is_tenant_member(_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.tenant_members m + where m.tenant_id = _tenant_id + and m.user_id = auth.uid() + and m.status = 'active' + ); +$$; + +CREATE FUNCTION public.is_therapist_tenant(_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + SELECT EXISTS ( + SELECT 1 FROM public.tenants t + WHERE t.id = _tenant_id AND t.kind = 'therapist' + ); +$$; + +CREATE FUNCTION public.jwt_email() RETURNS text + LANGUAGE sql STABLE + AS $$ + select nullif(lower(current_setting('request.jwt.claim.email', true)), ''); +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +CREATE FUNCTION public.my_tenants() RETURNS TABLE(tenant_id uuid, role text, status text, kind text) + LANGUAGE sql STABLE + AS $$ + select + tm.tenant_id, + tm.role, + tm.status, + t.kind + from public.tenant_members tm + join public.tenants t on t.id = tm.tenant_id + where tm.user_id = auth.uid(); +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +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; $$; + +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; +$$; + +CREATE FUNCTION public.on_new_user_seed_patient_groups() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ + BEGIN + PERFORM public.seed_default_patient_groups(NEW.id); + RETURN NEW; + END; + $$; + +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; +$$; + +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; +$$; + +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; +$$; + +CREATE FUNCTION public.prevent_promoting_to_system() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if new.is_system = true and old.is_system is distinct from true then + raise exception 'N??o ?? permitido transformar um grupo comum em grupo do sistema.'; + end if; + return new; +end; +$$; + +CREATE FUNCTION public.prevent_saas_membership() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM public.profiles + WHERE id = NEW.user_id + AND role = 'saas_admin' + ) THEN + RAISE EXCEPTION 'SaaS admin cannot belong to tenant'; + END IF; + + RETURN NEW; +END; +$$; + +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; +$$; + +CREATE FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text DEFAULT NULL::text) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_tenant_id uuid; + v_account_type text; + v_name text; +BEGIN + IF p_kind NOT IN ('therapist', 'clinic_coworking', 'clinic_reception', 'clinic_full') THEN + RAISE EXCEPTION 'kind inv??lido: "%". Use: therapist, clinic_coworking, clinic_reception, clinic_full.', p_kind + USING ERRCODE = 'P0001'; + END IF; + + v_account_type := CASE WHEN p_kind = 'therapist' THEN 'therapist' ELSE 'clinic' END; + + IF EXISTS ( + SELECT 1 + FROM public.tenant_members tm + JOIN public.tenants t ON t.id = tm.tenant_id + WHERE tm.user_id = p_user_id + AND tm.role = 'tenant_admin' + AND tm.status = 'active' + AND t.kind = p_kind + ) THEN + RAISE EXCEPTION 'Usu??rio j?? possui um tenant do tipo "%".', p_kind + USING ERRCODE = 'P0001'; + END IF; + + v_name := COALESCE( + NULLIF(TRIM(p_name), ''), + ( + SELECT COALESCE(NULLIF(TRIM(pr.full_name), ''), SPLIT_PART(au.email, '@', 1)) + FROM public.profiles pr + JOIN auth.users au ON au.id = pr.id + WHERE pr.id = p_user_id + ), + 'Conta' + ); + + INSERT INTO public.tenants (name, kind, created_at) + VALUES (v_name, p_kind, now()) + RETURNING id INTO v_tenant_id; + + INSERT INTO public.tenant_members (tenant_id, user_id, role, status, created_at) + VALUES (v_tenant_id, p_user_id, 'tenant_admin', 'active', now()); + + UPDATE public.profiles + SET account_type = v_account_type + WHERE id = p_user_id; + + PERFORM public.seed_determined_commitments(v_tenant_id); + + RETURN v_tenant_id; +END; +$$; + +CREATE FUNCTION public.reactivate_subscription(p_subscription_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_owner_type text; + v_owner_ref uuid; +begin + + select * + into v_sub + from public.subscriptions + where id = p_subscription_id + for update; + + if not found then + raise exception 'Subscription n??o encontrada'; + end if; + + if v_sub.status = 'active' then + return v_sub; + end if; + + if v_sub.tenant_id is not null then + v_owner_type := 'clinic'; + v_owner_ref := v_sub.tenant_id; + elsif v_sub.user_id is not null then + v_owner_type := 'therapist'; + v_owner_ref := v_sub.user_id; + else + v_owner_type := null; + v_owner_ref := null; + end if; + + update public.subscriptions + set status = 'active', + cancel_at_period_end = false, + updated_at = now() + where id = p_subscription_id + returning * into v_sub; + + insert into public.subscription_events( + subscription_id, + owner_id, + owner_type, + owner_ref, + event_type, + old_plan_id, + new_plan_id, + created_by, + reason, + source, + metadata + ) + values ( + v_sub.id, + v_owner_ref, + v_owner_type, + v_owner_ref, + 'reactivated', + v_sub.plan_id, + v_sub.plan_id, + auth.uid(), + 'Reativa????o manual via admin', + 'admin_panel', + jsonb_build_object('previous_status', 'canceled') + ); + + if v_owner_ref is not null then + insert into public.entitlements_invalidation(owner_id, changed_at) + values (v_owner_ref, now()) + on conflict (owner_id) + do update set changed_at = excluded.changed_at; + end if; + + return v_sub; + +end; +$$; + +CREATE FUNCTION public.rebuild_owner_entitlements(p_owner_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_plan_id uuid; +begin + -- Plano ativo do owner (owner = subscriptions.user_id) + select s.plan_id + into v_plan_id + from public.subscriptions s + where s.user_id = p_owner_id + and s.status = 'active' + order by s.created_at desc + limit 1; + + -- Sempre zera entitlements do owner (rebuild) + delete from public.owner_feature_entitlements e + where e.owner_id = p_owner_id; + + -- Se n??o tem assinatura ativa, acabou + if v_plan_id is null then + return; + end if; + + -- Recria entitlements esperados pelo plano + insert into public.owner_feature_entitlements (owner_id, feature_key, sources, limits_list) + select + p_owner_id as owner_id, + f.key as feature_key, + array['plan'::text] as sources, + '{}'::jsonb as limits_list + from public.plan_features pf + join public.features f on f.id = pf.feature_id + where pf.plan_id = v_plan_id; + +end; +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +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; +$$; + +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; $$; + +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; +$$; + +CREATE FUNCTION public.seed_default_patient_groups(p_tenant_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_owner_id uuid; +BEGIN + -- busca o owner (tenant_admin) do tenant + SELECT user_id INTO v_owner_id + FROM public.tenant_members + WHERE tenant_id = p_tenant_id + AND role = 'tenant_admin' + AND status = 'active' + LIMIT 1; + + IF v_owner_id IS NULL THEN + RETURN; + END IF; + + INSERT INTO public.patient_groups (owner_id, nome, cor, is_system, tenant_id) + VALUES + (v_owner_id, 'Crian??as', '#60a5fa', true, p_tenant_id), + (v_owner_id, 'Adolescentes', '#a78bfa', true, p_tenant_id), + (v_owner_id, 'Idosos', '#34d399', true, p_tenant_id) + ON CONFLICT (owner_id, nome) DO NOTHING; +END; +$$; + +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; +$$; + +CREATE FUNCTION public.set_insurance_plans_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN NEW.updated_at = now(); RETURN NEW; END; $$; + +CREATE FUNCTION public.set_medicos_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + +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; +$$; + +CREATE FUNCTION public.set_services_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + +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 + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +begin + -- ??? S?? owner ou admin do tenant podem alterar features + if not exists ( + select 1 from public.tenant_members + where tenant_id = p_tenant_id + and user_id = auth.uid() + and role in ('owner', 'admin') + and status = 'active' + ) then + raise exception 'Acesso negado: apenas owner/admin pode alterar features do tenant.'; + end if; + + insert into public.tenant_features (tenant_id, feature_key, enabled) + values (p_tenant_id, p_feature_key, p_enabled) + on conflict (tenant_id, feature_key) + do update set enabled = excluded.enabled; + + insert into public.tenant_feature_exceptions_log ( + tenant_id, feature_key, enabled, reason, created_by + ) values ( + p_tenant_id, p_feature_key, p_enabled, p_reason, auth.uid() + ); +end; +$$; + +CREATE FUNCTION public.set_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.set_updated_at_recurrence() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN NEW.updated_at = now(); RETURN NEW; END; +$$; + +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; +$$; + +CREATE FUNCTION public.subscription_intents_view_insert() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_target text; + v_plan_id uuid; +begin + select p.id, p.target into v_plan_id, v_target + from public.plans p + where p.key = new.plan_key; + + if v_plan_id is null then + raise exception 'Plano inv??lido: plan_key=%', new.plan_key; + end if; + + if lower(v_target) = 'clinic' then + if new.tenant_id is null then + raise exception 'Inten????o clinic exige tenant_id.'; + end if; + + insert into public.subscription_intents_tenant ( + id, tenant_id, created_by_user_id, email, + plan_id, plan_key, interval, amount_cents, currency, + status, source, notes, created_at, paid_at + ) values ( + coalesce(new.id, gen_random_uuid()), + new.tenant_id, new.created_by_user_id, new.email, + v_plan_id, new.plan_key, coalesce(new.interval,'month'), + new.amount_cents, coalesce(new.currency,'BRL'), + coalesce(new.status,'pending'), coalesce(new.source,'manual'), + new.notes, coalesce(new.created_at, now()), new.paid_at + ); + + new.plan_target := 'clinic'; + return new; + end if; + + -- therapist ou supervisor ??? tabela personal + if lower(v_target) in ('therapist', 'supervisor') then + insert into public.subscription_intents_personal ( + id, user_id, created_by_user_id, email, + plan_id, plan_key, interval, amount_cents, currency, + status, source, notes, created_at, paid_at + ) values ( + coalesce(new.id, gen_random_uuid()), + new.user_id, new.created_by_user_id, new.email, + v_plan_id, new.plan_key, coalesce(new.interval,'month'), + new.amount_cents, coalesce(new.currency,'BRL'), + coalesce(new.status,'pending'), coalesce(new.source,'manual'), + new.notes, coalesce(new.created_at, now()), new.paid_at + ); + + new.plan_target := lower(v_target); -- 'therapist' ou 'supervisor' + return new; + end if; + + raise exception 'Target de plano n??o suportado: %', v_target; +end; +$$; + +CREATE FUNCTION public.subscriptions_validate_scope() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE + v_target text; +BEGIN + SELECT lower(p.target) INTO v_target + FROM public.plans p + WHERE p.id = NEW.plan_id; + + IF v_target IS NULL THEN + RAISE EXCEPTION 'Plano inv??lido (target nulo).'; + END IF; + + IF v_target = 'clinic' THEN + IF NEW.tenant_id IS NULL THEN + RAISE EXCEPTION 'Assinatura clinic exige tenant_id.'; + END IF; + IF NEW.user_id IS NOT NULL THEN + RAISE EXCEPTION 'Assinatura clinic n??o pode ter user_id (XOR).'; + END IF; + + ELSIF v_target IN ('therapist', 'supervisor') THEN + -- supervisor ?? pessoal como therapist + IF NEW.tenant_id IS NOT NULL THEN + RAISE EXCEPTION 'Assinatura % n??o deve ter tenant_id.', v_target; + END IF; + IF NEW.user_id IS NULL THEN + RAISE EXCEPTION 'Assinatura % exige user_id.', v_target; + END IF; + + ELSIF v_target = 'patient' THEN + IF NEW.tenant_id IS NOT NULL THEN + RAISE EXCEPTION 'Assinatura patient n??o deve ter tenant_id.'; + END IF; + IF NEW.user_id IS NULL THEN + RAISE EXCEPTION 'Assinatura patient exige user_id.'; + END IF; + + ELSE + RAISE EXCEPTION 'Target de plano inv??lido: %', v_target; + END IF; + + RETURN NEW; +END; +$$; + +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; +$$; + +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; +$$; + +CREATE FUNCTION public.tenant_accept_invite(p_token uuid) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +declare + v_uid uuid; + v_email text; + v_invite public.tenant_invites%rowtype; +begin + -- 1) precisa estar autenticado + v_uid := auth.uid(); + if v_uid is null then + raise exception 'not_authenticated' using errcode = 'P0001'; + end if; + + -- 2) pega email real do usu??rio logado sem depender do JWT claim + select u.email + into v_email + from auth.users u + where u.id = v_uid; + + if v_email is null or length(trim(v_email)) = 0 then + raise exception 'missing_user_email' using errcode = 'P0001'; + end if; + + -- 3) carrega o invite e trava linha (evita 2 aceites concorrentes) + select * + into v_invite + from public.tenant_invites i + where i.token = p_token + for update; + + if not found then + raise exception 'invite_not_found' using errcode = 'P0001'; + end if; + + -- 4) valida????es de estado + if v_invite.revoked_at is not null then + raise exception 'invite_revoked' using errcode = 'P0001'; + end if; + + if v_invite.accepted_at is not null then + raise exception 'invite_already_accepted' using errcode = 'P0001'; + end if; + + if v_invite.expires_at is not null and v_invite.expires_at <= now() then + raise exception 'invite_expired' using errcode = 'P0001'; + end if; + + -- 5) valida email (case-insensitive) + if lower(trim(v_invite.email)) <> lower(trim(v_email)) then + raise exception 'email_mismatch' using errcode = 'P0001'; + end if; + + -- 6) consome o invite + update public.tenant_invites + set accepted_at = now(), + accepted_by = v_uid + where id = v_invite.id; + + -- 7) cria ou reativa o membership + insert into public.tenant_members (tenant_id, user_id, role, status, created_at) + values (v_invite.tenant_id, v_uid, v_invite.role, 'active', now()) + on conflict (tenant_id, user_id) + do update set + role = excluded.role, + status = 'active'; + + -- 8) retorno ??til pro front (voc?? j?? tenta ler tenant_id no AcceptInvitePage) + return jsonb_build_object( + 'ok', true, + 'tenant_id', v_invite.tenant_id, + 'role', v_invite.role + ); +end; +$$; + +CREATE FUNCTION public.tenant_add_member_by_email(p_tenant_id uuid, p_email text, p_role text DEFAULT 'therapist'::text) RETURNS public.tenant_members + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +declare + v_target_uid uuid; + v_member public.tenant_members%rowtype; + v_is_admin boolean; + v_email text; +begin + if p_tenant_id is null then + raise exception 'tenant_id ?? obrigat??rio'; + end if; + + v_email := lower(trim(coalesce(p_email, ''))); + if v_email = '' then + raise exception 'email ?? obrigat??rio'; + end if; + + -- valida role permitida + if p_role not in ('tenant_admin','therapist','secretary','patient') then + raise exception 'role inv??lida: %', p_role; + end if; + + -- apenas admin do tenant (role real no banco) + select exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + and tm.role = 'tenant_admin' + and coalesce(tm.status,'active') = 'active' + ) into v_is_admin; + + if not v_is_admin then + raise exception 'sem permiss??o: apenas admin da cl??nica pode adicionar membros'; + end if; + + -- acha usu??rio pelo e-mail no Supabase Auth + select u.id + into v_target_uid + from auth.users u + where lower(u.email) = v_email + limit 1; + + if v_target_uid is null then + raise exception 'nenhum usu??rio encontrado com este e-mail'; + end if; + + -- cria ou reativa membro + insert into public.tenant_members (tenant_id, user_id, role, status) + values (p_tenant_id, v_target_uid, p_role, 'active') + on conflict (tenant_id, user_id) + do update set + role = excluded.role, + status = 'active' + returning * into v_member; + + return v_member; +end; +$$; + +CREATE FUNCTION public.tenant_feature_allowed(p_tenant_id uuid, p_feature_key text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.v_tenant_entitlements v + where v.tenant_id = p_tenant_id + and v.feature_key = p_feature_key + and coalesce(v.allowed, false) = true + ); +$$; + +CREATE FUNCTION public.tenant_feature_enabled(p_tenant_id uuid, p_feature_key text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select coalesce( + (select tf.enabled + from public.tenant_features tf + where tf.tenant_id = p_tenant_id and tf.feature_key = p_feature_key), + false + ); +$$; + +CREATE FUNCTION public.tenant_features_guard_with_plan() RETURNS trigger + LANGUAGE plpgsql + AS $$ +declare + v_allowed boolean; +begin + -- s?? valida quando est?? habilitando + if new.enabled is distinct from true then + return new; + end if; + + -- permitido pelo plano do tenant? + select exists ( + select 1 + from public.v_tenant_entitlements_full v + where v.tenant_id = new.tenant_id + and v.feature_key = new.feature_key + and v.allowed = true + ) + into v_allowed; + + if not v_allowed then + raise exception 'Feature % n??o permitida pelo plano atual do tenant %.', + new.feature_key, new.tenant_id + using errcode = 'P0001'; + end if; + + return new; +end; +$$; + +CREATE FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select + exists ( + select 1 + from public.v_tenant_entitlements e + where e.tenant_id = _tenant_id + and e.feature_key = _feature + and e.allowed = true + ) + or exists ( + select 1 + from public.tenant_features tf + where tf.tenant_id = _tenant_id + and tf.feature_key = _feature + and tf.enabled = true + ); +$$; + +CREATE FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +declare + v_email text; + v_my_email text; + v_token uuid; + v_updated int; +begin + -- valida????es b??sicas + if p_tenant_id is null then + raise exception 'tenant_id inv??lido' using errcode = 'P0001'; + end if; + + v_email := lower(trim(coalesce(p_email, ''))); + if v_email = '' then + raise exception 'Informe um email' using errcode = 'P0001'; + end if; + + -- role permitido (ajuste se quiser) + if p_role is null or p_role not in ('therapist', 'secretary') then + raise exception 'Role inv??lido (use therapist/secretary)' using errcode = 'P0001'; + end if; + + -- ??? bloqueio: auto-convite + v_my_email := public.get_my_email(); + if v_my_email is not null and v_email = v_my_email then + raise exception 'Voc?? n??o pode convidar o seu pr??prio email.' using errcode = 'P0001'; + end if; + + -- ??? bloqueio: j?? ?? membro ativo do tenant + if exists ( + select 1 + from tenant_members tm + join auth.users au on au.id = tm.user_id + where tm.tenant_id = p_tenant_id + and tm.status = 'active' + and lower(au.email) = v_email + ) then + raise exception 'Este email j?? est?? vinculado a esta cl??nica.' using errcode = 'P0001'; + end if; + + -- ??? permiss??o: s?? admin do tenant pode convidar + if not exists ( + select 1 + from tenant_members me + where me.tenant_id = p_tenant_id + and me.user_id = auth.uid() + and me.status = 'active' + and me.role in ('tenant_admin','clinic_admin') + ) then + raise exception 'Sem permiss??o para convidar membros.' using errcode = 'P0001'; + end if; + + -- Gera token (reenvio simples / regenera????o) + v_token := gen_random_uuid(); + + -- 1) tenta "regerar" um convite pendente existente (mesmo email) + update tenant_invites + set token = v_token, + role = p_role, + created_at = now(), + expires_at = now() + interval '7 days', + accepted_at = null, + revoked_at = null + where tenant_id = p_tenant_id + and lower(email) = v_email + and accepted_at is null + and revoked_at is null; + + get diagnostics v_updated = row_count; + + -- 2) se n??o atualizou nada, cria convite novo + if v_updated = 0 then + insert into tenant_invites (tenant_id, email, role, token, created_at, expires_at) + values (p_tenant_id, v_email, p_role, v_token, now(), now() + interval '7 days'); + end if; + + return v_token; +end; +$$; + +CREATE FUNCTION public.tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + update public.tenant_members + set status = 'active' + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + +CREATE FUNCTION public.tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +declare + v_role text; +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + if p_member_user_id = auth.uid() then + raise exception 'cannot_remove_self'; + end if; + + -- pega role atual do membro (se n??o existir, erro) + select role into v_role + from public.tenant_members + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if v_role is null then + raise exception 'membership_not_found'; + end if; + + -- trava: se for therapist, n??o pode remover com eventos futuros + if v_role = 'therapist' then + if exists ( + select 1 + from public.agenda_eventos e + where e.owner_id = p_tenant_id + and e.terapeuta_id = p_member_user_id + and e.inicio_em >= now() + and e.status::text not in ('cancelado','cancelled','canceled') + limit 1 + ) then + raise exception 'cannot_remove_therapist_with_future_events'; + end if; + end if; + + update public.tenant_members + set status = 'inactive' + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + +CREATE FUNCTION public.tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + if p_member_user_id = auth.uid() then + raise exception 'cannot_remove_self'; + end if; + + update public.tenant_members + set status = 'inactive' + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + +CREATE FUNCTION public.tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +declare + v_email text; +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + v_email := lower(trim(p_email)); + + update public.tenant_invites + set revoked_at = now(), + revoked_by = auth.uid() + where tenant_id = p_tenant_id + and lower(email) = v_email + and role = p_role + and accepted_at is null + and revoked_at is null; + + if not found then + raise exception 'invite_not_found'; + end if; +end; +$$; + +CREATE FUNCTION public.tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + -- valida status (adapte aos seus valores reais) + if p_new_status not in ('active','inactive','suspended','invited') then + raise exception 'invalid_status: %', p_new_status; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + -- evita desativar a si mesmo (opcional) + if p_member_user_id = auth.uid() and p_new_status <> 'active' then + raise exception 'cannot_disable_self'; + end if; + + update public.tenant_members + set status = p_new_status + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + +CREATE FUNCTION public.tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + -- exige auth + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + -- valida role + if p_new_role not in ('tenant_admin','therapist','secretary','patient') then + raise exception 'invalid_role: %', p_new_role; + end if; + + -- somente tenant_admin ativo pode alterar role + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + -- evita o admin remover o pr??prio admin sem querer (opcional mas recomendado) + if p_member_user_id = auth.uid() and p_new_role <> 'tenant_admin' then + raise exception 'cannot_demote_self'; + end if; + + update public.tenant_members + set role = p_new_role + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + +CREATE FUNCTION public.toggle_plan(owner uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + current_key text; + new_key text; +begin + select p.key into current_key + from subscriptions s + join plans p on p.id = s.plan_id + where s.owner_id = owner + and s.status = 'active'; + + new_key := case + when current_key = 'pro' then 'free' + else 'pro' + end; + + update subscriptions s + set plan_id = p.id + from plans p + where p.key = new_key + and s.owner_id = owner + and s.status = 'active'; +end; +$$; + +CREATE FUNCTION public.transition_subscription(p_subscription_id uuid, p_to_status text, p_reason text DEFAULT NULL::text, p_metadata jsonb DEFAULT NULL::jsonb) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_uid uuid; + v_is_allowed boolean := false; +begin + v_uid := auth.uid(); + + select * + into v_sub + from public.subscriptions + where id = p_subscription_id; + + if not found then + raise exception 'Assinatura n??o encontrada'; + end if; + + -- ===================================================== + -- ???? BLOCO DE AUTORIZA????O + -- ===================================================== + + -- 1) SaaS admin pode tudo + if is_saas_admin() then + v_is_allowed := true; + end if; + + -- 2) Assinatura pessoal (therapist) + if not v_is_allowed + and v_sub.tenant_id is null + and v_sub.user_id = v_uid then + v_is_allowed := true; + end if; + + -- 3) Assinatura de clinic (tenant) + if not v_is_allowed + and v_sub.tenant_id is not null then + + if exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = v_sub.tenant_id + and tm.user_id = v_uid + and tm.status = 'active' + and tm.role = 'tenant_admin' + ) then + v_is_allowed := true; + end if; + + end if; + + if not v_is_allowed then + raise exception 'Sem permiss??o para transicionar esta assinatura'; + end if; + + -- ===================================================== + -- ???? TRANSI????O + -- ===================================================== + + update public.subscriptions + set status = p_to_status, + updated_at = now(), + cancelled_at = case when p_to_status = 'cancelled' then now() else cancelled_at end, + suspended_at = case when p_to_status = 'suspended' then now() else suspended_at end, + past_due_since = case when p_to_status = 'past_due' then now() else past_due_since end, + expired_at = case when p_to_status = 'expired' then now() else expired_at end, + activated_at = case when p_to_status = 'active' then now() else activated_at end + where id = p_subscription_id + returning * into v_sub; + + -- ===================================================== + -- ???? EVENT LOG + -- ===================================================== + + insert into public.subscription_events ( + subscription_id, + owner_id, + event_type, + created_at, + created_by, + source, + reason, + metadata, + owner_type, + owner_ref + ) + values ( + v_sub.id, + coalesce(v_sub.tenant_id, v_sub.user_id), + 'status_changed', + now(), + v_uid, + 'manual_transition', + p_reason, + p_metadata, + case when v_sub.tenant_id is not null then 'tenant' else 'personal' end, + coalesce(v_sub.tenant_id, v_sub.user_id) + ); + + return v_sub; +end; +$$; + +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; +$$; + +CREATE FUNCTION public.trg_fn_patient_risco_timeline() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF OLD.risco_elevado IS DISTINCT FROM NEW.risco_elevado THEN + INSERT INTO public.patient_timeline ( + patient_id, tenant_id, + evento_tipo, titulo, descricao, icone_cor, + gerado_por, ocorrido_em + ) VALUES ( + NEW.id, NEW.tenant_id, + CASE WHEN NEW.risco_elevado THEN 'risco_sinalizado' ELSE 'risco_removido' END, + CASE WHEN NEW.risco_elevado THEN 'Risco elevado sinalizado' ELSE 'Sinalização de risco removida' END, + NEW.risco_nota, + CASE WHEN NEW.risco_elevado THEN 'red' ELSE 'green' END, + auth.uid(), + now() + ); + END IF; + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.trg_fn_patient_status_history() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF (TG_OP = 'INSERT') OR (OLD.status IS DISTINCT FROM NEW.status) THEN + INSERT INTO public.patient_status_history ( + patient_id, tenant_id, + status_anterior, status_novo, + motivo, encaminhado_para, data_saida, + alterado_por, alterado_em + ) VALUES ( + NEW.id, NEW.tenant_id, + CASE WHEN TG_OP = 'INSERT' THEN NULL ELSE OLD.status END, + NEW.status, + NEW.motivo_saida, + NEW.encaminhado_para, + NEW.data_saida, + auth.uid(), + now() + ); + END IF; + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.trg_fn_patient_status_timeline() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF (TG_OP = 'INSERT') OR (OLD.status IS DISTINCT FROM NEW.status) THEN + INSERT INTO public.patient_timeline ( + patient_id, tenant_id, + evento_tipo, titulo, descricao, icone_cor, + gerado_por, ocorrido_em + ) VALUES ( + NEW.id, NEW.tenant_id, + 'status_alterado', + 'Status alterado para ' || NEW.status, + CASE + WHEN TG_OP = 'INSERT' THEN 'Paciente cadastrado' + ELSE 'De ' || OLD.status || ' → ' || NEW.status || + CASE WHEN NEW.motivo_saida IS NOT NULL THEN ' · ' || NEW.motivo_saida ELSE '' END + END, + CASE NEW.status + WHEN 'Ativo' THEN 'green' + WHEN 'Alta' THEN 'blue' + WHEN 'Inativo' THEN 'gray' + WHEN 'Encaminhado' THEN 'amber' + WHEN 'Arquivado' THEN 'gray' + ELSE 'gray' + END, + auth.uid(), + now() + ); + END IF; + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.trg_set_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ BEGIN + NEW.updated_at = now(); + RETURN NEW; + END; + $$; + +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; +$$; + +CREATE FUNCTION public.update_payment_settings_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.update_professional_pricing_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.user_has_feature(_user_id uuid, _feature text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.v_user_entitlements e + where e.user_id = _user_id + and e.feature_key = _feature + and e.allowed = true + ); +$$; + +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; +$$; + +CREATE FUNCTION public.whoami() RETURNS TABLE(uid uuid, role text) + LANGUAGE sql STABLE + AS $$ + select auth.uid() as uid, auth.role() as role; +$$; diff --git a/database-novo/schema/03_functions/realtime.sql b/database-novo/schema/03_functions/realtime.sql index 372d9f3..59ed93d 100644 --- a/database-novo/schema/03_functions/realtime.sql +++ b/database-novo/schema/03_functions/realtime.sql @@ -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 --- - diff --git a/database-novo/schema/03_functions/storage.sql b/database-novo/schema/03_functions/storage.sql index a7962c9..3ea94f0 100644 --- a/database-novo/schema/03_functions/storage.sql +++ b/database-novo/schema/03_functions/storage.sql @@ -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 --- - diff --git a/database-novo/schema/03_functions/supabase_functions.sql b/database-novo/schema/03_functions/supabase_functions.sql index dffb698..3423455 100644 --- a/database-novo/schema/03_functions/supabase_functions.sql +++ b/database-novo/schema/03_functions/supabase_functions.sql @@ -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 --- - diff --git a/database-novo/schema/04_tables/addons_cr_ditos.sql b/database-novo/schema/04_tables/addons_cr_ditos.sql new file mode 100644 index 0000000..e5218e9 --- /dev/null +++ b/database-novo/schema/04_tables/addons_cr_ditos.sql @@ -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 +); diff --git a/database-novo/schema/04_tables/agenda.sql b/database-novo/schema/04_tables/agenda_agendamento.sql similarity index 68% rename from database-novo/schema/04_tables/agenda.sql rename to database-novo/schema/04_tables/agenda_agendamento.sql index 5caae2d..a1a1662 100644 --- a/database-novo/schema/04_tables/agenda.sql +++ b/database-novo/schema/04_tables/agenda_agendamento.sql @@ -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)) -); - - diff --git a/database-novo/schema/04_tables/auth.sql b/database-novo/schema/04_tables/auth.sql deleted file mode 100644 index a68b2ef..0000000 --- a/database-novo/schema/04_tables/auth.sql +++ /dev/null @@ -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 --- - diff --git a/database-novo/schema/04_tables/central_saas_docs_faq.sql b/database-novo/schema/04_tables/central_saas_docs_faq.sql new file mode 100644 index 0000000..ba00291 --- /dev/null +++ b/database-novo/schema/04_tables/central_saas_docs_faq.sql @@ -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 +); diff --git a/database-novo/schema/04_tables/notifications.sql b/database-novo/schema/04_tables/comunica_o_notifica_es.sql similarity index 70% rename from database-novo/schema/04_tables/notifications.sql rename to database-novo/schema/04_tables/comunica_o_notifica_es.sql index ee593a3..e589fb4 100644 --- a/database-novo/schema/04_tables/notifications.sql +++ b/database-novo/schema/04_tables/comunica_o_notifica_es.sql @@ -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 -); - - diff --git a/database-novo/schema/04_tables/documentos.sql b/database-novo/schema/04_tables/documentos.sql new file mode 100644 index 0000000..c1f61a1 --- /dev/null +++ b/database-novo/schema/04_tables/documentos.sql @@ -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]))) +); diff --git a/database-novo/schema/04_tables/estrutura_calend_rio.sql b/database-novo/schema/04_tables/estrutura_calend_rio.sql new file mode 100644 index 0000000..810a92a --- /dev/null +++ b/database-novo/schema/04_tables/estrutura_calend_rio.sql @@ -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]))) +); diff --git a/database-novo/schema/04_tables/financial.sql b/database-novo/schema/04_tables/financeiro.sql similarity index 69% rename from database-novo/schema/04_tables/financial.sql rename to database-novo/schema/04_tables/financeiro.sql index 1174934..26de5c2 100644 --- a/database-novo/schema/04_tables/financial.sql +++ b/database-novo/schema/04_tables/financeiro.sql @@ -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() -); - - diff --git a/database-novo/schema/04_tables/infra.sql b/database-novo/schema/04_tables/infra.sql deleted file mode 100644 index 93e9498..0000000 --- a/database-novo/schema/04_tables/infra.sql +++ /dev/null @@ -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 --- diff --git a/database-novo/schema/04_tables/outros.sql b/database-novo/schema/04_tables/outros.sql new file mode 100644 index 0000000..3803ba4 --- /dev/null +++ b/database-novo/schema/04_tables/outros.sql @@ -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 +); diff --git a/database-novo/schema/04_tables/patients.sql b/database-novo/schema/04_tables/pacientes.sql similarity index 53% rename from database-novo/schema/04_tables/patients.sql rename to database-novo/schema/04_tables/pacientes.sql index fc665b4..acf7131 100644 --- a/database-novo/schema/04_tables/patients.sql +++ b/database-novo/schema/04_tables/pacientes.sql @@ -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() -); - - diff --git a/database-novo/schema/04_tables/saas_admin.sql b/database-novo/schema/04_tables/saas_admin.sql deleted file mode 100644 index d610e30..0000000 --- a/database-novo/schema/04_tables/saas_admin.sql +++ /dev/null @@ -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 -); - - diff --git a/database-novo/schema/04_tables/plans_billing.sql b/database-novo/schema/04_tables/saas_planos.sql similarity index 73% rename from database-novo/schema/04_tables/plans_billing.sql rename to database-novo/schema/04_tables/saas_planos.sql index 8ca5b01..913e2fe 100644 --- a/database-novo/schema/04_tables/plans_billing.sql +++ b/database-novo/schema/04_tables/saas_planos.sql @@ -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 +); diff --git a/database-novo/schema/04_tables/commitments.sql b/database-novo/schema/04_tables/servi_os_prontu_rios.sql similarity index 57% rename from database-novo/schema/04_tables/commitments.sql rename to database-novo/schema/04_tables/servi_os_prontu_rios.sql index 6ad4e51..d0aa74d 100644 --- a/database-novo/schema/04_tables/commitments.sql +++ b/database-novo/schema/04_tables/servi_os_prontu_rios.sql @@ -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() +); diff --git a/database-novo/schema/04_tables/core.sql b/database-novo/schema/04_tables/tenants_multi_tenant.sql similarity index 82% rename from database-novo/schema/04_tables/core.sql rename to database-novo/schema/04_tables/tenants_multi_tenant.sql index fd545c3..a6ec9c5 100644 --- a/database-novo/schema/04_tables/core.sql +++ b/database-novo/schema/04_tables/tenants_multi_tenant.sql @@ -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]))) +); diff --git a/database-novo/schema/05_views/views.sql b/database-novo/schema/05_views/views.sql index 2e28065..b78149a 100644 --- a/database-novo/schema/05_views/views.sql +++ b/database-novo/schema/05_views/views.sql @@ -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 --- - diff --git a/database-novo/schema/06_indexes/indexes.sql b/database-novo/schema/06_indexes/indexes.sql index dcb3910..a0b5c59 100644 --- a/database-novo/schema/06_indexes/indexes.sql +++ b/database-novo/schema/06_indexes/indexes.sql @@ -1,2049 +1,543 @@ --- ============================================================================= --- AgenciaPsi — Indexes --- ============================================================================= - -CREATE INDEX extensions_tenant_external_id_index ON _realtime.extensions USING btree (tenant_external_id); - - --- --- Name: extensions_tenant_external_id_type_index; Type: INDEX; Schema: _realtime; Owner: supabase_admin --- - -CREATE UNIQUE INDEX extensions_tenant_external_id_type_index ON _realtime.extensions USING btree (tenant_external_id, type); - - --- --- Name: tenants_external_id_index; Type: INDEX; Schema: _realtime; Owner: supabase_admin --- - -CREATE UNIQUE INDEX tenants_external_id_index ON _realtime.tenants USING btree (external_id); - - --- --- Name: audit_logs_instance_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX audit_logs_instance_id_idx ON auth.audit_log_entries USING btree (instance_id); - - --- --- Name: confirmation_token_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX confirmation_token_idx ON auth.users USING btree (confirmation_token) WHERE ((confirmation_token)::text !~ '^[0-9 ]*$'::text); - - --- --- Name: email_change_token_current_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX email_change_token_current_idx ON auth.users USING btree (email_change_token_current) WHERE ((email_change_token_current)::text !~ '^[0-9 ]*$'::text); - - --- --- Name: email_change_token_new_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX email_change_token_new_idx ON auth.users USING btree (email_change_token_new) WHERE ((email_change_token_new)::text !~ '^[0-9 ]*$'::text); - - --- --- Name: factor_id_created_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX factor_id_created_at_idx ON auth.mfa_factors USING btree (user_id, created_at); - - --- --- Name: flow_state_created_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX flow_state_created_at_idx ON auth.flow_state USING btree (created_at DESC); - - --- --- Name: identities_email_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX identities_email_idx ON auth.identities USING btree (email text_pattern_ops); - - --- --- Name: INDEX identities_email_idx; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON INDEX auth.identities_email_idx IS 'Auth: Ensures indexed queries on the email column'; - - --- --- Name: identities_user_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX identities_user_id_idx ON auth.identities USING btree (user_id); - - --- --- Name: idx_auth_code; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX idx_auth_code ON auth.flow_state USING btree (auth_code); - - --- --- Name: idx_oauth_client_states_created_at; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX idx_oauth_client_states_created_at ON auth.oauth_client_states USING btree (created_at); - - --- --- Name: idx_user_id_auth_method; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX idx_user_id_auth_method ON auth.flow_state USING btree (user_id, authentication_method); - - --- --- Name: mfa_challenge_created_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX mfa_challenge_created_at_idx ON auth.mfa_challenges USING btree (created_at DESC); - - --- --- Name: mfa_factors_user_friendly_name_unique; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX mfa_factors_user_friendly_name_unique ON auth.mfa_factors USING btree (friendly_name, user_id) WHERE (TRIM(BOTH FROM friendly_name) <> ''::text); - - --- --- Name: mfa_factors_user_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX mfa_factors_user_id_idx ON auth.mfa_factors USING btree (user_id); - - --- --- Name: oauth_auth_pending_exp_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX oauth_auth_pending_exp_idx ON auth.oauth_authorizations USING btree (expires_at) WHERE (status = 'pending'::auth.oauth_authorization_status); - - --- --- Name: oauth_clients_deleted_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX oauth_clients_deleted_at_idx ON auth.oauth_clients USING btree (deleted_at); - - --- --- Name: oauth_consents_active_client_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX oauth_consents_active_client_idx ON auth.oauth_consents USING btree (client_id) WHERE (revoked_at IS NULL); - - --- --- Name: oauth_consents_active_user_client_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX oauth_consents_active_user_client_idx ON auth.oauth_consents USING btree (user_id, client_id) WHERE (revoked_at IS NULL); - - --- --- Name: oauth_consents_user_order_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX oauth_consents_user_order_idx ON auth.oauth_consents USING btree (user_id, granted_at DESC); - - --- --- Name: one_time_tokens_relates_to_hash_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX one_time_tokens_relates_to_hash_idx ON auth.one_time_tokens USING hash (relates_to); - - --- --- Name: one_time_tokens_token_hash_hash_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX one_time_tokens_token_hash_hash_idx ON auth.one_time_tokens USING hash (token_hash); - - --- --- Name: one_time_tokens_user_id_token_type_key; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX one_time_tokens_user_id_token_type_key ON auth.one_time_tokens USING btree (user_id, token_type); - - --- --- Name: reauthentication_token_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX reauthentication_token_idx ON auth.users USING btree (reauthentication_token) WHERE ((reauthentication_token)::text !~ '^[0-9 ]*$'::text); - - --- --- Name: recovery_token_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX recovery_token_idx ON auth.users USING btree (recovery_token) WHERE ((recovery_token)::text !~ '^[0-9 ]*$'::text); - - --- --- Name: refresh_tokens_instance_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX refresh_tokens_instance_id_idx ON auth.refresh_tokens USING btree (instance_id); - - --- --- Name: refresh_tokens_instance_id_user_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX refresh_tokens_instance_id_user_id_idx ON auth.refresh_tokens USING btree (instance_id, user_id); - - --- --- Name: refresh_tokens_parent_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX refresh_tokens_parent_idx ON auth.refresh_tokens USING btree (parent); - - --- --- Name: refresh_tokens_session_id_revoked_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX refresh_tokens_session_id_revoked_idx ON auth.refresh_tokens USING btree (session_id, revoked); - - --- --- Name: refresh_tokens_updated_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX refresh_tokens_updated_at_idx ON auth.refresh_tokens USING btree (updated_at DESC); - - --- --- Name: saml_providers_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX saml_providers_sso_provider_id_idx ON auth.saml_providers USING btree (sso_provider_id); - - --- --- Name: saml_relay_states_created_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX saml_relay_states_created_at_idx ON auth.saml_relay_states USING btree (created_at DESC); - - --- --- Name: saml_relay_states_for_email_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX saml_relay_states_for_email_idx ON auth.saml_relay_states USING btree (for_email); - - --- --- Name: saml_relay_states_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX saml_relay_states_sso_provider_id_idx ON auth.saml_relay_states USING btree (sso_provider_id); - - --- --- Name: sessions_not_after_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX sessions_not_after_idx ON auth.sessions USING btree (not_after DESC); - - --- --- Name: sessions_oauth_client_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX sessions_oauth_client_id_idx ON auth.sessions USING btree (oauth_client_id); - - --- --- Name: sessions_user_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX sessions_user_id_idx ON auth.sessions USING btree (user_id); - - --- --- Name: sso_domains_domain_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX sso_domains_domain_idx ON auth.sso_domains USING btree (lower(domain)); - - --- --- Name: sso_domains_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX sso_domains_sso_provider_id_idx ON auth.sso_domains USING btree (sso_provider_id); - - --- --- Name: sso_providers_resource_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX sso_providers_resource_id_idx ON auth.sso_providers USING btree (lower(resource_id)); - - --- --- Name: sso_providers_resource_id_pattern_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX sso_providers_resource_id_pattern_idx ON auth.sso_providers USING btree (resource_id text_pattern_ops); - - --- --- Name: unique_phone_factor_per_user; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX unique_phone_factor_per_user ON auth.mfa_factors USING btree (user_id, phone); - - --- --- Name: user_id_created_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX user_id_created_at_idx ON auth.sessions USING btree (user_id, created_at); - - --- --- Name: users_email_partial_key; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX users_email_partial_key ON auth.users USING btree (email) WHERE (is_sso_user = false); - - --- --- Name: INDEX users_email_partial_key; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON INDEX auth.users_email_partial_key IS 'Auth: A partial unique index that applies only when is_sso_user is false'; - - --- --- Name: users_instance_id_email_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX users_instance_id_email_idx ON auth.users USING btree (instance_id, lower((email)::text)); - - --- --- Name: users_instance_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX users_instance_id_idx ON auth.users USING btree (instance_id); - - --- --- Name: users_is_anonymous_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX users_is_anonymous_idx ON auth.users USING btree (is_anonymous); - - --- --- Name: agenda_bloqueios_owner_data_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- +-- Indexes +-- Gerado automaticamente em 2026-04-17T12:23:05.235Z +-- Total: 270 CREATE INDEX agenda_bloqueios_owner_data_idx ON public.agenda_bloqueios USING btree (owner_id, data_inicio, data_fim); - --- --- Name: agenda_bloqueios_owner_id_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_bloqueios_owner_id_idx ON public.agenda_bloqueios USING btree (owner_id); - --- --- Name: agenda_bloqueios_recorrente_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_bloqueios_recorrente_idx ON public.agenda_bloqueios USING btree (owner_id, dia_semana) WHERE (recorrente = true); - --- --- Name: agenda_bloqueios_tenant_id_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_bloqueios_tenant_id_idx ON public.agenda_bloqueios USING btree (tenant_id); - --- --- Name: agenda_configuracoes_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_configuracoes_tenant_idx ON public.agenda_configuracoes USING btree (tenant_id); - --- --- Name: agenda_configuracoes_tenant_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_configuracoes_tenant_owner_idx ON public.agenda_configuracoes USING btree (tenant_id, owner_id); - --- --- Name: agenda_eventos_billing_contract_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_eventos_billing_contract_idx ON public.agenda_eventos USING btree (billing_contract_id) WHERE (billing_contract_id IS NOT NULL); - --- --- Name: agenda_eventos_insurance_plan_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_eventos_insurance_plan_idx ON public.agenda_eventos USING btree (insurance_plan_id); - --- --- Name: agenda_eventos_owner_inicio_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_eventos_owner_inicio_idx ON public.agenda_eventos USING btree (owner_id, inicio_em); - --- --- Name: agenda_eventos_owner_terapeuta_inicio_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_eventos_owner_terapeuta_inicio_idx ON public.agenda_eventos USING btree (owner_id, terapeuta_id, inicio_em); - --- --- Name: agenda_eventos_recurrence_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_eventos_recurrence_idx ON public.agenda_eventos USING btree (recurrence_id) WHERE (recurrence_id IS NOT NULL); - --- --- Name: agenda_eventos_tenant_inicio_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_eventos_tenant_inicio_idx ON public.agenda_eventos USING btree (tenant_id, inicio_em); - --- --- Name: agenda_eventos_tenant_owner_inicio_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_eventos_tenant_owner_inicio_idx ON public.agenda_eventos USING btree (tenant_id, owner_id, inicio_em); - --- --- Name: agenda_excecoes_owner_data_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_excecoes_owner_data_idx ON public.agenda_excecoes USING btree (owner_id, data); - --- --- Name: agenda_excecoes_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_excecoes_tenant_idx ON public.agenda_excecoes USING btree (tenant_id); - --- --- Name: agenda_excecoes_tenant_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_excecoes_tenant_owner_idx ON public.agenda_excecoes USING btree (tenant_id, owner_id); - --- --- Name: agenda_online_slots_owner_weekday_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_online_slots_owner_weekday_idx ON public.agenda_online_slots USING btree (owner_id, weekday); - --- --- Name: agenda_online_slots_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_online_slots_tenant_idx ON public.agenda_online_slots USING btree (tenant_id); - --- --- Name: agenda_online_slots_tenant_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_online_slots_tenant_owner_idx ON public.agenda_online_slots USING btree (tenant_id, owner_id); - --- --- Name: agenda_regras_semanais_owner_dia_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_regras_semanais_owner_dia_idx ON public.agenda_regras_semanais USING btree (owner_id, dia_semana); - --- --- Name: agenda_regras_semanais_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_regras_semanais_tenant_idx ON public.agenda_regras_semanais USING btree (tenant_id); - --- --- Name: agenda_regras_semanais_tenant_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_regras_semanais_tenant_owner_idx ON public.agenda_regras_semanais USING btree (tenant_id, owner_id); - --- --- Name: agenda_slots_bloqueados_semanais_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_slots_bloqueados_semanais_tenant_idx ON public.agenda_slots_bloqueados_semanais USING btree (tenant_id); - --- --- Name: agenda_slots_bloqueados_semanais_tenant_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_slots_bloqueados_semanais_tenant_owner_idx ON public.agenda_slots_bloqueados_semanais USING btree (tenant_id, owner_id); - --- --- Name: agenda_slots_regras_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_slots_regras_tenant_idx ON public.agenda_slots_regras USING btree (tenant_id); - --- --- Name: agenda_slots_regras_tenant_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agenda_slots_regras_tenant_owner_idx ON public.agenda_slots_regras USING btree (tenant_id, owner_id); - --- --- Name: agendador_cfg_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agendador_cfg_tenant_idx ON public.agendador_configuracoes USING btree (tenant_id); - --- --- Name: agendador_sol_data_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agendador_sol_data_idx ON public.agendador_solicitacoes USING btree (data_solicitada, hora_solicitada); - --- --- Name: agendador_sol_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agendador_sol_owner_idx ON public.agendador_solicitacoes USING btree (owner_id, status); - --- --- Name: agendador_sol_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX agendador_sol_tenant_idx ON public.agendador_solicitacoes USING btree (tenant_id); - --- --- Name: billing_contracts_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX billing_contracts_owner_idx ON public.billing_contracts USING btree (owner_id); - --- --- Name: billing_contracts_patient_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX billing_contracts_patient_idx ON public.billing_contracts USING btree (patient_id); - --- --- Name: billing_contracts_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX billing_contracts_tenant_idx ON public.billing_contracts USING btree (tenant_id); - --- --- Name: commitment_services_commitment_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX commitment_services_commitment_idx ON public.commitment_services USING btree (commitment_id); - --- --- Name: commitment_services_service_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX commitment_services_service_idx ON public.commitment_services USING btree (service_id); - --- --- Name: commitment_time_logs_calendar_event_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX commitment_time_logs_calendar_event_idx ON public.commitment_time_logs USING btree (calendar_event_id); - --- --- Name: commitment_time_logs_commitment_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX commitment_time_logs_commitment_idx ON public.commitment_time_logs USING btree (commitment_id, created_at DESC); - --- --- Name: commitment_time_logs_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX commitment_time_logs_tenant_idx ON public.commitment_time_logs USING btree (tenant_id, created_at DESC); +CREATE INDEX dal_documento_idx ON public.document_access_logs USING btree (documento_id, acessado_em DESC); --- --- Name: determined_commitment_fields_commitment_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- +CREATE INDEX dal_tenant_idx ON public.document_access_logs USING btree (tenant_id, acessado_em DESC); + +CREATE INDEX dal_user_idx ON public.document_access_logs USING btree (user_id, acessado_em DESC); CREATE INDEX determined_commitment_fields_commitment_idx ON public.determined_commitment_fields USING btree (commitment_id, sort_order); - --- --- Name: determined_commitment_fields_key_uniq; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE UNIQUE INDEX determined_commitment_fields_key_uniq ON public.determined_commitment_fields USING btree (commitment_id, key); - --- --- Name: determined_commitment_fields_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX determined_commitment_fields_tenant_idx ON public.determined_commitment_fields USING btree (tenant_id); - --- --- Name: determined_commitments_active_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX determined_commitments_active_idx ON public.determined_commitments USING btree (tenant_id, active); - --- --- Name: determined_commitments_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX determined_commitments_tenant_idx ON public.determined_commitments USING btree (tenant_id); - --- --- Name: determined_commitments_tenant_name_uniq; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE UNIQUE INDEX determined_commitments_tenant_name_uniq ON public.determined_commitments USING btree (tenant_id, lower(name)); +CREATE INDEX dg_gerado_por_idx ON public.document_generated USING btree (gerado_por, gerado_em DESC); --- --- Name: feriados_global_unique; Type: INDEX; Schema: public; Owner: supabase_admin --- +CREATE INDEX dg_patient_idx ON public.document_generated USING btree (patient_id, gerado_em DESC); + +CREATE INDEX dg_template_idx ON public.document_generated USING btree (template_id); + +CREATE INDEX dg_tenant_idx ON public.document_generated USING btree (tenant_id, gerado_em DESC); + +CREATE INDEX docs_active_idx ON public.documents USING btree (patient_id, uploaded_at DESC) WHERE (deleted_at IS NULL); + +CREATE INDEX docs_nome_trgm_idx ON public.documents USING gin (nome_original public.gin_trgm_ops); + +CREATE INDEX docs_owner_idx ON public.documents USING btree (owner_id); + +CREATE INDEX docs_patient_idx ON public.documents USING btree (patient_id); + +CREATE INDEX docs_tags_idx ON public.documents USING gin (tags); + +CREATE INDEX docs_tenant_idx ON public.documents USING btree (tenant_id); + +CREATE INDEX docs_tipo_idx ON public.documents USING btree (patient_id, tipo_documento); + +CREATE INDEX docs_uploaded_at_idx ON public.documents USING btree (patient_id, uploaded_at DESC); + +CREATE INDEX ds_documento_idx ON public.document_signatures USING btree (documento_id, ordem); + +CREATE INDEX ds_status_idx ON public.document_signatures USING btree (documento_id, status); + +CREATE INDEX ds_tenant_idx ON public.document_signatures USING btree (tenant_id); + +CREATE INDEX dsl_documento_idx ON public.document_share_links USING btree (documento_id); + +CREATE INDEX dsl_expira_idx ON public.document_share_links USING btree (expira_em) WHERE (ativo = true); + +CREATE INDEX dsl_token_idx ON public.document_share_links USING btree (token) WHERE (ativo = true); + +CREATE INDEX dt_global_idx ON public.document_templates USING btree (is_global) WHERE (is_global = true); + +CREATE INDEX dt_nome_trgm_idx ON public.document_templates USING gin (nome_template public.gin_trgm_ops); + +CREATE INDEX dt_owner_idx ON public.document_templates USING btree (owner_id); + +CREATE INDEX dt_tenant_idx ON public.document_templates USING btree (tenant_id); + +CREATE INDEX dt_tipo_idx ON public.document_templates USING btree (tipo); CREATE UNIQUE INDEX feriados_global_unique ON public.feriados USING btree (data, nome) WHERE (tenant_id IS NULL); - --- --- Name: financial_exceptions_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX financial_exceptions_owner_idx ON public.financial_exceptions USING btree (owner_id); - --- --- Name: financial_exceptions_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX financial_exceptions_tenant_idx ON public.financial_exceptions USING btree (tenant_id); - --- --- Name: idx_addon_credits_tenant; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_addon_credits_tenant ON public.addon_credits USING btree (tenant_id) WHERE (is_active = true); - --- --- Name: idx_addon_credits_type; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_addon_credits_type ON public.addon_credits USING btree (addon_type) WHERE (is_active = true); - --- --- Name: idx_addon_products_active; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_addon_products_active ON public.addon_products USING btree (is_active, is_visible) WHERE (deleted_at IS NULL); - --- --- Name: idx_addon_products_type; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_addon_products_type ON public.addon_products USING btree (addon_type) WHERE (deleted_at IS NULL); - --- --- Name: idx_addon_tx_created; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_addon_tx_created ON public.addon_transactions USING btree (created_at DESC); - --- --- Name: idx_addon_tx_queue; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_addon_tx_queue ON public.addon_transactions USING btree (queue_id) WHERE (queue_id IS NOT NULL); - --- --- Name: idx_addon_tx_tenant; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_addon_tx_tenant ON public.addon_transactions USING btree (tenant_id, addon_type); - --- --- Name: idx_addon_tx_type; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_addon_tx_type ON public.addon_transactions USING btree (type); - --- --- Name: idx_agenda_eventos_determined_commitment_id; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_agenda_eventos_determined_commitment_id ON public.agenda_eventos USING btree (determined_commitment_id); - --- --- Name: idx_agenda_excecoes_owner_data; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_agenda_excecoes_owner_data ON public.agenda_excecoes USING btree (owner_id, data); - --- --- Name: idx_agenda_slots_regras_owner_dia; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_agenda_slots_regras_owner_dia ON public.agenda_slots_regras USING btree (owner_id, dia_semana); - --- --- Name: idx_email_templates_global_domain; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_email_templates_global_domain ON public.email_templates_global USING btree (domain) WHERE (is_active = true); - --- --- Name: idx_email_templates_global_key; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_email_templates_global_key ON public.email_templates_global USING btree (key) WHERE (is_active = true); - --- --- Name: idx_email_templates_tenant_lookup; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_email_templates_tenant_lookup ON public.email_templates_tenant USING btree (tenant_id, template_key) WHERE (enabled = true); - --- --- Name: idx_email_templates_tenant_owner; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_email_templates_tenant_owner ON public.email_templates_tenant USING btree (owner_id, template_key) WHERE ((enabled = true) AND (owner_id IS NOT NULL)); - --- --- Name: idx_financial_categories_user_id; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_financial_categories_user_id ON public.financial_categories USING btree (user_id); - --- --- Name: idx_financial_records_active; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_financial_records_active ON public.financial_records USING btree (owner_id, paid_at DESC) WHERE (deleted_at IS NULL); - --- --- Name: idx_financial_records_agenda_evento_id; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_financial_records_agenda_evento_id ON public.financial_records USING btree (agenda_evento_id) WHERE (agenda_evento_id IS NOT NULL); - --- --- Name: idx_financial_records_category_id; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_financial_records_category_id ON public.financial_records USING btree (category_id) WHERE (category_id IS NOT NULL); - --- --- Name: idx_financial_records_due_date; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_financial_records_due_date ON public.financial_records USING btree (due_date); - --- --- Name: idx_financial_records_installment_group; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_financial_records_installment_group ON public.financial_records USING btree (installment_group) WHERE (installment_group IS NOT NULL); - --- --- Name: idx_financial_records_owner_id; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_financial_records_owner_id ON public.financial_records USING btree (owner_id); - --- --- Name: idx_financial_records_paid_at; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_financial_records_paid_at ON public.financial_records USING btree (paid_at DESC); - --- --- Name: idx_financial_records_patient_id; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_financial_records_patient_id ON public.financial_records USING btree (patient_id); - --- --- Name: idx_financial_records_status; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_financial_records_status ON public.financial_records USING btree (status) WHERE (deleted_at IS NULL); - --- --- Name: idx_financial_records_tenant_active; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_financial_records_tenant_active ON public.financial_records USING btree (tenant_id, paid_at DESC) WHERE ((deleted_at IS NULL) AND (tenant_id IS NOT NULL)); - --- --- Name: idx_financial_records_tenant_id; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_financial_records_tenant_id ON public.financial_records USING btree (tenant_id); - --- --- Name: idx_financial_records_type_status; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_financial_records_type_status ON public.financial_records USING btree (type, status); - --- --- Name: idx_global_notices_active_priority; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_global_notices_active_priority ON public.global_notices USING btree (is_active, priority DESC, starts_at, ends_at); - --- --- Name: idx_intakes_converted_patient_id; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_intakes_converted_patient_id ON public.patient_intake_requests USING btree (converted_patient_id); - --- --- Name: idx_intakes_owner_cpf; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_intakes_owner_cpf ON public.patient_intake_requests USING btree (owner_id, cpf); - --- --- Name: idx_intakes_owner_created; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_intakes_owner_created ON public.patient_intake_requests USING btree (owner_id, created_at DESC); - --- --- Name: idx_intakes_owner_status_created; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_intakes_owner_status_created ON public.patient_intake_requests USING btree (owner_id, status, created_at DESC); - --- --- Name: idx_intakes_status_created; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_intakes_status_created ON public.patient_intake_requests USING btree (status, created_at DESC); - --- --- Name: idx_notice_dismissals_user; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_notice_dismissals_user ON public.notice_dismissals USING btree (user_id, notice_id); - --- --- Name: idx_notif_channels_owner_active; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_notif_channels_owner_active ON public.notification_channels USING btree (owner_id, channel) WHERE ((is_active = true) AND (deleted_at IS NULL)); - --- --- Name: idx_notif_channels_tenant; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_notif_channels_tenant ON public.notification_channels USING btree (tenant_id) WHERE (deleted_at IS NULL); - --- --- Name: idx_notif_logs_owner_date; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_notif_logs_owner_date ON public.notification_logs USING btree (owner_id, created_at DESC); - --- --- Name: idx_notif_logs_patient; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_notif_logs_patient ON public.notification_logs USING btree (patient_id, created_at DESC); - --- --- Name: idx_notif_logs_provider_msg; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_notif_logs_provider_msg ON public.notification_logs USING btree (provider_message_id) WHERE (provider_message_id IS NOT NULL); - --- --- Name: idx_notif_logs_status; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_notif_logs_status ON public.notification_logs USING btree (status, created_at DESC); - --- --- Name: idx_notif_logs_tenant_date; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_notif_logs_tenant_date ON public.notification_logs USING btree (tenant_id, created_at DESC); - --- --- Name: idx_notif_prefs_owner; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_notif_prefs_owner ON public.notification_preferences USING btree (owner_id) WHERE (deleted_at IS NULL); - --- --- Name: idx_notif_prefs_patient; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_notif_prefs_patient ON public.notification_preferences USING btree (patient_id) WHERE (deleted_at IS NULL); - --- --- Name: idx_notif_prefs_whatsapp_active; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_notif_prefs_whatsapp_active ON public.notification_preferences USING btree (owner_id, patient_id) WHERE ((whatsapp_opt_in = true) AND (deleted_at IS NULL)); - --- --- Name: idx_notif_queue_evento; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_notif_queue_evento ON public.notification_queue USING btree (agenda_evento_id) WHERE (status = ANY (ARRAY['pendente'::text, 'processando'::text])); - --- --- Name: idx_notif_queue_patient; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_notif_queue_patient ON public.notification_queue USING btree (patient_id, channel) WHERE (status = 'pendente'::text); - --- --- Name: idx_notif_queue_pending; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_notif_queue_pending ON public.notification_queue USING btree (scheduled_at) WHERE (status = 'pendente'::text); - --- --- Name: idx_notif_queue_processing; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_notif_queue_processing ON public.notification_queue USING btree (status, updated_at) WHERE (status = 'processando'::text); - --- --- Name: idx_notif_queue_retry; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_notif_queue_retry ON public.notification_queue USING btree (next_retry_at) WHERE ((status = 'pendente'::text) AND (attempts > 0)); - --- --- Name: idx_notif_queue_tenant; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_notif_queue_tenant ON public.notification_queue USING btree (tenant_id, created_at DESC); - --- --- Name: idx_notif_schedules_owner_active; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_notif_schedules_owner_active ON public.notification_schedules USING btree (owner_id, event_type) WHERE ((is_active = true) AND (deleted_at IS NULL)); - --- --- Name: idx_notif_templates_default; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_notif_templates_default ON public.notification_templates USING btree (channel, event_type) WHERE ((is_default = true) AND (deleted_at IS NULL) AND (tenant_id IS NULL)); - --- --- Name: idx_notif_templates_lookup; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_notif_templates_lookup ON public.notification_templates USING btree (channel, event_type, is_active) WHERE (deleted_at IS NULL); - --- --- Name: idx_notif_templates_tenant; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_notif_templates_tenant ON public.notification_templates USING btree (tenant_id, channel, event_type) WHERE ((deleted_at IS NULL) AND (is_active = true)); +CREATE INDEX idx_notification_channels_twilio_subaccount_sid ON public.notification_channels USING btree (twilio_subaccount_sid) WHERE (twilio_subaccount_sid IS NOT NULL); --- --- Name: idx_patient_group_patient_group_id; Type: INDEX; Schema: public; Owner: supabase_admin --- +CREATE INDEX idx_patient_contacts_patient ON public.patient_contacts USING btree (patient_id); + +CREATE INDEX idx_patient_contacts_tenant ON public.patient_contacts USING btree (tenant_id); CREATE INDEX idx_patient_group_patient_group_id ON public.patient_group_patient USING btree (patient_group_id); - --- --- Name: idx_patient_groups_owner; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_patient_groups_owner ON public.patient_groups USING btree (owner_id); - --- --- Name: idx_patient_groups_owner_system_nome; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_patient_groups_owner_system_nome ON public.patient_groups USING btree (owner_id, is_system, nome); - --- --- Name: idx_patient_tags_owner; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_patient_tags_owner ON public.patient_tags USING btree (owner_id); - --- --- Name: idx_patients_created_at; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_patients_created_at ON public.patients USING btree (created_at DESC); - --- --- Name: idx_patients_last_attended; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_patients_last_attended ON public.patients USING btree (last_attended_at DESC); - --- --- Name: idx_patients_owner_email_principal; Type: INDEX; Schema: public; Owner: supabase_admin --- +CREATE INDEX idx_patients_origem ON public.patients USING btree (tenant_id, origem) WHERE (origem IS NOT NULL); CREATE INDEX idx_patients_owner_email_principal ON public.patients USING btree (owner_id, email_principal); - --- --- Name: idx_patients_owner_id; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_patients_owner_id ON public.patients USING btree (owner_id); - --- --- Name: idx_patients_owner_nome; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_patients_owner_nome ON public.patients USING btree (owner_id, nome_completo); - --- --- Name: idx_patients_responsible_member; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_patients_responsible_member ON public.patients USING btree (responsible_member_id); - --- --- Name: idx_patients_status; Type: INDEX; Schema: public; Owner: supabase_admin --- +CREATE INDEX idx_patients_risco_elevado ON public.patients USING btree (tenant_id, risco_elevado) WHERE (risco_elevado = true); CREATE INDEX idx_patients_status ON public.patients USING btree (status); - --- --- Name: idx_patients_tenant; Type: INDEX; Schema: public; Owner: supabase_admin --- +CREATE INDEX idx_patients_status_tenant ON public.patients USING btree (tenant_id, status); CREATE INDEX idx_patients_tenant ON public.patients USING btree (tenant_id); - --- --- Name: idx_patients_tenant_email_norm; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_patients_tenant_email_norm ON public.patients USING btree (tenant_id, lower(TRIM(BOTH FROM email_principal))); - --- --- Name: idx_pgp_group; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_pgp_group ON public.patient_group_patient USING btree (patient_group_id); - --- --- Name: idx_pgp_patient; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_pgp_patient ON public.patient_group_patient USING btree (patient_id); - --- --- Name: idx_ppt_patient; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_ppt_patient ON public.patient_patient_tag USING btree (patient_id); - --- --- Name: idx_ppt_tag; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_ppt_tag ON public.patient_patient_tag USING btree (tag_id); +CREATE INDEX idx_psh_patient ON public.patient_status_history USING btree (patient_id, alterado_em DESC); --- --- Name: idx_slots_bloq_owner_dia; Type: INDEX; Schema: public; Owner: supabase_admin --- +CREATE INDEX idx_psh_tenant ON public.patient_status_history USING btree (tenant_id, alterado_em DESC); + +CREATE INDEX idx_pt_evento_tipo ON public.patient_timeline USING btree (patient_id, evento_tipo); + +CREATE INDEX idx_pt_patient_ocorrido ON public.patient_timeline USING btree (patient_id, ocorrido_em DESC); + +CREATE INDEX idx_pt_tenant ON public.patient_timeline USING btree (tenant_id, ocorrido_em DESC); CREATE INDEX idx_slots_bloq_owner_dia ON public.agenda_slots_bloqueados_semanais USING btree (owner_id, dia_semana); - --- --- Name: idx_subscription_intents_plan_interval; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_subscription_intents_plan_interval ON public.subscription_intents_legacy USING btree (plan_key, "interval"); - --- --- Name: idx_subscription_intents_status; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_subscription_intents_status ON public.subscription_intents_legacy USING btree (status); - --- --- Name: idx_subscription_intents_user_id; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_subscription_intents_user_id ON public.subscription_intents_legacy USING btree (user_id); - --- --- Name: idx_tenant_features_tenant; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_tenant_features_tenant ON public.tenant_features USING btree (tenant_id); - --- --- Name: idx_tenant_invites_tenant; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_tenant_invites_tenant ON public.tenant_invites USING btree (tenant_id); - --- --- Name: idx_tenant_invites_token; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_tenant_invites_token ON public.tenant_invites USING btree (token); - --- --- Name: idx_therapist_payout_records_financial_record_id; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_therapist_payout_records_financial_record_id ON public.therapist_payout_records USING btree (financial_record_id); - --- --- Name: idx_therapist_payouts_owner_id; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_therapist_payouts_owner_id ON public.therapist_payouts USING btree (owner_id); - --- --- Name: idx_therapist_payouts_period; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_therapist_payouts_period ON public.therapist_payouts USING btree (period_start, period_end); - --- --- Name: idx_therapist_payouts_status; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_therapist_payouts_status ON public.therapist_payouts USING btree (status) WHERE (status = 'pending'::text); - --- --- Name: idx_therapist_payouts_tenant_id; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX idx_therapist_payouts_tenant_id ON public.therapist_payouts USING btree (tenant_id); +CREATE INDEX idx_twilio_usage_channel ON public.twilio_subaccount_usage USING btree (channel_id, period_start DESC); --- --- Name: insurance_plans_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- +CREATE INDEX idx_twilio_usage_tenant_period ON public.twilio_subaccount_usage USING btree (tenant_id, period_start DESC); + +CREATE UNIQUE INDEX idx_twilio_usage_unique_period ON public.twilio_subaccount_usage USING btree (channel_id, period_start, period_end); CREATE INDEX insurance_plans_owner_idx ON public.insurance_plans USING btree (owner_id); - --- --- Name: insurance_plans_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX insurance_plans_tenant_idx ON public.insurance_plans USING btree (tenant_id); - --- --- Name: ix_plan_prices_plan; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX ix_plan_prices_plan ON public.plan_prices USING btree (plan_id); - --- --- Name: ix_plan_public_bullets_plan; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX ix_plan_public_bullets_plan ON public.plan_public_bullets USING btree (plan_id); - --- --- Name: ix_plan_public_sort; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX ix_plan_public_sort ON public.plan_public USING btree (sort_order); +CREATE INDEX medicos_especialidade_idx ON public.medicos USING btree (especialidade); --- --- Name: notifications_owner_created; Type: INDEX; Schema: public; Owner: supabase_admin --- +CREATE INDEX medicos_nome_idx ON public.medicos USING btree (nome); + +CREATE INDEX medicos_nome_trgm_idx ON public.medicos USING gin (nome public.gin_trgm_ops); + +CREATE INDEX medicos_owner_idx ON public.medicos USING btree (owner_id); + +CREATE INDEX medicos_tenant_idx ON public.medicos USING btree (tenant_id); CREATE INDEX notifications_owner_created ON public.notifications USING btree (owner_id, created_at DESC); - --- --- Name: notifications_owner_unread; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX notifications_owner_unread ON public.notifications USING btree (owner_id, read_at) WHERE (read_at IS NULL); - --- --- Name: patient_discounts_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX patient_discounts_owner_idx ON public.patient_discounts USING btree (owner_id); - --- --- Name: patient_discounts_patient_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX patient_discounts_patient_idx ON public.patient_discounts USING btree (patient_id); - --- --- Name: patient_discounts_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX patient_discounts_tenant_idx ON public.patient_discounts USING btree (tenant_id); - --- --- Name: patient_group_patient_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX patient_group_patient_tenant_idx ON public.patient_group_patient USING btree (tenant_id); - --- --- Name: patient_groups_owner_nome_uniq; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE UNIQUE INDEX patient_groups_owner_nome_uniq ON public.patient_groups USING btree (owner_id, nome); - --- --- Name: patient_groups_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX patient_groups_tenant_idx ON public.patient_groups USING btree (tenant_id); - --- --- Name: patient_intake_owner_id_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX patient_intake_owner_id_idx ON public.patient_intake_requests USING btree (owner_id); - --- --- Name: patient_intake_requests_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX patient_intake_requests_tenant_idx ON public.patient_intake_requests USING btree (tenant_id); - --- --- Name: patient_intake_status_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX patient_intake_status_idx ON public.patient_intake_requests USING btree (status); - --- --- Name: patient_intake_token_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX patient_intake_token_idx ON public.patient_intake_requests USING btree (token); - --- --- Name: patient_invites_one_active_per_owner; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE UNIQUE INDEX patient_invites_one_active_per_owner ON public.patient_invites USING btree (owner_id) WHERE (active = true); - --- --- Name: patient_invites_owner_id_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX patient_invites_owner_id_idx ON public.patient_invites USING btree (owner_id); - --- --- Name: patient_invites_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX patient_invites_tenant_idx ON public.patient_invites USING btree (tenant_id); - --- --- Name: patient_invites_token_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX patient_invites_token_idx ON public.patient_invites USING btree (token); - --- --- Name: patient_patient_tag_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX patient_patient_tag_tenant_idx ON public.patient_patient_tag USING btree (tenant_id); - --- --- Name: patient_tags_owner_name_uq; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE UNIQUE INDEX patient_tags_owner_name_uq ON public.patient_tags USING btree (owner_id, lower(nome)); - --- --- Name: patient_tags_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX patient_tags_tenant_idx ON public.patient_tags USING btree (tenant_id); +CREATE INDEX patients_convenio_id_idx ON public.patients USING btree (convenio_id); --- --- Name: payment_settings_tenant_id_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- +CREATE INDEX patients_etnia_idx ON public.patients USING btree (etnia); + +CREATE INDEX patients_pronomes_idx ON public.patients USING btree (pronomes); CREATE INDEX payment_settings_tenant_id_idx ON public.payment_settings USING btree (tenant_id); - --- --- Name: ppt_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX ppt_owner_idx ON public.patient_patient_tag USING btree (owner_id); - --- --- Name: ppt_patient_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX ppt_patient_idx ON public.patient_patient_tag USING btree (patient_id); - --- --- Name: ppt_tag_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX ppt_tag_idx ON public.patient_patient_tag USING btree (tag_id); - --- --- Name: professional_pricing_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX professional_pricing_tenant_idx ON public.professional_pricing USING btree (tenant_id); - --- --- Name: profiles_work_description_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX profiles_work_description_idx ON public.profiles USING btree (work_description) WHERE (work_description IS NOT NULL); +CREATE INDEX psc_owner_idx ON public.patient_support_contacts USING btree (owner_id); --- --- Name: recurrence_exceptions_rule_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- +CREATE INDEX psc_patient_idx ON public.patient_support_contacts USING btree (patient_id); CREATE INDEX recurrence_exceptions_rule_idx ON public.recurrence_exceptions USING btree (recurrence_id); - --- --- Name: recurrence_exceptions_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX recurrence_exceptions_tenant_idx ON public.recurrence_exceptions USING btree (tenant_id); - --- --- Name: recurrence_rule_services_rule_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX recurrence_rule_services_rule_idx ON public.recurrence_rule_services USING btree (rule_id); - --- --- Name: recurrence_rule_services_service_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX recurrence_rule_services_service_idx ON public.recurrence_rule_services USING btree (service_id); - --- --- Name: recurrence_rules_active_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX recurrence_rules_active_idx ON public.recurrence_rules USING btree (owner_id, status) WHERE (status = 'ativo'::text); - --- --- Name: recurrence_rules_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX recurrence_rules_owner_idx ON public.recurrence_rules USING btree (owner_id); - --- --- Name: recurrence_rules_patient_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX recurrence_rules_patient_idx ON public.recurrence_rules USING btree (patient_id); - --- --- Name: recurrence_rules_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX recurrence_rules_tenant_idx ON public.recurrence_rules USING btree (tenant_id); - --- --- Name: saas_doc_votos_doc_id_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX saas_doc_votos_doc_id_idx ON public.saas_doc_votos USING btree (doc_id); - --- --- Name: saas_doc_votos_user_id_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX saas_doc_votos_user_id_idx ON public.saas_doc_votos USING btree (user_id); - --- --- Name: saas_docs_categoria_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX saas_docs_categoria_idx ON public.saas_docs USING btree (categoria); - --- --- Name: saas_docs_exibir_no_faq_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX saas_docs_exibir_no_faq_idx ON public.saas_docs USING btree (exibir_no_faq) WHERE (exibir_no_faq = true); - --- --- Name: saas_docs_path_ativo_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX saas_docs_path_ativo_idx ON public.saas_docs USING btree (pagina_path, ativo); - --- --- Name: saas_faq_ativo_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX saas_faq_ativo_idx ON public.saas_faq USING btree (ativo); - --- --- Name: saas_faq_categoria_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX saas_faq_categoria_idx ON public.saas_faq USING btree (categoria); - --- --- Name: saas_faq_fts_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX saas_faq_fts_idx ON public.saas_faq USING gin (to_tsvector('portuguese'::regconfig, ((COALESCE(pergunta, ''::text) || ' '::text) || COALESCE(conteudo, ''::text)))); - --- --- Name: saas_faq_itens_ativo_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX saas_faq_itens_ativo_idx ON public.saas_faq_itens USING btree (ativo); - --- --- Name: saas_faq_itens_doc_id_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX saas_faq_itens_doc_id_idx ON public.saas_faq_itens USING btree (doc_id); - --- --- Name: saas_faq_pagina_path_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX saas_faq_pagina_path_idx ON public.saas_faq USING btree (pagina_path); - --- --- Name: saas_faq_publico_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX saas_faq_publico_idx ON public.saas_faq USING btree (publico); - --- --- Name: saas_faq_votos_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX saas_faq_votos_idx ON public.saas_faq USING btree (votos DESC); - --- --- Name: services_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX services_owner_idx ON public.services USING btree (owner_id); - --- --- Name: services_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX services_tenant_idx ON public.services USING btree (tenant_id); - --- --- Name: sint_personal_created_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX sint_personal_created_idx ON public.subscription_intents_personal USING btree (created_at DESC); - --- --- Name: sint_personal_status_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX sint_personal_status_idx ON public.subscription_intents_personal USING btree (status); - --- --- Name: sint_tenant_created_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX sint_tenant_created_idx ON public.subscription_intents_tenant USING btree (created_at DESC); - --- --- Name: sint_tenant_status_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX sint_tenant_status_idx ON public.subscription_intents_tenant USING btree (status); - --- --- Name: sint_tenant_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX sint_tenant_tenant_idx ON public.subscription_intents_tenant USING btree (tenant_id); - --- --- Name: subscription_events_created_at_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX subscription_events_created_at_idx ON public.subscription_events USING btree (created_at DESC); - --- --- Name: subscription_events_owner_ref_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX subscription_events_owner_ref_idx ON public.subscription_events USING btree (owner_type, owner_ref); - --- --- Name: subscription_events_sub_created_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX subscription_events_sub_created_idx ON public.subscription_events USING btree (subscription_id, created_at DESC); - --- --- Name: subscription_events_subscription_id_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX subscription_events_subscription_id_idx ON public.subscription_events USING btree (subscription_id); - --- --- Name: subscriptions_one_active_per_tenant; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE UNIQUE INDEX subscriptions_one_active_per_tenant ON public.subscriptions USING btree (tenant_id) WHERE (status = 'active'::text); - --- --- Name: subscriptions_one_active_per_user; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE UNIQUE INDEX subscriptions_one_active_per_user ON public.subscriptions USING btree (user_id) WHERE (status = 'active'::text); - --- --- Name: subscriptions_one_active_per_user_personal; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE UNIQUE INDEX subscriptions_one_active_per_user_personal ON public.subscriptions USING btree (user_id) WHERE ((tenant_id IS NULL) AND (status = 'active'::text)); - --- --- Name: subscriptions_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX subscriptions_owner_idx ON public.subscriptions USING btree (user_id); - --- --- Name: subscriptions_plan_key_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX subscriptions_plan_key_idx ON public.subscriptions USING btree (plan_key); - --- --- Name: subscriptions_status_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX subscriptions_status_idx ON public.subscriptions USING btree (status); - --- --- Name: subscriptions_tenant_id_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX subscriptions_tenant_id_idx ON public.subscriptions USING btree (tenant_id); - --- --- Name: subscriptions_tenant_period_end_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX subscriptions_tenant_period_end_idx ON public.subscriptions USING btree (tenant_id, current_period_end); - --- --- Name: subscriptions_tenant_status_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX subscriptions_tenant_status_idx ON public.subscriptions USING btree (tenant_id, status); - --- --- Name: subscriptions_user_status_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX subscriptions_user_status_idx ON public.subscriptions USING btree (user_id, status, created_at DESC); - --- --- Name: support_sessions_expires_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX support_sessions_expires_idx ON public.support_sessions USING btree (expires_at); - --- --- Name: support_sessions_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX support_sessions_tenant_idx ON public.support_sessions USING btree (tenant_id); - --- --- Name: support_sessions_token_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX support_sessions_token_idx ON public.support_sessions USING btree (token); - --- --- Name: tenant_members_tenant_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX tenant_members_tenant_idx ON public.tenant_members USING btree (tenant_id); - --- --- Name: tenant_members_user_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX tenant_members_user_idx ON public.tenant_members USING btree (user_id); - --- --- Name: tenant_modules_owner_idx; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE INDEX tenant_modules_owner_idx ON public.tenant_modules USING btree (owner_id); - --- --- Name: unique_member_per_tenant; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE UNIQUE INDEX unique_member_per_tenant ON public.tenant_members USING btree (tenant_id, user_id); - --- --- Name: uq_patients_tenant_user; Type: INDEX; Schema: public; Owner: supabase_admin --- +CREATE UNIQUE INDEX uq_patient_contacts_primario ON public.patient_contacts USING btree (patient_id) WHERE ((is_primario = true) AND (ativo = true)); CREATE UNIQUE INDEX uq_patients_tenant_user ON public.patients USING btree (tenant_id, user_id) WHERE (user_id IS NOT NULL); - --- --- Name: uq_plan_price_active; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE UNIQUE INDEX uq_plan_price_active ON public.plan_prices USING btree (plan_id, "interval", currency) WHERE ((is_active = true) AND (active_to IS NULL)); - --- --- Name: uq_plan_prices_active; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE UNIQUE INDEX uq_plan_prices_active ON public.plan_prices USING btree (plan_id, "interval") WHERE (is_active = true); - --- --- Name: uq_subscriptions_active_by_tenant; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE UNIQUE INDEX uq_subscriptions_active_by_tenant ON public.subscriptions USING btree (tenant_id) WHERE ((tenant_id IS NOT NULL) AND (status = 'active'::text)); - --- --- Name: uq_subscriptions_active_personal_by_user; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE UNIQUE INDEX uq_subscriptions_active_personal_by_user ON public.subscriptions USING btree (user_id) WHERE ((tenant_id IS NULL) AND (status = 'active'::text)); - --- --- Name: uq_tenant_invites_pending; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE UNIQUE INDEX uq_tenant_invites_pending ON public.tenant_invites USING btree (tenant_id, lower(email), role) WHERE ((accepted_at IS NULL) AND (revoked_at IS NULL)); - --- --- Name: uq_tenant_members_tenant_user; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE UNIQUE INDEX uq_tenant_members_tenant_user ON public.tenant_members USING btree (tenant_id, user_id); - --- --- Name: ux_subscriptions_active_per_personal_user; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE UNIQUE INDEX ux_subscriptions_active_per_personal_user ON public.subscriptions USING btree (user_id) WHERE ((status = 'active'::text) AND (tenant_id IS NULL)); - --- --- Name: ux_subscriptions_active_per_tenant; Type: INDEX; Schema: public; Owner: supabase_admin --- - CREATE UNIQUE INDEX ux_subscriptions_active_per_tenant ON public.subscriptions USING btree (tenant_id) WHERE ((status = 'active'::text) AND (tenant_id IS NOT NULL)); - - --- --- Name: ix_realtime_subscription_entity; Type: INDEX; Schema: realtime; Owner: supabase_admin --- - -CREATE INDEX ix_realtime_subscription_entity ON realtime.subscription USING btree (entity); - - --- --- Name: messages_inserted_at_topic_index; Type: INDEX; Schema: realtime; Owner: supabase_realtime_admin --- - -CREATE INDEX messages_inserted_at_topic_index ON ONLY realtime.messages USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); - - --- --- Name: messages_2026_03_20_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: supabase_admin --- - -CREATE INDEX messages_2026_03_20_inserted_at_topic_idx ON realtime.messages_2026_03_20 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); - - --- --- Name: messages_2026_03_21_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: supabase_admin --- - -CREATE INDEX messages_2026_03_21_inserted_at_topic_idx ON realtime.messages_2026_03_21 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); - - --- --- Name: messages_2026_03_22_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: supabase_admin --- - -CREATE INDEX messages_2026_03_22_inserted_at_topic_idx ON realtime.messages_2026_03_22 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); - - --- --- Name: messages_2026_03_23_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: supabase_admin --- - -CREATE INDEX messages_2026_03_23_inserted_at_topic_idx ON realtime.messages_2026_03_23 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); - - --- --- Name: messages_2026_03_24_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: supabase_admin --- - -CREATE INDEX messages_2026_03_24_inserted_at_topic_idx ON realtime.messages_2026_03_24 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); - - --- --- Name: messages_2026_03_25_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: supabase_admin --- - -CREATE INDEX messages_2026_03_25_inserted_at_topic_idx ON realtime.messages_2026_03_25 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); - - --- --- Name: messages_2026_03_26_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: supabase_admin --- - -CREATE INDEX messages_2026_03_26_inserted_at_topic_idx ON realtime.messages_2026_03_26 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); - - --- --- Name: subscription_subscription_id_entity_filters_key; Type: INDEX; Schema: realtime; Owner: supabase_admin --- - -CREATE UNIQUE INDEX subscription_subscription_id_entity_filters_key ON realtime.subscription USING btree (subscription_id, entity, filters); - - --- --- Name: bname; Type: INDEX; Schema: storage; Owner: supabase_storage_admin --- - -CREATE UNIQUE INDEX bname ON storage.buckets USING btree (name); - - --- --- Name: bucketid_objname; Type: INDEX; Schema: storage; Owner: supabase_storage_admin --- - -CREATE UNIQUE INDEX bucketid_objname ON storage.objects USING btree (bucket_id, name); - - --- --- Name: buckets_analytics_unique_name_idx; Type: INDEX; Schema: storage; Owner: supabase_storage_admin --- - -CREATE UNIQUE INDEX buckets_analytics_unique_name_idx ON storage.buckets_analytics USING btree (name) WHERE (deleted_at IS NULL); - - --- --- Name: idx_iceberg_namespaces_bucket_id; Type: INDEX; Schema: storage; Owner: supabase_storage_admin --- - -CREATE UNIQUE INDEX idx_iceberg_namespaces_bucket_id ON storage.iceberg_namespaces USING btree (catalog_id, name); - - --- --- Name: idx_iceberg_tables_location; Type: INDEX; Schema: storage; Owner: supabase_storage_admin --- - -CREATE UNIQUE INDEX idx_iceberg_tables_location ON storage.iceberg_tables USING btree (location); - - --- --- Name: idx_iceberg_tables_namespace_id; Type: INDEX; Schema: storage; Owner: supabase_storage_admin --- - -CREATE UNIQUE INDEX idx_iceberg_tables_namespace_id ON storage.iceberg_tables USING btree (catalog_id, namespace_id, name); - - --- --- Name: idx_multipart_uploads_list; Type: INDEX; Schema: storage; Owner: supabase_storage_admin --- - -CREATE INDEX idx_multipart_uploads_list ON storage.s3_multipart_uploads USING btree (bucket_id, key, created_at); - - --- --- Name: idx_objects_bucket_id_name; Type: INDEX; Schema: storage; Owner: supabase_storage_admin --- - -CREATE INDEX idx_objects_bucket_id_name ON storage.objects USING btree (bucket_id, name COLLATE "C"); - - --- --- Name: idx_objects_bucket_id_name_lower; Type: INDEX; Schema: storage; Owner: supabase_storage_admin --- - -CREATE INDEX idx_objects_bucket_id_name_lower ON storage.objects USING btree (bucket_id, lower(name) COLLATE "C"); - - --- --- Name: name_prefix_search; Type: INDEX; Schema: storage; Owner: supabase_storage_admin --- - -CREATE INDEX name_prefix_search ON storage.objects USING btree (name text_pattern_ops); - - --- --- Name: vector_indexes_name_bucket_id_idx; Type: INDEX; Schema: storage; Owner: supabase_storage_admin --- - -CREATE UNIQUE INDEX vector_indexes_name_bucket_id_idx ON storage.vector_indexes USING btree (name, bucket_id); - - --- --- Name: supabase_functions_hooks_h_table_id_h_name_idx; Type: INDEX; Schema: supabase_functions; Owner: supabase_functions_admin --- - -CREATE INDEX supabase_functions_hooks_h_table_id_h_name_idx ON supabase_functions.hooks USING btree (hook_table_id, hook_name); - - --- --- Name: supabase_functions_hooks_request_id_idx; Type: INDEX; Schema: supabase_functions; Owner: supabase_functions_admin --- - -CREATE INDEX supabase_functions_hooks_request_id_idx ON supabase_functions.hooks USING btree (request_id); diff --git a/database-novo/schema/07_foreign_keys/constraints.sql b/database-novo/schema/07_foreign_keys/constraints.sql index defab1e..5fe585c 100644 --- a/database-novo/schema/07_foreign_keys/constraints.sql +++ b/database-novo/schema/07_foreign_keys/constraints.sql @@ -1,2629 +1,828 @@ --- ============================================================================= --- AgenciaPsi — Primary Keys, Unique Constraints, Foreign Keys --- ============================================================================= +-- Constraints (PK, FK, UNIQUE, CHECK) +-- Gerado automaticamente em 2026-04-17T12:23:05.237Z +-- Total: 275 --- ═══ PKs + UNIQUE + CHECK constraints ═══ +ALTER TABLE ONLY public._db_migrations + ADD CONSTRAINT _db_migrations_filename_key UNIQUE (filename); -ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_20 FOR VALUES FROM ('2026-03-20 00:00:00') TO ('2026-03-21 00:00:00'); - - --- --- Name: messages_2026_03_21; Type: TABLE ATTACH; Schema: realtime; Owner: supabase_admin --- - -ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_21 FOR VALUES FROM ('2026-03-21 00:00:00') TO ('2026-03-22 00:00:00'); - - --- --- Name: messages_2026_03_22; Type: TABLE ATTACH; Schema: realtime; Owner: supabase_admin --- - -ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_22 FOR VALUES FROM ('2026-03-22 00:00:00') TO ('2026-03-23 00:00:00'); - - --- --- Name: messages_2026_03_23; Type: TABLE ATTACH; Schema: realtime; Owner: supabase_admin --- - -ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_23 FOR VALUES FROM ('2026-03-23 00:00:00') TO ('2026-03-24 00:00:00'); - - --- --- Name: messages_2026_03_24; Type: TABLE ATTACH; Schema: realtime; Owner: supabase_admin --- - -ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_24 FOR VALUES FROM ('2026-03-24 00:00:00') TO ('2026-03-25 00:00:00'); - - --- --- Name: messages_2026_03_25; Type: TABLE ATTACH; Schema: realtime; Owner: supabase_admin --- - -ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_25 FOR VALUES FROM ('2026-03-25 00:00:00') TO ('2026-03-26 00:00:00'); - - --- --- Name: messages_2026_03_26; Type: TABLE ATTACH; Schema: realtime; Owner: supabase_admin --- - -ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_26 FOR VALUES FROM ('2026-03-26 00:00:00') TO ('2026-03-27 00:00:00'); - - --- --- Name: refresh_tokens id; Type: DEFAULT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.refresh_tokens ALTER COLUMN id SET DEFAULT nextval('auth.refresh_tokens_id_seq'::regclass); - - --- --- Name: agenda_online_slots id; Type: DEFAULT; Schema: public; Owner: supabase_admin --- - -ALTER TABLE ONLY public.agenda_online_slots ALTER COLUMN id SET DEFAULT nextval('public.agenda_online_slots_id_seq'::regclass); - - --- --- Name: hooks id; Type: DEFAULT; Schema: supabase_functions; Owner: supabase_functions_admin --- - -ALTER TABLE ONLY supabase_functions.hooks ALTER COLUMN id SET DEFAULT nextval('supabase_functions.hooks_id_seq'::regclass); - - --- --- Name: extensions extensions_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: supabase_admin --- - -ALTER TABLE ONLY _realtime.extensions - ADD CONSTRAINT extensions_pkey PRIMARY KEY (id); - - --- --- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: supabase_admin --- - -ALTER TABLE ONLY _realtime.schema_migrations - ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); - - --- --- Name: tenants tenants_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: supabase_admin --- - -ALTER TABLE ONLY _realtime.tenants - ADD CONSTRAINT tenants_pkey PRIMARY KEY (id); - - --- --- Name: mfa_amr_claims amr_id_pk; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.mfa_amr_claims - ADD CONSTRAINT amr_id_pk PRIMARY KEY (id); - - --- --- Name: audit_log_entries audit_log_entries_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.audit_log_entries - ADD CONSTRAINT audit_log_entries_pkey PRIMARY KEY (id); - - --- --- Name: flow_state flow_state_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.flow_state - ADD CONSTRAINT flow_state_pkey PRIMARY KEY (id); - - --- --- Name: identities identities_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.identities - ADD CONSTRAINT identities_pkey PRIMARY KEY (id); - - --- --- Name: identities identities_provider_id_provider_unique; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.identities - ADD CONSTRAINT identities_provider_id_provider_unique UNIQUE (provider_id, provider); - - --- --- Name: instances instances_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.instances - ADD CONSTRAINT instances_pkey PRIMARY KEY (id); - - --- --- Name: mfa_amr_claims mfa_amr_claims_session_id_authentication_method_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.mfa_amr_claims - ADD CONSTRAINT mfa_amr_claims_session_id_authentication_method_pkey UNIQUE (session_id, authentication_method); - - --- --- Name: mfa_challenges mfa_challenges_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.mfa_challenges - ADD CONSTRAINT mfa_challenges_pkey PRIMARY KEY (id); - - --- --- Name: mfa_factors mfa_factors_last_challenged_at_key; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.mfa_factors - ADD CONSTRAINT mfa_factors_last_challenged_at_key UNIQUE (last_challenged_at); - - --- --- Name: mfa_factors mfa_factors_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.mfa_factors - ADD CONSTRAINT mfa_factors_pkey PRIMARY KEY (id); - - --- --- Name: oauth_authorizations oauth_authorizations_authorization_code_key; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.oauth_authorizations - ADD CONSTRAINT oauth_authorizations_authorization_code_key UNIQUE (authorization_code); - - --- --- Name: oauth_authorizations oauth_authorizations_authorization_id_key; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.oauth_authorizations - ADD CONSTRAINT oauth_authorizations_authorization_id_key UNIQUE (authorization_id); - - --- --- Name: oauth_authorizations oauth_authorizations_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.oauth_authorizations - ADD CONSTRAINT oauth_authorizations_pkey PRIMARY KEY (id); - - --- --- Name: oauth_client_states oauth_client_states_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.oauth_client_states - ADD CONSTRAINT oauth_client_states_pkey PRIMARY KEY (id); - - --- --- Name: oauth_clients oauth_clients_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.oauth_clients - ADD CONSTRAINT oauth_clients_pkey PRIMARY KEY (id); - - --- --- Name: oauth_consents oauth_consents_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.oauth_consents - ADD CONSTRAINT oauth_consents_pkey PRIMARY KEY (id); - - --- --- Name: oauth_consents oauth_consents_user_client_unique; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.oauth_consents - ADD CONSTRAINT oauth_consents_user_client_unique UNIQUE (user_id, client_id); - - --- --- Name: one_time_tokens one_time_tokens_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.one_time_tokens - ADD CONSTRAINT one_time_tokens_pkey PRIMARY KEY (id); - - --- --- Name: refresh_tokens refresh_tokens_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.refresh_tokens - ADD CONSTRAINT refresh_tokens_pkey PRIMARY KEY (id); - - --- --- Name: refresh_tokens refresh_tokens_token_unique; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.refresh_tokens - ADD CONSTRAINT refresh_tokens_token_unique UNIQUE (token); - - --- --- Name: saml_providers saml_providers_entity_id_key; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.saml_providers - ADD CONSTRAINT saml_providers_entity_id_key UNIQUE (entity_id); - - --- --- Name: saml_providers saml_providers_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.saml_providers - ADD CONSTRAINT saml_providers_pkey PRIMARY KEY (id); - - --- --- Name: saml_relay_states saml_relay_states_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.saml_relay_states - ADD CONSTRAINT saml_relay_states_pkey PRIMARY KEY (id); - - --- --- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.schema_migrations - ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); - - --- --- Name: sessions sessions_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.sessions - ADD CONSTRAINT sessions_pkey PRIMARY KEY (id); - - --- --- Name: sso_domains sso_domains_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.sso_domains - ADD CONSTRAINT sso_domains_pkey PRIMARY KEY (id); - - --- --- Name: sso_providers sso_providers_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.sso_providers - ADD CONSTRAINT sso_providers_pkey PRIMARY KEY (id); - - --- --- Name: users users_phone_key; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.users - ADD CONSTRAINT users_phone_key UNIQUE (phone); - - --- --- Name: users users_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.users - ADD CONSTRAINT users_pkey PRIMARY KEY (id); - - --- --- Name: addon_credits addon_credits_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- +ALTER TABLE ONLY public._db_migrations + ADD CONSTRAINT _db_migrations_pkey PRIMARY KEY (id); ALTER TABLE ONLY public.addon_credits ADD CONSTRAINT addon_credits_pkey PRIMARY KEY (id); - --- --- Name: addon_products addon_products_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.addon_products ADD CONSTRAINT addon_products_pkey PRIMARY KEY (id); - --- --- Name: addon_products addon_products_slug_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.addon_products ADD CONSTRAINT addon_products_slug_key UNIQUE (slug); - --- --- Name: addon_transactions addon_transactions_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.addon_transactions ADD CONSTRAINT addon_transactions_pkey PRIMARY KEY (id); - --- --- Name: agenda_bloqueios agenda_bloqueios_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_bloqueios ADD CONSTRAINT agenda_bloqueios_pkey PRIMARY KEY (id); - --- --- Name: agenda_configuracoes agenda_configuracoes_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_configuracoes ADD CONSTRAINT agenda_configuracoes_pkey PRIMARY KEY (owner_id); - --- --- Name: agenda_eventos agenda_eventos_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_eventos ADD CONSTRAINT agenda_eventos_pkey PRIMARY KEY (id); - --- --- Name: agenda_eventos agenda_eventos_sem_sobreposicao; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_eventos ADD CONSTRAINT agenda_eventos_sem_sobreposicao EXCLUDE USING gist (owner_id WITH =, tstzrange(inicio_em, fim_em, '[)'::text) WITH &&); - --- --- Name: agenda_excecoes agenda_excecoes_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_excecoes ADD CONSTRAINT agenda_excecoes_pkey PRIMARY KEY (id); - --- --- Name: agenda_online_slots agenda_online_slots_owner_id_weekday_time_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_online_slots ADD CONSTRAINT agenda_online_slots_owner_id_weekday_time_key UNIQUE (owner_id, weekday, "time"); - --- --- Name: agenda_online_slots agenda_online_slots_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_online_slots ADD CONSTRAINT agenda_online_slots_pkey PRIMARY KEY (id); - --- --- Name: agenda_regras_semanais agenda_regras_semanais_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_regras_semanais ADD CONSTRAINT agenda_regras_semanais_pkey PRIMARY KEY (id); - --- --- Name: agenda_regras_semanais agenda_regras_semanais_unique; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_regras_semanais ADD CONSTRAINT agenda_regras_semanais_unique UNIQUE (owner_id, dia_semana, hora_inicio, hora_fim, modalidade); - --- --- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_seman_owner_id_dia_semana_hora_inic_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_slots_bloqueados_semanais ADD CONSTRAINT agenda_slots_bloqueados_seman_owner_id_dia_semana_hora_inic_key UNIQUE (owner_id, dia_semana, hora_inicio); - --- --- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_slots_bloqueados_semanais ADD CONSTRAINT agenda_slots_bloqueados_semanais_pkey PRIMARY KEY (id); - --- --- Name: agenda_slots_regras agenda_slots_regras_owner_id_dia_semana_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_slots_regras ADD CONSTRAINT agenda_slots_regras_owner_id_dia_semana_key UNIQUE (owner_id, dia_semana); - --- --- Name: agenda_slots_regras agenda_slots_regras_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_slots_regras ADD CONSTRAINT agenda_slots_regras_pkey PRIMARY KEY (id); - --- --- Name: agendador_configuracoes agendador_configuracoes_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agendador_configuracoes ADD CONSTRAINT agendador_configuracoes_pkey PRIMARY KEY (owner_id); - --- --- Name: agendador_solicitacoes agendador_solicitacoes_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agendador_solicitacoes ADD CONSTRAINT agendador_solicitacoes_pkey PRIMARY KEY (id); - --- --- Name: billing_contracts billing_contracts_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.billing_contracts ADD CONSTRAINT billing_contracts_pkey PRIMARY KEY (id); - --- --- Name: commitment_services commitment_services_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.commitment_services ADD CONSTRAINT commitment_services_pkey PRIMARY KEY (id); - --- --- Name: commitment_time_logs commitment_time_logs_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.commitment_time_logs ADD CONSTRAINT commitment_time_logs_pkey PRIMARY KEY (id); - --- --- Name: company_profiles company_profiles_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.company_profiles ADD CONSTRAINT company_profiles_pkey PRIMARY KEY (id); - --- --- Name: company_profiles company_profiles_tenant_id_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.company_profiles ADD CONSTRAINT company_profiles_tenant_id_key UNIQUE (tenant_id); - --- --- Name: determined_commitment_fields determined_commitment_fields_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.determined_commitment_fields ADD CONSTRAINT determined_commitment_fields_pkey PRIMARY KEY (id); - --- --- Name: determined_commitments determined_commitments_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.determined_commitments ADD CONSTRAINT determined_commitments_pkey PRIMARY KEY (id); - --- --- Name: determined_commitments determined_commitments_tenant_native_key_uq; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.determined_commitments ADD CONSTRAINT determined_commitments_tenant_native_key_uq UNIQUE (tenant_id, native_key); - --- --- Name: dev_user_credentials dev_user_credentials_email_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.dev_user_credentials ADD CONSTRAINT dev_user_credentials_email_key UNIQUE (email); - --- --- Name: dev_user_credentials dev_user_credentials_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.dev_user_credentials ADD CONSTRAINT dev_user_credentials_pkey PRIMARY KEY (id); +ALTER TABLE ONLY public.document_access_logs + ADD CONSTRAINT document_access_logs_pkey PRIMARY KEY (id); --- --- Name: email_layout_config email_layout_config_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- +ALTER TABLE ONLY public.document_generated + ADD CONSTRAINT document_generated_pkey PRIMARY KEY (id); + +ALTER TABLE ONLY public.document_share_links + ADD CONSTRAINT document_share_links_pkey PRIMARY KEY (id); + +ALTER TABLE ONLY public.document_signatures + ADD CONSTRAINT document_signatures_pkey PRIMARY KEY (id); + +ALTER TABLE ONLY public.document_templates + ADD CONSTRAINT document_templates_pkey PRIMARY KEY (id); + +ALTER TABLE ONLY public.documents + ADD CONSTRAINT documents_pkey PRIMARY KEY (id); + +ALTER TABLE ONLY public.document_share_links + ADD CONSTRAINT dsl_token_unique UNIQUE (token); ALTER TABLE ONLY public.email_layout_config ADD CONSTRAINT email_layout_config_pkey PRIMARY KEY (id); - --- --- Name: email_layout_config email_layout_config_tenant_id_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.email_layout_config ADD CONSTRAINT email_layout_config_tenant_id_key UNIQUE (tenant_id); - --- --- Name: email_templates_global email_templates_global_key_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.email_templates_global ADD CONSTRAINT email_templates_global_key_key UNIQUE (key); - --- --- Name: email_templates_global email_templates_global_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.email_templates_global ADD CONSTRAINT email_templates_global_pkey PRIMARY KEY (id); - --- --- Name: email_templates_tenant email_templates_tenant_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.email_templates_tenant ADD CONSTRAINT email_templates_tenant_pkey PRIMARY KEY (id); - --- --- Name: email_templates_tenant email_templates_tenant_tenant_id_owner_id_template_key_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.email_templates_tenant ADD CONSTRAINT email_templates_tenant_tenant_id_owner_id_template_key_key UNIQUE (tenant_id, owner_id, template_key); - --- --- Name: entitlements_invalidation entitlements_invalidation_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.entitlements_invalidation ADD CONSTRAINT entitlements_invalidation_pkey PRIMARY KEY (owner_id); - --- --- Name: features features_key_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.features ADD CONSTRAINT features_key_key UNIQUE (key); - --- --- Name: features features_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.features ADD CONSTRAINT features_pkey PRIMARY KEY (id); - --- --- Name: feriados feriados_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.feriados ADD CONSTRAINT feriados_pkey PRIMARY KEY (id); - --- --- Name: feriados feriados_tenant_id_data_nome_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.feriados ADD CONSTRAINT feriados_tenant_id_data_nome_key UNIQUE (tenant_id, data, nome); - --- --- Name: financial_categories financial_categories_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.financial_categories ADD CONSTRAINT financial_categories_pkey PRIMARY KEY (id); - --- --- Name: financial_exceptions financial_exceptions_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.financial_exceptions ADD CONSTRAINT financial_exceptions_pkey PRIMARY KEY (id); - --- --- Name: financial_records financial_records_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.financial_records ADD CONSTRAINT financial_records_pkey PRIMARY KEY (id); - --- --- Name: global_notices global_notices_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.global_notices ADD CONSTRAINT global_notices_pkey PRIMARY KEY (id); - --- --- Name: insurance_plan_services insurance_plan_services_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.insurance_plan_services ADD CONSTRAINT insurance_plan_services_pkey PRIMARY KEY (id); - --- --- Name: insurance_plans insurance_plans_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.insurance_plans ADD CONSTRAINT insurance_plans_pkey PRIMARY KEY (id); - --- --- Name: login_carousel_slides login_carousel_slides_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.login_carousel_slides ADD CONSTRAINT login_carousel_slides_pkey PRIMARY KEY (id); +ALTER TABLE ONLY public.medicos + ADD CONSTRAINT medicos_crm_owner_unique UNIQUE NULLS NOT DISTINCT (owner_id, crm); --- --- Name: module_features module_features_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- +ALTER TABLE ONLY public.medicos + ADD CONSTRAINT medicos_pkey PRIMARY KEY (id); ALTER TABLE ONLY public.module_features ADD CONSTRAINT module_features_pkey PRIMARY KEY (module_id, feature_id); - --- --- Name: modules modules_key_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.modules ADD CONSTRAINT modules_key_key UNIQUE (key); - --- --- Name: modules modules_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.modules ADD CONSTRAINT modules_pkey PRIMARY KEY (id); - --- --- Name: notice_dismissals notice_dismissals_notice_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.notice_dismissals ADD CONSTRAINT notice_dismissals_notice_id_user_id_key UNIQUE (notice_id, user_id); - --- --- Name: notice_dismissals notice_dismissals_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.notice_dismissals ADD CONSTRAINT notice_dismissals_pkey PRIMARY KEY (id); - --- --- Name: notification_channels notification_channels_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.notification_channels ADD CONSTRAINT notification_channels_pkey PRIMARY KEY (id); - --- --- Name: notification_logs notification_logs_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.notification_logs ADD CONSTRAINT notification_logs_pkey PRIMARY KEY (id); - --- --- Name: notification_preferences notification_preferences_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.notification_preferences ADD CONSTRAINT notification_preferences_pkey PRIMARY KEY (id); - --- --- Name: notification_queue notification_queue_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.notification_queue ADD CONSTRAINT notification_queue_pkey PRIMARY KEY (id); - --- --- Name: notification_schedules notification_schedules_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.notification_schedules ADD CONSTRAINT notification_schedules_pkey PRIMARY KEY (id); - --- --- Name: notification_templates notification_templates_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.notification_templates ADD CONSTRAINT notification_templates_pkey PRIMARY KEY (id); - --- --- Name: notifications notifications_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.notifications ADD CONSTRAINT notifications_pkey PRIMARY KEY (id); - --- --- Name: owner_users owner_users_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.owner_users ADD CONSTRAINT owner_users_pkey PRIMARY KEY (owner_id, user_id); - --- --- Name: patient_discounts patient_discounts_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- +ALTER TABLE ONLY public.patient_contacts + ADD CONSTRAINT patient_contacts_pkey PRIMARY KEY (id); ALTER TABLE ONLY public.patient_discounts ADD CONSTRAINT patient_discounts_pkey PRIMARY KEY (id); - --- --- Name: patient_group_patient patient_group_patient_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.patient_group_patient ADD CONSTRAINT patient_group_patient_pkey PRIMARY KEY (patient_group_id, patient_id); - --- --- Name: patient_groups patient_groups_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.patient_groups ADD CONSTRAINT patient_groups_pkey PRIMARY KEY (id); - --- --- Name: patient_intake_requests patient_intake_requests_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.patient_intake_requests ADD CONSTRAINT patient_intake_requests_pkey PRIMARY KEY (id); - --- --- Name: patient_invites patient_invites_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.patient_invites ADD CONSTRAINT patient_invites_pkey PRIMARY KEY (id); - --- --- Name: patient_invites patient_invites_token_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.patient_invites ADD CONSTRAINT patient_invites_token_key UNIQUE (token); - --- --- Name: patient_patient_tag patient_patient_tag_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.patient_patient_tag ADD CONSTRAINT patient_patient_tag_pkey PRIMARY KEY (patient_id, tag_id); +ALTER TABLE ONLY public.patient_status_history + ADD CONSTRAINT patient_status_history_pkey PRIMARY KEY (id); --- --- Name: patient_tags patient_tags_owner_name_uniq; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- +ALTER TABLE ONLY public.patient_support_contacts + ADD CONSTRAINT patient_support_contacts_pkey PRIMARY KEY (id); ALTER TABLE ONLY public.patient_tags ADD CONSTRAINT patient_tags_owner_name_uniq UNIQUE (owner_id, nome); - --- --- Name: patient_tags patient_tags_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.patient_tags ADD CONSTRAINT patient_tags_pkey PRIMARY KEY (id); - --- --- Name: patients patients_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- +ALTER TABLE ONLY public.patient_timeline + ADD CONSTRAINT patient_timeline_pkey PRIMARY KEY (id); ALTER TABLE ONLY public.patients ADD CONSTRAINT patients_pkey PRIMARY KEY (id); - --- --- Name: payment_settings payment_settings_owner_id_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.payment_settings ADD CONSTRAINT payment_settings_owner_id_key UNIQUE (owner_id); - --- --- Name: payment_settings payment_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.payment_settings ADD CONSTRAINT payment_settings_pkey PRIMARY KEY (id); - --- --- Name: plan_features plan_features_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.plan_features ADD CONSTRAINT plan_features_pkey PRIMARY KEY (plan_id, feature_id); - --- --- Name: plan_prices plan_prices_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.plan_prices ADD CONSTRAINT plan_prices_pkey PRIMARY KEY (id); - --- --- Name: plan_public_bullets plan_public_bullets_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.plan_public_bullets ADD CONSTRAINT plan_public_bullets_pkey PRIMARY KEY (id); - --- --- Name: plan_public plan_public_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.plan_public ADD CONSTRAINT plan_public_pkey PRIMARY KEY (plan_id); - --- --- Name: plans plans_key_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.plans ADD CONSTRAINT plans_key_key UNIQUE (key); - --- --- Name: plans plans_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.plans ADD CONSTRAINT plans_pkey PRIMARY KEY (id); - --- --- Name: professional_pricing professional_pricing_owner_commitment_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.professional_pricing ADD CONSTRAINT professional_pricing_owner_commitment_key UNIQUE (owner_id, determined_commitment_id); - --- --- Name: professional_pricing professional_pricing_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.professional_pricing ADD CONSTRAINT professional_pricing_pkey PRIMARY KEY (id); - --- --- Name: profiles profiles_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.profiles ADD CONSTRAINT profiles_pkey PRIMARY KEY (id); - --- --- Name: recurrence_exceptions recurrence_exceptions_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.recurrence_exceptions ADD CONSTRAINT recurrence_exceptions_pkey PRIMARY KEY (id); - --- --- Name: recurrence_exceptions recurrence_exceptions_unique; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.recurrence_exceptions ADD CONSTRAINT recurrence_exceptions_unique UNIQUE (recurrence_id, original_date); - --- --- Name: recurrence_rule_services recurrence_rule_services_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.recurrence_rule_services ADD CONSTRAINT recurrence_rule_services_pkey PRIMARY KEY (id); - --- --- Name: recurrence_rules recurrence_rules_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.recurrence_rules ADD CONSTRAINT recurrence_rules_pkey PRIMARY KEY (id); - --- --- Name: saas_admins saas_admins_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.saas_admins ADD CONSTRAINT saas_admins_pkey PRIMARY KEY (user_id); - --- --- Name: saas_doc_votos saas_doc_votos_doc_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.saas_doc_votos ADD CONSTRAINT saas_doc_votos_doc_id_user_id_key UNIQUE (doc_id, user_id); - --- --- Name: saas_doc_votos saas_doc_votos_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.saas_doc_votos ADD CONSTRAINT saas_doc_votos_pkey PRIMARY KEY (id); - --- --- Name: saas_docs saas_docs_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.saas_docs ADD CONSTRAINT saas_docs_pkey PRIMARY KEY (id); - --- --- Name: saas_faq_itens saas_faq_itens_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.saas_faq_itens ADD CONSTRAINT saas_faq_itens_pkey PRIMARY KEY (id); - --- --- Name: saas_faq saas_faq_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.saas_faq ADD CONSTRAINT saas_faq_pkey PRIMARY KEY (id); - --- --- Name: services services_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.services ADD CONSTRAINT services_pkey PRIMARY KEY (id); - --- --- Name: subscription_events subscription_events_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.subscription_events ADD CONSTRAINT subscription_events_pkey PRIMARY KEY (id); - --- --- Name: subscription_intents_personal subscription_intents_personal_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.subscription_intents_personal ADD CONSTRAINT subscription_intents_personal_pkey PRIMARY KEY (id); - --- --- Name: subscription_intents_legacy subscription_intents_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.subscription_intents_legacy ADD CONSTRAINT subscription_intents_pkey PRIMARY KEY (id); - --- --- Name: subscription_intents_tenant subscription_intents_tenant_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.subscription_intents_tenant ADD CONSTRAINT subscription_intents_tenant_pkey PRIMARY KEY (id); - --- --- Name: subscriptions subscriptions_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.subscriptions ADD CONSTRAINT subscriptions_pkey PRIMARY KEY (id); - --- --- Name: support_sessions support_sessions_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.support_sessions ADD CONSTRAINT support_sessions_pkey PRIMARY KEY (id); - --- --- Name: support_sessions support_sessions_token_unique; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.support_sessions ADD CONSTRAINT support_sessions_token_unique UNIQUE (token); - --- --- Name: tenant_feature_exceptions_log tenant_feature_exceptions_log_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.tenant_feature_exceptions_log ADD CONSTRAINT tenant_feature_exceptions_log_pkey PRIMARY KEY (id); - --- --- Name: tenant_features tenant_features_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.tenant_features ADD CONSTRAINT tenant_features_pkey PRIMARY KEY (tenant_id, feature_key); - --- --- Name: tenant_invites tenant_invites_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.tenant_invites ADD CONSTRAINT tenant_invites_pkey PRIMARY KEY (id); - --- --- Name: tenant_members tenant_members_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.tenant_members ADD CONSTRAINT tenant_members_pkey PRIMARY KEY (id); - --- --- Name: tenant_members tenant_members_tenant_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.tenant_members ADD CONSTRAINT tenant_members_tenant_id_user_id_key UNIQUE (tenant_id, user_id); - --- --- Name: tenant_modules tenant_modules_owner_id_module_id_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.tenant_modules ADD CONSTRAINT tenant_modules_owner_id_module_id_key UNIQUE (owner_id, module_id); - --- --- Name: tenant_modules tenant_modules_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.tenant_modules ADD CONSTRAINT tenant_modules_pkey PRIMARY KEY (id); - --- --- Name: tenants tenants_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.tenants ADD CONSTRAINT tenants_pkey PRIMARY KEY (id); - --- --- Name: therapist_payout_records therapist_payout_records_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.therapist_payout_records ADD CONSTRAINT therapist_payout_records_pkey PRIMARY KEY (payout_id, financial_record_id); - --- --- Name: therapist_payouts therapist_payouts_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.therapist_payouts ADD CONSTRAINT therapist_payouts_pkey PRIMARY KEY (id); - --- --- Name: addon_credits uq_addon_credits_tenant_type; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- +ALTER TABLE ONLY public.twilio_subaccount_usage + ADD CONSTRAINT twilio_subaccount_usage_pkey PRIMARY KEY (id); ALTER TABLE ONLY public.addon_credits ADD CONSTRAINT uq_addon_credits_tenant_type UNIQUE (tenant_id, addon_type); - --- --- Name: notification_channels uq_channel_per_owner; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.notification_channels ADD CONSTRAINT uq_channel_per_owner UNIQUE NULLS NOT DISTINCT (owner_id, channel, deleted_at); - --- --- Name: notification_preferences uq_notif_prefs_patient_owner; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.notification_preferences ADD CONSTRAINT uq_notif_prefs_patient_owner UNIQUE NULLS NOT DISTINCT (owner_id, patient_id, deleted_at); - --- --- Name: notification_queue uq_notif_queue_idempotency; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.notification_queue ADD CONSTRAINT uq_notif_queue_idempotency UNIQUE (idempotency_key); - --- --- Name: notification_schedules uq_notif_schedule_owner; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.notification_schedules ADD CONSTRAINT uq_notif_schedule_owner UNIQUE NULLS NOT DISTINCT (owner_id, schedule_key, deleted_at); - --- --- Name: notification_templates uq_notif_template_key; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.notification_templates ADD CONSTRAINT uq_notif_template_key UNIQUE NULLS NOT DISTINCT (tenant_id, owner_id, key, deleted_at); - --- --- Name: user_settings user_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.user_settings ADD CONSTRAINT user_settings_pkey PRIMARY KEY (user_id); - --- --- Name: messages messages_pkey; Type: CONSTRAINT; Schema: realtime; Owner: supabase_realtime_admin --- - -ALTER TABLE ONLY realtime.messages - ADD CONSTRAINT messages_pkey PRIMARY KEY (id, inserted_at); - - --- --- Name: messages_2026_03_20 messages_2026_03_20_pkey; Type: CONSTRAINT; Schema: realtime; Owner: supabase_admin --- - -ALTER TABLE ONLY realtime.messages_2026_03_20 - ADD CONSTRAINT messages_2026_03_20_pkey PRIMARY KEY (id, inserted_at); - - --- --- Name: messages_2026_03_21 messages_2026_03_21_pkey; Type: CONSTRAINT; Schema: realtime; Owner: supabase_admin --- - -ALTER TABLE ONLY realtime.messages_2026_03_21 - ADD CONSTRAINT messages_2026_03_21_pkey PRIMARY KEY (id, inserted_at); - - --- --- Name: messages_2026_03_22 messages_2026_03_22_pkey; Type: CONSTRAINT; Schema: realtime; Owner: supabase_admin --- - -ALTER TABLE ONLY realtime.messages_2026_03_22 - ADD CONSTRAINT messages_2026_03_22_pkey PRIMARY KEY (id, inserted_at); - - --- --- Name: messages_2026_03_23 messages_2026_03_23_pkey; Type: CONSTRAINT; Schema: realtime; Owner: supabase_admin --- - -ALTER TABLE ONLY realtime.messages_2026_03_23 - ADD CONSTRAINT messages_2026_03_23_pkey PRIMARY KEY (id, inserted_at); - - --- --- Name: messages_2026_03_24 messages_2026_03_24_pkey; Type: CONSTRAINT; Schema: realtime; Owner: supabase_admin --- - -ALTER TABLE ONLY realtime.messages_2026_03_24 - ADD CONSTRAINT messages_2026_03_24_pkey PRIMARY KEY (id, inserted_at); - - --- --- Name: messages_2026_03_25 messages_2026_03_25_pkey; Type: CONSTRAINT; Schema: realtime; Owner: supabase_admin --- - -ALTER TABLE ONLY realtime.messages_2026_03_25 - ADD CONSTRAINT messages_2026_03_25_pkey PRIMARY KEY (id, inserted_at); - - --- --- Name: messages_2026_03_26 messages_2026_03_26_pkey; Type: CONSTRAINT; Schema: realtime; Owner: supabase_admin --- - -ALTER TABLE ONLY realtime.messages_2026_03_26 - ADD CONSTRAINT messages_2026_03_26_pkey PRIMARY KEY (id, inserted_at); - - --- --- Name: subscription pk_subscription; Type: CONSTRAINT; Schema: realtime; Owner: supabase_admin --- - -ALTER TABLE ONLY realtime.subscription - ADD CONSTRAINT pk_subscription PRIMARY KEY (id); - - --- --- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: realtime; Owner: supabase_admin --- - -ALTER TABLE ONLY realtime.schema_migrations - ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); - - --- --- Name: buckets_analytics buckets_analytics_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.buckets_analytics - ADD CONSTRAINT buckets_analytics_pkey PRIMARY KEY (id); - - --- --- Name: buckets buckets_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.buckets - ADD CONSTRAINT buckets_pkey PRIMARY KEY (id); - - --- --- Name: buckets_vectors buckets_vectors_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.buckets_vectors - ADD CONSTRAINT buckets_vectors_pkey PRIMARY KEY (id); - - --- --- Name: iceberg_namespaces iceberg_namespaces_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.iceberg_namespaces - ADD CONSTRAINT iceberg_namespaces_pkey PRIMARY KEY (id); - - --- --- Name: iceberg_tables iceberg_tables_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.iceberg_tables - ADD CONSTRAINT iceberg_tables_pkey PRIMARY KEY (id); - - --- --- Name: migrations migrations_name_key; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.migrations - ADD CONSTRAINT migrations_name_key UNIQUE (name); - - --- --- Name: migrations migrations_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.migrations - ADD CONSTRAINT migrations_pkey PRIMARY KEY (id); - - --- --- Name: objects objects_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.objects - ADD CONSTRAINT objects_pkey PRIMARY KEY (id); - - --- --- Name: s3_multipart_uploads_parts s3_multipart_uploads_parts_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.s3_multipart_uploads_parts - ADD CONSTRAINT s3_multipart_uploads_parts_pkey PRIMARY KEY (id); - - --- --- Name: s3_multipart_uploads s3_multipart_uploads_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.s3_multipart_uploads - ADD CONSTRAINT s3_multipart_uploads_pkey PRIMARY KEY (id); - - --- --- Name: vector_indexes vector_indexes_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.vector_indexes - ADD CONSTRAINT vector_indexes_pkey PRIMARY KEY (id); - - --- --- Name: hooks hooks_pkey; Type: CONSTRAINT; Schema: supabase_functions; Owner: supabase_functions_admin --- - -ALTER TABLE ONLY supabase_functions.hooks - ADD CONSTRAINT hooks_pkey PRIMARY KEY (id); - - --- --- Name: migrations migrations_pkey; Type: CONSTRAINT; Schema: supabase_functions; Owner: supabase_functions_admin --- - -ALTER TABLE ONLY supabase_functions.migrations - ADD CONSTRAINT migrations_pkey PRIMARY KEY (version); - - --- --- Name: extensions_tenant_external_id_index; Type: INDEX; Schema: _realtime; Owner: supabase_admin --- - - --- ═══ Foreign Keys ═══ - - - --- --- Name: extensions extensions_tenant_external_id_fkey; Type: FK CONSTRAINT; Schema: _realtime; Owner: supabase_admin --- - -ALTER TABLE ONLY _realtime.extensions - ADD CONSTRAINT extensions_tenant_external_id_fkey FOREIGN KEY (tenant_external_id) REFERENCES _realtime.tenants(external_id) ON DELETE CASCADE; - - --- --- Name: identities identities_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.identities - ADD CONSTRAINT identities_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; - - --- --- Name: mfa_amr_claims mfa_amr_claims_session_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.mfa_amr_claims - ADD CONSTRAINT mfa_amr_claims_session_id_fkey FOREIGN KEY (session_id) REFERENCES auth.sessions(id) ON DELETE CASCADE; - - --- --- Name: mfa_challenges mfa_challenges_auth_factor_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.mfa_challenges - ADD CONSTRAINT mfa_challenges_auth_factor_id_fkey FOREIGN KEY (factor_id) REFERENCES auth.mfa_factors(id) ON DELETE CASCADE; - - --- --- Name: mfa_factors mfa_factors_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.mfa_factors - ADD CONSTRAINT mfa_factors_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; - - --- --- Name: oauth_authorizations oauth_authorizations_client_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.oauth_authorizations - ADD CONSTRAINT oauth_authorizations_client_id_fkey FOREIGN KEY (client_id) REFERENCES auth.oauth_clients(id) ON DELETE CASCADE; - - --- --- Name: oauth_authorizations oauth_authorizations_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.oauth_authorizations - ADD CONSTRAINT oauth_authorizations_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; - - --- --- Name: oauth_consents oauth_consents_client_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.oauth_consents - ADD CONSTRAINT oauth_consents_client_id_fkey FOREIGN KEY (client_id) REFERENCES auth.oauth_clients(id) ON DELETE CASCADE; - - --- --- Name: oauth_consents oauth_consents_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.oauth_consents - ADD CONSTRAINT oauth_consents_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; - - --- --- Name: one_time_tokens one_time_tokens_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.one_time_tokens - ADD CONSTRAINT one_time_tokens_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; - - --- --- Name: refresh_tokens refresh_tokens_session_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.refresh_tokens - ADD CONSTRAINT refresh_tokens_session_id_fkey FOREIGN KEY (session_id) REFERENCES auth.sessions(id) ON DELETE CASCADE; - - --- --- Name: saml_providers saml_providers_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.saml_providers - ADD CONSTRAINT saml_providers_sso_provider_id_fkey FOREIGN KEY (sso_provider_id) REFERENCES auth.sso_providers(id) ON DELETE CASCADE; - - --- --- Name: saml_relay_states saml_relay_states_flow_state_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.saml_relay_states - ADD CONSTRAINT saml_relay_states_flow_state_id_fkey FOREIGN KEY (flow_state_id) REFERENCES auth.flow_state(id) ON DELETE CASCADE; - - --- --- Name: saml_relay_states saml_relay_states_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.saml_relay_states - ADD CONSTRAINT saml_relay_states_sso_provider_id_fkey FOREIGN KEY (sso_provider_id) REFERENCES auth.sso_providers(id) ON DELETE CASCADE; - - --- --- Name: sessions sessions_oauth_client_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.sessions - ADD CONSTRAINT sessions_oauth_client_id_fkey FOREIGN KEY (oauth_client_id) REFERENCES auth.oauth_clients(id) ON DELETE CASCADE; - - --- --- Name: sessions sessions_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.sessions - ADD CONSTRAINT sessions_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; - - --- --- Name: sso_domains sso_domains_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.sso_domains - ADD CONSTRAINT sso_domains_sso_provider_id_fkey FOREIGN KEY (sso_provider_id) REFERENCES auth.sso_providers(id) ON DELETE CASCADE; - - --- --- Name: addon_credits addon_credits_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.addon_credits ADD CONSTRAINT addon_credits_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id); - --- --- Name: addon_credits addon_credits_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.addon_credits ADD CONSTRAINT addon_credits_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id); - --- --- Name: addon_transactions addon_transactions_admin_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.addon_transactions ADD CONSTRAINT addon_transactions_admin_user_id_fkey FOREIGN KEY (admin_user_id) REFERENCES auth.users(id); - --- --- Name: addon_transactions addon_transactions_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.addon_transactions ADD CONSTRAINT addon_transactions_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id); - --- --- Name: addon_transactions addon_transactions_product_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.addon_transactions ADD CONSTRAINT addon_transactions_product_id_fkey FOREIGN KEY (product_id) REFERENCES public.addon_products(id); - --- --- Name: addon_transactions addon_transactions_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.addon_transactions ADD CONSTRAINT addon_transactions_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id); - --- --- Name: agenda_bloqueios agenda_bloqueios_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_bloqueios ADD CONSTRAINT agenda_bloqueios_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: agenda_bloqueios agenda_bloqueios_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_bloqueios ADD CONSTRAINT agenda_bloqueios_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE SET NULL; - --- --- Name: agenda_configuracoes agenda_configuracoes_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_configuracoes ADD CONSTRAINT agenda_configuracoes_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; - --- --- Name: agenda_eventos agenda_eventos_billing_contract_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_eventos ADD CONSTRAINT agenda_eventos_billing_contract_id_fkey FOREIGN KEY (billing_contract_id) REFERENCES public.billing_contracts(id) ON DELETE SET NULL; - --- --- Name: agenda_eventos agenda_eventos_determined_commitment_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_eventos ADD CONSTRAINT agenda_eventos_determined_commitment_fk FOREIGN KEY (determined_commitment_id) REFERENCES public.determined_commitments(id) ON DELETE SET NULL; - --- --- Name: agenda_eventos agenda_eventos_insurance_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_eventos ADD CONSTRAINT agenda_eventos_insurance_plan_id_fkey FOREIGN KEY (insurance_plan_id) REFERENCES public.insurance_plans(id) ON DELETE SET NULL; - --- --- Name: agenda_eventos agenda_eventos_insurance_plan_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_eventos ADD CONSTRAINT agenda_eventos_insurance_plan_service_id_fkey FOREIGN KEY (insurance_plan_service_id) REFERENCES public.insurance_plan_services(id) ON DELETE SET NULL; - --- --- Name: agenda_eventos agenda_eventos_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_eventos ADD CONSTRAINT agenda_eventos_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE SET NULL; - --- --- Name: agenda_eventos agenda_eventos_recurrence_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_eventos ADD CONSTRAINT agenda_eventos_recurrence_id_fkey FOREIGN KEY (recurrence_id) REFERENCES public.recurrence_rules(id) ON DELETE SET NULL; - --- --- Name: agenda_eventos agenda_eventos_terapeuta_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_eventos ADD CONSTRAINT agenda_eventos_terapeuta_fk FOREIGN KEY (terapeuta_id) REFERENCES auth.users(id) ON DELETE SET NULL; - --- --- Name: agenda_excecoes agenda_excecoes_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_excecoes ADD CONSTRAINT agenda_excecoes_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; - --- --- Name: agenda_online_slots agenda_online_slots_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_online_slots ADD CONSTRAINT agenda_online_slots_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: agenda_online_slots agenda_online_slots_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_online_slots ADD CONSTRAINT agenda_online_slots_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; - --- --- Name: agenda_regras_semanais agenda_regras_semanais_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_regras_semanais ADD CONSTRAINT agenda_regras_semanais_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; - --- --- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_slots_bloqueados_semanais ADD CONSTRAINT agenda_slots_bloqueados_semanais_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; - --- --- Name: agenda_slots_regras agenda_slots_regras_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agenda_slots_regras ADD CONSTRAINT agenda_slots_regras_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; - --- --- Name: agendador_configuracoes agendador_configuracoes_owner_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agendador_configuracoes ADD CONSTRAINT agendador_configuracoes_owner_fk FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: agendador_configuracoes agendador_configuracoes_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agendador_configuracoes ADD CONSTRAINT agendador_configuracoes_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; - --- --- Name: agendador_solicitacoes agendador_sol_owner_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agendador_solicitacoes ADD CONSTRAINT agendador_sol_owner_fk FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: agendador_solicitacoes agendador_sol_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.agendador_solicitacoes ADD CONSTRAINT agendador_sol_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; - --- --- Name: billing_contracts billing_contracts_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.billing_contracts ADD CONSTRAINT billing_contracts_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: billing_contracts billing_contracts_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.billing_contracts ADD CONSTRAINT billing_contracts_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; - --- --- Name: commitment_services commitment_services_commitment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.commitment_services ADD CONSTRAINT commitment_services_commitment_id_fkey FOREIGN KEY (commitment_id) REFERENCES public.agenda_eventos(id) ON DELETE CASCADE; - --- --- Name: commitment_services commitment_services_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.commitment_services ADD CONSTRAINT commitment_services_service_id_fkey FOREIGN KEY (service_id) REFERENCES public.services(id) ON DELETE RESTRICT; - --- --- Name: commitment_time_logs commitment_time_logs_calendar_event_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.commitment_time_logs ADD CONSTRAINT commitment_time_logs_calendar_event_id_fkey FOREIGN KEY (calendar_event_id) REFERENCES public.agenda_eventos(id) ON DELETE SET NULL; - --- --- Name: commitment_time_logs commitment_time_logs_commitment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.commitment_time_logs ADD CONSTRAINT commitment_time_logs_commitment_id_fkey FOREIGN KEY (commitment_id) REFERENCES public.determined_commitments(id) ON DELETE CASCADE; - --- --- Name: commitment_time_logs commitment_time_logs_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.commitment_time_logs ADD CONSTRAINT commitment_time_logs_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; - --- --- Name: company_profiles company_profiles_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.company_profiles - ADD CONSTRAINT company_profiles_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES auth.users(id) ON DELETE CASCADE; - - --- --- Name: determined_commitment_fields determined_commitment_fields_commitment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- + ADD CONSTRAINT company_profiles_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; ALTER TABLE ONLY public.determined_commitment_fields ADD CONSTRAINT determined_commitment_fields_commitment_id_fkey FOREIGN KEY (commitment_id) REFERENCES public.determined_commitments(id) ON DELETE CASCADE; - --- --- Name: determined_commitment_fields determined_commitment_fields_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.determined_commitment_fields ADD CONSTRAINT determined_commitment_fields_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; - --- --- Name: determined_commitments determined_commitments_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.determined_commitments ADD CONSTRAINT determined_commitments_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; +ALTER TABLE ONLY public.document_access_logs + ADD CONSTRAINT document_access_logs_documento_id_fkey FOREIGN KEY (documento_id) REFERENCES public.documents(id) ON DELETE CASCADE; --- --- Name: email_layout_config email_layout_config_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- +ALTER TABLE ONLY public.document_generated + ADD CONSTRAINT document_generated_documento_id_fkey FOREIGN KEY (documento_id) REFERENCES public.documents(id) ON DELETE SET NULL; + +ALTER TABLE ONLY public.document_generated + ADD CONSTRAINT document_generated_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + +ALTER TABLE ONLY public.document_generated + ADD CONSTRAINT document_generated_template_id_fkey FOREIGN KEY (template_id) REFERENCES public.document_templates(id) ON DELETE RESTRICT; + +ALTER TABLE ONLY public.document_share_links + ADD CONSTRAINT document_share_links_documento_id_fkey FOREIGN KEY (documento_id) REFERENCES public.documents(id) ON DELETE CASCADE; + +ALTER TABLE ONLY public.document_signatures + ADD CONSTRAINT document_signatures_documento_id_fkey FOREIGN KEY (documento_id) REFERENCES public.documents(id) ON DELETE CASCADE; + +ALTER TABLE ONLY public.documents + ADD CONSTRAINT documents_agenda_evento_id_fkey FOREIGN KEY (agenda_evento_id) REFERENCES public.agenda_eventos(id) ON DELETE SET NULL; + +ALTER TABLE ONLY public.documents + ADD CONSTRAINT documents_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; ALTER TABLE ONLY public.email_layout_config ADD CONSTRAINT email_layout_config_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: email_templates_tenant email_templates_tenant_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.email_templates_tenant ADD CONSTRAINT email_templates_tenant_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: email_templates_tenant email_templates_tenant_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.email_templates_tenant ADD CONSTRAINT email_templates_tenant_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: feriados feriados_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.feriados ADD CONSTRAINT feriados_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE SET NULL; - --- --- Name: feriados feriados_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.feriados ADD CONSTRAINT feriados_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; - --- --- Name: financial_categories financial_categories_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.financial_categories ADD CONSTRAINT financial_categories_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: financial_exceptions financial_exceptions_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.financial_exceptions ADD CONSTRAINT financial_exceptions_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: financial_records financial_records_agenda_evento_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.financial_records ADD CONSTRAINT financial_records_agenda_evento_id_fkey FOREIGN KEY (agenda_evento_id) REFERENCES public.agenda_eventos(id) ON DELETE SET NULL; - --- --- Name: financial_records financial_records_category_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.financial_records ADD CONSTRAINT financial_records_category_id_fkey FOREIGN KEY (category_id) REFERENCES public.financial_categories(id) ON DELETE SET NULL; - --- --- Name: financial_records financial_records_insurance_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.financial_records ADD CONSTRAINT financial_records_insurance_plan_id_fkey FOREIGN KEY (insurance_plan_id) REFERENCES public.insurance_plans(id) ON DELETE SET NULL; - --- --- Name: financial_records financial_records_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.financial_records ADD CONSTRAINT financial_records_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE SET NULL; - --- --- Name: financial_records financial_records_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.financial_records ADD CONSTRAINT financial_records_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE SET NULL; - --- --- Name: financial_records financial_records_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.financial_records ADD CONSTRAINT financial_records_user_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: global_notices global_notices_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.global_notices ADD CONSTRAINT global_notices_created_by_fkey FOREIGN KEY (created_by) REFERENCES auth.users(id) ON DELETE SET NULL; - --- --- Name: insurance_plan_services insurance_plan_services_plan_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.insurance_plan_services ADD CONSTRAINT insurance_plan_services_plan_fkey FOREIGN KEY (insurance_plan_id) REFERENCES public.insurance_plans(id) ON DELETE CASCADE; - --- --- Name: insurance_plans insurance_plans_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.insurance_plans ADD CONSTRAINT insurance_plans_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: module_features module_features_feature_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.module_features ADD CONSTRAINT module_features_feature_id_fkey FOREIGN KEY (feature_id) REFERENCES public.features(id) ON DELETE CASCADE; - --- --- Name: module_features module_features_module_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.module_features ADD CONSTRAINT module_features_module_id_fkey FOREIGN KEY (module_id) REFERENCES public.modules(id) ON DELETE CASCADE; - --- --- Name: notice_dismissals notice_dismissals_notice_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.notice_dismissals ADD CONSTRAINT notice_dismissals_notice_id_fkey FOREIGN KEY (notice_id) REFERENCES public.global_notices(id) ON DELETE CASCADE; - --- --- Name: notice_dismissals notice_dismissals_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.notice_dismissals ADD CONSTRAINT notice_dismissals_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: notification_channels notification_channels_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.notification_channels ADD CONSTRAINT notification_channels_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: notification_logs notification_logs_queue_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.notification_logs ADD CONSTRAINT notification_logs_queue_id_fkey FOREIGN KEY (queue_id) REFERENCES public.notification_queue(id) ON DELETE SET NULL; - --- --- Name: notification_schedules notification_schedules_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.notification_schedules ADD CONSTRAINT notification_schedules_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: notification_templates notification_templates_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.notification_templates ADD CONSTRAINT notification_templates_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: notifications notifications_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.notifications ADD CONSTRAINT notifications_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; +ALTER TABLE ONLY public.patient_contacts + ADD CONSTRAINT patient_contacts_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; --- --- Name: patient_discounts patient_discounts_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- +ALTER TABLE ONLY public.patient_contacts + ADD CONSTRAINT patient_contacts_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; ALTER TABLE ONLY public.patient_discounts ADD CONSTRAINT patient_discounts_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: patient_discounts patient_discounts_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.patient_discounts ADD CONSTRAINT patient_discounts_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; - --- --- Name: patient_group_patient patient_group_patient_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.patient_group_patient ADD CONSTRAINT patient_group_patient_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; - --- --- Name: patient_group_patient patient_group_patient_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.patient_group_patient ADD CONSTRAINT patient_group_patient_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; - --- --- Name: patient_groups patient_groups_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.patient_groups ADD CONSTRAINT patient_groups_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; - --- --- Name: patient_intake_requests patient_intake_requests_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.patient_intake_requests ADD CONSTRAINT patient_intake_requests_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; - --- --- Name: patient_invites patient_invites_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.patient_invites ADD CONSTRAINT patient_invites_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; - --- --- Name: patient_patient_tag patient_patient_tag_tag_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.patient_patient_tag ADD CONSTRAINT patient_patient_tag_tag_id_fkey FOREIGN KEY (tag_id) REFERENCES public.patient_tags(id) ON DELETE CASCADE; - --- --- Name: patient_patient_tag patient_patient_tag_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.patient_patient_tag ADD CONSTRAINT patient_patient_tag_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; +ALTER TABLE ONLY public.patient_status_history + ADD CONSTRAINT patient_status_history_alterado_por_fkey FOREIGN KEY (alterado_por) REFERENCES auth.users(id) ON DELETE SET NULL; --- --- Name: patient_tags patient_tags_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- +ALTER TABLE ONLY public.patient_status_history + ADD CONSTRAINT patient_status_history_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + +ALTER TABLE ONLY public.patient_status_history + ADD CONSTRAINT patient_status_history_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + +ALTER TABLE ONLY public.patient_support_contacts + ADD CONSTRAINT patient_support_contacts_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; ALTER TABLE ONLY public.patient_tags ADD CONSTRAINT patient_tags_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; +ALTER TABLE ONLY public.patient_timeline + ADD CONSTRAINT patient_timeline_gerado_por_fkey FOREIGN KEY (gerado_por) REFERENCES auth.users(id) ON DELETE SET NULL; --- --- Name: patients patients_responsible_member_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- +ALTER TABLE ONLY public.patient_timeline + ADD CONSTRAINT patient_timeline_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + +ALTER TABLE ONLY public.patient_timeline + ADD CONSTRAINT patient_timeline_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_convenio_id_fkey FOREIGN KEY (convenio_id) REFERENCES public.insurance_plans(id) ON DELETE SET NULL; ALTER TABLE ONLY public.patients ADD CONSTRAINT patients_responsible_member_id_fkey FOREIGN KEY (responsible_member_id) REFERENCES public.tenant_members(id) ON DELETE RESTRICT; - --- --- Name: patients patients_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_risco_sinalizado_por_fkey FOREIGN KEY (risco_sinalizado_por) REFERENCES auth.users(id) ON DELETE SET NULL; ALTER TABLE ONLY public.patients ADD CONSTRAINT patients_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; - --- --- Name: patients patients_therapist_member_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.patients ADD CONSTRAINT patients_therapist_member_id_fkey FOREIGN KEY (therapist_member_id) REFERENCES public.tenant_members(id); - --- --- Name: patients patients_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.patients ADD CONSTRAINT patients_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE SET NULL; - --- --- Name: payment_settings payment_settings_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.payment_settings ADD CONSTRAINT payment_settings_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: payment_settings payment_settings_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.payment_settings ADD CONSTRAINT payment_settings_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; - --- --- Name: plan_features plan_features_feature_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.plan_features ADD CONSTRAINT plan_features_feature_id_fkey FOREIGN KEY (feature_id) REFERENCES public.features(id) ON DELETE CASCADE; - --- --- Name: plan_features plan_features_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.plan_features ADD CONSTRAINT plan_features_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE CASCADE; - --- --- Name: plan_prices plan_prices_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.plan_prices ADD CONSTRAINT plan_prices_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE CASCADE; - --- --- Name: plan_public_bullets plan_public_bullets_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.plan_public_bullets ADD CONSTRAINT plan_public_bullets_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE CASCADE; - --- --- Name: plan_public plan_public_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.plan_public ADD CONSTRAINT plan_public_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE CASCADE; - --- --- Name: patient_patient_tag ppt_patient_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.patient_patient_tag ADD CONSTRAINT ppt_patient_fk FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; - --- --- Name: patient_patient_tag ppt_tag_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.patient_patient_tag ADD CONSTRAINT ppt_tag_fk FOREIGN KEY (tag_id) REFERENCES public.patient_tags(id) ON DELETE CASCADE; - --- --- Name: professional_pricing professional_pricing_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.professional_pricing ADD CONSTRAINT professional_pricing_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: profiles profiles_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.profiles ADD CONSTRAINT profiles_id_fkey FOREIGN KEY (id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: recurrence_exceptions recurrence_exceptions_agenda_evento_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- +ALTER TABLE ONLY public.profiles + ADD CONSTRAINT profiles_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id); ALTER TABLE ONLY public.recurrence_exceptions ADD CONSTRAINT recurrence_exceptions_agenda_evento_id_fkey FOREIGN KEY (agenda_evento_id) REFERENCES public.agenda_eventos(id) ON DELETE SET NULL; - --- --- Name: recurrence_exceptions recurrence_exceptions_recurrence_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.recurrence_exceptions ADD CONSTRAINT recurrence_exceptions_recurrence_id_fkey FOREIGN KEY (recurrence_id) REFERENCES public.recurrence_rules(id) ON DELETE CASCADE; - --- --- Name: recurrence_rule_services recurrence_rule_services_rule_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.recurrence_rule_services ADD CONSTRAINT recurrence_rule_services_rule_id_fkey FOREIGN KEY (rule_id) REFERENCES public.recurrence_rules(id) ON DELETE CASCADE; - --- --- Name: recurrence_rule_services recurrence_rule_services_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.recurrence_rule_services ADD CONSTRAINT recurrence_rule_services_service_id_fkey FOREIGN KEY (service_id) REFERENCES public.services(id) ON DELETE RESTRICT; - --- --- Name: recurrence_rules recurrence_rules_insurance_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.recurrence_rules ADD CONSTRAINT recurrence_rules_insurance_plan_id_fkey FOREIGN KEY (insurance_plan_id) REFERENCES public.insurance_plans(id) ON DELETE SET NULL; - --- --- Name: recurrence_rules recurrence_rules_insurance_plan_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.recurrence_rules ADD CONSTRAINT recurrence_rules_insurance_plan_service_id_fkey FOREIGN KEY (insurance_plan_service_id) REFERENCES public.insurance_plan_services(id) ON DELETE SET NULL; - --- --- Name: saas_admins saas_admins_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.saas_admins ADD CONSTRAINT saas_admins_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: saas_doc_votos saas_doc_votos_doc_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.saas_doc_votos ADD CONSTRAINT saas_doc_votos_doc_id_fkey FOREIGN KEY (doc_id) REFERENCES public.saas_docs(id) ON DELETE CASCADE; - --- --- Name: saas_doc_votos saas_doc_votos_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.saas_doc_votos ADD CONSTRAINT saas_doc_votos_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: saas_faq_itens saas_faq_itens_doc_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.saas_faq_itens ADD CONSTRAINT saas_faq_itens_doc_id_fkey FOREIGN KEY (doc_id) REFERENCES public.saas_docs(id) ON DELETE CASCADE; - --- --- Name: services services_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.services ADD CONSTRAINT services_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: subscription_intents_personal sint_personal_subscription_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.subscription_intents_personal ADD CONSTRAINT sint_personal_subscription_id_fkey FOREIGN KEY (subscription_id) REFERENCES public.subscriptions(id) ON DELETE SET NULL; - --- --- Name: subscription_intents_tenant sint_tenant_subscription_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.subscription_intents_tenant ADD CONSTRAINT sint_tenant_subscription_id_fkey FOREIGN KEY (subscription_id) REFERENCES public.subscriptions(id) ON DELETE SET NULL; - --- --- Name: subscription_events subscription_events_subscription_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.subscription_events ADD CONSTRAINT subscription_events_subscription_id_fkey FOREIGN KEY (subscription_id) REFERENCES public.subscriptions(id) ON DELETE CASCADE; - --- --- Name: subscription_intents_personal subscription_intents_personal_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.subscription_intents_personal ADD CONSTRAINT subscription_intents_personal_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE RESTRICT; - --- --- Name: subscription_intents_tenant subscription_intents_tenant_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.subscription_intents_tenant ADD CONSTRAINT subscription_intents_tenant_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE RESTRICT; - --- --- Name: subscription_intents_legacy subscription_intents_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.subscription_intents_legacy ADD CONSTRAINT subscription_intents_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE SET NULL; - --- --- Name: subscriptions subscriptions_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.subscriptions ADD CONSTRAINT subscriptions_owner_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: subscriptions subscriptions_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.subscriptions ADD CONSTRAINT subscriptions_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE RESTRICT; - --- --- Name: support_sessions support_sessions_admin_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.support_sessions ADD CONSTRAINT support_sessions_admin_fk FOREIGN KEY (admin_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: support_sessions support_sessions_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.support_sessions ADD CONSTRAINT support_sessions_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; - --- --- Name: tenant_features tenant_features_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.tenant_features ADD CONSTRAINT tenant_features_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; - --- --- Name: tenant_invites tenant_invites_accepted_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.tenant_invites ADD CONSTRAINT tenant_invites_accepted_by_fkey FOREIGN KEY (accepted_by) REFERENCES auth.users(id) ON DELETE SET NULL; - --- --- Name: tenant_invites tenant_invites_invited_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.tenant_invites ADD CONSTRAINT tenant_invites_invited_by_fkey FOREIGN KEY (invited_by) REFERENCES auth.users(id) ON DELETE SET NULL; - --- --- Name: tenant_invites tenant_invites_revoked_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.tenant_invites ADD CONSTRAINT tenant_invites_revoked_by_fkey FOREIGN KEY (revoked_by) REFERENCES auth.users(id) ON DELETE SET NULL; - --- --- Name: tenant_invites tenant_invites_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.tenant_invites ADD CONSTRAINT tenant_invites_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; - --- --- Name: tenant_members tenant_members_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.tenant_members ADD CONSTRAINT tenant_members_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; - --- --- Name: tenant_members tenant_members_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.tenant_members ADD CONSTRAINT tenant_members_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: tenant_modules tenant_modules_module_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.tenant_modules ADD CONSTRAINT tenant_modules_module_id_fkey FOREIGN KEY (module_id) REFERENCES public.modules(id) ON DELETE CASCADE; - --- --- Name: tenant_modules tenant_modules_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.tenant_modules ADD CONSTRAINT tenant_modules_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: therapist_payout_records therapist_payout_records_financial_record_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.therapist_payout_records ADD CONSTRAINT therapist_payout_records_financial_record_id_fkey FOREIGN KEY (financial_record_id) REFERENCES public.financial_records(id) ON DELETE RESTRICT; - --- --- Name: therapist_payout_records therapist_payout_records_payout_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.therapist_payout_records ADD CONSTRAINT therapist_payout_records_payout_id_fkey FOREIGN KEY (payout_id) REFERENCES public.therapist_payouts(id) ON DELETE CASCADE; - --- --- Name: therapist_payouts therapist_payouts_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.therapist_payouts ADD CONSTRAINT therapist_payouts_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; - --- --- Name: therapist_payouts therapist_payouts_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- - ALTER TABLE ONLY public.therapist_payouts ADD CONSTRAINT therapist_payouts_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; - --- --- Name: user_settings user_settings_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin --- +ALTER TABLE ONLY public.twilio_subaccount_usage + ADD CONSTRAINT twilio_subaccount_usage_channel_fk FOREIGN KEY (channel_id) REFERENCES public.notification_channels(id) ON DELETE CASCADE; ALTER TABLE ONLY public.user_settings ADD CONSTRAINT user_settings_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; - - --- --- Name: iceberg_namespaces iceberg_namespaces_catalog_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.iceberg_namespaces - ADD CONSTRAINT iceberg_namespaces_catalog_id_fkey FOREIGN KEY (catalog_id) REFERENCES storage.buckets_analytics(id) ON DELETE CASCADE; - - --- --- Name: iceberg_tables iceberg_tables_catalog_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.iceberg_tables - ADD CONSTRAINT iceberg_tables_catalog_id_fkey FOREIGN KEY (catalog_id) REFERENCES storage.buckets_analytics(id) ON DELETE CASCADE; - - --- --- Name: iceberg_tables iceberg_tables_namespace_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.iceberg_tables - ADD CONSTRAINT iceberg_tables_namespace_id_fkey FOREIGN KEY (namespace_id) REFERENCES storage.iceberg_namespaces(id) ON DELETE CASCADE; - - --- --- Name: objects objects_bucketId_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.objects - ADD CONSTRAINT "objects_bucketId_fkey" FOREIGN KEY (bucket_id) REFERENCES storage.buckets(id); - - --- --- Name: s3_multipart_uploads s3_multipart_uploads_bucket_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.s3_multipart_uploads - ADD CONSTRAINT s3_multipart_uploads_bucket_id_fkey FOREIGN KEY (bucket_id) REFERENCES storage.buckets(id); - - --- --- Name: s3_multipart_uploads_parts s3_multipart_uploads_parts_bucket_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.s3_multipart_uploads_parts - ADD CONSTRAINT s3_multipart_uploads_parts_bucket_id_fkey FOREIGN KEY (bucket_id) REFERENCES storage.buckets(id); - - --- --- Name: s3_multipart_uploads_parts s3_multipart_uploads_parts_upload_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.s3_multipart_uploads_parts - ADD CONSTRAINT s3_multipart_uploads_parts_upload_id_fkey FOREIGN KEY (upload_id) REFERENCES storage.s3_multipart_uploads(id) ON DELETE CASCADE; - - --- --- Name: vector_indexes vector_indexes_bucket_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.vector_indexes - ADD CONSTRAINT vector_indexes_bucket_id_fkey FOREIGN KEY (bucket_id) REFERENCES storage.buckets_vectors(id); - - --- --- Name: audit_log_entries; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin --- - diff --git a/database-novo/schema/08_triggers/triggers.sql b/database-novo/schema/08_triggers/triggers.sql index d015679..88538b6 100644 --- a/database-novo/schema/08_triggers/triggers.sql +++ b/database-novo/schema/08_triggers/triggers.sql @@ -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(); diff --git a/database-novo/schema/09_policies/policies.sql b/database-novo/schema/09_policies/policies.sql index 426edd1..ff69308 100644 --- a/database-novo/schema/09_policies/policies.sql +++ b/database-novo/schema/09_policies/policies.sql @@ -1,2313 +1,761 @@ --- ============================================================================= --- AgenciaPsi — Row Level Security Policies --- ============================================================================= - -ALTER TABLE auth.audit_log_entries ENABLE ROW LEVEL SECURITY; - --- --- Name: flow_state; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE auth.flow_state ENABLE ROW LEVEL SECURITY; - --- --- Name: identities; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE auth.identities ENABLE ROW LEVEL SECURITY; - --- --- Name: instances; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE auth.instances ENABLE ROW LEVEL SECURITY; - --- --- Name: mfa_amr_claims; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE auth.mfa_amr_claims ENABLE ROW LEVEL SECURITY; - --- --- Name: mfa_challenges; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE auth.mfa_challenges ENABLE ROW LEVEL SECURITY; - --- --- Name: mfa_factors; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE auth.mfa_factors ENABLE ROW LEVEL SECURITY; - --- --- Name: one_time_tokens; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE auth.one_time_tokens ENABLE ROW LEVEL SECURITY; - --- --- Name: refresh_tokens; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE auth.refresh_tokens ENABLE ROW LEVEL SECURITY; - --- --- Name: saml_providers; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE auth.saml_providers ENABLE ROW LEVEL SECURITY; - --- --- Name: saml_relay_states; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE auth.saml_relay_states ENABLE ROW LEVEL SECURITY; - --- --- Name: schema_migrations; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE auth.schema_migrations ENABLE ROW LEVEL SECURITY; - --- --- Name: sessions; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE auth.sessions ENABLE ROW LEVEL SECURITY; - --- --- Name: sso_domains; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE auth.sso_domains ENABLE ROW LEVEL SECURITY; - --- --- Name: sso_providers; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE auth.sso_providers ENABLE ROW LEVEL SECURITY; - --- --- Name: users; Type: ROW SECURITY; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE auth.users ENABLE ROW LEVEL SECURITY; - --- --- Name: addon_credits; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- +-- RLS Policies +-- Gerado automaticamente em 2026-04-17T12:23:05.240Z +-- Enable RLS: 88 tabelas +-- Policies: 252 +-- Enable RLS ALTER TABLE public.addon_credits ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.addon_products ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.addon_transactions ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.agenda_bloqueios ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.agenda_configuracoes ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.agenda_eventos ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.agenda_excecoes ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.agenda_online_slots ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.agenda_regras_semanais ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.agenda_slots_bloqueados_semanais ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.agenda_slots_regras ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.agendador_configuracoes ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.agendador_solicitacoes ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.billing_contracts ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.commitment_services ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.commitment_time_logs ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.company_profiles ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.determined_commitment_fields ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.determined_commitments ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.dev_user_credentials ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.document_access_logs ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.document_generated ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.document_share_links ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.document_signatures ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.document_templates ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.documents ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.email_layout_config ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.email_templates_global ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.email_templates_tenant ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.entitlements_invalidation ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.features ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.feriados ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.financial_categories ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.financial_exceptions ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.financial_records ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.global_notices ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.insurance_plan_services ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.insurance_plans ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.login_carousel_slides ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.medicos ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.module_features ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.modules ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.notice_dismissals ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.notification_channels ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.notification_logs ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.notification_preferences ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.notification_queue ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.notification_schedules ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.notification_templates ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.notifications ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.owner_users ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.patient_contacts ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.patient_discounts ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.patient_group_patient ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.patient_groups ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.patient_intake_requests ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.patient_invites ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.patient_patient_tag ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.patient_status_history ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.patient_support_contacts ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.patient_tags ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.patient_timeline ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.patients ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.payment_settings ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.plan_features ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.plans ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.professional_pricing ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.recurrence_exceptions ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.recurrence_rule_services ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.recurrence_rules ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.saas_admins ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.saas_doc_votos ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.saas_docs ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.saas_faq ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.saas_faq_itens ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.services ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.subscription_events ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.subscription_intents_legacy ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.subscriptions ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.support_sessions ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.tenant_members ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.tenant_modules ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.tenants ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.therapist_payout_records ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.therapist_payouts ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.twilio_subaccount_usage ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.user_settings ENABLE ROW LEVEL SECURITY; --- --- Name: addon_credits addon_credits_admin_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - +-- Policies CREATE POLICY addon_credits_admin_select ON public.addon_credits FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 FROM public.saas_admins WHERE (saas_admins.user_id = auth.uid())))); - --- --- Name: addon_credits addon_credits_admin_write; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY addon_credits_admin_write ON public.addon_credits TO authenticated USING ((EXISTS ( SELECT 1 FROM public.saas_admins WHERE (saas_admins.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 FROM public.saas_admins WHERE (saas_admins.user_id = auth.uid())))); - --- --- Name: addon_credits addon_credits_select_own; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY addon_credits_select_own ON public.addon_credits FOR SELECT TO authenticated USING ((public.is_tenant_member(tenant_id) OR (owner_id = auth.uid()))); - --- --- Name: addon_products; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.addon_products ENABLE ROW LEVEL SECURITY; - --- --- Name: addon_products addon_products_admin_all; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY addon_products_admin_all ON public.addon_products TO authenticated USING ((EXISTS ( SELECT 1 FROM public.saas_admins WHERE (saas_admins.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 FROM public.saas_admins WHERE (saas_admins.user_id = auth.uid())))); - --- --- Name: addon_products addon_products_select_authenticated; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY addon_products_select_authenticated ON public.addon_products FOR SELECT TO authenticated USING (((deleted_at IS NULL) AND (is_active = true) AND (is_visible = true))); - --- --- Name: addon_transactions; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.addon_transactions ENABLE ROW LEVEL SECURITY; - --- --- Name: addon_transactions addon_transactions_admin_insert; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY addon_transactions_admin_insert ON public.addon_transactions FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 FROM public.saas_admins WHERE (saas_admins.user_id = auth.uid())))); - --- --- Name: addon_transactions addon_transactions_admin_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY addon_transactions_admin_select ON public.addon_transactions FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 FROM public.saas_admins WHERE (saas_admins.user_id = auth.uid())))); - --- --- Name: addon_transactions addon_transactions_select_own; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY addon_transactions_select_own ON public.addon_transactions FOR SELECT TO authenticated USING ((public.is_tenant_member(tenant_id) OR (owner_id = auth.uid()))); - --- --- Name: agenda_bloqueios; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.agenda_bloqueios ENABLE ROW LEVEL SECURITY; - --- --- Name: agenda_configuracoes; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.agenda_configuracoes ENABLE ROW LEVEL SECURITY; - --- --- Name: agenda_configuracoes agenda_configuracoes_clinic_read; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agenda_configuracoes_clinic_read ON public.agenda_configuracoes FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); - --- --- Name: agenda_configuracoes agenda_configuracoes_clinic_write; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agenda_configuracoes_clinic_write ON public.agenda_configuracoes USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); - --- --- Name: agenda_configuracoes agenda_configuracoes_owner; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agenda_configuracoes_owner ON public.agenda_configuracoes USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: agenda_eventos; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.agenda_eventos ENABLE ROW LEVEL SECURITY; - --- --- Name: agenda_eventos agenda_eventos_delete; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agenda_eventos_delete ON public.agenda_eventos FOR DELETE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.delete'::text))); - --- --- Name: agenda_eventos agenda_eventos_insert; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agenda_eventos_insert ON public.agenda_eventos FOR INSERT WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.create'::text))); - --- --- Name: agenda_eventos agenda_eventos_owner_all; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agenda_eventos_owner_all ON public.agenda_eventos TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: agenda_eventos agenda_eventos_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agenda_eventos_select ON public.agenda_eventos FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); - --- --- Name: agenda_eventos agenda_eventos_update; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agenda_eventos_update ON public.agenda_eventos FOR UPDATE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); - --- --- Name: agenda_excecoes; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.agenda_excecoes ENABLE ROW LEVEL SECURITY; - --- --- Name: agenda_excecoes agenda_excecoes_owner; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agenda_excecoes_owner ON public.agenda_excecoes USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: agenda_excecoes agenda_excecoes_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agenda_excecoes_select ON public.agenda_excecoes FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); - --- --- Name: agenda_excecoes agenda_excecoes_write; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agenda_excecoes_write ON public.agenda_excecoes USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); - --- --- Name: agenda_online_slots; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.agenda_online_slots ENABLE ROW LEVEL SECURITY; - --- --- Name: agenda_online_slots agenda_online_slots_owner; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agenda_online_slots_owner ON public.agenda_online_slots USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: agenda_online_slots agenda_online_slots_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agenda_online_slots_select ON public.agenda_online_slots FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); - --- --- Name: agenda_online_slots agenda_online_slots_write; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agenda_online_slots_write ON public.agenda_online_slots USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); - --- --- Name: agenda_regras_semanais; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.agenda_regras_semanais ENABLE ROW LEVEL SECURITY; - --- --- Name: agenda_regras_semanais agenda_regras_semanais_owner; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agenda_regras_semanais_owner ON public.agenda_regras_semanais USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: agenda_regras_semanais agenda_regras_semanais_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agenda_regras_semanais_select ON public.agenda_regras_semanais FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); - --- --- Name: agenda_regras_semanais agenda_regras_semanais_write; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agenda_regras_semanais_write ON public.agenda_regras_semanais USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); - --- --- Name: agenda_slots_bloqueados_semanais; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.agenda_slots_bloqueados_semanais ENABLE ROW LEVEL SECURITY; - --- --- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agenda_slots_bloqueados_semanais_select ON public.agenda_slots_bloqueados_semanais FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); - --- --- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_write; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agenda_slots_bloqueados_semanais_write ON public.agenda_slots_bloqueados_semanais USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); - --- --- Name: agenda_slots_regras; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.agenda_slots_regras ENABLE ROW LEVEL SECURITY; - --- --- Name: agenda_slots_regras agenda_slots_regras_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agenda_slots_regras_select ON public.agenda_slots_regras FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); - --- --- Name: agenda_slots_regras agenda_slots_regras_write; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agenda_slots_regras_write ON public.agenda_slots_regras USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); - --- --- Name: agendador_configuracoes agendador_cfg_public_read; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agendador_cfg_public_read ON public.agendador_configuracoes FOR SELECT TO anon USING (((ativo = true) AND (link_slug IS NOT NULL))); - --- --- Name: agendador_configuracoes agendador_cfg_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agendador_cfg_select ON public.agendador_configuracoes FOR SELECT USING ((auth.uid() = owner_id)); - --- --- Name: agendador_configuracoes agendador_cfg_write; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agendador_cfg_write ON public.agendador_configuracoes USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); - --- --- Name: agendador_configuracoes; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.agendador_configuracoes ENABLE ROW LEVEL SECURITY; - --- --- Name: agendador_solicitacoes agendador_sol_owner_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agendador_sol_owner_select ON public.agendador_solicitacoes FOR SELECT USING ((auth.uid() = owner_id)); - --- --- Name: agendador_solicitacoes agendador_sol_owner_write; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agendador_sol_owner_write ON public.agendador_solicitacoes USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); - --- --- Name: agendador_solicitacoes agendador_sol_patient_read; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agendador_sol_patient_read ON public.agendador_solicitacoes FOR SELECT TO authenticated USING (((auth.uid() = user_id) OR (auth.uid() = owner_id))); - --- --- Name: agendador_solicitacoes agendador_sol_public_insert; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY agendador_sol_public_insert ON public.agendador_solicitacoes FOR INSERT TO anon WITH CHECK (true); - --- --- Name: agendador_solicitacoes; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.agendador_solicitacoes ENABLE ROW LEVEL SECURITY; - --- --- Name: billing_contracts; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.billing_contracts ENABLE ROW LEVEL SECURITY; - --- --- Name: billing_contracts billing_contracts: owner full access; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "billing_contracts: owner full access" ON public.billing_contracts USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: agenda_bloqueios bloqueios_delete; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY bloqueios_delete ON public.agenda_bloqueios FOR DELETE TO authenticated USING ((owner_id = auth.uid())); - --- --- Name: agenda_bloqueios bloqueios_insert; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY bloqueios_insert ON public.agenda_bloqueios FOR INSERT TO authenticated WITH CHECK ((owner_id = auth.uid())); - --- --- Name: agenda_bloqueios bloqueios_select_clinic; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY bloqueios_select_clinic ON public.agenda_bloqueios FOR SELECT TO authenticated USING ((tenant_id IN ( SELECT tenant_members.tenant_id FROM public.tenant_members WHERE ((tenant_members.user_id = auth.uid()) AND (tenant_members.role = ANY (ARRAY['admin'::text, 'clinic_admin'::text, 'tenant_admin'::text, 'secretary'::text])))))); - --- --- Name: agenda_bloqueios bloqueios_select_own; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY bloqueios_select_own ON public.agenda_bloqueios FOR SELECT TO authenticated USING ((owner_id = auth.uid())); - --- --- Name: agenda_bloqueios bloqueios_update; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY bloqueios_update ON public.agenda_bloqueios FOR UPDATE TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: saas_docs clinic_admin_read_all_docs; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY clinic_admin_read_all_docs ON public.saas_docs FOR SELECT TO authenticated USING (((ativo = true) AND (EXISTS ( SELECT 1 FROM public.profiles WHERE ((profiles.id = auth.uid()) AND (profiles.role = ANY (ARRAY['clinic_admin'::text, 'tenant_admin'::text]))))))); - --- --- Name: commitment_services; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.commitment_services ENABLE ROW LEVEL SECURITY; - --- --- Name: commitment_services commitment_services: owner full access; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "commitment_services: owner full access" ON public.commitment_services USING ((EXISTS ( SELECT 1 FROM public.services s WHERE ((s.id = commitment_services.service_id) AND (s.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 FROM public.services s WHERE ((s.id = commitment_services.service_id) AND (s.owner_id = auth.uid()))))); +CREATE POLICY company_profiles_delete ON public.company_profiles FOR DELETE USING ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); --- --- Name: commitment_time_logs; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- +CREATE POLICY company_profiles_insert ON public.company_profiles FOR INSERT WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); -ALTER TABLE public.commitment_time_logs ENABLE ROW LEVEL SECURITY; +CREATE POLICY company_profiles_select ON public.company_profiles FOR SELECT USING ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); --- --- Name: company_profiles; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.company_profiles ENABLE ROW LEVEL SECURITY; - --- --- Name: company_profiles company_profiles_delete; Type: POLICY; Schema: public; Owner: supabase_admin --- - -CREATE POLICY company_profiles_delete ON public.company_profiles FOR DELETE USING ((tenant_id = auth.uid())); - - --- --- Name: company_profiles company_profiles_insert; Type: POLICY; Schema: public; Owner: supabase_admin --- - -CREATE POLICY company_profiles_insert ON public.company_profiles FOR INSERT WITH CHECK ((tenant_id = auth.uid())); - - --- --- Name: company_profiles company_profiles_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - -CREATE POLICY company_profiles_select ON public.company_profiles FOR SELECT USING ((tenant_id = auth.uid())); - - --- --- Name: company_profiles company_profiles_update; Type: POLICY; Schema: public; Owner: supabase_admin --- - -CREATE POLICY company_profiles_update ON public.company_profiles FOR UPDATE USING ((tenant_id = auth.uid())); - - --- --- Name: commitment_time_logs ctl_delete_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin --- +CREATE POLICY company_profiles_update ON public.company_profiles FOR UPDATE USING ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); CREATE POLICY ctl_delete_for_active_member ON public.commitment_time_logs FOR DELETE TO authenticated USING ((EXISTS ( SELECT 1 FROM public.tenant_members tm WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); - --- --- Name: commitment_time_logs ctl_insert_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY ctl_insert_for_active_member ON public.commitment_time_logs FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 FROM public.tenant_members tm WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); - --- --- Name: commitment_time_logs ctl_select_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY ctl_select_for_active_member ON public.commitment_time_logs FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 FROM public.tenant_members tm WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); - --- --- Name: commitment_time_logs ctl_update_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY ctl_update_for_active_member ON public.commitment_time_logs FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 FROM public.tenant_members tm WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))) WITH CHECK ((EXISTS ( SELECT 1 FROM public.tenant_members tm WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); +CREATE POLICY "dal: tenant members can insert" ON public.document_access_logs FOR INSERT WITH CHECK (true); --- --- Name: determined_commitments dc_delete_custom_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin --- +CREATE POLICY "dal: tenant members can select" ON public.document_access_logs FOR SELECT USING ((tenant_id IN ( SELECT tm.tenant_id + FROM public.tenant_members tm + WHERE ((tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); CREATE POLICY dc_delete_custom_for_active_member ON public.determined_commitments FOR DELETE TO authenticated USING (((is_native = false) AND (EXISTS ( SELECT 1 FROM public.tenant_members tm WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text)))))); - --- --- Name: determined_commitments dc_insert_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY dc_insert_for_active_member ON public.determined_commitments FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 FROM public.tenant_members tm WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); - --- --- Name: determined_commitments dc_select_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY dc_select_for_active_member ON public.determined_commitments FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 FROM public.tenant_members tm WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); - --- --- Name: determined_commitments dc_update_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY dc_update_for_active_member ON public.determined_commitments FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 FROM public.tenant_members tm WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))) WITH CHECK ((EXISTS ( SELECT 1 FROM public.tenant_members tm WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); - --- --- Name: determined_commitment_fields dcf_delete_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY dcf_delete_for_active_member ON public.determined_commitment_fields FOR DELETE TO authenticated USING ((EXISTS ( SELECT 1 FROM public.tenant_members tm WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); - --- --- Name: determined_commitment_fields dcf_insert_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY dcf_insert_for_active_member ON public.determined_commitment_fields FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 FROM public.tenant_members tm WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); - --- --- Name: determined_commitment_fields dcf_select_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY dcf_select_for_active_member ON public.determined_commitment_fields FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 FROM public.tenant_members tm WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); - --- --- Name: determined_commitment_fields dcf_update_for_active_member; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY dcf_update_for_active_member ON public.determined_commitment_fields FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 FROM public.tenant_members tm WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))) WITH CHECK ((EXISTS ( SELECT 1 FROM public.tenant_members tm WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); - --- --- Name: agenda_bloqueios delete own; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "delete own" ON public.agenda_bloqueios FOR DELETE USING ((owner_id = auth.uid())); - --- --- Name: determined_commitment_fields; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.determined_commitment_fields ENABLE ROW LEVEL SECURITY; - --- --- Name: determined_commitments; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.determined_commitments ENABLE ROW LEVEL SECURITY; - --- --- Name: dev_user_credentials dev_creds_select_saas_admin; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY dev_creds_select_saas_admin ON public.dev_user_credentials FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 FROM public.profiles p WHERE ((p.id = auth.uid()) AND (p.role = 'saas_admin'::text))))); - --- --- Name: dev_user_credentials dev_creds_write_saas_admin; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY dev_creds_write_saas_admin ON public.dev_user_credentials TO authenticated USING ((EXISTS ( SELECT 1 FROM public.profiles p WHERE ((p.id = auth.uid()) AND (p.role = 'saas_admin'::text))))) WITH CHECK ((EXISTS ( SELECT 1 FROM public.profiles p WHERE ((p.id = auth.uid()) AND (p.role = 'saas_admin'::text))))); +CREATE POLICY "dg: generator full access" ON public.document_generated USING ((gerado_por = auth.uid())) WITH CHECK ((gerado_por = auth.uid())); --- --- Name: dev_user_credentials; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- +CREATE POLICY "dg: tenant members can select" ON public.document_generated FOR SELECT USING ((tenant_id IN ( SELECT tm.tenant_id + FROM public.tenant_members tm + WHERE ((tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); -ALTER TABLE public.dev_user_credentials ENABLE ROW LEVEL SECURITY; +CREATE POLICY "documents: owner full access" ON public.documents USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); --- --- Name: email_layout_config; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- +CREATE POLICY "ds: tenant members access" ON public.document_signatures USING ((tenant_id IN ( SELECT tm.tenant_id + FROM public.tenant_members tm + WHERE ((tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))) WITH CHECK ((tenant_id IN ( SELECT tm.tenant_id + FROM public.tenant_members tm + WHERE ((tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); -ALTER TABLE public.email_layout_config ENABLE ROW LEVEL SECURITY; +CREATE POLICY "dsl: creator full access" ON public.document_share_links USING ((criado_por = auth.uid())) WITH CHECK ((criado_por = auth.uid())); --- --- Name: email_templates_global; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- +CREATE POLICY "dsl: public read by token" ON public.document_share_links FOR SELECT USING (((ativo = true) AND (expira_em > now()) AND (usos < usos_max))); -ALTER TABLE public.email_templates_global ENABLE ROW LEVEL SECURITY; +CREATE POLICY "dt: global templates readable by all" ON public.document_templates FOR SELECT USING ((is_global = true)); --- --- Name: email_templates_tenant; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- +CREATE POLICY "dt: owner can delete" ON public.document_templates FOR DELETE USING (((owner_id = auth.uid()) AND (is_global = false))); -ALTER TABLE public.email_templates_tenant ENABLE ROW LEVEL SECURITY; +CREATE POLICY "dt: owner can insert" ON public.document_templates FOR INSERT WITH CHECK (((owner_id = auth.uid()) AND (is_global = false))); --- --- Name: entitlements_invalidation ent_inv_select_own; Type: POLICY; Schema: public; Owner: supabase_admin --- +CREATE POLICY "dt: owner can update" ON public.document_templates FOR UPDATE USING (((owner_id = auth.uid()) AND (is_global = false))) WITH CHECK (((owner_id = auth.uid()) AND (is_global = false))); + +CREATE POLICY "dt: saas admin can delete global" ON public.document_templates FOR DELETE USING (((is_global = true) AND public.is_saas_admin())); + +CREATE POLICY "dt: saas admin can insert global" ON public.document_templates FOR INSERT WITH CHECK (((is_global = true) AND public.is_saas_admin())); + +CREATE POLICY "dt: saas admin can update global" ON public.document_templates FOR UPDATE USING (((is_global = true) AND public.is_saas_admin())) WITH CHECK (((is_global = true) AND public.is_saas_admin())); + +CREATE POLICY "dt: tenant members can select" ON public.document_templates FOR SELECT USING (((is_global = false) AND (tenant_id IN ( SELECT tm.tenant_id + FROM public.tenant_members tm + WHERE ((tm.user_id = auth.uid()) AND (tm.status = 'active'::text)))))); CREATE POLICY ent_inv_select_own ON public.entitlements_invalidation FOR SELECT USING (((owner_id = auth.uid()) OR public.is_saas_admin())); - --- --- Name: entitlements_invalidation ent_inv_update_saas; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY ent_inv_update_saas ON public.entitlements_invalidation FOR UPDATE USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); - --- --- Name: entitlements_invalidation ent_inv_write_saas; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY ent_inv_write_saas ON public.entitlements_invalidation FOR INSERT WITH CHECK (public.is_saas_admin()); - --- --- Name: entitlements_invalidation; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.entitlements_invalidation ENABLE ROW LEVEL SECURITY; - --- --- Name: saas_faq faq_admin_write; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY faq_admin_write ON public.saas_faq TO authenticated USING ((EXISTS ( SELECT 1 FROM public.profiles WHERE ((profiles.id = auth.uid()) AND (profiles.role = ANY (ARRAY['saas_admin'::text, 'tenant_admin'::text, 'clinic_admin'::text])))))); - --- --- Name: saas_faq faq_auth_read; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY faq_auth_read ON public.saas_faq FOR SELECT TO authenticated USING ((ativo = true)); - --- --- Name: saas_faq_itens faq_itens_admin_write; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY faq_itens_admin_write ON public.saas_faq_itens TO authenticated USING ((EXISTS ( SELECT 1 FROM public.profiles WHERE ((profiles.id = auth.uid()) AND (profiles.role = ANY (ARRAY['saas_admin'::text, 'tenant_admin'::text, 'clinic_admin'::text])))))); - --- --- Name: saas_faq_itens faq_itens_auth_read; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY faq_itens_auth_read ON public.saas_faq_itens FOR SELECT TO authenticated USING (((ativo = true) AND (EXISTS ( SELECT 1 FROM public.saas_docs d WHERE ((d.id = saas_faq_itens.doc_id) AND (d.ativo = true)))))); - --- --- Name: saas_faq faq_public_read; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY faq_public_read ON public.saas_faq FOR SELECT USING (((publico = true) AND (ativo = true))); - --- --- Name: features; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.features ENABLE ROW LEVEL SECURITY; - --- --- Name: features features_read_authenticated; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY features_read_authenticated ON public.features FOR SELECT TO authenticated USING (true); - --- --- Name: features features_write_saas_admin; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY features_write_saas_admin ON public.features TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); - --- --- Name: feriados; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.feriados ENABLE ROW LEVEL SECURITY; - --- --- Name: feriados feriados_delete; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY feriados_delete ON public.feriados FOR DELETE USING ((owner_id = auth.uid())); - --- --- Name: feriados feriados_global_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY feriados_global_select ON public.feriados FOR SELECT USING ((tenant_id IS NULL)); - --- --- Name: feriados feriados_insert; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY feriados_insert ON public.feriados FOR INSERT WITH CHECK ((tenant_id IN ( SELECT tenant_members.tenant_id FROM public.tenant_members WHERE (tenant_members.user_id = auth.uid())))); - --- --- Name: feriados feriados_saas_delete; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY feriados_saas_delete ON public.feriados FOR DELETE USING ((EXISTS ( SELECT 1 FROM public.saas_admins WHERE (saas_admins.user_id = auth.uid())))); - --- --- Name: feriados feriados_saas_insert; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY feriados_saas_insert ON public.feriados FOR INSERT WITH CHECK ((EXISTS ( SELECT 1 FROM public.saas_admins WHERE (saas_admins.user_id = auth.uid())))); - --- --- Name: feriados feriados_saas_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY feriados_saas_select ON public.feriados FOR SELECT USING ((EXISTS ( SELECT 1 FROM public.saas_admins WHERE (saas_admins.user_id = auth.uid())))); - --- --- Name: feriados feriados_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY feriados_select ON public.feriados FOR SELECT USING ((tenant_id IN ( SELECT tenant_members.tenant_id FROM public.tenant_members WHERE (tenant_members.user_id = auth.uid())))); - --- --- Name: financial_categories; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.financial_categories ENABLE ROW LEVEL SECURITY; - --- --- Name: financial_categories financial_categories_self; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY financial_categories_self ON public.financial_categories USING ((auth.uid() = user_id)) WITH CHECK ((auth.uid() = user_id)); - --- --- Name: financial_exceptions; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.financial_exceptions ENABLE ROW LEVEL SECURITY; - --- --- Name: financial_exceptions financial_exceptions: owner full access; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "financial_exceptions: owner full access" ON public.financial_exceptions USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: financial_exceptions financial_exceptions: tenant members read clinic rules; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "financial_exceptions: tenant members read clinic rules" ON public.financial_exceptions FOR SELECT USING (((owner_id IS NULL) AND (EXISTS ( SELECT 1 FROM public.owner_users ou WHERE ((ou.owner_id = financial_exceptions.tenant_id) AND (ou.user_id = auth.uid())))))); - --- --- Name: financial_records; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.financial_records ENABLE ROW LEVEL SECURITY; - --- --- Name: financial_records financial_records_self; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY financial_records_self ON public.financial_records USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); - --- --- Name: financial_records financial_records_tenant_admin; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY financial_records_tenant_admin ON public.financial_records FOR SELECT USING (((tenant_id IS NOT NULL) AND public.is_tenant_admin(tenant_id))); - --- --- Name: financial_records financial_records_tenant_member_read; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY financial_records_tenant_member_read ON public.financial_records FOR SELECT USING (((tenant_id IS NOT NULL) AND (EXISTS ( SELECT 1 FROM public.tenant_members tm WHERE ((tm.tenant_id = financial_records.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text)))))); - --- --- Name: email_templates_global global templates readable by authenticated; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "global templates readable by authenticated" ON public.email_templates_global FOR SELECT USING ((auth.role() = 'authenticated'::text)); - --- --- Name: global_notices; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.global_notices ENABLE ROW LEVEL SECURITY; - --- --- Name: global_notices global_notices_saas_all; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY global_notices_saas_all ON public.global_notices TO authenticated USING ((EXISTS ( SELECT 1 FROM public.saas_admins WHERE (saas_admins.user_id = auth.uid())))); - --- --- Name: global_notices global_notices_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY global_notices_select ON public.global_notices FOR SELECT TO authenticated USING ((is_active = true)); - --- --- Name: agenda_bloqueios insert own; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "insert own" ON public.agenda_bloqueios FOR INSERT WITH CHECK ((owner_id = auth.uid())); - --- --- Name: insurance_plan_services; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.insurance_plan_services ENABLE ROW LEVEL SECURITY; - --- --- Name: insurance_plan_services insurance_plan_services_owner; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY insurance_plan_services_owner ON public.insurance_plan_services USING ((EXISTS ( SELECT 1 FROM public.insurance_plans ip WHERE ((ip.id = insurance_plan_services.insurance_plan_id) AND (ip.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 FROM public.insurance_plans ip WHERE ((ip.id = insurance_plan_services.insurance_plan_id) AND (ip.owner_id = auth.uid()))))); - --- --- Name: insurance_plans; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.insurance_plans ENABLE ROW LEVEL SECURITY; - --- --- Name: insurance_plans insurance_plans: owner full access; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "insurance_plans: owner full access" ON public.insurance_plans USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: login_carousel_slides; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.login_carousel_slides ENABLE ROW LEVEL SECURITY; - --- --- Name: module_features; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.module_features ENABLE ROW LEVEL SECURITY; - --- --- Name: module_features module_features_read_authenticated; Type: POLICY; Schema: public; Owner: supabase_admin --- +CREATE POLICY "medicos: owner full access" ON public.medicos USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); CREATE POLICY module_features_read_authenticated ON public.module_features FOR SELECT TO authenticated USING (true); - --- --- Name: module_features module_features_write_saas_admin; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY module_features_write_saas_admin ON public.module_features TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); - --- --- Name: modules; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.modules ENABLE ROW LEVEL SECURITY; - --- --- Name: modules modules_read_authenticated; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY modules_read_authenticated ON public.modules FOR SELECT TO authenticated USING (true); - --- --- Name: modules modules_write_saas_admin; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY modules_write_saas_admin ON public.modules TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); - --- --- Name: notice_dismissals; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.notice_dismissals ENABLE ROW LEVEL SECURITY; - --- --- Name: notice_dismissals notice_dismissals_own; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY notice_dismissals_own ON public.notice_dismissals TO authenticated USING ((user_id = auth.uid())) WITH CHECK ((user_id = auth.uid())); - --- --- Name: notification_logs notif_logs_owner; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY notif_logs_owner ON public.notification_logs FOR SELECT USING ((owner_id = auth.uid())); - --- --- Name: notification_preferences notif_prefs_owner; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY notif_prefs_owner ON public.notification_preferences USING (((owner_id = auth.uid()) AND (deleted_at IS NULL))) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: notification_queue notif_queue_owner; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY notif_queue_owner ON public.notification_queue FOR SELECT USING ((owner_id = auth.uid())); - --- --- Name: notification_schedules notif_schedules_owner; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY notif_schedules_owner ON public.notification_schedules USING (((owner_id = auth.uid()) AND (deleted_at IS NULL))) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: notification_templates notif_templates_admin_all; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY notif_templates_admin_all ON public.notification_templates TO authenticated USING ((EXISTS ( SELECT 1 FROM public.saas_admins WHERE (saas_admins.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 FROM public.saas_admins WHERE (saas_admins.user_id = auth.uid())))); - --- --- Name: notification_templates notif_templates_read_global; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY notif_templates_read_global ON public.notification_templates FOR SELECT TO authenticated USING (((deleted_at IS NULL) AND (((tenant_id IS NULL) AND (is_default = true)) OR (owner_id = auth.uid()) OR public.is_tenant_member(tenant_id)))); - --- --- Name: notification_templates notif_templates_write_owner; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY notif_templates_write_owner ON public.notification_templates TO authenticated USING (((owner_id = auth.uid()) OR public.is_tenant_member(tenant_id))) WITH CHECK (((owner_id = auth.uid()) OR public.is_tenant_member(tenant_id))); - --- --- Name: notification_channels; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.notification_channels ENABLE ROW LEVEL SECURITY; - --- --- Name: notification_channels notification_channels_owner; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY notification_channels_owner ON public.notification_channels USING (((owner_id = auth.uid()) AND (deleted_at IS NULL))) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: notification_logs; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.notification_logs ENABLE ROW LEVEL SECURITY; - --- --- Name: notification_preferences; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.notification_preferences ENABLE ROW LEVEL SECURITY; - --- --- Name: notification_queue; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.notification_queue ENABLE ROW LEVEL SECURITY; - --- --- Name: notification_schedules; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.notification_schedules ENABLE ROW LEVEL SECURITY; - --- --- Name: notification_templates; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.notification_templates ENABLE ROW LEVEL SECURITY; - --- --- Name: notifications; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.notifications ENABLE ROW LEVEL SECURITY; - --- --- Name: notifications owner only; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "owner only" ON public.notifications USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: owner_users; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.owner_users ENABLE ROW LEVEL SECURITY; - --- --- Name: owner_users owner_users: user can read own links; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "owner_users: user can read own links" ON public.owner_users FOR SELECT TO authenticated USING ((user_id = auth.uid())); +CREATE POLICY patient_contacts_select ON public.patient_contacts FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); --- --- Name: patient_discounts; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.patient_discounts ENABLE ROW LEVEL SECURITY; - --- --- Name: patient_discounts patient_discounts: owner full access; Type: POLICY; Schema: public; Owner: supabase_admin --- +CREATE POLICY patient_contacts_write ON public.patient_contacts USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); CREATE POLICY "patient_discounts: owner full access" ON public.patient_discounts USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: patient_group_patient; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.patient_group_patient ENABLE ROW LEVEL SECURITY; - --- --- Name: patient_group_patient patient_group_patient_owner_all; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patient_group_patient_owner_all ON public.patient_group_patient TO authenticated USING ((EXISTS ( SELECT 1 FROM public.patients p WHERE ((p.id = patient_group_patient.patient_id) AND (p.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 FROM public.patients p WHERE ((p.id = patient_group_patient.patient_id) AND (p.owner_id = auth.uid()))))); - --- --- Name: patient_group_patient patient_group_patient_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patient_group_patient_select ON public.patient_group_patient FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); - --- --- Name: patient_group_patient patient_group_patient_write; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patient_group_patient_write ON public.patient_group_patient USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); - --- --- Name: patient_groups; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.patient_groups ENABLE ROW LEVEL SECURITY; - --- --- Name: patient_groups patient_groups_owner_all; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patient_groups_owner_all ON public.patient_groups TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: patient_groups patient_groups_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patient_groups_select ON public.patient_groups FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); - --- --- Name: patient_groups patient_groups_write; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patient_groups_write ON public.patient_groups USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); - --- --- Name: patient_intake_requests; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.patient_intake_requests ENABLE ROW LEVEL SECURITY; - --- --- Name: patient_intake_requests patient_intake_requests_owner_all; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patient_intake_requests_owner_all ON public.patient_intake_requests TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: patient_intake_requests patient_intake_requests_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patient_intake_requests_select ON public.patient_intake_requests FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); - --- --- Name: patient_intake_requests patient_intake_requests_write; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patient_intake_requests_write ON public.patient_intake_requests USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); - --- --- Name: patient_invites; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.patient_invites ENABLE ROW LEVEL SECURITY; - --- --- Name: patient_invites patient_invites_owner_all; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patient_invites_owner_all ON public.patient_invites TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: patient_invites patient_invites_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patient_invites_select ON public.patient_invites FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); - --- --- Name: patient_invites patient_invites_write; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patient_invites_write ON public.patient_invites USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); - --- --- Name: patient_patient_tag; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.patient_patient_tag ENABLE ROW LEVEL SECURITY; - --- --- Name: patient_patient_tag patient_patient_tag_owner_all; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patient_patient_tag_owner_all ON public.patient_patient_tag TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: patient_patient_tag patient_patient_tag_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patient_patient_tag_select ON public.patient_patient_tag FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); - --- --- Name: patient_patient_tag patient_patient_tag_write; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patient_patient_tag_write ON public.patient_patient_tag USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); - --- --- Name: patient_tags; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.patient_tags ENABLE ROW LEVEL SECURITY; - --- --- Name: patient_tags patient_tags_owner_all; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patient_tags_owner_all ON public.patient_tags TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: patient_tags patient_tags_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patient_tags_select ON public.patient_tags FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); - --- --- Name: patient_tags patient_tags_write; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patient_tags_write ON public.patient_tags USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); - --- --- Name: patients; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.patients ENABLE ROW LEVEL SECURITY; - --- --- Name: patients patients_delete; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patients_delete ON public.patients FOR DELETE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.delete'::text))); - --- --- Name: patients patients_insert; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patients_insert ON public.patients FOR INSERT WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.create'::text))); - --- --- Name: patients patients_owner_all; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patients_owner_all ON public.patients TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: patients patients_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patients_select ON public.patients FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); - --- --- Name: patients patients_update; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY patients_update ON public.patients FOR UPDATE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); - --- --- Name: payment_settings; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.payment_settings ENABLE ROW LEVEL SECURITY; - --- --- Name: payment_settings payment_settings: owner full access; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "payment_settings: owner full access" ON public.payment_settings USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: plan_features; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.plan_features ENABLE ROW LEVEL SECURITY; - --- --- Name: plan_features plan_features_read_authenticated; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY plan_features_read_authenticated ON public.plan_features FOR SELECT TO authenticated USING (true); - --- --- Name: plan_features plan_features_write_saas_admin; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY plan_features_write_saas_admin ON public.plan_features TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); - --- --- Name: plans; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.plans ENABLE ROW LEVEL SECURITY; - --- --- Name: plans plans_read_authenticated; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY plans_read_authenticated ON public.plans FOR SELECT TO authenticated USING (true); - --- --- Name: plans plans_write_saas_admin; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY plans_write_saas_admin ON public.plans TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); - --- --- Name: professional_pricing; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.professional_pricing ENABLE ROW LEVEL SECURITY; - --- --- Name: professional_pricing professional_pricing: owner full access; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "professional_pricing: owner full access" ON public.professional_pricing USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: profiles; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY; - --- --- Name: profiles profiles_insert_own; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY profiles_insert_own ON public.profiles FOR INSERT WITH CHECK ((id = auth.uid())); - --- --- Name: profiles profiles_read_saas_admin; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY profiles_read_saas_admin ON public.profiles FOR SELECT USING (public.is_saas_admin()); - --- --- Name: profiles profiles_select_own; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY profiles_select_own ON public.profiles FOR SELECT USING ((id = auth.uid())); - --- --- Name: profiles profiles_update_own; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY profiles_update_own ON public.profiles FOR UPDATE USING ((id = auth.uid())) WITH CHECK ((id = auth.uid())); +CREATE POLICY "psc: owner full access" ON public.patient_support_contacts USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); --- --- Name: login_carousel_slides public_read; Type: POLICY; Schema: public; Owner: supabase_admin --- +CREATE POLICY psh_insert ON public.patient_status_history FOR INSERT WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + +CREATE POLICY psh_select ON public.patient_status_history FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + +CREATE POLICY pt_insert ON public.patient_timeline FOR INSERT WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + +CREATE POLICY pt_select ON public.patient_timeline FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); CREATE POLICY public_read ON public.login_carousel_slides FOR SELECT USING ((ativo = true)); - --- --- Name: features read features (auth); Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "read features (auth)" ON public.features FOR SELECT TO authenticated USING (true); - --- --- Name: plan_features read plan_features (auth); Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "read plan_features (auth)" ON public.plan_features FOR SELECT TO authenticated USING (true); - --- --- Name: plans read plans (auth); Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "read plans (auth)" ON public.plans FOR SELECT TO authenticated USING (true); - --- --- Name: recurrence_exceptions; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.recurrence_exceptions ENABLE ROW LEVEL SECURITY; - --- --- Name: recurrence_exceptions recurrence_exceptions_tenant; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY recurrence_exceptions_tenant ON public.recurrence_exceptions TO authenticated USING ((tenant_id IN ( SELECT tenant_members.tenant_id FROM public.tenant_members WHERE (tenant_members.user_id = auth.uid())))) WITH CHECK ((tenant_id IN ( SELECT tenant_members.tenant_id FROM public.tenant_members WHERE (tenant_members.user_id = auth.uid())))); - --- --- Name: recurrence_rule_services; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.recurrence_rule_services ENABLE ROW LEVEL SECURITY; - --- --- Name: recurrence_rule_services recurrence_rule_services: clinic read; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "recurrence_rule_services: clinic read" ON public.recurrence_rule_services FOR SELECT USING ((EXISTS ( SELECT 1 FROM public.recurrence_rules r WHERE ((r.id = recurrence_rule_services.rule_id) AND public.is_clinic_tenant(r.tenant_id) AND public.is_tenant_member(r.tenant_id) AND public.tenant_has_feature(r.tenant_id, 'agenda.view'::text))))); - --- --- Name: recurrence_rule_services recurrence_rule_services: clinic write; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "recurrence_rule_services: clinic write" ON public.recurrence_rule_services USING ((EXISTS ( SELECT 1 FROM public.recurrence_rules r WHERE ((r.id = recurrence_rule_services.rule_id) AND public.is_clinic_tenant(r.tenant_id) AND public.is_tenant_member(r.tenant_id) AND public.tenant_has_feature(r.tenant_id, 'agenda.edit'::text))))) WITH CHECK ((EXISTS ( SELECT 1 FROM public.recurrence_rules r WHERE ((r.id = recurrence_rule_services.rule_id) AND public.is_clinic_tenant(r.tenant_id) AND public.is_tenant_member(r.tenant_id) AND public.tenant_has_feature(r.tenant_id, 'agenda.edit'::text))))); - --- --- Name: recurrence_rule_services recurrence_rule_services: owner full access; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "recurrence_rule_services: owner full access" ON public.recurrence_rule_services TO authenticated USING ((EXISTS ( SELECT 1 FROM public.recurrence_rules r WHERE ((r.id = recurrence_rule_services.rule_id) AND (r.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 FROM public.recurrence_rules r WHERE ((r.id = recurrence_rule_services.rule_id) AND (r.owner_id = auth.uid()))))); - --- --- Name: recurrence_rules; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.recurrence_rules ENABLE ROW LEVEL SECURITY; - --- --- Name: recurrence_rules recurrence_rules_clinic_read; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY recurrence_rules_clinic_read ON public.recurrence_rules FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); - --- --- Name: recurrence_rules recurrence_rules_clinic_write; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY recurrence_rules_clinic_write ON public.recurrence_rules USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); - --- --- Name: recurrence_rules recurrence_rules_owner; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY recurrence_rules_owner ON public.recurrence_rules TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: subscription_intents_legacy saas_admin can read subscription_intents; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "saas_admin can read subscription_intents" ON public.subscription_intents_legacy FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 FROM public.saas_admins a WHERE (a.user_id = auth.uid())))); - --- --- Name: subscription_intents_legacy saas_admin can update subscription_intents; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "saas_admin can update subscription_intents" ON public.subscription_intents_legacy FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 FROM public.saas_admins a WHERE (a.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 FROM public.saas_admins a WHERE (a.user_id = auth.uid())))); - --- --- Name: login_carousel_slides saas_admin_full; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY saas_admin_full ON public.login_carousel_slides USING ((EXISTS ( SELECT 1 FROM public.profiles WHERE ((profiles.id = auth.uid()) AND (profiles.role = 'saas_admin'::text))))); - --- --- Name: saas_docs saas_admin_full_access; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY saas_admin_full_access ON public.saas_docs TO authenticated USING ((EXISTS ( SELECT 1 FROM public.saas_admins WHERE (saas_admins.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 FROM public.saas_admins WHERE (saas_admins.user_id = auth.uid())))); - --- --- Name: saas_admins; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.saas_admins ENABLE ROW LEVEL SECURITY; - --- --- Name: saas_admins saas_admins_select_self; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY saas_admins_select_self ON public.saas_admins FOR SELECT TO authenticated USING ((user_id = auth.uid())); - --- --- Name: saas_doc_votos; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.saas_doc_votos ENABLE ROW LEVEL SECURITY; - --- --- Name: saas_docs; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.saas_docs ENABLE ROW LEVEL SECURITY; - --- --- Name: saas_faq; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.saas_faq ENABLE ROW LEVEL SECURITY; - --- --- Name: saas_faq_itens; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.saas_faq_itens ENABLE ROW LEVEL SECURITY; - --- --- Name: agenda_bloqueios select own; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "select own" ON public.agenda_bloqueios FOR SELECT USING ((owner_id = auth.uid())); - --- --- Name: services; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.services ENABLE ROW LEVEL SECURITY; - --- --- Name: services services: owner full access; Type: POLICY; Schema: public; Owner: supabase_admin --- +CREATE POLICY service_role_manage_usage ON public.twilio_subaccount_usage USING ((auth.role() = 'service_role'::text)); CREATE POLICY "services: owner full access" ON public.services USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); - --- --- Name: subscription_events; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.subscription_events ENABLE ROW LEVEL SECURITY; - --- --- Name: subscription_events subscription_events_read_saas; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY subscription_events_read_saas ON public.subscription_events FOR SELECT USING (public.is_saas_admin()); - --- --- Name: subscription_events subscription_events_write_saas; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY subscription_events_write_saas ON public.subscription_events FOR INSERT WITH CHECK (public.is_saas_admin()); - --- --- Name: subscription_intents_legacy subscription_intents_insert_own; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY subscription_intents_insert_own ON public.subscription_intents_legacy FOR INSERT TO authenticated WITH CHECK ((user_id = auth.uid())); - --- --- Name: subscription_intents_legacy; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.subscription_intents_legacy ENABLE ROW LEVEL SECURITY; - --- --- Name: subscription_intents_legacy subscription_intents_select_own; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY subscription_intents_select_own ON public.subscription_intents_legacy FOR SELECT TO authenticated USING ((user_id = auth.uid())); - --- --- Name: subscriptions; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.subscriptions ENABLE ROW LEVEL SECURITY; - --- --- Name: subscriptions subscriptions read own; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "subscriptions read own" ON public.subscriptions FOR SELECT TO authenticated USING ((user_id = auth.uid())); - --- --- Name: subscriptions subscriptions: read if linked owner_users; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "subscriptions: read if linked owner_users" ON public.subscriptions FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 FROM public.owner_users ou WHERE ((ou.owner_id = subscriptions.user_id) AND (ou.user_id = auth.uid()))))); - --- --- Name: subscriptions subscriptions_insert_own_personal; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY subscriptions_insert_own_personal ON public.subscriptions FOR INSERT TO authenticated WITH CHECK (((user_id = auth.uid()) AND (tenant_id IS NULL))); - --- --- Name: subscriptions subscriptions_no_direct_update; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY subscriptions_no_direct_update ON public.subscriptions FOR UPDATE TO authenticated USING (false) WITH CHECK (false); - --- --- Name: subscriptions subscriptions_read_own; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY subscriptions_read_own ON public.subscriptions FOR SELECT TO authenticated USING (((user_id = auth.uid()) OR public.is_saas_admin())); - --- --- Name: subscriptions subscriptions_select_for_tenant_members; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY subscriptions_select_for_tenant_members ON public.subscriptions FOR SELECT TO authenticated USING (((tenant_id IS NOT NULL) AND (EXISTS ( SELECT 1 FROM public.tenant_members tm WHERE ((tm.tenant_id = subscriptions.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text)))))); - --- --- Name: subscriptions subscriptions_select_own_personal; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY subscriptions_select_own_personal ON public.subscriptions FOR SELECT TO authenticated USING (((user_id = auth.uid()) AND (tenant_id IS NULL))); - --- --- Name: subscriptions subscriptions_update_only_saas_admin; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY subscriptions_update_only_saas_admin ON public.subscriptions FOR UPDATE TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); - --- --- Name: support_sessions; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.support_sessions ENABLE ROW LEVEL SECURITY; - --- --- Name: support_sessions support_sessions_saas_delete; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY support_sessions_saas_delete ON public.support_sessions FOR DELETE USING (((auth.uid() = admin_id) AND (EXISTS ( SELECT 1 FROM public.profiles WHERE ((profiles.id = auth.uid()) AND (profiles.role = 'saas_admin'::text)))))); - --- --- Name: support_sessions support_sessions_saas_insert; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY support_sessions_saas_insert ON public.support_sessions FOR INSERT WITH CHECK (((auth.uid() = admin_id) AND (EXISTS ( SELECT 1 FROM public.profiles WHERE ((profiles.id = auth.uid()) AND (profiles.role = 'saas_admin'::text)))))); - --- --- Name: support_sessions support_sessions_saas_select; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY support_sessions_saas_select ON public.support_sessions FOR SELECT USING (((auth.uid() = admin_id) AND (EXISTS ( SELECT 1 FROM public.profiles WHERE ((profiles.id = auth.uid()) AND (profiles.role = 'saas_admin'::text)))))); - --- --- Name: email_templates_tenant tenant manages own overrides; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "tenant manages own overrides" ON public.email_templates_tenant USING ((tenant_id = auth.uid())) WITH CHECK ((tenant_id = auth.uid())); - --- --- Name: email_layout_config tenant owns email layout config; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "tenant owns email layout config" ON public.email_layout_config USING ((tenant_id = auth.uid())) WITH CHECK ((tenant_id = auth.uid())); - --- --- Name: tenant_members; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.tenant_members ENABLE ROW LEVEL SECURITY; - --- --- Name: tenant_members tenant_members_write_saas; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY tenant_members_write_saas ON public.tenant_members TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); - --- --- Name: tenant_modules; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.tenant_modules ENABLE ROW LEVEL SECURITY; - --- --- Name: tenant_modules tenant_modules_read_own; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY tenant_modules_read_own ON public.tenant_modules FOR SELECT TO authenticated USING (((owner_id = auth.uid()) OR public.is_saas_admin())); - --- --- Name: tenant_modules tenant_modules_write_saas; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY tenant_modules_write_saas ON public.tenant_modules TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); - --- --- Name: tenants; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.tenants ENABLE ROW LEVEL SECURITY; - --- --- Name: tenants tenants_read_members; Type: POLICY; Schema: public; Owner: supabase_admin --- +CREATE POLICY tenant_select_own_usage ON public.twilio_subaccount_usage FOR SELECT USING ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE (tenant_members.user_id = auth.uid())))); CREATE POLICY tenants_read_members ON public.tenants FOR SELECT TO authenticated USING ((public.is_saas_admin() OR (EXISTS ( SELECT 1 FROM public.tenant_members tm WHERE ((tm.tenant_id = tenants.id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text)))))); - --- --- Name: tenants tenants_write_saas; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY tenants_write_saas ON public.tenants TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); - --- --- Name: therapist_payout_records; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.therapist_payout_records ENABLE ROW LEVEL SECURITY; - --- --- Name: therapist_payout_records therapist_payout_records_self; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY therapist_payout_records_self ON public.therapist_payout_records USING ((EXISTS ( SELECT 1 FROM public.therapist_payouts tp WHERE ((tp.id = therapist_payout_records.payout_id) AND (tp.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 FROM public.therapist_payouts tp WHERE ((tp.id = therapist_payout_records.payout_id) AND (tp.owner_id = auth.uid()))))); - --- --- Name: therapist_payout_records therapist_payout_records_tenant_admin; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY therapist_payout_records_tenant_admin ON public.therapist_payout_records FOR SELECT USING ((EXISTS ( SELECT 1 FROM public.therapist_payouts tp WHERE ((tp.id = therapist_payout_records.payout_id) AND public.is_tenant_admin(tp.tenant_id))))); - --- --- Name: therapist_payouts; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.therapist_payouts ENABLE ROW LEVEL SECURITY; - --- --- Name: therapist_payouts therapist_payouts_self; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY therapist_payouts_self ON public.therapist_payouts USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); - --- --- Name: therapist_payouts therapist_payouts_tenant_admin; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY therapist_payouts_tenant_admin ON public.therapist_payouts FOR SELECT USING (((tenant_id IS NOT NULL) AND public.is_tenant_admin(tenant_id))); - --- --- Name: tenant_members tm_select_admin_all_members; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY tm_select_admin_all_members ON public.tenant_members FOR SELECT TO authenticated USING (public.is_tenant_admin(tenant_id)); - --- --- Name: tenant_members tm_select_own_membership; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY tm_select_own_membership ON public.tenant_members FOR SELECT TO authenticated USING ((user_id = auth.uid())); - --- --- Name: agenda_bloqueios update own; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY "update own" ON public.agenda_bloqueios FOR UPDATE USING ((owner_id = auth.uid())); - --- --- Name: user_settings; Type: ROW SECURITY; Schema: public; Owner: supabase_admin --- - -ALTER TABLE public.user_settings ENABLE ROW LEVEL SECURITY; - --- --- Name: user_settings user_settings_insert_own; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY user_settings_insert_own ON public.user_settings FOR INSERT WITH CHECK ((user_id = auth.uid())); - --- --- Name: user_settings user_settings_select_own; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY user_settings_select_own ON public.user_settings FOR SELECT USING ((user_id = auth.uid())); - --- --- Name: user_settings user_settings_update_own; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY user_settings_update_own ON public.user_settings FOR UPDATE USING ((user_id = auth.uid())) WITH CHECK ((user_id = auth.uid())); - --- --- Name: saas_docs users_read_usuario_docs; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY users_read_usuario_docs ON public.saas_docs FOR SELECT TO authenticated USING (((ativo = true) AND (tipo_acesso = 'usuario'::text))); - --- --- Name: saas_doc_votos votos_select_own; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY votos_select_own ON public.saas_doc_votos FOR SELECT TO authenticated USING ((user_id = auth.uid())); - --- --- Name: saas_doc_votos votos_upsert_own; Type: POLICY; Schema: public; Owner: supabase_admin --- - CREATE POLICY votos_upsert_own ON public.saas_doc_votos TO authenticated USING ((user_id = auth.uid())) WITH CHECK ((user_id = auth.uid())); +CREATE POLICY "Allow authenticated updates" ON storage.objects FOR UPDATE TO authenticated USING ((bucket_id = ANY (ARRAY['avatars'::text, 'logos'::text]))); --- --- Name: messages; Type: ROW SECURITY; Schema: realtime; Owner: supabase_realtime_admin --- +CREATE POLICY "Allow authenticated uploads" ON storage.objects FOR INSERT TO authenticated WITH CHECK ((bucket_id = ANY (ARRAY['avatars'::text, 'logos'::text]))); -ALTER TABLE realtime.messages ENABLE ROW LEVEL SECURITY; - --- --- Name: objects agendador_storage_owner_delete; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- +CREATE POLICY "Public read" ON storage.objects FOR SELECT USING ((bucket_id = ANY (ARRAY['avatars'::text, 'logos'::text]))); CREATE POLICY agendador_storage_owner_delete ON storage.objects FOR DELETE TO authenticated USING (((bucket_id = 'agendador'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); - --- --- Name: objects agendador_storage_owner_insert; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- - CREATE POLICY agendador_storage_owner_insert ON storage.objects FOR INSERT TO authenticated WITH CHECK (((bucket_id = 'agendador'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); - --- --- Name: objects agendador_storage_owner_update; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- - CREATE POLICY agendador_storage_owner_update ON storage.objects FOR UPDATE TO authenticated USING (((bucket_id = 'agendador'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); - --- --- Name: objects agendador_storage_public_read; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- - CREATE POLICY agendador_storage_public_read ON storage.objects FOR SELECT USING ((bucket_id = 'agendador'::text)); - --- --- Name: objects avatars authenticated upload; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- - CREATE POLICY "avatars authenticated upload" ON storage.objects FOR INSERT WITH CHECK (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); - --- --- Name: objects avatars owner delete; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- - CREATE POLICY "avatars owner delete" ON storage.objects FOR DELETE USING (((bucket_id = 'avatars'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); - --- --- Name: objects avatars owner update; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- - CREATE POLICY "avatars owner update" ON storage.objects FOR UPDATE USING (((bucket_id = 'avatars'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); - --- --- Name: objects avatars public read; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- - CREATE POLICY "avatars public read" ON storage.objects FOR SELECT USING ((bucket_id = 'avatars'::text)); - --- --- Name: objects avatars_delete_own; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- - CREATE POLICY avatars_delete_own ON storage.objects FOR DELETE TO authenticated USING (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); - --- --- Name: objects avatars_delete_own_folder; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- - CREATE POLICY avatars_delete_own_folder ON storage.objects FOR DELETE USING (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))); - --- --- Name: objects avatars_insert_own; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- - CREATE POLICY avatars_insert_own ON storage.objects FOR INSERT TO authenticated WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); - --- --- Name: objects avatars_insert_own_folder; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- - CREATE POLICY avatars_insert_own_folder ON storage.objects FOR INSERT WITH CHECK (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))); - --- --- Name: objects avatars_read; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- - CREATE POLICY avatars_read ON storage.objects FOR SELECT USING ((bucket_id = 'avatars'::text)); - --- --- Name: objects avatars_select_own; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- - CREATE POLICY avatars_select_own ON storage.objects FOR SELECT TO authenticated USING (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); - --- --- Name: objects avatars_update_own; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- - CREATE POLICY avatars_update_own ON storage.objects FOR UPDATE TO authenticated USING (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))) WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); - --- --- Name: objects avatars_update_own_folder; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- - CREATE POLICY avatars_update_own_folder ON storage.objects FOR UPDATE USING (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))) WITH CHECK (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))); +CREATE POLICY "documents: authenticated delete" ON storage.objects FOR DELETE TO authenticated USING ((bucket_id = 'documents'::text)); --- --- Name: buckets; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin --- +CREATE POLICY "documents: authenticated read" ON storage.objects FOR SELECT TO authenticated USING ((bucket_id = 'documents'::text)); -ALTER TABLE storage.buckets ENABLE ROW LEVEL SECURITY; +CREATE POLICY "documents: authenticated upload" ON storage.objects FOR INSERT TO authenticated WITH CHECK ((bucket_id = 'documents'::text)); --- --- Name: buckets_analytics; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin --- +CREATE POLICY "generated-docs: authenticated delete" ON storage.objects FOR DELETE TO authenticated USING ((bucket_id = 'generated-docs'::text)); -ALTER TABLE storage.buckets_analytics ENABLE ROW LEVEL SECURITY; +CREATE POLICY "generated-docs: authenticated read" ON storage.objects FOR SELECT TO authenticated USING ((bucket_id = 'generated-docs'::text)); --- --- Name: buckets_vectors; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE storage.buckets_vectors ENABLE ROW LEVEL SECURITY; - --- --- Name: iceberg_namespaces; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE storage.iceberg_namespaces ENABLE ROW LEVEL SECURITY; - --- --- Name: iceberg_tables; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE storage.iceberg_tables ENABLE ROW LEVEL SECURITY; - --- --- Name: objects intake_read_anon; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- +CREATE POLICY "generated-docs: authenticated upload" ON storage.objects FOR INSERT TO authenticated WITH CHECK ((bucket_id = 'generated-docs'::text)); CREATE POLICY intake_read_anon ON storage.objects FOR SELECT TO anon USING (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); - --- --- Name: objects intake_read_public; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- - CREATE POLICY intake_read_public ON storage.objects FOR SELECT USING (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); - --- --- Name: objects intake_upload_anon; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- - CREATE POLICY intake_upload_anon ON storage.objects FOR INSERT TO anon WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); - --- --- Name: objects intake_upload_public; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- - CREATE POLICY intake_upload_public ON storage.objects FOR INSERT WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); - --- --- Name: migrations; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE storage.migrations ENABLE ROW LEVEL SECURITY; - --- --- Name: objects; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE storage.objects ENABLE ROW LEVEL SECURITY; - --- --- Name: objects public_read; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- - CREATE POLICY public_read ON storage.objects FOR SELECT USING ((bucket_id = 'saas-docs'::text)); - --- --- Name: s3_multipart_uploads; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE storage.s3_multipart_uploads ENABLE ROW LEVEL SECURITY; - --- --- Name: s3_multipart_uploads_parts; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE storage.s3_multipart_uploads_parts ENABLE ROW LEVEL SECURITY; - --- --- Name: objects saas_admin_delete; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- - CREATE POLICY saas_admin_delete ON storage.objects FOR DELETE TO authenticated USING (((bucket_id = 'saas-docs'::text) AND (EXISTS ( SELECT 1 FROM public.saas_admins WHERE (saas_admins.user_id = auth.uid()))))); - --- --- Name: objects saas_admin_upload; Type: POLICY; Schema: storage; Owner: supabase_storage_admin --- - CREATE POLICY saas_admin_upload ON storage.objects FOR INSERT TO authenticated WITH CHECK (((bucket_id = 'saas-docs'::text) AND (EXISTS ( SELECT 1 FROM public.saas_admins WHERE (saas_admins.user_id = auth.uid()))))); - - --- --- Name: vector_indexes; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE storage.vector_indexes ENABLE ROW LEVEL SECURITY; - --- --- Name: supabase_realtime; Type: PUBLICATION; Schema: -; Owner: postgres --- - -CREATE PUBLICATION supabase_realtime WITH (publish = 'insert, update, delete, truncate'); - - -ALTER PUBLICATION supabase_realtime OWNER TO postgres; - --- --- Name: supabase_realtime_messages_publication; Type: PUBLICATION; Schema: -; Owner: supabase_admin --- - -CREATE PUBLICATION supabase_realtime_messages_publication WITH (publish = 'insert, update, delete, truncate'); - - -ALTER PUBLICATION supabase_realtime_messages_publication OWNER TO supabase_admin; - --- --- Name: supabase_realtime notifications; Type: PUBLICATION TABLE; Schema: public; Owner: postgres --- - -ALTER PUBLICATION supabase_realtime ADD TABLE ONLY public.notifications; - - --- --- Name: supabase_realtime_messages_publication messages; Type: PUBLICATION TABLE; Schema: realtime; Owner: supabase_admin --- - -ALTER PUBLICATION supabase_realtime_messages_publication ADD TABLE ONLY realtime.messages; - - --- --- Name: SCHEMA auth; Type: ACL; Schema: -; Owner: supabase_admin --- - diff --git a/database-novo/schema/10_grants/grants.sql b/database-novo/schema/10_grants/grants.sql deleted file mode 100644 index f64cece..0000000 --- a/database-novo/schema/10_grants/grants.sql +++ /dev/null @@ -1,6534 +0,0 @@ --- ============================================================================= --- AgenciaPsi — Grants + Default Privileges --- ============================================================================= - -GRANT USAGE ON SCHEMA auth TO anon; -GRANT USAGE ON SCHEMA auth TO authenticated; -GRANT USAGE ON SCHEMA auth TO service_role; -GRANT ALL ON SCHEMA auth TO supabase_auth_admin; -GRANT ALL ON SCHEMA auth TO dashboard_user; -GRANT USAGE ON SCHEMA auth TO postgres; - - --- --- Name: SCHEMA cron; Type: ACL; Schema: -; Owner: supabase_admin --- - -GRANT USAGE ON SCHEMA cron TO postgres WITH GRANT OPTION; - - --- --- Name: SCHEMA extensions; Type: ACL; Schema: -; Owner: postgres --- - -GRANT USAGE ON SCHEMA extensions TO anon; -GRANT USAGE ON SCHEMA extensions TO authenticated; -GRANT USAGE ON SCHEMA extensions TO service_role; -GRANT ALL ON SCHEMA extensions TO dashboard_user; - - --- --- Name: SCHEMA net; Type: ACL; Schema: -; Owner: supabase_admin --- - -GRANT USAGE ON SCHEMA net TO supabase_functions_admin; -GRANT USAGE ON SCHEMA net TO postgres; -GRANT USAGE ON SCHEMA net TO anon; -GRANT USAGE ON SCHEMA net TO authenticated; -GRANT USAGE ON SCHEMA net TO service_role; - - --- --- Name: SCHEMA public; Type: ACL; Schema: -; Owner: pg_database_owner --- - -GRANT USAGE ON SCHEMA public TO postgres; -GRANT USAGE ON SCHEMA public TO anon; -GRANT USAGE ON SCHEMA public TO authenticated; -GRANT USAGE ON SCHEMA public TO service_role; - - --- --- Name: SCHEMA realtime; Type: ACL; Schema: -; Owner: supabase_admin --- - -GRANT USAGE ON SCHEMA realtime TO postgres; -GRANT USAGE ON SCHEMA realtime TO anon; -GRANT USAGE ON SCHEMA realtime TO authenticated; -GRANT USAGE ON SCHEMA realtime TO service_role; -GRANT ALL ON SCHEMA realtime TO supabase_realtime_admin; - - --- --- Name: SCHEMA storage; Type: ACL; Schema: -; Owner: supabase_admin --- - -GRANT USAGE ON SCHEMA storage TO postgres WITH GRANT OPTION; -GRANT USAGE ON SCHEMA storage TO anon; -GRANT USAGE ON SCHEMA storage TO authenticated; -GRANT USAGE ON SCHEMA storage TO service_role; -GRANT ALL ON SCHEMA storage TO supabase_storage_admin WITH GRANT OPTION; -GRANT ALL ON SCHEMA storage TO dashboard_user; - - --- --- Name: SCHEMA supabase_functions; Type: ACL; Schema: -; Owner: supabase_admin --- - -GRANT USAGE ON SCHEMA supabase_functions TO postgres; -GRANT USAGE ON SCHEMA supabase_functions TO anon; -GRANT USAGE ON SCHEMA supabase_functions TO authenticated; -GRANT USAGE ON SCHEMA supabase_functions TO service_role; -GRANT ALL ON SCHEMA supabase_functions TO supabase_functions_admin; - - --- --- Name: SCHEMA vault; Type: ACL; Schema: -; Owner: supabase_admin --- - -GRANT USAGE ON SCHEMA vault TO postgres WITH GRANT OPTION; -GRANT USAGE ON SCHEMA vault TO service_role; - - --- --- Name: FUNCTION citextin(cstring); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citextin(cstring) TO postgres; -GRANT ALL ON FUNCTION public.citextin(cstring) TO anon; -GRANT ALL ON FUNCTION public.citextin(cstring) TO authenticated; -GRANT ALL ON FUNCTION public.citextin(cstring) TO service_role; - - --- --- Name: FUNCTION citextout(public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citextout(public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citextout(public.citext) TO anon; -GRANT ALL ON FUNCTION public.citextout(public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citextout(public.citext) TO service_role; - - --- --- Name: FUNCTION citextrecv(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citextrecv(internal) TO postgres; -GRANT ALL ON FUNCTION public.citextrecv(internal) TO anon; -GRANT ALL ON FUNCTION public.citextrecv(internal) TO authenticated; -GRANT ALL ON FUNCTION public.citextrecv(internal) TO service_role; - - --- --- Name: FUNCTION citextsend(public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citextsend(public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citextsend(public.citext) TO anon; -GRANT ALL ON FUNCTION public.citextsend(public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citextsend(public.citext) TO service_role; - - --- --- Name: FUNCTION gbtreekey16_in(cstring); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey16_in(cstring) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey16_in(cstring) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey16_in(cstring) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey16_in(cstring) TO service_role; - - --- --- Name: FUNCTION gbtreekey16_out(public.gbtreekey16); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey16_out(public.gbtreekey16) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey16_out(public.gbtreekey16) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey16_out(public.gbtreekey16) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey16_out(public.gbtreekey16) TO service_role; - - --- --- Name: FUNCTION gbtreekey2_in(cstring); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey2_in(cstring) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey2_in(cstring) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey2_in(cstring) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey2_in(cstring) TO service_role; - - --- --- Name: FUNCTION gbtreekey2_out(public.gbtreekey2); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey2_out(public.gbtreekey2) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey2_out(public.gbtreekey2) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey2_out(public.gbtreekey2) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey2_out(public.gbtreekey2) TO service_role; - - --- --- Name: FUNCTION gbtreekey32_in(cstring); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey32_in(cstring) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey32_in(cstring) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey32_in(cstring) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey32_in(cstring) TO service_role; - - --- --- Name: FUNCTION gbtreekey32_out(public.gbtreekey32); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey32_out(public.gbtreekey32) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey32_out(public.gbtreekey32) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey32_out(public.gbtreekey32) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey32_out(public.gbtreekey32) TO service_role; - - --- --- Name: FUNCTION gbtreekey4_in(cstring); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey4_in(cstring) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey4_in(cstring) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey4_in(cstring) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey4_in(cstring) TO service_role; - - --- --- Name: FUNCTION gbtreekey4_out(public.gbtreekey4); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey4_out(public.gbtreekey4) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey4_out(public.gbtreekey4) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey4_out(public.gbtreekey4) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey4_out(public.gbtreekey4) TO service_role; - - --- --- Name: FUNCTION gbtreekey8_in(cstring); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey8_in(cstring) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey8_in(cstring) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey8_in(cstring) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey8_in(cstring) TO service_role; - - --- --- Name: FUNCTION gbtreekey8_out(public.gbtreekey8); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey8_out(public.gbtreekey8) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey8_out(public.gbtreekey8) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey8_out(public.gbtreekey8) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey8_out(public.gbtreekey8) TO service_role; - - --- --- Name: FUNCTION gbtreekey_var_in(cstring); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey_var_in(cstring) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey_var_in(cstring) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey_var_in(cstring) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey_var_in(cstring) TO service_role; - - --- --- Name: FUNCTION gbtreekey_var_out(public.gbtreekey_var); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbtreekey_var_out(public.gbtreekey_var) TO postgres; -GRANT ALL ON FUNCTION public.gbtreekey_var_out(public.gbtreekey_var) TO anon; -GRANT ALL ON FUNCTION public.gbtreekey_var_out(public.gbtreekey_var) TO authenticated; -GRANT ALL ON FUNCTION public.gbtreekey_var_out(public.gbtreekey_var) TO service_role; - - --- --- Name: FUNCTION gtrgm_in(cstring); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gtrgm_in(cstring) TO postgres; -GRANT ALL ON FUNCTION public.gtrgm_in(cstring) TO anon; -GRANT ALL ON FUNCTION public.gtrgm_in(cstring) TO authenticated; -GRANT ALL ON FUNCTION public.gtrgm_in(cstring) TO service_role; - - --- --- Name: FUNCTION gtrgm_out(public.gtrgm); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gtrgm_out(public.gtrgm) TO postgres; -GRANT ALL ON FUNCTION public.gtrgm_out(public.gtrgm) TO anon; -GRANT ALL ON FUNCTION public.gtrgm_out(public.gtrgm) TO authenticated; -GRANT ALL ON FUNCTION public.gtrgm_out(public.gtrgm) TO service_role; - - --- --- Name: FUNCTION citext(boolean); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext(boolean) TO postgres; -GRANT ALL ON FUNCTION public.citext(boolean) TO anon; -GRANT ALL ON FUNCTION public.citext(boolean) TO authenticated; -GRANT ALL ON FUNCTION public.citext(boolean) TO service_role; - - --- --- Name: FUNCTION citext(character); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext(character) TO postgres; -GRANT ALL ON FUNCTION public.citext(character) TO anon; -GRANT ALL ON FUNCTION public.citext(character) TO authenticated; -GRANT ALL ON FUNCTION public.citext(character) TO service_role; - - --- --- Name: FUNCTION citext(inet); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext(inet) TO postgres; -GRANT ALL ON FUNCTION public.citext(inet) TO anon; -GRANT ALL ON FUNCTION public.citext(inet) TO authenticated; -GRANT ALL ON FUNCTION public.citext(inet) TO service_role; - - --- --- Name: FUNCTION email(); Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON FUNCTION auth.email() TO dashboard_user; - - --- --- Name: FUNCTION jwt(); Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON FUNCTION auth.jwt() TO postgres; -GRANT ALL ON FUNCTION auth.jwt() TO dashboard_user; - - --- --- Name: FUNCTION role(); Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON FUNCTION auth.role() TO dashboard_user; - - --- --- Name: FUNCTION uid(); Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON FUNCTION auth.uid() TO dashboard_user; - - --- --- Name: FUNCTION alter_job(job_id bigint, schedule text, command text, database text, username text, active boolean); Type: ACL; Schema: cron; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION cron.alter_job(job_id bigint, schedule text, command text, database text, username text, active boolean) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION job_cache_invalidate(); Type: ACL; Schema: cron; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION cron.job_cache_invalidate() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION schedule(schedule text, command text); Type: ACL; Schema: cron; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION cron.schedule(schedule text, command text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION schedule(job_name text, schedule text, command text); Type: ACL; Schema: cron; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION cron.schedule(job_name text, schedule text, command text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION schedule_in_database(job_name text, schedule text, command text, database text, username text, active boolean); Type: ACL; Schema: cron; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION cron.schedule_in_database(job_name text, schedule text, command text, database text, username text, active boolean) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION unschedule(job_id bigint); Type: ACL; Schema: cron; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION cron.unschedule(job_id bigint) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION unschedule(job_name text); Type: ACL; Schema: cron; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION cron.unschedule(job_name text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION armor(bytea); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.armor(bytea) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.armor(bytea) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION armor(bytea, text[], text[]); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.armor(bytea, text[], text[]) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.armor(bytea, text[], text[]) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION crypt(text, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.crypt(text, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.crypt(text, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION dearmor(text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.dearmor(text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.dearmor(text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION decrypt(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.decrypt(bytea, bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.decrypt(bytea, bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION decrypt_iv(bytea, bytea, bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.decrypt_iv(bytea, bytea, bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.decrypt_iv(bytea, bytea, bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION digest(bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.digest(bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.digest(bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION digest(text, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.digest(text, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.digest(text, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION encrypt(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.encrypt(bytea, bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.encrypt(bytea, bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION encrypt_iv(bytea, bytea, bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.encrypt_iv(bytea, bytea, bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.encrypt_iv(bytea, bytea, bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION gen_random_bytes(integer); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.gen_random_bytes(integer) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.gen_random_bytes(integer) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION gen_random_uuid(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.gen_random_uuid() TO dashboard_user; -GRANT ALL ON FUNCTION extensions.gen_random_uuid() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION gen_salt(text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.gen_salt(text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.gen_salt(text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION gen_salt(text, integer); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.gen_salt(text, integer) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.gen_salt(text, integer) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION grant_pg_cron_access(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION extensions.grant_pg_cron_access() FROM supabase_admin; -GRANT ALL ON FUNCTION extensions.grant_pg_cron_access() TO supabase_admin WITH GRANT OPTION; -GRANT ALL ON FUNCTION extensions.grant_pg_cron_access() TO dashboard_user; - - --- --- Name: FUNCTION grant_pg_graphql_access(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.grant_pg_graphql_access() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION grant_pg_net_access(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION extensions.grant_pg_net_access() FROM supabase_admin; -GRANT ALL ON FUNCTION extensions.grant_pg_net_access() TO supabase_admin WITH GRANT OPTION; -GRANT ALL ON FUNCTION extensions.grant_pg_net_access() TO dashboard_user; - - --- --- Name: FUNCTION hmac(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.hmac(bytea, bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.hmac(bytea, bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION hmac(text, text, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.hmac(text, text, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.hmac(text, text, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pg_stat_statements(showtext boolean, OUT userid oid, OUT dbid oid, OUT toplevel boolean, OUT queryid bigint, OUT query text, OUT plans bigint, OUT total_plan_time double precision, OUT min_plan_time double precision, OUT max_plan_time double precision, OUT mean_plan_time double precision, OUT stddev_plan_time double precision, OUT calls bigint, OUT total_exec_time double precision, OUT min_exec_time double precision, OUT max_exec_time double precision, OUT mean_exec_time double precision, OUT stddev_exec_time double precision, OUT rows bigint, OUT shared_blks_hit bigint, OUT shared_blks_read bigint, OUT shared_blks_dirtied bigint, OUT shared_blks_written bigint, OUT local_blks_hit bigint, OUT local_blks_read bigint, OUT local_blks_dirtied bigint, OUT local_blks_written bigint, OUT temp_blks_read bigint, OUT temp_blks_written bigint, OUT shared_blk_read_time double precision, OUT shared_blk_write_time double precision, OUT local_blk_read_time double precision, OUT local_blk_write_time double precision, OUT temp_blk_read_time double precision, OUT temp_blk_write_time double precision, OUT wal_records bigint, OUT wal_fpi bigint, OUT wal_bytes numeric, OUT jit_functions bigint, OUT jit_generation_time double precision, OUT jit_inlining_count bigint, OUT jit_inlining_time double precision, OUT jit_optimization_count bigint, OUT jit_optimization_time double precision, OUT jit_emission_count bigint, OUT jit_emission_time double precision, OUT jit_deform_count bigint, OUT jit_deform_time double precision, OUT stats_since timestamp with time zone, OUT minmax_stats_since timestamp with time zone); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pg_stat_statements(showtext boolean, OUT userid oid, OUT dbid oid, OUT toplevel boolean, OUT queryid bigint, OUT query text, OUT plans bigint, OUT total_plan_time double precision, OUT min_plan_time double precision, OUT max_plan_time double precision, OUT mean_plan_time double precision, OUT stddev_plan_time double precision, OUT calls bigint, OUT total_exec_time double precision, OUT min_exec_time double precision, OUT max_exec_time double precision, OUT mean_exec_time double precision, OUT stddev_exec_time double precision, OUT rows bigint, OUT shared_blks_hit bigint, OUT shared_blks_read bigint, OUT shared_blks_dirtied bigint, OUT shared_blks_written bigint, OUT local_blks_hit bigint, OUT local_blks_read bigint, OUT local_blks_dirtied bigint, OUT local_blks_written bigint, OUT temp_blks_read bigint, OUT temp_blks_written bigint, OUT shared_blk_read_time double precision, OUT shared_blk_write_time double precision, OUT local_blk_read_time double precision, OUT local_blk_write_time double precision, OUT temp_blk_read_time double precision, OUT temp_blk_write_time double precision, OUT wal_records bigint, OUT wal_fpi bigint, OUT wal_bytes numeric, OUT jit_functions bigint, OUT jit_generation_time double precision, OUT jit_inlining_count bigint, OUT jit_inlining_time double precision, OUT jit_optimization_count bigint, OUT jit_optimization_time double precision, OUT jit_emission_count bigint, OUT jit_emission_time double precision, OUT jit_deform_count bigint, OUT jit_deform_time double precision, OUT stats_since timestamp with time zone, OUT minmax_stats_since timestamp with time zone) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pg_stat_statements_info(OUT dealloc bigint, OUT stats_reset timestamp with time zone); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pg_stat_statements_info(OUT dealloc bigint, OUT stats_reset timestamp with time zone) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pg_stat_statements_reset(userid oid, dbid oid, queryid bigint, minmax_only boolean); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pg_stat_statements_reset(userid oid, dbid oid, queryid bigint, minmax_only boolean) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_armor_headers(text, OUT key text, OUT value text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_armor_headers(text, OUT key text, OUT value text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_armor_headers(text, OUT key text, OUT value text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_key_id(bytea); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_key_id(bytea) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_key_id(bytea) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_pub_decrypt(bytea, bytea); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt(bytea, bytea) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt(bytea, bytea) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_pub_decrypt(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt(bytea, bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt(bytea, bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_pub_decrypt(bytea, bytea, text, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt(bytea, bytea, text, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt(bytea, bytea, text, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_pub_decrypt_bytea(bytea, bytea); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt_bytea(bytea, bytea) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt_bytea(bytea, bytea) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_pub_decrypt_bytea(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt_bytea(bytea, bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt_bytea(bytea, bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_pub_decrypt_bytea(bytea, bytea, text, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt_bytea(bytea, bytea, text, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt_bytea(bytea, bytea, text, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_pub_encrypt(text, bytea); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt(text, bytea) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt(text, bytea) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_pub_encrypt(text, bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt(text, bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt(text, bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_pub_encrypt_bytea(bytea, bytea); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt_bytea(bytea, bytea) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt_bytea(bytea, bytea) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_pub_encrypt_bytea(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt_bytea(bytea, bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt_bytea(bytea, bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_sym_decrypt(bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt(bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt(bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_sym_decrypt(bytea, text, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt(bytea, text, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt(bytea, text, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_sym_decrypt_bytea(bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt_bytea(bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt_bytea(bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_sym_decrypt_bytea(bytea, text, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt_bytea(bytea, text, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt_bytea(bytea, text, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_sym_encrypt(text, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt(text, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt(text, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_sym_encrypt(text, text, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt(text, text, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt(text, text, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_sym_encrypt_bytea(bytea, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt_bytea(bytea, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt_bytea(bytea, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgp_sym_encrypt_bytea(bytea, text, text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt_bytea(bytea, text, text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt_bytea(bytea, text, text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgrst_ddl_watch(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgrst_ddl_watch() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION pgrst_drop_watch(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.pgrst_drop_watch() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION set_graphql_placeholder(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.set_graphql_placeholder() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION uuid_generate_v1(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.uuid_generate_v1() TO dashboard_user; -GRANT ALL ON FUNCTION extensions.uuid_generate_v1() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION uuid_generate_v1mc(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.uuid_generate_v1mc() TO dashboard_user; -GRANT ALL ON FUNCTION extensions.uuid_generate_v1mc() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION uuid_generate_v3(namespace uuid, name text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.uuid_generate_v3(namespace uuid, name text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.uuid_generate_v3(namespace uuid, name text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION uuid_generate_v4(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.uuid_generate_v4() TO dashboard_user; -GRANT ALL ON FUNCTION extensions.uuid_generate_v4() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION uuid_generate_v5(namespace uuid, name text); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.uuid_generate_v5(namespace uuid, name text) TO dashboard_user; -GRANT ALL ON FUNCTION extensions.uuid_generate_v5(namespace uuid, name text) TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION uuid_nil(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.uuid_nil() TO dashboard_user; -GRANT ALL ON FUNCTION extensions.uuid_nil() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION uuid_ns_dns(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.uuid_ns_dns() TO dashboard_user; -GRANT ALL ON FUNCTION extensions.uuid_ns_dns() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION uuid_ns_oid(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.uuid_ns_oid() TO dashboard_user; -GRANT ALL ON FUNCTION extensions.uuid_ns_oid() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION uuid_ns_url(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.uuid_ns_url() TO dashboard_user; -GRANT ALL ON FUNCTION extensions.uuid_ns_url() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION uuid_ns_x500(); Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION extensions.uuid_ns_x500() TO dashboard_user; -GRANT ALL ON FUNCTION extensions.uuid_ns_x500() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION graphql("operationName" text, query text, variables jsonb, extensions jsonb); Type: ACL; Schema: graphql_public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION graphql_public.graphql("operationName" text, query text, variables jsonb, extensions jsonb) TO postgres; -GRANT ALL ON FUNCTION graphql_public.graphql("operationName" text, query text, variables jsonb, extensions jsonb) TO anon; -GRANT ALL ON FUNCTION graphql_public.graphql("operationName" text, query text, variables jsonb, extensions jsonb) TO authenticated; -GRANT ALL ON FUNCTION graphql_public.graphql("operationName" text, query text, variables jsonb, extensions jsonb) TO service_role; - - --- --- Name: FUNCTION http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer); Type: ACL; Schema: net; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; -GRANT ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin; -GRANT ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO postgres; -GRANT ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO anon; -GRANT ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO authenticated; -GRANT ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO service_role; - - --- --- Name: FUNCTION http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer); Type: ACL; Schema: net; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; -GRANT ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin; -GRANT ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO postgres; -GRANT ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO anon; -GRANT ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO authenticated; -GRANT ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO service_role; - - --- --- Name: FUNCTION pg_reload_conf(); Type: ACL; Schema: pg_catalog; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION pg_catalog.pg_reload_conf() TO postgres WITH GRANT OPTION; - - --- --- Name: FUNCTION get_auth(p_usename text); Type: ACL; Schema: pgbouncer; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION pgbouncer.get_auth(p_usename text) FROM PUBLIC; -GRANT ALL ON FUNCTION pgbouncer.get_auth(p_usename text) TO pgbouncer; - - --- --- Name: FUNCTION __rls_ping(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.__rls_ping() TO postgres; -GRANT ALL ON FUNCTION public.__rls_ping() TO anon; -GRANT ALL ON FUNCTION public.__rls_ping() TO authenticated; -GRANT ALL ON FUNCTION public.__rls_ping() TO service_role; - - --- --- Name: TABLE subscriptions; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.subscriptions TO postgres; -GRANT ALL ON TABLE public.subscriptions TO anon; -GRANT ALL ON TABLE public.subscriptions TO authenticated; -GRANT ALL ON TABLE public.subscriptions TO service_role; - - --- --- Name: FUNCTION activate_subscription_from_intent(p_intent_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.activate_subscription_from_intent(p_intent_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.activate_subscription_from_intent(p_intent_id uuid) TO anon; -GRANT ALL ON FUNCTION public.activate_subscription_from_intent(p_intent_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.activate_subscription_from_intent(p_intent_id uuid) TO service_role; - - --- --- Name: FUNCTION admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer) TO postgres; -GRANT ALL ON FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer) TO anon; -GRANT ALL ON FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer) TO authenticated; -GRANT ALL ON FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer) TO service_role; - - --- --- Name: FUNCTION admin_delete_email_template_global(p_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.admin_delete_email_template_global(p_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.admin_delete_email_template_global(p_id uuid) TO anon; -GRANT ALL ON FUNCTION public.admin_delete_email_template_global(p_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.admin_delete_email_template_global(p_id uuid) TO service_role; - - --- --- Name: FUNCTION admin_fix_plan_target(p_plan_key text, p_new_target text); Type: ACL; Schema: public; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION public.admin_fix_plan_target(p_plan_key text, p_new_target text) FROM PUBLIC; -GRANT ALL ON FUNCTION public.admin_fix_plan_target(p_plan_key text, p_new_target text) TO postgres; -GRANT ALL ON FUNCTION public.admin_fix_plan_target(p_plan_key text, p_new_target text) TO anon; -GRANT ALL ON FUNCTION public.admin_fix_plan_target(p_plan_key text, p_new_target text) TO authenticated; -GRANT ALL ON FUNCTION public.admin_fix_plan_target(p_plan_key text, p_new_target text) TO service_role; - - --- --- Name: FUNCTION admin_upsert_email_template_global(p_id uuid, p_key text, p_domain text, p_channel text, p_subject text, p_body_html text, p_body_text text, p_is_active boolean, p_variables jsonb); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.admin_upsert_email_template_global(p_id uuid, p_key text, p_domain text, p_channel text, p_subject text, p_body_html text, p_body_text text, p_is_active boolean, p_variables jsonb) TO postgres; -GRANT ALL ON FUNCTION public.admin_upsert_email_template_global(p_id uuid, p_key text, p_domain text, p_channel text, p_subject text, p_body_html text, p_body_text text, p_is_active boolean, p_variables jsonb) TO anon; -GRANT ALL ON FUNCTION public.admin_upsert_email_template_global(p_id uuid, p_key text, p_domain text, p_channel text, p_subject text, p_body_html text, p_body_text text, p_is_active boolean, p_variables jsonb) TO authenticated; -GRANT ALL ON FUNCTION public.admin_upsert_email_template_global(p_id uuid, p_key text, p_domain text, p_channel text, p_subject text, p_body_html text, p_body_text text, p_is_active boolean, p_variables jsonb) TO service_role; - - --- --- Name: FUNCTION agenda_cfg_sync(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.agenda_cfg_sync() TO postgres; -GRANT ALL ON FUNCTION public.agenda_cfg_sync() TO anon; -GRANT ALL ON FUNCTION public.agenda_cfg_sync() TO authenticated; -GRANT ALL ON FUNCTION public.agenda_cfg_sync() TO service_role; - - --- --- Name: FUNCTION agendador_dias_disponiveis(p_slug text, p_ano integer, p_mes integer); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.agendador_dias_disponiveis(p_slug text, p_ano integer, p_mes integer) TO postgres; -GRANT ALL ON FUNCTION public.agendador_dias_disponiveis(p_slug text, p_ano integer, p_mes integer) TO anon; -GRANT ALL ON FUNCTION public.agendador_dias_disponiveis(p_slug text, p_ano integer, p_mes integer) TO authenticated; -GRANT ALL ON FUNCTION public.agendador_dias_disponiveis(p_slug text, p_ano integer, p_mes integer) TO service_role; - - --- --- Name: FUNCTION agendador_gerar_slug(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.agendador_gerar_slug() TO postgres; -GRANT ALL ON FUNCTION public.agendador_gerar_slug() TO anon; -GRANT ALL ON FUNCTION public.agendador_gerar_slug() TO authenticated; -GRANT ALL ON FUNCTION public.agendador_gerar_slug() TO service_role; - - --- --- Name: FUNCTION agendador_slots_disponiveis(p_slug text, p_data date); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.agendador_slots_disponiveis(p_slug text, p_data date) TO postgres; -GRANT ALL ON FUNCTION public.agendador_slots_disponiveis(p_slug text, p_data date) TO anon; -GRANT ALL ON FUNCTION public.agendador_slots_disponiveis(p_slug text, p_data date) TO authenticated; -GRANT ALL ON FUNCTION public.agendador_slots_disponiveis(p_slug text, p_data date) TO service_role; - - --- --- Name: FUNCTION auto_create_financial_record_from_session(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.auto_create_financial_record_from_session() TO postgres; -GRANT ALL ON FUNCTION public.auto_create_financial_record_from_session() TO anon; -GRANT ALL ON FUNCTION public.auto_create_financial_record_from_session() TO authenticated; -GRANT ALL ON FUNCTION public.auto_create_financial_record_from_session() TO service_role; - - --- --- Name: FUNCTION can_delete_patient(p_patient_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.can_delete_patient(p_patient_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.can_delete_patient(p_patient_id uuid) TO anon; -GRANT ALL ON FUNCTION public.can_delete_patient(p_patient_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.can_delete_patient(p_patient_id uuid) TO service_role; - - --- --- Name: FUNCTION cancel_notifications_on_opt_out(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.cancel_notifications_on_opt_out() TO postgres; -GRANT ALL ON FUNCTION public.cancel_notifications_on_opt_out() TO anon; -GRANT ALL ON FUNCTION public.cancel_notifications_on_opt_out() TO authenticated; -GRANT ALL ON FUNCTION public.cancel_notifications_on_opt_out() TO service_role; - - --- --- Name: FUNCTION cancel_notifications_on_session_cancel(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.cancel_notifications_on_session_cancel() TO postgres; -GRANT ALL ON FUNCTION public.cancel_notifications_on_session_cancel() TO anon; -GRANT ALL ON FUNCTION public.cancel_notifications_on_session_cancel() TO authenticated; -GRANT ALL ON FUNCTION public.cancel_notifications_on_session_cancel() TO service_role; - - --- --- Name: FUNCTION cancel_patient_pending_notifications(p_patient_id uuid, p_channel text, p_evento_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.cancel_patient_pending_notifications(p_patient_id uuid, p_channel text, p_evento_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.cancel_patient_pending_notifications(p_patient_id uuid, p_channel text, p_evento_id uuid) TO anon; -GRANT ALL ON FUNCTION public.cancel_patient_pending_notifications(p_patient_id uuid, p_channel text, p_evento_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.cancel_patient_pending_notifications(p_patient_id uuid, p_channel text, p_evento_id uuid) TO service_role; - - --- --- Name: FUNCTION cancel_recurrence_from(p_recurrence_id uuid, p_from_date date); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.cancel_recurrence_from(p_recurrence_id uuid, p_from_date date) TO postgres; -GRANT ALL ON FUNCTION public.cancel_recurrence_from(p_recurrence_id uuid, p_from_date date) TO anon; -GRANT ALL ON FUNCTION public.cancel_recurrence_from(p_recurrence_id uuid, p_from_date date) TO authenticated; -GRANT ALL ON FUNCTION public.cancel_recurrence_from(p_recurrence_id uuid, p_from_date date) TO service_role; - - --- --- Name: FUNCTION cancel_subscription(p_subscription_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.cancel_subscription(p_subscription_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.cancel_subscription(p_subscription_id uuid) TO anon; -GRANT ALL ON FUNCTION public.cancel_subscription(p_subscription_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.cancel_subscription(p_subscription_id uuid) TO service_role; - - --- --- Name: FUNCTION cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone) TO postgres; -GRANT ALL ON FUNCTION public.cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone) TO anon; -GRANT ALL ON FUNCTION public.cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone) TO authenticated; -GRANT ALL ON FUNCTION public.cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone) TO service_role; - - --- --- Name: FUNCTION cash_dist(money, money); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.cash_dist(money, money) TO postgres; -GRANT ALL ON FUNCTION public.cash_dist(money, money) TO anon; -GRANT ALL ON FUNCTION public.cash_dist(money, money) TO authenticated; -GRANT ALL ON FUNCTION public.cash_dist(money, money) TO service_role; - - --- --- Name: FUNCTION change_subscription_plan(p_subscription_id uuid, p_new_plan_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.change_subscription_plan(p_subscription_id uuid, p_new_plan_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.change_subscription_plan(p_subscription_id uuid, p_new_plan_id uuid) TO anon; -GRANT ALL ON FUNCTION public.change_subscription_plan(p_subscription_id uuid, p_new_plan_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.change_subscription_plan(p_subscription_id uuid, p_new_plan_id uuid) TO service_role; - - --- --- Name: FUNCTION citext_cmp(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_cmp(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_cmp(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_cmp(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_cmp(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_eq(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_eq(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_eq(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_eq(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_eq(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_ge(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_ge(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_ge(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_ge(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_ge(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_gt(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_gt(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_gt(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_gt(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_gt(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_hash(public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_hash(public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_hash(public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_hash(public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_hash(public.citext) TO service_role; - - --- --- Name: FUNCTION citext_hash_extended(public.citext, bigint); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_hash_extended(public.citext, bigint) TO postgres; -GRANT ALL ON FUNCTION public.citext_hash_extended(public.citext, bigint) TO anon; -GRANT ALL ON FUNCTION public.citext_hash_extended(public.citext, bigint) TO authenticated; -GRANT ALL ON FUNCTION public.citext_hash_extended(public.citext, bigint) TO service_role; - - --- --- Name: FUNCTION citext_larger(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_larger(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_larger(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_larger(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_larger(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_le(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_le(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_le(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_le(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_le(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_lt(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_lt(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_lt(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_lt(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_lt(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_ne(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_ne(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_ne(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_ne(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_ne(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_pattern_cmp(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_pattern_cmp(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_pattern_cmp(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_pattern_cmp(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_pattern_cmp(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_pattern_ge(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_pattern_ge(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_pattern_ge(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_pattern_ge(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_pattern_ge(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_pattern_gt(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_pattern_gt(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_pattern_gt(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_pattern_gt(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_pattern_gt(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_pattern_le(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_pattern_le(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_pattern_le(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_pattern_le(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_pattern_le(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_pattern_lt(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_pattern_lt(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_pattern_lt(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_pattern_lt(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_pattern_lt(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION citext_smaller(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.citext_smaller(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.citext_smaller(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.citext_smaller(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.citext_smaller(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION cleanup_notification_queue(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.cleanup_notification_queue() TO postgres; -GRANT ALL ON FUNCTION public.cleanup_notification_queue() TO anon; -GRANT ALL ON FUNCTION public.cleanup_notification_queue() TO authenticated; -GRANT ALL ON FUNCTION public.cleanup_notification_queue() TO service_role; - - --- --- Name: FUNCTION create_clinic_tenant(p_name text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.create_clinic_tenant(p_name text) TO postgres; -GRANT ALL ON FUNCTION public.create_clinic_tenant(p_name text) TO anon; -GRANT ALL ON FUNCTION public.create_clinic_tenant(p_name text) TO authenticated; -GRANT ALL ON FUNCTION public.create_clinic_tenant(p_name text) TO service_role; - - --- --- Name: TABLE financial_records; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.financial_records TO postgres; -GRANT ALL ON TABLE public.financial_records TO anon; -GRANT ALL ON TABLE public.financial_records TO authenticated; -GRANT ALL ON TABLE public.financial_records TO service_role; - - --- --- Name: FUNCTION 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); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON 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) TO postgres; -GRANT ALL ON 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) TO anon; -GRANT ALL ON 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) TO authenticated; -GRANT ALL ON 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) TO service_role; - - --- --- Name: FUNCTION create_patient_intake_request(p_token text, p_name text, p_email text, p_phone text, p_notes text, p_consent boolean); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.create_patient_intake_request(p_token text, p_name text, p_email text, p_phone text, p_notes text, p_consent boolean) TO postgres; -GRANT ALL ON FUNCTION public.create_patient_intake_request(p_token text, p_name text, p_email text, p_phone text, p_notes text, p_consent boolean) TO anon; -GRANT ALL ON FUNCTION public.create_patient_intake_request(p_token text, p_name text, p_email text, p_phone text, p_notes text, p_consent boolean) TO authenticated; -GRANT ALL ON FUNCTION public.create_patient_intake_request(p_token text, p_name text, p_email text, p_phone text, p_notes text, p_consent boolean) TO service_role; - - --- --- Name: FUNCTION create_patient_intake_request_v2(p_token text, p_payload jsonb); Type: ACL; Schema: public; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION public.create_patient_intake_request_v2(p_token text, p_payload jsonb) FROM PUBLIC; -GRANT ALL ON FUNCTION public.create_patient_intake_request_v2(p_token text, p_payload jsonb) TO postgres; -GRANT ALL ON FUNCTION public.create_patient_intake_request_v2(p_token text, p_payload jsonb) TO anon; -GRANT ALL ON FUNCTION public.create_patient_intake_request_v2(p_token text, p_payload jsonb) TO authenticated; -GRANT ALL ON FUNCTION public.create_patient_intake_request_v2(p_token text, p_payload jsonb) TO service_role; - - --- --- Name: FUNCTION create_support_session(p_tenant_id uuid, p_ttl_minutes integer); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.create_support_session(p_tenant_id uuid, p_ttl_minutes integer) TO postgres; -GRANT ALL ON FUNCTION public.create_support_session(p_tenant_id uuid, p_ttl_minutes integer) TO anon; -GRANT ALL ON FUNCTION public.create_support_session(p_tenant_id uuid, p_ttl_minutes integer) TO authenticated; -GRANT ALL ON FUNCTION public.create_support_session(p_tenant_id uuid, p_ttl_minutes integer) TO service_role; - - --- --- Name: TABLE therapist_payouts; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.therapist_payouts TO postgres; -GRANT ALL ON TABLE public.therapist_payouts TO anon; -GRANT ALL ON TABLE public.therapist_payouts TO authenticated; -GRANT ALL ON TABLE public.therapist_payouts TO service_role; - - --- --- Name: FUNCTION create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date) TO postgres; -GRANT ALL ON FUNCTION public.create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date) TO anon; -GRANT ALL ON FUNCTION public.create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date) TO authenticated; -GRANT ALL ON FUNCTION public.create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date) TO service_role; - - --- --- Name: FUNCTION current_member_id(p_tenant_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.current_member_id(p_tenant_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.current_member_id(p_tenant_id uuid) TO anon; -GRANT ALL ON FUNCTION public.current_member_id(p_tenant_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.current_member_id(p_tenant_id uuid) TO service_role; - - --- --- Name: FUNCTION current_member_role(p_tenant_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.current_member_role(p_tenant_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.current_member_role(p_tenant_id uuid) TO anon; -GRANT ALL ON FUNCTION public.current_member_role(p_tenant_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.current_member_role(p_tenant_id uuid) TO service_role; - - --- --- Name: FUNCTION date_dist(date, date); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.date_dist(date, date) TO postgres; -GRANT ALL ON FUNCTION public.date_dist(date, date) TO anon; -GRANT ALL ON FUNCTION public.date_dist(date, date) TO authenticated; -GRANT ALL ON FUNCTION public.date_dist(date, date) TO service_role; - - --- --- Name: FUNCTION debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text) TO postgres; -GRANT ALL ON FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text) TO anon; -GRANT ALL ON FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text) TO authenticated; -GRANT ALL ON FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text) TO service_role; - - --- --- Name: FUNCTION delete_commitment_full(p_tenant_id uuid, p_commitment_id uuid); Type: ACL; Schema: public; Owner: postgres --- - -GRANT ALL ON FUNCTION public.delete_commitment_full(p_tenant_id uuid, p_commitment_id uuid) TO anon; -GRANT ALL ON FUNCTION public.delete_commitment_full(p_tenant_id uuid, p_commitment_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.delete_commitment_full(p_tenant_id uuid, p_commitment_id uuid) TO service_role; - - --- --- Name: FUNCTION delete_determined_commitment(p_tenant_id uuid, p_commitment_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.delete_determined_commitment(p_tenant_id uuid, p_commitment_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.delete_determined_commitment(p_tenant_id uuid, p_commitment_id uuid) TO anon; -GRANT ALL ON FUNCTION public.delete_determined_commitment(p_tenant_id uuid, p_commitment_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.delete_determined_commitment(p_tenant_id uuid, p_commitment_id uuid) TO service_role; - - --- --- Name: FUNCTION dev_list_auth_users(p_limit integer); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.dev_list_auth_users(p_limit integer) TO postgres; -GRANT ALL ON FUNCTION public.dev_list_auth_users(p_limit integer) TO anon; -GRANT ALL ON FUNCTION public.dev_list_auth_users(p_limit integer) TO authenticated; -GRANT ALL ON FUNCTION public.dev_list_auth_users(p_limit integer) TO service_role; - - --- --- Name: FUNCTION dev_list_custom_users(); Type: ACL; Schema: public; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION public.dev_list_custom_users() FROM PUBLIC; -GRANT ALL ON FUNCTION public.dev_list_custom_users() TO postgres; -GRANT ALL ON FUNCTION public.dev_list_custom_users() TO anon; -GRANT ALL ON FUNCTION public.dev_list_custom_users() TO authenticated; -GRANT ALL ON FUNCTION public.dev_list_custom_users() TO service_role; - - --- --- Name: FUNCTION dev_list_intent_leads(); Type: ACL; Schema: public; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION public.dev_list_intent_leads() FROM PUBLIC; -GRANT ALL ON FUNCTION public.dev_list_intent_leads() TO postgres; -GRANT ALL ON FUNCTION public.dev_list_intent_leads() TO anon; -GRANT ALL ON FUNCTION public.dev_list_intent_leads() TO authenticated; -GRANT ALL ON FUNCTION public.dev_list_intent_leads() TO service_role; - - --- --- Name: FUNCTION dev_public_debug_snapshot(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.dev_public_debug_snapshot() TO postgres; -GRANT ALL ON FUNCTION public.dev_public_debug_snapshot() TO anon; -GRANT ALL ON FUNCTION public.dev_public_debug_snapshot() TO authenticated; -GRANT ALL ON FUNCTION public.dev_public_debug_snapshot() TO service_role; - - --- --- Name: FUNCTION ensure_personal_tenant(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.ensure_personal_tenant() TO postgres; -GRANT ALL ON FUNCTION public.ensure_personal_tenant() TO anon; -GRANT ALL ON FUNCTION public.ensure_personal_tenant() TO authenticated; -GRANT ALL ON FUNCTION public.ensure_personal_tenant() TO service_role; - - --- --- Name: FUNCTION ensure_personal_tenant_for_user(p_user_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.ensure_personal_tenant_for_user(p_user_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.ensure_personal_tenant_for_user(p_user_id uuid) TO anon; -GRANT ALL ON FUNCTION public.ensure_personal_tenant_for_user(p_user_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.ensure_personal_tenant_for_user(p_user_id uuid) TO service_role; - - --- --- Name: FUNCTION faq_votar(faq_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.faq_votar(faq_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.faq_votar(faq_id uuid) TO anon; -GRANT ALL ON FUNCTION public.faq_votar(faq_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.faq_votar(faq_id uuid) TO service_role; - - --- --- Name: FUNCTION fix_all_subscription_mismatches(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.fix_all_subscription_mismatches() TO postgres; -GRANT ALL ON FUNCTION public.fix_all_subscription_mismatches() TO anon; -GRANT ALL ON FUNCTION public.fix_all_subscription_mismatches() TO authenticated; -GRANT ALL ON FUNCTION public.fix_all_subscription_mismatches() TO service_role; - - --- --- Name: FUNCTION float4_dist(real, real); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.float4_dist(real, real) TO postgres; -GRANT ALL ON FUNCTION public.float4_dist(real, real) TO anon; -GRANT ALL ON FUNCTION public.float4_dist(real, real) TO authenticated; -GRANT ALL ON FUNCTION public.float4_dist(real, real) TO service_role; - - --- --- Name: FUNCTION float8_dist(double precision, double precision); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.float8_dist(double precision, double precision) TO postgres; -GRANT ALL ON FUNCTION public.float8_dist(double precision, double precision) TO anon; -GRANT ALL ON FUNCTION public.float8_dist(double precision, double precision) TO authenticated; -GRANT ALL ON FUNCTION public.float8_dist(double precision, double precision) TO service_role; - - --- --- Name: FUNCTION fn_agenda_regras_semanais_no_overlap(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.fn_agenda_regras_semanais_no_overlap() TO postgres; -GRANT ALL ON FUNCTION public.fn_agenda_regras_semanais_no_overlap() TO anon; -GRANT ALL ON FUNCTION public.fn_agenda_regras_semanais_no_overlap() TO authenticated; -GRANT ALL ON FUNCTION public.fn_agenda_regras_semanais_no_overlap() TO service_role; - - --- --- Name: FUNCTION gbt_bit_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bit_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bit_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bit_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bit_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_bit_consistent(internal, bit, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bit_consistent(internal, bit, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bit_consistent(internal, bit, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bit_consistent(internal, bit, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bit_consistent(internal, bit, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bit_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bit_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bit_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bit_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bit_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bit_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bit_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bit_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bit_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bit_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bit_same(public.gbtreekey_var, public.gbtreekey_var, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bit_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bit_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bit_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bit_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bit_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bit_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bit_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bit_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bit_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bool_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bool_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bool_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bool_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bool_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_bool_consistent(internal, boolean, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bool_consistent(internal, boolean, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bool_consistent(internal, boolean, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bool_consistent(internal, boolean, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bool_consistent(internal, boolean, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bool_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bool_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bool_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bool_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bool_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_bool_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bool_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bool_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bool_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bool_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bool_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bool_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bool_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bool_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bool_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bool_same(public.gbtreekey2, public.gbtreekey2, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bool_same(public.gbtreekey2, public.gbtreekey2, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bool_same(public.gbtreekey2, public.gbtreekey2, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bool_same(public.gbtreekey2, public.gbtreekey2, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bool_same(public.gbtreekey2, public.gbtreekey2, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bool_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bool_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bool_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bool_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bool_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bpchar_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bpchar_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bpchar_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bpchar_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bpchar_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_bpchar_consistent(internal, character, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bpchar_consistent(internal, character, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bpchar_consistent(internal, character, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bpchar_consistent(internal, character, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bpchar_consistent(internal, character, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bytea_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bytea_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bytea_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bytea_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bytea_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_bytea_consistent(internal, bytea, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bytea_consistent(internal, bytea, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bytea_consistent(internal, bytea, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bytea_consistent(internal, bytea, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bytea_consistent(internal, bytea, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bytea_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bytea_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bytea_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bytea_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bytea_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bytea_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bytea_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bytea_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bytea_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bytea_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bytea_same(public.gbtreekey_var, public.gbtreekey_var, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bytea_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bytea_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bytea_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bytea_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO service_role; - - --- --- Name: FUNCTION gbt_bytea_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_bytea_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_bytea_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_bytea_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_bytea_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_cash_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_cash_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_cash_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_cash_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_cash_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_cash_consistent(internal, money, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_cash_consistent(internal, money, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_cash_consistent(internal, money, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_cash_consistent(internal, money, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_cash_consistent(internal, money, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_cash_distance(internal, money, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_cash_distance(internal, money, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_cash_distance(internal, money, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_cash_distance(internal, money, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_cash_distance(internal, money, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_cash_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_cash_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_cash_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_cash_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_cash_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_cash_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_cash_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_cash_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_cash_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_cash_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_cash_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_cash_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_cash_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_cash_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_cash_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_cash_same(public.gbtreekey16, public.gbtreekey16, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_cash_same(public.gbtreekey16, public.gbtreekey16, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_cash_same(public.gbtreekey16, public.gbtreekey16, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_cash_same(public.gbtreekey16, public.gbtreekey16, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_cash_same(public.gbtreekey16, public.gbtreekey16, internal) TO service_role; - - --- --- Name: FUNCTION gbt_cash_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_cash_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_cash_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_cash_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_cash_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_date_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_date_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_date_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_date_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_date_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_date_consistent(internal, date, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_date_consistent(internal, date, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_date_consistent(internal, date, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_date_consistent(internal, date, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_date_consistent(internal, date, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_date_distance(internal, date, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_date_distance(internal, date, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_date_distance(internal, date, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_date_distance(internal, date, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_date_distance(internal, date, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_date_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_date_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_date_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_date_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_date_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_date_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_date_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_date_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_date_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_date_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_date_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_date_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_date_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_date_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_date_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_date_same(public.gbtreekey8, public.gbtreekey8, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_date_same(public.gbtreekey8, public.gbtreekey8, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_date_same(public.gbtreekey8, public.gbtreekey8, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_date_same(public.gbtreekey8, public.gbtreekey8, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_date_same(public.gbtreekey8, public.gbtreekey8, internal) TO service_role; - - --- --- Name: FUNCTION gbt_date_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_date_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_date_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_date_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_date_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_decompress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_decompress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_decompress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_decompress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_decompress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_enum_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_enum_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_enum_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_enum_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_enum_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_enum_consistent(internal, anyenum, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_enum_consistent(internal, anyenum, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_enum_consistent(internal, anyenum, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_enum_consistent(internal, anyenum, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_enum_consistent(internal, anyenum, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_enum_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_enum_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_enum_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_enum_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_enum_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_enum_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_enum_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_enum_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_enum_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_enum_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_enum_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_enum_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_enum_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_enum_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_enum_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_enum_same(public.gbtreekey8, public.gbtreekey8, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_enum_same(public.gbtreekey8, public.gbtreekey8, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_enum_same(public.gbtreekey8, public.gbtreekey8, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_enum_same(public.gbtreekey8, public.gbtreekey8, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_enum_same(public.gbtreekey8, public.gbtreekey8, internal) TO service_role; - - --- --- Name: FUNCTION gbt_enum_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_enum_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_enum_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_enum_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_enum_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float4_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float4_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float4_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float4_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float4_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_float4_consistent(internal, real, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float4_consistent(internal, real, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float4_consistent(internal, real, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float4_consistent(internal, real, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float4_consistent(internal, real, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float4_distance(internal, real, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float4_distance(internal, real, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float4_distance(internal, real, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float4_distance(internal, real, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float4_distance(internal, real, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float4_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float4_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float4_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float4_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float4_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_float4_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float4_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float4_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float4_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float4_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float4_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float4_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float4_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float4_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float4_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float4_same(public.gbtreekey8, public.gbtreekey8, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float4_same(public.gbtreekey8, public.gbtreekey8, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float4_same(public.gbtreekey8, public.gbtreekey8, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float4_same(public.gbtreekey8, public.gbtreekey8, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float4_same(public.gbtreekey8, public.gbtreekey8, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float4_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float4_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float4_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float4_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float4_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float8_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float8_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float8_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float8_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float8_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_float8_consistent(internal, double precision, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float8_consistent(internal, double precision, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float8_consistent(internal, double precision, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float8_consistent(internal, double precision, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float8_consistent(internal, double precision, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float8_distance(internal, double precision, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float8_distance(internal, double precision, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float8_distance(internal, double precision, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float8_distance(internal, double precision, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float8_distance(internal, double precision, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float8_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float8_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float8_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float8_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float8_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_float8_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float8_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float8_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float8_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float8_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float8_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float8_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float8_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float8_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float8_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float8_same(public.gbtreekey16, public.gbtreekey16, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float8_same(public.gbtreekey16, public.gbtreekey16, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float8_same(public.gbtreekey16, public.gbtreekey16, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float8_same(public.gbtreekey16, public.gbtreekey16, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float8_same(public.gbtreekey16, public.gbtreekey16, internal) TO service_role; - - --- --- Name: FUNCTION gbt_float8_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_float8_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_float8_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_float8_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_float8_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_inet_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_inet_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_inet_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_inet_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_inet_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_inet_consistent(internal, inet, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_inet_consistent(internal, inet, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_inet_consistent(internal, inet, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_inet_consistent(internal, inet, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_inet_consistent(internal, inet, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_inet_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_inet_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_inet_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_inet_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_inet_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_inet_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_inet_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_inet_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_inet_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_inet_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_inet_same(public.gbtreekey16, public.gbtreekey16, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_inet_same(public.gbtreekey16, public.gbtreekey16, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_inet_same(public.gbtreekey16, public.gbtreekey16, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_inet_same(public.gbtreekey16, public.gbtreekey16, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_inet_same(public.gbtreekey16, public.gbtreekey16, internal) TO service_role; - - --- --- Name: FUNCTION gbt_inet_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_inet_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_inet_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_inet_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_inet_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int2_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int2_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int2_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int2_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int2_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_int2_consistent(internal, smallint, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int2_consistent(internal, smallint, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int2_consistent(internal, smallint, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int2_consistent(internal, smallint, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int2_consistent(internal, smallint, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int2_distance(internal, smallint, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int2_distance(internal, smallint, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int2_distance(internal, smallint, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int2_distance(internal, smallint, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int2_distance(internal, smallint, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int2_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int2_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int2_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int2_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int2_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_int2_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int2_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int2_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int2_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int2_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int2_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int2_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int2_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int2_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int2_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int2_same(public.gbtreekey4, public.gbtreekey4, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int2_same(public.gbtreekey4, public.gbtreekey4, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int2_same(public.gbtreekey4, public.gbtreekey4, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int2_same(public.gbtreekey4, public.gbtreekey4, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int2_same(public.gbtreekey4, public.gbtreekey4, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int2_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int2_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int2_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int2_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int2_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int4_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int4_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int4_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int4_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int4_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_int4_consistent(internal, integer, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int4_consistent(internal, integer, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int4_consistent(internal, integer, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int4_consistent(internal, integer, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int4_consistent(internal, integer, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int4_distance(internal, integer, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int4_distance(internal, integer, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int4_distance(internal, integer, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int4_distance(internal, integer, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int4_distance(internal, integer, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int4_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int4_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int4_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int4_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int4_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_int4_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int4_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int4_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int4_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int4_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int4_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int4_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int4_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int4_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int4_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int4_same(public.gbtreekey8, public.gbtreekey8, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int4_same(public.gbtreekey8, public.gbtreekey8, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int4_same(public.gbtreekey8, public.gbtreekey8, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int4_same(public.gbtreekey8, public.gbtreekey8, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int4_same(public.gbtreekey8, public.gbtreekey8, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int4_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int4_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int4_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int4_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int4_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int8_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int8_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int8_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int8_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int8_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_int8_consistent(internal, bigint, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int8_consistent(internal, bigint, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int8_consistent(internal, bigint, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int8_consistent(internal, bigint, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int8_consistent(internal, bigint, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int8_distance(internal, bigint, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int8_distance(internal, bigint, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int8_distance(internal, bigint, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int8_distance(internal, bigint, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int8_distance(internal, bigint, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int8_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int8_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int8_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int8_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int8_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_int8_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int8_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int8_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int8_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int8_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int8_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int8_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int8_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int8_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int8_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int8_same(public.gbtreekey16, public.gbtreekey16, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int8_same(public.gbtreekey16, public.gbtreekey16, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int8_same(public.gbtreekey16, public.gbtreekey16, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int8_same(public.gbtreekey16, public.gbtreekey16, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int8_same(public.gbtreekey16, public.gbtreekey16, internal) TO service_role; - - --- --- Name: FUNCTION gbt_int8_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_int8_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_int8_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_int8_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_int8_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_intv_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_intv_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_intv_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_intv_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_intv_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_intv_consistent(internal, interval, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_intv_consistent(internal, interval, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_intv_consistent(internal, interval, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_intv_consistent(internal, interval, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_intv_consistent(internal, interval, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_intv_decompress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_intv_decompress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_intv_decompress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_intv_decompress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_intv_decompress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_intv_distance(internal, interval, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_intv_distance(internal, interval, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_intv_distance(internal, interval, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_intv_distance(internal, interval, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_intv_distance(internal, interval, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_intv_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_intv_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_intv_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_intv_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_intv_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_intv_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_intv_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_intv_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_intv_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_intv_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_intv_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_intv_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_intv_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_intv_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_intv_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_intv_same(public.gbtreekey32, public.gbtreekey32, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_intv_same(public.gbtreekey32, public.gbtreekey32, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_intv_same(public.gbtreekey32, public.gbtreekey32, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_intv_same(public.gbtreekey32, public.gbtreekey32, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_intv_same(public.gbtreekey32, public.gbtreekey32, internal) TO service_role; - - --- --- Name: FUNCTION gbt_intv_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_intv_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_intv_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_intv_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_intv_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad8_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad8_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad8_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad8_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad8_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad8_consistent(internal, macaddr8, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad8_consistent(internal, macaddr8, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad8_consistent(internal, macaddr8, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad8_consistent(internal, macaddr8, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad8_consistent(internal, macaddr8, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad8_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad8_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad8_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad8_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad8_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad8_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad8_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad8_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad8_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad8_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad8_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad8_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad8_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad8_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad8_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad8_same(public.gbtreekey16, public.gbtreekey16, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad8_same(public.gbtreekey16, public.gbtreekey16, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad8_same(public.gbtreekey16, public.gbtreekey16, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad8_same(public.gbtreekey16, public.gbtreekey16, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad8_same(public.gbtreekey16, public.gbtreekey16, internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad8_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad8_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad8_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad8_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad8_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad_consistent(internal, macaddr, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad_consistent(internal, macaddr, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad_consistent(internal, macaddr, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad_consistent(internal, macaddr, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad_consistent(internal, macaddr, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad_same(public.gbtreekey16, public.gbtreekey16, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad_same(public.gbtreekey16, public.gbtreekey16, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad_same(public.gbtreekey16, public.gbtreekey16, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad_same(public.gbtreekey16, public.gbtreekey16, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad_same(public.gbtreekey16, public.gbtreekey16, internal) TO service_role; - - --- --- Name: FUNCTION gbt_macad_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_macad_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_macad_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_macad_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_macad_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_numeric_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_numeric_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_numeric_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_numeric_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_numeric_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_numeric_consistent(internal, numeric, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_numeric_consistent(internal, numeric, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_numeric_consistent(internal, numeric, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_numeric_consistent(internal, numeric, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_numeric_consistent(internal, numeric, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_numeric_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_numeric_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_numeric_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_numeric_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_numeric_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_numeric_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_numeric_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_numeric_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_numeric_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_numeric_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_numeric_same(public.gbtreekey_var, public.gbtreekey_var, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_numeric_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_numeric_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_numeric_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_numeric_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO service_role; - - --- --- Name: FUNCTION gbt_numeric_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_numeric_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_numeric_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_numeric_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_numeric_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_oid_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_oid_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_oid_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_oid_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_oid_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_oid_consistent(internal, oid, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_oid_consistent(internal, oid, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_oid_consistent(internal, oid, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_oid_consistent(internal, oid, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_oid_consistent(internal, oid, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_oid_distance(internal, oid, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_oid_distance(internal, oid, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_oid_distance(internal, oid, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_oid_distance(internal, oid, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_oid_distance(internal, oid, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_oid_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_oid_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_oid_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_oid_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_oid_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_oid_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_oid_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_oid_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_oid_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_oid_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_oid_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_oid_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_oid_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_oid_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_oid_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_oid_same(public.gbtreekey8, public.gbtreekey8, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_oid_same(public.gbtreekey8, public.gbtreekey8, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_oid_same(public.gbtreekey8, public.gbtreekey8, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_oid_same(public.gbtreekey8, public.gbtreekey8, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_oid_same(public.gbtreekey8, public.gbtreekey8, internal) TO service_role; - - --- --- Name: FUNCTION gbt_oid_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_oid_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_oid_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_oid_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_oid_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_text_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_text_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_text_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_text_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_text_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_text_consistent(internal, text, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_text_consistent(internal, text, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_text_consistent(internal, text, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_text_consistent(internal, text, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_text_consistent(internal, text, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_text_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_text_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_text_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_text_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_text_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_text_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_text_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_text_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_text_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_text_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_text_same(public.gbtreekey_var, public.gbtreekey_var, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_text_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_text_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_text_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_text_same(public.gbtreekey_var, public.gbtreekey_var, internal) TO service_role; - - --- --- Name: FUNCTION gbt_text_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_text_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_text_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_text_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_text_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_time_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_time_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_time_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_time_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_time_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_time_consistent(internal, time without time zone, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_time_consistent(internal, time without time zone, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_time_consistent(internal, time without time zone, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_time_consistent(internal, time without time zone, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_time_consistent(internal, time without time zone, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_time_distance(internal, time without time zone, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_time_distance(internal, time without time zone, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_time_distance(internal, time without time zone, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_time_distance(internal, time without time zone, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_time_distance(internal, time without time zone, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_time_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_time_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_time_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_time_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_time_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_time_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_time_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_time_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_time_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_time_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_time_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_time_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_time_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_time_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_time_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_time_same(public.gbtreekey16, public.gbtreekey16, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_time_same(public.gbtreekey16, public.gbtreekey16, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_time_same(public.gbtreekey16, public.gbtreekey16, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_time_same(public.gbtreekey16, public.gbtreekey16, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_time_same(public.gbtreekey16, public.gbtreekey16, internal) TO service_role; - - --- --- Name: FUNCTION gbt_time_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_time_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_time_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_time_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_time_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_timetz_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_timetz_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_timetz_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_timetz_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_timetz_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_timetz_consistent(internal, time with time zone, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_timetz_consistent(internal, time with time zone, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_timetz_consistent(internal, time with time zone, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_timetz_consistent(internal, time with time zone, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_timetz_consistent(internal, time with time zone, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_ts_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_ts_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_ts_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_ts_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_ts_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_ts_consistent(internal, timestamp without time zone, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_ts_consistent(internal, timestamp without time zone, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_ts_consistent(internal, timestamp without time zone, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_ts_consistent(internal, timestamp without time zone, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_ts_consistent(internal, timestamp without time zone, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_ts_distance(internal, timestamp without time zone, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_ts_distance(internal, timestamp without time zone, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_ts_distance(internal, timestamp without time zone, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_ts_distance(internal, timestamp without time zone, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_ts_distance(internal, timestamp without time zone, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_ts_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_ts_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_ts_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_ts_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_ts_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_ts_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_ts_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_ts_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_ts_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_ts_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_ts_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_ts_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_ts_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_ts_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_ts_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_ts_same(public.gbtreekey16, public.gbtreekey16, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_ts_same(public.gbtreekey16, public.gbtreekey16, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_ts_same(public.gbtreekey16, public.gbtreekey16, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_ts_same(public.gbtreekey16, public.gbtreekey16, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_ts_same(public.gbtreekey16, public.gbtreekey16, internal) TO service_role; - - --- --- Name: FUNCTION gbt_ts_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_ts_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_ts_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_ts_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_ts_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_tstz_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_tstz_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_tstz_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_tstz_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_tstz_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_tstz_consistent(internal, timestamp with time zone, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_tstz_consistent(internal, timestamp with time zone, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_tstz_consistent(internal, timestamp with time zone, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_tstz_consistent(internal, timestamp with time zone, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_tstz_consistent(internal, timestamp with time zone, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_tstz_distance(internal, timestamp with time zone, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_tstz_distance(internal, timestamp with time zone, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_tstz_distance(internal, timestamp with time zone, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_tstz_distance(internal, timestamp with time zone, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_tstz_distance(internal, timestamp with time zone, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_uuid_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_uuid_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_uuid_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_uuid_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_uuid_compress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_uuid_consistent(internal, uuid, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_uuid_consistent(internal, uuid, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_uuid_consistent(internal, uuid, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_uuid_consistent(internal, uuid, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_uuid_consistent(internal, uuid, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gbt_uuid_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_uuid_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_uuid_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_uuid_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_uuid_fetch(internal) TO service_role; - - --- --- Name: FUNCTION gbt_uuid_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_uuid_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_uuid_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_uuid_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_uuid_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_uuid_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_uuid_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_uuid_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_uuid_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_uuid_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_uuid_same(public.gbtreekey32, public.gbtreekey32, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_uuid_same(public.gbtreekey32, public.gbtreekey32, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_uuid_same(public.gbtreekey32, public.gbtreekey32, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_uuid_same(public.gbtreekey32, public.gbtreekey32, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_uuid_same(public.gbtreekey32, public.gbtreekey32, internal) TO service_role; - - --- --- Name: FUNCTION gbt_uuid_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_uuid_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_uuid_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_uuid_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_uuid_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION gbt_var_decompress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_var_decompress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_var_decompress(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_var_decompress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_var_decompress(internal) TO service_role; - - --- --- Name: FUNCTION gbt_var_fetch(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gbt_var_fetch(internal) TO postgres; -GRANT ALL ON FUNCTION public.gbt_var_fetch(internal) TO anon; -GRANT ALL ON FUNCTION public.gbt_var_fetch(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gbt_var_fetch(internal) TO service_role; - - --- --- Name: FUNCTION get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text) TO postgres; -GRANT ALL ON FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text) TO anon; -GRANT ALL ON FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text) TO authenticated; -GRANT ALL ON FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text) TO service_role; - - --- --- Name: FUNCTION get_financial_summary(p_owner_id uuid, p_year integer, p_month integer); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.get_financial_summary(p_owner_id uuid, p_year integer, p_month integer) TO postgres; -GRANT ALL ON FUNCTION public.get_financial_summary(p_owner_id uuid, p_year integer, p_month integer) TO anon; -GRANT ALL ON FUNCTION public.get_financial_summary(p_owner_id uuid, p_year integer, p_month integer) TO authenticated; -GRANT ALL ON FUNCTION public.get_financial_summary(p_owner_id uuid, p_year integer, p_month integer) TO service_role; - - --- --- Name: FUNCTION get_my_email(); Type: ACL; Schema: public; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION public.get_my_email() FROM PUBLIC; -GRANT ALL ON FUNCTION public.get_my_email() TO postgres; -GRANT ALL ON FUNCTION public.get_my_email() TO anon; -GRANT ALL ON FUNCTION public.get_my_email() TO authenticated; -GRANT ALL ON FUNCTION public.get_my_email() TO service_role; - - --- --- Name: FUNCTION gin_extract_query_trgm(text, internal, smallint, internal, internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gin_extract_query_trgm(text, internal, smallint, internal, internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gin_extract_query_trgm(text, internal, smallint, internal, internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gin_extract_query_trgm(text, internal, smallint, internal, internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gin_extract_query_trgm(text, internal, smallint, internal, internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gin_extract_value_trgm(text, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gin_extract_value_trgm(text, internal) TO postgres; -GRANT ALL ON FUNCTION public.gin_extract_value_trgm(text, internal) TO anon; -GRANT ALL ON FUNCTION public.gin_extract_value_trgm(text, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gin_extract_value_trgm(text, internal) TO service_role; - - --- --- Name: FUNCTION gin_trgm_consistent(internal, smallint, text, integer, internal, internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gin_trgm_consistent(internal, smallint, text, integer, internal, internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gin_trgm_consistent(internal, smallint, text, integer, internal, internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gin_trgm_consistent(internal, smallint, text, integer, internal, internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gin_trgm_consistent(internal, smallint, text, integer, internal, internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gin_trgm_triconsistent(internal, smallint, text, integer, internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gin_trgm_triconsistent(internal, smallint, text, integer, internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gin_trgm_triconsistent(internal, smallint, text, integer, internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gin_trgm_triconsistent(internal, smallint, text, integer, internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gin_trgm_triconsistent(internal, smallint, text, integer, internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gtrgm_compress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gtrgm_compress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gtrgm_compress(internal) TO anon; -GRANT ALL ON FUNCTION public.gtrgm_compress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gtrgm_compress(internal) TO service_role; - - --- --- Name: FUNCTION gtrgm_consistent(internal, text, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gtrgm_consistent(internal, text, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gtrgm_consistent(internal, text, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gtrgm_consistent(internal, text, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gtrgm_consistent(internal, text, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gtrgm_decompress(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gtrgm_decompress(internal) TO postgres; -GRANT ALL ON FUNCTION public.gtrgm_decompress(internal) TO anon; -GRANT ALL ON FUNCTION public.gtrgm_decompress(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gtrgm_decompress(internal) TO service_role; - - --- --- Name: FUNCTION gtrgm_distance(internal, text, smallint, oid, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gtrgm_distance(internal, text, smallint, oid, internal) TO postgres; -GRANT ALL ON FUNCTION public.gtrgm_distance(internal, text, smallint, oid, internal) TO anon; -GRANT ALL ON FUNCTION public.gtrgm_distance(internal, text, smallint, oid, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gtrgm_distance(internal, text, smallint, oid, internal) TO service_role; - - --- --- Name: FUNCTION gtrgm_options(internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gtrgm_options(internal) TO postgres; -GRANT ALL ON FUNCTION public.gtrgm_options(internal) TO anon; -GRANT ALL ON FUNCTION public.gtrgm_options(internal) TO authenticated; -GRANT ALL ON FUNCTION public.gtrgm_options(internal) TO service_role; - - --- --- Name: FUNCTION gtrgm_penalty(internal, internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gtrgm_penalty(internal, internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gtrgm_penalty(internal, internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gtrgm_penalty(internal, internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gtrgm_penalty(internal, internal, internal) TO service_role; - - --- --- Name: FUNCTION gtrgm_picksplit(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gtrgm_picksplit(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gtrgm_picksplit(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gtrgm_picksplit(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gtrgm_picksplit(internal, internal) TO service_role; - - --- --- Name: FUNCTION gtrgm_same(public.gtrgm, public.gtrgm, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gtrgm_same(public.gtrgm, public.gtrgm, internal) TO postgres; -GRANT ALL ON FUNCTION public.gtrgm_same(public.gtrgm, public.gtrgm, internal) TO anon; -GRANT ALL ON FUNCTION public.gtrgm_same(public.gtrgm, public.gtrgm, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gtrgm_same(public.gtrgm, public.gtrgm, internal) TO service_role; - - --- --- Name: FUNCTION gtrgm_union(internal, internal); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.gtrgm_union(internal, internal) TO postgres; -GRANT ALL ON FUNCTION public.gtrgm_union(internal, internal) TO anon; -GRANT ALL ON FUNCTION public.gtrgm_union(internal, internal) TO authenticated; -GRANT ALL ON FUNCTION public.gtrgm_union(internal, internal) TO service_role; - - --- --- Name: FUNCTION guard_account_type_immutable(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.guard_account_type_immutable() TO postgres; -GRANT ALL ON FUNCTION public.guard_account_type_immutable() TO anon; -GRANT ALL ON FUNCTION public.guard_account_type_immutable() TO authenticated; -GRANT ALL ON FUNCTION public.guard_account_type_immutable() TO service_role; - - --- --- Name: FUNCTION guard_locked_commitment(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.guard_locked_commitment() TO postgres; -GRANT ALL ON FUNCTION public.guard_locked_commitment() TO anon; -GRANT ALL ON FUNCTION public.guard_locked_commitment() TO authenticated; -GRANT ALL ON FUNCTION public.guard_locked_commitment() TO service_role; - - --- --- Name: FUNCTION guard_no_change_core_plan_key(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.guard_no_change_core_plan_key() TO postgres; -GRANT ALL ON FUNCTION public.guard_no_change_core_plan_key() TO anon; -GRANT ALL ON FUNCTION public.guard_no_change_core_plan_key() TO authenticated; -GRANT ALL ON FUNCTION public.guard_no_change_core_plan_key() TO service_role; - - --- --- Name: FUNCTION guard_no_change_plan_target(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.guard_no_change_plan_target() TO postgres; -GRANT ALL ON FUNCTION public.guard_no_change_plan_target() TO anon; -GRANT ALL ON FUNCTION public.guard_no_change_plan_target() TO authenticated; -GRANT ALL ON FUNCTION public.guard_no_change_plan_target() TO service_role; - - --- --- Name: FUNCTION guard_no_delete_core_plans(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.guard_no_delete_core_plans() TO postgres; -GRANT ALL ON FUNCTION public.guard_no_delete_core_plans() TO anon; -GRANT ALL ON FUNCTION public.guard_no_delete_core_plans() TO authenticated; -GRANT ALL ON FUNCTION public.guard_no_delete_core_plans() TO service_role; - - --- --- Name: FUNCTION guard_patient_cannot_own_tenant(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.guard_patient_cannot_own_tenant() TO postgres; -GRANT ALL ON FUNCTION public.guard_patient_cannot_own_tenant() TO anon; -GRANT ALL ON FUNCTION public.guard_patient_cannot_own_tenant() TO authenticated; -GRANT ALL ON FUNCTION public.guard_patient_cannot_own_tenant() TO service_role; - - --- --- Name: FUNCTION guard_tenant_kind_immutable(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.guard_tenant_kind_immutable() TO postgres; -GRANT ALL ON FUNCTION public.guard_tenant_kind_immutable() TO anon; -GRANT ALL ON FUNCTION public.guard_tenant_kind_immutable() TO authenticated; -GRANT ALL ON FUNCTION public.guard_tenant_kind_immutable() TO service_role; - - --- --- Name: FUNCTION handle_new_user(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.handle_new_user() TO postgres; -GRANT ALL ON FUNCTION public.handle_new_user() TO anon; -GRANT ALL ON FUNCTION public.handle_new_user() TO authenticated; -GRANT ALL ON FUNCTION public.handle_new_user() TO service_role; - - --- --- Name: FUNCTION handle_new_user_create_personal_tenant(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.handle_new_user_create_personal_tenant() TO postgres; -GRANT ALL ON FUNCTION public.handle_new_user_create_personal_tenant() TO anon; -GRANT ALL ON FUNCTION public.handle_new_user_create_personal_tenant() TO authenticated; -GRANT ALL ON FUNCTION public.handle_new_user_create_personal_tenant() TO service_role; - - --- --- Name: FUNCTION has_feature(p_owner_id uuid, p_feature_key text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.has_feature(p_owner_id uuid, p_feature_key text) TO postgres; -GRANT ALL ON FUNCTION public.has_feature(p_owner_id uuid, p_feature_key text) TO anon; -GRANT ALL ON FUNCTION public.has_feature(p_owner_id uuid, p_feature_key text) TO authenticated; -GRANT ALL ON FUNCTION public.has_feature(p_owner_id uuid, p_feature_key text) TO service_role; - - --- --- Name: FUNCTION int2_dist(smallint, smallint); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.int2_dist(smallint, smallint) TO postgres; -GRANT ALL ON FUNCTION public.int2_dist(smallint, smallint) TO anon; -GRANT ALL ON FUNCTION public.int2_dist(smallint, smallint) TO authenticated; -GRANT ALL ON FUNCTION public.int2_dist(smallint, smallint) TO service_role; - - --- --- Name: FUNCTION int4_dist(integer, integer); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.int4_dist(integer, integer) TO postgres; -GRANT ALL ON FUNCTION public.int4_dist(integer, integer) TO anon; -GRANT ALL ON FUNCTION public.int4_dist(integer, integer) TO authenticated; -GRANT ALL ON FUNCTION public.int4_dist(integer, integer) TO service_role; - - --- --- Name: FUNCTION int8_dist(bigint, bigint); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.int8_dist(bigint, bigint) TO postgres; -GRANT ALL ON FUNCTION public.int8_dist(bigint, bigint) TO anon; -GRANT ALL ON FUNCTION public.int8_dist(bigint, bigint) TO authenticated; -GRANT ALL ON FUNCTION public.int8_dist(bigint, bigint) TO service_role; - - --- --- Name: FUNCTION interval_dist(interval, interval); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.interval_dist(interval, interval) TO postgres; -GRANT ALL ON FUNCTION public.interval_dist(interval, interval) TO anon; -GRANT ALL ON FUNCTION public.interval_dist(interval, interval) TO authenticated; -GRANT ALL ON FUNCTION public.interval_dist(interval, interval) TO service_role; - - --- --- Name: FUNCTION is_clinic_tenant(_tenant_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.is_clinic_tenant(_tenant_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.is_clinic_tenant(_tenant_id uuid) TO anon; -GRANT ALL ON FUNCTION public.is_clinic_tenant(_tenant_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.is_clinic_tenant(_tenant_id uuid) TO service_role; - - --- --- Name: FUNCTION is_saas_admin(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.is_saas_admin() TO postgres; -GRANT ALL ON FUNCTION public.is_saas_admin() TO anon; -GRANT ALL ON FUNCTION public.is_saas_admin() TO authenticated; -GRANT ALL ON FUNCTION public.is_saas_admin() TO service_role; - - --- --- Name: FUNCTION is_tenant_admin(p_tenant_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.is_tenant_admin(p_tenant_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.is_tenant_admin(p_tenant_id uuid) TO anon; -GRANT ALL ON FUNCTION public.is_tenant_admin(p_tenant_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.is_tenant_admin(p_tenant_id uuid) TO service_role; - - --- --- Name: FUNCTION is_tenant_member(_tenant_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.is_tenant_member(_tenant_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.is_tenant_member(_tenant_id uuid) TO anon; -GRANT ALL ON FUNCTION public.is_tenant_member(_tenant_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.is_tenant_member(_tenant_id uuid) TO service_role; - - --- --- Name: FUNCTION is_therapist_tenant(_tenant_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.is_therapist_tenant(_tenant_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.is_therapist_tenant(_tenant_id uuid) TO anon; -GRANT ALL ON FUNCTION public.is_therapist_tenant(_tenant_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.is_therapist_tenant(_tenant_id uuid) TO service_role; - - --- --- Name: FUNCTION jwt_email(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.jwt_email() TO postgres; -GRANT ALL ON FUNCTION public.jwt_email() TO anon; -GRANT ALL ON FUNCTION public.jwt_email() TO authenticated; -GRANT ALL ON FUNCTION public.jwt_email() TO service_role; - - --- --- Name: FUNCTION 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); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON 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) TO postgres; -GRANT ALL ON 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) TO anon; -GRANT ALL ON 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) TO authenticated; -GRANT ALL ON 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) TO service_role; - - --- --- Name: FUNCTION mark_as_paid(p_financial_record_id uuid, p_payment_method text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.mark_as_paid(p_financial_record_id uuid, p_payment_method text) TO postgres; -GRANT ALL ON FUNCTION public.mark_as_paid(p_financial_record_id uuid, p_payment_method text) TO anon; -GRANT ALL ON FUNCTION public.mark_as_paid(p_financial_record_id uuid, p_payment_method text) TO authenticated; -GRANT ALL ON FUNCTION public.mark_as_paid(p_financial_record_id uuid, p_payment_method text) TO service_role; - - --- --- Name: FUNCTION mark_payout_as_paid(p_payout_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.mark_payout_as_paid(p_payout_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.mark_payout_as_paid(p_payout_id uuid) TO anon; -GRANT ALL ON FUNCTION public.mark_payout_as_paid(p_payout_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.mark_payout_as_paid(p_payout_id uuid) TO service_role; - - --- --- Name: FUNCTION my_tenants(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.my_tenants() TO postgres; -GRANT ALL ON FUNCTION public.my_tenants() TO anon; -GRANT ALL ON FUNCTION public.my_tenants() TO authenticated; -GRANT ALL ON FUNCTION public.my_tenants() TO service_role; - - --- --- Name: FUNCTION notice_track_click(p_notice_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.notice_track_click(p_notice_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.notice_track_click(p_notice_id uuid) TO anon; -GRANT ALL ON FUNCTION public.notice_track_click(p_notice_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.notice_track_click(p_notice_id uuid) TO service_role; - - --- --- Name: FUNCTION notice_track_view(p_notice_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.notice_track_view(p_notice_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.notice_track_view(p_notice_id uuid) TO anon; -GRANT ALL ON FUNCTION public.notice_track_view(p_notice_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.notice_track_view(p_notice_id uuid) TO service_role; - - --- --- Name: FUNCTION notify_on_intake(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.notify_on_intake() TO postgres; -GRANT ALL ON FUNCTION public.notify_on_intake() TO anon; -GRANT ALL ON FUNCTION public.notify_on_intake() TO authenticated; -GRANT ALL ON FUNCTION public.notify_on_intake() TO service_role; - - --- --- Name: FUNCTION notify_on_scheduling(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.notify_on_scheduling() TO postgres; -GRANT ALL ON FUNCTION public.notify_on_scheduling() TO anon; -GRANT ALL ON FUNCTION public.notify_on_scheduling() TO authenticated; -GRANT ALL ON FUNCTION public.notify_on_scheduling() TO service_role; - - --- --- Name: FUNCTION notify_on_session_status(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.notify_on_session_status() TO postgres; -GRANT ALL ON FUNCTION public.notify_on_session_status() TO anon; -GRANT ALL ON FUNCTION public.notify_on_session_status() TO authenticated; -GRANT ALL ON FUNCTION public.notify_on_session_status() TO service_role; - - --- --- Name: FUNCTION oid_dist(oid, oid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.oid_dist(oid, oid) TO postgres; -GRANT ALL ON FUNCTION public.oid_dist(oid, oid) TO anon; -GRANT ALL ON FUNCTION public.oid_dist(oid, oid) TO authenticated; -GRANT ALL ON FUNCTION public.oid_dist(oid, oid) TO service_role; - - --- --- Name: FUNCTION on_new_user_seed_patient_groups(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.on_new_user_seed_patient_groups() TO postgres; -GRANT ALL ON FUNCTION public.on_new_user_seed_patient_groups() TO anon; -GRANT ALL ON FUNCTION public.on_new_user_seed_patient_groups() TO authenticated; -GRANT ALL ON FUNCTION public.on_new_user_seed_patient_groups() TO service_role; - - --- --- Name: FUNCTION patients_validate_member_consistency(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.patients_validate_member_consistency() TO postgres; -GRANT ALL ON FUNCTION public.patients_validate_member_consistency() TO anon; -GRANT ALL ON FUNCTION public.patients_validate_member_consistency() TO authenticated; -GRANT ALL ON FUNCTION public.patients_validate_member_consistency() TO service_role; - - --- --- Name: FUNCTION patients_validate_responsible_member_tenant(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.patients_validate_responsible_member_tenant() TO postgres; -GRANT ALL ON FUNCTION public.patients_validate_responsible_member_tenant() TO anon; -GRANT ALL ON FUNCTION public.patients_validate_responsible_member_tenant() TO authenticated; -GRANT ALL ON FUNCTION public.patients_validate_responsible_member_tenant() TO service_role; - - --- --- Name: FUNCTION populate_notification_queue(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.populate_notification_queue() TO postgres; -GRANT ALL ON FUNCTION public.populate_notification_queue() TO anon; -GRANT ALL ON FUNCTION public.populate_notification_queue() TO authenticated; -GRANT ALL ON FUNCTION public.populate_notification_queue() TO service_role; - - --- --- Name: FUNCTION prevent_promoting_to_system(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.prevent_promoting_to_system() TO postgres; -GRANT ALL ON FUNCTION public.prevent_promoting_to_system() TO anon; -GRANT ALL ON FUNCTION public.prevent_promoting_to_system() TO authenticated; -GRANT ALL ON FUNCTION public.prevent_promoting_to_system() TO service_role; - - --- --- Name: FUNCTION prevent_saas_membership(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.prevent_saas_membership() TO postgres; -GRANT ALL ON FUNCTION public.prevent_saas_membership() TO anon; -GRANT ALL ON FUNCTION public.prevent_saas_membership() TO authenticated; -GRANT ALL ON FUNCTION public.prevent_saas_membership() TO service_role; - - --- --- Name: FUNCTION prevent_system_group_changes(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.prevent_system_group_changes() TO postgres; -GRANT ALL ON FUNCTION public.prevent_system_group_changes() TO anon; -GRANT ALL ON FUNCTION public.prevent_system_group_changes() TO authenticated; -GRANT ALL ON FUNCTION public.prevent_system_group_changes() TO service_role; - - --- --- Name: FUNCTION provision_account_tenant(p_user_id uuid, p_kind text, p_name text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text) TO postgres; -GRANT ALL ON FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text) TO anon; -GRANT ALL ON FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text) TO authenticated; -GRANT ALL ON FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text) TO service_role; - - --- --- Name: FUNCTION reactivate_subscription(p_subscription_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.reactivate_subscription(p_subscription_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.reactivate_subscription(p_subscription_id uuid) TO anon; -GRANT ALL ON FUNCTION public.reactivate_subscription(p_subscription_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.reactivate_subscription(p_subscription_id uuid) TO service_role; - - --- --- Name: FUNCTION rebuild_owner_entitlements(p_owner_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.rebuild_owner_entitlements(p_owner_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.rebuild_owner_entitlements(p_owner_id uuid) TO anon; -GRANT ALL ON FUNCTION public.rebuild_owner_entitlements(p_owner_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.rebuild_owner_entitlements(p_owner_id uuid) TO service_role; - - --- --- Name: FUNCTION regexp_match(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.regexp_match(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.regexp_match(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.regexp_match(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.regexp_match(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION regexp_match(public.citext, public.citext, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.regexp_match(public.citext, public.citext, text) TO postgres; -GRANT ALL ON FUNCTION public.regexp_match(public.citext, public.citext, text) TO anon; -GRANT ALL ON FUNCTION public.regexp_match(public.citext, public.citext, text) TO authenticated; -GRANT ALL ON FUNCTION public.regexp_match(public.citext, public.citext, text) TO service_role; - - --- --- Name: FUNCTION regexp_matches(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.regexp_matches(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.regexp_matches(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.regexp_matches(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.regexp_matches(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION regexp_matches(public.citext, public.citext, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.regexp_matches(public.citext, public.citext, text) TO postgres; -GRANT ALL ON FUNCTION public.regexp_matches(public.citext, public.citext, text) TO anon; -GRANT ALL ON FUNCTION public.regexp_matches(public.citext, public.citext, text) TO authenticated; -GRANT ALL ON FUNCTION public.regexp_matches(public.citext, public.citext, text) TO service_role; - - --- --- Name: FUNCTION regexp_replace(public.citext, public.citext, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.regexp_replace(public.citext, public.citext, text) TO postgres; -GRANT ALL ON FUNCTION public.regexp_replace(public.citext, public.citext, text) TO anon; -GRANT ALL ON FUNCTION public.regexp_replace(public.citext, public.citext, text) TO authenticated; -GRANT ALL ON FUNCTION public.regexp_replace(public.citext, public.citext, text) TO service_role; - - --- --- Name: FUNCTION regexp_replace(public.citext, public.citext, text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.regexp_replace(public.citext, public.citext, text, text) TO postgres; -GRANT ALL ON FUNCTION public.regexp_replace(public.citext, public.citext, text, text) TO anon; -GRANT ALL ON FUNCTION public.regexp_replace(public.citext, public.citext, text, text) TO authenticated; -GRANT ALL ON FUNCTION public.regexp_replace(public.citext, public.citext, text, text) TO service_role; - - --- --- Name: FUNCTION regexp_split_to_array(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.regexp_split_to_array(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.regexp_split_to_array(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.regexp_split_to_array(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.regexp_split_to_array(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION regexp_split_to_array(public.citext, public.citext, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.regexp_split_to_array(public.citext, public.citext, text) TO postgres; -GRANT ALL ON FUNCTION public.regexp_split_to_array(public.citext, public.citext, text) TO anon; -GRANT ALL ON FUNCTION public.regexp_split_to_array(public.citext, public.citext, text) TO authenticated; -GRANT ALL ON FUNCTION public.regexp_split_to_array(public.citext, public.citext, text) TO service_role; - - --- --- Name: FUNCTION regexp_split_to_table(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.regexp_split_to_table(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.regexp_split_to_table(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.regexp_split_to_table(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.regexp_split_to_table(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION regexp_split_to_table(public.citext, public.citext, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.regexp_split_to_table(public.citext, public.citext, text) TO postgres; -GRANT ALL ON FUNCTION public.regexp_split_to_table(public.citext, public.citext, text) TO anon; -GRANT ALL ON FUNCTION public.regexp_split_to_table(public.citext, public.citext, text) TO authenticated; -GRANT ALL ON FUNCTION public.regexp_split_to_table(public.citext, public.citext, text) TO service_role; - - --- --- Name: FUNCTION replace(public.citext, public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.replace(public.citext, public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.replace(public.citext, public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.replace(public.citext, public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.replace(public.citext, public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION revoke_support_session(p_token text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.revoke_support_session(p_token text) TO postgres; -GRANT ALL ON FUNCTION public.revoke_support_session(p_token text) TO anon; -GRANT ALL ON FUNCTION public.revoke_support_session(p_token text) TO authenticated; -GRANT ALL ON FUNCTION public.revoke_support_session(p_token text) TO service_role; - - --- --- Name: FUNCTION rotate_patient_invite_token(p_new_token text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.rotate_patient_invite_token(p_new_token text) TO postgres; -GRANT ALL ON FUNCTION public.rotate_patient_invite_token(p_new_token text) TO anon; -GRANT ALL ON FUNCTION public.rotate_patient_invite_token(p_new_token text) TO authenticated; -GRANT ALL ON FUNCTION public.rotate_patient_invite_token(p_new_token text) TO service_role; - - --- --- Name: FUNCTION saas_votar_doc(p_doc_id uuid, p_util boolean); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.saas_votar_doc(p_doc_id uuid, p_util boolean) TO postgres; -GRANT ALL ON FUNCTION public.saas_votar_doc(p_doc_id uuid, p_util boolean) TO anon; -GRANT ALL ON FUNCTION public.saas_votar_doc(p_doc_id uuid, p_util boolean) TO authenticated; -GRANT ALL ON FUNCTION public.saas_votar_doc(p_doc_id uuid, p_util boolean) TO service_role; - - --- --- Name: FUNCTION safe_delete_patient(p_patient_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.safe_delete_patient(p_patient_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.safe_delete_patient(p_patient_id uuid) TO anon; -GRANT ALL ON FUNCTION public.safe_delete_patient(p_patient_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.safe_delete_patient(p_patient_id uuid) TO service_role; - - --- --- Name: FUNCTION sanitize_phone_br(raw_phone text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.sanitize_phone_br(raw_phone text) TO postgres; -GRANT ALL ON FUNCTION public.sanitize_phone_br(raw_phone text) TO anon; -GRANT ALL ON FUNCTION public.sanitize_phone_br(raw_phone text) TO authenticated; -GRANT ALL ON FUNCTION public.sanitize_phone_br(raw_phone text) TO service_role; - - --- --- Name: FUNCTION seed_default_financial_categories(p_user_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.seed_default_financial_categories(p_user_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.seed_default_financial_categories(p_user_id uuid) TO anon; -GRANT ALL ON FUNCTION public.seed_default_financial_categories(p_user_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.seed_default_financial_categories(p_user_id uuid) TO service_role; - - --- --- Name: FUNCTION seed_determined_commitments(p_tenant_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.seed_determined_commitments(p_tenant_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.seed_determined_commitments(p_tenant_id uuid) TO anon; -GRANT ALL ON FUNCTION public.seed_determined_commitments(p_tenant_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.seed_determined_commitments(p_tenant_id uuid) TO service_role; - - --- --- Name: FUNCTION set_insurance_plans_updated_at(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.set_insurance_plans_updated_at() TO postgres; -GRANT ALL ON FUNCTION public.set_insurance_plans_updated_at() TO anon; -GRANT ALL ON FUNCTION public.set_insurance_plans_updated_at() TO authenticated; -GRANT ALL ON FUNCTION public.set_insurance_plans_updated_at() TO service_role; - - --- --- Name: FUNCTION set_limit(real); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.set_limit(real) TO postgres; -GRANT ALL ON FUNCTION public.set_limit(real) TO anon; -GRANT ALL ON FUNCTION public.set_limit(real) TO authenticated; -GRANT ALL ON FUNCTION public.set_limit(real) TO service_role; - - --- --- Name: FUNCTION set_owner_id(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.set_owner_id() TO postgres; -GRANT ALL ON FUNCTION public.set_owner_id() TO anon; -GRANT ALL ON FUNCTION public.set_owner_id() TO authenticated; -GRANT ALL ON FUNCTION public.set_owner_id() TO service_role; - - --- --- Name: FUNCTION set_services_updated_at(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.set_services_updated_at() TO postgres; -GRANT ALL ON FUNCTION public.set_services_updated_at() TO anon; -GRANT ALL ON FUNCTION public.set_services_updated_at() TO authenticated; -GRANT ALL ON FUNCTION public.set_services_updated_at() TO service_role; - - --- --- Name: FUNCTION set_tenant_feature_exception(p_tenant_id uuid, p_feature_key text, p_enabled boolean, p_reason text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.set_tenant_feature_exception(p_tenant_id uuid, p_feature_key text, p_enabled boolean, p_reason text) TO postgres; -GRANT ALL ON FUNCTION public.set_tenant_feature_exception(p_tenant_id uuid, p_feature_key text, p_enabled boolean, p_reason text) TO anon; -GRANT ALL ON FUNCTION public.set_tenant_feature_exception(p_tenant_id uuid, p_feature_key text, p_enabled boolean, p_reason text) TO authenticated; -GRANT ALL ON FUNCTION public.set_tenant_feature_exception(p_tenant_id uuid, p_feature_key text, p_enabled boolean, p_reason text) TO service_role; - - --- --- Name: FUNCTION set_updated_at(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.set_updated_at() TO postgres; -GRANT ALL ON FUNCTION public.set_updated_at() TO anon; -GRANT ALL ON FUNCTION public.set_updated_at() TO authenticated; -GRANT ALL ON FUNCTION public.set_updated_at() TO service_role; - - --- --- Name: FUNCTION set_updated_at_recurrence(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.set_updated_at_recurrence() TO postgres; -GRANT ALL ON FUNCTION public.set_updated_at_recurrence() TO anon; -GRANT ALL ON FUNCTION public.set_updated_at_recurrence() TO authenticated; -GRANT ALL ON FUNCTION public.set_updated_at_recurrence() TO service_role; - - --- --- Name: FUNCTION show_limit(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.show_limit() TO postgres; -GRANT ALL ON FUNCTION public.show_limit() TO anon; -GRANT ALL ON FUNCTION public.show_limit() TO authenticated; -GRANT ALL ON FUNCTION public.show_limit() TO service_role; - - --- --- Name: FUNCTION show_trgm(text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.show_trgm(text) TO postgres; -GRANT ALL ON FUNCTION public.show_trgm(text) TO anon; -GRANT ALL ON FUNCTION public.show_trgm(text) TO authenticated; -GRANT ALL ON FUNCTION public.show_trgm(text) TO service_role; - - --- --- Name: FUNCTION similarity(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.similarity(text, text) TO postgres; -GRANT ALL ON FUNCTION public.similarity(text, text) TO anon; -GRANT ALL ON FUNCTION public.similarity(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.similarity(text, text) TO service_role; - - --- --- Name: FUNCTION similarity_dist(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.similarity_dist(text, text) TO postgres; -GRANT ALL ON FUNCTION public.similarity_dist(text, text) TO anon; -GRANT ALL ON FUNCTION public.similarity_dist(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.similarity_dist(text, text) TO service_role; - - --- --- Name: FUNCTION similarity_op(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.similarity_op(text, text) TO postgres; -GRANT ALL ON FUNCTION public.similarity_op(text, text) TO anon; -GRANT ALL ON FUNCTION public.similarity_op(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.similarity_op(text, text) TO service_role; - - --- --- Name: FUNCTION split_part(public.citext, public.citext, integer); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.split_part(public.citext, public.citext, integer) TO postgres; -GRANT ALL ON FUNCTION public.split_part(public.citext, public.citext, integer) TO anon; -GRANT ALL ON FUNCTION public.split_part(public.citext, public.citext, integer) TO authenticated; -GRANT ALL ON FUNCTION public.split_part(public.citext, public.citext, integer) TO service_role; - - --- --- Name: FUNCTION split_recurrence_at(p_recurrence_id uuid, p_from_date date); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.split_recurrence_at(p_recurrence_id uuid, p_from_date date) TO postgres; -GRANT ALL ON FUNCTION public.split_recurrence_at(p_recurrence_id uuid, p_from_date date) TO anon; -GRANT ALL ON FUNCTION public.split_recurrence_at(p_recurrence_id uuid, p_from_date date) TO authenticated; -GRANT ALL ON FUNCTION public.split_recurrence_at(p_recurrence_id uuid, p_from_date date) TO service_role; - - --- --- Name: FUNCTION strict_word_similarity(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.strict_word_similarity(text, text) TO postgres; -GRANT ALL ON FUNCTION public.strict_word_similarity(text, text) TO anon; -GRANT ALL ON FUNCTION public.strict_word_similarity(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.strict_word_similarity(text, text) TO service_role; - - --- --- Name: FUNCTION strict_word_similarity_commutator_op(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.strict_word_similarity_commutator_op(text, text) TO postgres; -GRANT ALL ON FUNCTION public.strict_word_similarity_commutator_op(text, text) TO anon; -GRANT ALL ON FUNCTION public.strict_word_similarity_commutator_op(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.strict_word_similarity_commutator_op(text, text) TO service_role; - - --- --- Name: FUNCTION strict_word_similarity_dist_commutator_op(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.strict_word_similarity_dist_commutator_op(text, text) TO postgres; -GRANT ALL ON FUNCTION public.strict_word_similarity_dist_commutator_op(text, text) TO anon; -GRANT ALL ON FUNCTION public.strict_word_similarity_dist_commutator_op(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.strict_word_similarity_dist_commutator_op(text, text) TO service_role; - - --- --- Name: FUNCTION strict_word_similarity_dist_op(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.strict_word_similarity_dist_op(text, text) TO postgres; -GRANT ALL ON FUNCTION public.strict_word_similarity_dist_op(text, text) TO anon; -GRANT ALL ON FUNCTION public.strict_word_similarity_dist_op(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.strict_word_similarity_dist_op(text, text) TO service_role; - - --- --- Name: FUNCTION strict_word_similarity_op(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.strict_word_similarity_op(text, text) TO postgres; -GRANT ALL ON FUNCTION public.strict_word_similarity_op(text, text) TO anon; -GRANT ALL ON FUNCTION public.strict_word_similarity_op(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.strict_word_similarity_op(text, text) TO service_role; - - --- --- Name: FUNCTION strpos(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.strpos(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.strpos(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.strpos(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.strpos(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION subscription_intents_view_insert(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.subscription_intents_view_insert() TO postgres; -GRANT ALL ON FUNCTION public.subscription_intents_view_insert() TO anon; -GRANT ALL ON FUNCTION public.subscription_intents_view_insert() TO authenticated; -GRANT ALL ON FUNCTION public.subscription_intents_view_insert() TO service_role; - - --- --- Name: FUNCTION subscriptions_validate_scope(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.subscriptions_validate_scope() TO postgres; -GRANT ALL ON FUNCTION public.subscriptions_validate_scope() TO anon; -GRANT ALL ON FUNCTION public.subscriptions_validate_scope() TO authenticated; -GRANT ALL ON FUNCTION public.subscriptions_validate_scope() TO service_role; - - --- --- Name: FUNCTION sync_busy_mirror_agenda_eventos(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.sync_busy_mirror_agenda_eventos() TO postgres; -GRANT ALL ON FUNCTION public.sync_busy_mirror_agenda_eventos() TO anon; -GRANT ALL ON FUNCTION public.sync_busy_mirror_agenda_eventos() TO authenticated; -GRANT ALL ON FUNCTION public.sync_busy_mirror_agenda_eventos() TO service_role; - - --- --- Name: FUNCTION sync_overdue_financial_records(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.sync_overdue_financial_records() TO postgres; -GRANT ALL ON FUNCTION public.sync_overdue_financial_records() TO anon; -GRANT ALL ON FUNCTION public.sync_overdue_financial_records() TO authenticated; -GRANT ALL ON FUNCTION public.sync_overdue_financial_records() TO service_role; - - --- --- Name: FUNCTION tenant_accept_invite(p_token uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_accept_invite(p_token uuid) TO postgres; -GRANT ALL ON FUNCTION public.tenant_accept_invite(p_token uuid) TO anon; -GRANT ALL ON FUNCTION public.tenant_accept_invite(p_token uuid) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_accept_invite(p_token uuid) TO service_role; - - --- --- Name: TABLE tenant_members; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.tenant_members TO postgres; -GRANT SELECT,REFERENCES,TRIGGER,TRUNCATE,MAINTAIN ON TABLE public.tenant_members TO authenticated; -GRANT ALL ON TABLE public.tenant_members TO service_role; - - --- --- Name: FUNCTION tenant_add_member_by_email(p_tenant_id uuid, p_email text, p_role text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_add_member_by_email(p_tenant_id uuid, p_email text, p_role text) TO postgres; -GRANT ALL ON FUNCTION public.tenant_add_member_by_email(p_tenant_id uuid, p_email text, p_role text) TO anon; -GRANT ALL ON FUNCTION public.tenant_add_member_by_email(p_tenant_id uuid, p_email text, p_role text) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_add_member_by_email(p_tenant_id uuid, p_email text, p_role text) TO service_role; - - --- --- Name: FUNCTION tenant_feature_allowed(p_tenant_id uuid, p_feature_key text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_feature_allowed(p_tenant_id uuid, p_feature_key text) TO postgres; -GRANT ALL ON FUNCTION public.tenant_feature_allowed(p_tenant_id uuid, p_feature_key text) TO anon; -GRANT ALL ON FUNCTION public.tenant_feature_allowed(p_tenant_id uuid, p_feature_key text) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_feature_allowed(p_tenant_id uuid, p_feature_key text) TO service_role; - - --- --- Name: FUNCTION tenant_feature_enabled(p_tenant_id uuid, p_feature_key text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_feature_enabled(p_tenant_id uuid, p_feature_key text) TO postgres; -GRANT ALL ON FUNCTION public.tenant_feature_enabled(p_tenant_id uuid, p_feature_key text) TO anon; -GRANT ALL ON FUNCTION public.tenant_feature_enabled(p_tenant_id uuid, p_feature_key text) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_feature_enabled(p_tenant_id uuid, p_feature_key text) TO service_role; - - --- --- Name: FUNCTION tenant_features_guard_with_plan(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_features_guard_with_plan() TO postgres; -GRANT ALL ON FUNCTION public.tenant_features_guard_with_plan() TO anon; -GRANT ALL ON FUNCTION public.tenant_features_guard_with_plan() TO authenticated; -GRANT ALL ON FUNCTION public.tenant_features_guard_with_plan() TO service_role; - - --- --- Name: FUNCTION tenant_has_feature(_tenant_id uuid, _feature text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) TO postgres; -GRANT ALL ON FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) TO anon; -GRANT ALL ON FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) TO service_role; - - --- --- Name: FUNCTION tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text); Type: ACL; Schema: public; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) FROM PUBLIC; -GRANT ALL ON FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) TO postgres; -GRANT ALL ON FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) TO anon; -GRANT ALL ON FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) TO service_role; - - --- --- Name: FUNCTION tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid) TO anon; -GRANT ALL ON FUNCTION public.tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid) TO service_role; - - --- --- Name: FUNCTION tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid) TO anon; -GRANT ALL ON FUNCTION public.tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid) TO service_role; - - --- --- Name: FUNCTION tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid) TO postgres; -GRANT ALL ON FUNCTION public.tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid) TO anon; -GRANT ALL ON FUNCTION public.tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid) TO service_role; - - --- --- Name: FUNCTION tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text) TO postgres; -GRANT ALL ON FUNCTION public.tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text) TO anon; -GRANT ALL ON FUNCTION public.tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text) TO service_role; - - --- --- Name: FUNCTION tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text) TO postgres; -GRANT ALL ON FUNCTION public.tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text) TO anon; -GRANT ALL ON FUNCTION public.tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text) TO service_role; - - --- --- Name: FUNCTION tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text) TO postgres; -GRANT ALL ON FUNCTION public.tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text) TO anon; -GRANT ALL ON FUNCTION public.tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text) TO authenticated; -GRANT ALL ON FUNCTION public.tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text) TO service_role; - - --- --- Name: FUNCTION texticlike(public.citext, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.texticlike(public.citext, text) TO postgres; -GRANT ALL ON FUNCTION public.texticlike(public.citext, text) TO anon; -GRANT ALL ON FUNCTION public.texticlike(public.citext, text) TO authenticated; -GRANT ALL ON FUNCTION public.texticlike(public.citext, text) TO service_role; - - --- --- Name: FUNCTION texticlike(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.texticlike(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.texticlike(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.texticlike(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.texticlike(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION texticnlike(public.citext, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.texticnlike(public.citext, text) TO postgres; -GRANT ALL ON FUNCTION public.texticnlike(public.citext, text) TO anon; -GRANT ALL ON FUNCTION public.texticnlike(public.citext, text) TO authenticated; -GRANT ALL ON FUNCTION public.texticnlike(public.citext, text) TO service_role; - - --- --- Name: FUNCTION texticnlike(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.texticnlike(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.texticnlike(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.texticnlike(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.texticnlike(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION texticregexeq(public.citext, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.texticregexeq(public.citext, text) TO postgres; -GRANT ALL ON FUNCTION public.texticregexeq(public.citext, text) TO anon; -GRANT ALL ON FUNCTION public.texticregexeq(public.citext, text) TO authenticated; -GRANT ALL ON FUNCTION public.texticregexeq(public.citext, text) TO service_role; - - --- --- Name: FUNCTION texticregexeq(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.texticregexeq(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.texticregexeq(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.texticregexeq(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.texticregexeq(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION texticregexne(public.citext, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.texticregexne(public.citext, text) TO postgres; -GRANT ALL ON FUNCTION public.texticregexne(public.citext, text) TO anon; -GRANT ALL ON FUNCTION public.texticregexne(public.citext, text) TO authenticated; -GRANT ALL ON FUNCTION public.texticregexne(public.citext, text) TO service_role; - - --- --- Name: FUNCTION texticregexne(public.citext, public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.texticregexne(public.citext, public.citext) TO postgres; -GRANT ALL ON FUNCTION public.texticregexne(public.citext, public.citext) TO anon; -GRANT ALL ON FUNCTION public.texticregexne(public.citext, public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.texticregexne(public.citext, public.citext) TO service_role; - - --- --- Name: FUNCTION time_dist(time without time zone, time without time zone); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.time_dist(time without time zone, time without time zone) TO postgres; -GRANT ALL ON FUNCTION public.time_dist(time without time zone, time without time zone) TO anon; -GRANT ALL ON FUNCTION public.time_dist(time without time zone, time without time zone) TO authenticated; -GRANT ALL ON FUNCTION public.time_dist(time without time zone, time without time zone) TO service_role; - - --- --- Name: FUNCTION toggle_plan(owner uuid); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.toggle_plan(owner uuid) TO postgres; -GRANT ALL ON FUNCTION public.toggle_plan(owner uuid) TO anon; -GRANT ALL ON FUNCTION public.toggle_plan(owner uuid) TO authenticated; -GRANT ALL ON FUNCTION public.toggle_plan(owner uuid) TO service_role; - - --- --- Name: FUNCTION transition_subscription(p_subscription_id uuid, p_to_status text, p_reason text, p_metadata jsonb); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.transition_subscription(p_subscription_id uuid, p_to_status text, p_reason text, p_metadata jsonb) TO postgres; -GRANT ALL ON FUNCTION public.transition_subscription(p_subscription_id uuid, p_to_status text, p_reason text, p_metadata jsonb) TO anon; -GRANT ALL ON FUNCTION public.transition_subscription(p_subscription_id uuid, p_to_status text, p_reason text, p_metadata jsonb) TO authenticated; -GRANT ALL ON FUNCTION public.transition_subscription(p_subscription_id uuid, p_to_status text, p_reason text, p_metadata jsonb) TO service_role; - - --- --- Name: FUNCTION translate(public.citext, public.citext, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.translate(public.citext, public.citext, text) TO postgres; -GRANT ALL ON FUNCTION public.translate(public.citext, public.citext, text) TO anon; -GRANT ALL ON FUNCTION public.translate(public.citext, public.citext, text) TO authenticated; -GRANT ALL ON FUNCTION public.translate(public.citext, public.citext, text) TO service_role; - - --- --- Name: FUNCTION trg_fn_financial_records_auto_overdue(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.trg_fn_financial_records_auto_overdue() TO postgres; -GRANT ALL ON FUNCTION public.trg_fn_financial_records_auto_overdue() TO anon; -GRANT ALL ON FUNCTION public.trg_fn_financial_records_auto_overdue() TO authenticated; -GRANT ALL ON FUNCTION public.trg_fn_financial_records_auto_overdue() TO service_role; - - --- --- Name: FUNCTION ts_dist(timestamp without time zone, timestamp without time zone); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.ts_dist(timestamp without time zone, timestamp without time zone) TO postgres; -GRANT ALL ON FUNCTION public.ts_dist(timestamp without time zone, timestamp without time zone) TO anon; -GRANT ALL ON FUNCTION public.ts_dist(timestamp without time zone, timestamp without time zone) TO authenticated; -GRANT ALL ON FUNCTION public.ts_dist(timestamp without time zone, timestamp without time zone) TO service_role; - - --- --- Name: FUNCTION tstz_dist(timestamp with time zone, timestamp with time zone); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.tstz_dist(timestamp with time zone, timestamp with time zone) TO postgres; -GRANT ALL ON FUNCTION public.tstz_dist(timestamp with time zone, timestamp with time zone) TO anon; -GRANT ALL ON FUNCTION public.tstz_dist(timestamp with time zone, timestamp with time zone) TO authenticated; -GRANT ALL ON FUNCTION public.tstz_dist(timestamp with time zone, timestamp with time zone) TO service_role; - - --- --- Name: FUNCTION unstick_notification_queue(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.unstick_notification_queue() TO postgres; -GRANT ALL ON FUNCTION public.unstick_notification_queue() TO anon; -GRANT ALL ON FUNCTION public.unstick_notification_queue() TO authenticated; -GRANT ALL ON FUNCTION public.unstick_notification_queue() TO service_role; - - --- --- Name: FUNCTION update_payment_settings_updated_at(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.update_payment_settings_updated_at() TO postgres; -GRANT ALL ON FUNCTION public.update_payment_settings_updated_at() TO anon; -GRANT ALL ON FUNCTION public.update_payment_settings_updated_at() TO authenticated; -GRANT ALL ON FUNCTION public.update_payment_settings_updated_at() TO service_role; - - --- --- Name: FUNCTION update_professional_pricing_updated_at(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.update_professional_pricing_updated_at() TO postgres; -GRANT ALL ON FUNCTION public.update_professional_pricing_updated_at() TO anon; -GRANT ALL ON FUNCTION public.update_professional_pricing_updated_at() TO authenticated; -GRANT ALL ON FUNCTION public.update_professional_pricing_updated_at() TO service_role; - - --- --- Name: FUNCTION user_has_feature(_user_id uuid, _feature text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.user_has_feature(_user_id uuid, _feature text) TO postgres; -GRANT ALL ON FUNCTION public.user_has_feature(_user_id uuid, _feature text) TO anon; -GRANT ALL ON FUNCTION public.user_has_feature(_user_id uuid, _feature text) TO authenticated; -GRANT ALL ON FUNCTION public.user_has_feature(_user_id uuid, _feature text) TO service_role; - - --- --- Name: FUNCTION validate_support_session(p_token text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.validate_support_session(p_token text) TO postgres; -GRANT ALL ON FUNCTION public.validate_support_session(p_token text) TO anon; -GRANT ALL ON FUNCTION public.validate_support_session(p_token text) TO authenticated; -GRANT ALL ON FUNCTION public.validate_support_session(p_token text) TO service_role; - - --- --- Name: FUNCTION whoami(); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.whoami() TO postgres; -GRANT ALL ON FUNCTION public.whoami() TO anon; -GRANT ALL ON FUNCTION public.whoami() TO authenticated; -GRANT ALL ON FUNCTION public.whoami() TO service_role; - - --- --- Name: FUNCTION word_similarity(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.word_similarity(text, text) TO postgres; -GRANT ALL ON FUNCTION public.word_similarity(text, text) TO anon; -GRANT ALL ON FUNCTION public.word_similarity(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.word_similarity(text, text) TO service_role; - - --- --- Name: FUNCTION word_similarity_commutator_op(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.word_similarity_commutator_op(text, text) TO postgres; -GRANT ALL ON FUNCTION public.word_similarity_commutator_op(text, text) TO anon; -GRANT ALL ON FUNCTION public.word_similarity_commutator_op(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.word_similarity_commutator_op(text, text) TO service_role; - - --- --- Name: FUNCTION word_similarity_dist_commutator_op(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.word_similarity_dist_commutator_op(text, text) TO postgres; -GRANT ALL ON FUNCTION public.word_similarity_dist_commutator_op(text, text) TO anon; -GRANT ALL ON FUNCTION public.word_similarity_dist_commutator_op(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.word_similarity_dist_commutator_op(text, text) TO service_role; - - --- --- Name: FUNCTION word_similarity_dist_op(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.word_similarity_dist_op(text, text) TO postgres; -GRANT ALL ON FUNCTION public.word_similarity_dist_op(text, text) TO anon; -GRANT ALL ON FUNCTION public.word_similarity_dist_op(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.word_similarity_dist_op(text, text) TO service_role; - - --- --- Name: FUNCTION word_similarity_op(text, text); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.word_similarity_op(text, text) TO postgres; -GRANT ALL ON FUNCTION public.word_similarity_op(text, text) TO anon; -GRANT ALL ON FUNCTION public.word_similarity_op(text, text) TO authenticated; -GRANT ALL ON FUNCTION public.word_similarity_op(text, text) TO service_role; - - --- --- Name: FUNCTION apply_rls(wal jsonb, max_record_bytes integer); Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION realtime.apply_rls(wal jsonb, max_record_bytes integer) TO postgres; -GRANT ALL ON FUNCTION realtime.apply_rls(wal jsonb, max_record_bytes integer) TO dashboard_user; -GRANT ALL ON FUNCTION realtime.apply_rls(wal jsonb, max_record_bytes integer) TO anon; -GRANT ALL ON FUNCTION realtime.apply_rls(wal jsonb, max_record_bytes integer) TO authenticated; -GRANT ALL ON FUNCTION realtime.apply_rls(wal jsonb, max_record_bytes integer) TO service_role; -GRANT ALL ON FUNCTION realtime.apply_rls(wal jsonb, max_record_bytes integer) TO supabase_realtime_admin; - - --- --- Name: FUNCTION broadcast_changes(topic_name text, event_name text, operation text, table_name text, table_schema text, new record, old record, level text); Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION realtime.broadcast_changes(topic_name text, event_name text, operation text, table_name text, table_schema text, new record, old record, level text) TO postgres; -GRANT ALL ON FUNCTION realtime.broadcast_changes(topic_name text, event_name text, operation text, table_name text, table_schema text, new record, old record, level text) TO dashboard_user; - - --- --- Name: FUNCTION build_prepared_statement_sql(prepared_statement_name text, entity regclass, columns realtime.wal_column[]); Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION realtime.build_prepared_statement_sql(prepared_statement_name text, entity regclass, columns realtime.wal_column[]) TO postgres; -GRANT ALL ON FUNCTION realtime.build_prepared_statement_sql(prepared_statement_name text, entity regclass, columns realtime.wal_column[]) TO dashboard_user; -GRANT ALL ON FUNCTION realtime.build_prepared_statement_sql(prepared_statement_name text, entity regclass, columns realtime.wal_column[]) TO anon; -GRANT ALL ON FUNCTION realtime.build_prepared_statement_sql(prepared_statement_name text, entity regclass, columns realtime.wal_column[]) TO authenticated; -GRANT ALL ON FUNCTION realtime.build_prepared_statement_sql(prepared_statement_name text, entity regclass, columns realtime.wal_column[]) TO service_role; -GRANT ALL ON FUNCTION realtime.build_prepared_statement_sql(prepared_statement_name text, entity regclass, columns realtime.wal_column[]) TO supabase_realtime_admin; - - --- --- Name: FUNCTION "cast"(val text, type_ regtype); Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION realtime."cast"(val text, type_ regtype) TO postgres; -GRANT ALL ON FUNCTION realtime."cast"(val text, type_ regtype) TO dashboard_user; -GRANT ALL ON FUNCTION realtime."cast"(val text, type_ regtype) TO anon; -GRANT ALL ON FUNCTION realtime."cast"(val text, type_ regtype) TO authenticated; -GRANT ALL ON FUNCTION realtime."cast"(val text, type_ regtype) TO service_role; -GRANT ALL ON FUNCTION realtime."cast"(val text, type_ regtype) TO supabase_realtime_admin; - - --- --- Name: FUNCTION check_equality_op(op realtime.equality_op, type_ regtype, val_1 text, val_2 text); Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION realtime.check_equality_op(op realtime.equality_op, type_ regtype, val_1 text, val_2 text) TO postgres; -GRANT ALL ON FUNCTION realtime.check_equality_op(op realtime.equality_op, type_ regtype, val_1 text, val_2 text) TO dashboard_user; -GRANT ALL ON FUNCTION realtime.check_equality_op(op realtime.equality_op, type_ regtype, val_1 text, val_2 text) TO anon; -GRANT ALL ON FUNCTION realtime.check_equality_op(op realtime.equality_op, type_ regtype, val_1 text, val_2 text) TO authenticated; -GRANT ALL ON FUNCTION realtime.check_equality_op(op realtime.equality_op, type_ regtype, val_1 text, val_2 text) TO service_role; -GRANT ALL ON FUNCTION realtime.check_equality_op(op realtime.equality_op, type_ regtype, val_1 text, val_2 text) TO supabase_realtime_admin; - - --- --- Name: FUNCTION is_visible_through_filters(columns realtime.wal_column[], filters realtime.user_defined_filter[]); Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION realtime.is_visible_through_filters(columns realtime.wal_column[], filters realtime.user_defined_filter[]) TO postgres; -GRANT ALL ON FUNCTION realtime.is_visible_through_filters(columns realtime.wal_column[], filters realtime.user_defined_filter[]) TO dashboard_user; -GRANT ALL ON FUNCTION realtime.is_visible_through_filters(columns realtime.wal_column[], filters realtime.user_defined_filter[]) TO anon; -GRANT ALL ON FUNCTION realtime.is_visible_through_filters(columns realtime.wal_column[], filters realtime.user_defined_filter[]) TO authenticated; -GRANT ALL ON FUNCTION realtime.is_visible_through_filters(columns realtime.wal_column[], filters realtime.user_defined_filter[]) TO service_role; -GRANT ALL ON FUNCTION realtime.is_visible_through_filters(columns realtime.wal_column[], filters realtime.user_defined_filter[]) TO supabase_realtime_admin; - - --- --- Name: FUNCTION list_changes(publication name, slot_name name, max_changes integer, max_record_bytes integer); Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION realtime.list_changes(publication name, slot_name name, max_changes integer, max_record_bytes integer) TO postgres; -GRANT ALL ON FUNCTION realtime.list_changes(publication name, slot_name name, max_changes integer, max_record_bytes integer) TO dashboard_user; -GRANT ALL ON FUNCTION realtime.list_changes(publication name, slot_name name, max_changes integer, max_record_bytes integer) TO anon; -GRANT ALL ON FUNCTION realtime.list_changes(publication name, slot_name name, max_changes integer, max_record_bytes integer) TO authenticated; -GRANT ALL ON FUNCTION realtime.list_changes(publication name, slot_name name, max_changes integer, max_record_bytes integer) TO service_role; -GRANT ALL ON FUNCTION realtime.list_changes(publication name, slot_name name, max_changes integer, max_record_bytes integer) TO supabase_realtime_admin; - - --- --- Name: FUNCTION quote_wal2json(entity regclass); Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION realtime.quote_wal2json(entity regclass) TO postgres; -GRANT ALL ON FUNCTION realtime.quote_wal2json(entity regclass) TO dashboard_user; -GRANT ALL ON FUNCTION realtime.quote_wal2json(entity regclass) TO anon; -GRANT ALL ON FUNCTION realtime.quote_wal2json(entity regclass) TO authenticated; -GRANT ALL ON FUNCTION realtime.quote_wal2json(entity regclass) TO service_role; -GRANT ALL ON FUNCTION realtime.quote_wal2json(entity regclass) TO supabase_realtime_admin; - - --- --- Name: FUNCTION send(payload jsonb, event text, topic text, private boolean); Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION realtime.send(payload jsonb, event text, topic text, private boolean) TO postgres; -GRANT ALL ON FUNCTION realtime.send(payload jsonb, event text, topic text, private boolean) TO dashboard_user; - - --- --- Name: FUNCTION subscription_check_filters(); Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION realtime.subscription_check_filters() TO postgres; -GRANT ALL ON FUNCTION realtime.subscription_check_filters() TO dashboard_user; -GRANT ALL ON FUNCTION realtime.subscription_check_filters() TO anon; -GRANT ALL ON FUNCTION realtime.subscription_check_filters() TO authenticated; -GRANT ALL ON FUNCTION realtime.subscription_check_filters() TO service_role; -GRANT ALL ON FUNCTION realtime.subscription_check_filters() TO supabase_realtime_admin; - - --- --- Name: FUNCTION to_regrole(role_name text); Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION realtime.to_regrole(role_name text) TO postgres; -GRANT ALL ON FUNCTION realtime.to_regrole(role_name text) TO dashboard_user; -GRANT ALL ON FUNCTION realtime.to_regrole(role_name text) TO anon; -GRANT ALL ON FUNCTION realtime.to_regrole(role_name text) TO authenticated; -GRANT ALL ON FUNCTION realtime.to_regrole(role_name text) TO service_role; -GRANT ALL ON FUNCTION realtime.to_regrole(role_name text) TO supabase_realtime_admin; - - --- --- Name: FUNCTION topic(); Type: ACL; Schema: realtime; Owner: supabase_realtime_admin --- - -GRANT ALL ON FUNCTION realtime.topic() TO postgres; -GRANT ALL ON FUNCTION realtime.topic() TO dashboard_user; - - --- --- Name: FUNCTION http_request(); Type: ACL; Schema: supabase_functions; Owner: supabase_functions_admin --- - -REVOKE ALL ON FUNCTION supabase_functions.http_request() FROM PUBLIC; -GRANT ALL ON FUNCTION supabase_functions.http_request() TO postgres; -GRANT ALL ON FUNCTION supabase_functions.http_request() TO anon; -GRANT ALL ON FUNCTION supabase_functions.http_request() TO authenticated; -GRANT ALL ON FUNCTION supabase_functions.http_request() TO service_role; - - --- --- Name: FUNCTION _crypto_aead_det_decrypt(message bytea, additional bytea, key_id bigint, context bytea, nonce bytea); Type: ACL; Schema: vault; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION vault._crypto_aead_det_decrypt(message bytea, additional bytea, key_id bigint, context bytea, nonce bytea) TO postgres WITH GRANT OPTION; -GRANT ALL ON FUNCTION vault._crypto_aead_det_decrypt(message bytea, additional bytea, key_id bigint, context bytea, nonce bytea) TO service_role; - - --- --- Name: FUNCTION create_secret(new_secret text, new_name text, new_description text, new_key_id uuid); Type: ACL; Schema: vault; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION vault.create_secret(new_secret text, new_name text, new_description text, new_key_id uuid) TO postgres WITH GRANT OPTION; -GRANT ALL ON FUNCTION vault.create_secret(new_secret text, new_name text, new_description text, new_key_id uuid) TO service_role; - - --- --- Name: FUNCTION update_secret(secret_id uuid, new_secret text, new_name text, new_description text, new_key_id uuid); Type: ACL; Schema: vault; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION vault.update_secret(secret_id uuid, new_secret text, new_name text, new_description text, new_key_id uuid) TO postgres WITH GRANT OPTION; -GRANT ALL ON FUNCTION vault.update_secret(secret_id uuid, new_secret text, new_name text, new_description text, new_key_id uuid) TO service_role; - - --- --- Name: FUNCTION max(public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.max(public.citext) TO postgres; -GRANT ALL ON FUNCTION public.max(public.citext) TO anon; -GRANT ALL ON FUNCTION public.max(public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.max(public.citext) TO service_role; - - --- --- Name: FUNCTION min(public.citext); Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION public.min(public.citext) TO postgres; -GRANT ALL ON FUNCTION public.min(public.citext) TO anon; -GRANT ALL ON FUNCTION public.min(public.citext) TO authenticated; -GRANT ALL ON FUNCTION public.min(public.citext) TO service_role; - - --- --- Name: TABLE audit_log_entries; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.audit_log_entries TO dashboard_user; -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.audit_log_entries TO postgres; -GRANT SELECT ON TABLE auth.audit_log_entries TO postgres WITH GRANT OPTION; - - --- --- Name: TABLE flow_state; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.flow_state TO postgres; -GRANT SELECT ON TABLE auth.flow_state TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE auth.flow_state TO dashboard_user; - - --- --- Name: TABLE identities; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.identities TO postgres; -GRANT SELECT ON TABLE auth.identities TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE auth.identities TO dashboard_user; - - --- --- Name: TABLE instances; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.instances TO dashboard_user; -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.instances TO postgres; -GRANT SELECT ON TABLE auth.instances TO postgres WITH GRANT OPTION; - - --- --- Name: TABLE mfa_amr_claims; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.mfa_amr_claims TO postgres; -GRANT SELECT ON TABLE auth.mfa_amr_claims TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE auth.mfa_amr_claims TO dashboard_user; - - --- --- Name: TABLE mfa_challenges; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.mfa_challenges TO postgres; -GRANT SELECT ON TABLE auth.mfa_challenges TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE auth.mfa_challenges TO dashboard_user; - - --- --- Name: TABLE mfa_factors; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.mfa_factors TO postgres; -GRANT SELECT ON TABLE auth.mfa_factors TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE auth.mfa_factors TO dashboard_user; - - --- --- Name: TABLE oauth_authorizations; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.oauth_authorizations TO postgres; -GRANT ALL ON TABLE auth.oauth_authorizations TO dashboard_user; - - --- --- Name: TABLE oauth_client_states; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.oauth_client_states TO postgres; -GRANT ALL ON TABLE auth.oauth_client_states TO dashboard_user; - - --- --- Name: TABLE oauth_clients; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.oauth_clients TO postgres; -GRANT ALL ON TABLE auth.oauth_clients TO dashboard_user; - - --- --- Name: TABLE oauth_consents; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.oauth_consents TO postgres; -GRANT ALL ON TABLE auth.oauth_consents TO dashboard_user; - - --- --- Name: TABLE one_time_tokens; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.one_time_tokens TO postgres; -GRANT SELECT ON TABLE auth.one_time_tokens TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE auth.one_time_tokens TO dashboard_user; - - --- --- Name: TABLE refresh_tokens; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.refresh_tokens TO dashboard_user; -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.refresh_tokens TO postgres; -GRANT SELECT ON TABLE auth.refresh_tokens TO postgres WITH GRANT OPTION; - - --- --- Name: SEQUENCE refresh_tokens_id_seq; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON SEQUENCE auth.refresh_tokens_id_seq TO dashboard_user; -GRANT ALL ON SEQUENCE auth.refresh_tokens_id_seq TO postgres; - - --- --- Name: TABLE saml_providers; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.saml_providers TO postgres; -GRANT SELECT ON TABLE auth.saml_providers TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE auth.saml_providers TO dashboard_user; - - --- --- Name: TABLE saml_relay_states; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.saml_relay_states TO postgres; -GRANT SELECT ON TABLE auth.saml_relay_states TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE auth.saml_relay_states TO dashboard_user; - - --- --- Name: TABLE schema_migrations; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT SELECT ON TABLE auth.schema_migrations TO postgres WITH GRANT OPTION; - - --- --- Name: TABLE sessions; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.sessions TO postgres; -GRANT SELECT ON TABLE auth.sessions TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE auth.sessions TO dashboard_user; - - --- --- Name: TABLE sso_domains; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.sso_domains TO postgres; -GRANT SELECT ON TABLE auth.sso_domains TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE auth.sso_domains TO dashboard_user; - - --- --- Name: TABLE sso_providers; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.sso_providers TO postgres; -GRANT SELECT ON TABLE auth.sso_providers TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE auth.sso_providers TO dashboard_user; - - --- --- Name: TABLE users; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.users TO dashboard_user; -GRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLE auth.users TO postgres; -GRANT SELECT ON TABLE auth.users TO postgres WITH GRANT OPTION; - - --- --- Name: TABLE job; Type: ACL; Schema: cron; Owner: supabase_admin --- - -GRANT SELECT ON TABLE cron.job TO postgres WITH GRANT OPTION; - - --- --- Name: TABLE job_run_details; Type: ACL; Schema: cron; Owner: supabase_admin --- - -GRANT ALL ON TABLE cron.job_run_details TO postgres WITH GRANT OPTION; - - --- --- Name: TABLE pg_stat_statements; Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON TABLE extensions.pg_stat_statements TO postgres WITH GRANT OPTION; - - --- --- Name: TABLE pg_stat_statements_info; Type: ACL; Schema: extensions; Owner: supabase_admin --- - -GRANT ALL ON TABLE extensions.pg_stat_statements_info TO postgres WITH GRANT OPTION; - - --- --- Name: TABLE addon_credits; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.addon_credits TO postgres; -GRANT ALL ON TABLE public.addon_credits TO anon; -GRANT ALL ON TABLE public.addon_credits TO authenticated; -GRANT ALL ON TABLE public.addon_credits TO service_role; - - --- --- Name: TABLE addon_products; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.addon_products TO postgres; -GRANT ALL ON TABLE public.addon_products TO anon; -GRANT ALL ON TABLE public.addon_products TO authenticated; -GRANT ALL ON TABLE public.addon_products TO service_role; - - --- --- Name: TABLE addon_transactions; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.addon_transactions TO postgres; -GRANT ALL ON TABLE public.addon_transactions TO anon; -GRANT ALL ON TABLE public.addon_transactions TO authenticated; -GRANT ALL ON TABLE public.addon_transactions TO service_role; - - --- --- Name: TABLE agenda_bloqueios; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.agenda_bloqueios TO postgres; -GRANT ALL ON TABLE public.agenda_bloqueios TO anon; -GRANT ALL ON TABLE public.agenda_bloqueios TO authenticated; -GRANT ALL ON TABLE public.agenda_bloqueios TO service_role; - - --- --- Name: TABLE agenda_configuracoes; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.agenda_configuracoes TO postgres; -GRANT ALL ON TABLE public.agenda_configuracoes TO anon; -GRANT ALL ON TABLE public.agenda_configuracoes TO authenticated; -GRANT ALL ON TABLE public.agenda_configuracoes TO service_role; - - --- --- Name: TABLE agenda_eventos; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.agenda_eventos TO postgres; -GRANT ALL ON TABLE public.agenda_eventos TO anon; -GRANT ALL ON TABLE public.agenda_eventos TO authenticated; -GRANT ALL ON TABLE public.agenda_eventos TO service_role; - - --- --- Name: TABLE agenda_excecoes; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.agenda_excecoes TO postgres; -GRANT ALL ON TABLE public.agenda_excecoes TO anon; -GRANT ALL ON TABLE public.agenda_excecoes TO authenticated; -GRANT ALL ON TABLE public.agenda_excecoes TO service_role; - - --- --- Name: TABLE agenda_online_slots; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.agenda_online_slots TO postgres; -GRANT ALL ON TABLE public.agenda_online_slots TO anon; -GRANT ALL ON TABLE public.agenda_online_slots TO authenticated; -GRANT ALL ON TABLE public.agenda_online_slots TO service_role; - - --- --- Name: SEQUENCE agenda_online_slots_id_seq; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON SEQUENCE public.agenda_online_slots_id_seq TO postgres; -GRANT ALL ON SEQUENCE public.agenda_online_slots_id_seq TO anon; -GRANT ALL ON SEQUENCE public.agenda_online_slots_id_seq TO authenticated; -GRANT ALL ON SEQUENCE public.agenda_online_slots_id_seq TO service_role; - - --- --- Name: TABLE agenda_regras_semanais; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.agenda_regras_semanais TO postgres; -GRANT ALL ON TABLE public.agenda_regras_semanais TO anon; -GRANT ALL ON TABLE public.agenda_regras_semanais TO authenticated; -GRANT ALL ON TABLE public.agenda_regras_semanais TO service_role; - - --- --- Name: TABLE agenda_slots_bloqueados_semanais; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.agenda_slots_bloqueados_semanais TO postgres; -GRANT ALL ON TABLE public.agenda_slots_bloqueados_semanais TO anon; -GRANT ALL ON TABLE public.agenda_slots_bloqueados_semanais TO authenticated; -GRANT ALL ON TABLE public.agenda_slots_bloqueados_semanais TO service_role; - - --- --- Name: TABLE agenda_slots_regras; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.agenda_slots_regras TO postgres; -GRANT ALL ON TABLE public.agenda_slots_regras TO anon; -GRANT ALL ON TABLE public.agenda_slots_regras TO authenticated; -GRANT ALL ON TABLE public.agenda_slots_regras TO service_role; - - --- --- Name: TABLE agendador_configuracoes; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.agendador_configuracoes TO postgres; -GRANT ALL ON TABLE public.agendador_configuracoes TO anon; -GRANT ALL ON TABLE public.agendador_configuracoes TO authenticated; -GRANT ALL ON TABLE public.agendador_configuracoes TO service_role; - - --- --- Name: TABLE agendador_solicitacoes; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.agendador_solicitacoes TO postgres; -GRANT ALL ON TABLE public.agendador_solicitacoes TO anon; -GRANT ALL ON TABLE public.agendador_solicitacoes TO authenticated; -GRANT ALL ON TABLE public.agendador_solicitacoes TO service_role; - - --- --- Name: TABLE billing_contracts; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.billing_contracts TO postgres; -GRANT ALL ON TABLE public.billing_contracts TO anon; -GRANT ALL ON TABLE public.billing_contracts TO authenticated; -GRANT ALL ON TABLE public.billing_contracts TO service_role; - - --- --- Name: TABLE commitment_services; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.commitment_services TO postgres; -GRANT ALL ON TABLE public.commitment_services TO anon; -GRANT ALL ON TABLE public.commitment_services TO authenticated; -GRANT ALL ON TABLE public.commitment_services TO service_role; - - --- --- Name: TABLE commitment_time_logs; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.commitment_time_logs TO postgres; -GRANT ALL ON TABLE public.commitment_time_logs TO anon; -GRANT ALL ON TABLE public.commitment_time_logs TO authenticated; -GRANT ALL ON TABLE public.commitment_time_logs TO service_role; - - --- --- Name: TABLE company_profiles; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.company_profiles TO postgres; -GRANT ALL ON TABLE public.company_profiles TO anon; -GRANT ALL ON TABLE public.company_profiles TO authenticated; -GRANT ALL ON TABLE public.company_profiles TO service_role; - - --- --- Name: TABLE current_tenant_id; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.current_tenant_id TO postgres; -GRANT ALL ON TABLE public.current_tenant_id TO anon; -GRANT ALL ON TABLE public.current_tenant_id TO authenticated; -GRANT ALL ON TABLE public.current_tenant_id TO service_role; - - --- --- Name: TABLE determined_commitment_fields; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.determined_commitment_fields TO postgres; -GRANT ALL ON TABLE public.determined_commitment_fields TO anon; -GRANT ALL ON TABLE public.determined_commitment_fields TO authenticated; -GRANT ALL ON TABLE public.determined_commitment_fields TO service_role; - - --- --- Name: TABLE determined_commitments; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.determined_commitments TO postgres; -GRANT ALL ON TABLE public.determined_commitments TO anon; -GRANT ALL ON TABLE public.determined_commitments TO authenticated; -GRANT ALL ON TABLE public.determined_commitments TO service_role; - - --- --- Name: TABLE dev_user_credentials; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.dev_user_credentials TO postgres; -GRANT ALL ON TABLE public.dev_user_credentials TO anon; -GRANT ALL ON TABLE public.dev_user_credentials TO authenticated; -GRANT ALL ON TABLE public.dev_user_credentials TO service_role; - - --- --- Name: TABLE email_layout_config; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.email_layout_config TO postgres; -GRANT ALL ON TABLE public.email_layout_config TO anon; -GRANT ALL ON TABLE public.email_layout_config TO authenticated; -GRANT ALL ON TABLE public.email_layout_config TO service_role; - - --- --- Name: TABLE email_templates_global; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.email_templates_global TO postgres; -GRANT ALL ON TABLE public.email_templates_global TO anon; -GRANT ALL ON TABLE public.email_templates_global TO authenticated; -GRANT ALL ON TABLE public.email_templates_global TO service_role; - - --- --- Name: TABLE email_templates_tenant; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.email_templates_tenant TO postgres; -GRANT ALL ON TABLE public.email_templates_tenant TO anon; -GRANT ALL ON TABLE public.email_templates_tenant TO authenticated; -GRANT ALL ON TABLE public.email_templates_tenant TO service_role; - - --- --- Name: TABLE entitlements_invalidation; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.entitlements_invalidation TO postgres; -GRANT ALL ON TABLE public.entitlements_invalidation TO anon; -GRANT ALL ON TABLE public.entitlements_invalidation TO authenticated; -GRANT ALL ON TABLE public.entitlements_invalidation TO service_role; - - --- --- Name: TABLE features; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.features TO postgres; -GRANT ALL ON TABLE public.features TO anon; -GRANT ALL ON TABLE public.features TO authenticated; -GRANT ALL ON TABLE public.features TO service_role; - - --- --- Name: TABLE feriados; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.feriados TO postgres; -GRANT ALL ON TABLE public.feriados TO anon; -GRANT ALL ON TABLE public.feriados TO authenticated; -GRANT ALL ON TABLE public.feriados TO service_role; - - --- --- Name: TABLE financial_categories; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.financial_categories TO postgres; -GRANT ALL ON TABLE public.financial_categories TO anon; -GRANT ALL ON TABLE public.financial_categories TO authenticated; -GRANT ALL ON TABLE public.financial_categories TO service_role; - - --- --- Name: TABLE financial_exceptions; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.financial_exceptions TO postgres; -GRANT ALL ON TABLE public.financial_exceptions TO anon; -GRANT ALL ON TABLE public.financial_exceptions TO authenticated; -GRANT ALL ON TABLE public.financial_exceptions TO service_role; - - --- --- Name: TABLE global_notices; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.global_notices TO postgres; -GRANT ALL ON TABLE public.global_notices TO anon; -GRANT ALL ON TABLE public.global_notices TO authenticated; -GRANT ALL ON TABLE public.global_notices TO service_role; - - --- --- Name: TABLE insurance_plan_services; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.insurance_plan_services TO postgres; -GRANT ALL ON TABLE public.insurance_plan_services TO anon; -GRANT ALL ON TABLE public.insurance_plan_services TO authenticated; -GRANT ALL ON TABLE public.insurance_plan_services TO service_role; - - --- --- Name: TABLE insurance_plans; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.insurance_plans TO postgres; -GRANT ALL ON TABLE public.insurance_plans TO anon; -GRANT ALL ON TABLE public.insurance_plans TO authenticated; -GRANT ALL ON TABLE public.insurance_plans TO service_role; - - --- --- Name: TABLE login_carousel_slides; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.login_carousel_slides TO postgres; -GRANT ALL ON TABLE public.login_carousel_slides TO anon; -GRANT ALL ON TABLE public.login_carousel_slides TO authenticated; -GRANT ALL ON TABLE public.login_carousel_slides TO service_role; - - --- --- Name: TABLE module_features; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.module_features TO postgres; -GRANT ALL ON TABLE public.module_features TO anon; -GRANT ALL ON TABLE public.module_features TO authenticated; -GRANT ALL ON TABLE public.module_features TO service_role; - - --- --- Name: TABLE modules; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.modules TO postgres; -GRANT ALL ON TABLE public.modules TO anon; -GRANT ALL ON TABLE public.modules TO authenticated; -GRANT ALL ON TABLE public.modules TO service_role; - - --- --- Name: TABLE notice_dismissals; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.notice_dismissals TO postgres; -GRANT ALL ON TABLE public.notice_dismissals TO anon; -GRANT ALL ON TABLE public.notice_dismissals TO authenticated; -GRANT ALL ON TABLE public.notice_dismissals TO service_role; - - --- --- Name: TABLE notification_channels; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.notification_channels TO postgres; -GRANT ALL ON TABLE public.notification_channels TO anon; -GRANT ALL ON TABLE public.notification_channels TO authenticated; -GRANT ALL ON TABLE public.notification_channels TO service_role; - - --- --- Name: TABLE notification_logs; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.notification_logs TO postgres; -GRANT ALL ON TABLE public.notification_logs TO anon; -GRANT ALL ON TABLE public.notification_logs TO authenticated; -GRANT ALL ON TABLE public.notification_logs TO service_role; - - --- --- Name: TABLE notification_preferences; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.notification_preferences TO postgres; -GRANT ALL ON TABLE public.notification_preferences TO anon; -GRANT ALL ON TABLE public.notification_preferences TO authenticated; -GRANT ALL ON TABLE public.notification_preferences TO service_role; - - --- --- Name: TABLE notification_queue; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.notification_queue TO postgres; -GRANT ALL ON TABLE public.notification_queue TO anon; -GRANT ALL ON TABLE public.notification_queue TO authenticated; -GRANT ALL ON TABLE public.notification_queue TO service_role; - - --- --- Name: TABLE notification_schedules; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.notification_schedules TO postgres; -GRANT ALL ON TABLE public.notification_schedules TO anon; -GRANT ALL ON TABLE public.notification_schedules TO authenticated; -GRANT ALL ON TABLE public.notification_schedules TO service_role; - - --- --- Name: TABLE notification_templates; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.notification_templates TO postgres; -GRANT ALL ON TABLE public.notification_templates TO anon; -GRANT ALL ON TABLE public.notification_templates TO authenticated; -GRANT ALL ON TABLE public.notification_templates TO service_role; - - --- --- Name: TABLE notifications; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.notifications TO postgres; -GRANT ALL ON TABLE public.notifications TO anon; -GRANT ALL ON TABLE public.notifications TO authenticated; -GRANT ALL ON TABLE public.notifications TO service_role; - - --- --- Name: TABLE plan_features; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.plan_features TO postgres; -GRANT ALL ON TABLE public.plan_features TO anon; -GRANT ALL ON TABLE public.plan_features TO authenticated; -GRANT ALL ON TABLE public.plan_features TO service_role; - - --- --- Name: TABLE tenant_modules; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.tenant_modules TO postgres; -GRANT ALL ON TABLE public.tenant_modules TO anon; -GRANT ALL ON TABLE public.tenant_modules TO authenticated; -GRANT ALL ON TABLE public.tenant_modules TO service_role; - - --- --- Name: TABLE owner_feature_entitlements; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.owner_feature_entitlements TO postgres; -GRANT ALL ON TABLE public.owner_feature_entitlements TO anon; -GRANT ALL ON TABLE public.owner_feature_entitlements TO authenticated; -GRANT ALL ON TABLE public.owner_feature_entitlements TO service_role; - - --- --- Name: TABLE owner_users; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.owner_users TO postgres; -GRANT ALL ON TABLE public.owner_users TO anon; -GRANT ALL ON TABLE public.owner_users TO authenticated; -GRANT ALL ON TABLE public.owner_users TO service_role; - - --- --- Name: TABLE patient_discounts; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.patient_discounts TO postgres; -GRANT ALL ON TABLE public.patient_discounts TO anon; -GRANT ALL ON TABLE public.patient_discounts TO authenticated; -GRANT ALL ON TABLE public.patient_discounts TO service_role; - - --- --- Name: TABLE patient_group_patient; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.patient_group_patient TO postgres; -GRANT ALL ON TABLE public.patient_group_patient TO anon; -GRANT ALL ON TABLE public.patient_group_patient TO authenticated; -GRANT ALL ON TABLE public.patient_group_patient TO service_role; - - --- --- Name: TABLE patient_groups; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.patient_groups TO postgres; -GRANT ALL ON TABLE public.patient_groups TO anon; -GRANT ALL ON TABLE public.patient_groups TO authenticated; -GRANT ALL ON TABLE public.patient_groups TO service_role; - - --- --- Name: TABLE patient_intake_requests; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.patient_intake_requests TO postgres; -GRANT ALL ON TABLE public.patient_intake_requests TO authenticated; -GRANT ALL ON TABLE public.patient_intake_requests TO service_role; - - --- --- Name: TABLE patient_invites; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.patient_invites TO postgres; -GRANT ALL ON TABLE public.patient_invites TO authenticated; -GRANT ALL ON TABLE public.patient_invites TO service_role; - - --- --- Name: TABLE patient_patient_tag; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.patient_patient_tag TO postgres; -GRANT ALL ON TABLE public.patient_patient_tag TO anon; -GRANT ALL ON TABLE public.patient_patient_tag TO authenticated; -GRANT ALL ON TABLE public.patient_patient_tag TO service_role; - - --- --- Name: TABLE patient_tags; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.patient_tags TO postgres; -GRANT ALL ON TABLE public.patient_tags TO anon; -GRANT ALL ON TABLE public.patient_tags TO authenticated; -GRANT ALL ON TABLE public.patient_tags TO service_role; - - --- --- Name: TABLE patients; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.patients TO postgres; -GRANT ALL ON TABLE public.patients TO anon; -GRANT ALL ON TABLE public.patients TO authenticated; -GRANT ALL ON TABLE public.patients TO service_role; - - --- --- Name: TABLE payment_settings; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.payment_settings TO postgres; -GRANT ALL ON TABLE public.payment_settings TO anon; -GRANT ALL ON TABLE public.payment_settings TO authenticated; -GRANT ALL ON TABLE public.payment_settings TO service_role; - - --- --- Name: TABLE plan_prices; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.plan_prices TO postgres; -GRANT ALL ON TABLE public.plan_prices TO anon; -GRANT ALL ON TABLE public.plan_prices TO authenticated; -GRANT ALL ON TABLE public.plan_prices TO service_role; - - --- --- Name: TABLE plan_public; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.plan_public TO postgres; -GRANT ALL ON TABLE public.plan_public TO anon; -GRANT ALL ON TABLE public.plan_public TO authenticated; -GRANT ALL ON TABLE public.plan_public TO service_role; - - --- --- Name: TABLE plan_public_bullets; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.plan_public_bullets TO postgres; -GRANT ALL ON TABLE public.plan_public_bullets TO anon; -GRANT ALL ON TABLE public.plan_public_bullets TO authenticated; -GRANT ALL ON TABLE public.plan_public_bullets TO service_role; - - --- --- Name: TABLE plans; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.plans TO postgres; -GRANT ALL ON TABLE public.plans TO anon; -GRANT ALL ON TABLE public.plans TO authenticated; -GRANT ALL ON TABLE public.plans TO service_role; - - --- --- Name: TABLE professional_pricing; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.professional_pricing TO postgres; -GRANT ALL ON TABLE public.professional_pricing TO anon; -GRANT ALL ON TABLE public.professional_pricing TO authenticated; -GRANT ALL ON TABLE public.professional_pricing TO service_role; - - --- --- Name: TABLE profiles; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.profiles TO postgres; -GRANT ALL ON TABLE public.profiles TO anon; -GRANT ALL ON TABLE public.profiles TO authenticated; -GRANT ALL ON TABLE public.profiles TO service_role; - - --- --- Name: TABLE recurrence_exceptions; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.recurrence_exceptions TO postgres; -GRANT ALL ON TABLE public.recurrence_exceptions TO anon; -GRANT ALL ON TABLE public.recurrence_exceptions TO authenticated; -GRANT ALL ON TABLE public.recurrence_exceptions TO service_role; - - --- --- Name: TABLE recurrence_rule_services; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.recurrence_rule_services TO postgres; -GRANT ALL ON TABLE public.recurrence_rule_services TO anon; -GRANT ALL ON TABLE public.recurrence_rule_services TO authenticated; -GRANT ALL ON TABLE public.recurrence_rule_services TO service_role; - - --- --- Name: TABLE recurrence_rules; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.recurrence_rules TO postgres; -GRANT ALL ON TABLE public.recurrence_rules TO anon; -GRANT ALL ON TABLE public.recurrence_rules TO authenticated; -GRANT ALL ON TABLE public.recurrence_rules TO service_role; - - --- --- Name: TABLE saas_admins; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.saas_admins TO postgres; -GRANT ALL ON TABLE public.saas_admins TO anon; -GRANT ALL ON TABLE public.saas_admins TO authenticated; -GRANT ALL ON TABLE public.saas_admins TO service_role; - - --- --- Name: TABLE saas_doc_votos; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.saas_doc_votos TO postgres; -GRANT ALL ON TABLE public.saas_doc_votos TO anon; -GRANT ALL ON TABLE public.saas_doc_votos TO authenticated; -GRANT ALL ON TABLE public.saas_doc_votos TO service_role; - - --- --- Name: TABLE saas_docs; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.saas_docs TO postgres; -GRANT ALL ON TABLE public.saas_docs TO anon; -GRANT ALL ON TABLE public.saas_docs TO authenticated; -GRANT ALL ON TABLE public.saas_docs TO service_role; - - --- --- Name: TABLE saas_faq; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.saas_faq TO postgres; -GRANT ALL ON TABLE public.saas_faq TO anon; -GRANT ALL ON TABLE public.saas_faq TO authenticated; -GRANT ALL ON TABLE public.saas_faq TO service_role; - - --- --- Name: TABLE saas_faq_itens; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.saas_faq_itens TO postgres; -GRANT ALL ON TABLE public.saas_faq_itens TO anon; -GRANT ALL ON TABLE public.saas_faq_itens TO authenticated; -GRANT ALL ON TABLE public.saas_faq_itens TO service_role; - - --- --- Name: TABLE services; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.services TO postgres; -GRANT ALL ON TABLE public.services TO anon; -GRANT ALL ON TABLE public.services TO authenticated; -GRANT ALL ON TABLE public.services TO service_role; - - --- --- Name: TABLE subscription_events; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.subscription_events TO postgres; -GRANT ALL ON TABLE public.subscription_events TO anon; -GRANT ALL ON TABLE public.subscription_events TO authenticated; -GRANT ALL ON TABLE public.subscription_events TO service_role; - - --- --- Name: TABLE subscription_intents_personal; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.subscription_intents_personal TO postgres; -GRANT ALL ON TABLE public.subscription_intents_personal TO anon; -GRANT ALL ON TABLE public.subscription_intents_personal TO authenticated; -GRANT ALL ON TABLE public.subscription_intents_personal TO service_role; - - --- --- Name: TABLE subscription_intents_tenant; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.subscription_intents_tenant TO postgres; -GRANT ALL ON TABLE public.subscription_intents_tenant TO anon; -GRANT ALL ON TABLE public.subscription_intents_tenant TO authenticated; -GRANT ALL ON TABLE public.subscription_intents_tenant TO service_role; - - --- --- Name: TABLE subscription_intents; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.subscription_intents TO postgres; -GRANT ALL ON TABLE public.subscription_intents TO anon; -GRANT ALL ON TABLE public.subscription_intents TO authenticated; -GRANT ALL ON TABLE public.subscription_intents TO service_role; - - --- --- Name: TABLE subscription_intents_legacy; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.subscription_intents_legacy TO postgres; -GRANT ALL ON TABLE public.subscription_intents_legacy TO anon; -GRANT ALL ON TABLE public.subscription_intents_legacy TO authenticated; -GRANT ALL ON TABLE public.subscription_intents_legacy TO service_role; - - --- --- Name: TABLE support_sessions; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.support_sessions TO postgres; -GRANT ALL ON TABLE public.support_sessions TO anon; -GRANT ALL ON TABLE public.support_sessions TO authenticated; -GRANT ALL ON TABLE public.support_sessions TO service_role; - - --- --- Name: TABLE tenant_feature_exceptions_log; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.tenant_feature_exceptions_log TO postgres; -GRANT ALL ON TABLE public.tenant_feature_exceptions_log TO anon; -GRANT ALL ON TABLE public.tenant_feature_exceptions_log TO authenticated; -GRANT ALL ON TABLE public.tenant_feature_exceptions_log TO service_role; - - --- --- Name: TABLE tenant_features; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.tenant_features TO postgres; -GRANT ALL ON TABLE public.tenant_features TO anon; -GRANT ALL ON TABLE public.tenant_features TO authenticated; -GRANT ALL ON TABLE public.tenant_features TO service_role; - - --- --- Name: TABLE tenant_invites; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.tenant_invites TO postgres; -GRANT ALL ON TABLE public.tenant_invites TO authenticated; -GRANT ALL ON TABLE public.tenant_invites TO service_role; - - --- --- Name: TABLE tenants; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.tenants TO postgres; -GRANT ALL ON TABLE public.tenants TO anon; -GRANT ALL ON TABLE public.tenants TO authenticated; -GRANT ALL ON TABLE public.tenants TO service_role; - - --- --- Name: TABLE therapist_payout_records; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.therapist_payout_records TO postgres; -GRANT ALL ON TABLE public.therapist_payout_records TO anon; -GRANT ALL ON TABLE public.therapist_payout_records TO authenticated; -GRANT ALL ON TABLE public.therapist_payout_records TO service_role; - - --- --- Name: TABLE user_settings; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.user_settings TO postgres; -GRANT ALL ON TABLE public.user_settings TO anon; -GRANT ALL ON TABLE public.user_settings TO authenticated; -GRANT ALL ON TABLE public.user_settings TO service_role; - - --- --- Name: TABLE v_auth_users_public; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_auth_users_public TO postgres; -GRANT ALL ON TABLE public.v_auth_users_public TO anon; -GRANT ALL ON TABLE public.v_auth_users_public TO authenticated; -GRANT ALL ON TABLE public.v_auth_users_public TO service_role; - - --- --- Name: TABLE v_cashflow_projection; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_cashflow_projection TO postgres; -GRANT ALL ON TABLE public.v_cashflow_projection TO anon; -GRANT ALL ON TABLE public.v_cashflow_projection TO authenticated; -GRANT ALL ON TABLE public.v_cashflow_projection TO service_role; - - --- --- Name: TABLE v_commitment_totals; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_commitment_totals TO postgres; -GRANT ALL ON TABLE public.v_commitment_totals TO anon; -GRANT ALL ON TABLE public.v_commitment_totals TO authenticated; -GRANT ALL ON TABLE public.v_commitment_totals TO service_role; - - --- --- Name: TABLE v_patient_groups_with_counts; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_patient_groups_with_counts TO postgres; -GRANT ALL ON TABLE public.v_patient_groups_with_counts TO anon; -GRANT ALL ON TABLE public.v_patient_groups_with_counts TO authenticated; -GRANT ALL ON TABLE public.v_patient_groups_with_counts TO service_role; - - --- --- Name: TABLE v_plan_active_prices; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_plan_active_prices TO postgres; -GRANT ALL ON TABLE public.v_plan_active_prices TO anon; -GRANT ALL ON TABLE public.v_plan_active_prices TO authenticated; -GRANT ALL ON TABLE public.v_plan_active_prices TO service_role; - - --- --- Name: TABLE v_public_pricing; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_public_pricing TO postgres; -GRANT ALL ON TABLE public.v_public_pricing TO anon; -GRANT ALL ON TABLE public.v_public_pricing TO authenticated; -GRANT ALL ON TABLE public.v_public_pricing TO service_role; - - --- --- Name: TABLE v_subscription_feature_mismatch; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_subscription_feature_mismatch TO postgres; -GRANT ALL ON TABLE public.v_subscription_feature_mismatch TO anon; -GRANT ALL ON TABLE public.v_subscription_feature_mismatch TO authenticated; -GRANT ALL ON TABLE public.v_subscription_feature_mismatch TO service_role; - - --- --- Name: TABLE v_subscription_health; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_subscription_health TO postgres; -GRANT ALL ON TABLE public.v_subscription_health TO anon; -GRANT ALL ON TABLE public.v_subscription_health TO authenticated; -GRANT ALL ON TABLE public.v_subscription_health TO service_role; - - --- --- Name: TABLE v_subscription_health_v2; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_subscription_health_v2 TO postgres; -GRANT ALL ON TABLE public.v_subscription_health_v2 TO anon; -GRANT ALL ON TABLE public.v_subscription_health_v2 TO authenticated; -GRANT ALL ON TABLE public.v_subscription_health_v2 TO service_role; - - --- --- Name: TABLE v_tag_patient_counts; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_tag_patient_counts TO postgres; -GRANT ALL ON TABLE public.v_tag_patient_counts TO anon; -GRANT ALL ON TABLE public.v_tag_patient_counts TO authenticated; -GRANT ALL ON TABLE public.v_tag_patient_counts TO service_role; - - --- --- Name: TABLE v_tenant_active_subscription; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_tenant_active_subscription TO postgres; -GRANT ALL ON TABLE public.v_tenant_active_subscription TO anon; -GRANT ALL ON TABLE public.v_tenant_active_subscription TO authenticated; -GRANT ALL ON TABLE public.v_tenant_active_subscription TO service_role; - - --- --- Name: TABLE v_tenant_entitlements; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_tenant_entitlements TO postgres; -GRANT ALL ON TABLE public.v_tenant_entitlements TO anon; -GRANT ALL ON TABLE public.v_tenant_entitlements TO authenticated; -GRANT ALL ON TABLE public.v_tenant_entitlements TO service_role; - - --- --- Name: TABLE v_tenant_entitlements_full; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_tenant_entitlements_full TO postgres; -GRANT ALL ON TABLE public.v_tenant_entitlements_full TO anon; -GRANT ALL ON TABLE public.v_tenant_entitlements_full TO authenticated; -GRANT ALL ON TABLE public.v_tenant_entitlements_full TO service_role; - - --- --- Name: TABLE v_tenant_entitlements_json; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_tenant_entitlements_json TO postgres; -GRANT ALL ON TABLE public.v_tenant_entitlements_json TO anon; -GRANT ALL ON TABLE public.v_tenant_entitlements_json TO authenticated; -GRANT ALL ON TABLE public.v_tenant_entitlements_json TO service_role; - - --- --- Name: TABLE v_tenant_feature_exceptions; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_tenant_feature_exceptions TO postgres; -GRANT ALL ON TABLE public.v_tenant_feature_exceptions TO anon; -GRANT ALL ON TABLE public.v_tenant_feature_exceptions TO authenticated; -GRANT ALL ON TABLE public.v_tenant_feature_exceptions TO service_role; - - --- --- Name: TABLE v_tenant_feature_mismatch; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_tenant_feature_mismatch TO postgres; -GRANT ALL ON TABLE public.v_tenant_feature_mismatch TO anon; -GRANT ALL ON TABLE public.v_tenant_feature_mismatch TO authenticated; -GRANT ALL ON TABLE public.v_tenant_feature_mismatch TO service_role; - - --- --- Name: TABLE v_tenant_members_with_profiles; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_tenant_members_with_profiles TO postgres; -GRANT ALL ON TABLE public.v_tenant_members_with_profiles TO anon; -GRANT ALL ON TABLE public.v_tenant_members_with_profiles TO authenticated; -GRANT ALL ON TABLE public.v_tenant_members_with_profiles TO service_role; - - --- --- Name: TABLE v_tenant_people; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_tenant_people TO postgres; -GRANT ALL ON TABLE public.v_tenant_people TO anon; -GRANT ALL ON TABLE public.v_tenant_people TO authenticated; -GRANT ALL ON TABLE public.v_tenant_people TO service_role; - - --- --- Name: TABLE v_tenant_staff; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_tenant_staff TO postgres; -GRANT ALL ON TABLE public.v_tenant_staff TO anon; -GRANT ALL ON TABLE public.v_tenant_staff TO authenticated; -GRANT ALL ON TABLE public.v_tenant_staff TO service_role; - - --- --- Name: TABLE v_user_active_subscription; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_user_active_subscription TO postgres; -GRANT ALL ON TABLE public.v_user_active_subscription TO anon; -GRANT ALL ON TABLE public.v_user_active_subscription TO authenticated; -GRANT ALL ON TABLE public.v_user_active_subscription TO service_role; - - --- --- Name: TABLE v_user_entitlements; Type: ACL; Schema: public; Owner: supabase_admin --- - -GRANT ALL ON TABLE public.v_user_entitlements TO postgres; -GRANT ALL ON TABLE public.v_user_entitlements TO anon; -GRANT ALL ON TABLE public.v_user_entitlements TO authenticated; -GRANT ALL ON TABLE public.v_user_entitlements TO service_role; - - --- --- Name: TABLE messages; Type: ACL; Schema: realtime; Owner: supabase_realtime_admin --- - -GRANT ALL ON TABLE realtime.messages TO postgres; -GRANT ALL ON TABLE realtime.messages TO dashboard_user; -GRANT SELECT,INSERT,UPDATE ON TABLE realtime.messages TO anon; -GRANT SELECT,INSERT,UPDATE ON TABLE realtime.messages TO authenticated; -GRANT SELECT,INSERT,UPDATE ON TABLE realtime.messages TO service_role; - - --- --- Name: TABLE messages_2026_03_20; Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON TABLE realtime.messages_2026_03_20 TO postgres; -GRANT ALL ON TABLE realtime.messages_2026_03_20 TO dashboard_user; - - --- --- Name: TABLE messages_2026_03_21; Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON TABLE realtime.messages_2026_03_21 TO postgres; -GRANT ALL ON TABLE realtime.messages_2026_03_21 TO dashboard_user; - - --- --- Name: TABLE messages_2026_03_22; Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON TABLE realtime.messages_2026_03_22 TO postgres; -GRANT ALL ON TABLE realtime.messages_2026_03_22 TO dashboard_user; - - --- --- Name: TABLE messages_2026_03_23; Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON TABLE realtime.messages_2026_03_23 TO postgres; -GRANT ALL ON TABLE realtime.messages_2026_03_23 TO dashboard_user; - - --- --- Name: TABLE messages_2026_03_24; Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON TABLE realtime.messages_2026_03_24 TO postgres; -GRANT ALL ON TABLE realtime.messages_2026_03_24 TO dashboard_user; - - --- --- Name: TABLE messages_2026_03_25; Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON TABLE realtime.messages_2026_03_25 TO postgres; -GRANT ALL ON TABLE realtime.messages_2026_03_25 TO dashboard_user; - - --- --- Name: TABLE messages_2026_03_26; Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON TABLE realtime.messages_2026_03_26 TO postgres; -GRANT ALL ON TABLE realtime.messages_2026_03_26 TO dashboard_user; - - --- --- Name: TABLE schema_migrations; Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON TABLE realtime.schema_migrations TO postgres; -GRANT ALL ON TABLE realtime.schema_migrations TO dashboard_user; -GRANT SELECT ON TABLE realtime.schema_migrations TO anon; -GRANT SELECT ON TABLE realtime.schema_migrations TO authenticated; -GRANT SELECT ON TABLE realtime.schema_migrations TO service_role; -GRANT ALL ON TABLE realtime.schema_migrations TO supabase_realtime_admin; - - --- --- Name: TABLE subscription; Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON TABLE realtime.subscription TO postgres; -GRANT ALL ON TABLE realtime.subscription TO dashboard_user; -GRANT SELECT ON TABLE realtime.subscription TO anon; -GRANT SELECT ON TABLE realtime.subscription TO authenticated; -GRANT SELECT ON TABLE realtime.subscription TO service_role; -GRANT ALL ON TABLE realtime.subscription TO supabase_realtime_admin; - - --- --- Name: SEQUENCE subscription_id_seq; Type: ACL; Schema: realtime; Owner: supabase_admin --- - -GRANT ALL ON SEQUENCE realtime.subscription_id_seq TO postgres; -GRANT ALL ON SEQUENCE realtime.subscription_id_seq TO dashboard_user; -GRANT USAGE ON SEQUENCE realtime.subscription_id_seq TO anon; -GRANT USAGE ON SEQUENCE realtime.subscription_id_seq TO authenticated; -GRANT USAGE ON SEQUENCE realtime.subscription_id_seq TO service_role; -GRANT ALL ON SEQUENCE realtime.subscription_id_seq TO supabase_realtime_admin; - - --- --- Name: TABLE buckets; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON TABLE storage.buckets TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE storage.buckets TO service_role; -GRANT ALL ON TABLE storage.buckets TO authenticated; -GRANT ALL ON TABLE storage.buckets TO anon; - - --- --- Name: TABLE buckets_analytics; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON TABLE storage.buckets_analytics TO service_role; -GRANT ALL ON TABLE storage.buckets_analytics TO authenticated; -GRANT ALL ON TABLE storage.buckets_analytics TO anon; - - --- --- Name: TABLE buckets_vectors; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT SELECT ON TABLE storage.buckets_vectors TO service_role; -GRANT SELECT ON TABLE storage.buckets_vectors TO authenticated; -GRANT SELECT ON TABLE storage.buckets_vectors TO anon; - - --- --- Name: TABLE iceberg_namespaces; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON TABLE storage.iceberg_namespaces TO service_role; -GRANT SELECT ON TABLE storage.iceberg_namespaces TO authenticated; -GRANT SELECT ON TABLE storage.iceberg_namespaces TO anon; - - --- --- Name: TABLE iceberg_tables; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON TABLE storage.iceberg_tables TO service_role; -GRANT SELECT ON TABLE storage.iceberg_tables TO authenticated; -GRANT SELECT ON TABLE storage.iceberg_tables TO anon; - - --- --- Name: TABLE objects; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON TABLE storage.objects TO postgres WITH GRANT OPTION; -GRANT ALL ON TABLE storage.objects TO service_role; -GRANT ALL ON TABLE storage.objects TO authenticated; -GRANT ALL ON TABLE storage.objects TO anon; - - --- --- Name: TABLE s3_multipart_uploads; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON TABLE storage.s3_multipart_uploads TO service_role; -GRANT SELECT ON TABLE storage.s3_multipart_uploads TO authenticated; -GRANT SELECT ON TABLE storage.s3_multipart_uploads TO anon; - - --- --- Name: TABLE s3_multipart_uploads_parts; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON TABLE storage.s3_multipart_uploads_parts TO service_role; -GRANT SELECT ON TABLE storage.s3_multipart_uploads_parts TO authenticated; -GRANT SELECT ON TABLE storage.s3_multipart_uploads_parts TO anon; - - --- --- Name: TABLE vector_indexes; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT SELECT ON TABLE storage.vector_indexes TO service_role; -GRANT SELECT ON TABLE storage.vector_indexes TO authenticated; -GRANT SELECT ON TABLE storage.vector_indexes TO anon; - - --- --- Name: TABLE hooks; Type: ACL; Schema: supabase_functions; Owner: supabase_functions_admin --- - -GRANT ALL ON TABLE supabase_functions.hooks TO postgres; -GRANT ALL ON TABLE supabase_functions.hooks TO anon; -GRANT ALL ON TABLE supabase_functions.hooks TO authenticated; -GRANT ALL ON TABLE supabase_functions.hooks TO service_role; - - --- --- Name: SEQUENCE hooks_id_seq; Type: ACL; Schema: supabase_functions; Owner: supabase_functions_admin --- - -GRANT ALL ON SEQUENCE supabase_functions.hooks_id_seq TO postgres; -GRANT ALL ON SEQUENCE supabase_functions.hooks_id_seq TO anon; -GRANT ALL ON SEQUENCE supabase_functions.hooks_id_seq TO authenticated; -GRANT ALL ON SEQUENCE supabase_functions.hooks_id_seq TO service_role; - - --- --- Name: TABLE migrations; Type: ACL; Schema: supabase_functions; Owner: supabase_functions_admin --- - -GRANT ALL ON TABLE supabase_functions.migrations TO postgres; -GRANT ALL ON TABLE supabase_functions.migrations TO anon; -GRANT ALL ON TABLE supabase_functions.migrations TO authenticated; -GRANT ALL ON TABLE supabase_functions.migrations TO service_role; - - --- --- Name: TABLE secrets; Type: ACL; Schema: vault; Owner: supabase_admin --- - -GRANT SELECT,REFERENCES,DELETE,TRUNCATE ON TABLE vault.secrets TO postgres WITH GRANT OPTION; -GRANT SELECT,DELETE ON TABLE vault.secrets TO service_role; - - --- --- Name: TABLE decrypted_secrets; Type: ACL; Schema: vault; Owner: supabase_admin --- - -GRANT SELECT,REFERENCES,DELETE,TRUNCATE ON TABLE vault.decrypted_secrets TO postgres WITH GRANT OPTION; -GRANT SELECT,DELETE ON TABLE vault.decrypted_secrets TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: auth; Owner: supabase_auth_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON SEQUENCES TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: auth; Owner: supabase_auth_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON FUNCTIONS TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: auth; Owner: supabase_auth_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON TABLES TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: cron; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA cron GRANT ALL ON SEQUENCES TO postgres WITH GRANT OPTION; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: cron; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA cron GRANT ALL ON FUNCTIONS TO postgres WITH GRANT OPTION; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: cron; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA cron GRANT ALL ON TABLES TO postgres WITH GRANT OPTION; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: extensions; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA extensions GRANT ALL ON SEQUENCES TO postgres WITH GRANT OPTION; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: extensions; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA extensions GRANT ALL ON FUNCTIONS TO postgres WITH GRANT OPTION; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: extensions; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA extensions GRANT ALL ON TABLES TO postgres WITH GRANT OPTION; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: graphql; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: graphql; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: graphql; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON TABLES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: graphql_public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: graphql_public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: graphql_public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON TABLES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: public; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: public; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: public; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: realtime; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON SEQUENCES TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: realtime; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON FUNCTIONS TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: realtime; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON TABLES TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: storage; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: storage; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: storage; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON TABLES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: supabase_functions; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: supabase_functions; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: supabase_functions; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON TABLES TO service_role; - - --- --- Name: issue_graphql_placeholder; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin --- - -CREATE EVENT TRIGGER issue_graphql_placeholder ON sql_drop - WHEN TAG IN ('DROP EXTENSION') - EXECUTE FUNCTION extensions.set_graphql_placeholder(); - - -ALTER EVENT TRIGGER issue_graphql_placeholder OWNER TO supabase_admin; - --- --- Name: issue_pg_cron_access; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin --- - -CREATE EVENT TRIGGER issue_pg_cron_access ON ddl_command_end - WHEN TAG IN ('CREATE EXTENSION') - EXECUTE FUNCTION extensions.grant_pg_cron_access(); - - -ALTER EVENT TRIGGER issue_pg_cron_access OWNER TO supabase_admin; - --- --- Name: issue_pg_graphql_access; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin --- - -CREATE EVENT TRIGGER issue_pg_graphql_access ON ddl_command_end - WHEN TAG IN ('CREATE FUNCTION') - EXECUTE FUNCTION extensions.grant_pg_graphql_access(); - - -ALTER EVENT TRIGGER issue_pg_graphql_access OWNER TO supabase_admin; - --- --- Name: issue_pg_net_access; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin --- - -CREATE EVENT TRIGGER issue_pg_net_access ON ddl_command_end - WHEN TAG IN ('CREATE EXTENSION') - EXECUTE FUNCTION extensions.grant_pg_net_access(); - - -ALTER EVENT TRIGGER issue_pg_net_access OWNER TO supabase_admin; - --- --- Name: pgrst_ddl_watch; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin --- - -CREATE EVENT TRIGGER pgrst_ddl_watch ON ddl_command_end - EXECUTE FUNCTION extensions.pgrst_ddl_watch(); - - -ALTER EVENT TRIGGER pgrst_ddl_watch OWNER TO supabase_admin; - --- --- Name: pgrst_drop_watch; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin --- - -CREATE EVENT TRIGGER pgrst_drop_watch ON sql_drop - EXECUTE FUNCTION extensions.pgrst_drop_watch(); - - -ALTER EVENT TRIGGER pgrst_drop_watch OWNER TO supabase_admin; - --- --- PostgreSQL database dump complete --- - -\unrestrict ABfzP9IZJ8pAzvgt6E9jKpFn1phQ3b3Lgk09BZZTle5el6ODr77nIXlXnCf1PS1 - diff --git a/database-novo/seeds/seed_030_dev_phases_items.sql b/database-novo/seeds/seed_030_dev_phases_items.sql new file mode 100644 index 0000000..16abca2 --- /dev/null +++ b/database-novo/seeds/seed_030_dev_phases_items.sql @@ -0,0 +1,229 @@ +-- ============================================================================= +-- Seed 030 — dev_roadmap_phases + dev_roadmap_items +-- Importa Fase 1 / 2 / 3 do development/04-roadmap/ROADMAP.md +-- ============================================================================= +-- ATENÇÃO: este seed faz TRUNCATE RESTART IDENTITY CASCADE. +-- Re-rodá-lo **apaga** edições feitas na UI (roadmap). Rode só no 1º setup ou +-- depois de confirmar que quer redefinir. +-- ============================================================================= + +BEGIN; + +TRUNCATE TABLE public.dev_roadmap_items RESTART IDENTITY CASCADE; +TRUNCATE TABLE public.dev_roadmap_phases RESTART IDENTITY CASCADE; + +-- ----------------------------------------------------------------------------- +-- Fases +-- ----------------------------------------------------------------------------- +INSERT INTO public.dev_roadmap_phases (numero, nome, objetivo, timeline_sugerida, criterio_saida, status, ordem) VALUES +(1, 'MVP Launch', + 'Ter um produto cobrável, confiável, completo o suficiente pra um terapeuta solo trocar Psicomanager/PsicoPlanner pelo AgenciaPsi.', + '4-6 semanas', + '5 usuários pagantes em produção sem churn por "falta de feature básica".', + 'em_andamento', 1), +(2, 'Paridade Competitiva', + 'Qualquer usuário avaliando AgenciaPsi × Psicomanager × PsicoPlanner deve ver paridade ou mais de features. Nenhum "ah, mas lá tem X" válido.', + '2-3 meses após Fase 1', + 'Feature checklist empatada com top-3 concorrentes nichados.', + 'planejada', 2), +(3, 'Diferenciação IA-first', + 'Ter 2-3 features que nenhum concorrente BR tem em paridade. Virar marketing: "o sistema com IA pra psicólogos".', + '3-6 meses após Fase 2', + 'Justificar tier premium 2x mais caro que o básico.', + 'planejada', 3); + +-- ----------------------------------------------------------------------------- +-- FASE 1 — MVP Launch (18 items) +-- ----------------------------------------------------------------------------- + +-- 1.1 Monetização (bloqueador) +INSERT INTO public.dev_roadmap_items (phase_id, numero, bloco, feature, descricao, esforco, prioridade, status, ordem) VALUES +(1, 1, 'Monetização', 'Integração com gateway de pagamento', + 'Asaas ou Iugu (PIX + cartão + boleto nativos). Escolher 1. Stripe só se for internacional depois.', + 'L', 'bloqueador', 'pendente', 1), +(1, 2, 'Monetização', 'Cartão on file (tokenização)', + 'Desdobra do gateway — salvar cartão do paciente pra cobranças recorrentes.', + 'M', 'bloqueador', 'pendente', 2), +(1, 3, 'Monetização', 'Auto-billing recorrente', + 'Trigger: sessão realizada → gera fatura → cobra automaticamente no cartão on file.', + 'M', 'bloqueador', 'pendente', 3), +(1, 4, 'Monetização', 'Cobrança das próprias assinaturas SaaS', + 'Tenants pagam pelo plano via gateway. Aproveita estrutura de subscriptions que já existe.', + 'M', 'bloqueador', 'pendente', 4), + +-- 1.2 Compliance básico BR +(1, 5, 'Compliance BR', 'Tipo de registro profissional', + 'Campo obrigatório no cadastro (CRP, CRM, CRFa, RMS…). Aparece em recibos/laudos.', + 'S', 'bloqueador', 'pendente', 5), +(1, 6, 'Compliance BR', 'Biblioteca de consent forms editáveis', + 'Templates pré-prontos: TCLE, Telehealth, LGPD, Gravação, TCLE menores. Profissional customiza.', + 'M', 'bloqueador', 'pendente', 6), +(1, 7, 'Compliance BR', 'Assinatura eletrônica pelo paciente no portal', + 'Simples — IP + timestamp. Não precisa ICP-Brasil nesta fase.', + 'M', 'bloqueador', 'pendente', 7), +(1, 8, 'Compliance BR', 'Nome social no cadastro', + 'Além do nome de registro. CFP exige. Aparece em todas as telas voltadas ao paciente.', + 'S', 'bloqueador', 'pendente', 8), +(1, 9, 'Compliance BR', 'Especialidades no cadastro do profissional', + 'Lista pré-definida + "outra". DB: tabela specialties + FK em profiles.', + 'S', 'bloqueador', 'pendente', 9), + +-- 1.3 UX mínima esperada +(1, 10, 'UX mínima', 'Busca global no topbar', + 'Paciente, email, telefone. Autocomplete com debounce + highlight.', + 'S', 'alta', 'pendente', 10), +(1, 11, 'UX mínima', 'Recently viewed (últimos 5 pacientes)', + 'Por usuário. localStorage ou tabela user_recent_access.', + 'S', 'alta', 'pendente', 11), +(1, 12, 'UX mínima', 'Papel timbrado', + 'Portar do UniaoApp (já existe noutro projeto) — só adaptar pro AgenciaPsi.', + 'M', 'alta', 'pendente', 12), +(1, 13, 'UX mínima', 'Relatórios com export PDF/Excel', + 'Fechar gaps que o MVP assessment apontou. Estrutura já existe.', + 'M', 'alta', 'pendente', 13), + +-- 1.4 Fiscal mínimo +(1, 14, 'Fiscal mínimo', 'Recibo profissional (PDF)', + 'Gerado com dados CFP (nome, CRP, especialidade, tenant). Já tem base de documentos — formalizar template.', + 'S', 'alta', 'pendente', 14), +(1, 15, 'Fiscal mínimo', 'NFS-e emissão', + 'Integração Focus NF-e ou NFS-e direto da prefeitura. Pode empurrar pra 1.5 se apertar prazo. Diferencia de Psicomanager.', + 'L', 'alta', 'pendente', 15), + +-- 1.5 Qualidade pra lançar +(1, 16, 'Qualidade', 'Testes E2E dos fluxos críticos', + 'Playwright ou Cypress. Prioridade: cadastro, login, agendamento, cobrança, prontuário.', + 'L', 'alta', 'pendente', 16), +(1, 17, 'Qualidade', 'Responsividade mobile validada', + 'Terapeuta consulta agenda no celular. Auditoria de breakpoints + fix.', + 'M', 'alta', 'pendente', 17), +(1, 18, 'Qualidade', 'Monitoramento de produção', + 'Sentry + Supabase dashboards. Detectar bugs antes do usuário reclamar.', + 'S', 'alta', 'pendente', 18); + +-- ----------------------------------------------------------------------------- +-- FASE 2 — Paridade Competitiva (16 items) +-- ----------------------------------------------------------------------------- + +-- 2.1 Comunicação / Engajamento +INSERT INTO public.dev_roadmap_items (phase_id, numero, bloco, feature, descricao, esforco, prioridade, status, ordem) VALUES +(2, 19, 'Comunicação / Engajamento', 'Agenda diária automática 7h via WhatsApp', + 'Cron job + template. Feature signature do PsicoPlanner.', + 'S', 'alta', 'pendente', 1), +(2, 20, 'Comunicação / Engajamento', 'Confirmação de presença pelo paciente', + 'Parser de resposta WhatsApp/SMS ("SIM") → atualiza status da sessão.', + 'M', 'alta', 'pendente', 2), +(2, 21, 'Comunicação / Engajamento', 'Rastreamento de engajamento em tempo real', + 'Webhooks da Evolution API → dashboard (recebeu/leu/respondeu).', + 'M', 'alta', 'pendente', 3), +(2, 22, 'Comunicação / Engajamento', 'Envio de prescrição/documento via WhatsApp direto', + 'Botão "Enviar via WA" gerando link temporário pro documento.', + 'S', 'alta', 'pendente', 4), + +-- 2.2 Prontuário +(2, 23, 'Prontuário', 'Templates de nota (SOAP / DAP / BIRP / CFP)', + 'Biblioteca com templates selecionáveis ao criar evolução.', + 'M', 'alta', 'pendente', 5), +(2, 24, 'Prontuário', 'Biblioteca de instrumentos de avaliação', + 'GAD-7, PHQ-9, BDI, BAI, DASS-21, SRQ-20 com scoring automático + gráfico de evolução.', + 'L', 'alta', 'pendente', 6), +(2, 25, 'Prontuário', 'Histórico em gráfico/tabela', + 'Evolução de escalas ao longo do tempo. Chart.js já no projeto.', + 'M', 'alta', 'pendente', 7), +(2, 26, 'Prontuário', 'Versionamento de notas', + 'Auditoria de alterações + diff visual.', + 'M', 'media', 'pendente', 8), + +-- 2.3 Intake / Onboarding +(2, 27, 'Intake / Onboarding', 'Pacote de intake pré-1ª-sessão', + 'Formulários + anamnese + consent forms enviados ao paciente antes da 1ª sessão. Fluxo: terapeuta monta pacote → paciente recebe link → preenche → assina → terapeuta vê tudo.', + 'M', 'media', 'pendente', 9), +(2, 28, 'Intake / Onboarding', 'Upload de arquivo pelo paciente', + 'Exames, relatórios externos. Storage bucket já configurado.', + 'S', 'media', 'pendente', 10), + +-- 2.4 Agenda / Integrações +(2, 29, 'Agenda / Integrações', 'Google Calendar 2-way sync', + 'API OAuth + conflict resolution. Feature mais pedida.', + 'L', 'media', 'pendente', 11), +(2, 30, 'Agenda / Integrações', 'iCal feed (Apple Calendar / Outlook)', + 'Endpoint que serve .ics. Read-only.', + 'S', 'media', 'pendente', 12), + +-- 2.5 Fiscal avançado BR +(2, 31, 'Fiscal avançado BR', 'Assinatura digital ICP-Brasil', + 'Laudos com validade jurídica. Integração com ValidCertificadora ou similar.', + 'L', 'media', 'pendente', 13), +(2, 32, 'Fiscal avançado BR', 'Faturamento TISS básico', + 'Pra clínicas que têm convênio. Nichado — só se tier clínica/rede for prioridade. Recomendado NÃO fazer nesta fase.', + 'XL', 'media', 'pendente', 14), + +-- 2.6 Marketing / Presença +(2, 33, 'Marketing / Presença', 'Perfil público do terapeuta', + 'Página /p/ com bio, horários, agendamento. Já tem agendador público — só enriquecer.', + 'M', 'media', 'pendente', 15), +(2, 34, 'Marketing / Presença', 'SEO básico', + 'schema.org/MedicalBusiness + meta tags. Melhora descoberta orgânica.', + 'S', 'media', 'pendente', 16); + +-- ----------------------------------------------------------------------------- +-- FASE 3 — Diferenciação IA-first (14 items) +-- ----------------------------------------------------------------------------- + +-- 3.1 IA +INSERT INTO public.dev_roadmap_items (phase_id, numero, bloco, feature, descricao, esforco, prioridade, status, ordem) VALUES +(3, 35, 'IA', 'Bot WhatsApp que agenda sozinho', + 'Equivalente Amélia Agendamento da Amplimed. Stack: Evolution API + LLM + RAG na disponibilidade da agenda. ROI: 24/7 sem recepcionista.', + 'XL', 'diferencial', 'pendente', 1), +(3, 36, 'IA', 'Transcrição de sessão áudio→texto', + 'Equivalente AI Scribe do Jane / Amélia Transcrição. Whisper API ou Deepgram. Paciente consente. Transcrição vira rascunho de nota.', + 'L', 'diferencial', 'pendente', 2), +(3, 37, 'IA', 'Copilot no prontuário', + 'Resumir histórico, sugerir diagnóstico diferencial, busca semântica em notas anteriores. RAG em cima das notas do paciente.', + 'L', 'diferencial', 'pendente', 3), +(3, 38, 'IA', 'Gerador de documentos com compliance CFP', + 'Equivalente PsiAssist do PsicoPlanner. LLM com system prompt CFP + templates.', + 'M', 'diferencial', 'pendente', 4), + +-- 3.2 Teleconsulta nativa +(3, 39, 'Teleconsulta', 'Vídeo nativo integrado', + 'Daily.co ou Jitsi Meet. Sala gerada por consulta + link no lembrete.', + 'L', 'diferencial', 'pendente', 5), +(3, 40, 'Teleconsulta', 'Sala de espera virtual', + 'Profissional admite paciente quando estiver pronto.', + 'M', 'diferencial', 'pendente', 6), +(3, 41, 'Teleconsulta', 'Whiteboard digital + screen share', + 'Daily.co tem nativo. Útil pra TCC visual e crianças.', + 'M', 'diferencial', 'pendente', 7), + +-- 3.3 Rede / Multi-unidade +(3, 42, 'Rede / Multi-unidade', 'Multi-unidade / filiais com Main Office', + 'Tabela clinic_units + FK em consultas/profissionais. Só se posicionar pra tier enterprise.', + 'L', 'diferencial', 'pendente', 8), +(3, 43, 'Rede / Multi-unidade', 'Salas e equipamentos como recursos', + 'Estilo Jane App. Evita double-booking.', + 'M', 'diferencial', 'pendente', 9), +(3, 44, 'Rede / Multi-unidade', 'CRM de leads', + 'Captura de landing → funil → matching com terapeuta. Aproveita perfil público da Fase 2.', + 'L', 'diferencial', 'pendente', 10), +(3, 45, 'Rede / Multi-unidade', 'BI avançado', + 'MRR, cohort retention, LTV por terapeuta. Dashboards dedicados.', + 'M', 'diferencial', 'pendente', 11), + +-- 3.4 UX premium / diferenciação fina +(3, 46, 'UX premium', 'Website builder pra clínica', + 'Estilo Jane — sem precisar de Wix/WordPress. Grande mas ROI de marketing enorme.', + 'XL', 'diferencial', 'pendente', 12), +(3, 47, 'UX premium', 'App mobile (PWA otimizado)', + 'Paciente instala no celular. Vue já dá PWA grátis, só polir.', + 'M', 'diferencial', 'pendente', 13), +(3, 48, 'UX premium', 'Migração assistida de Psicomanager', + 'Importador de CSV/API. Feature de venda: "deixa o outro, a gente migra".', + 'M', 'diferencial', 'pendente', 14); + +COMMIT; + +-- Log +INSERT INTO public.dev_generation_log (tipo, comando, sucesso, metadata) +VALUES ('seed', 'seed_030_dev_phases_items.sql', true, + jsonb_build_object('phases', 3, 'items', 48, 'source', 'development/04-roadmap/ROADMAP.md')); diff --git a/database-novo/seeds/seed_031_dev_auditoria.sql b/database-novo/seeds/seed_031_dev_auditoria.sql new file mode 100644 index 0000000..fec2129 --- /dev/null +++ b/database-novo/seeds/seed_031_dev_auditoria.sql @@ -0,0 +1,142 @@ +-- ============================================================================= +-- Seed 031 — dev_auditoria_items +-- Importa bugs/débitos técnicos do development/02-auditoria/AUDITORIA.md +-- ============================================================================= +-- ATENÇÃO: TRUNCATE RESTART IDENTITY — re-rodar apaga edições na UI. +-- ============================================================================= + +BEGIN; + +TRUNCATE TABLE public.dev_auditoria_items RESTART IDENTITY CASCADE; + +-- ----------------------------------------------------------------------------- +-- Bugs e dívidas técnicas identificadas em 2026-03-11 (Sessões 1-4) +-- Fonte: AUDITORIA.md +-- ----------------------------------------------------------------------------- + +INSERT INTO public.dev_auditoria_items + (categoria, titulo, descricao_problema, solucao, severidade, status, resolvido_em, sessao_resolucao, arquivo_afetado, tags) +VALUES + +-- Bugs Críticos +('Bug crítico', + 'useRecurrence.js — variável occurrenceCount não declarada', + 'Branches custom_weekdays, monthly e yearly usavam occurrenceCount sem declará-la → ReferenceError em runtime. Nenhum dos três contava ocorrências anteriores ao range, então max_occurrences nunca funcionava corretamente.', + 'Cada branch ganhou let occurrenceCount = 0 + fase de pré-contagem de ruleStart até effStart.', + 'critico', 'resolvido', '2026-03-11', 'Sessão 2 — 2026-03-11', + 'src/features/agenda/composables/useRecurrence.js', + ARRAY['agenda','recorrencia','runtime-error']), + +('Bug crítico', + 'Exceção de remarcação fora do range não aparece', + 'loadExceptions só buscava original_date no range. Se original_date estivesse fora mas new_date caísse dentro, a sessão remarcada não aparecia.', + 'loadExceptions: duas queries paralelas — q1 (original_date no range) + q2 (reschedule com new_date no range). Mescladas e deduplicadas por id. expandRules post-pass: itera exceções não consumidas, injeta inbound reschedules.', + 'alto', 'resolvido', '2026-03-11', 'Sessão 3 — 2026-03-11', + 'src/features/agenda/composables/useRecurrence.js', + ARRAY['agenda','recorrencia','edge-case']), + +-- Segurança +('Segurança', + 'SQL dumps no repositório', + 'Dumps com dados sensíveis versionados no git.', + 'Removidos do tracking + adicionados ao .gitignore. Backups ficam em database-novo/backups/ (também gitignored).', + 'critico', 'resolvido', '2026-03-11', 'Sessão 1 — 2026-03-11', + '.gitignore', + ARRAY['segurança','git','lgpd']), + +('Segurança', + 'useAgendaEvents — sem tenant_id em nenhuma operação', + 'Todas as operações CRUD de eventos faltavam filtro tenant_id → risco de vazamento cross-tenant.', + 'Adicionado filtro tenant_id em select/insert/update/delete + validação na composable.', + 'critico', 'resolvido', '2026-03-11', 'Sessão 1 — 2026-03-11', + 'src/features/agenda/composables/useAgendaEvents.js', + ARRAY['multi-tenant','rls','segurança']), + +('Segurança', + 'loadRules em useRecurrence sem filtro tenant_id', + 'Regras de recorrência carregavam sem filtrar por tenant → possível leak.', + 'Filtro tenant_id adicionado em loadRules.', + 'critico', 'resolvido', '2026-03-11', 'Sessão 1 — 2026-03-11', + 'src/features/agenda/composables/useRecurrence.js', + ARRAY['multi-tenant','segurança']), + +('Segurança', + 'console.log expõe dados de pacientes no browser', + 'Logs com PII (nome, CPF, email, telefone) sendo enviados ao console.', + 'Removidos console.log sensíveis. Logs restantes filtrados via wrapper que só loga em dev mode.', + 'alto', 'resolvido', '2026-03-11', 'Sessão 1 — 2026-03-11', + 'multiple', + ARRAY['segurança','lgpd','logs']), + +-- Arquitetura / Performance +('Arquitetura', + 'window.__guardsBound / window.__supabaseAuthListenerBound', + 'Uso de flags globais no window para evitar bind duplicado de listeners. Anti-pattern — vazamento de escopo + difícil debug.', + NULL, + 'medio', 'aberto', NULL, NULL, + 'src/router/guards.js', + ARRAY['arquitetura','global-state','refactor']), + +('Arquitetura', + 'globalRole do profiles sem cache no guard', + 'Guard fazia fetch do profile a cada navegação — N queries desnecessárias.', + 'Cache de globalRole no store com invalidation via Supabase auth state changes.', + 'medio', 'resolvido', '2026-03-11', 'Sessão 2 — 2026-03-11', + 'src/router/guards.js', + ARRAY['performance','cache']), + +('Arquitetura', + 'Dois composables para a mesma entidade', + 'Duplicação: useAgendaEvents e outro composable fazendo a mesma coisa.', + 'Consolidado em um único useAgendaEvents. Outro removido.', + 'medio', 'resolvido', '2026-03-11', 'Sessão 2 — 2026-03-11', + 'src/features/agenda/composables/', + ARRAY['refactor','duplicação']), + +('Arquitetura', + 'Dois mappers para agenda', + 'Duplicação: agendaMapper e outro mapper convertendo mesma estrutura.', + 'Unificados em um único agendaMapper canônico.', + 'medio', 'resolvido', '2026-03-11', 'Sessão 2 — 2026-03-11', + 'src/features/agenda/mappers/', + ARRAY['refactor','duplicação']), + +('Performance', + 'N+1 Query — migração paciente_id → patient_id', + 'Queries em N+1 pattern durante transição de naming. Cada agenda_evento fazia query separada pra paciente.', + 'Migrados todos os queries pra usar JOIN em patient_id. Migration de schema unificou naming.', + 'alto', 'resolvido', '2026-03-11', 'Sessão 4 — 2026-03-11', + 'multiple', + ARRAY['performance','query','n+1']), + +-- Build / Produção +('Build', + 'Template Sakai removido — bundle de produção', + 'Bundle de produção carregando código do template Sakai que não era usado. Peso desnecessário.', + 'Cleanup do template, tree-shake manual dos componentes não usados.', + 'medio', 'resolvido', '2026-03-11', 'Sessão 2 — 2026-03-11', + 'vite.config.mjs', + ARRAY['build','bundle-size']), + +('Dívida técnica', + 'Arquivos obsoletos no projeto', + 'Vários arquivos .vue/.js deprecated ou não importados ainda no repo, confundindo navegação.', + 'Parcial — alguns removidos, outros ainda a mapear.', + 'baixo', 'em_analise', NULL, NULL, + 'multiple', + ARRAY['cleanup','dívida-técnica']), + +('Produção', + 'Logs excessivos em produção', + 'Muitos console.log/console.trace rodando em prod degradando performance.', + 'Removidos console.trace em router.push e queries Supabase. Logs restantes condicionais a DEV mode.', + 'medio', 'resolvido', '2026-03-11', 'Sessão 4 — 2026-03-11', + 'multiple', + ARRAY['performance','logs','produção']); + +COMMIT; + +-- Log +INSERT INTO public.dev_generation_log (tipo, comando, sucesso, metadata) +VALUES ('seed', 'seed_031_dev_auditoria.sql', true, + jsonb_build_object('items', 14, 'abertos', 2, 'resolvidos', 12, 'source', 'development/02-auditoria/AUDITORIA.md')); diff --git a/database-novo/seeds/seed_032_dev_competitors.sql b/database-novo/seeds/seed_032_dev_competitors.sql new file mode 100644 index 0000000..1b0cf55 --- /dev/null +++ b/database-novo/seeds/seed_032_dev_competitors.sql @@ -0,0 +1,271 @@ +-- ============================================================================= +-- Seed 032 — dev_competitors + dev_competitor_features + dev_comparison_matrix +-- Importa benchmark do development/03-concorrentes/concorrentes.md +-- ============================================================================= +-- ATENÇÃO: TRUNCATE RESTART IDENTITY CASCADE — re-rodar apaga edições na UI. +-- ============================================================================= + +BEGIN; + +TRUNCATE TABLE public.dev_comparison_competitor_status RESTART IDENTITY CASCADE; +TRUNCATE TABLE public.dev_comparison_matrix RESTART IDENTITY CASCADE; +TRUNCATE TABLE public.dev_competitor_features RESTART IDENTITY CASCADE; +TRUNCATE TABLE public.dev_competitors RESTART IDENTITY CASCADE; + +-- ============================================================================= +-- 1. dev_competitors — 7 players +-- ============================================================================= +INSERT INTO public.dev_competitors + (slug, nome, pais, foco, pricing, posicionamento, url, ultima_pesquisa, ordem) +VALUES +('simplepractice', 'SimplePractice', 'EUA', + 'Mental health EHR', + 'USD ~29-99/mês por profissional (Essential, Plus). 30-day ou 7-day trial.', + '"Salesforce da saúde mental" — líder EUA com 20M+ clientes e 250k+ practitioners. HIPAA+HITRUST+PCI+BAA.', + 'https://www.simplepractice.com', + '2026-04-17', 1), + +('psicomanager', 'Psicomanager', 'BR', + 'Psicologia-first', + 'R$ ~50-150/mês/profissional (confirmar)', + 'O "padrão" do mercado brasileiro pra psicólogos. Site é SPA — info via exploração manual.', + 'https://psicomanager.com.br', + NULL, 2), + +('psicoplanner', 'PsicoPlanner', 'BR', + 'Psicologia-first', + 'Individual R$ 59/mês · Plus R$ 79 · Duo R$ 99 · Clínicas R$ 395 (até 5 profissionais)', + '"Psicólogo que odeia planilha" — simples, WhatsApp-first, IA nativa (PsiAssist com compliance CFP).', + 'https://psicoplanner.com.br', + '2026-04-17', 3), + +('iclinic', 'iClinic', 'BR', + 'Multispecialidade', + 'A confirmar', + 'EHR completo pro consultório/clínica médica BR. Psicólogos usam bastante apesar de não ser foco.', + 'https://iclinic.com.br', + '2026-04-17', 4), + +('amplimed', 'Amplimed', 'BR', + 'Multispecialidade com IA', + 'A confirmar', + '"IA que trabalha pela sua clínica" — suite Amélia (agendamento 24/7, transcrição, copilot) + TISS automatizado + NFS-e + ICP-Brasil.', + 'https://www.amplimed.com.br', + '2026-04-17', 5), + +('ninsaude', 'Ninsaúde', 'BR', + 'ERP clínico', + 'A confirmar', + 'ERP clínico completo (várias especialidades) — mais robusto/caro. Produtos satélites: CRM, Safe, Sign.', + 'https://ninsaude.com', + '2026-04-17', 6), + +('jane-app', 'Jane App', 'CA', + 'Practice management premium', + 'A confirmar (mercados CA, US, UK)', + '"O app mais amado pelos profissionais de saúde" — referência internacional de UX. AI Scribe + Jane Payments (online+POS físico) + HIPAA/PIPEDA/GDPR/SOC-2.', + 'https://jane.app', + '2026-04-17', 7); + +-- ============================================================================= +-- 2. dev_competitor_features — features por concorrente +-- ============================================================================= + +-- SimplePractice (fetched) +INSERT INTO public.dev_competitor_features (competitor_id, categoria, nome, descricao, fonte, fonte_url, data_fonte, destaque) VALUES +((SELECT id FROM public.dev_competitors WHERE slug='simplepractice'), 'Compliance', 'HIPAA + HITRUST + PCI + BAA', 'Certificações de segurança top-tier', 'fetched', 'https://www.simplepractice.com/features/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='simplepractice'), 'Telehealth', 'Vídeo nativo integrado', 'Launch sessions direto do calendário, sem extra login', 'fetched', 'https://www.simplepractice.com/features/telehealth/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='simplepractice'), 'Telehealth', 'Sala de espera virtual', 'Admit client quando pronto', 'fetched', 'https://www.simplepractice.com/features/telehealth/', '2026-04-17', false), +((SELECT id FROM public.dev_competitors WHERE slug='simplepractice'), 'Telehealth', 'Digital whiteboard', 'Quadro branco colaborativo na sessão', 'fetched', 'https://www.simplepractice.com/features/telehealth/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='simplepractice'), 'Telehealth', 'Video em grupo (até 15)', 'Sessões de grupo/terapia familiar', 'fetched', 'https://www.simplepractice.com/features/telehealth/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='simplepractice'), 'Telehealth', 'Screen sharing + blurred background', 'Compartilhamento de tela e fundo desfocado', 'fetched', 'https://www.simplepractice.com/features/telehealth/', '2026-04-17', false), +((SELECT id FROM public.dev_competitors WHERE slug='simplepractice'), 'Portal Paciente', 'Self-scheduling', 'Paciente solicita/cancela/reagenda', 'fetched', 'https://www.simplepractice.com/features/client-portal/', '2026-04-17', false), +((SELECT id FROM public.dev_competitors WHERE slug='simplepractice'), 'Portal Paciente', 'Paperless intake forms', 'Formulários de entrada preenchidos online', 'fetched', 'https://www.simplepractice.com/features/client-portal/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='simplepractice'), 'Portal Paciente', 'Questionnaires (measures)', 'Instrumentos de medição (GAD-7, PHQ-9, etc)', 'fetched', 'https://www.simplepractice.com/features/client-portal/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='simplepractice'), 'Portal Paciente', 'Portal em espanhol', 'Multi-idioma pro LATAM', 'fetched', 'https://www.simplepractice.com/features/client-portal/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='simplepractice'), 'Portal Paciente', 'Secure messaging', 'Chat seguro terapeuta↔cliente', 'fetched', 'https://www.simplepractice.com/features/client-portal/', '2026-04-17', false), +((SELECT id FROM public.dev_competitors WHERE slug='simplepractice'), 'Portal Paciente', 'Invoice payment pelo portal', 'Paciente paga fatura diretamente', 'fetched', 'https://www.simplepractice.com/features/client-portal/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='simplepractice'), 'Documentação', 'Biblioteca GAD-7/PHQ-9/BDI', 'Instrumentos validados com scoring', 'publico', NULL, NULL, true), +((SELECT id FROM public.dev_competitors WHERE slug='simplepractice'), 'Documentação', 'Consent forms assinados', 'Biblioteca editável + assinatura eletrônica via portal', 'observacao', NULL, '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='simplepractice'), 'Billing', 'AutoPay + Superbills', 'Cobrança automática + recibo detalhado', 'publico', NULL, NULL, false), +((SELECT id FROM public.dev_competitors WHERE slug='simplepractice'), 'Onboarding', 'Credentialing grátis', 'Auxílio com credenciamento junto a seguradoras', 'fetched', 'https://www.simplepractice.com/features/', '2026-04-17', false), +((SELECT id FROM public.dev_competitors WHERE slug='simplepractice'), 'Onboarding', 'Switching Assistance', 'Suporte de migração de outra plataforma', 'fetched', 'https://www.simplepractice.com/features/', '2026-04-17', false); + +-- Psicomanager (publico only - site SPA) +INSERT INTO public.dev_competitor_features (competitor_id, categoria, nome, descricao, fonte, destaque) VALUES +((SELECT id FROM public.dev_competitors WHERE slug='psicomanager'), 'Agenda', 'Agenda online/presencial + recorrências', 'Agenda nativa multi-profissional', 'publico', false), +((SELECT id FROM public.dev_competitors WHERE slug='psicomanager'), 'Prontuário', 'Prontuário eletrônico', 'Anamnese + evolução + sessões', 'publico', false), +((SELECT id FROM public.dev_competitors WHERE slug='psicomanager'), 'Financeiro', 'Cobranças via PIX/cartão', 'Gateway a confirmar (Asaas? Iugu?)', 'publico', false), +((SELECT id FROM public.dev_competitors WHERE slug='psicomanager'), 'Comunicação', 'Lembretes Email/SMS/WhatsApp', 'Sistema de notificações', 'publico', false), +((SELECT id FROM public.dev_competitors WHERE slug='psicomanager'), 'Fiscal', 'Recibo / NFSe', 'Emissão de nota fiscal de serviço', 'publico', true), +((SELECT id FROM public.dev_competitors WHERE slug='psicomanager'), 'Telehealth', 'Teleconsulta integrada', 'Próprio ou via parceiro (confirmar)', 'publico', false), +((SELECT id FROM public.dev_competitors WHERE slug='psicomanager'), 'Portal Paciente', 'Portal do Paciente', 'Visão das sessões/faturas', 'publico', false), +((SELECT id FROM public.dev_competitors WHERE slug='psicomanager'), 'Documentação', 'Documentos / atestados', 'Laudos, declarações', 'publico', false), +((SELECT id FROM public.dev_competitors WHERE slug='psicomanager'), 'Compliance', 'LGPD compliance', 'Consentimento de dados', 'publico', false), +((SELECT id FROM public.dev_competitors WHERE slug='psicomanager'), 'Multi-profissional', 'Gestão de clínica multi-pro', 'Multi-terapeuta na mesma instância', 'publico', false); + +-- PsicoPlanner (fetched) +INSERT INTO public.dev_competitor_features (competitor_id, categoria, nome, descricao, fonte, fonte_url, data_fonte, destaque) VALUES +((SELECT id FROM public.dev_competitors WHERE slug='psicoplanner'), 'Comunicação', 'Lembretes WhatsApp ilimitados', 'Personalizáveis por profissional', 'fetched', 'https://psicoplanner.com.br/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='psicoplanner'), 'Comunicação', 'Agenda diária automática 7h via WhatsApp', 'Push automático do cronograma do dia — diferencial', 'fetched', 'https://psicoplanner.com.br/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='psicoplanner'), 'Comunicação', 'Rastreamento engajamento tempo real', 'Recebeu/leu/respondeu', 'fetched', 'https://psicoplanner.com.br/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='psicoplanner'), 'Telehealth', 'Sala de vídeo integrada', 'Nativa no app', 'fetched', 'https://psicoplanner.com.br/', '2026-04-17', false), +((SELECT id FROM public.dev_competitors WHERE slug='psicoplanner'), 'Prontuário', 'Prontuários + anamnese customizáveis', 'Adaptável à metodologia do profissional', 'fetched', 'https://psicoplanner.com.br/', '2026-04-17', false), +((SELECT id FROM public.dev_competitors WHERE slug='psicoplanner'), 'Agenda', 'Autoagendamento por link', 'Paciente escolhe horário', 'fetched', 'https://psicoplanner.com.br/', '2026-04-17', false), +((SELECT id FROM public.dev_competitors WHERE slug='psicoplanner'), 'Financeiro', 'Gestão financeira visual', 'Pagamentos, recebidos, pendentes', 'fetched', 'https://psicoplanner.com.br/', '2026-04-17', false), +((SELECT id FROM public.dev_competitors WHERE slug='psicoplanner'), 'IA', 'PsiAssist AI com compliance CFP', 'Gera relatórios/documentos/resumos com regras CFP', 'fetched', 'https://psicoplanner.com.br/', '2026-04-17', true); + +-- iClinic (fetched) +INSERT INTO public.dev_competitor_features (competitor_id, categoria, nome, descricao, fonte, fonte_url, data_fonte, destaque) VALUES +((SELECT id FROM public.dev_competitors WHERE slug='iclinic'), 'Prontuário', 'Histórico de valores (gráfico/tabela)', 'Evolução de dados do paciente', 'fetched', 'https://iclinic.com.br/funcionalidades/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='iclinic'), 'Prontuário', 'Campos personalizados por especialidade', 'Adaptável a qualquer medicina', 'fetched', 'https://iclinic.com.br/funcionalidades/', '2026-04-17', false), +((SELECT id FROM public.dev_competitors WHERE slug='iclinic'), 'Prontuário', 'Assinatura digital com validade jurídica', 'Documentação assinada juridicamente', 'fetched', 'https://iclinic.com.br/funcionalidades/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='iclinic'), 'Prontuário', 'CID-10 integrado', 'Consulta de códigos internacionais', 'fetched', 'https://iclinic.com.br/funcionalidades/', '2026-04-17', false), +((SELECT id FROM public.dev_competitors WHERE slug='iclinic'), 'Prescrição', 'Prescrição eletrônica com envio WhatsApp', 'Base de medicamentos + envio direto', 'fetched', 'https://iclinic.com.br/funcionalidades/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='iclinic'), 'Documentos', 'Modelos com campos automáticos', 'Preenchimento automático de dados do paciente', 'fetched', 'https://iclinic.com.br/funcionalidades/', '2026-04-17', false), +((SELECT id FROM public.dev_competitors WHERE slug='iclinic'), 'Marketing', 'Marketing Médico (email campaigns)', 'Otimização de experiência do paciente', 'fetched', 'https://iclinic.com.br/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='iclinic'), 'Operação', 'Módulo Recepcionista', 'Fluxo de atendimento', 'fetched', 'https://iclinic.com.br/', '2026-04-17', false); + +-- Amplimed (fetched) — AI-heavy +INSERT INTO public.dev_competitor_features (competitor_id, categoria, nome, descricao, fonte, fonte_url, data_fonte, destaque) VALUES +((SELECT id FROM public.dev_competitors WHERE slug='amplimed'), 'IA', 'Amélia Agendamento (bot WhatsApp 24/7)', 'Atende e responde pacientes via WhatsApp com sincronização automática da agenda', 'fetched', 'https://www.amplimed.com.br/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='amplimed'), 'IA', 'Amélia Transcrição (áudio→texto)', 'Converte áudio em texto e preenche prontuário automaticamente', 'fetched', 'https://www.amplimed.com.br/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='amplimed'), 'IA', 'Amélia Copilot (prontuário)', 'Localiza, resume e organiza info do paciente', 'fetched', 'https://www.amplimed.com.br/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='amplimed'), 'Agenda', 'Agenda inteligente (redução 38% ausências)', 'ML pra otimização', 'fetched', 'https://www.amplimed.com.br/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='amplimed'), 'Fiscal', 'Faturamento TISS (99% menos glosas)', 'Padrão automático de validação', 'fetched', 'https://www.amplimed.com.br/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='amplimed'), 'Fiscal', 'NFS-e emissão integrada', 'Simplificada ao faturamento', 'fetched', 'https://www.amplimed.com.br/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='amplimed'), 'Fiscal', 'Certificado Digital ICP-Brasil', 'Assinatura com validade jurídica', 'fetched', 'https://www.amplimed.com.br/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='amplimed'), 'Telehealth', 'Teleconsulta E2E + prescrição digital', 'Criptografia ponta a ponta', 'fetched', 'https://www.amplimed.com.br/', '2026-04-17', false), +((SELECT id FROM public.dev_competitors WHERE slug='amplimed'), 'Comunicação', 'WhatsApp Connect + SMS', 'Mensagens personalizadas automáticas', 'fetched', 'https://www.amplimed.com.br/', '2026-04-17', false), +((SELECT id FROM public.dev_competitors WHERE slug='amplimed'), 'Operação', 'Painel de chamados (TV recepção)', 'Fluxo organizado com exibição pública', 'fetched', 'https://www.amplimed.com.br/', '2026-04-17', false); + +-- Ninsaúde (fetched) +INSERT INTO public.dev_competitor_features (competitor_id, categoria, nome, descricao, fonte, fonte_url, data_fonte, destaque) VALUES +((SELECT id FROM public.dev_competitors WHERE slug='ninsaude'), 'Prontuário', 'Prontuário Eletrônico', 'Registro digital estruturado', 'fetched', 'https://ninsaude.com/', '2026-04-17', false), +((SELECT id FROM public.dev_competitors WHERE slug='ninsaude'), 'Financeiro', 'Faturamento de Convênios', 'Integração com planos de saúde', 'fetched', 'https://ninsaude.com/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='ninsaude'), 'Operação', 'Controle de Estoque', 'Medicamentos e materiais', 'fetched', 'https://ninsaude.com/', '2026-04-17', false), +((SELECT id FROM public.dev_competitors WHERE slug='ninsaude'), 'CRM', 'Ninsaúde CRM', 'Gestão de leads e funil', 'fetched', 'https://ninsaude.com/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='ninsaude'), 'Segurança', 'Ninsaúde Safe', 'Produto satélite de segurança', 'fetched', 'https://ninsaude.com/', '2026-04-17', false), +((SELECT id FROM public.dev_competitors WHERE slug='ninsaude'), 'Documentos', 'Ninsaúde Sign', 'Assinatura digital', 'fetched', 'https://ninsaude.com/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='ninsaude'), 'Cadastro', 'Nome Social', 'Respeita identidade de gênero', 'fetched', 'https://ninsaude.com/', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='ninsaude'), 'BI', 'Análise Inteligente', 'Relatórios e insights da operação', 'fetched', 'https://ninsaude.com/', '2026-04-17', false), +((SELECT id FROM public.dev_competitors WHERE slug='ninsaude'), 'Retenção', 'Retenção de Pacientes automatizada', 'Estratégias automatizadas', 'fetched', 'https://ninsaude.com/', '2026-04-17', false); + +-- Jane App (fetched) +INSERT INTO public.dev_competitor_features (competitor_id, categoria, nome, descricao, fonte, fonte_url, data_fonte, destaque) VALUES +((SELECT id FROM public.dev_competitors WHERE slug='jane-app'), 'Agenda', 'Online Booking com branding', 'Site visita → consulta marcada 24/7', 'fetched', 'https://jane.app/features', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='jane-app'), 'Agenda', 'Staff Scheduling multi-location', 'Serviços, salas, recursos, waitlist na mesma visão', 'fetched', 'https://jane.app/features', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='jane-app'), 'Documentação', 'Template library (SOAP, forms, surveys)', 'Customizável por tipo de clínica', 'fetched', 'https://jane.app/features', '2026-04-17', false), +((SELECT id FROM public.dev_competitors WHERE slug='jane-app'), 'IA', 'AI Scribe (grava e gera nota)', 'Record/dictate e rascunho em minutos', 'fetched', 'https://jane.app/features', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='jane-app'), 'Telehealth', 'Telehealth até 12 clientes', 'Video HIPAA-compliant', 'fetched', 'https://jane.app/features', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='jane-app'), 'Pagamentos', 'Jane Payments (online + terminal POS)', 'PCI-compliant', 'fetched', 'https://jane.app/features', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='jane-app'), 'Fiscal', 'Insurance eligibility + claims (CA/US/UK)', 'Multi-região', 'fetched', 'https://jane.app/features', '2026-04-17', false), +((SELECT id FROM public.dev_competitors WHERE slug='jane-app'), 'BI', 'Real-time dashboards', 'Métricas de saúde do negócio', 'fetched', 'https://jane.app/features', '2026-04-17', false), +((SELECT id FROM public.dev_competitors WHERE slug='jane-app'), 'Compliance', 'HIPAA + PIPEDA + GDPR + SOC-2', 'Certificações multi-região', 'fetched', 'https://jane.app/features', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='jane-app'), 'Marketing', 'Website Builder com IA', 'Cria site da clínica auto-sincronizado', 'fetched', 'https://jane.app/features', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='jane-app'), 'Onboarding', 'Migração de dados grátis', 'Feature de venda', 'fetched', 'https://jane.app/features', '2026-04-17', true), +((SELECT id FROM public.dev_competitors WHERE slug='jane-app'), 'Suporte', 'Suporte ilimitado phone/email/chat', 'Premium', 'fetched', 'https://jane.app/features', '2026-04-17', false); + +-- ============================================================================= +-- 3. dev_comparison_matrix — AgenciaPsi × features esperadas do mercado +-- ============================================================================= +INSERT INTO public.dev_comparison_matrix (dominio, feature, nosso_status, nossa_nota, importancia, ordem) VALUES +-- Cadastro/Clientes +('Pacientes', 'Cadastro de pacientes', 'tem', 'Completo', 'alta', 1), +('Pacientes', 'Grupos / Tags', 'tem', NULL, 'media', 2), +('Pacientes', 'Busca global no topbar', 'gap', 'Todos os concorrentes têm — quick win', 'alta', 3), +('Pacientes', 'Recently viewed (últimos acessados)', 'gap', 'Quick win de UX', 'media', 4), +('Pacientes', 'Merge de duplicatas', 'gap', NULL, 'baixa', 5), +('Pacientes', 'Nome social', 'a_definir', 'Validar se já temos — CFP/LGPD exige', 'alta', 6), + +-- Agenda +('Agenda', 'Agenda / Calendário', 'tem', 'FullCalendar completo', 'alta', 10), +('Agenda', 'Recorrências', 'tem', 'useRecurrence composable', 'alta', 11), +('Agenda', 'Agendamento online público', 'tem', NULL, 'alta', 12), +('Agenda', 'Google Calendar 2-way sync', 'gap', 'Fase 2', 'alta', 13), +('Agenda', 'iCal feed', 'gap', 'Fase 2 - quick win', 'media', 14), +('Agenda', 'Agenda diária 7h WhatsApp automática', 'gap', 'Diferencial PsicoPlanner - Fase 2', 'alta', 15), + +-- Teleconsulta +('Teleconsulta', 'Vídeo nativo integrado', 'gap', 'Fase 3 - aposta diferenciação', 'alta', 20), +('Teleconsulta', 'Sala de espera virtual', 'gap', 'Fase 3', 'media', 21), +('Teleconsulta', 'Screen sharing + whiteboard', 'gap', 'SimplePractice benchmark', 'media', 22), +('Teleconsulta', 'Video em grupo (5+ pessoas)', 'gap', 'SP faz até 15, Jane até 12', 'media', 23), + +-- Prontuário +('Prontuário', 'Prontuário eletrônico', 'tem', 'Completo', 'alta', 30), +('Prontuário', 'Templates de nota (SOAP/DAP/BIRP)', 'a_definir', 'Validar - Fase 2', 'alta', 31), +('Prontuário', 'Versionamento de notas', 'a_definir', 'Validar - Fase 2', 'media', 32), +('Prontuário', 'Biblioteca de avaliações (GAD-7/PHQ-9)', 'gap', 'Diferencial forte BR - Fase 2', 'alta', 33), +('Prontuário', 'Histórico em gráfico (evolução)', 'a_definir', 'Validar - Fase 2', 'media', 34), + +-- Compliance / Legal +('Compliance', 'Consent forms editáveis (TCLE etc)', 'gap', 'Bloqueador MVP - Fase 1', 'alta', 40), +('Compliance', 'Assinatura eletrônica paciente', 'gap', 'Fase 1', 'alta', 41), +('Compliance', 'Assinatura digital ICP-Brasil', 'gap', 'Fase 2', 'media', 42), +('Compliance', 'Papel timbrado', 'gap', 'Portar do UniaoApp - Fase 1', 'alta', 43), +('Compliance', 'Tipo de registro (CRP/CRM)', 'gap', 'Bloqueador MVP - Fase 1', 'alta', 44), +('Compliance', 'Especialidades no cadastro', 'gap', 'Bloqueador MVP - Fase 1', 'alta', 45), + +-- Intake / Onboarding +('Intake', 'Pacote de intake pré-1ª-sessão', 'parcial', 'Documentos existem, pacote estruturado não', 'media', 50), +('Intake', 'Upload de arquivo pelo paciente', 'a_definir', 'Validar no portal', 'media', 51), + +-- Financeiro +('Financeiro', 'Lançamentos financeiros', 'tem', 'Completo', 'alta', 60), +('Financeiro', 'Gateway de pagamento (Stripe/PIX)', 'gap', 'BLOQUEADOR MVP - Fase 1', 'alta', 61), +('Financeiro', 'Cartão on file', 'gap', 'Bloqueador MVP - Fase 1', 'alta', 62), +('Financeiro', 'Auto-billing recorrente', 'parcial', 'Recorrência de consulta sim, cobrança não', 'alta', 63), +('Financeiro', 'Superbill / recibo detalhado', 'parcial', 'Recibo existe, formato detalhado a validar', 'media', 64), +('Financeiro', 'NFS-e emissão', 'gap', 'Fase 1 (preferível) ou 2', 'alta', 65), +('Financeiro', 'Faturamento TISS', 'gap', 'Nichado - Fase 2+ se for enterprise', 'baixa', 66), + +-- Comunicação +('Comunicação', 'Lembretes Email/SMS/WhatsApp', 'tem', 'Completo', 'alta', 70), +('Comunicação', 'Confirmação do paciente ("SIM")', 'a_definir', 'Validar no sistema - Fase 2', 'media', 71), +('Comunicação', 'Rastreamento engajamento tempo real', 'gap', 'Diferencial PsicoPlanner - Fase 2', 'media', 72), + +-- Portal Paciente +('Portal Paciente', 'Portal do paciente autenticado', 'parcial', 'Existe mas limitado - expandir Fase 2', 'alta', 80), +('Portal Paciente', 'Self-scheduling no portal', 'parcial', 'Agendador público existe, portal autenticado não', 'media', 81), +('Portal Paciente', 'Push notifications (portal)', 'gap', NULL, 'baixa', 82), +('Portal Paciente', 'Portal multi-idioma (ES)', 'gap', 'Pensar pra LATAM', 'baixa', 83), +('Portal Paciente', 'Paciente paga fatura no portal', 'gap', 'Depende do gateway (Fase 1)', 'alta', 84), +('Portal Paciente', 'App mobile paciente', 'gap', 'PWA pode resolver - Fase 3', 'media', 85), + +-- Analytics +('Analytics', 'Dashboard com KPIs', 'tem', 'Existe mas pode ampliar', 'alta', 90), +('Analytics', 'Relatórios com export PDF/Excel', 'parcial', 'Estrutura existe, fechar na Fase 1', 'alta', 91), +('Analytics', 'BI avançado (MRR/cohort/LTV)', 'gap', 'Fase 3', 'baixa', 92), + +-- Supervisão +('Supervisão', 'Sala de Supervisão', 'parcial', 'Estrutura existe, features avançadas não', 'media', 100), +('Supervisão', 'Co-assinatura de supervisor em notas', 'gap', 'Fase 2+', 'media', 101), + +-- Infra / Multi-tenant +('Infra', 'Multi-tenant SaaS', 'tem', 'RLS por tenant_id em todas tabelas', 'alta', 110), +('Infra', 'Multi-unidade / filiais', 'gap', 'Fase 3 se for enterprise', 'baixa', 111), +('Infra', 'Compliance LGPD', 'parcial', 'RLS + logs, faltam políticas formais', 'alta', 112), + +-- Marketing +('Marketing', 'Perfil público do terapeuta', 'gap', 'Fase 2', 'media', 120), +('Marketing', 'SEO básico', 'gap', 'Fase 2', 'baixa', 121), +('Marketing', 'Website builder', 'gap', 'Fase 3 - Jane benchmark', 'baixa', 122), + +-- IA (tendência 2026) +('IA', 'Bot WhatsApp que agenda sozinho', 'gap', 'Diferencial Fase 3 - Amplimed benchmark', 'alta', 130), +('IA', 'Transcrição áudio→texto', 'gap', 'Diferencial Fase 3 - Jane/Amplimed', 'alta', 131), +('IA', 'Copilot no prontuário', 'gap', 'Diferencial Fase 3 - Amplimed', 'alta', 132), +('IA', 'Gerador de documentos (compliance CFP)', 'gap', 'Diferencial Fase 3 - PsicoPlanner benchmark', 'alta', 133); + +COMMIT; + +-- Log +INSERT INTO public.dev_generation_log (tipo, comando, sucesso, metadata) +VALUES ('seed', 'seed_032_dev_competitors.sql', true, + jsonb_build_object( + 'competitors', 7, + 'features', (SELECT count(*) FROM public.dev_competitor_features), + 'comparison_rows', (SELECT count(*) FROM public.dev_comparison_matrix), + 'source', 'development/03-concorrentes/concorrentes.md' + )); diff --git a/database-novo/supabase/backup_full.sql b/database-novo/supabase/backup_full.sql new file mode 100644 index 0000000..2f52a48 Binary files /dev/null and b/database-novo/supabase/backup_full.sql differ diff --git a/database-novo/supabase/backup_postgres.sql b/database-novo/supabase/backup_postgres.sql new file mode 100644 index 0000000..f429811 Binary files /dev/null and b/database-novo/supabase/backup_postgres.sql differ diff --git a/database-novo/supabase/backup_volume_agenciapsi.tar.gz b/database-novo/supabase/backup_volume_agenciapsi.tar.gz new file mode 100644 index 0000000..c3dadfe Binary files /dev/null and b/database-novo/supabase/backup_volume_agenciapsi.tar.gz differ diff --git a/database-novo/tests/run.cjs b/database-novo/tests/run.cjs new file mode 100644 index 0000000..0cae444 --- /dev/null +++ b/database-novo/tests/run.cjs @@ -0,0 +1,405 @@ +#!/usr/bin/env node +// ============================================================================= +// AgenciaPsi — RPC integration tests (T#8) +// ============================================================================= +// Executa cenários SQL via `docker exec` no container do Postgres. +// Cada cenário roda em transação isolada (BEGIN ... ROLLBACK), zero side effect. +// +// Uso: node database-novo/tests/run.cjs +// +// Estrutura de cada caso: +// { name, sub: 'as user|as saas|anon', sql, expect: { ok|errorIncludes, jsonHas } } +// ============================================================================= + +const { execSync } = require('child_process'); +const path = require('path'); + +const CONFIG = JSON.parse(require('fs').readFileSync(path.join(__dirname, '..', 'db.config.json'), 'utf8')); +const CONTAINER = CONFIG.container; +const DB = CONFIG.database; +const USER = CONFIG.user; + +// ───────────────────────────────────────────────────────────────────────── +// Helpers +// ----------------------------------------------------------------------------- +function runSql(sql) { + const cmd = `docker exec -i -e PGCLIENTENCODING=UTF8 ${CONTAINER} psql -U ${USER} -d ${DB} -v ON_ERROR_STOP=1 -t -A`; + try { + const out = execSync(cmd, { input: sql, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }); + return { ok: true, out: String(out).trim(), err: null }; + } catch (e) { + const stderr = String(e?.stderr || e?.message || '').trim(); + return { ok: false, out: null, err: stderr }; + } +} + +function asUser(uid) { + return `SET LOCAL ROLE authenticated; SELECT set_config('request.jwt.claim.sub', '${uid}', true);`; +} + +// IDs do seed que sabemos existir (db.config.json + seeds) +const SAAS_ADMIN_UID = 'aaaaaaaa-0006-0006-0006-000000000006'; +const TENANT_ADMIN_UID = 'aaaaaaaa-0005-0005-0005-000000000005'; // owner de Clínica Bem Estar +const TENANT_BEM_ESTAR = 'bbbbbbbb-0005-0005-0005-000000000005'; +const PATIENT_UID = 'aaaaaaaa-0001-0001-0001-000000000001'; + +// ───────────────────────────────────────────────────────────────────────── +// Cases +// ----------------------------------------------------------------------------- +const cases = [ + // ────── set_tenant_feature_exception ────── + { + name: 'set_tenant_feature_exception: anônimo é rejeitado', + sql: `BEGIN; + SET LOCAL ROLE authenticated; + SELECT public.set_tenant_feature_exception('${TENANT_BEM_ESTAR}'::uuid, 'patients', true, NULL); + ROLLBACK;`, + expect: { errorIncludes: 'Não autenticado' } + }, + { + name: 'set_tenant_feature_exception: tenant_admin tenta override+ fora do plano → rejeitado', + sql: `BEGIN; + ${asUser(TENANT_ADMIN_UID)} + SELECT public.set_tenant_feature_exception('${TENANT_BEM_ESTAR}'::uuid, 'documents.signatures', true, 'tentativa'); + ROLLBACK;`, + expect: { errorIncludes: 'saas_admin' } + }, + { + name: 'set_tenant_feature_exception: saas_admin sem reason em exceção → rejeitado', + sql: `BEGIN; + ${asUser(SAAS_ADMIN_UID)} + SELECT public.set_tenant_feature_exception('${TENANT_BEM_ESTAR}'::uuid, 'documents.signatures', true, NULL); + ROLLBACK;`, + expect: { errorIncludes: 'reason' } + }, + { + name: 'set_tenant_feature_exception: saas_admin com reason curto (<4 chars) — RPC aceita, frontend valida', + sql: `BEGIN; + ${asUser(SAAS_ADMIN_UID)} + SELECT (public.set_tenant_feature_exception('${TENANT_BEM_ESTAR}'::uuid, 'documents.signatures', true, 'PIX'))->>'is_exception'; + ROLLBACK;`, + expect: { ok: true, jsonHas: 'true' } // is_exception=true + }, + { + name: 'set_tenant_feature_exception: tenant_admin desliga feature DO plano → permitido', + sql: `BEGIN; + ${asUser(TENANT_ADMIN_UID)} + SELECT (public.set_tenant_feature_exception('${TENANT_BEM_ESTAR}'::uuid, 'patients', false, 'pref'))->>'plan_allows'; + ROLLBACK;`, + expect: { ok: true, jsonHas: 'true' } + }, + { + name: 'set_tenant_feature_exception: feature_key inválida (uppercase) → rejeitado', + sql: `BEGIN; + ${asUser(SAAS_ADMIN_UID)} + SELECT public.set_tenant_feature_exception('${TENANT_BEM_ESTAR}'::uuid, 'PATIENTS', false, NULL); + ROLLBACK;`, + expect: { errorIncludes: 'formato inválido' } + }, + { + name: 'set_tenant_feature_exception: feature_key desconhecida → rejeitado', + sql: `BEGIN; + ${asUser(SAAS_ADMIN_UID)} + SELECT public.set_tenant_feature_exception('${TENANT_BEM_ESTAR}'::uuid, 'unknown_feature', false, NULL); + ROLLBACK;`, + expect: { errorIncludes: 'desconhecida' } + }, + { + name: 'set_tenant_feature_exception: tenant inexistente → rejeitado', + sql: `BEGIN; + ${asUser(SAAS_ADMIN_UID)} + SELECT public.set_tenant_feature_exception('99999999-9999-9999-9999-999999999999'::uuid, 'patients', false, NULL); + ROLLBACK;`, + expect: { errorIncludes: 'tenant não encontrado' } + }, + { + name: 'set_tenant_feature_exception: trigger guard ainda bloqueia INSERT direto fora do plano', + sql: `BEGIN; + INSERT INTO tenant_features (tenant_id, feature_key, enabled) + VALUES ('${TENANT_BEM_ESTAR}', 'documents.signatures', true); + ROLLBACK;`, + expect: { errorIncludes: 'não permitida pelo plano' } + }, + + // ────── delete_plan_safe ────── + { + name: 'delete_plan_safe: anônimo é rejeitado', + sql: `BEGIN; + SET LOCAL ROLE authenticated; + SELECT public.delete_plan_safe((SELECT id FROM plans WHERE key='clinic_free')); + ROLLBACK;`, + expect: { errorIncludes: 'Não autenticado' } + }, + { + name: 'delete_plan_safe: tenant_admin não-saas é rejeitado', + sql: `BEGIN; + ${asUser(TENANT_ADMIN_UID)} + SELECT public.delete_plan_safe((SELECT id FROM plans WHERE key='clinic_free')); + ROLLBACK;`, + expect: { errorIncludes: 'saas_admin' } + }, + { + name: 'delete_plan_safe: bloqueia delete de plano com subscriptions ativas', + sql: `BEGIN; + ${asUser(SAAS_ADMIN_UID)} + SELECT public.delete_plan_safe((SELECT id FROM plans WHERE key='clinic_free')); + ROLLBACK;`, + expect: { errorIncludes: 'assinatura' } + }, + { + name: 'delete_plan_safe: plan_id null → rejeitado', + sql: `BEGIN; + ${asUser(SAAS_ADMIN_UID)} + SELECT public.delete_plan_safe(NULL); + ROLLBACK;`, + expect: { errorIncludes: 'plan_id obrigatório' } + }, + { + name: 'delete_plan_safe: plano inexistente → rejeitado', + sql: `BEGIN; + ${asUser(SAAS_ADMIN_UID)} + SELECT public.delete_plan_safe('99999999-9999-9999-9999-999999999999'::uuid); + ROLLBACK;`, + expect: { errorIncludes: 'plano não encontrado' } + }, + + // ────── create_patient_intake_request_v2 ────── + { + name: 'create_patient_intake_request_v2: A#20 — anon NÃO chama mais o RPC direto', + sql: `BEGIN; + SET LOCAL ROLE anon; + SELECT public.create_patient_intake_request_v2('token-inexistente'::text, '{}'::jsonb, NULL::text); + ROLLBACK;`, + expect: { errorIncludes: 'permission denied' } + }, + { + name: 'create_patient_intake_request_v2: token inválido (via authenticated) → Token inválido', + sql: `BEGIN; + ${asUser(TENANT_ADMIN_UID)} + SELECT public.create_patient_intake_request_v2('token-inexistente'::text, '{}'::jsonb, NULL::text); + ROLLBACK;`, + expect: { errorIncludes: 'Token inválido' } + }, + { + name: 'create_patient_intake_request_v2: payload sem nome_completo → rejeitado', + sql: `BEGIN; + -- desativa invites pré-existentes desse owner (constraint one_active_per_owner) + UPDATE patient_invites SET active=false WHERE owner_id='${TENANT_ADMIN_UID}' AND active=true; + WITH inv AS ( + INSERT INTO patient_invites (token, owner_id, tenant_id, active) + VALUES ('test-token-' || md5(random()::text), '${TENANT_ADMIN_UID}', '${TENANT_BEM_ESTAR}', true) + RETURNING token + ) + SELECT public.create_patient_intake_request_v2( + (SELECT token FROM inv), + jsonb_build_object('email_principal','test@x.com','telefone','11999999999','consent',true), + NULL::text + ); + ROLLBACK;`, + expect: { errorIncludes: 'Nome' } + }, + + // ────── features.is_active (V#40 sanity) ────── + { + name: 'features.is_active existe e default true', + sql: `SELECT column_default FROM information_schema.columns WHERE table_name='features' AND column_name='is_active';`, + expect: { ok: true, jsonHas: 'true' } + }, + + // ────── A#20 rev2: defesa em camadas ────── + { + name: 'check_rate_limit: IP novo → allowed=true', + sql: `BEGIN; + SET LOCAL ROLE service_role; + SELECT (public.check_rate_limit('test-hash-novo-' || gen_random_uuid()::text, 'patient_intake'))->>'allowed'; + ROLLBACK;`, + expect: { ok: true, jsonHas: 'true' } + }, + { + name: 'record_submission_attempt: 3 falhas seguidas → marca requires_captcha', + sql: `BEGIN; + SET LOCAL ROLE service_role; + SELECT public.record_submission_attempt('patient_intake', 'h-fail-3', false, 'rpc', 'X', 'Y', NULL, NULL); + SELECT public.record_submission_attempt('patient_intake', 'h-fail-3', false, 'rpc', 'X', 'Y', NULL, NULL); + SELECT public.record_submission_attempt('patient_intake', 'h-fail-3', false, 'rpc', 'X', 'Y', NULL, NULL); + SELECT (public.check_rate_limit('h-fail-3', 'patient_intake'))->>'requires_captcha'; + ROLLBACK;`, + expect: { ok: true, jsonHas: 'true' } + }, + { + name: 'record_submission_attempt: > max_attempts → bloqueia (allowed=false)', + sql: `BEGIN; + SET LOCAL ROLE service_role; + SELECT public.record_submission_attempt('patient_intake', 'h-block', false, 'rpc', 'X', 'Y', NULL, NULL); + SELECT public.record_submission_attempt('patient_intake', 'h-block', false, 'rpc', 'X', 'Y', NULL, NULL); + SELECT public.record_submission_attempt('patient_intake', 'h-block', false, 'rpc', 'X', 'Y', NULL, NULL); + SELECT public.record_submission_attempt('patient_intake', 'h-block', false, 'rpc', 'X', 'Y', NULL, NULL); + SELECT public.record_submission_attempt('patient_intake', 'h-block', false, 'rpc', 'X', 'Y', NULL, NULL); + SELECT (public.check_rate_limit('h-block', 'patient_intake'))->>'allowed'; + ROLLBACK;`, + expect: { ok: true, jsonHas: 'false' } + }, + { + name: 'generate_math_challenge: cria id + question', + sql: `BEGIN; + SET LOCAL ROLE service_role; + SELECT (public.generate_math_challenge())->>'question'; + ROLLBACK;`, + expect: { ok: true, jsonHas: 'Quanto' } + }, + { + name: 'verify_math_challenge: id desconhecido → false', + sql: `BEGIN; + SET LOCAL ROLE service_role; + SELECT public.verify_math_challenge('00000000-0000-0000-0000-000000000000'::uuid, 1)::text; + ROLLBACK;`, + expect: { ok: true, jsonHas: 'f' } + }, + { + name: 'check_rate_limit: anon não pode chamar (só service_role)', + sql: `BEGIN; + SET LOCAL ROLE anon; + SELECT public.check_rate_limit('h', 'patient_intake'); + ROLLBACK;`, + expect: { errorIncludes: 'permission denied' } + }, + { + name: 'saas_security_config singleton existe com defaults', + sql: `SELECT honeypot_enabled::text || ',' || rate_limit_enabled::text FROM saas_security_config WHERE id=true;`, + expect: { ok: true, jsonHas: 'true,true' } + }, + + // ────── saas_twilio_config + RPCs ────── + { + name: 'get_twilio_config: anon NÃO pode chamar', + sql: `BEGIN; + SET LOCAL ROLE anon; + SELECT public.get_twilio_config(); + ROLLBACK;`, + expect: { errorIncludes: 'permission denied' } + }, + { + name: 'get_twilio_config: authenticated não-saas → "Sem permissão"', + sql: `BEGIN; + ${asUser(TENANT_ADMIN_UID)} + SELECT public.get_twilio_config(); + ROLLBACK;`, + expect: { errorIncludes: 'Sem permissão' } + }, + { + name: 'get_twilio_config: saas_admin retorna defaults', + sql: `BEGIN; + ${asUser(SAAS_ADMIN_UID)} + SELECT (public.get_twilio_config())->>'usd_brl_rate'; + ROLLBACK;`, + expect: { ok: true, jsonHas: '5.5' } + }, + { + name: 'update_twilio_config: tenant_admin é rejeitado', + sql: `BEGIN; + ${asUser(TENANT_ADMIN_UID)} + SELECT public.update_twilio_config(p_usd_brl_rate := 6); + ROLLBACK;`, + expect: { errorIncludes: 'saas_admin' } + }, + { + name: 'update_twilio_config: SID inválido (sem AC) → rejeitado', + sql: `BEGIN; + ${asUser(SAAS_ADMIN_UID)} + SELECT public.update_twilio_config(p_account_sid := 'XX123'); + ROLLBACK;`, + expect: { errorIncludes: 'account_sid inválido' } + }, + { + name: 'update_twilio_config: webhook sem http(s) → rejeitado', + sql: `BEGIN; + ${asUser(SAAS_ADMIN_UID)} + SELECT public.update_twilio_config(p_whatsapp_webhook_url := 'ftp://x.com'); + ROLLBACK;`, + expect: { errorIncludes: 'webhook' } + }, + { + name: 'update_twilio_config: rate fora da faixa → rejeitado', + sql: `BEGIN; + ${asUser(SAAS_ADMIN_UID)} + SELECT public.update_twilio_config(p_usd_brl_rate := 200); + ROLLBACK;`, + expect: { errorIncludes: 'usd_brl_rate' } + }, + { + name: 'update_twilio_config: saas_admin com payload válido → ok', + sql: `BEGIN; + ${asUser(SAAS_ADMIN_UID)} + SELECT (public.update_twilio_config( + p_account_sid := 'ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', + p_usd_brl_rate := 5.85, + p_margin_multiplier := 1.5 + ))->>'usd_brl_rate'; + ROLLBACK;`, + expect: { ok: true, jsonHas: '5.85' } + } +]; + +// ───────────────────────────────────────────────────────────────────────── +// Runner +// ----------------------------------------------------------------------------- +function color(s, c) { + const map = { red: 31, green: 32, yellow: 33, gray: 90 }; + return `\x1b[${map[c] || 0}m${s}\x1b[0m`; +} + +let pass = 0; +let fail = 0; +const failures = []; + +console.log(color('▶ AgenciaPsi RPC integration tests', 'gray')); +console.log(color('───────────────────────────────────', 'gray')); + +for (const c of cases) { + process.stdout.write(` ${c.name} ... `); + const r = runSql(c.sql); + + let ok = true; + let why = ''; + + if (c.expect.errorIncludes) { + if (r.ok) { + ok = false; + why = `esperava erro com "${c.expect.errorIncludes}" mas SQL passou. saída=${r.out}`; + } else if (!r.err.includes(c.expect.errorIncludes)) { + ok = false; + why = `erro não contém "${c.expect.errorIncludes}". stderr=${r.err.slice(0, 200)}`; + } + } else { + if (!r.ok) { + ok = false; + why = `esperava sucesso mas falhou. stderr=${r.err.slice(0, 300)}`; + } else if (c.expect.jsonHas && !r.out.includes(c.expect.jsonHas)) { + ok = false; + why = `saída não contém "${c.expect.jsonHas}". out=${r.out}`; + } + } + + if (ok) { + pass++; + console.log(color('✓', 'green')); + } else { + fail++; + failures.push({ name: c.name, why }); + console.log(color('✗', 'red')); + } +} + +console.log(color('───────────────────────────────────', 'gray')); +console.log(`${color(pass + ' passed', 'green')}, ${fail > 0 ? color(fail + ' failed', 'red') : color('0 failed', 'gray')}`); + +if (failures.length) { + console.log('\n' + color('FAILURES:', 'red')); + for (const f of failures) { + console.log(` ${color('×', 'red')} ${f.name}`); + console.log(` ${color(f.why, 'yellow')}`); + } + process.exit(1); +} + +process.exit(0); diff --git a/development/01-visao-geral/ESTRUTURA.md b/development/01-visao-geral/ESTRUTURA.md new file mode 100644 index 0000000..5ec47e8 --- /dev/null +++ b/development/01-visao-geral/ESTRUTURA.md @@ -0,0 +1,223 @@ +# AgenciaPsi — Estrutura Atual + +> Snapshot do que já está construído no sistema hoje (2026-04-17). +> Inventário extraído dos menus de cada perfil de usuário. + +**Legenda:** `[ok]` pronto · `[~]` parcial (estrutura existe, refinar) · `[--]` placeholder/vazio + +--- + +``` +AgenciaPsi (v5.0.0) +│ +├── SaaS Admin (operação da plataforma) +│ │ +│ ├── Início +│ │ └── Dashboard [ok] /saas +│ │ +│ ├── Planos +│ │ ├── Planos e Preços [ok] /saas/plans +│ │ ├── Vitrine Pública [ok] /saas/plans-public +│ │ ├── Recursos (features) [ok] /saas/features +│ │ ├── Controle de Recursos [ok] /saas/plan-features +│ │ └── Limites por Plano [ok] /saas/plan-limits +│ │ +│ ├── Assinaturas +│ │ ├── Listagem [ok] /saas/subscriptions +│ │ ├── Intenções [ok] /saas/subscription-intents +│ │ ├── Histórico (eventos) [ok] /saas/subscription-events +│ │ └── Saúde das Assinaturas [ok] /saas/subscription-health +│ │ +│ ├── Operações +│ │ ├── Clínicas (Tenants) [ok] /saas/tenants +│ │ ├── Feriados [ok] /saas/feriados +│ │ └── Suporte Técnico [ok] /saas/support +│ │ +│ ├── Canais de Comunicação +│ │ ├── WhatsApp (Evolution API) [ok] /saas/whatsapp +│ │ ├── WhatsApp Twilio (Subcontas) [ok] /saas/twilio-whatsapp +│ │ ├── Templates WhatsApp/SMS [ok] /saas/notification-templates +│ │ └── Add-ons / Créditos SMS [ok] /saas/addons +│ │ +│ └── Conteúdo +│ ├── Documentação [ok] /saas/docs +│ ├── FAQ [ok] /saas/faq +│ ├── Carrossel de Login [ok] /saas/login-carousel +│ ├── Avisos Globais [ok] /saas/global-notices +│ ├── Templates de E-mail [ok] /saas/email-templates +│ └── Templates de Documentos [ok] /saas/document-templates +│ +├── Clínica Admin (gestão da clínica) +│ │ +│ ├── Início +│ │ ├── Dashboard da Clínica [ok] /admin +│ │ ├── Agenda da Clínica [ok] /admin/agenda/clinica +│ │ ├── Recorrências [ok] /admin/agenda/recorrencias +│ │ └── Compromissos [ok] /admin/agenda/compromissos +│ │ +│ ├── Pacientes +│ │ ├── Lista de Pacientes [ok] /admin/pacientes +│ │ ├── Grupos [ok] /admin/pacientes/grupos +│ │ ├── Tags [ok] /admin/pacientes/tags +│ │ ├── Médicos & Referências [ok] /admin/pacientes/medicos +│ │ ├── Link Externo (cadastro) [ok] /admin/pacientes/link-externo +│ │ ├── Cadastros Recebidos [ok] /admin/pacientes/cadastro/recebidos +│ │ └── Templates de Documentos [ok] /admin/documents/templates +│ │ +│ ├── Gestão +│ │ ├── Profissionais [ok] /admin/clinic/professionals +│ │ ├── Tipos de Clínicas (features ativas) [ok] /admin/clinic/features +│ │ └── Meu Plano [ok] /admin/meu-plano +│ │ +│ ├── Financeiro +│ │ └── Cobranças [ok] /admin/financeiro +│ │ +│ └── Sistema +│ ├── Meu Perfil [ok] /account/profile +│ ├── Meu Negócio [ok] /account/negocio +│ ├── Segurança [ok] /admin/settings/security +│ ├── Agendamento Online (PRO) [ok] /admin/online-scheduling +│ └── Agendamentos Recebidos [ok] /admin/agendamentos-recebidos +│ +├── Terapeuta (dia-a-dia do profissional) +│ │ +│ ├── Início +│ │ └── Dashboard [ok] /therapist +│ │ +│ ├── Agenda +│ │ ├── Agenda [ok] /therapist/agenda +│ │ ├── Recorrências [ok] /therapist/agenda/recorrencias +│ │ └── Compromissos [ok] /therapist/agenda/compromissos +│ │ +│ ├── Pacientes +│ │ ├── Meus Pacientes [ok] /therapist/patients +│ │ ├── Grupos de Pacientes [ok] /therapist/patients/grupos +│ │ ├── Tags [ok] /therapist/patients/tags +│ │ ├── Médicos & Referências [ok] /therapist/patients/medicos +│ │ ├── Documentos [ok] /therapist/documents +│ │ ├── Templates de Documentos [ok] /therapist/documents/templates +│ │ ├── Meu Link de Cadastro [ok] /therapist/patients/link-externo +│ │ └── Cadastros Recebidos [ok] /therapist/patients/cadastro/recebidos +│ │ +│ ├── Agendamento Online +│ │ ├── Configurar Página Pública [ok] /therapist/online-scheduling +│ │ └── Agendamentos Recebidos [ok] /therapist/agendamentos-recebidos +│ │ +│ ├── Financeiro +│ │ ├── Cobranças [ok] /therapist/financeiro +│ │ └── Lançamentos [ok] /therapist/financeiro/lancamentos +│ │ +│ ├── Relatórios +│ │ └── Relatórios [~] /therapist/relatorios (export PDF/Excel a validar) +│ │ +│ └── Conta +│ ├── Meu Plano [ok] /therapist/meu-plano +│ ├── Meu Perfil [ok] /account/profile +│ ├── Meu Negócio [ok] /account/negocio +│ └── Segurança [ok] /account/security +│ +├── Supervisor (supervisão de casos) +│ │ +│ ├── Início +│ │ └── Dashboard [ok] /supervisor +│ │ +│ ├── Supervisão +│ │ └── Sala de Supervisão [~] /supervisor/sala (features avançadas a confirmar) +│ │ +│ └── Conta +│ ├── Meu Plano [ok] /supervisor/meu-plano +│ ├── Meu Perfil [ok] /account/profile +│ └── Segurança [ok] /account/security +│ +├── Portal do Paciente (portal web para o paciente) +│ │ +│ ├── Início +│ │ └── Dashboard [ok] /portal +│ │ +│ ├── Minhas Sessões +│ │ └── Sessões [ok] /portal/sessoes +│ │ +│ └── Conta +│ ├── Meu Plano [ok] /portal/meu-plano +│ ├── Minha Conta [ok] /account/profile +│ └── Segurança [ok] /account/security +│ +└── Editor de Conteúdo (criação de cursos — roadmap futuro) + │ + ├── Início + │ └── Dashboard [ok] /editor + │ + ├── Conteúdo + │ ├── Cursos [--] /editor/cursos (placeholder) + │ ├── Módulos [--] /editor/modulos (placeholder) + │ └── Publicados [--] /editor/publicados (placeholder) + │ + └── Conta + ├── Meu Plano [ok] /editor/meu-plano + ├── Meu Perfil [ok] /account/profile + └── Segurança [ok] /account/security +``` + +--- + +## Stack / Infraestrutura (suporte) + +``` +Infraestrutura +│ +├── Banco & Backend +│ ├── Supabase (Postgres + Auth + Storage + Realtime + Edge Functions) [ok] +│ ├── PostgreSQL 15 (container Docker local + cloud) [ok] +│ └── Docker Compose (supabase + evolution-api) [ok] +│ +├── Comunicação +│ ├── Evolution API (WhatsApp) [ok] +│ ├── Twilio (SMS / Voz / WhatsApp Business) [ok] +│ ├── Mailpit (SMTP dev local) [ok] +│ └── SMTP produção (envio real de e-mails) [--] pendente +│ +├── Geração de Documentos +│ ├── pdfmake + html2pdf + jsPDF [ok] +│ ├── Jodit (editor rico) [ok] +│ └── html2canvas-pro [ok] +│ +├── Frontend +│ ├── Vue 3 + Composition API [ok] +│ ├── Vite 5 + Brotli/Gzip [ok] +│ ├── PrimeVue 4 (tema Sakai) [ok] +│ ├── Tailwind v4 [ok] +│ ├── Vue Router (guards por role/tenant) [ok] +│ ├── Pinia (state) [ok] +│ ├── FullCalendar 6 (agenda) [ok] +│ └── Chart.js 3 (dashboards) [ok] +│ +└── Dev / Tooling + ├── Supabase CLI [ok] + ├── db.cjs (setup/backup/restore/migrate/verify) [ok] + ├── generate-dashboard.cjs (mapa do banco) [ok] + ├── Vitest 4 [~] base pronta, cobertura a expandir + └── ESLint + Prettier [ok] +``` + +--- + +## Resumo numérico + +- **6 perfis** (roles) +- **27 grupos** de navegação +- **79 features** ativas no menu + - **73** `[ok]` prontas + - **2** `[~]` parciais + - **4** `[--]` placeholders +- **97 tabelas** no banco (11 domínios) +- **27 views** +- **165 funções SQL** + +--- + +## Próximos passos (a definir nesta manhã) + +- [ ] Mapear módulos que **deveriam existir** mas ainda não estão (comparando com Psicomanager / SimplePractice / iClinic) +- [ ] Organizar o roadmap em **fases** (MVP → v2 → v3) +- [ ] Identificar dependências entre módulos (ex: pagamento integrado antes de faturamento automático) +- [ ] Decidir cortes (o que fica pra pós-MVP) diff --git a/estrutura.txt b/development/01-visao-geral/estrutura.txt similarity index 100% rename from estrutura.txt rename to development/01-visao-geral/estrutura.txt diff --git a/development/01-visao-geral/mapa-sistema.html b/development/01-visao-geral/mapa-sistema.html new file mode 100644 index 0000000..818113a --- /dev/null +++ b/development/01-visao-geral/mapa-sistema.html @@ -0,0 +1,741 @@ + + + + + +Mapa do Sistema · AgenciaPsi + + + + + +
+
Mapa do SistemaAgenciaPsi · v5.0.0
+ +
+
+ Pronto + Parcial + Faltando +
+
+ +
+

Mapa do Sistema

+

Tudo que o AgenciaPsi entrega hoje, agrupado por perfil de usuário. Clique em qualquer feature pra ver detalhes — rota, status e notas. Use a busca no topo pra filtrar.

+
SaaSAgenciaPsi
+
+
+ +
+ + + + + + + diff --git a/AUDITORIA.md b/development/02-auditoria/AUDITORIA.md similarity index 100% rename from AUDITORIA.md rename to development/02-auditoria/AUDITORIA.md diff --git a/development/03-concorrentes/concorrentes.md b/development/03-concorrentes/concorrentes.md new file mode 100644 index 0000000..b7f76c3 --- /dev/null +++ b/development/03-concorrentes/concorrentes.md @@ -0,0 +1,740 @@ +# Concorrentes — Análise Comparativa + +> Benchmark de referência pro **AgenciaPsi**. Objetivo: mapear features dos principais players, confrontar com o que já temos e identificar gaps de roadmap. + +## Como usar este documento +Cada produto tem sua seção com módulos organizados por domínio. Tags de fonte: +- `[observação 2026-04-17]` — observado direto pelo usuário navegando no produto +- `[fetched 2026-04-17]` — extraído via WebFetch das páginas oficiais +- `[público]` — inferido de conhecimento público (marketing/demos/reviews) +- `[?]` — ainda não verificado + +## Legenda de status (vs AgenciaPsi) +- `[tem]` — AgenciaPsi já tem esta feature +- `[parcial]` — tem estrutura mas não está completo +- `[gap]` — AgenciaPsi **não** tem → candidato a roadmap +- `[?]` — a investigar (no concorrente ou no AgenciaPsi) + +--- + +## 1. SimplePractice (EUA) + +### Resumo executivo +- **País:** EUA · **Idioma:** inglês (+ Client Portal em espanhol) `[fetched 2026-04-17]` +- **Escala:** **20M+ clientes** e **250k+ practitioners** na plataforma `[fetched: /pricing/]` +- **Pricing:** USD ~29–99/mês por profissional (Essential, Plus, e um terceiro tier) · **30-day** ou **7-day trial** `[fetched: /pricing/]` +- **Público-alvo:** terapeutas, psicólogos, counselors, psiquiatras — solo e pequenas clínicas (até ~10 profissionais) `[público]` +- **Diferencial:** maturidade em telehealth HIPAA, claims de seguro (insurance billing), consent forms assinados eletronicamente, biblioteca de instrumentos de avaliação `[público]` +- **Compliance:** HIPAA + HITRUST + PCI + BAA `[fetched: /features/]` +- **Posicionamento:** "o Salesforce da saúde mental" no mercado americano `[público]` + +### Categorias top-level confirmadas `[fetched: /features/]` +- **Admin & Practice Management** — Reports Access, AutoPay Enrollment, Practitioner Profiles, Permissions Management +- **Client Care & Documentation** — Secure Messaging, Client Scoring/Assessment Tools, Prescription/Medication Renewal Requests +- **Client-Facing Services** — Client Portal, Telehealth +- **Compliance & Security** — HIPAA, HITRUST, PCI, BAA +- **Additional Services** — Credentialing Services (grátis, ofertado), Switching Assistance (ajuda na migração de outra plataforma) + +### Navegação principal observada +`[observação 2026-04-17]` +Menus top-level vistos no app: +- **Clients** (Clientes) +- **Billing / Payments** (Pagamentos) +- **Insurance** (Convênios EUA) +- **Analytics** +- **Activities** (Atividades) +- **Supervision** +- **Settings** +- **Reminders** +- **Requests** +- **Marketing** +- **Recently Viewed** — atalho pros últimos clientes acessados +- **Busca global** no topbar por clientes + +### Módulos + +#### 1.1 Clientes (Clients) +- Cadastro completo do paciente — `[tem]` +- **Recently Viewed** (últimos clientes acessados) — `[gap]` +- **Busca global** no topbar — `[?]` investigar no AgenciaPsi +- Insurance info no perfil — `[parcial]` (temos convênios cadastrados, mas não ligados ao perfil do paciente desse jeito) +- Emergency contacts — `[?]` +- Tags / grupos — `[tem]` +- Merge de clientes duplicados — `[gap]` + +#### 1.2 Settings → Clinical Info +`[observação 2026-04-17]` +> Estou vendo a tela de Clinical Info. Para os EUA, tem NPI Number, Taxonomy Code e Specialty. Nós já temos um cadastro de clínica completo para o Brasil. Acho que poderíamos colocar o campo **Especialidades** também, listar um monte e, se o usuário não encontrar, cadastrar o seu. + +**Propostas derivadas:** +- Campo **Especialidades** da clínica/profissional com lista pré-definida + "outro" — `[gap]` +- Equivalente BR do NPI/Taxonomy seria: **número de registro** (CRP, CRM, etc) e **área de atuação** — talvez já exista em user_settings/profile, a confirmar — `[?]` + +`[observação 2026-04-17]` +> Tem um campo **Licenses Type** onde o usuário seleciona se é Médico, PhD, etc. (Não sei se isso seria aqui.) + +**Proposta derivada:** campo **Tipo de registro profissional** (CRP, CRM, CRFa, RMS, etc) no cadastro do terapeuta — útil pra aparecer em recibos/laudos — `[gap]` + +#### 1.3 Settings → Practice Details +`[observação 2026-04-17]` +> Cadastra a localização da clínica e digamos filiais (no caso de gestão de clínicas). É possível definir qual é o **Main Office**. + +**Proposta derivada:** **multi-unidade/filiais** com marcação de "unidade principal" — `[gap]` +- Ao criar consulta, escolher em qual unidade ocorre +- Faturamento pode ser consolidado ou por unidade +- Cada unidade tem endereço, telefone, papel timbrado próprio (opcional) + +#### 1.4 Client Care → Template Library +`[observação 2026-04-17]` +> Variados tipos de arquivos prontos pra personalizar. Ex: **GAD-7** (Generalized Anxiety Disorder), **PHQ-9** (Patient Health Questionnaire), **COVID-19 Pre-Appointment Screening Questionnaire**. + +**Proposta derivada (alto valor):** **Biblioteca de instrumentos de avaliação psicológica** com scoring automático — `[gap]` +- GAD-7 (ansiedade generalizada) +- PHQ-9 (depressão) +- BDI (Inventário Beck) +- BAI (Beck ansiedade) +- PCL-5 (PTSD) +- AUDIT (uso de álcool) +- DASS-21 (depressão/ansiedade/estresse) +- SRQ-20 (MS/BR) +- Escala de qualidade de vida (WHOQOL) + +> **Diferencial possível pro BR:** curar uma biblioteca com instrumentos validados em português (muitos psicólogos ainda aplicam em papel). + +#### 1.5 Client Care → Shareable Documents +`[observação 2026-04-17]` +> Manage default intake documents and upload files. + +**Interpretação:** documentos que vão com o paciente durante onboarding/intake (antes da 1ª sessão). +- AgenciaPsi já tem módulo de Documentos — `[tem]` +- Mas "enviar pacote de intake" estruturado antes da 1ª sessão — `[parcial / ?]` +- Upload de arquivo pelo paciente (anexos, exames) — `[?]` + +#### 1.6 Consent Forms +`[observação 2026-04-17]` +> Read and signed by clients via the client portal. Legal disclaimer: consent forms are for reference only. It's your responsibility to customize them and ensure they meet the legal requirements of your state. +> +> Formulários default observados: +> - Consent for Telehealth Consultation +> - Credit Card Authorization +> - Notice of Privacy Practices +> - Informed Consent for Psychotherapy +> - Practice Policies +> - Release Forms +> - Consent to Record Audio +> - (+ New Consent Form) +> +> O usuário consegue adicionar, editar um novo e visualizar. + +**Proposta derivada:** **biblioteca de termos de consentimento** editáveis + assinatura eletrônica no portal — `[gap]` + +Equivalentes BR a disponibilizar: +- **TCLE** — Termo de Consentimento Livre e Esclarecido (CFP exige) +- **Termo de Atendimento Online** — atendimento remoto (Resolução CFP 11/2018) +- **Política de Privacidade LGPD** — tratamento de dados sensíveis de saúde +- **Autorização de Cobrança** (cartão recorrente) +- **Termo de Gravação** (se for gravar sessão) +- **Termo de Sigilo** em caso de supervisão/atendimento em grupo +- **Autorização de uso de imagem** (se aplicável) +- **+ Novo termo personalizado** (botão) + +#### 1.7 Papel Timbrado +`[observação 2026-04-17]` +> Precisa criar a parte de papel timbrado, assim como fizemos no outro projeto. Quando chegar a hora, pode me perguntar. + +**Status:** `[gap]` — reaproveitar a implementação do outro projeto (UniaoApp). Quando chegar a hora, me pergunta pra eu buscar lá e portar. + +#### 1.8 Outros módulos públicos do SimplePractice `[público]` + +> Essas features são conhecidas publicamente (pricing pages / demos). A confirmar navegando. + +##### Client Portal (Portal do Paciente) `[fetched: /features/client-portal/]` +Portal do cliente completo, HIPAA-compliant. Extraído da página oficial: +- **Self-scheduling** — cliente solicita / cancela / reagenda consultas — `[tem parcial]` (agendador público existe mas não é portal autenticado) +- **Paperless intake forms** — cliente preenche formulários antes da 1ª sessão — `[gap]` +- **Questionnaires** — instrumentos de medição/progresso (GAD-7, PHQ-9 etc) — `[gap]` +- **Secure messaging** — chat seguro terapeuta ↔ cliente — `[gap]` +- **Invoice payment** — paciente paga direto pelo portal — `[gap]` +- **Push notifications** — notif antes da consulta — `[gap]` (temos email/SMS/WhatsApp, push seria diferencial) +- **Appointment reminders** — lembretes programados — `[tem]` +- **Portal em espanhol** — multi-idioma — `[gap]` (pensar se vale traduzir pra ES pro LATAM) +- **Administrative controls** — clínica decide quais features o paciente enxerga — `[parcial]` +- **HIPAA-compliant** — `[parcial]` (LGPD equivalente no BR) + +##### Telehealth `[fetched: /features/telehealth/]` +SP tem um **módulo de telehealth próprio**, totalmente integrado ao EHR. Confirmado na página oficial: +- **Vídeo nativo** — "launch sessions directly from your calendar — no extra login required" — `[gap]` +- **Sala de espera virtual** — "admit clients when you're ready" — `[gap]` +- **Screen sharing** embutido — `[gap]` +- **Chat seguro** durante a sessão — `[gap]` +- **Digital whiteboard** (quadro branco digital) — `[gap]` · bom pra sessões com crianças / TCC visual +- **Video em grupo** — até 15 clientes por sessão — `[gap]` · suporta terapia de grupo +- **Timer de sessão** embutido + **fundos desfocados (blurred backgrounds)** — `[gap]` +- **Lembretes automáticos** SMS/email — `[tem parcial]` (temos lembretes; falta o link da sala) +- **HIPAA + HITRUST + PCI** compliance — `[tem parcial]` (temos LGPD compliance mas não certificação formal) +- **Integração nativa com o EHR** — notas da sessão + billing automáticos — `[gap]` +- **Apps iOS / Android** — `[gap]` (AgenciaPsi é só web) + +##### Documentation / Notes de sessão +- Templates de notas pré-prontos (**SOAP**, **BIRP**, **DAP**) — `[?]` +- Progress notes com versionamento — `[parcial]` (temos Prontuário, validar versionamento) +- Co-assinatura de supervisor em notas de supervisionando — `[gap]` + +##### Billing +- **Credit card on file** (cartão salvo) — `[gap]` +- **Auto-billing** / cobrança recorrente automática — `[parcial]` (temos recorrências de consulta, não de cobrança) +- **Superbill** (recibo detalhado com CID, CPT codes equivalente) — `[gap]` +- **AR reports** (contas a receber, aging) — `[?]` + +##### Insurance (EUA) — equivalente BR: **Convênios** +- Eligibility check automático — `[gap]` (BR seria checagem TISS mas raramente integra) +- Claims submission eletrônico (837) — `[gap]` +- ERA (Electronic Remittance Advice) — `[gap]` + +##### Reminders (Lembretes) +- Email / SMS / Voice — `[tem]` (temos Email + SMS + WhatsApp) +- Confirmação pelo paciente (responder "SIM") — `[?]` +- Configuração de quanto tempo antes — `[tem]` +- Templates customizáveis — `[tem]` + +##### Marketing +- Perfil público / diretório — `[gap]` (similar a Doctoralia) +- Website/landing de divulgação — `[gap]` +- SEO básico — `[gap]` + +##### Analytics / Relatórios +- Receita (MRR, por período, por profissional) — `[parcial]` (dashboard tem alguns KPIs, não sei se tudo) +- Sessões realizadas (por tipo, por profissional, por convênio) — `[?]` +- Clientes ativos / novos / churn — `[?]` +- Export PDF/Excel — `[parcial]` + +##### Supervisão +- Compartilhar notas com supervisor — `[parcial]` (Sala de Supervisão existe, features a confirmar) +- Co-assinar antes de fechar nota — `[gap]` + +##### Sync & Integrações +- **Google Calendar** 2-way sync — `[gap]` (já listado no assessment como pós-MVP) +- **Apple Calendar** — `[gap]` +- **Stripe** / gateway de pagamento — `[gap]` (crítico) + +##### Tasks / To-do +- Lista de tarefas por cliente — `[?]` +- Task follow-ups (ex: "ligar em 30 dias") — `[gap]` + +--- + +## 2. Psicomanager (Brasil) + +### Resumo executivo `[público]` +- **País:** Brasil · **Pricing:** R$ ~50–150/mês por profissional (varia por plano e módulos contratados) — **confirmar** +- **Público-alvo:** psicólogos solo e clínicas no BR +- **Diferencial:** nicho-específico em psicologia no BR, familiaridade com CFP/LGPD, integração com WhatsApp/PIX +- **Posicionamento:** o "padrão" do mercado brasileiro pra psicólogos + +### Módulos conhecidos publicamente `[público / ?]` + +> **Tentativa de WebFetch em 2026-04-17:** `psicomanager.com.br/`, `/planos/`, `/funcionalidades/`, `/blog/`, `/agenda-online/`, `/prontuario-eletronico/` — **todas retornaram SPA vazia ou 404**. O site do Psicomanager é single-page app renderizado via JS, o que torna scraping impossível sem browser headless. +> +> **Caminhos alternativos pra validar:** +> 1. Você abrir o site logado e me mandar screenshots/texto das principais telas +> 2. Criar um trial e navegar junto comigo +> 3. Buscar reviews detalhados em sites tipo **B2BStack**, **Reclame Aqui**, **SoftwareSuggest BR** +> 4. Buscar por posts do blog deles via Google (`site:psicomanager.com.br`) +> +> A lista abaixo é conhecimento de mercado. Priorizar validação antes de tomar decisões de roadmap baseadas nela. + +- **Agenda** — online, presencial, bloqueios, recorrências +- **Prontuário Eletrônico** — anamnese, evolução, sessões +- **Cadastro de Pacientes** — multi-paciente, anamnese inicial +- **Financeiro** — cobranças, PIX, cartão (Asaas? Iugu? a confirmar) +- **Lembretes** — Email/SMS/WhatsApp +- **Recibo / NFSe** — emissão de nota fiscal de serviço +- **Teleconsulta** — integrada ao sistema (próprio ou externo? a confirmar) +- **Anamnese e Evolução** — templates customizáveis +- **Portal do Paciente** — visão do paciente das sessões/faturas +- **Documentos** — atestados, laudos, declarações +- **Relatórios** — produção, financeiro, agenda +- **LGPD** — compliance, consentimento de dados +- **Multi-profissional** — gestão de clínica com múltiplos psicólogos +- **Assinatura eletrônica** — documentos assinados pelo paciente `[?]` +- **App mobile** pro paciente/profissional `[?]` + +### Observações durante exploração +> *Em branco — preencher navegando no Psicomanager.* + +--- + +## 3. PsicoPlanner (Brasil) — concorrente direto `[fetched 2026-04-17]` + +### Resumo executivo +- **País:** Brasil · **Nicho:** psicologia-first +- **Preços (publicados):** + - **Individual:** R$ 59/mês (1 acesso) + - **Plus:** R$ 79/mês (1 profissional + 1 recepcionista) + - **Duo:** R$ 99/mês (2 logins independentes) + - **Clínicas:** R$ 395/mês (até 5 profissionais) +- **Posicionamento:** "psicólogo que odeia planilha" — simples, WhatsApp-first, IA + +### Features confirmadas +- **Lembretes WhatsApp** ilimitados e personalizáveis (confirmações, lembretes, avisos de pagamento) +- **Sala de vídeo integrada** (teleconsulta com confidencialidade dentro do app) +- **Prontuários e anamnese customizáveis** por profissional +- **Autoagendamento** via link compartilhável (paciente escolhe horário) +- **Agenda diária por WhatsApp às 7h** (push automático do cronograma do dia) — `[gap]` diferencial forte +- **Gestão financeira** visual (pagamentos, recebidos, pendentes) +- **PsiAssist AI** — gera relatórios, documentos, resumos e conteúdo com **compliance CFP** +- **Rastreamento de engajamento em tempo real** — ver se paciente recebeu, leu e respondeu + +### Destaques comparativos +| Feature | PsicoPlanner | AgenciaPsi | +|---|---|---| +| WhatsApp nativo com dashboard engajamento | ✅ | `[parcial]` (enviamos, mas tracking?) | +| Agenda diária automática 7AM via WA | ✅ | `[gap]` | +| IA com compliance CFP | ✅ (PsiAssist) | `[gap]` | +| Preço entrada baixo | R$ 59 solo | (definir pricing) | + +--- + +## 4. iClinic (Brasil) — multispecialidade popular `[fetched 2026-04-17]` + +### Resumo executivo +- **País:** Brasil · **Nicho:** multispecialidade (psicólogos usam bastante apesar de não ser foco) +- **Posicionamento:** "o EHR completo pro consultório/clínica médica no BR" + +### Módulos confirmados + +#### Prontuário Eletrônico +- Histórico de valores em tabela/gráfico +- Campos personalizados por especialidade +- Anexo de arquivos centralizado +- **Assinatura Digital** com validade jurídica +- Consulta CID-10 integrada +- Compartilhamento de prontuário entre profissionais +- **Controle de vacinas** (rastreamento por idade) — menos relevante pra psico +- Tags de lembrete +- Comparação de imagens (evolução do tratamento) + +#### Prescrição Eletrônica +- Base de medicamentos atualizada +- Modelos personalizados +- **Envio por WhatsApp** direto ao paciente + +#### Emissão de Documentos +- Modelos com campos automáticos +- Preenchimento automático de dados do paciente + +#### Ferramentas Clínicas +- Calculadoras: IMC, curva de crescimento, IG, DPP +- Integração com Glic (diabetes) + +#### Outros módulos mencionados +- Agenda Médica · Teleconsulta · Agendamento Online · Marketing Médico (email campaigns) · Gestão Financeira · Recepcionista + +### Destaques comparativos +| Feature | iClinic | AgenciaPsi | +|---|---|---| +| Assinatura digital com validade jurídica | ✅ | `[gap]` | +| CID-10 integrado | ✅ | `[?]` | +| Prescrição eletrônica com WhatsApp | ✅ | `[gap]` | +| Email campaigns (marketing) | ✅ | `[gap]` | +| Histórico em gráfico/tabela evolutiva | ✅ | `[?]` | + +--- + +## 5. Amplimed (Brasil) — AI-first `[fetched 2026-04-17]` + +### Resumo executivo +- **País:** Brasil · **Nicho:** multispecialidade com **forte aposta em IA** +- **Posicionamento:** "IA que trabalha pela sua clínica" + +### Módulos confirmados + +#### 🤖 Amélia (IA proprietária — 3 produtos) +- **Amélia Agendamento** — bot virtual que atende e responde pacientes via WhatsApp **24/7** com sincronização automática da agenda +- **Amélia Transcrição** — converte áudio em texto e preenche prontuário automaticamente +- **Amélia Copilot** — localiza, resume e organiza info do paciente + pesquisas médicas + +#### Gestão +- Gestão Clínica com redução de até **38% de ausências** via agenda inteligente +- Prontuário Eletrônico estruturado + compartilhamento seguro +- Agenda de Procedimentos (cirurgias, exames) com controle de conflitos + +#### Agendamento & Comunicação +- Agendamento Online 24/7 com integração a prontuário + pagamento +- SMS (redução de faltas) +- **WhatsApp Connect** — mensagens personalizadas + lembretes + +#### Financeiro +- **Faturamento TISS** — "99% menos glosas" via padrão automático — `[gap]` (psico usa pouco TISS, mas é diferencial enorme pra clínica grande) +- **NFS-e** emissão simplificada integrada +- Dashboards de indicadores + +#### Outros +- Teleconsulta com criptografia E2E + prescrição digital +- Estoque com alertas de validade +- **Painel de chamados** com TV na recepção +- **Certificado Digital ICP-Brasil** embutido + +### Destaques comparativos (e FORTES alertas) +| Feature | Amplimed | AgenciaPsi | +|---|---|---| +| Bot WhatsApp 24/7 que agenda sozinho | ✅ Amélia | `[gap]` 🔥 | +| Transcrição de sessão via áudio | ✅ Amélia | `[gap]` 🔥 | +| Copilot de IA no prontuário | ✅ Amélia | `[gap]` 🔥 | +| NFS-e integrado | ✅ | `[gap]` | +| Faturamento TISS com validação automática | ✅ | `[gap]` | +| Certificado digital ICP-Brasil | ✅ | `[gap]` | + +> **Nota:** AI-heavy é a principal tendência BR em 2026. Amplimed e Psicomanager (prov.) vão nessa direção. Pode ser **o diferencial** do AgenciaPsi se investir. + +--- + +## 6. Ninsaúde (Brasil) — ERP clínico `[fetched 2026-04-17]` + +### Resumo executivo +- **País:** Brasil · **Nicho:** ERP clínico completo (várias especialidades) — menos focado em psico +- **Posicionamento:** "ERP da clínica" — mais robusto, mais caro + +### Módulos confirmados +- Prontuário Eletrônico +- Sistema de Atendimento (fluxo) +- Gerenciamento Financeiro +- **Faturamento de Convênios** +- Controle de Estoque +- Engajamento dos Pacientes (retenção) +- Engajamento da Equipe (motivação interna) +- Análise Inteligente (BI) +- Retenção de Pacientes + +### Produtos satélites +- **Ninsaúde CRM** — gestão de relacionamento +- **Ninsaúde Safe** — segurança de dados +- **Ninsaúde Sign** — assinatura digital +- Integração QR Code +- **Nome Social** — respeita identidade de gênero no cadastro + +### Destaques comparativos +| Feature | Ninsaúde | AgenciaPsi | +|---|---|---| +| CRM interno (leads, funil) | ✅ | `[gap]` | +| Nome social | ✅ | `[?]` (validar se temos) | +| Retenção automatizada (CRM) | ✅ | `[gap]` | +| BI avançado | ✅ | `[parcial]` | + +--- + +## 7. Jane App (Canadá) — referência internacional de UX `[fetched 2026-04-17]` + +### Resumo executivo +- **País:** Canadá · **Idioma:** inglês · **Mercados:** CA, US, UK +- **Posicionamento:** "o app mais amado pelos profissionais de saúde" — UX premium + +### Módulos confirmados + +#### Cliente & Agendamento +- **Online Booking** com branding customizável da clínica + 24/7 +- **Staff & Appointment Scheduling** multi-location (sala, recurso, waitlist, pagamento — tudo na mesma visão) + +#### Documentação +- **Charting & Documentation** — biblioteca de templates, SOAP notes, forms, surveys +- **AI Scribe** — grava ou dicta a sessão e gera nota em minutos (integrado) + +#### Comunicação +- **Telehealth** HIPAA-compliant — até **12 clientes** por sessão (solo + grupo/family) + +#### Financeiro +- **Jane Payments** — online + terminal físico, PCI-compliant +- Billing & Insurance multi-região (CA, US, UK) +- Invoicing de serviços, memberships e produtos + +#### BI +- Real-time dashboards de saúde do negócio +- Relatórios customizáveis + +#### Infra & Support +- Compliance: HIPAA + PIPEDA + GDPR + SOC-2 + 256-bit encryption +- Integrações com apps third-party +- **Website Builder com IA** — cria site da clínica auto-sincronizado com staff/branding +- Suporte ilimitado (phone/email/chat) +- **Migração de dados grátis** + +### Destaques comparativos +| Feature | Jane App | AgenciaPsi | +|---|---|---| +| AI Scribe (grava e gera nota) | ✅ | `[gap]` 🔥 | +| Terminal físico de pagamento | ✅ | `[gap]` (POS integrado) | +| Multi-location + resources (sala/equipamento) | ✅ | `[parcial]` | +| Website builder integrado | ✅ | `[gap]` | +| Cert SOC-2 | ✅ | `[gap]` | +| Migração grátis de outra plataforma | ✅ | `[gap]` | + +--- + +## 8. Comparação rápida (quadro) + +> Linhas com `*` têm fonte `[fetched]` confirmada do SimplePractice. Demais são `[público]`/`[?]`. + +| Domínio | AgenciaPsi | SimplePractice | Psicomanager | +|---|---|---|---| +| Cadastro de pacientes | `[tem]` | `[tem]` | `[tem]` | +| Grupos / Tags | `[tem]` | `[tem]` | `[?]` | +| **Busca global topbar** | `[?]` | `[tem]` | `[?]` | +| **Recently viewed** | `[gap]` | `[tem]` | `[?]` | +| **Merge de duplicatas** | `[gap]` | `[tem]` | `[?]` | +| Agenda | `[tem]` | `[tem]` | `[tem]` | +| Agendamento online público | `[tem]` | `[tem]` | `[?]` | +| Recorrências | `[tem]` | `[tem]` | `[?]` | +| **Teleconsulta integrada** | `[gap]` | `[tem]` * | `[?]` | +| **Sala de espera virtual** | `[gap]` | `[tem]` * | `[gap]` | +| **Screen sharing na sessão** | `[gap]` | `[tem]` * | `[?]` | +| **Digital whiteboard** | `[gap]` | `[tem]` * | `[?]` | +| **Video em grupo (até 15)** | `[gap]` | `[tem]` * | `[?]` | +| **Blurred background (vídeo)** | `[gap]` | `[tem]` * | `[?]` | +| Prontuário / Notas | `[tem]` | `[tem]` | `[tem]` | +| **Templates de nota (SOAP/BIRP/DAP)** | `[?]` | `[tem]` | `[?]` | +| **Versionamento de nota** | `[?]` | `[tem]` | `[?]` | +| **Biblioteca de avaliações (GAD-7/PHQ-9)** | `[gap]` | `[tem]` | `[?]` | +| **Consent forms assinados** | `[gap]` | `[tem]` | `[?]` | +| **Assinatura eletrônica paciente** | `[?]` | `[tem]` | `[?]` | +| **Papel timbrado** | `[gap]` | `[tem]` | `[tem]` | +| **Intake / pacote onboarding** | `[parcial]` | `[tem]` | `[?]` | +| **Upload de arquivo pelo paciente** | `[?]` | `[tem]` | `[?]` | +| Cobrança / financeiro | `[tem]` | `[tem]` | `[tem]` | +| **Gateway de pagamento (Stripe/PIX)** | `[gap]` | `[tem]` | `[tem]` | +| **Cartão on file** | `[gap]` | `[tem]` | `[?]` | +| **Auto-billing recorrente** | `[parcial]` | `[tem]` | `[?]` | +| **Superbill / recibo detalhado** | `[parcial]` | `[tem]` | `[?]` | +| **NFSe emissão** | `[?]` | N/A (EUA) | `[tem]` | +| Convênios / Insurance | `[parcial]` | `[tem]` | `[?]` | +| **Claims eletrônico (TISS/837)** | `[gap]` | `[tem]` | `[gap]` | +| Lembretes Email/SMS/WA | `[tem]` | `[tem]` | `[tem]` | +| **Confirmação pelo paciente** | `[?]` | `[tem]` | `[?]` | +| Portal do paciente | `[parcial]` | `[tem]` * | `[tem]` | +| **Push notifications (portal)** | `[gap]` | `[tem]` * | `[?]` | +| **Portal multi-idioma (ES)** | `[gap]` | `[tem]` * | `[?]` | +| **Paciente paga fatura no portal** | `[gap]` | `[tem]` * | `[?]` | +| **App mobile paciente** | `[gap]` | `[tem]` * | `[?]` | +| Analytics / Relatórios | `[parcial]` | `[tem]` | `[tem]` | +| **Export PDF/Excel** | `[parcial]` | `[tem]` | `[?]` | +| Supervisão | `[parcial]` | `[tem]` | `[?]` | +| **Co-assinatura supervisor** | `[gap]` | `[tem]` | `[?]` | +| **Marketing / perfil público** | `[gap]` | `[tem]` | `[?]` | +| **Google Calendar sync** | `[gap]` | `[tem]` | `[?]` | +| **Multi-unidade / filiais** | `[gap]` | `[tem]` | `[?]` | +| **Especialidades (cadastro)** | `[gap]` | `[tem]` | `[?]` | +| **Tipo de registro (CRP/CRM)** | `[?]` | `[tem]` | `[tem]` | +| **Tasks / To-do por cliente** | `[gap]` | `[?]` | `[?]` | +| **E-prescribing** | N/A | `[tem]` | N/A | +| Multi-tenant SaaS | `[tem]` | `[tem]` | `[?]` | +| RLS por tenant | `[tem]` | N/A | `[?]` | +| **Credentialing grátis** (EUA) | N/A | `[tem]` * | N/A | +| **Migração assistida de outra plataforma** | `[gap]` | `[tem]` * | `[?]` | +| **HITRUST cert** | `[gap]` | `[tem]` * | `[?]` | +| **Compliance HIPAA/PCI** | N/A (LGPD) | `[tem]` * | N/A | + +--- + +## 9. Quem faz melhor — diferenciais por player + +Resumo punchy: cada concorrente tem um ponto onde está **claramente na frente** do que AgenciaPsi entrega hoje. Mapa pra você pensar em "copiar o melhor de cada um": + +### 🤖 Inteligência Artificial +- **Amplimed — Amélia Agendamento** 🥇 · bot 24/7 que agenda pelo WhatsApp sozinho +- **Amplimed — Amélia Transcrição** · áudio→texto no prontuário +- **Amplimed — Amélia Copilot** · resumo/pesquisa inteligente no prontuário +- **Jane App — AI Scribe** · grava sessão e gera nota em minutos +- **PsicoPlanner — PsiAssist** · gera relatórios/documentos com **compliance CFP** (diferencial BR) + +> Conclusão: IA é **a tendência dominante em 2026**. AgenciaPsi tem `[gap]` nos 3 casos (agendador, transcrição, resumo). Virar IA-first pode ser o **diferencial de posicionamento**. + +### 💬 WhatsApp / Engajamento +- **PsicoPlanner** 🥇 · agenda diária às 7h automática, rastreamento de leitura/resposta em tempo real +- **Amplimed** · WhatsApp Connect + Amélia como bot de atendimento +- **iClinic** · envio de prescrição direto pelo WhatsApp + +### 💰 Faturamento / Fiscal BR +- **Amplimed** 🥇 · "99% menos glosas" no TISS + NFS-e integrada + ICP-Brasil +- **iClinic** · assinatura digital com validade jurídica embutida +- **Ninsaúde Sign** · assinatura digital como produto satélite + +### 🎥 Teleconsulta +- **SimplePractice** 🥇 · vídeo grupo (15 pessoas), whiteboard digital, blurred BG, screen share +- **Jane App** · telehealth multi-cliente (até 12) +- **PsicoPlanner** · sala de vídeo integrada (solo, mas nativa) +- **Amplimed** · teleconsulta E2E + prescrição digital + +### 📋 Prontuário / Documentação +- **SimplePractice** 🥇 · biblioteca GAD-7/PHQ-9/BDI + consent forms assinados + Spanish portal +- **Jane App** · template library + SOAP/notes + AI Scribe +- **iClinic** · histórico em gráfico/tabela + CID-10 + vacinação + +### 💳 Pagamentos +- **Jane App** 🥇 · Jane Payments online + **terminal físico POS** +- **SimplePractice** · AutoPay + Superbills +- **Amplimed** · integrado ao prontuário + +### 🌐 Portal Paciente +- **SimplePractice** 🥇 · portal em EN+ES, self-scheduling, intake, questionnaires, invoice payment, messaging seguro +- **Jane App** · online booking + website builder com IA + +### 🏢 Multi-unidade / Rede +- **Jane App** 🥇 · multi-location + sala/equipamento como resources +- **SimplePractice** · Practice Details com Main Office definido + +### 🔒 Compliance & Segurança +- **Jane App** 🥇 · HIPAA + PIPEDA + GDPR + SOC-2 + 256-bit +- **SimplePractice** · HIPAA + HITRUST + PCI + BAA +- **Ninsaúde Safe** · produto de segurança dedicado + +### 🎁 Onboarding / Migração +- **Jane App** 🥇 · migração de dados **grátis** +- **SimplePractice** · "Switching Assistance" + Credentialing grátis + +### 🎯 Retenção / CRM +- **Ninsaúde CRM** 🥇 · produto dedicado +- **Amplimed** · redução 38% ausências via agenda inteligente +- **iClinic** · marketing médico (email campaigns) + +--- + +## 10. Gaps priorizados (primeira leitura) + +Agrupando os `[gap]` acima por tema e impacto-no-usuário: + +### 🔴 Críticos pro MVP (quick wins, dependência fraca) +1. **Especialidades** (lista + "outro") no cadastro da clínica/profissional +2. **Tipo de registro** (CRP/CRM) no cadastro do profissional +3. **Busca global de clientes** no topbar +4. **Recently viewed** (últimos pacientes acessados) +5. **Papel timbrado** (portar do UniaoApp quando chegar a hora) + +### 🟡 Alto valor, esforço médio +6. **Biblioteca de instrumentos de avaliação** (GAD-7, PHQ-9, BDI, BAI, DASS-21) com scoring automático — **diferencial forte no BR** +7. **Biblioteca de termos de consentimento** (TCLE, Telehealth, LGPD) — editáveis +8. **Assinatura eletrônica** no portal do paciente +9. **Templates de nota de sessão** (SOAP, DAP, BIRP ou equivalente BR) +10. **Auto-billing recorrente** (cobrança automática baseada em agenda) + +### 🔵 Grandes (esforço alto, dependências) +11. **Gateway de pagamento integrado** (Stripe/Asaas/Iugu) — destrava cartão on file, auto-billing, superbill +12. **Multi-unidade / filiais** com unidade principal +13. **Teleconsulta integrada** (video próprio ou Daily/Jitsi) +14. **Google Calendar 2-way sync** +15. **Co-assinatura de supervisor** +16. **Claims eletrônico TISS** (convênios BR — mercado nichado mas alto valor pra clínicas grandes) + +### ⚪ Diferenciação / marketing +17. **Perfil público / diretório de profissionais** +18. **App mobile** (PWA talvez seja suficiente) +19. **NFSe automatizada** (se for posicionar contra Amplimed/iClinic) +20. **Website builder** pra clínica (Jane App faz com IA) +21. **Terminal POS físico** pra pagamento presencial + +### 🤖 IA (novo balde — tendência forte 2026) +22. **Bot WhatsApp que agenda sozinho** — equivalente Amélia Agendamento da Amplimed (ROI claro: 24/7 sem recepcionista) +23. **Transcrição de sessão áudio→texto** — equivalente Amélia Transcrição ou AI Scribe do Jane +24. **Copilot no prontuário** — resumo da história clínica do paciente + busca semântica +25. **Gerador de documentos** (relatórios, laudos) com **compliance CFP** — equivalente PsiAssist do PsicoPlanner + +### 🧾 Fiscal / Jurídico BR (diferencial local) +26. **Assinatura digital ICP-Brasil** (Amplimed, iClinic têm) +27. **NFS-e integrada** com dados da consulta +28. **Faturamento TISS automatizado** (nichado mas essencial pra clínicas grandes) +29. **Nome social** no cadastro do paciente (Ninsaúde tem) — LGPD + CFP + +--- + +## 11. Próximos passos + +### Feito em 2026-04-17 +- [x] Estrutura do doc criada e notas do usuário organizadas +- [x] **Primeira rodada de WebFetch** (SP + Psicomanager) + - ✅ SP `/features/` — categorias top-level + compliance + - ✅ SP `/features/telehealth/` — módulo completo extraído + - ✅ SP `/features/client-portal/` — módulo completo extraído + - ✅ SP `/pricing/` — escala (20M clientes, 250k practitioners) + trials + - ❌ SP `/features/scheduling|billing|documentation/` — 404 (URLs mudaram) + - ❌ Psicomanager — site é SPA JS, scraping não retorna conteúdo +- [x] **Segunda rodada — varredura de concorrentes BR + internacional** + - ✅ **iClinic** (`/funcionalidades/` + `/`) — Prontuário + Prescrição + Documentos + Ferramentas + - ✅ **Amplimed** (`/`) — **AI-first** (Amélia Agendamento/Transcrição/Copilot) + TISS + NFS-e + ICP + - ✅ **PsicoPlanner** (`/`) — concorrente direto: WhatsApp + PsiAssist (IA com CFP) + preços + - ✅ **Jane App** (`/features`) — referência internacional de UX + AI Scribe + Jane Payments + - ✅ **Ninsaúde** (`/`) — ERP clínico + CRM + Safe + Sign + Nome Social + - ❌ Feegow — SSL handshake falhou + - ❌ Mapps (mapps.com.br, mappsbr.com.br) — DNS não resolve + - ❌ TheraNest (`/features/`) — SSL handshake falhou + - ❌ Doctoralia (`/empresa/produtos`) — 404 +- [x] Seções 3-7 adicionadas ao doc com features extraídas +- [x] Seção 9 "Quem faz melhor" criada — diferenciais por player +- [x] Gaps priorizados expandidos (+12 itens novos, com balde 🤖 IA e 🧾 Fiscal BR) + +### Pendente +- [ ] **Validar este doc** — você lê e marca o que está certo/errado/incompleto +- [ ] **SimplePractice restante** (alta prioridade): Documentation/Notes, Billing & Insurance, Analytics, Supervision + - Estratégia: você navega logado/trial e me manda prints — eu transcrevo pro doc +- [ ] **Psicomanager completo** — impossível via WebFetch, precisa de: + - Prints das telas principais, ou + - Reviews em B2BStack / Capterra BR / SoftwareSuggest, ou + - Trial pra navegar junto +- [ ] **Decidir fases** — transformar a lista de gaps em roadmap v1/v2/v3 +- [ ] **Atualizar `mapa-sistema.html`** marcando os itens `missing` conforme formos confirmando gaps + +--- + +## Rascunho original do usuário (17/04/2026) + +> *Preservado aqui pra não perder nenhuma observação. Tudo acima foi organizado a partir destas notas.* + +``` +SIMPLE PRATICTICE +CLIENTES +PAGAMENTOS +INSURENCE +ANALYTICS +ATIVIDADES +SUPERVISION +SETTINGS +REMINDERS +REQUESTS +MARKETING +RECENTLY VIEWS +BUSCA NO TOPBAR POR CLIENTES + +ESTOU VENDO A TELA DE CLINICAL INFO. PARA OS EUA, TEM NPI NUMBER, TAXONOMY CODE E SPECIALITY, +NOS JA TEMOS UM CADASTRO DE CLINICA COMPLETO PARA O BRASIL. ACHO QUE PODERIAMOS COLOCAR O CAMPO +ESPECIALIDADES TBM, LISTAR UM MONTE E SE O USUARIO NAO ENCONTRAR, CADASTRAR O SEU. + +TBM TEM UM CAMPO LICENSES TYPE, ONDE O USUARIO SELECIONA SE É MEDICO, PHD, ... +(NÃO SEI SE ISSO SERIA AKI). + +AI TEM UMA SESSAO CHAMADA PRACTICE DETAILS ONDE CADASTRA A LOCALIZACAO DA CLINICA +E DIGAMOS FILIAIS (NO CASO DE GESTAO DE CLINICAS). É POSSIVEL DEFINIR QUAL É O MAIN OFFICE. + +EM OUTRA ABA ACHAMADA CLIENT CARE, TEM O TEMPLATE LIBRARY, QUE TEM VARIADOS TIPOS +DE ARQUIVOS PRONTOS PARA PERSONALIZAR. E INLUSIVE MONTAR O TEMPLATE. +EX. GAD-7 (GENERALIZED ANXIETY DISORDER), PHQ-9 (PATIENT HEALTH QUESTIONAIRE), +COVID-19 PRE-APPOINTMENT SCREENING QUESTIONAIRE. + +TBM TEM UMA OUTRA SESSAO CHAMADA SHARABLE DOCUMENTS: A SESSAO DIZ +MANAGE DEFAULT INTAKE DOCUMENTS AND UPLOADS FILES. + +CONSENT FORMS: READ AN SIGNED BY CLIENTS VIA THA CLIENT PORTAL +LEGAL DISCLAIMER: CONSENT FORMS ARE FOR REFERENCE ONLY. +ITS YOUR RESPONSIBILITY TO CUSTOMIZE THEM AND ENSURE THEY MEET +THE LEGAL REQUERIMENTS OF YOUR STATE. + +- CONSENT FOR TELEHEALTH CONSULTATION +- CREDIT CARD AUTORIZATION +- NOTICE OF PRIVACY PRATICES +- INFORMED CONSENT FOR PSYCOTHERAPY +- PRACTICE POLICIES +- RELEASE FORMS +- CONSENT TO RECORD AUDIO +- + NEW CONSENT FORM + +O USUARIO CONSEGUE ADICIONAR, EDITAR UM NOVO E VISUALIZAR. + +PRECISA CRIAR A PARTE DE PAPEL TIMBRADO, ASSIM COMO FIZEMOS NO OUTRO PROJETO, +QUANDO CHEGAR A HORA VC PODE ME PERGUNTAR. +``` diff --git a/development/04-roadmap/ROADMAP.md b/development/04-roadmap/ROADMAP.md new file mode 100644 index 0000000..c88dedb --- /dev/null +++ b/development/04-roadmap/ROADMAP.md @@ -0,0 +1,278 @@ +# AgenciaPsi — Roadmap em Fases + +> Proposta derivada de `ESTRUTURA.md` (o que já existe) + `concorrentes.md` (o que o mercado tem). +> Data: 2026-04-17 +> **Decisões estratégicas** abertas pra validação estão marcadas com ⚠️ + +--- + +## Posicionamento recomendado (proposta) + +Olhando o estado atual e o terreno competitivo, recomendo: + +> **"Psicomanager moderno + IA"** — competir diretamente no nicho de psicologia BR (onde já temos 75-80% pronto), ganhar por **UX moderna** (Vue 3 / PrimeVue / dark mode) e **IA nativa** (onde Psicomanager/PsicoPlanner ainda estão correndo atrás). Pricing entry baixo (tipo PsicoPlanner R$59-79/mês solo) + tier clínica pra monetizar rede. + +**Por quê esse corte:** +- 🎯 **Foco:** psico-first (e não generalista como iClinic/Amplimed) — o diferencial é conhecer profundamente o fluxo CFP +- 🎯 **Competitividade:** IA é o campo aberto — Amplimed (Amélia) e Jane (AI Scribe) já estão lá; ainda dá pra chegar +- 🎯 **Custo:** solo-dev → priorizar features de **alto ROI por esforço**; deixar TISS/hospital-grade pra depois +- 🎯 **UX:** PrimeVue/Tailwind dá dignidade visual que Psicomanager ainda não tem + +⚠️ **Decisão aberta:** se você quer competir por preço-baixo (PsicoPlanner) ou por valor-alto (Jane App), o roadmap muda. Supus mid-market aqui. + +--- + +## Legenda de esforço +- **S** — <1 semana (quick win) +- **M** — 1-3 semanas +- **L** — 3-8 semanas +- **XL** — 2+ meses (feature grande, múltiplas deps) + +## Legenda de prioridade +- 🔴 **Bloqueador** — sem isso, não rola lançar a fase +- 🟠 **Alta** — cria valor imediato pro usuário, reduz churn +- 🟡 **Média** — completa a paridade, não é urgente +- 🔵 **Diferencial** — aposta de posicionamento + +--- + +# 🚀 FASE 1 — MVP Launch + +**Objetivo:** ter um produto cobrável, confiável, completo o suficiente pra um terapeuta solo trocar Psicomanager/PsicoPlanner pelo AgenciaPsi. +**Timeline sugerida:** 4-6 semanas +**Critério de saída:** 5 usuários pagantes em produção sem churn por "falta de feature básica". + +## 1.1 Monetização (bloqueador total) 🔴 + +Você **não consegue cobrar** os próprios clientes nem as assinaturas SaaS sem isso. + +| # | Feature | Esforço | Notas | +|---|---|---|---| +| 1 | **Integração com gateway de pagamento** (Asaas ou Iugu recomendado pro BR — PIX + cartão + boleto nativos) | L | Asaas é o mais barato pra começar; Iugu tem API melhor. Escolha 1. Stripe só se for internacional. | +| 2 | **Cartão on file** (tokenização via gateway) | M | Desdobra de #1 | +| 3 | **Auto-billing recorrente** (baseado na agenda) | M | Trigger: sessão realizada → gera fatura → cobra automaticamente | +| 4 | **Cobrança das próprias assinaturas SaaS** (tenants pagam pelo plano) | M | Aproveita estrutura de `subscriptions` que já existe | + +## 1.2 Compliance básico BR 🔴 + +Não dá pra lançar sem isso ou você pega processo em 6 meses. + +| # | Feature | Esforço | Notas | +|---|---|---|---| +| 5 | **Tipo de registro profissional** (CRP, CRM, CRFa, RMS…) — campo obrigatório no cadastro | S | Aparece em recibos/laudos | +| 6 | **Biblioteca de consent forms editáveis** (TCLE, Telehealth, LGPD, Gravação, TCLE menores) | M | Templates pré-prontos; profissional customiza | +| 7 | **Assinatura eletrônica pelo paciente no portal** (simples, com IP+timestamp) | M | Não precisa ICP-Brasil nessa fase | +| 8 | **Nome social** no cadastro (além do nome de registro) — CFP exige | S | Campo adicional; aparece em todas as telas voltadas ao paciente | +| 9 | **Especialidades** no cadastro do profissional (lista + "outra") | S | DB: tabela `specialties` + FK em `profiles` | + +## 1.3 UX mínima esperada 🟠 + +Todo concorrente tem — usuário novo estranha se não tiver. + +| # | Feature | Esforço | Notas | +|---|---|---|---| +| 10 | **Busca global no topbar** (paciente, email, telefone) | S | Autocomplete com debounce + highlight | +| 11 | **Recently viewed** (últimos 5 pacientes acessados, por usuário) | S | localStorage ou tabela `user_recent_access` | +| 12 | **Papel timbrado** (portar do UniaoApp como você mencionou) | M | Já existe noutro projeto — só adaptar | +| 13 | **Relatórios com export PDF/Excel** (já tem estrutura, fechar) | M | Fechar os gaps que o MVP assessment apontou | + +## 1.4 Fiscal mínimo 🟠 + +Pra o terapeuta conseguir operar. + +| # | Feature | Esforço | Notas | +|---|---|---|---| +| 14 | **Recibo profissional** (PDF gerado, com dados CFP) | S | Já tem base de documentos — só formalizar template | +| 15 | **NFS-e emissão** (integração Focus NF-e ou NFS-e direto da prefeitura) | L | Pode ficar pra 1.5 se apertar prazo; faz diferença pro profissional se diferenciar | + +## 1.5 Qualidade pra lançar 🟠 + +| # | Feature | Esforço | Notas | +|---|---|---|---| +| 16 | **Testes E2E dos fluxos críticos** (cadastro, login, agendamento, cobrança, prontuário) | L | Playwright ou Cypress; prioridade: compra + agendamento | +| 17 | **Responsividade mobile validada** (terapeuta consulta agenda no celular) | M | Auditoria + fix de breakpoints | +| 18 | **Monitoramento de produção** (Sentry + Supabase dashboards) | S | Detectar bugs antes do usuário reclamar | + +### 📦 Entregas da Fase 1 +- **Produto cobrável** (gateway integrado) +- **Legal safe** (consent forms, nome social, especialidades) +- **UX polida** (busca, papel timbrado, relatórios) +- **Testado** (E2E crítico + mobile) + +### ⚠️ Decisões abertas +- **Gateway:** Asaas (barato) vs Iugu (melhor DX) vs Stripe (internacional depois) — recomendo **Asaas** pra começar rápido +- **NFS-e:** incluir na 1.4 ou empurrar pra 2? Recomendo **incluir** — é quick win e diferencia de Psicomanager +- **Pricing:** qual é o seu valor? (R$59 solo como PsicoPlanner? R$99? R$149?) — afeta quanto time você tem pra chegar nas próximas fases + +--- + +# 🏗️ FASE 2 — Paridade Competitiva + +**Objetivo:** qualquer usuário avaliando AgenciaPsi × Psicomanager × PsicoPlanner deve ver **paridade ou mais** de features. Nenhum "ah, mas lá tem X" válido. +**Timeline sugerida:** 2-3 meses após Fase 1 +**Critério de saída:** feature checklist empatada com top-3 concorrentes nichados. + +## 2.1 Comunicação / Engajamento (onde PsicoPlanner ganha hoje) 🟠 + +| # | Feature | Esforço | Notas | +|---|---|---|---| +| 19 | **Agenda diária automática 7h via WhatsApp** | S | Cron job + template; feature signature do PsicoPlanner | +| 20 | **Confirmação de presença pelo paciente** ("responder SIM") | M | Parser de resposta WhatsApp/SMS → atualiza status | +| 21 | **Rastreamento de engajamento em tempo real** (recebeu/leu/respondeu) | M | Webhooks da Evolution API + dashboard no topbar | +| 22 | **Envio de prescrição/documento via WhatsApp direto** | S | Botão "Enviar via WA" já gerando link temporário | + +## 2.2 Prontuário (onde iClinic ganha hoje) 🟠 + +| # | Feature | Esforço | Notas | +|---|---|---|---| +| 23 | **Templates de nota** (SOAP / DAP / BIRP / evolução livre CFP-style) | M | Biblioteca com templates selecionáveis | +| 24 | **Biblioteca de instrumentos de avaliação** (GAD-7, PHQ-9, BDI, BAI, DASS-21, SRQ-20) com scoring automático | L | Cada escala tem perguntas + score calculado + gráfico de evolução | +| 25 | **Histórico em gráfico/tabela** (evolução de escalas ao longo do tempo) | M | Chart.js já no projeto | +| 26 | **Versionamento de notas** (auditoria de alterações) | M | Log de alterações + diff visual | + +## 2.3 Intake / Onboarding do paciente 🟡 + +| # | Feature | Esforço | Notas | +|---|---|---|---| +| 27 | **Pacote de intake** (formulários + anamnese + consent forms enviados pré-1ª-sessão) | M | Fluxo: terapeuta monta pacote → paciente recebe link → preenche → assina → terapeuta vê tudo | +| 28 | **Upload de arquivo pelo paciente** (exames, relatórios externos) | S | Storage bucket já configurado | + +## 2.4 Agenda / Integrações 🟡 + +| # | Feature | Esforço | Notas | +|---|---|---|---| +| 29 | **Google Calendar 2-way sync** | L | API OAuth + conflict resolution | +| 30 | **iCal feed** (leitura por Apple Calendar / Outlook) | S | Endpoint que serve .ics | + +## 2.5 Fiscal avançado BR (onde Amplimed/iClinic ganham) 🟡 + +| # | Feature | Esforço | Notas | +|---|---|---|---| +| 31 | **Assinatura digital ICP-Brasil** (laudos com validade jurídica) | L | Integração com ValidCertificadora ou similar | +| 32 | **Faturamento TISS básico** (pra clínicas que têm convênio) | XL | Nichado — só se você quiser ir pra tier clínica/rede | + +## 2.6 Marketing / Presença 🟡 + +| # | Feature | Esforço | Notas | +|---|---|---|---| +| 33 | **Perfil público do terapeuta** (página /p/ com bio, horários, agendamento) | M | Já tem agendador público — só enriquecer | +| 34 | **SEO básico** (schema.org/MedicalBusiness + meta tags) | S | Melhora descoberta orgânica | + +### 📦 Entregas da Fase 2 +- Paridade completa com Psicomanager/PsicoPlanner/iClinic +- **Sem furo comparativo** em review de feature +- Começo de presença pública (perfil + SEO) + +### ⚠️ Decisões abertas +- **TISS (item 32):** grande esforço por nichado — só faz sentido se você for atrás de mercado de clínica com convênio. **Recomendo não fazer** nesta fase. +- **ICP-Brasil (item 31):** pesa? Depende do público. Terapeuta solo raramente precisa. Clínica com laudo pericial, sim. **Recomendo empurrar pra Fase 3** se o MVP foi pra solo. + +--- + +# 🧠 FASE 3 — Diferenciação (IA-first) + +**Objetivo:** ter 2-3 features que **nenhum concorrente BR tem em paridade**. Vira marketing: "o sistema com IA pra psicólogos". +**Timeline sugerida:** 3-6 meses após Fase 2 +**Critério de saída:** você consegue justificar um **tier premium 2x mais caro** que o básico. + +## 3.1 IA — Onde compensa correr 🔵 + +O time de IA geral (Claude, GPT) tá commoditizado — vantagem vai pra quem **integra bem** ao workflow do usuário. + +| # | Feature | Esforço | Notas | +|---|---|---|---| +| 35 | **Bot WhatsApp que agenda sozinho** (equivalente Amélia Agendamento) | XL | Stack: Evolution API + LLM + RAG na disponibilidade da agenda. ROI claro: **24/7 sem recepcionista** | +| 36 | **Transcrição de sessão áudio→texto** (equivalente AI Scribe do Jane + Amélia Transcrição) | L | Whisper API local ou Deepgram; paciente consente; transcrição vira rascunho de nota | +| 37 | **Copilot no prontuário** (resumir histórico, sugerir diagnóstico diferencial baseado em notas anteriores, buscar semântica) | L | RAG em cima das notas do paciente | +| 38 | **Gerador de documentos com compliance CFP** (equivalente PsiAssist) | M | LLM com system prompt de CFP + templates | + +## 3.2 Teleconsulta nativa 🔵 + +Se o público-alvo inclui teleatendimento (grande chance pós-pandemia), isso é essencial. + +| # | Feature | Esforço | Notas | +|---|---|---|---| +| 39 | **Vídeo nativo integrado** (Daily.co ou Jitsi Meet) | L | Sala gerada por consulta + link no lembrete | +| 40 | **Sala de espera virtual** | M | Profissional admite paciente | +| 41 | **Whiteboard digital + screen share** | M | Daily.co tem nativo | + +## 3.3 Rede / Multi-unidade (se posicionar pra clínicas) 🔵 + +Só fazer se posicionar pra tier **enterprise**. Solo-therapist não precisa. + +| # | Feature | Esforço | Notas | +|---|---|---|---| +| 42 | **Multi-unidade / filiais** com Main Office | L | Tabela `clinic_units` + FK em consultas/profissionais | +| 43 | **Salas e equipamentos como recursos** (estilo Jane App) | M | Evita double-booking | +| 44 | **CRM de leads** (captura de landing → funil → matching com terapeuta) | L | Aproveita o perfil público da Fase 2 | +| 45 | **BI avançado** (MRR, cohort retention, LTV por terapeuta) | M | Dashboards dedicados | + +## 3.4 UX premium / diferenciação fina 🔵 + +| # | Feature | Esforço | Notas | +|---|---|---|---| +| 46 | **Website builder pra clínica** (estilo Jane) — sem precisar de Wix/WordPress | XL | Grande mas ROI de marketing enorme | +| 47 | **App mobile (PWA otimizado)** — paciente instala no celular | M | Vue já dá PWA grátis, só polir | +| 48 | **Migração assistida de Psicomanager** (importador de CSV/API) | M | Feature de venda: "deixa o outro, a gente migra" | + +### 📦 Entregas da Fase 3 +- **IA-nativa** posicionamento (bot WhatsApp, transcrição, copilot) — Amplimed/Jane nível +- **Teleconsulta própria** (se optar) +- Opcional: tier **enterprise** com multi-unidade +- **Website builder** como moat de marketing + +### ⚠️ Decisões abertas +- **Foco único ou múltiplas apostas?** Recomendo: **só IA (3.1)** no primeiro semestre da Fase 3. Teleconsulta e Multi-unidade podem esperar Fase 4. +- **Custo de IA:** transcrição (Whisper) + LLM (Claude/GPT) tem custo por uso — precisa embutir no pricing ou cobrar add-on + +--- + +# ❌ O que ficou FORA do roadmap (propositalmente) + +Decisões de "não fazer" são tão importantes quanto as de fazer: + +- **Prescrição eletrônica de medicamentos** — só psiquiatras usam; mercado nichado demais +- **Marketplace de terapeutas público** (tipo Doctoralia) — é um negócio completamente diferente; seu negócio é gestão, não captação +- **E-prescribing com farmácia integrada** — idem, só pra psiquiatra +- **Controle de estoque** (Ninsaúde/iClinic têm) — irrelevante pra psicólogo +- **Integração com laboratórios** — irrelevante +- **Controle de vacinas** (iClinic tem) — irrelevante +- **Multi-idioma** (SimplePractice tem espanhol) — empurrar pra Fase 4+ quando for pensar LATAM +- **Certificação HIPAA/HITRUST** — caríssima; LGPD suficiente pra BR +- **App iOS/Android nativo** — PWA resolve 90% do caso; nativo só se houver demanda explícita + +--- + +# 📊 Resumo em tabela + +| Fase | Foco | Duração | Features | Esforço total | +|---|---|---|---|---| +| **1 — MVP** | Cobrável + legal safe + UX básica | 4-6 semanas | 18 itens | ~3M de backlog | +| **2 — Paridade** | Empatar com competição BR | 2-3 meses | 16 itens | ~4L de backlog | +| **3 — Diferenciação IA** | Virar "o SaaS com IA pra psico" | 3-6 meses | 14 itens | ~5XL de backlog | + +--- + +# 🎯 Decisões estratégicas pendentes (pra você) + +Antes de começar, definir: + +1. **Pricing** — R$59, R$99 ou R$149 solo? Afeta quanto tempo você tem pra chegar na Fase 2. +2. **Tier clínica** — vai existir desde o MVP ou só na Fase 3? Afeta item 32 (TISS) e 42 (multi-unidade). +3. **Gateway de pagamento** — Asaas (barato) vs Iugu (melhor DX)? Escolher agora. +4. **IA própria ou terceirizada?** — chamar API da Anthropic/OpenAI (rápido, custo variável) ou rodar modelos open-source localmente (lento, custo fixo)? **Recomendo API** pra começar. +5. **Foco na Fase 3** — IA-first, teleconsulta-first, enterprise-first? Você só tem tempo pra **uma** dessas apostas no primeiro semestre. +6. **Data de lançamento do MVP** — se a meta é 2026-06-01, Fase 1 tem que terminar mid-maio. Planejar capacity. + +--- + +# 📎 Referências cruzadas + +- **`ESTRUTURA.md`** — o que já existe no sistema (79 features construídas) +- **`concorrentes.md`** — benchmark de 7 players (SP, Psicomanager, iClinic, Amplimed, PsicoPlanner, Ninsaúde, Jane) +- **`mapa-sistema.html`** — visualização interativa do que está feito +- **`memory/project_mvp_assessment.md`** (25/03/2026) — gaps críticos MVP-assessment original + +--- + +**Próximo passo sugerido:** você lê, contesta o que discorda, responde as 6 decisões pendentes lá em cima, e a gente passa pra **backlog executável** da Fase 1 (breakdown task-por-task do que você vai fazer esta semana / próxima). diff --git a/development/05-database/README.md b/development/05-database/README.md new file mode 100644 index 0000000..d1febb4 --- /dev/null +++ b/development/05-database/README.md @@ -0,0 +1,33 @@ +# Database — ferramentas + +As ferramentas de banco vivem em [`../../database-novo/`](../../database-novo/) na raiz do projeto. Esta pasta existe apenas como **index** de referência pra navegação. + +## Scripts principais + +### `database-novo/db.cjs` +CLI completa do banco. Comandos: +- `setup` — instalação do zero (schema + fixes + seeds + migrations) + auto-backup + verify +- `backup` — exporta `schema.sql`, `data.sql`, `full_dump.sql`, `supabase_restore.sql` (restauração completa) +- `restore [data]` — restaura do backup mais recente (ou de uma data específica) +- `migrate` — aplica migrations pendentes com auto-backup antes +- `seed [grupo]` — roda seeds (`all` / `users` / `system` / `test_data`) +- `status` — estado do banco + contagens em tabelas-chave +- `diff` — compara schema atual vs último backup +- `reset` — dropa schema public e reinstala tudo (com safety backup) +- `verify` — checa integridade (tabelas e views definidas em `db.config.json`) +- `schema-export` — gera `schema/00_full`…`10_grants/` granulares +- `dashboard` — gera `agenciapsi-db-dashboard.html` interativo + +### `database-novo/generate-dashboard.cjs` +Gera dashboard HTML do banco lendo schema do backup mais recente. Lê config de `db.config.json` (domínios, cores, infraestrutura). + +## Config + +`database-novo/db.config.json` — domínios (11), cores, infraestrutura (6 grupos × 23 itens), contagens esperadas pra verify. + +## Na UI + +A página `/saas/desenvolvimento > Banco de Dados` vai: +- Exibir os comandos com botão "copiar" (executar no terminal) +- Mostrar status dos últimos runs (lido de `dev_generation_log`) +- Link pro dashboard HTML gerado (abre em nova aba) diff --git a/development/PENDENTE.md b/development/PENDENTE.md new file mode 100644 index 0000000..7e61fda --- /dev/null +++ b/development/PENDENTE.md @@ -0,0 +1,170 @@ +# Development — Pendências + +> Registro vivo do que ficou pra próximas etapas da área de desenvolvimento. +> **Nada aqui pode se perder** — cada item vira task quando for tratado. + +**Última atualização:** 2026-04-17 +**Responsável:** Leonardo (user) · **Implementação:** Claude (dev sessions) + +--- + +## ✅ Entregue até agora + +### Parte A — Banco + estrutura (2026-04-17) +- [x] Pasta `development/` criada com estrutura hierárquica (`01-visao-geral`…`05-database`) +- [x] 6 arquivos `.md`/`.txt`/`.html` movidos da raiz pra subpastas temáticas +- [x] `development/README.md` de navegação +- [x] Migration `20260417000001_dev_tables.sql` aplicada com 8 tabelas (`dev_*`) + RLS via `is_saas_admin()` +- [x] 3 seeds executados: + - `seed_030_dev_phases_items.sql` → 3 fases + 48 items + - `seed_031_dev_auditoria.sql` → 14 bugs/débitos (12 resolvidos + 2 abertos) + - `seed_032_dev_competitors.sql` → 7 concorrentes + 74 features + 60 linhas de matriz + +### Parte B — UI read-only (2026-04-17) +- [x] Rota `/saas/desenvolvimento` registrada em `routes.saas.js` +- [x] Item de menu "Desenvolvimento > Área de Dev" em `saas.menu.js` +- [x] `SaasDevelopmentPage.vue` com TabView de 7 abas +- [x] 7 sub-componentes (read-only): + - `DevOverviewTab.vue` — stats + progresso das fases + últimas gerações + - `DevRoadmapTab.vue` — fases, items, filtros por status/prioridade/bloco + - `DevEstruturaTab.vue` — ESTRUTURA.md embutido + iframe do mapa-sistema.html + - `DevAuditoriaTab.vue` — bugs/débitos expansíveis, filtros por status/severidade + - `DevCompetitorsTab.vue` — concorrentes + features agrupadas por categoria + matriz de gaps + - `DevDatabaseTab.vue` — comandos do db.cjs com copy-to-clipboard + log de execuções + - `DevExportTab.vue` — stubs de "Gerar ROADMAP.md/AUDITORIA.md/concorrentes.md/ESTRUTURA.md" (desabilitados, mensagem "Pendente — Parte C") + +--- + +## 🔨 Parte C — Edição + integração CLI + +### C.1 Edição inline na UI ✅ **CONCLUÍDA (2026-04-17)** + +Banco agora é source-of-truth. Implementado: + +**Infra reutilizável:** +- [x] `components/DevDrawer.vue` — drawer lateral com footer (save/cancel/delete) +- [x] `components/DevField.vue` — wrapper de label/input padronizado +- [x] `composables/useDraggableList.js` — drag-drop HTML5 nativo (sem deps) +- [x] Migration `20260417000002_dev_tables_ordem.sql` — coluna `ordem` em `dev_auditoria_items` e `dev_competitor_features` + +**Roadmap (`DevRoadmapTab.vue`)** +- [x] Drawer pra editar item (todos os campos) +- [x] Drag & drop pra reordenar items dentro de um bloco (via `BlocoItems.vue`) +- [x] Mover item entre fases (via select no drawer) +- [x] Criar novo item +- [x] Excluir item (com confirm) +- [x] Criar nova fase (botão "+" no seletor) +- [x] Editar fase (nome, objetivo, timeline, status, datas) +- [x] Excluir fase (cascade nos items) + +**Auditoria (`DevAuditoriaTab.vue`)** +- [x] Drawer com todos os campos (título, categoria, severidade, status, problema, solução, arquivo, tags, datas) +- [x] Criar/editar/excluir +- [x] Drag-reorder (desativado quando filtro ativo) + +**Concorrentes (`DevCompetitorsTab.vue`)** +- [x] CRUD de concorrente (slug, nome, país, foco, pricing, posicionamento, url, notas, ativo) +- [x] CRUD de feature por concorrente (categoria, nome, descrição, fonte, url, destaque) +- [x] CRUD de linha da matriz (domínio, feature, status, nota, importância) +- [x] **Edição inline de status na matriz** (dropdown colorido) + +**Pendências menores que ficaram:** +- [ ] Drag-reorder no overview das fases +- [ ] Drag-reorder na matriz de comparação +- [ ] Drag-reorder nas features dentro de um concorrente +- [ ] Drag-reorder do topo da lista de concorrentes +- [ ] Autosave nos drawers (hoje é "clique em Salvar") +- [ ] Undo recente +- [ ] Keyboard shortcuts (`/` busca, `n` novo, `Esc` fecha) +- [ ] Mudança rápida de status direto no badge do card (sem abrir drawer) +- [ ] Marcar fase como concluída automaticamente quando 100% items concluídos +- [ ] Quick filters no overview (só bloqueadores, só Fase 1, etc.) + +### C.2 Integração CLI local (opcional — usuário disse "copy-to-clipboard por ora") + +Hoje todos os comandos do `DevDatabaseTab.vue` são copy-to-clipboard. Na próxima onda (se for quiser): + +- [ ] Pequeno server Node em `development/cli-server/server.cjs` + - Escuta em `127.0.0.1:3456` (localhost-only) + - Whitelist de comandos: `backup`, `dashboard`, `schema-export`, `status`, `verify`, `migrate`, `seed`, `diff` + - Endpoints: `POST /run/:cmd` com SSE pra streaming do stdout + - Escreve em `dev_generation_log` o resultado +- [ ] Script `npm run dev:cli` pra iniciar o server +- [ ] Indicador na UI (verde/vermelho) mostrando se server está online +- [ ] Botões "Executar agora" ao lado de cada comando (alternativo ao copy) + +### C.3 Export banco → .md (botões da aba "Exportar") + +- [ ] Edge Function `generate-roadmap-md` que lê `dev_roadmap_phases` + `dev_roadmap_items` e retorna markdown +- [ ] Edge Function `generate-auditoria-md` análogo +- [ ] Edge Function `generate-concorrentes-md` análogo +- [ ] Na UI: botão clica → fetch da edge function → `` do resultado +- [ ] OU: endpoint no CLI server escreve direto em `development/04-roadmap/ROADMAP.md` (se CLI server existir) +- [ ] Versionar os arquivos gerados no git (script "publish": gera tudo + `git add development/`) + +### C.4 Pesquisa automática de concorrentes + +O usuário **disse "não sei"** sobre essa feature. Quando for decidir: + +- [ ] Edge Function `fetch-competitor-page` — recebe URL, faz `fetch()` do HTML, retorna raw +- [ ] UI: botão "Pesquisar este concorrente" abre modal com: + - Input de URL + - Preview do HTML capturado + - Campo de texto livre pra colar observações + - Opção: "extrair com IA" (chama Anthropic API com system prompt pra listar features) +- [ ] Features extraídas entram numa fila de revisão (usuário aprova → salva em `dev_competitor_features`) +- [ ] Histórico: `dev_competitor_features` ganha campos `aprovado_em`, `aprovado_por`, `revisao_status` + +### C.5 Melhorias secundárias + +- [ ] **Autosave** nos drawers de edição (debounce 1s) +- [ ] **Toast de feedback** em toda mutação +- [ ] **Undo recente** (Ctrl+Z nos últimos 5 minutos via revision log) +- [ ] **Keyboard shortcuts** — `/` foca busca, `n` novo item, `Esc` fecha drawer +- [ ] **Dark mode** — se o app tem, garantir compatibilidade (surface tokens estão OK mas validar) +- [ ] **Responsividade mobile** — tabs scroll horizontal já tratado, mas drawers precisam virar modals full-screen em mobile +- [ ] **Paginação/virtualização** nas listas grandes (quando matriz passar de 100 linhas) + +--- + +## 📋 Itens órfãos / decisões estratégicas + +### Decisões **abertas** (do `ROADMAP.md`) +Repetidas aqui pra não perder: + +1. **Pricing** — R$ 59, R$ 99 ou R$ 149 solo? Afeta quanto tempo você tem pra Fase 2. +2. **Tier clínica** — existe desde o MVP ou só na Fase 3? Afeta TISS (item 32) e multi-unidade (42). +3. **Gateway de pagamento** — Asaas (barato) vs Iugu (melhor DX)? Escolher agora. +4. **IA própria ou terceirizada?** — Claude/OpenAI via API (rápido, custo variável) vs open-source local (lento, custo fixo)? Recomendo API pra começar. +5. **Foco da Fase 3** — IA-first, teleconsulta-first ou enterprise-first? Capacity só dá pra uma aposta no primeiro semestre. +6. **Data de lançamento MVP** — se meta é 2026-06-01, Fase 1 termina mid-maio. + +### Itens técnicos **abertos** do `AUDITORIA.md` +Ainda não resolvidos (já no banco como `dev_auditoria_items` com status `aberto` / `em_analise`): + +- `window.__guardsBound / window.__supabaseAuthListenerBound` — flags globais no window (anti-pattern) +- Arquivos obsoletos no projeto (cleanup parcial — a finalizar) + +--- + +## 🎯 Ordem sugerida pra Parte C + +Se quiser atacar **agora**: + +1. **C.1 roadmap** — drawer de edição (mais valor imediato — marcar items concluídos) +2. **C.1 auditoria** — mesmo padrão do roadmap +3. **C.3 export** — ROADMAP.md e AUDITORIA.md (para versionar o que você editou) +4. **C.1 concorrentes** — edição completa +5. **C.2 CLI server** — quando cansar de copy-paste +6. **C.4 pesquisa auto** — se decidir que vale + +--- + +## 📁 Referências + +- Migração: `database-novo/migrations/20260417000001_dev_tables.sql` +- Seeds: `database-novo/seeds/seed_030_dev_phases_items.sql`, `seed_031_dev_auditoria.sql`, `seed_032_dev_competitors.sql` +- Frontend: `src/views/pages/saas/development/` +- Rota: `src/router/routes.saas.js` (path `desenvolvimento`) +- Menu: `src/navigation/menus/saas.menu.js` (grupo "Desenvolvimento") +- Docs fonte: `development/04-roadmap/ROADMAP.md` · `development/02-auditoria/AUDITORIA.md` · `development/03-concorrentes/concorrentes.md` diff --git a/development/README.md b/development/README.md new file mode 100644 index 0000000..ae29e9d --- /dev/null +++ b/development/README.md @@ -0,0 +1,53 @@ +# Development — AgenciaPsi + +> Área de trabalho de desenvolvimento. Centraliza documentação interna, análise competitiva, roadmap e ferramentas de banco de dados. +> +> Este diretório é a **contraparte em arquivos** da página `/saas/desenvolvimento` no sistema. Dados estruturados (roadmap, auditoria, concorrentes) ficam no banco (`dev_*` tables); os `.md` aqui são **snapshots exportáveis** a partir do banco ou leitura humana da situação atual. + +## Navegação + +### [`01-visao-geral/`](./01-visao-geral/) +Snapshot do sistema hoje. +- [`ESTRUTURA.md`](./01-visao-geral/ESTRUTURA.md) — tree-view dos 6 perfis × ~79 features ativas +- [`mapa-sistema.html`](./01-visao-geral/mapa-sistema.html) — visualização interativa (abrir no navegador) +- [`estrutura.txt`](./01-visao-geral/estrutura.txt) — snapshot antigo da árvore de arquivos (histórico) + +### [`02-auditoria/`](./02-auditoria/) +Bugs, dívidas técnicas e decisões arquiteturais. +- [`AUDITORIA.md`](./02-auditoria/AUDITORIA.md) — auditoria técnica com status `[RESOLVIDO]` / `[ABERTO]` + +### [`03-concorrentes/`](./03-concorrentes/) +Benchmark competitivo. +- [`concorrentes.md`](./03-concorrentes/concorrentes.md) — 7 players analisados (SimplePractice, Psicomanager, PsicoPlanner, iClinic, Amplimed, Ninsaúde, Jane App) + tabela comparativa + diferenciais por player + +### [`04-roadmap/`](./04-roadmap/) +Fases de evolução do produto. +- [`ROADMAP.md`](./04-roadmap/ROADMAP.md) — Fase 1 (MVP Launch) · Fase 2 (Paridade) · Fase 3 (Diferenciação IA) · decisões estratégicas abertas + +### [`05-database/`](./05-database/) +Ferramentas de banco (read-only aqui — os scripts vivem em `database-novo/` na raiz do projeto). + +## Fluxo de trabalho + +1. **Edição primária na UI** — a página `/saas/desenvolvimento` lê/escreve diretamente nas tabelas `dev_*` +2. **Export pra git** — botões "Gerar {ROADMAP, concorrentes, ESTRUTURA}.md" serializam o banco pros arquivos aqui (pra versionar) +3. **Import inicial** (uma vez) — seeds em `database-novo/seeds/seed_030_*_dev.sql` populam o banco a partir dos `.md` atuais + +## Tabelas no banco (schema `public`, prefixo `dev_`) + +Criadas pela migration `20260417000001_dev_tables.sql`. Todas com RLS restrita a `saas_admins`. + +| Tabela | Pra quê | +|---|---| +| `dev_roadmap_phases` | Fases (1/2/3) com status, datas, objetivo | +| `dev_roadmap_items` | Itens das fases (prioridade, esforço, status, notas) | +| `dev_auditoria_items` | Bugs/débitos técnicos (severidade, status, solução) | +| `dev_competitors` | Concorrentes (pricing, URL, última pesquisa) | +| `dev_competitor_features` | Features deles (categoria, nome, fonte) | +| `dev_comparison_matrix` | AgenciaPsi × feature-de-concorrente (nosso status) | +| `dev_generation_log` | Histórico de execuções (backup/dashboard/export) | + +## Relacionados (fora de `development/`) + +- [`database-novo/`](../database-novo/) — CLI do banco (db.cjs, generate-dashboard.cjs) e schema/migrations/seeds +- [`diagrama-visualizacao-dados.webp`](../diagrama-visualizacao-dados.webp) — referência visual (fica na raiz) diff --git a/diagrama-visualizacao-dados.webp b/diagrama-visualizacao-dados.webp new file mode 100644 index 0000000..9868b7b Binary files /dev/null and b/diagrama-visualizacao-dados.webp differ diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..2d44213 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,42 @@ +services: + agenciapsi_app: + container_name: agenciapsi_app + image: node:20-alpine + working_dir: /app + volumes: + - .:/app + - agenciapsi_node_modules:/app/node_modules + ports: + - "5173:5173" + command: sh -c "npm install && npm run dev -- --host 0.0.0.0" + environment: + - NODE_ENV=development + networks: + - agenciapsi_net + restart: unless-stopped + + agenciapsi_mysql: + container_name: agenciapsi_mysql + image: mysql:8.0 + ports: + - "3307:3306" + environment: + MYSQL_ROOT_PASSWORD: agenciapsi123 + MYSQL_ROOT_HOST: "%" + MYSQL_DATABASE: agenciapsi + MYSQL_USER: agenciapsi + MYSQL_PASSWORD: agenciapsi123 + volumes: + - agenciapsi_mysql_data:/var/lib/mysql + command: --default-authentication-plugin=mysql_native_password + networks: + - agenciapsi_net + restart: always + +volumes: + agenciapsi_node_modules: + agenciapsi_mysql_data: + +networks: + agenciapsi_net: + driver: bridge diff --git a/e2e/patient-intake.spec.js b/e2e/patient-intake.spec.js new file mode 100644 index 0000000..c55f8fb --- /dev/null +++ b/e2e/patient-intake.spec.js @@ -0,0 +1,149 @@ +// ============================================================================= +// T#10 — Golden path: paciente abre link → preenche → submit → sucesso +// ============================================================================= +// Strategy: intercepta as chamadas pra Supabase Functions/REST e responde +// com mocks. Sem precisar de banco real ou edge functions rodando. +// +// Cobre as camadas de defesa em camadas (A#20 rev2): +// - honeypot (campo invisível, garantimos que não preenchemos) +// - submit normal funciona +// - 403 captcha-required → componente MathCaptchaChallenge aparece +// - 429 rate-limited → toast amigável +// - token UUID inválido bloqueia a página +// ============================================================================= +import { test, expect } from '@playwright/test'; + +const VALID_TOKEN = '550e8400-e29b-41d4-a716-446655440000'; // UUID v4 válido (TOKEN_RX bate) +const PUBLIC_URL = `/cadastro/paciente?t=${VALID_TOKEN}`; + +const FUNCTIONS_RX = /\/functions\/v1\/submit-patient-intake/; + +// Helper: preenche os 3 campos obrigatórios + consent +async function fillRequired(page, { nome, email, telefone }) { + await page.locator('#f_nome').fill(nome); + await page.locator('#f_email_principal').fill(email); + await page.locator('#f_telefone').fill(telefone); + // PrimeVue Checkbox é um div com role=checkbox; .check() não funciona, precisa click + await page.locator('label[for="ext_consent"]').click(); +} + +test.describe('Cadastro paciente externo — golden path', () => { + test('paciente preenche e submete com sucesso (sem captcha)', async ({ page }) => { + await page.route(FUNCTIONS_RX, async (route) => { + const body = route.request().postDataJSON(); + + // honeypot NÃO foi preenchido (humano) + expect(body?.website).toBeFalsy(); + expect(body?.token).toBe(VALID_TOKEN); + expect(body?.payload?.nome_completo).toBe('Maria da Silva'); + expect(body?.payload?.consent).toBe(true); + + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ ok: true, intake_id: 'fake-intake-uuid' }) + }); + }); + + await page.goto(PUBLIC_URL); + await expect(page.getByText('Pré-cadastro do paciente')).toBeVisible(); + + await fillRequired(page, { + nome: 'Maria da Silva', + email: 'maria@test.com', + telefone: '11987654321' + }); + + await page.getByRole('button', { name: 'Enviar cadastro', exact: true }).click(); + await expect(page.getByText(/Enviado com sucesso/i).first()).toBeVisible({ timeout: 10_000 }); + }); + + test('rate limit (429) mostra mensagem amigável de tentativas', async ({ page }) => { + await page.route(FUNCTIONS_RX, async (route) => { + await route.fulfill({ + status: 429, + contentType: 'application/json', + body: JSON.stringify({ error: 'rate-limited', retry_after_seconds: 600 }) + }); + }); + + await page.goto(PUBLIC_URL); + await expect(page.getByText('Pré-cadastro do paciente')).toBeVisible(); + + await fillRequired(page, { + nome: 'João Bot', + email: 'joao@test.com', + telefone: '11999999999' + }); + + await page.getByRole('button', { name: 'Enviar cadastro', exact: true }).click(); + await expect(page.getByText(/Muitas tentativas/i)).toBeVisible({ timeout: 6_000 }); + }); + + test('captcha-required mostra MathCaptchaChallenge e bloqueia botão até resposta', async ({ page }) => { + let firstSubmitCall = true; + + await page.route(FUNCTIONS_RX, async (route) => { + const url = route.request().url(); + if (url.includes('/captcha-challenge')) { + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ challenge: { id: 'fake-challenge-id', question: 'Quanto é 2 + 3?' } }) + }); + return; + } + if (firstSubmitCall) { + firstSubmitCall = false; + await route.fulfill({ + status: 403, + contentType: 'application/json', + body: JSON.stringify({ error: 'captcha-required' }) + }); + return; + } + // segunda call: ok + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ ok: true, intake_id: 'after-captcha' }) + }); + }); + + await page.goto(PUBLIC_URL); + await expect(page.getByText('Pré-cadastro do paciente')).toBeVisible(); + + await fillRequired(page, { + nome: 'Ana Pereira', + email: 'ana@test.com', + telefone: '11955554444' + }); + + await page.getByRole('button', { name: 'Enviar cadastro', exact: true }).click(); + + // MathCaptchaChallenge aparece + await expect(page.getByText('Quanto é 2 + 3?')).toBeVisible({ timeout: 8_000 }); + + // Botão "Enviar cadastro" do footer fica disabled enquanto sem resposta + await expect(page.getByRole('button', { name: 'Enviar cadastro', exact: true })).toBeDisabled(); + }); + + test('honeypot field existe no DOM mas está fora da viewport', async ({ page }) => { + await page.goto(PUBLIC_URL); + await expect(page.getByText('Pré-cadastro do paciente')).toBeVisible(); + + const honeypot = page.locator('input#ext_website'); + await expect(honeypot).toHaveCount(1); + await expect(honeypot).not.toBeInViewport(); + }); + + test('token inválido bloqueia toda a página de cadastro', async ({ page }) => { + await page.goto('/cadastro/paciente?t=token-invalido'); + + // Mensagem de erro aparece + await expect(page.getByText(/Link inválido ou ausente/i)).toBeVisible(); + + // Form não renderiza (botão Enviar não existe) + await expect(page.getByRole('button', { name: 'Enviar cadastro', exact: true })).toHaveCount(0); + }); +}); diff --git a/package-lock.json b/package-lock.json index 96576b1..4b8f7a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "vue-router": "^4.4.0" }, "devDependencies": { + "@playwright/test": "^1.59.1", "@primevue/auto-import-resolver": "^4.3.1", "@rushstack/eslint-patch": "^1.8.0", "@tailwindcss/vite": "^4.1.17", @@ -1133,6 +1134,21 @@ "url": "https://opencollective.com/pkgr" } }, + "node_modules/@playwright/test": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", + "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==", + "dev": true, + "dependencies": { + "playwright": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@polka/url": { "version": "1.0.0-next.29", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", @@ -4546,6 +4562,50 @@ "pathe": "^2.0.1" } }, + "node_modules/playwright": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", + "dev": true, + "dependencies": { + "playwright-core": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/png-js": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz", @@ -7221,6 +7281,15 @@ "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", "dev": true }, + "@playwright/test": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", + "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==", + "dev": true, + "requires": { + "playwright": "1.59.1" + } + }, "@polka/url": { "version": "1.0.0-next.29", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", @@ -9541,6 +9610,31 @@ "pathe": "^2.0.1" } }, + "playwright": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", + "dev": true, + "requires": { + "fsevents": "2.3.2", + "playwright-core": "1.59.1" + }, + "dependencies": { + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + } + } + }, + "playwright-core": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", + "dev": true + }, "png-js": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz", diff --git a/package.json b/package.json index 398d6d7..f125d82 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,9 @@ "test": "vitest run", "test:watch": "vitest", "test:ui": "vitest --ui", + "test:sql": "node database-novo/tests/run.cjs", + "test:e2e": "playwright test", + "test:e2e:ui": "playwright test --ui", "simulate": "node scripts/simulation/simulateUsage.js" }, "dependencies": { @@ -41,6 +44,7 @@ "vue-router": "^4.4.0" }, "devDependencies": { + "@playwright/test": "^1.59.1", "@primevue/auto-import-resolver": "^4.3.1", "@rushstack/eslint-patch": "^1.8.0", "@tailwindcss/vite": "^4.1.17", diff --git a/playwright.config.js b/playwright.config.js new file mode 100644 index 0000000..ae95252 --- /dev/null +++ b/playwright.config.js @@ -0,0 +1,43 @@ +// ============================================================================= +// AgenciaPsi — Playwright config (T#10) +// ============================================================================= +// E2E roda contra dev server local. Specs em e2e/, isoladas dos unit tests. +// +// Uso: +// npm run test:e2e — roda todos +// npm run test:e2e:ui — modo UI (debug) +// npx playwright test e2e/patient-intake.spec.js +// ============================================================================= +import { defineConfig, devices } from '@playwright/test'; + +export default defineConfig({ + testDir: './e2e', + timeout: 30_000, + expect: { timeout: 5_000 }, + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 1 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: [['list']], + + use: { + baseURL: 'http://localhost:5173', + trace: 'retain-on-failure', + screenshot: 'only-on-failure' + }, + + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] } + } + ], + + // Sobe o dev server automaticamente se ainda não estiver rodando + webServer: { + command: 'npm run dev', + url: 'http://localhost:5173', + reuseExistingServer: true, + timeout: 120_000 + } +}); diff --git a/src/app/__tests__/session.spec.js b/src/app/__tests__/session.spec.js new file mode 100644 index 0000000..eff066c --- /dev/null +++ b/src/app/__tests__/session.spec.js @@ -0,0 +1,219 @@ +/** + * session.spec.js + * + * Cobre o módulo de sessão global — foco nas race conditions documentadas + * no próprio session.js (singleflight, SIGNED_IN redundante, TOKEN_REFRESHED). + * + * Mock do supabase: capturamos o callback de onAuthStateChange pra disparar + * eventos manualmente e observar o state dos refs reativos. + */ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +// Captura do callback de onAuthStateChange (setado no listenAuthChanges) +let authCallback = null; + +// Mock configurável de getSession (pode trocar em cada teste via mockImplementation) +const getSessionMock = vi.fn(); +const profileSingleMock = vi.fn(); +const saasMaybeSingleMock = vi.fn(); + +vi.mock('@/lib/supabase/client', () => { + const from = vi.fn((table) => { + return { + select: vi.fn().mockReturnThis(), + eq: vi.fn().mockReturnThis(), + maybeSingle: table === 'saas_admins' ? saasMaybeSingleMock : profileSingleMock, + single: vi.fn().mockResolvedValue({ data: null, error: null }) + }; + }); + + return { + supabase: { + auth: { + getSession: getSessionMock, + onAuthStateChange: vi.fn((cb) => { + authCallback = cb; + return { data: { subscription: { unsubscribe: vi.fn() } } }; + }) + }, + from + } + }; +}); + +vi.mock('@/support/supportLogger', () => ({ + logAuth: vi.fn(), + logError: vi.fn() +})); + +// Importa depois dos mocks +const session = await import('../session.js'); + +beforeEach(() => { + // Reseta state dos refs e mocks a cada teste + session.sessionUser.value = null; + session.sessionRole.value = null; + session.sessionIsSaasAdmin.value = false; + session.sessionReady.value = false; + session.sessionRefreshing.value = false; + + // Desfaz listenAuthChanges de teste anterior pra permitir re-registro + session.stopAuthChanges(); + + authCallback = null; + getSessionMock.mockReset(); + profileSingleMock.mockReset(); + saasMaybeSingleMock.mockReset(); + + // defaults razoáveis + profileSingleMock.mockResolvedValue({ data: { role: 'therapist' }, error: null }); + saasMaybeSingleMock.mockResolvedValue({ data: null, error: null }); +}); + +describe('initSession — boot inicial', () => { + it('sem sessão → zera user/role/saasAdmin', async () => { + getSessionMock.mockResolvedValue({ data: { session: null }, error: null }); + + await session.initSession({ initial: true }); + + expect(session.sessionUser.value).toBe(null); + expect(session.sessionRole.value).toBe(null); + expect(session.sessionIsSaasAdmin.value).toBe(false); + expect(session.sessionReady.value).toBe(true); + expect(session.sessionRefreshing.value).toBe(false); + }); + + it('com sessão → hydrata user + busca role', async () => { + getSessionMock.mockResolvedValue({ + data: { session: { user: { id: 'uid-1' } } }, + error: null + }); + profileSingleMock.mockResolvedValue({ data: { role: 'clinic_admin' }, error: null }); + + await session.initSession({ initial: true }); + + expect(session.sessionUser.value?.id).toBe('uid-1'); + expect(session.sessionRole.value).toBe('clinic_admin'); + expect(session.sessionReady.value).toBe(true); + }); + + it('erro em getSession → state zerado (não propaga)', async () => { + getSessionMock.mockRejectedValue(new Error('network down')); + + await session.initSession({ initial: true }); + + expect(session.sessionUser.value).toBe(null); + expect(session.sessionRole.value).toBe(null); + expect(session.sessionReady.value).toBe(true); // ainda marca ready pra não travar o guard + }); + + it('singleflight: 2 chamadas concorrentes fazem apenas 1 getSession', async () => { + let resolveGet; + getSessionMock.mockImplementation(() => new Promise((resolve) => { resolveGet = resolve; })); + + const p1 = session.initSession({ initial: true }); + const p2 = session.initSession({ initial: true }); + + // segunda chamada deve ter retornado a mesma promise (sem disparar getSession de novo) + expect(getSessionMock).toHaveBeenCalledTimes(1); + + resolveGet({ data: { session: null }, error: null }); + await Promise.all([p1, p2]); + }); +}); + +describe('refreshSession — evita corrida', () => { + it('não dispara se já está refreshing', async () => { + session.sessionRefreshing.value = true; + + await session.refreshSession(); + + expect(getSessionMock).not.toHaveBeenCalled(); + }); + + it('sem sessão → não zera state existente (SIGNED_OUT cuida)', async () => { + session.sessionUser.value = { id: 'uid-1' }; + session.sessionRole.value = 'therapist'; + getSessionMock.mockResolvedValue({ data: { session: null }, error: null }); + + await session.refreshSession(); + + // State preservado — refreshSession não é quem zera (é SIGNED_OUT) + expect(session.sessionUser.value?.id).toBe('uid-1'); + expect(session.sessionRole.value).toBe('therapist'); + }); + + it('mesma sessão consistente → no-op', async () => { + session.sessionUser.value = { id: 'uid-1' }; + session.sessionRole.value = 'therapist'; + getSessionMock.mockResolvedValue({ + data: { session: { user: { id: 'uid-1' } } }, + error: null + }); + + await session.refreshSession(); + + // initSession não foi chamado de novo (state já era consistente) + expect(getSessionMock).toHaveBeenCalledTimes(1); // só o refreshSession próprio + }); +}); + +describe('listenAuthChanges — callbacks de auth', () => { + it('SIGNED_OUT zera state + chama callback', async () => { + const onOut = vi.fn(); + session.setOnSignedOut(onOut); + session.listenAuthChanges(); + + // simula state previamente hydratado + session.sessionUser.value = { id: 'uid-1' }; + session.sessionRole.value = 'therapist'; + session.sessionIsSaasAdmin.value = true; + session.sessionRefreshing.value = true; + + expect(authCallback).toBeTypeOf('function'); + await authCallback('SIGNED_OUT', null); + + expect(session.sessionUser.value).toBe(null); + expect(session.sessionRole.value).toBe(null); + expect(session.sessionIsSaasAdmin.value).toBe(false); + expect(session.sessionRefreshing.value).toBe(false); + expect(session.sessionReady.value).toBe(true); + expect(onOut).toHaveBeenCalledTimes(1); + }); + + it('SIGNED_IN com mesmo user (redundante) é ignorado', async () => { + session.listenAuthChanges(); + session.sessionUser.value = { id: 'uid-1' }; + session.sessionRole.value = 'therapist'; + session.sessionReady.value = true; + + await authCallback('SIGNED_IN', { user: { id: 'uid-1' } }); + + // Não rehidratou — profileSingleMock não foi chamado + expect(profileSingleMock).not.toHaveBeenCalled(); + }); + + it('SIGNED_IN com user diferente → hydrata novo', async () => { + session.listenAuthChanges(); + session.sessionUser.value = { id: 'uid-1' }; + session.sessionRole.value = 'therapist'; + session.sessionReady.value = true; + + profileSingleMock.mockResolvedValue({ data: { role: 'clinic_admin' }, error: null }); + + await authCallback('SIGNED_IN', { user: { id: 'uid-2' } }); + + expect(session.sessionUser.value?.id).toBe('uid-2'); + expect(session.sessionRole.value).toBe('clinic_admin'); + }); +}); + +describe('stopAuthChanges — cleanup', () => { + it('unsubscribe é chamado', () => { + session.listenAuthChanges(); + session.stopAuthChanges(); + + // não deve lançar erro se chamar de novo + expect(() => session.stopAuthChanges()).not.toThrow(); + }); +}); diff --git a/src/app/session.js b/src/app/session.js index 5daf054..b0e8564 100644 --- a/src/app/session.js +++ b/src/app/session.js @@ -16,6 +16,7 @@ */ import { ref } from 'vue'; import { supabase } from '@/lib/supabase/client'; +import { logAuth, logError } from '@/support/supportLogger'; /** * ⚠️ IMPORTANTE — ESTABILIDADE DE NAVEGAÇÃO @@ -57,11 +58,44 @@ export function setOnSignedOut(cb) { onSignedOutCallback = typeof cb === 'function' ? cb : null; } +// V#2: session.js é o único registrante de supabase.auth.onAuthStateChange. +// Outros módulos (guards.js, etc.) se inscrevem aqui via onSessionEvent +// em vez de registrar listeners próprios. +const eventHandlers = { + SIGNED_IN: [], + SIGNED_OUT: [], + TOKEN_REFRESHED: [], + USER_UPDATED: [] +}; + +/** + * Inscreve handler para um evento de auth processado por listenAuthChanges. + * Retorna função pra desregistrar. + * + * @param {'SIGNED_IN'|'SIGNED_OUT'|'TOKEN_REFRESHED'|'USER_UPDATED'} event + * @param {(session: object|null) => void} handler + */ +export function onSessionEvent(event, handler) { + if (!eventHandlers[event] || typeof handler !== 'function') return () => {}; + eventHandlers[event].push(handler); + return () => { + eventHandlers[event] = eventHandlers[event].filter((h) => h !== handler); + }; +} + +function dispatch(event, sess) { + const handlers = eventHandlers[event]; + if (!handlers || !handlers.length) return; + for (const h of handlers) { + try { h(sess); } catch (e) { logError('session', `${event} handler failed`, e); } + } +} + // evita init concorrente let initPromise = null; async function fetchRole(userId) { - const { data, error } = await supabase.from('profiles').select('role').eq('id', userId).single(); + const { data, error } = await supabase.from('profiles').select('role').eq('id', userId).maybeSingle(); if (error) return null; return data?.role || null; @@ -129,7 +163,7 @@ export async function initSession({ initial = false } = {}) { sessionIsSaasAdmin.value = false; } } catch (e) { - console.warn('[initSession] getSession falhou (tratando como sem sessão):', e); + logError('session', 'initSession.getSession falhou (tratando como sem sessão)', e); // não deixa estourar pro router guard sessionUser.value = null; sessionRole.value = null; @@ -173,7 +207,7 @@ export function listenAuthChanges() { if (authSubscription) return; const { data } = supabase.auth.onAuthStateChange(async (event, sess) => { - console.log('[AUTH EVENT]', event); + logAuth('event', { event }); // ✅ SIGNED_OUT: zera e chama callback if (event === 'SIGNED_OUT') { @@ -183,6 +217,7 @@ export function listenAuthChanges() { sessionRefreshing.value = false; sessionReady.value = true; if (onSignedOutCallback) onSignedOutCallback(); + dispatch('SIGNED_OUT', sess); return; } @@ -190,6 +225,7 @@ export function listenAuthChanges() { if (event === 'SIGNED_IN') { const uid = sess?.user?.id || null; if (uid && sessionReady.value && sessionUser.value?.id === uid && sessionRole.value) { + dispatch('SIGNED_IN', sess); return; } } @@ -204,10 +240,11 @@ export function listenAuthChanges() { await hydrateFromSession(sess); sessionReady.value = true; } catch (e) { - console.warn('[auth hydrate error]', e); + logError('session', 'auth hydrate error', e); } finally { sessionRefreshing.value = false; } + dispatch(event === 'TOKEN_REFRESHED' ? 'TOKEN_REFRESHED' : 'SIGNED_IN', sess); return; } @@ -215,7 +252,7 @@ export function listenAuthChanges() { try { await refreshSession(); } catch (e) { - console.error('[refreshSession error]', e); + logError('session', 'refreshSession error', e); } }); diff --git a/src/components/security/MathCaptchaChallenge.vue b/src/components/security/MathCaptchaChallenge.vue new file mode 100644 index 0000000..7a23d92 --- /dev/null +++ b/src/components/security/MathCaptchaChallenge.vue @@ -0,0 +1,96 @@ + + + + diff --git a/src/composables/useAgendaFinanceiro.js b/src/composables/useAgendaFinanceiro.js index 11b80cc..6dca753 100644 --- a/src/composables/useAgendaFinanceiro.js +++ b/src/composables/useAgendaFinanceiro.js @@ -222,8 +222,8 @@ export function useAgendaFinanceiro() { return { ok: true }; } - // ── remarcar → atualizar due_date da cobrança existente ──────────── - if (novoStatus === 'remarcar' && evento.billed) { + // ── remarcado → atualizar due_date da cobrança existente ──────────── + if (novoStatus === 'remarcado' && evento.billed) { // due_date mantém a data da sessão original por enquanto // (a nova data virá quando a sessão for reagendada) return { ok: true }; diff --git a/src/features/agenda/components/AgendaEventDialog.vue b/src/features/agenda/components/AgendaEventDialog.vue index 75f9398..192dd82 100644 --- a/src/features/agenda/components/AgendaEventDialog.vue +++ b/src/features/agenda/components/AgendaEventDialog.vue @@ -295,7 +295,7 @@ watch( async (newVal, oldVal) => { if (_skipStatusWatch.value) return; if (!isEdit.value || !form.value?.id) return; - if (newVal !== 'cancelado' && newVal !== 'remarcar') return; + if (newVal !== 'cancelado' && newVal !== 'remarcado') return; _prevStatus.value = oldVal; @@ -1073,7 +1073,7 @@ const statusOptions = [ { label: 'Realizado', value: 'realizado' }, { label: 'Faltou', value: 'faltou' }, { label: 'Cancelado', value: 'cancelado' }, - { label: 'Remarcar', value: 'remarcar' } + { label: 'Remarcar', value: 'remarcado' } ]; const serieCountByStatus = computed(() => { @@ -1122,7 +1122,7 @@ const statusOptionsFiltered = computed(() => [ { label: 'Realizado', value: 'realizado' }, { label: 'Faltou', value: 'faltou' }, { label: 'Cancelado', value: 'cancelado' }, - { label: 'Remarcar', value: 'remarcar', disabled: isInativoFutureEdit.value } + { label: 'Remarcar', value: 'remarcado', disabled: isInativoFutureEdit.value } ]); function fmtWeekdayShort(iso) { return new Date(iso).toLocaleDateString('pt-BR', { weekday: 'short' }).replace('.', '').slice(0, 3); @@ -1661,7 +1661,7 @@ const googleCalendarUrl = computed(() => { }); function labelStatusSessao(v) { - const map = { agendado: 'Agendado', realizado: 'Realizado', faltou: 'Faltou', cancelado: 'Cancelado', remarcar: 'Remarcar' }; + const map = { agendado: 'Agendado', realizado: 'Realizado', faltou: 'Faltou', cancelado: 'Cancelado', remarcado: 'Remarcado' }; return map[v] || '—'; } function statusSeverity(v) { @@ -1669,11 +1669,11 @@ function statusSeverity(v) { if (v === 'realizado') return 'success'; if (v === 'faltou') return 'warn'; if (v === 'cancelado') return 'danger'; - if (v === 'remarcar') return 'secondary'; // cor real via classe CSS + if (v === 'remarcado') return 'secondary'; // cor real via classe CSS return 'secondary'; } function statusExtraClass(v) { - return v === 'remarcar' ? 'tag-remarcar' : ''; + return v === 'remarcado' ? 'tag-remarcado' : ''; } @@ -1949,7 +1949,7 @@ function statusExtraClass(v) { · {{ serieCountByStatus.realizado }} realizadas · {{ serieCountByStatus.faltou }} faltaram · {{ serieCountByStatus.cancelado }} canceladas - · {{ serieCountByStatus.remarcar }} para remarcar + · {{ serieCountByStatus.remarcado }} para remarcar
Carregando… @@ -2482,8 +2482,8 @@ function statusExtraClass(v) { padding: 0.75rem; } -/* ── tag: remarcar (roxo — sem severity nativo no PrimeVue) ─ */ -:deep(.tag-remarcar) { +/* ── tag: remarcado (roxo — sem severity nativo no PrimeVue) ─ */ +:deep(.tag-remarcado) { background: #a855f7 !important; color: #fff !important; } @@ -3302,7 +3302,7 @@ function statusExtraClass(v) { .serie-pill--cancelado { border-left-color: var(--surface-border); } -.serie-pill--remarcar { +.serie-pill--remarcado { border-left-color: var(--orange-400, #fb923c); } .serie-pill__date { diff --git a/src/features/agenda/components/BloqueioDialog.vue b/src/features/agenda/components/BloqueioDialog.vue index 3b77f7c..c274d81 100644 --- a/src/features/agenda/components/BloqueioDialog.vue +++ b/src/features/agenda/components/BloqueioDialog.vue @@ -207,7 +207,7 @@ async function confirmar() { const { error } = await supabase.from('agenda_bloqueios').insert(rows); if (error) throw error; - // Marcar sessões existentes como "remarcar" + // Marcar sessões existentes como "remarcado" await marcarSessoesParaRemarcar(rows); toast.add({ @@ -226,10 +226,10 @@ async function confirmar() { } async function marcarSessoesParaRemarcar(bloqueios) { - // Para cada bloqueio, tenta marcar sessões existentes como 'remarcar' + // Para cada bloqueio, tenta marcar sessões existentes como 'remarcado' for (const b of bloqueios) { try { - let query = supabase.from('agenda_eventos').update({ status: 'remarcar' }).eq('owner_id', props.ownerId).eq('tipo', 'sessao').gte('inicio_em', `${b.data_inicio}T00:00:00`).lte('inicio_em', `${b.data_fim}T23:59:59`); + let query = supabase.from('agenda_eventos').update({ status: 'remarcado' }).eq('owner_id', props.ownerId).eq('tipo', 'sessao').gte('inicio_em', `${b.data_inicio}T00:00:00`).lte('inicio_em', `${b.data_fim}T23:59:59`); if (b.hora_inicio && b.hora_fim) { // filtra pela hora aproximada — comparação UTC simplificada diff --git a/src/features/agenda/components/ProximosFeriadosCard.vue b/src/features/agenda/components/ProximosFeriadosCard.vue index 1435e53..1f55356 100644 --- a/src/features/agenda/components/ProximosFeriadosCard.vue +++ b/src/features/agenda/components/ProximosFeriadosCard.vue @@ -156,8 +156,8 @@ async function confirmarBloqueio(feriado) { const { error } = await supabase.from('agenda_bloqueios').insert([row]); if (error) throw error; - // Marcar sessões existentes no dia como 'remarcar' - await supabase.from('agenda_eventos').update({ status: 'remarcar' }).eq('owner_id', _ownerId.value).eq('tipo', 'sessao').gte('inicio_em', `${feriado.data}T00:00:00`).lte('inicio_em', `${feriado.data}T23:59:59`); + // Marcar sessões existentes no dia como 'remarcado' + await supabase.from('agenda_eventos').update({ status: 'remarcado' }).eq('owner_id', _ownerId.value).eq('tipo', 'sessao').gte('inicio_em', `${feriado.data}T00:00:00`).lte('inicio_em', `${feriado.data}T23:59:59`); bloqueiosDatas.value = new Set([...bloqueiosDatas.value, feriado.data]); toast.add({ diff --git a/src/features/agenda/composables/__tests__/useAgendaEvents.spec.js b/src/features/agenda/composables/__tests__/useAgendaEvents.spec.js new file mode 100644 index 0000000..245e04c --- /dev/null +++ b/src/features/agenda/composables/__tests__/useAgendaEvents.spec.js @@ -0,0 +1,155 @@ +/** + * useAgendaEvents.spec.js — T#9 + * + * Wrapper fino do agendaRepository. Cobertura focada nos contratos: + * - rows/loading/error reativos + * - delega I/O ao repository (nada de fetch direto) + * - sem ownerId, loadMyRange é no-op (proteção tenant scoping) + * - error é capturado em loadMyRange e zera rows; create/update/remove propagam + */ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const listMock = vi.fn(); +const createMock = vi.fn(); +const updateMock = vi.fn(); +const removeMock = vi.fn(); + +vi.mock('@/features/agenda/services/agendaRepository', () => ({ + listMyAgendaEvents: (...a) => listMock(...a), + createAgendaEvento: (...a) => createMock(...a), + updateAgendaEvento: (...a) => updateMock(...a), + deleteAgendaEvento: (...a) => removeMock(...a) +})); + +const { useAgendaEvents } = await import('../useAgendaEvents.js'); + +beforeEach(() => { + listMock.mockReset(); + createMock.mockReset(); + updateMock.mockReset(); + removeMock.mockReset(); +}); + +describe('useAgendaEvents — estado inicial', () => { + it('rows vazio, loading false, error null', () => { + const { rows, loading, error } = useAgendaEvents(); + expect(rows.value).toEqual([]); + expect(loading.value).toBe(false); + expect(error.value).toBe(null); + }); +}); + +describe('loadMyRange', () => { + it('sem ownerId, não chama o repository (no-op de segurança)', async () => { + const { rows, loadMyRange } = useAgendaEvents(); + await loadMyRange('2026-01-01', '2026-01-31', null); + await loadMyRange('2026-01-01', '2026-01-31', ''); + await loadMyRange('2026-01-01', '2026-01-31', undefined); + expect(listMock).not.toHaveBeenCalled(); + expect(rows.value).toEqual([]); + }); + + it('chama listMyAgendaEvents com payload correto e popula rows', async () => { + listMock.mockResolvedValue([{ id: 'a' }, { id: 'b' }]); + const { rows, loading, loadMyRange } = useAgendaEvents(); + + const promise = loadMyRange('2026-01-01', '2026-01-31', 'u-1'); + // loading vira true durante a execução + expect(loading.value).toBe(true); + await promise; + + expect(listMock).toHaveBeenCalledWith({ + startISO: '2026-01-01', + endISO: '2026-01-31', + ownerId: 'u-1' + }); + expect(rows.value).toEqual([{ id: 'a' }, { id: 'b' }]); + expect(loading.value).toBe(false); + }); + + it('captura erro, zera rows e seta error.message', async () => { + listMock.mockRejectedValue(new Error('fetch fail')); + const { rows, loading, error, loadMyRange } = useAgendaEvents(); + + await loadMyRange('2026-01-01', '2026-01-31', 'u-1'); + + expect(rows.value).toEqual([]); + expect(loading.value).toBe(false); + expect(error.value).toBe('fetch fail'); + }); + + it('reseta error em call subsequente bem-sucedida', async () => { + listMock.mockRejectedValueOnce(new Error('first fail')).mockResolvedValueOnce([{ id: 'x' }]); + const { error, loadMyRange } = useAgendaEvents(); + + await loadMyRange('2026-01-01', '2026-01-31', 'u-1'); + expect(error.value).toBe('first fail'); + + await loadMyRange('2026-01-01', '2026-01-31', 'u-1'); + expect(error.value).toBe(null); + }); + + it('error sem message vira fallback string', async () => { + listMock.mockRejectedValue({}); // sem .message + const { error, loadMyRange } = useAgendaEvents(); + await loadMyRange('2026-01-01', '2026-01-31', 'u-1'); + expect(error.value).toBe('Erro ao carregar eventos'); + }); +}); + +describe('create / update / remove — delegação pura', () => { + it('create encaminha payload e retorna o resultado do repository', async () => { + createMock.mockResolvedValue({ id: 'new', titulo: 'X' }); + const { create } = useAgendaEvents(); + const result = await create({ titulo: 'X' }); + expect(createMock).toHaveBeenCalledWith({ titulo: 'X' }); + expect(result).toEqual({ id: 'new', titulo: 'X' }); + }); + + it('update encaminha id+patch e retorna o resultado', async () => { + updateMock.mockResolvedValue({ id: '42', titulo: 'updated' }); + const { update } = useAgendaEvents(); + const result = await update('42', { titulo: 'updated' }); + expect(updateMock).toHaveBeenCalledWith('42', { titulo: 'updated' }); + expect(result).toEqual({ id: '42', titulo: 'updated' }); + }); + + it('remove encaminha id (sem retorno)', async () => { + removeMock.mockResolvedValue(undefined); + const { remove } = useAgendaEvents(); + const result = await remove('99'); + expect(removeMock).toHaveBeenCalledWith('99'); + expect(result).toBeUndefined(); + }); + + it('create propaga erro (não engole)', async () => { + createMock.mockRejectedValue(new Error('insert blocked by RLS')); + const { create } = useAgendaEvents(); + await expect(create({ titulo: 'X' })).rejects.toThrow(/RLS/); + }); + + it('update propaga erro', async () => { + updateMock.mockRejectedValue(new Error('not found')); + const { update } = useAgendaEvents(); + await expect(update('1', { x: 1 })).rejects.toThrow(/not found/); + }); + + it('remove propaga erro', async () => { + removeMock.mockRejectedValue(new Error('cascade fail')); + const { remove } = useAgendaEvents(); + await expect(remove('1')).rejects.toThrow(/cascade/); + }); +}); + +describe('isolamento entre instâncias', () => { + it('cada useAgendaEvents() retorna refs independentes', async () => { + listMock.mockResolvedValue([{ id: 'a' }]); + const a = useAgendaEvents(); + const b = useAgendaEvents(); + + await a.loadMyRange('2026-01-01', '2026-01-31', 'u-1'); + + expect(a.rows.value).toEqual([{ id: 'a' }]); + expect(b.rows.value).toEqual([]); // b não foi tocado + }); +}); diff --git a/src/features/agenda/composables/useAgendaEvents.js b/src/features/agenda/composables/useAgendaEvents.js index c00d83d..8568474 100644 --- a/src/features/agenda/composables/useAgendaEvents.js +++ b/src/features/agenda/composables/useAgendaEvents.js @@ -16,75 +16,33 @@ */ /** * useAgendaEvents.js - * src/features/agenda/composables/useAgendaEvents.js * - * Gerencia apenas eventos reais (agenda_eventos). - * Sessões com recurrence_id são sessões reais de uma série. + * Wrapper fino sobre agendaRepository — agrega estado reativo (rows/loading/error) + * e delega toda a lógica de I/O ao repository. Mesmo padrão de useAgendaClinicEvents. + * + * Só gerencia eventos reais (agenda_eventos). Ocorrências virtuais de séries são + * responsabilidade do useRecurrence. */ import { ref } from 'vue'; -import { supabase } from '@/lib/supabase/client'; -import { useTenantStore } from '@/stores/tenantStore'; - -// ─── helpers internos ──────────────────────────────────────────────────────── - -function assertTenantId(tenantId) { - if (!tenantId || tenantId === 'null' || tenantId === 'undefined') { - throw new Error('Tenant ativo inválido. Selecione a clínica/tenant antes de operar na agenda.'); - } -} - -async function getUid() { - const { data, error } = await supabase.auth.getUser(); - if (error) throw error; - const uid = data?.user?.id; - if (!uid) throw new Error('Usuário não autenticado.'); - return uid; -} - -const BASE_SELECT = ` - id, owner_id, patient_id, tipo, status, - titulo, titulo_custom, observacoes, inicio_em, fim_em, - terapeuta_id, tenant_id, visibility_scope, - determined_commitment_id, link_online, extra_fields, modalidade, - recurrence_id, recurrence_date, - mirror_of_event_id, price, - insurance_plan_id, insurance_guide_number, insurance_value, insurance_plan_service_id, - patients!agenda_eventos_patient_id_fkey ( - id, nome_completo, avatar_url, status - ), - determined_commitments!agenda_eventos_determined_commitment_fk ( - id, bg_color, text_color - ) -`.trim(); +import { + listMyAgendaEvents, + createAgendaEvento, + updateAgendaEvento, + deleteAgendaEvento +} from '@/features/agenda/services/agendaRepository'; export function useAgendaEvents() { const rows = ref([]); const loading = ref(false); const error = ref(null); - async function loadMyRange(start, end, ownerId) { + async function loadMyRange(startISO, endISO, ownerId) { if (!ownerId) return; - - const tenantStore = useTenantStore(); - const tenantId = tenantStore.activeTenantId; - assertTenantId(tenantId); - loading.value = true; error.value = null; try { - const { data, error: err } = await supabase - .from('agenda_eventos') - .select(BASE_SELECT) - .eq('tenant_id', tenantId) - .eq('owner_id', ownerId) - .is('mirror_of_event_id', null) - .gte('inicio_em', start) - .lte('inicio_em', end) - .order('inicio_em', { ascending: true }); - - if (err) throw err; - rows.value = (data || []).map(flattenRow); + rows.value = await listMyAgendaEvents({ startISO, endISO, ownerId }); } catch (e) { error.value = e?.message || 'Erro ao carregar eventos'; rows.value = []; @@ -93,89 +51,17 @@ export function useAgendaEvents() { } } - /** - * Cria um evento injetando tenant_id e owner_id automaticamente. - * owner_id é sempre o usuário autenticado — nunca vem do payload externo. - * tenant_id vem do tenantStore ativo — nunca do payload externo. - */ async function create(payload) { - const tenantStore = useTenantStore(); - const tenantId = tenantStore.activeTenantId; - assertTenantId(tenantId); - - const uid = await getUid(); - - // eslint-disable-next-line no-unused-vars - const { paciente_id: _dropped, ...rest } = payload; - const safePayload = { - ...rest, - tenant_id: tenantId, - owner_id: uid - }; - - const { data, error: err } = await supabase.from('agenda_eventos').insert([safePayload]).select(BASE_SELECT).single(); - if (err) throw err; - return flattenRow(data); + return createAgendaEvento(payload); } async function update(id, patch) { - if (!id) throw new Error('ID inválido.'); - - const tenantStore = useTenantStore(); - const tenantId = tenantStore.activeTenantId; - assertTenantId(tenantId); - - // eslint-disable-next-line no-unused-vars - const { paciente_id: _dropped, ...safePatch } = patch; - - const { data, error: err } = await supabase.from('agenda_eventos').update(safePatch).eq('id', id).eq('tenant_id', tenantId).select(BASE_SELECT).single(); - if (err) throw err; - return flattenRow(data); + return updateAgendaEvento(id, patch); } async function remove(id) { - if (!id) throw new Error('ID inválido.'); - - const tenantStore = useTenantStore(); - const tenantId = tenantStore.activeTenantId; - assertTenantId(tenantId); - - const { error: err } = await supabase.from('agenda_eventos').delete().eq('id', id).eq('tenant_id', tenantId); - if (err) throw err; + await deleteAgendaEvento(id); } - async function removeSeriesFrom(recurrenceId, fromDateISO) { - if (!recurrenceId) throw new Error('recurrenceId inválido.'); - - const tenantStore = useTenantStore(); - const tenantId = tenantStore.activeTenantId; - assertTenantId(tenantId); - - const { error: err } = await supabase.from('agenda_eventos').delete().eq('recurrence_id', recurrenceId).eq('tenant_id', tenantId).gte('recurrence_date', fromDateISO); - if (err) throw err; - } - - async function removeAllSeries(recurrenceId) { - if (!recurrenceId) throw new Error('recurrenceId inválido.'); - - const tenantStore = useTenantStore(); - const tenantId = tenantStore.activeTenantId; - assertTenantId(tenantId); - - const { error: err } = await supabase.from('agenda_eventos').delete().eq('recurrence_id', recurrenceId).eq('tenant_id', tenantId); - if (err) throw err; - } - - return { rows, loading, error, loadMyRange, create, update, remove, removeSeriesFrom, removeAllSeries }; -} - -function flattenRow(r) { - if (!r) return r; - const patient = r.patients || null; - const out = { ...r }; - delete out.patients; - out.paciente_nome = patient?.nome_completo || out.paciente_nome || ''; - out.paciente_avatar = patient?.avatar_url || out.paciente_avatar || ''; - out.paciente_status = patient?.status || out.paciente_status || ''; - return out; + return { rows, loading, error, loadMyRange, create, update, remove }; } diff --git a/src/features/agenda/composables/useRecurrence.js b/src/features/agenda/composables/useRecurrence.js index b93c829..5f03899 100644 --- a/src/features/agenda/composables/useRecurrence.js +++ b/src/features/agenda/composables/useRecurrence.js @@ -31,6 +31,8 @@ import { ref } from 'vue'; import { supabase } from '@/lib/supabase/client'; +import { useTenantStore } from '@/stores/tenantStore'; +import { assertTenantId } from '@/features/agenda/services/_tenantGuards'; import { logRecurrence, logError, logPerf } from '@/support/supportLogger'; // ─── helpers de data ──────────────────────────────────────────────────────── @@ -197,6 +199,11 @@ export function generateDates(rule, rangeStart, rangeEnd) { // ─── expansão principal ────────────────────────────────────────────────────── +// Cap defensivo: a agenda real sempre passa ranges mensais/semanais (≤42d). +// Range muito grande com muitas regras = milhares de ocorrências no browser. +// Não bloqueamos (relatórios legítimos podem precisar), só avisamos. +const MAX_RANGE_DAYS = 730; // 2 anos + /** * Expande regras em ocorrências, aplica exceções. * @@ -207,6 +214,15 @@ export function generateDates(rule, rangeStart, rangeEnd) { * @returns {Array} occurrences — objetos com shape compatível com FullCalendar */ export function expandRules(rules, exceptions, rangeStart, rangeEnd) { + const rangeDays = Math.round((rangeEnd.getTime() - rangeStart.getTime()) / 86_400_000); + if (rangeDays > MAX_RANGE_DAYS) { + logError('useRecurrence', 'expandRules: range grande pode degradar UI', { + rangeDays, + maxRecommended: MAX_RANGE_DAYS, + ruleCount: (rules || []).length + }); + } + // índice de exceções por regra+data const exMap = new Map(); for (const ex of exceptions || []) { @@ -399,6 +415,13 @@ export function useRecurrence() { const exceptions = ref([]); const loading = ref(false); const error = ref(null); + const tenantStore = useTenantStore(); + + function currentTenantId() { + const tid = tenantStore.activeTenantId; + assertTenantId(tid); + return tid; + } /** * Carrega regras ativas para um owner no range dado. @@ -493,6 +516,7 @@ export function useRecurrence() { return true; }); } catch (e) { + logError('useRecurrence', 'loadExceptions ERRO', e); error.value = e?.message || 'Erro ao carregar exceções'; exceptions.value = []; } @@ -546,13 +570,16 @@ export function useRecurrence() { // ── CRUD de regras ────────────────────────────────────────────────────────── /** - * Cria uma nova regra de recorrência + * Cria uma nova regra de recorrência. + * tenant_id é injetado do tenantStore se não vier no payload (defesa em profundidade). * @param {Object} rule - campos da tabela recurrence_rules * @returns {Object} regra criada */ async function createRule(rule) { + const tenantId = currentTenantId(); logRecurrence('createRule →', { patient_id: rule?.patient_id, type: rule?.type }); - const { data, error: err } = await supabase.from('recurrence_rules').insert([rule]).select('*').single(); + const safeRule = { ...rule, tenant_id: rule?.tenant_id || tenantId }; + const { data, error: err } = await supabase.from('recurrence_rules').insert([safeRule]).select('*').single(); if (err) { logError('useRecurrence', 'createRule ERRO', err); throw err; @@ -562,13 +589,16 @@ export function useRecurrence() { } /** - * Atualiza a regra toda (editar todos) + * Atualiza a regra toda (editar todos). + * Filtro adicional por tenant_id — defesa em profundidade (RLS cobre, mas reforçamos). */ async function updateRule(id, patch) { + const tenantId = currentTenantId(); const { data, error: err } = await supabase .from('recurrence_rules') .update({ ...patch, updated_at: new Date().toISOString() }) .eq('id', id) + .eq('tenant_id', tenantId) .select('*') .single(); if (err) throw err; @@ -576,10 +606,15 @@ export function useRecurrence() { } /** - * Cancela a série inteira + * Cancela a série inteira (filtro por tenant_id — defesa em profundidade). */ async function cancelRule(id) { - const { error: err } = await supabase.from('recurrence_rules').update({ status: 'cancelado', updated_at: new Date().toISOString() }).eq('id', id); + const tenantId = currentTenantId(); + const { error: err } = await supabase + .from('recurrence_rules') + .update({ status: 'cancelado', updated_at: new Date().toISOString() }) + .eq('id', id) + .eq('tenant_id', tenantId); if (err) throw err; } @@ -610,19 +645,33 @@ export function useRecurrence() { // ── CRUD de exceções ──────────────────────────────────────────────────────── /** - * Cria ou atualiza uma exceção para uma ocorrência específica + * Cria ou atualiza uma exceção para uma ocorrência específica. + * tenant_id é injetado do tenantStore se não vier no payload. */ async function upsertException(ex) { - const { data, error: err } = await supabase.from('recurrence_exceptions').upsert([ex], { onConflict: 'recurrence_id,original_date' }).select('*').single(); + const tenantId = currentTenantId(); + const safeEx = { ...ex, tenant_id: ex?.tenant_id || tenantId }; + const { data, error: err } = await supabase + .from('recurrence_exceptions') + .upsert([safeEx], { onConflict: 'recurrence_id,original_date' }) + .select('*') + .single(); if (err) throw err; return data; } /** - * Remove uma exceção (restaura a ocorrência ao normal) + * Remove uma exceção (restaura a ocorrência ao normal). + * Filtro por tenant_id — defesa em profundidade. */ async function deleteException(recurrenceId, originalDate) { - const { error: err } = await supabase.from('recurrence_exceptions').delete().eq('recurrence_id', recurrenceId).eq('original_date', originalDate); + const tenantId = currentTenantId(); + const { error: err } = await supabase + .from('recurrence_exceptions') + .delete() + .eq('recurrence_id', recurrenceId) + .eq('original_date', originalDate) + .eq('tenant_id', tenantId); if (err) throw err; } diff --git a/src/features/agenda/pages/AgendaClinicaPage.vue b/src/features/agenda/pages/AgendaClinicaPage.vue index 6a3a299..e0efd4a 100644 --- a/src/features/agenda/pages/AgendaClinicaPage.vue +++ b/src/features/agenda/pages/AgendaClinicaPage.vue @@ -1968,7 +1968,7 @@ async function bloquearFeriadoDoAlerta(feriado) { } ]); if (error) throw error; - await supabase.from('agenda_eventos').update({ status: 'remarcar' }).eq('owner_id', clinicOwnerId.value).eq('tipo', 'sessao').gte('inicio_em', `${feriado.data}T00:00:00`).lte('inicio_em', `${feriado.data}T23:59:59`); + await supabase.from('agenda_eventos').update({ status: 'remarcado' }).eq('owner_id', clinicOwnerId.value).eq('tipo', 'sessao').gte('inicio_em', `${feriado.data}T00:00:00`).lte('inicio_em', `${feriado.data}T23:59:59`); feriadosAlertaBloqueados.value = new Set([...feriadosAlertaBloqueados.value, feriado.data]); miniBlockedDaySet.value = new Set([...miniBlockedDaySet.value, feriado.data]); diff --git a/src/features/agenda/pages/AgendaTerapeutaPage.vue b/src/features/agenda/pages/AgendaTerapeutaPage.vue index fb54673..13dddf4 100644 --- a/src/features/agenda/pages/AgendaTerapeutaPage.vue +++ b/src/features/agenda/pages/AgendaTerapeutaPage.vue @@ -1330,7 +1330,7 @@ async function bloquearFeriadoDoAlerta(feriado) { } ]); if (error) throw error; - await supabase.from('agenda_eventos').update({ status: 'remarcar' }).eq('owner_id', ownerId.value).eq('tipo', 'sessao').gte('inicio_em', `${feriado.data}T00:00:00`).lte('inicio_em', `${feriado.data}T23:59:59`); + await supabase.from('agenda_eventos').update({ status: 'remarcado' }).eq('owner_id', ownerId.value).eq('tipo', 'sessao').gte('inicio_em', `${feriado.data}T00:00:00`).lte('inicio_em', `${feriado.data}T23:59:59`); feriadosAlertaBloqueados.value = new Set([...feriadosAlertaBloqueados.value, feriado.data]); miniBlockedDaySet.value = new Set([...miniBlockedDaySet.value, feriado.data]); diff --git a/src/features/agenda/services/__tests__/agendaMappers.spec.js b/src/features/agenda/services/__tests__/agendaMappers.spec.js index 9164932..8700414 100644 --- a/src/features/agenda/services/__tests__/agendaMappers.spec.js +++ b/src/features/agenda/services/__tests__/agendaMappers.spec.js @@ -81,12 +81,12 @@ describe('mapAgendaEventosToCalendarEvents', () => { it('aplica cor de fundo para status faltou', () => { const [ev] = mapAgendaEventosToCalendarEvents([evento({ status: 'faltou' })]); - expect(ev.backgroundColor).toBe('#ef4444'); + expect(ev.backgroundColor).toBe('#f97316'); }); it('aplica cor de fundo para status cancelado', () => { const [ev] = mapAgendaEventosToCalendarEvents([evento({ status: 'cancelado' })]); - expect(ev.backgroundColor).toBe('#f97316'); + expect(ev.backgroundColor).toBe('#ef4444'); }); it('aplica cor de fundo para status remarcado', () => { diff --git a/src/features/agenda/services/_tenantGuards.js b/src/features/agenda/services/_tenantGuards.js new file mode 100644 index 0000000..f2b184f --- /dev/null +++ b/src/features/agenda/services/_tenantGuards.js @@ -0,0 +1,33 @@ +/* +|-------------------------------------------------------------------------- +| Agência PSI +|-------------------------------------------------------------------------- +| Arquivo: src/features/agenda/services/_tenantGuards.js +| +| Guards compartilhados entre composables e repositories do feature agenda. +| Antes: assertTenantId e getUid duplicados em 3+ arquivos. +|-------------------------------------------------------------------------- +*/ +import { supabase } from '@/lib/supabase/client'; + +export function assertTenantId(tenantId) { + if (!tenantId || tenantId === 'null' || tenantId === 'undefined') { + throw new Error('Tenant ativo inválido. Selecione a clínica/tenant antes de operar na agenda.'); + } +} + +export async function getUid() { + const { data, error } = await supabase.auth.getUser(); + if (error) throw error; + const uid = data?.user?.id; + if (!uid) throw new Error('Usuário não autenticado.'); + return uid; +} + +export function assertIsoRange(startISO, endISO) { + if (!startISO || !endISO) throw new Error('Intervalo inválido (startISO/endISO).'); +} + +export function sanitizeOwnerIds(ownerIds) { + return (ownerIds || []).filter((id) => typeof id === 'string' && id && id !== 'null' && id !== 'undefined'); +} diff --git a/src/features/agenda/services/agenda.service.js b/src/features/agenda/services/agenda.service.js deleted file mode 100644 index b23604a..0000000 --- a/src/features/agenda/services/agenda.service.js +++ /dev/null @@ -1,16 +0,0 @@ -/* -|-------------------------------------------------------------------------- -| Agência PSI -|-------------------------------------------------------------------------- -| Criado e desenvolvido por Leonardo Nohama -| -| Tecnologia aplicada à escuta. -| Estrutura para o cuidado. -| -| Arquivo: src/features/agenda/services/agenda.service.js -| Data: 2026 -| Local: São Carlos/SP — Brasil -|-------------------------------------------------------------------------- -| © 2026 — Todos os direitos reservados -|-------------------------------------------------------------------------- -*/ diff --git a/src/features/agenda/services/agendaClinicRepository.js b/src/features/agenda/services/agendaClinicRepository.js index df4ec5a..b5d781a 100644 --- a/src/features/agenda/services/agendaClinicRepository.js +++ b/src/features/agenda/services/agendaClinicRepository.js @@ -15,20 +15,12 @@ |-------------------------------------------------------------------------- */ import { supabase } from '@/lib/supabase/client'; - -function assertValidTenantId(tenantId) { - if (!tenantId || tenantId === 'null' || tenantId === 'undefined') { - throw new Error('Tenant ativo inválido. Selecione a clínica/tenant antes de carregar a agenda.'); - } -} - -function assertValidIsoRange(startISO, endISO) { - if (!startISO || !endISO) throw new Error('Intervalo inválido (startISO/endISO).'); -} - -function sanitizeOwnerIds(ownerIds) { - return (ownerIds || []).filter((id) => typeof id === 'string' && id && id !== 'null' && id !== 'undefined'); -} +import { + assertTenantId as assertValidTenantId, + assertIsoRange as assertValidIsoRange, + sanitizeOwnerIds +} from './_tenantGuards'; +import { AGENDA_EVENT_SELECT, flattenAgendaRow } from './agendaSelects'; /** * Lista eventos para mosaico da clínica (admin/secretaria) dentro de um tenant específico. @@ -44,7 +36,7 @@ export async function listClinicEvents({ tenantId, ownerIds, startISO, endISO } const { data, error } = await supabase .from('agenda_eventos') - .select('*, patients!agenda_eventos_patient_id_fkey(id, nome_completo, avatar_url, status), determined_commitments!agenda_eventos_determined_commitment_fk(id, bg_color, text_color)') + .select(AGENDA_EVENT_SELECT) .eq('tenant_id', tenantId) .in('owner_id', safeOwnerIds) .gte('inicio_em', startISO) @@ -52,7 +44,7 @@ export async function listClinicEvents({ tenantId, ownerIds, startISO, endISO } .order('inicio_em', { ascending: true }); if (error) throw error; - return data || []; + return (data || []).map(flattenAgendaRow); } /** @@ -91,10 +83,14 @@ export async function createClinicAgendaEvento(payload, { tenantId } = {}) { tenant_id: tenantId }; - const { data, error } = await supabase.from('agenda_eventos').insert(insertPayload).select('*').single(); + const { data, error } = await supabase + .from('agenda_eventos') + .insert(insertPayload) + .select(AGENDA_EVENT_SELECT) + .single(); if (error) throw error; - return data; + return flattenAgendaRow(data); } /** @@ -107,10 +103,16 @@ export async function updateClinicAgendaEvento(id, patch, { tenantId } = {}) { if (!patch) throw new Error('Patch vazio.'); assertValidTenantId(tenantId); - const { data, error } = await supabase.from('agenda_eventos').update(patch).eq('id', id).eq('tenant_id', tenantId).select('*').single(); + const { data, error } = await supabase + .from('agenda_eventos') + .update(patch) + .eq('id', id) + .eq('tenant_id', tenantId) + .select(AGENDA_EVENT_SELECT) + .single(); if (error) throw error; - return data; + return flattenAgendaRow(data); } /** diff --git a/src/features/agenda/services/agendaMappers.js b/src/features/agenda/services/agendaMappers.js index 5f00900..fd5f0ae 100644 --- a/src/features/agenda/services/agendaMappers.js +++ b/src/features/agenda/services/agendaMappers.js @@ -243,7 +243,7 @@ function _statusBgColor(status) { realizado: '#22c55e', // verde faltou: '#f97316', // laranja cancelado: '#ef4444', // vermelho - remarcar: '#a855f7', // roxo + remarcado: '#a855f7', // roxo bloqueado: '#6b7280' // cinza }; return map[status] ?? null; @@ -253,7 +253,7 @@ function _statusIcon(status, isOccurrence, hasSerie) { if (status === 'realizado') return '✓ '; if (status === 'faltou') return '✗ '; if (status === 'cancelado') return '∅ '; - if (status === 'remarcar') return '↺ '; + if (status === 'remarcado') return '↺ '; if (status === 'bloqueado') return '⊘ '; if (hasSerie || isOccurrence) return '↻ '; return ''; diff --git a/src/features/agenda/services/agendaRepository.js b/src/features/agenda/services/agendaRepository.js index 0b81b0c..1ad2c84 100644 --- a/src/features/agenda/services/agendaRepository.js +++ b/src/features/agenda/services/agendaRepository.js @@ -16,206 +16,140 @@ */ import { supabase } from '@/lib/supabase/client'; import { useTenantStore } from '@/stores/tenantStore'; +import { assertTenantId as assertValidTenantId, assertIsoRange, getUid } from './_tenantGuards'; +import { AGENDA_EVENT_SELECT, flattenAgendaRow } from './agendaSelects'; -function assertValidTenantId(tenantId) { - if (!tenantId || tenantId === 'null' || tenantId === 'undefined') { - throw new Error('Tenant ativo inválido. Selecione a clínica/tenant antes de carregar a agenda.'); - } -} +// ─── Configurações & jornada ──────────────────────────────────────────────── -async function getUid() { - const { data: userRes, error: userErr } = await supabase.auth.getUser(); - if (userErr) throw userErr; - - const uid = userRes?.user?.id; - if (!uid) throw new Error('Usuário não autenticado.'); - return uid; -} - -/** - * Configurações da agenda (por owner) - * Se você decidir que configurações são por tenant também, adicionamos tenant_id aqui. - */ export async function getMyAgendaSettings() { const uid = await getUid(); - - const { data, error } = await supabase.from('agenda_configuracoes').select('*').eq('owner_id', uid).order('created_at', { ascending: false }).limit(1).maybeSingle(); - + const { data, error } = await supabase + .from('agenda_configuracoes') + .select('*') + .eq('owner_id', uid) + .order('created_at', { ascending: false }) + .limit(1) + .maybeSingle(); if (error) throw error; return data; } -/** - * Regras semanais de jornada (agenda_regras_semanais): - * retorna os dias ativos com hora_inicio/hora_fim por dia. - */ export async function getMyWorkSchedule() { const uid = await getUid(); - - const { data, error } = await supabase.from('agenda_regras_semanais').select('dia_semana, hora_inicio, hora_fim, ativo').eq('owner_id', uid).eq('ativo', true).order('dia_semana'); - - if (error) throw error; - return data || []; -} - -/** - * Lista agenda do terapeuta (somente do owner logado) dentro do tenant ativo. - * Isso impede misturar eventos caso o terapeuta atue em múltiplas clínicas. - */ -export async function listMyAgendaEvents({ startISO, endISO, tenantId: tenantIdArg } = {}) { - const uid = await getUid(); - - const tenantStore = useTenantStore(); - const tenantId = tenantIdArg || tenantStore.activeTenantId; - assertValidTenantId(tenantId); - - if (!startISO || !endISO) throw new Error('Intervalo inválido (startISO/endISO).'); - const { data, error } = await supabase - .from('agenda_eventos') - .select('*, patients(id, nome_completo, avatar_url), determined_commitments!determined_commitment_id(id, bg_color, text_color)') - .eq('tenant_id', tenantId) + .from('agenda_regras_semanais') + .select('dia_semana, hora_inicio, hora_fim, ativo') .eq('owner_id', uid) - .gte('inicio_em', startISO) - .lt('inicio_em', endISO) - .order('inicio_em', { ascending: true }); - - if (error) throw error; - return data || []; -} - -/** - * Lista eventos para mosaico da clínica (admin/secretaria) dentro de um tenant específico. - * IMPORTANTE: SEM tenant_id aqui vira vazamento multi-tenant. - */ -export async function listClinicEvents({ tenantId, ownerIds, startISO, endISO }) { - assertValidTenantId(tenantId); - if (!ownerIds?.length) return []; - if (!startISO || !endISO) throw new Error('Intervalo inválido (startISO/endISO).'); - - // Sanitiza ownerIds - const safeOwnerIds = ownerIds.filter((id) => typeof id === 'string' && id && id !== 'null' && id !== 'undefined'); - - if (!safeOwnerIds.length) return []; - - const { data, error } = await supabase - .from('agenda_eventos') - .select('*, determined_commitments!determined_commitment_id(id, bg_color, text_color)') - .eq('tenant_id', tenantId) - .in('owner_id', safeOwnerIds) - .gte('inicio_em', startISO) - .lt('inicio_em', endISO) - .order('inicio_em', { ascending: true }); - + .eq('ativo', true) + .order('dia_semana'); if (error) throw error; return data || []; } export async function listTenantStaff(tenantId) { assertValidTenantId(tenantId); - const { data, error } = await supabase.from('v_tenant_staff').select('*').eq('tenant_id', tenantId); - if (error) throw error; return data || []; } +// ─── Agenda do terapeuta ──────────────────────────────────────────────────── + +function resolveTenantId(tenantIdArg) { + const tenantStore = useTenantStore(); + const tenantId = tenantIdArg || tenantStore.activeTenantId; + assertValidTenantId(tenantId); + return tenantId; +} + +/** + * Lista eventos do terapeuta logado (owner_id = uid) dentro do tenant ativo. + * Retorna rows flattened (paciente_nome/paciente_avatar/paciente_status no topo). + * + * Parâmetros: + * - startISO/endISO: half-open range (lt no final — consistente com clinic) + * - ownerId: opcional — default é o uid do usuário logado + * - tenantId: opcional — default é tenantStore.activeTenantId + * - excludeMirror: se true, filtra mirror_of_event_id IS NULL (agenda do terapeuta + * não deve mostrar eventos espelho de outra clínica) + */ +export async function listMyAgendaEvents({ startISO, endISO, ownerId, tenantId, excludeMirror = true } = {}) { + assertIsoRange(startISO, endISO); + const uid = ownerId || (await getUid()); + const tid = resolveTenantId(tenantId); + + let q = supabase + .from('agenda_eventos') + .select(AGENDA_EVENT_SELECT) + .eq('tenant_id', tid) + .eq('owner_id', uid) + .gte('inicio_em', startISO) + .lt('inicio_em', endISO) + .order('inicio_em', { ascending: true }); + + if (excludeMirror) q = q.is('mirror_of_event_id', null); + + const { data, error } = await q; + if (error) throw error; + return (data || []).map(flattenAgendaRow); +} + /** * Criação segura: * - injeta tenant_id do tenantStore * - injeta owner_id do usuário logado (ignora owner_id vindo de fora) - * - * Observação: - * - Para admin/secretária criar para outros owners, o ideal é ter uma função separada - * (ex.: createClinicAgendaEvento) que permita owner_id explicitamente. - * Por enquanto, deixo esta função como "safe default" para terapeuta. + * - dropa paciente_id (campo legado) se vier no payload */ export async function createAgendaEvento(payload) { - const uid = await getUid(); - const tenantStore = useTenantStore(); - const tenantId = tenantStore.activeTenantId; - assertValidTenantId(tenantId); - if (!payload) throw new Error('Payload vazio.'); + const uid = await getUid(); + const tid = resolveTenantId(); - const insertPayload = { - ...payload, - tenant_id: tenantId, - owner_id: uid - }; + // eslint-disable-next-line no-unused-vars + const { paciente_id: _dropped, ...rest } = payload; + const insertPayload = { ...rest, tenant_id: tid, owner_id: uid }; - const { data, error } = await supabase.from('agenda_eventos').insert(insertPayload).select('*').single(); + const { data, error } = await supabase + .from('agenda_eventos') + .insert([insertPayload]) + .select(AGENDA_EVENT_SELECT) + .single(); if (error) throw error; - return data; + return flattenAgendaRow(data); } /** - * Atualização segura: - * - filtra por id + tenant_id (evita update cruzado por acidente) - * RLS deve reforçar isso no banco. + * Atualização segura: filtra por id + tenant_id (RLS reforça no banco). */ -export async function updateAgendaEvento(id, patch, { tenantId: tenantIdArg } = {}) { +export async function updateAgendaEvento(id, patch, { tenantId } = {}) { if (!id) throw new Error('ID inválido.'); if (!patch) throw new Error('Patch vazio.'); + const tid = resolveTenantId(tenantId); - const tenantStore = useTenantStore(); - const tenantId = tenantIdArg || tenantStore.activeTenantId; - assertValidTenantId(tenantId); + // eslint-disable-next-line no-unused-vars + const { paciente_id: _dropped, ...safePatch } = patch; - const { data, error } = await supabase.from('agenda_eventos').update(patch).eq('id', id).eq('tenant_id', tenantId).select('*').single(); + const { data, error } = await supabase + .from('agenda_eventos') + .update(safePatch) + .eq('id', id) + .eq('tenant_id', tid) + .select(AGENDA_EVENT_SELECT) + .single(); if (error) throw error; - return data; + return flattenAgendaRow(data); } /** - * Delete seguro: - * - filtra por id + tenant_id + * Delete seguro: filtra por id + tenant_id. */ -export async function deleteAgendaEvento(id, { tenantId: tenantIdArg } = {}) { +export async function deleteAgendaEvento(id, { tenantId } = {}) { if (!id) throw new Error('ID inválido.'); + const tid = resolveTenantId(tenantId); - const tenantStore = useTenantStore(); - const tenantId = tenantIdArg || tenantStore.activeTenantId; - assertValidTenantId(tenantId); - - const { error } = await supabase.from('agenda_eventos').delete().eq('id', id).eq('tenant_id', tenantId); - + const { error } = await supabase.from('agenda_eventos').delete().eq('id', id).eq('tenant_id', tid); if (error) throw error; return true; } - -// Adicione no mesmo arquivo: src/features/agenda/services/agendaRepository.js - -/** - * Criação para a área da clínica (admin/secretária): - * - exige tenantId explícito (ou cai no tenantStore) - * - permite definir owner_id (terapeuta dono do compromisso) - * - * Segurança real deve ser garantida por RLS: - * - clinic_admin/tenant_admin pode criar para qualquer owner dentro do tenant - * - therapist não deve conseguir passar daqui (guard + RLS) - */ -export async function createClinicAgendaEvento(payload, { tenantId: tenantIdArg } = {}) { - const tenantStore = useTenantStore(); - const tenantId = tenantIdArg || tenantStore.activeTenantId; - assertValidTenantId(tenantId); - - if (!payload) throw new Error('Payload vazio.'); - - const ownerId = payload.owner_id; - if (!ownerId || ownerId === 'null' || ownerId === 'undefined') { - throw new Error('owner_id é obrigatório para criação pela clínica.'); - } - - const insertPayload = { - ...payload, - tenant_id: tenantId - }; - - const { data, error } = await supabase.from('agenda_eventos').insert(insertPayload).select('*').single(); - - if (error) throw error; - return data; -} diff --git a/src/features/agenda/services/agendaSelects.js b/src/features/agenda/services/agendaSelects.js new file mode 100644 index 0000000..fdf5726 --- /dev/null +++ b/src/features/agenda/services/agendaSelects.js @@ -0,0 +1,50 @@ +/* +|-------------------------------------------------------------------------- +| Agência PSI +|-------------------------------------------------------------------------- +| Arquivo: src/features/agenda/services/agendaSelects.js +| +| Fonte única do SELECT de agenda_eventos. Antes estava duplicado em 3 +| lugares (useAgendaEvents, agendaRepository, agendaClinicRepository) com +| inconsistências sutis (FKs explícitas em uns, não em outros). +|-------------------------------------------------------------------------- +*/ + +/** + * Select canônico de agenda_eventos com joins do paciente e do commitment. + * + * FKs explícitas (obrigatórias para evitar ambiguidade quando agenda_eventos + * tem múltiplas colunas apontando para a mesma tabela): + * - agenda_eventos_patient_id_fkey + * - agenda_eventos_determined_commitment_fk + */ +export const AGENDA_EVENT_SELECT = ` + id, owner_id, patient_id, tipo, status, + titulo, titulo_custom, observacoes, inicio_em, fim_em, + terapeuta_id, tenant_id, visibility_scope, + determined_commitment_id, link_online, extra_fields, modalidade, + recurrence_id, recurrence_date, + mirror_of_event_id, price, + insurance_plan_id, insurance_guide_number, insurance_value, insurance_plan_service_id, + patients!agenda_eventos_patient_id_fkey ( + id, nome_completo, avatar_url, status + ), + determined_commitments!agenda_eventos_determined_commitment_fk ( + id, bg_color, text_color + ) +`.trim(); + +/** + * Achata o aninhamento de patients dentro da row. + * O resto do app usa tanto `paciente_nome` (flat) quanto `patients?.nome_completo` + * (aninhado). Mantemos ambos após o flatten. + */ +export function flattenAgendaRow(r) { + if (!r) return r; + const patient = r.patients || null; + const out = { ...r }; + out.paciente_nome = patient?.nome_completo || r.paciente_nome || ''; + out.paciente_avatar = patient?.avatar_url || r.paciente_avatar || ''; + out.paciente_status = patient?.status || r.paciente_status || ''; + return out; +} diff --git a/src/features/financeiro/pages/FinanceiroDashboardPage.vue b/src/features/financeiro/pages/FinanceiroDashboardPage.vue index 1384c9f..15e878f 100644 --- a/src/features/financeiro/pages/FinanceiroDashboardPage.vue +++ b/src/features/financeiro/pages/FinanceiroDashboardPage.vue @@ -281,7 +281,9 @@ onMounted(async () => { ═══════════════════════════════════════ -->
- +
+ +
Receita × Despesa
Comparativo dos últimos 6 meses
@@ -303,7 +305,9 @@ onMounted(async () => { ═══════════════════════════════════════ -->
- +
+ +
Projeção de Caixa
Cobranças em aberto — próximos 6 meses
@@ -345,7 +349,9 @@ onMounted(async () => { ═══════════════════════════════════════ -->
- +
+ +
Últimos lançamentos
Cobranças e receitas recentes
diff --git a/src/features/financeiro/pages/FinanceiroPage.vue b/src/features/financeiro/pages/FinanceiroPage.vue index 68c0133..51f79a1 100644 --- a/src/features/financeiro/pages/FinanceiroPage.vue +++ b/src/features/financeiro/pages/FinanceiroPage.vue @@ -397,7 +397,9 @@ onBeforeUnmount(() => { ═══════════════════════════════════════ -->
- +
+ +
Filtros
Refine por status, tipo, paciente ou período
@@ -547,7 +549,9 @@ onBeforeUnmount(() => {