# Transparência Fiscal — Resumo

> Módulo: `V2/Modules/DadosAbertos/TransparenciaFiscal`
> Grupo: `DadosAbertos` (vizinho: `V2/Modules/DadosAbertos/Operators`)
> Índice desta pasta: `00` a `09` (ver "Mapa do dossiê" ao final).
> Raiz de referência dos caminhos: `/home/one/projects/maxima/v7/lotep`.

## Objetivo

Publicar, no portal, **itens de transparência fiscal** — arrecadação, repasses,
folha de pagamento, despesas — organizados em uma **árvore de categorias de dois
níveis** (raiz e filhas) e mantidos por um CRUD simples no CMS.

O módulo não é um portal de dados abertos: é uma **vitrine editorial de valores
fiscais**. Não há upload de anexo, exportação, API nem metadados padronizados
(DCAT/INDA). Cada item é um registro com título, descrição livre, valor
monetário e uma data de referência (competência).

Para quem:

- **Cidadão** — consulta `/transparencia` e vê os cards por categoria e o
  detalhe de cada item em `/transparencia/{slug}`.
- **Operador do CMS** — com permissão `write` no módulo, cadastra, edita,
  ordena (por coluna) e exclui itens; **não** cria categorias.

## Efeito / impacto

| Aspecto | Situação |
|---|---|
| Dado publicado | 2 tabelas próprias: `v2_transparency_categories`, `v2_transparency_items` |
| Superfícies | 2 rotas públicas (`V2/Modules/DadosAbertos/TransparenciaFiscal/Routes/web.php`) + 6 rotas de CMS (`V2/Modules/DadosAbertos/TransparenciaFiscal/Routes/cms.php`) |
| Integrações | SEO (título), auditoria (`V2AuditLogger`), log de ações (`CmsActionLogger`), importador legado V1 |
| Efeito externo | Nenhum: sem fila, sem e-mail, sem storage, sem cache |
| Ativação | Rota CMS protegida por `module.active:TransparenciaFiscal` (flag em banco) |
| Desativação | Item/categoria inativos somem do portal; o CMS continua acessível com `read` |

## Comportamento observável

### Portal público

`GET /transparencia` — `TransparencyPortalController@index`
(`V2/Modules/DadosAbertos/TransparenciaFiscal/Controllers/Portal/TransparencyPortalController.php:15-21`):

1. Carrega **somente categorias raiz ativas** (`active()->root()->ordered()`).
2. Eager loads `children` (todas, **sem filtro de `is_active`**) e `items`
   apenas da raiz, filtrados por `active()`.
3. A view renderiza um card por raiz:
   - `$cat->items` (ativos);
   - depois, para cada `$cat->children`, um bloco com `$child->items` — estes
     são carregados sob demanda na view (`V2/Modules/DadosAbertos/TransparenciaFiscal/Views/portal/index.blade.php:54`),
     gerando **N+1** e **incluindo itens inativos** porque o filtro `active()`
     não foi aplicado à relação `children.items`.
4. Não há paginação, busca, filtro por ano nem totalização.

`GET /transparencia/{slug}` — `show`
(`V2/Modules/DadosAbertos/TransparenciaFiscal/Controllers/Portal/TransparencyPortalController.php:26-34`):

- Busca **apenas item ativo** por `slug` (`firstOrFail` ⇒ 404 se inativo).
- Define o título da página no `SEOService` (singleton, ver
  `V2/Services/SEO/Providers/SEOServiceProvider.php:70`), dentro de `try/catch`
  silencioso.
- Renderiza descrição, valor (formatado `R$ 0,00`), data de referência e
  categoria. Um item **sem categoria** mostra "Sem categoria" apenas na
  ausência do bloco (a view omite a linha).

### CMS

`/cms/v2/transparencia` — `TransparencyCmsController`
(`V2/Modules/DadosAbertos/TransparenciaFiscal/Controllers/Admin/TransparencyCmsController.php`):

| Ação | Comportamento |
|---|---|
| `index` | busca por título (`ILIKE`), filtro por categoria, ordenação por coluna permitida, **paginação de 20** (`:39-41`) |
| `create`/`store` | formulário + validação; `id = v2_uuid()`; auditoria `created` |
| `edit`/`update` | edição; auditoria `updated` |
| `destroy` | soft delete; auditoria `deleted` |

Cada acesso ao `index` grava um evento INFO de log de CMS (`:43-49`), mesmo
sendo leitura.

## Detalhes e particularidades técnicas

- **Slug do item** é derivado na criação: `Str::slug(title)` + `-` + primeiros 8
  caracteres do UUID (`V2/Modules/DadosAbertos/TransparenciaFiscal/Models/V2TransparencyItem.php:68-70`). Não há hook de
  atualização: renomear o título **mantém** o slug antigo. O slug é `nullable` e
  tem índice **não único** (`Migrations/2026_08_31_000001_...:17-18`).
- **Slug da categoria** é derivado de `name` na criação **e** na atualização
  quando `name` muda (`V2/Modules/DadosAbertos/TransparenciaFiscal/Models/V2TransparencyCategory.php:63-77`). A coluna é
  `unique` global (`Migrations/2026_07_17_000001_...:20`).
- **Hierarquia**: só existe `parent_id` (sem FK auto-referente, sem limite de
  profundidade no banco). O portal renderiza **um único nível** de filhas; netas
  existem no schema, importador as cria, e **nunca aparecem** no portal.
- **Ordenação**: `order` (inteiro, default 0) e, no empate, `name`
  (`V2/Modules/DadosAbertos/TransparenciaFiscal/Models/V2TransparencyCategory.php:112-115`). `order` não tem índice.
- **Valor**: `decimal(15,2)` no banco, cast **`float`** no model
  (`V2/Modules/DadosAbertos/TransparenciaFiscal/Models/V2TransparencyItem.php:59`) — precisão monetária tratada como
  ponto flutuante.
- **Competência**: `reference_date` (`date`, nullable) é a única dimensão
  temporal; não há `period`/`year`/`month` normalizados.
- **`metadata` jsonb**: existe nos dois models (`array` cast), mas **nenhuma
  view ou controller o consome**. O importador V1 grava ali
  `{imported_from: v1, legado: {arquivos: [...]}}`
  (`V2/Modules/Support/MigracaoLegada/Services/TransparenciaFiscalLegacyImporter.php:146-158`).
- **`TransparencyRequest`**: calcula `$id` na linha 21 e **não usa**; além
  disso, `authorize()` duplica o `v2_can()` que os controllers já fazem.
- **Sem CRUD de categorias**: o único caminho de criação/edição é o
  `V2/Modules/DadosAbertos/TransparenciaFiscal/Seeds/TransparenciaSeeder.php` (idempotente por slug) e o importador legado.
- **Sem anexo/CSV/API**: não há tabela de arquivos, rota de download nem
  endpoint JSON — o requisito LAI de dado legível por máquina não é atendido.

## Estrutura de dados (resumo)

```
v2_transparency_categories (id uuid char36, name, slug UNIQUE, description,
                            parent_id, is_active, "order", metadata jsonb,
                            timestamps, deleted_at, v1_id, imported_at)
        ^ parent_id (sem FK)          ^ 1:N
v2_transparency_items     (id uuid char36, title, slug nullable/index,
                           description, value decimal(15,2), reference_date date,
                           is_active, v2_transparency_category_id FK -> categories
                           ON DELETE SET NULL, metadata jsonb, timestamps,
                           deleted_at, v1_id, imported_at)
```

Detalhamento em `02-MODELAGEM.md`.

## Como testar (resumo)

Hoje **não existe nenhum teste do módulo**. Cobertura indireta:

- `tests/Feature/V2/Rbac/CommonUserModuleAccessE2ETest.php:52` — acesso de
  usuário comum à rota `v2.transparencia.items.index` com a chave canônica
  `TransparenciaFiscal` e o slug legado `transparencia`.
- `tests/Unit/V2/Modules/Infraestrutura/MigracaoLegada/TransparenciaFiscalLegacyImporterTest.php`
  — testa o importador (módulo Support), não os controllers/views.
- `tests/Feature/V2/Modules/Infraestrutura/MigracaoLegada/LegacyNovosImportadoresEndpointsTest.php:67`
  — preview/run do importador.

Plano de testes proposto (16 casos) em `07-TESTES.md`.

Para rodar a suíte existente:

```bash
cd /home/one/projects/maxima/v7/lotep
vendor/bin/phpunit --filter 'Transparencia|CommonUserModuleAccess' --testsuite Feature
```

## Migração para base moderna (resumo)

O V7 já possui `src/modules/Portal/DadosAbertos` (caminhos do V7 relativos a
`public-v7/`; os do legado, a `lotep/`) — catálogo de **datasets** com
`jsonb` (`columns`/`rows`), metadados DCAT/INDA, export CSV/JSON/XML com
`ETag`/SHA-256 e API pública paginada. A Transparência Fiscal **não deve ser
recriada como CRUD de itens soltos**: deve virar **datasets fiscais** nesse
módulo (ou um módulo irmão que reutilize as mesmas Actions).

| Peça legada | Destino V7 |
|---|---|
| Árvore de categorias com `order` | `theme` DCAT + `tags`, ou hierarquia em tabela de catálogo |
| `reference_date` (competência) | coluna de dados + `periodicity`/`updated_at` do dataset |
| `value decimal(15,2)` | célula `jsonb` da matriz (numérica), nunca `float` PHP |
| Item com descrição livre | `summary`/`description` do dataset, RichText AST se necessário |
| Sem export | `DadosAbertosExport` (CSV/JSON/XML + `ETag` + checksum) |
| Sem API | `DadosAbertosApiIndexController` (`/api/v1/datasets`) |
| `is_active` booleano | `status` `rascunho|publicado` + `published_at` |

Justificativa nas ADR-018 (dados abertos), ADR-017 (`jsonb`, migrations
seguras), ADR-033 (RBAC `{module}.{ver|editar|aprovar|arquivar}`) e ADR-022
(`ETag`/CSP). Análise completa em `09-APROVEITAMENTO-V7.md`.

## Resumo geral

Transparência Fiscal é o módulo mais simples do grupo `DadosAbertos`: duas
tabelas, dois controllers, cinco blades e um seeder. Cumpre a função de
publicar números no portal, mas tem **dívidas estruturais** claras:

1. hierarquia sem CRUD e renderizada só em um nível;
2. `children` sem `active()` — N+1 e itens inativos expostos;
3. slug imutável após renomear e sem `UNIQUE`;
4. sem paginação no portal;
5. sem anexo, CSV ou API — **não atende LAI** para dado reutilizável;
6. nenhum teste do módulo;
7. `TransparencyRequest` com código morto (`$id`) e autorização duplicada.

O V7 já superou o modelo (dataset + DCAT + export com `ETag`); o que merece
porte é a **semântica de competência** e a **hierarquia ordenada** como
metadado, não o CRUD.

## Mapa do dossiê

| Arquivo | Conteúdo |
|---|---|
| `00-RESUMO.md` | este arquivo |
| `01-ARQUITETURA.md` | fluxo de requisição, classes, dependências |
| `02-MODELAGEM.md` | tabelas, colunas, migrations, seeder, importador |
| `03-ROTAS-E-CONTROLADORES.md` | rotas, middleware, permissões, validação |
| `04-VIEWS-E-UX.md` | blades, layouts, a11y, ausência de JS/export |
| `05-REGRAS-E-PORTABILIDADE.md` | regras de negócio, acoplamentos, armadilhas |
| `06-MIGRACAO-MODERNA.md` | migration/model/request/policy/endpoints alvo |
| `07-TESTES.md` | estratégia e casos de teste |
| `08-GAPS-E-DIVIDAS.md` | bugs, lacunas, riscos de dados, mapa V1→V2 |
| `09-APROVEITAMENTO-V7.md` | o que portar/adaptar/rejeitar frente ao V7 |
