# Requerimentos — Gaps, Bugs e Dúvidas

> **Versão analisada:** `v6` — módulo **novo na v6**
> **Slug:** `requerimentos` (`modulos.id = 41`)

---

## 1. Bugs e fragilidades confirmadas no código

| # | Severidade | Descrição | Evidência | Correção sugerida |
|---|---|---|---|---|
| B1 | **Alta** | `RequerimentoController@view` não valida a **categoria**: busca o conteúdo apenas por `slug`. Um conteúdo de qualquer outro módulo é acessível em `/requerimentos/{qualquer}/{slug}` | `v6/app/Http/Controllers/Portal/RequerimentoController.php:187` (`Content::where('slug', $slug2)->first()`) | Restringir a busca às descendentes da raiz `requerimentos` (como faz a view SQL) |
| B2 | **Alta** | Divergência de escopo entre portal e CMS: o portal lista só as **filhas diretas** da raiz, enquanto a view SQL lista **toda a árvore**. Conteúdo em sub-subcategoria é gerenciado no CMS mas **invisível** no portal | `RequerimentoController@show` (`whereHas('categoryContents')`) vs. view `requerimentos` (`WITH RECURSIVE cat_tree`) | Unificar em um único escopo (recomendação: árvore completa) |
| B3 | **Alta** | O CMS lista rascunhos e conteúdos futuros (a view SQL não filtra `status` nem `publication_date`), enquanto o portal oculta os futuros | view `requerimentos` | Filtrar por `status`/`published_at` no DataTable ou sinalizar visualmente |
| B4 | **Alta** | Permissões `requerimentos.*` **não existem** para o papel `administrador` (verificado em `roles.permissions`); só `super-admin` (que passa por `has_access`) opera o módulo | `v6/app/Library/Helper/appHelpers.php:22-42` + banco | Carregar as permissões `requerimentos.index|create|edit|delete|restore|inactivate` para o papel `administrador` |
| B5 | **Média** | Busca textual com `translate()` **hardcoded**: acentos fora da lista (`ñ`, por exemplo) não são normalizados; `clean_slug()` e o `translate()` do banco usam tabelas diferentes | `RequerimentoController@show` | Unificar a normalização em uma função (ou usar `unaccent` do PostgreSQL) |
| B6 | **Média** | O parâmetro `texto` é concatenado na URL **sem `encodeURIComponent`**: termos com `&`, `#`, `+` quebram a requisição Ajax | JS em `*/requerimentos/show.blade.php` | `encodeURIComponent(text)` |
| B7 | **Média** | O bloco de filtros/Ajax está **duplicado em 6 temas**; correções precisam ser replicadas | `modelo1`…`modelo6/requerimentos/show.blade.php` | Componente Blade único (v7) |
| B8 | **Média** | `row_number()` na view gera a coluna `value` **instável** entre requisições | view `requerimentos` | Usar `c.id` como identificador e remover `value` |
| B9 | **Média** | A view SQL foi recriada **4 vezes**; o `down()` da migration vigente recria a view **sem** a coluna `requerente`, e o `down()` da migration de 2026-06-09 volta à versão de filhas diretas — um `migrate:rollback` parcial deixa a view fora do estado esperado | migrations `2024_10_08_005745`, `2025_08_12_112801`, `2026_06_09_110000`, `2026_06_12_100100` | Migrar o dado para uma tabela real (v7) e aposentar a view |
| B10 | **Média** | `str_limit()` foi removido no Laravel 6+ — incompatível com a v7 | `*/requerimentos/list.blade.php` | `Str::limit()` |
| B11 | **Baixa** | Divergência entre a dica da UI (`maxlen 150`) e a validação (`max:120`) | `prefecture/contents/requerimentos/form.blade.php` vs. `ContentRequest.php:243` | Alinhar os dois valores |
| B12 | **Baixa** | O título da aba lateral é o nome da **categoria ativa**, mas o link do item aponta para a categoria ativa, não para a categoria real do conteúdo | `*/requerimentos/list.blade.php` | Usar `$content->categoryContents->slug` no link |
| B13 | **Baixa** | A raiz `requerimentos` aparece na view do CMS? Não — a view exclui a raiz por `id NOT IN (...)`; mas o **formulário** de criação oferece apenas as filhas, e se nenhuma existir não há como criar conteúdo | `ContentController.php:218-221`, view `requerimentos` | Permitir o uso da raiz ou criar subcategoria por padrão |
| B14 | **Baixa** | `publication_date` é `timestamp(0) without time zone` e a view converte com `::timestamp with time zone`: o `to_char` pode exibir o dia errado próximo à meia-noite, conforme o fuso do servidor PostgreSQL | view `requerimentos` | Migrar para `timestamp with time zone` |
| B15 | **Baixa** | `contents.councilmen_id` é uma coluna genérica em tabela genérica, usada por um único módulo | `v6/database/migrations/2026_06_12_100000_add_councilmen_id_to_contents_table.php` | Modelo `Requerimento` próprio (v7) |

## 2. Padrão inseguro em módulo vizinho

O `Portal\AtasEPontosController` — módulo **irmão** (mesmo padrão de conteúdo
genérico) — monta a busca concatenando o termo **dentro** da string SQL:

```php
// v6/app/Http/Controllers/Portal/AtasEPontosController.php:80
$query->whereRaw("translate(lower(title), ' ''àáâãäéèëêíìïîóòõöôúùüûçÇ', '--aaaaaeeeeiiiiooooouuuucc') ilike '%" . $newTXT . "%'")
```

Isso é **injeção SQL potencial** (o termo do usuário entra cru na query). O
`RequerimentoController`, por sorte, usa binding `?`:

```php
->whereRaw("translate(lower(title), ...) ilike ?", array('%' . $newTXT . '%'))
```

**Regra para a v7:** nunca replicar o padrão do `AtasEPontosController`; a
busca textual deve ser feita com bindings (`whereRaw` + `?`) ou, melhor, com
`whereLike` do Eloquent.

## 3. Funcionalidades incompletas ou mortas

| Item | Situação |
|---|---|
| Módulo `requerimentos` na tabela `modulos` | `status = false` no seed — só aparece após o operador ligá-lo em `/cms/modulos` |
| `menu_orders.clicks` do slug `requerimentos` | `0` — o módulo praticamente não foi usado no ambiente analisado |
| Subcategorias | Nenhuma subcategoria cadastrada no banco; o portal cai no fallback (raiz) |
| Exportação (Excel/PDF/CSV) | Inexistente no módulo |
| Numeração/protocolo do requerimento | Inexistente (campo `referencia` existe em `contents`, mas não é usado por este módulo) |
| API pública | Inexistente; só há JSON “HTML dentro” no Ajax do portal e JSON DataTables no CMS |
| `contents.status` | O formulário de requerimentos escondeu o campo “Publicar” (bloco comentado em `form.blade.php`), mas o `store`/`update` continuam lendo `status` do request |

## 4. Riscos de migração de schema

| Risco | Descrição | Mitigação |
|---|---|---|
| Colisão de slugs | O `slug` é único apenas por convenção do `ContentController` (checagem global). A migração para tabela própria precisa garantir unicidade | Gerar slug por requerimento com sufixo determinístico e índice `unique` |
| `clicks` | `contents.clicks` vira `requerimentos.views` — perder esse dado é perda de métrica institucional | Copiar no `INSERT ... SELECT` e validar contagem |
| `councilmen_id` de vereador removido | `LEFT JOIN ... AND cm.deleted_at IS NULL` faz a view devolver `NULL` | Preservar o nome histórico em `metadata` ou desnormalizar `requerente_nome` |
| Anexos | `archives` é compartilhado; `highlight` e `view_pdf` são flags genéricas | Criar `requerimento_attachments` com `kind` |
| View SQL | Consumida apenas pelo `DatatableController` | Dropar a view depois da migração |
| Fuso horário | `timestamp(0) without time zone` | Converter para `timestamp with time zone` na migração |
| Volume | `contents` guarda também os demais módulos | Migrar apenas os descendentes da raiz `requerimentos` |

## 5. Mapa v5 → v6

### 5.1 O módulo não existe na v5

| Dimensão | v5 | v6 |
|---|---|---|
| Rota de portal | **Nenhuma** — `v5/routes/web.php` não tem rota de requerimentos | `/requerimentos`, `/requerimentos/{slug}`, `/requerimentos/{slug1}/{slug2}` (`v6/routes/web.php:44-46`) |
| Rota de CMS | **Nenhuma** — `v5/routes/basepro.php` não tem rota de requerimentos | CRUD genérico com slug `requerimentos` (`v6/routes/basepro.php:235-240`) |
| Migration | **Nenhuma** | 4 migrations da view `requerimentos` (2024-10-08, 2025-08-12, 2026-06-09, 2026-06-12) |
| Model | **Nenhum** | Reaproveita `Content`, `CategoryContent`, `CityCouncilor` |
| Controller | **Nenhum** | `Portal\RequerimentoController` + CRUD genérico `Prefecture\ContentController` |
| View | **Nenhuma** | `*/requerimentos/{show,list,view,nav|pub-nav}.blade.php` nos 6 temas |
| View SQL | **Nenhuma** | `requerimentos` |
| Seed | **Nenhum** | `CategoryContentTableSeeder`, `ResourcesTableSeeder`, `MenuOrderSeeder`, `MenuItemsTableSeeder` |
| Permissão | **Nenhuma** | `requerimentos.*` (apenas `super-admin` no ambiente analisado) |
| Coluna do requerente | `contents` **sem** `councilmen_id` | `contents.councilmen_id` (migration `2026_06_12_100000`) |

> Confirmação por busca: em `v5/`, as únicas ocorrências de “requerimento”
> pertencem ao módulo **Requerimento de Diárias**
> (`v5/routes/basepro.php:186`, `v5/routes/web.php:189`, tabela `req_diarias`),
> que é **outro produto** (solicitação de diárias de viagem) e não tem relação
> funcional com requerimentos de vereador.

### 5.2 Lacuna funcional reprezentada

Na v5 o portal **não oferecia**:

1. **Publicação** de requerimentos de vereadores — nenhuma entidade, tela ou
   registro administrativo para criar, categorizar e publicar uma propositura.
2. **Consulta pública** de requerimentos — nenhuma busca, listagem ou página
   pública de proposituras.
3. **Vínculo com o autor** — não havia coluna que identificasse o vereador
   requerente de um conteúdo.
4. **Anexos do requerimento** — o portal não expunha documentos vinculados a
   uma propositura com download.
5. **Métricas de acesso** por requerimento (`clicks`).

Consequência prática: qualquer pedido de propositura era respondido por e-mail,
ofício ou atendimento presencial, sem rastro público e sem contagem de
interesse da população.

### 5.3 O que precisa ser decidido/replicado na v7

| # | Decisão | Detalhe |
|---|---|---|
| D1 | Modelo próprio ou `contents` genérico | Recomendação: tabela `requerimentos` dedicada (elimina B2, B3, B15) |
| D2 | Escopo das categorias | Árvore completa (como a view) ou filhas diretas (como o portal)? Unificar |
| D3 | Regra de “publicado” | `status` + `published_at`; rascunhos e futuros visíveis no CMS? |
| D4 | Requerente obrigatório | Manter opcional ou exigir vereador? |
| D5 | Numeração/protocolo | Adicionar `protocolo`/`numero` sequencial por ano? |
| D6 | Permissões | Definir quem pode criar/editar/excluir além do `super-admin` |
| D7 | Exportação | Oferecer CSV/Excel dos requerimentos (transparência ativa)? |
| D8 | Filtros | Incluir filtro por **requerente** e por **categoria** na listagem? |
| D9 | Contador de acessos | Manter `clicks` (renomeado para `views`) com proteção contra incremento por robô? |
| D10 | Catálogo de categorias | Categorias padrão (“Requerimento de Informação”, “Urgência”, “Requerimento de Moção”) devem ser seedadas? |

## 6. Perguntas em aberto para o negócio

1. Um requerimento deve poder ser **vinculado a uma sessão da Câmara**?
2. Deve existir o fluxo de **protocolo e resposta** do requerimento (cidadão
   acompanha o andamento)?
3. Os requerimentos devem ser **numerados** por ordem cronológica?
4. O portal deve exibir o **parlamentar** (partido, mandato) junto ao nome do
   requerente?
5. Deve haver **notificação** aos vereadores quando o requerimento é publicado?
6. Em caso de **requerimento sigiloso**, como o CMS o oculta do portal?
7. Qual o tempo de **retenção** dos requerimentos e de seus anexos?

## 7. Checklist de validação antes de migrar

- [ ] Confirmar com o município se a listagem deve ser unificada ou por categoria.
- [ ] Exportar `contents` (descendentes da raiz `requerimentos`) com
      `councilmen_id`, `clicks`, `publication_date` e `slug`.
- [ ] Conferir slugs duplicados e decidir a renomeação.
- [ ] Testar a migração dos anexos (`archives`) e do `highlight`/`view_pdf`.
- [ ] Validar a carga das permissões `requerimentos.*` para o papel
      `administrador`.
- [ ] Verificar o fuso horário do servidor PostgreSQL antes de converter
      `publication_date`.
