Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 47 additions & 79 deletions db/ESTRUTURA.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
> ⚠️ **ARQUIVO GERADO. Não edite à mão.**
> `npx tsx scripts/db/gerar-estrutura.ts`

Fonte: `db/snapshot/schema.sql` (dump de **2026-08-11**, até a migration **00000000000000**)
Fonte: `db/snapshot/schema.sql` (dump de **2026-08-12**, até a migration **20260812010000**)
cruzado com o código vivo em `src/`.

A verdade do banco é **a baseline `00000000000000_baseline.sql` + as migrations
Expand All @@ -18,21 +18,17 @@ posteriores a ela**. Este documento é a leitura humana disso; a leitura de máq
| Views | **12** |
| Funções | **73** |
| Triggers | **93** |
| Policies RLS | **348** |
| Tabelas com RLS | **107** de 112 |
| Policies RLS | **360** |
| Tabelas com RLS | **112** de 112 |
| Tabelas referenciadas no código | **88** de 112 |

## Sinais que pedem ação

Contas que este gerador fecha toda vez que roda. Item aqui não é opinião — é o dump.

### Tabelas sem RLS (5)
### Tabelas sem RLS (0)

- `categorization_patterns`
- `company_ai_dossiers`
- `consultant_memories`
- `employees`
- `meetings`
_Nenhuma — todas as tabelas têm RLS._

### Tabelas sem nenhuma referência em `src/` (24)

Expand Down Expand Up @@ -73,40 +69,12 @@ Isto é bug em produção esperando o caminho ser exercitado.
- `pulse_checks` — 2 referência(s)
- `system_notifications` — 1 referência(s)

### Funções `SECURITY DEFINER` sem `search_path` fixo (29)
### Funções `SECURITY DEFINER` sem `search_path` fixo (0)

`SECURITY DEFINER` roda com os poderes do dono. Sem `SET search_path`, um schema
na frente do `public` pode sequestrar as chamadas de dentro da função.

- `accept_invite()`
- `avancar_jornada_etapa()`
- `calculate_risk_score()`
- `check_cnpj_duplicates()`
- `compile_financial_context()`
- `create_invite()`
- `create_lite_company_and_user()`
- `extract_financial_gaps()`
- `get_dre_readiness()`
- `get_lancamentos_unicos()`
- `get_status_mensal()`
- `guard_bank_account_inactive_company()`
- `handle_new_lite_company()`
- `increment_assertiva_credits()`
- `is_admin()`
- `is_consultor()`
- `list_consultores()`
- `lite_get_company_id()`
- `mark_dossier_stale()`
- `recalcular_progresso_modulo()`
- `recalcular_progresso_projeto()`
- `reset_assertiva_monthly_credits()`
- `trg_jornada_categorizacao()`
- `trg_jornada_impeditivos()`
- `trg_jornada_primeira_dre()`
- `trg_jornada_upload_feito()`
- `trg_mark_dossier_stale_direct()`
- `trg_mark_dossier_stale_via_client()`
- `validate_invite()`
_Nenhuma._

### Views sem `security_invoker` (12 de 12)

Expand Down Expand Up @@ -174,7 +142,7 @@ _O catálogo e as regras que dão sentido contábil a cada lançamento._
| Tabela | Cols | PK | RLS | Policies | Triggers | Índices | Usos em `src/` |
|---|--:|---|:-:|--:|--:|--:|--:|
| [`categories`](#categories) | 11 | id | ✅ | 2 | 1 | 2 | **0** |
| [`categorization_patterns`](#categorization_patterns) | 13 | id | | 0 | 1 | 2 | 1 |
| [`categorization_patterns`](#categorization_patterns) | 13 | id | | 4 | 1 | 2 | 1 |
| [`categorization_rules`](#categorization_rules) | 18 | id | ✅ | 2 | 1 | 4 | 2 |
| [`dre_categories`](#dre_categories) | 10 | id | ✅ | 1 | 0 | 1 | 4 |
| [`dre_subcategories`](#dre_subcategories) | 21 | id | ✅ | 3 | 0 | 5 | 5 |
Expand Down Expand Up @@ -288,9 +256,9 @@ _Pessoas, entregas, reuniões e catálogo de produto/serviço._

| Tabela | Cols | PK | RLS | Policies | Triggers | Índices | Usos em `src/` |
|---|--:|---|:-:|--:|--:|--:|--:|
| [`employees`](#employees) | 34 | id | | 6 | 2 | 6 | 17 |
| [`employees`](#employees) | 34 | id | | 6 | 2 | 6 | 17 |
| [`estoque_produtos`](#estoque_produtos) | 12 | id | ✅ | 4 | 1 | 1 | 8 |
| [`meetings`](#meetings) | 20 | id | | 0 | 1 | 1 | **0** |
| [`meetings`](#meetings) | 20 | id | | 0 | 1 | 1 | **0** |
| [`products_services`](#products_services) | 12 | id | ✅ | 4 | 2 | 1 | 5 |
| [`project_deliverables`](#project_deliverables) | 13 | id | ✅ | 4 | 1 | 2 | **0** |
| [`project_meetings`](#project_meetings) | 16 | id | ✅ | 2 | 1 | 3 | **0** |
Expand Down Expand Up @@ -322,8 +290,8 @@ _Telemetria de chamadas, dossiês e memória do consultor._
| Tabela | Cols | PK | RLS | Policies | Triggers | Índices | Usos em `src/` |
|---|--:|---|:-:|--:|--:|--:|--:|
| [`ai_calls`](#ai_calls) | 18 | id | ✅ | 4 | 0 | 4 | 3 |
| [`company_ai_dossiers`](#company_ai_dossiers) | 9 | id | | 0 | 0 | 1 | 4 |
| [`consultant_memories`](#consultant_memories) | 10 | id | | 0 | 0 | 1 | 5 |
| [`company_ai_dossiers`](#company_ai_dossiers) | 9 | id | | 4 | 0 | 1 | 4 |
| [`consultant_memories`](#consultant_memories) | 10 | id | | 4 | 0 | 1 | 5 |

### Lite (produto paralelo)

Expand Down Expand Up @@ -1040,7 +1008,7 @@ _Tabelas do produto Lite — namespace próprio, não se mistura com o principal

> Padrões genéricos de auto-categorização. Filosofia: qualidade > quantidade. Padrões específicos de empresa devem ser categorizados manualmente ou pela IA futura.

**Domínio:** Financeiro · categorização · **RLS:** ❌ **desligada** · **Usos em `src/`:** 1
**Domínio:** Financeiro · categorização · **RLS:** ✅ 4 policies · **Usos em `src/`:** 1

| Coluna | Tipo | Nulo | Padrão |
|---|---|:-:|---|
Expand Down Expand Up @@ -2672,7 +2640,7 @@ _Tabelas do produto Lite — namespace próprio, não se mistura com o principal

> Colaboradores para análise de Custo Hora-Homem

**Domínio:** Gestão e operação · **RLS:** ❌ **desligada** · **Usos em `src/`:** 17
**Domínio:** Gestão e operação · **RLS:** ✅ 6 policies · **Usos em `src/`:** 17

| Coluna | Tipo | Nulo | Padrão |
|---|---|:-:|---|
Expand Down Expand Up @@ -2742,7 +2710,7 @@ _Tabelas do produto Lite — namespace próprio, não se mistura com o principal

### meetings

**Domínio:** Gestão e operação · **RLS:** ❌ **desligada** · **Usos em `src/`:** 0
**Domínio:** Gestão e operação · **RLS:** ✅ 0 policies · **Usos em `src/`:** 0

| Coluna | Tipo | Nulo | Padrão |
|---|---|:-:|---|
Expand Down Expand Up @@ -3159,7 +3127,7 @@ _Tabelas do produto Lite — namespace próprio, não se mistura com o principal

### company_ai_dossiers

**Domínio:** IA · **RLS:** ❌ **desligada** · **Usos em `src/`:** 4
**Domínio:** IA · **RLS:** ✅ 4 policies · **Usos em `src/`:** 4

| Coluna | Tipo | Nulo | Padrão |
|---|---|:-:|---|
Expand All @@ -3177,7 +3145,7 @@ _Tabelas do produto Lite — namespace próprio, não se mistura com o principal

### consultant_memories

**Domínio:** IA · **RLS:** ❌ **desligada** · **Usos em `src/`:** 5
**Domínio:** IA · **RLS:** ✅ 4 policies · **Usos em `src/`:** 5

| Coluna | Tipo | Nulo | Padrão |
|---|---|:-:|---|
Expand Down Expand Up @@ -3306,62 +3274,62 @@ _Tabelas do produto Lite — namespace próprio, não se mistura com o principal

| Função | `SECURITY DEFINER` | `search_path` fixo | Chamadas via `.rpc()` |
|---|:-:|:-:|--:|
| `accept_invite` | ✅ | | 1 |
| `accept_invite` | ✅ | | 1 |
| `analisar_impacto_mover_bank_account` | ✅ | ✅ | 1 |
| `apply_demo_snapshot` | ✅ | ✅ | 0 |
| `auto_move_to_em_contato` | — | — | 0 |
| `avancar_jornada_etapa` | ✅ | | 1 |
| `avancar_jornada_etapa` | ✅ | | 1 |
| `calculate_monthly_dre` | — | — | 9 |
| `calculate_monthly_dre_v2` | — | — | 0 |
| `calculate_monthly_dre_v3` | — | — | 2 |
| `calculate_risk_score` | ✅ | | 1 |
| `check_cnpj_duplicates` | ✅ | | 1 |
| `compile_financial_context` | ✅ | | 1 |
| `create_invite` | ✅ | | 1 |
| `create_lite_company_and_user` | ✅ | | 0 |
| `extract_financial_gaps` | ✅ | | 1 |
| `calculate_risk_score` | ✅ | | 1 |
| `check_cnpj_duplicates` | ✅ | | 1 |
| `compile_financial_context` | ✅ | | 1 |
| `create_invite` | ✅ | | 1 |
| `create_lite_company_and_user` | ✅ | | 0 |
| `extract_financial_gaps` | ✅ | | 1 |
| `generate_invite_token` | — | — | 0 |
| `get_abc_buckets_despesas` | — | — | 2 |
| `get_abc_buckets_despesas_grupo` | — | — | 1 |
| `get_category_breakdown` | — | — | 0 |
| `get_dre_readiness` | ✅ | | 3 |
| `get_dre_readiness` | ✅ | | 3 |
| `get_dre_summary` | — | — | 0 |
| `get_exercicios_fiscais` | — | — | 1 |
| `get_lancamentos_unicos` | ✅ | | 5 |
| `get_status_mensal` | ✅ | | 2 |
| `get_lancamentos_unicos` | ✅ | | 5 |
| `get_status_mensal` | ✅ | | 2 |
| `get_user_role` | ✅ | ✅ | 2 |
| `guard_bank_account_inactive_company` | ✅ | | 0 |
| `handle_new_lite_company` | ✅ | | 0 |
| `guard_bank_account_inactive_company` | ✅ | | 0 |
| `handle_new_lite_company` | ✅ | | 0 |
| `handle_new_user` | ✅ | ✅ | 0 |
| `increment_assertiva_credits` | ✅ | | 1 |
| `increment_pattern_hit` | | | 1 |
| `increment_assertiva_credits` | ✅ | | 1 |
| `increment_pattern_hit` | | | 1 |
| `invalidar_endividamento_por_ancora` | ✅ | ✅ | 0 |
| `is_admin` | ✅ | | 0 |
| `is_admin` | ✅ | | 0 |
| `is_admin_or_sdr` | ✅ | ✅ | 0 |
| `is_consultor` | ✅ | | 0 |
| `list_consultores` | ✅ | | 0 |
| `lite_get_company_id` | ✅ | | 0 |
| `is_consultor` | ✅ | | 0 |
| `list_consultores` | ✅ | | 0 |
| `lite_get_company_id` | ✅ | | 0 |
| `marcar_snapshot_desatualizado` | ✅ | ✅ | 0 |
| `mark_dossier_stale` | ✅ | | 0 |
| `mark_dossier_stale` | ✅ | | 0 |
| `move_bank_account_with_transactions` | ✅ | ✅ | 1 |
| `nivel_jornada_etapa` | — | — | 0 |
| `recalcular_progresso_modulo` | ✅ | | 0 |
| `recalcular_progresso_projeto` | ✅ | | 0 |
| `recalcular_progresso_modulo` | ✅ | | 0 |
| `recalcular_progresso_projeto` | ✅ | | 0 |
| `registrar_ancora_saldo` | ✅ | ✅ | 1 |
| `reset_assertiva_monthly_credits` | ✅ | | 0 |
| `reset_assertiva_monthly_credits` | ✅ | | 0 |
| `reset_demo_project` | ✅ | ✅ | 0 |
| `set_feature_flag_overrides_updated_at` | — | — | 0 |
| `touch_card_acquirer_rates_updated_at` | — | — | 0 |
| `touch_dre_det_antecipacao_linha_updated_at` | — | — | 0 |
| `touch_endividamento_contratos_updated_at` | — | — | 0 |
| `touch_loan_contracts_updated_at` | — | — | 0 |
| `trg_jornada_categorizacao` | ✅ | | 0 |
| `trg_jornada_impeditivos` | ✅ | | 0 |
| `trg_jornada_primeira_dre` | ✅ | | 0 |
| `trg_jornada_upload_feito` | ✅ | | 0 |
| `trg_mark_dossier_stale_direct` | ✅ | | 0 |
| `trg_jornada_categorizacao` | ✅ | | 0 |
| `trg_jornada_impeditivos` | ✅ | | 0 |
| `trg_jornada_primeira_dre` | ✅ | | 0 |
| `trg_jornada_upload_feito` | ✅ | | 0 |
| `trg_mark_dossier_stale_direct` | ✅ | | 0 |
| `trg_mark_dossier_stale_module_analyses` | ✅ | ✅ | 0 |
| `trg_mark_dossier_stale_via_client` | ✅ | | 0 |
| `trg_mark_dossier_stale_via_client` | ✅ | | 0 |
| `trg_project_analise_rica` | ✅ | ✅ | 0 |
| `update_cockpit_actions_updated_at` | — | — | 0 |
| `update_employees_updated_at` | — | — | 0 |
Expand All @@ -3378,5 +3346,5 @@ _Tabelas do produto Lite — namespace próprio, não se mistura com o principal
| `upsert_cost_audit_items` | — | — | 1 |
| `validar_folha_vs_dre` | — | — | 1 |
| `validar_soma_transacao_linha_split` | — | — | 0 |
| `validate_invite` | ✅ | | 1 |
| `validate_invite` | ✅ | | 1 |

49 changes: 43 additions & 6 deletions db/snapshot/SNAPSHOT.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

| Campo | Valor |
|---|---|
| **Status** | 🟢 **AUTORITATIVO** — marco zero. A baseline `00000000000000_baseline.sql` **é** este dump, e o replay dela do zero foi provado em Postgres 17.6 limpo (log de 2026-08-11 abaixo). |
| Dump gerado em | **2026-08-11** (`supabase db dump --linked` + `--role-only`, projeto `tdlxqqgechxhkygdmsxq`, CLI v2.84.0) — byte a byte igual ao de 2026-08-10, **zero drift** em 24h |
| Commit git no dump | `chore/baseline-do-banco-real` |
| `ultima_migration_incluida` | **`00000000000000`** (a baseline) — nenhuma migration incremental depois dela ainda |
| `ultima_migration_no_repo` | **`00000000000000`** |
| **Veredito** | 🟢 **autoritativo** — `schema.sql` **14.899 linhas**; `roles.sql` **13** (diff vazio). |
| **Status** | 🟢 **AUTORITATIVO** — inclui as duas migrations de RLS de 2026-08-12, aplicadas no SQL Editor e conferidas neste dump. |
| Dump gerado em | **2026-08-12** (`supabase db dump --linked`, `--role-only` e `--data-only`, projeto `tdlxqqgechxhkygdmsxq`, CLI v2.84.0) |
| Commit git no dump | `fix/rls-tabelas-orfas` |
| `ultima_migration_incluida` | **`20260812010000`** |
| `ultima_migration_no_repo` | **`20260812010000`** |
| **Veredito** | 🟢 **autoritativo** — `schema.sql` **15.030 linhas**; `roles.sql` **13** (diff vazio); `seed.sql` **25.175**. |

## A regra, depois da baseline de 2026-08-11

Expand All @@ -26,6 +26,43 @@ O mapa legível de tudo isso é [`db/ESTRUTURA.md`](../ESTRUTURA.md), **gerado**
`npx tsx scripts/db/gerar-estrutura.ts`. Depois de todo re-dump, rode o gerador: o
diff do `.md` mostra exatamente o que mudou no banco.

### Validação do re-dump (2026-08-12, RLS das tabelas órfãs)

Migrations `20260812000000` (RLS nas 5 tabelas + `search_path` em 39 funções) e
`20260812010000` (corrige o DELETE de `employees`), aplicadas no SQL Editor.

| Objeto | Antes | Depois |
|---|--:|--:|
| Tabelas | 112 | **112** |
| Views | 12 | **12** |
| Funções | 73 | **73** |
| Triggers | 92 | **92** |
| **Policies** | 348 | **360** |
| **Tabelas com RLS** | 107 | **112 de 112** |
| Funções `SECURITY DEFINER` sem `search_path` | 29 | **0** |

`roles.sql`: diff vazio. `seed.sql` 25.164 → 25.175: nenhuma tabela perdeu linha.

**O re-dump pegou um efeito colateral que a migration não mostrava.** Esperávamos
+16 policies e vieram +12. A diferença revelou que `employees` **já tinha 6
policies** — dormentes, porque policy existe mesmo com RLS desligada e só não é
aplicada. O `DROP POLICY IF EXISTS` substituiu 4 delas às cegas. Comparadas uma a
uma contra o dump anterior: `select`/`insert`/`update` não ampliaram nada (as
antigas dependiam de `project_members`, e as policies `employees_cliente_*`, não
tocadas, já traziam a mesma cláusula de staff), mas `employees_delete` era
`is_admin()` puro e passou a permitir consultor apagar linha de folha. A
`20260812010000` devolveu ao original.

⚠️ **Correção de um número afirmado antes de aplicar.** Foi dito que as 5 tabelas
estavam com zero linhas; o método de contagem estava errado (procurava blocos
`COPY`, e este dump usa `INSERT`). Contagem correta: `employees` 0 · `meetings` 0
· `consultant_memories` ~4 · `company_ai_dossiers` ~5 · `categorization_patterns`
~33. Conferido depois, no dado: **ninguém perdeu acesso** — `profiles` tem 1
`owner` + 2 `admin` (cobertos por `is_admin`) e 3 `cliente`, e `project_members`
tem exatamente 3 linhas `role='cliente'` para eles (é o que a view
`client_project_access` enxerga). O 4º membro é `consultor` no projeto mas
`owner` em `profiles`, coberto pelas duas pontas.

### Prova do replay do zero (2026-08-11)

O que a baseline existe para garantir — e que **nunca** foi verdade antes dela.
Expand Down
Loading
Loading