# Atas e Pontos de Frequência — Gaps, Dívidas e Riscos

> Versão analisada: `v6`. Módulo **novo na v6** (nada equivalente na v5).
> Slug `modulos`: `atas-e-pontos-de-frequencia` (id 46).

---

## 1. Bugs conhecidos

| # | Severidade | Descrição | Local | Comportamento observado | Correção proposta |
|---|---|---|---|---|---|
| B1 | **Crítica** | Injeção SQL via `$_GET['ano']` interpolado em SQL cru. | `v6/app/Http/Controllers/Portal/AtasEPontosController.php` (bloco `$request->ano`) | `/atas-e-pontos/atas?ano=2024) OR 1=1 --` altera o resultado; `UNION SELECT` permite ler outras tabelas. | Binding + validação `integer|min:1900|max:2100`. |
| B2 | **Crítica** | `whereRaw` com o texto do usuário concatenado (sem escape). | `AtasEPontosController@show` (filtro `texto`) | Um termo com `'` causa erro 500; payload com `'; DROP TABLE contents; --` executa comandos. | `whereRaw('... ilike ?', ['%'.$termo.'%'])`. |
| B3 | **Alta** | Precedência `AND`/`OR` na view `atas_e_pontos`. | `v6/database/migrations/2025_01_20_122421_create_view_atas_e_pontos_table.php` | Conteúdo com soft delete vinculado à categoria **raiz** aparece na listagem do CMS, porque a segunda disjuncão não checa `deleted_at IS NULL`. | `WHERE c.deleted_at IS NULL AND (cc.category_contents_id = cc2.id OR c.category_contents_id = cc2.id)`. |
| B4 | **Alta** | `getIndex()` acessa `->slug` de um objeto possivelmente `null`. | `AtasEPontosController@getIndex` | Sem categoria filha, `/atas-e-pontos` retorna erro 500 (link do menu quebrado). | `abort(404)` ou redirecionamento institucional. |
| B5 | **Alta** | `view()` usa `first()` e não valida a categoria do conteúdo. | `AtasEPontosController@view` | Slug inexistente → erro 500; slug de **outro módulo** é servido sob `/atas-e-pontos/{cat}/{slug}` (vazamento de conteúdo). | `firstOrFail()` + filtro por categoria do módulo. |
| B6 | **Média** | Título do CMS copiado de outro módulo: `'Unidades de Atendimento'`. | `v6/app/Http/Controllers/Prefecture/ContentController.php:116` | Cabeçalho da tela e **log de auditoria** gravam o nome errado. | Usar `'Atas e Pontos de Frequência'` (como já ocorre na linha 242). |
| B7 | **Média** | JSON de Ajax montado por concatenação, sem `encodeURIComponent`. | `show.blade.php` (6 temas) | Texto com `&`, `#` ou espaço quebra a query string; o usuário vê um `alert` genérico. | `encodeURIComponent` / `URLSearchParams`. |
| B8 | **Baixa** | `$anos` não filtra `publication_date <= NOW()`, mas a view oculta anos maiores que o corrente — os dois filtros divergem. | `AtasEPontosController@show` + `list.blade.php` | Anos futuros podem aparecer em contextos diferentes da view. | Filtrar na consulta e centralizar a regra. |
| B9 | **Baixa** | `ContentRequest` não tem `case 'atas-e-pontos'`; o `default` não exige `text` nem `publication_date`. | `v6/app/Http/Requests/ContentRequest.php:308-314` | O formulário marca os campos como obrigatórios, mas a validação real é só do lado do cliente. | `case 'atas-e-pontos'` com regras explícitas. |
| B10 | **Informativa** | Sem rota de download: link direto `url($archive->path)`. | `v6/resources/views/portal/components/view_pdf/files-list.blade.php` | Qualquer arquivo publicado é baixável por URL previsível, sem autorização nem log de acesso. | Rota de download com autorização e `Content-Disposition`. |

## 2. Funcionalidades incompletas ou mortas

| Item | Situação |
|---|---|
| Presence/frequência estruturada | **Inexistente**. Sem tabela, sem registro por vereador, sem quórum. O nome do módulo promete mais do que o sistema entrega. |
| Módulo desligado por padrão | `modulos.status = 0` no seed e no banco; o operador precisa habilitar em `/cms/modulos`. |
| Conteúdo inexistente na base | A consulta às categorias `atas` e `pontos-de-frequencia` retorna **0** documentos na base analisada — o módulo está implementado, mas sem carga de dados. |
| Filtro de mês | Só é populado quando há ano selecionado; ao mudar de ano, a lista de meses é recarregada por Ajax. |
| Exportação | Inexistente (sem CSV, Excel ou PDF do relatório). |
| Relatório de presença | Inexistente. |
| Vinc ATA ↔ SESSÃO | Inexistente; a ata não referencia a sessão em que foi aprovada. |

## 3. Dívidas técnicas

| Dívida | Impacto | Esforço estimado |
|---|---|---|
| SQL cru em três pontos do controller do portal | Segurança e manutenibilidade | Baixo (bindings) |
| View SQL criada dentro de migration | Esquema divergente entre ambientes; dependência do `up()` roda | Médio (migrar para Eloquent/query builder) |
| 6 cópias das telas e do JS de Ajax | Correção precisa ser replicada 6 vezes | Médio (componente único) |
| Bloco PHP de filtro de extensões dentro do Blade | Lógica de negócio na view | Baixo (mover para policy/helper) |
| `str_limit`, `strip_tags`, `stripslashes` na view | Presentation layer fazendo sanitização | Baixo (accessor no model) |
| Ausência de testes do módulo | Regressões não detectadas | Médio (ver `07-TESTES.md`) |
| Ausência de `SessionAttendance` | Sem quórum/maioria auditável | Alto (exige decisão de negócio) |

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

| Risco | Descrição | Mitigação |
|---|---|---|
| Colisão de slug | `contents.slug` é global; documentos de módulos diferentes podem usar o mesmo slug, e o `view()` não checa a categoria. | Índice único por módulo ou `(category_contents_id, slug)`. |
| Duplicidade de categoria | O seed usa `updateOrCreate` por `slug`, então reexecução é idempotente — mas categorias criadas manualmente pelo operador podem conflitar. | Constraint única em `category_contents.slug`. |
| Soft delete inconsistente | A view SQL trata `deleted_at` de forma inconsistente por causa do `or`. | Corrigir a view antes de qualquer migração de dados. |
| Anexos órfãos | `archives.contents_id` aponta para `contents`; ao migrar para `document_files`, é preciso mapear `view_pdf` → papel `principal` e `highlight` → papel `destaque`. | Script de migração idempotente com relatório. |
| Perfil do banco atual | `contents` em 2026 tem colunas de vários módulos; extrair o que é exclusivo de atas evita arrastar colunas irrelevantes. | Migrar apenas o subconjunto usado. |
| Contadores | `contents.clicks` e `menu_orders.clicks` devem ser preservados. | Copiar na migração. |

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

1. **Frequência deve ser estruturada?** O município precisa de quórum/maioria
   consultável ou basta o PDF publicado? A resposta define se a v7 cria
   `session_attendances` ou mantém o modelo documental.
2. **Quem digita a presença?** Se estruturada, de onde vem o dado: digitação
   manual, leitura de scanners, integração com a ata?
3. **Ata deve referenciar a sessão?** Convém ligar a ata ao módulo `sessoes`
   (data, tipo, número da sessão) em vez de deixar a relação apenas no texto.
4. **Quantas categorias filhos são permitidas?** O seed cria duas (`atas`,
   `pontos-de-frequencia`); o operador pode criar quantas quiser. Deve haver
   limite ou lista fixa (ex.: "Ata", "Ata Aberta", "Ponto de Frequência")?
5. **É aceitável publicar ata com data futura?** O sistema impede a visualização
   (redireciona), mas o documento fica gravado. Manter ou remover o agendamento?
6. **Downloads devem exigir autenticação?** Hoje são públicos e diretos.
7. **Qual o período de retenção** de atas? A tabela `contents` não tem
   particionamento nem política de expurgo.
8. **Qual o nome canônico?** Hoje coexistem `atas-e-pontos-de-frequencia`
   (módulo) e `atas-e-pontos` (categoria e rota) — pode gerar confusão para o
   operador.

## 6. Mapa v5 → v6

### 6.1 O que existia na v5

**Nada.** Verificação realizada em `v5/`:

| Artefato procurado | Resultado |
|---|---|
| Rotas com `atas`, `pontos`, `frequencia`, `vereador`, `sessao`, `pauta` em `v5/routes/` | Nenhuma encontrada (o único hit foi `requerimento_diarias`, outro módulo). |
| Controller em `v5/app/Http/Controllers/` | Nenhum `AtasEPontosController` ou equivalente. |
| Migration criando `atas_e_pontos` ou tabela de atas/frequência | Nenhuma. |
| Model `Atas`, `PontoFrequencia`, `Attendance` | Nenhum. |
| View Blade de atas em `v5/resources/views/` | Nenhuma. |
| Seed da categoria `atas-e-pontos` | Nenhum. |

A base técnica reaproveitada pela v6 (tabelas `contents`,
`category_contents`, `archives` e o CRUD genérico de conteúdos) **existia na
v5**; o que não existia era o módulo de atas e pontos de frequência.

### 6.2 Lacuna funcional representada pela v6

Antes da v6, o portal municipal **não oferecia**:

- Publicação nem consulta de **atas das sessões** da Câmara.
- Publicação nem consulta de **pontos de frequência**.
- Qualquer mecanismo de filtro por ano/mês sobre documentos legislativos.
- Qualquer registro (nem documental) de presença de vereadores.

### 6.3 O que a v6 entrega e o que ainda falta

| Antes (v5) | Depois (v6) | Ainda falta |
|---|---|---|
| Nada | Módulo novo com 2 categorias filhas, listagem paginada, filtros de ano/mês/texto, CMS com DataTable e download de anexos | Dados estruturados de presença, vínculo ata↔sessão, quórum, rota de download autorizada, correções de segurança e de robustez |

### 6.4 Decisões a tomar para a v7

| Decisão | Alternativas | Impacto |
|---|---|---|
| Modelo do dado | (a) manter documental; (b) separar `Ata` e `PontoFrequencia`; (c) adicionar `SessionAttendance` | (c) exige fluxo de digitação novo e escopo adicional |
| Endpoints | manter URLs atuais + API JSON, ou migrar para REST | Migração de URLs quebra links publicados |
| Tabelas | reaproveitar `contents` ou criar tabelas próprias | Reaproveitar mantém a migração barata, mas mantém o acoplamento por slug |
| Arquivos | manter link direto ou criar rota autorizada | Rota autorizada é mais segura, exige regra de cache e CDN |
| Views SQL | remover `atas_e_pontos` e usar Eloquent | Elimina a divergência entre ambientes |

## 7. Resumo do risco

| Dimensão | Nível | Observação |
|---|---|---|
| Segurança | **Alto** | Duas injeções SQL acessíveis ao público, sem autenticação. |
| Disponibilidade | **Médio** | Erro 500 em `/atas-e-pontos` se faltarem categorias filhas. |
| Integridade de dados | **Médio** | Conteúdo removido visível no CMS; slugs sem unicidade por módulo. |
| Privacidade | **Baixo** | Atas e pontos de frequência são públicos por natureza; sem dado pessoal estruturado. |
| Legalidade | **Médio** | Sem registro estruturado de presença, não há como demonstrar quórum de uma sessão. |
| Manutenibilidade | **Médio** | Duplicação em 6 temas, SQL em view, lógica na view. |
