# Comitê de Investimentos — Gaps, Bugs e Dívidas

## 1. Bugs e erros conhecidos

| # | Severidade | Local | Descrição | Impacto |
|---|---|---|---|---|
| B1 | **Crítico** | `v6/app/Http/Controllers/Portal/ComiteDeInvestimentosController.php:57` | SQL montado por concatenação de `{$_GET['ano']}` em `DB::select`, numa rota **pública** | SQL injection explorável por visitante anônimo |
| B2 | **Crítico** | `v6/app/Http/Controllers/Portal/ComiteDeInvestimentosController.php:84-87` | `whereRaw` com o texto concatenado (o texto passa por `clean_slug`, que hoje remove aspas, mas não escapa `%`/`_`) | Busca por curinga; risco de injeção reintroduzido se `clean_slug` mudar |
| B3 | Alto | `v6/app/Http/Controllers/Portal/ComiteDeInvestimentosController.php:100-113` | A resposta AJAX renderiza `publicacoes.list` em vez de `comite-de-investimentos.list` | O HTML muda ao filtrar; layout inconsistente |
| B4 | Alto | `v6/app/Http/Controllers/Portal/ComiteDeInvestimentosController.php:27` | `CategoryContent::where(slug,comite)->first()->id` sem verificação de nulo | Erro 500 se a categoria-pai não existir |
| B5 | Alto | `v6/app/Http/Controllers/Portal/ComiteDeInvestimentosController.php:15-22` | `index()` usa `->first()->slug` sem verificação | Erro 500 se o comitê não tiver subcategorias |
| B6 | Alto | `v6/app/Http/Controllers/Portal/ComiteDeInvestimentosController.php:119` | `view()` busca a ata por `slug` **sem** filtrar `deleted_at` nem a categoria do comitê; slug inexistente → erro 500 | Ata excluída continua acessível; acesso indevido a conteúdo de outro módulo com slug homônimo |
| B7 | Alto | `v6/app/Http/Requests/ContentRequest.php` (ramo `default`) | `publication_date` e `text` **não** são validados, embora o formulário os marque como obrigatórios | Ata sem data nunca aparece no portal (filtro `publication_date <= now` não casa `NULL`) |
| B8 | Médio | `v6/resources/views/prefecture/contents/comite-de-investimentos/form.blade.php` | `title` com `maxlen=150` e texto "Máximo de 150 caracteres", mas a regra exige `max:120` | Operador recebe erro de validação inesperado |
| B9 | Médio | `v6/app/Http/Controllers/Portal/ComiteDeInvestimentosController.php:29-31` | `CategoryContent::whereHas('categoryContents', function ($query) {})` — closure vazia | Consulta inerte executada a cada requisição |
| B10 | Médio | `v6/app/Http/Controllers/Prefecture/CategoryContentController.php:282-284` | Log registra "Acessou criação de categoria de **Requerimentos**" no ramo do comitê | Rastreabilidade de auditoria incorreta |
| B11 | Médio | `v6/app/Http/Controllers/Portal/ComiteDeInvestimentosController.php:35-37` | `if (empty($category) && !isset($category))` — condição redundante | Código confuso; risco de refatoração incorreta |
| B12 | Médio | `v6/app/Http/Controllers/Portal/ComiteDeInvestimentosController.php:126-127` | Incremento de `clicks` sem transação nem lock | Perda de contagem em concorrência |
| B13 | Baixo | migration `2024_10_16_011540` | `row_number() OVER (ORDER BY publication_date asc)` com `ORDER BY publication_date desc` | A coluna `value` não acompanha a ordem exibida |
| B14 | Baixo | migration `2024_11_29_103842` | `DROP VIEW if exists` antes de `CREATE OR REPLACE` | Redundante; quebra dependências e revoga concessões |
| B15 | Baixo | `v6/resources/views/prefecture/category-contents/comite-de-investimentos/create.blade.php` | Título "Categorias de Comitê de **Invertimentos**" | Erro de digitação visível ao operador |
| B16 | Baixo | `v6/resources/views/prefecture/contents/comite-de-investimentos/form.blade.php` | Carrega `assets/js/custom/contents/noticias/edit.js` | Dependência cruzada indevida |
| B17 | Baixo | `v6/app/Http/Controllers/Portal/ComiteDeInvestimentosController.php:54-60` | O filtro de meses só é montado se `ano` estiver preenchido | O seletor de mês nasce vazio na primeira visita |

## 2. Funcionalidades incompletas ou mortas

| Item | Situação |
|---|---|
| Checkbox "Publicar" (`status`) no formulário do comitê | Está **comentado** no Blade (`{{-- bloco comentado --}}`), mas o `ContentController@store` grava `$request->get('status')` — sempre `null` |
| Campo "Link Permanente" (`slug`) | Exibido em edição, mas **não validado** e não é gravado pelo `store` (a slug é gerada do título) |
| `highlight` / `galery` | O `form.blade.php` não expõe os campos; o `ContentController` os grava a partir da request |
| `modulos.status` | O módulo nasce **desativado** (`status = 0` na migration e no banco de dev); o item de menu só aparece se o administrador o ativar em `/cms/modulos` |
| View SQL `comite_de_investimentos` | Consumida apenas pelo DataTable; o portal não a usa |
| Subcategorias do comitê | **Não existem** no banco de desenvolvimento — o módulo está praticamente inerte |

## 3. Itens existentes na v5 e removidos na v6 (e vice-versa)

**Nada do comitê existe na v5.** Verificação executada:

```bash
find v5 -iname '*comite*'                # nenhum resultado
grep -rli 'comite' v5/app v5/resources v5/routes v5/database   # nenhum resultado
```

| Elemento | v5 | v6 |
|---|---|---|
| Rota portal `/comite-de-investimentos` | ausente | `v6/routes/web.php:219-221` |
| `Portal\ComiteDeInvestimentosController` | ausente | presente |
| Views portal do comitê (6 temas) | ausentes | presentes |
| Views CMS de conteúdo e categoria do comitê | ausentes | presentes |
| View SQL `comite_de_investimentos` | ausente | criada em out/2024, ampliada em nov/2024 |
| Registro em `modulos` | ausente | id 48 (`status = 0`) |
| `resources` com `slug = 'comite-de-investimentos'` | ausente | id 68, `modulo_id = 48` |
| `menu_orders` com `slug = 'comite-de-investimentos'` | ausente | id 31, `modulo_id = 48`, `clicks = 0` |
| Item no menu do CMS | ausente | `v6/app/Providers/MenuServiceProvider.php:148` |

O módulo é, portanto, **funcionalidade nova da v6**.

## 4. Riscos de dados (migração de schema)

| Risco | Descrição | Mitigação |
|---|---|---|
| View SQL dependente de estrutura | `comite_de_investimentos` depende de `contents` e `category_contents`; alterar colunas quebra a migration de atualização (que recria a view) | Versionar as views junto com o schema; testar `DROP`/`CREATE` em ambiente de staging |
| `DROP VIEW` derruba GRANTs | `DROP VIEW` remove as permissões da view no PostgreSQL | Substituir por `CREATE OR REPLACE` na v7 |
| Slugs não são únicos porconstraint | `contents.slug` e `category_contents.slug` **não** têm índice único; a unicidade é apenas convenção do controller (`Content::where('slug',$slug2)`) | Adicionar índice único parcial na v7 |
| Auto-FK em `category_contents` | `category_contents.category_contents_id` referencia a própria tabela; a "categoria-pai" é identificada por `NULL`, não por um tipo | Modelar `parent_id` explícito na v7 |
| Hard-dependência do slug `comite-de-investimentos` | A view e o controller identificam o comitê **pelo slug textual**; renomear o slug apaga o módulo silenciosamente | Usar `modulo_id` ou uma coluna de tipo |
| Ausência de backfill | Não há seed que crie a categoria-pai nem subcategorias | Script de seed idempotente na v7 |
| Contador `clicks` herdado | O mesmo campo é usado por ~15 módulos; migração para tabela própria exige backfill e queries de soma | Manter em `contents` ou migrar com relatório de conferência |

## 5. Dívidas técnicas

| # | Dívida | Esforço estimado na v7 |
|---|---|---|
| D1 | Eliminar o SQL interpolado (`DB::select` e `whereRaw`) | Baixo — trocar por bindings/Eloquent |
| D2 | Criar `FormRequest` próprio (`StoreComiteMinuteRequest`) com `title`, `text`, `publication_date`, `category_contents_id` | Baixo |
| D3 | Deixar de despachar por `switch` de slug — criar `ComiteController` e `ComiteCategoryController` | Médio |
| D4 | Trocar `check.route.permission` textual por Policies | Médio |
| D5 | Unificar o partial AJAX (deixar de usar `publicacoes.list`) | Baixo |
| D6 | Consolidar as 6 cópias de `comite-de-investimentos/*.blade.php` em componentes Blade reutilizáveis | Médio |
| D7 | Migrar o DataTable de `yajra/laravel-datatables-oracle` para paginação server-side | Médio |
| D8 | Substituir a view SQL por Resource JSON | Médio |
| D9 | Internacionalizar as strings fixas ("Comitê de Investimentos", "Categorias de Comitê de Investimento", mensagens de log) | Médio |
| D10 | Criar testes automatizados (hoje zero cobertura) | Médio |

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

1. O comitê deve ter **tabela própria** na v7, ou as atas continuam em `contents`?
   A resposta muda todo o plano de migração de dados.
2. A listagem pública deve mostrar **atas futuras** (como o CMS faz) ou apenas as
   publicadas? Hoje portal e CMS divergem.
3. O filtro de texto deve buscar também no **corpo** do documento ou apenas no
   título? Hoje busca nos dois, o que pode ser lento em arquivos grandes.
4. Quantas subcategorias são esperadas? O menu lateral usa rolagem a partir de
   14 itens.
5. Deve existir uma visualização "por ano" (calendário) além da lista paginada?
6. As atas precisam de **controle de acesso** (somente para membros do comitê) ou
   são públicas por definição?
7. O `clicks` deve contar abertura de ata, download de PDF ou ambos?
8. É preciso preservar o formato dos **links antigos** (`/comite-de-investimentos/{cat}/{slug}`) na migração?
9. O módulo entra no **menu do portal** (link visível ao cidadão) ou permanece
   acessível apenas por URL direta? Hoje há rota pública, mas o link do menu é
   gerenciado fora do módulo.

## 7. Resumo do mapa v5 → v6

```mermaid
flowchart LR
    V5["v5<br/>(nada existe)"] -->|"módulo novo"| V6["v6<br/>portal + CMS + view SQL"]
    V6 --> V6A["Portal\ComiteDeInvestimentosController<br/>index / show / view"]
    V6 --> V6B["ContentController + CategoryContentController<br/>(dispatch por slug)"]
    V6 --> V6C["view SQL comite_de_investimentos<br/>(out/2024 + nov/2024)"]
    V6 --> V6D["modulos id 48<br/>resources 68 / menu_orders 31"]
```

Conclusão: o Comitê de Investimentos é um **módulo novo da v6**, construído
sobre a infraestrutura genérica de `contents` / `category_contents`, com uma
view SQL própria para o CMS. A reconstrução na v7 deve preservar o
comportamento observável (rotas, filtros, paginação de 12, cliques, anexos) e
eliminar os três problemas mais graves: SQL interpolado, partial AJAX herdado de
Publicações e ausência de validação de `publication_date`/`text`.
