# Ouvidoria — Resumo Executivo

> **Versão analisada:** `lotep` (Laravel 5.5.50 / PHP 7.4) — camada modular `V2/`
> **Slug do módulo:** `Ouvidoria` (grupo `Servicos`, namespace `V2\Modules\Servicos\Ouvidoria`)
> **Natureza:** Portal público (formulário) + CMS (leitura, status e resposta); sem API JSON
> **Estado:** implementado e testado em nível de controller e de importação legada; sem CRUD
> de dicionários, sem prazos e sem acompanhamento público por protocolo

> **Convenção de caminhos:** os caminhos completos começam em `V2/...`, `tests/...` ou
> `app/...`. Nas tabelas, um nome de arquivo sem diretório (ex.: `OmbudsmanRequest.php:87`) é
> abreviação do arquivo homônimo em `V2/Modules/Servicos/Ouvidoria/Models/`,
> `Controllers/Admin/`, `Controllers/Portal/` ou `Views/...`, sempre apresentado por extenso na
> primeira menção da seção.

---

## 1. Objetivo

A **Ouvidoria** é o canal oficial de manifestações do cidadão: reclamação, sugestão, elogio
e denúncia. O cidadão registra a manifestação pelo portal, recebe um protocolo e a
administração a lê, classifica, altera o status e registra uma resposta no CMS.

O módulo substitui a versão V1 (`app/Http/Controllers/Portal/OmbudsmanController.php`) por
uma implementação enxuta na camada `V2/`, com tabelas próprias (`v2_ombudsman_*`), dicionários
seedados e um importador idempotente dos dados legados.

---

## 2. Efeito / Impacto

| Faixa | Efeito |
|---|---|
| Cidadão | Preenche um formulário público em `/ouvidoria`, escolhe tipo/assunto/origem/forma de resposta e recebe, em mensagem *flash*, o protocolo `OUV-20261023-a1b2c3d4` (formato `OUV-{Ymd}-{8}`). |
| Prefeitura (CMS) | Lista e busca manifestações em `/cms/v2/ouvidoria/requests`, vê o detalhe (inclusive IP e dados pessoais), altera o status entre 1/2/3 e registra respostas em texto livre. |
| Transparência e legalidade | Existe protocolo estável e trilha de auditoria das ações de CMS; **não** existe prazo, SLA, prorrogação, recurso nem controle de consentimento LGPD persistido. |
| Operação técnica | Um módulo autocontido; os dicionários vêm do `V2EnvironmentSeeder`; a importação V1 vive em `V2/Modules/Support/MigracaoLegada` e grava nas mesmas tabelas. |

---

## 3. Comportamento

- **Protocolo** é gerado no *hook* `creating` do model: `OUV-` + `Ymd` + os 8 primeiros
  caracteres do UUID (`V2/Modules/Servicos/Ouvidoria/Models/OmbudsmanRequest.php:86-88`).
  O `id` é UUID v4 (`v2_uuid()`), apesar do nome do helper.
- **Status é inteiro mágico**: `1` = Em tramitação (default da migration), `2` = Atendido,
  `3` = Indeferido. Não há enum, constante, cast dedicado nem validação.
- **Registrar resposta força o status para 2**: `answer()` insere em `v2_ombudsman_answers` e
  executa `$request->update(['status' => 2])`, independentemente de a resposta ser um
  indeferimento (`OmbudsmanRequestCmsController.php:87-88`). O status `3` só é alcançável pelo
  formulário de status.
- **Anônimo não é uma escolha do portal**: a coluna `is_anonymous` existe, é *fillable* e tem
  default `false`, mas o formulário público não tem esse campo. Na prática “anônimo” é apenas
  “nome vazio”, e o valor pode ser injetado via POST.
- **Dicionários são somente leitura para o operador**: tipos, assuntos, origens e formas de
  resposta são criados por seed e não têm tela de CRUD no V2. O portal lista apenas
  `is_active = true`, ordenado por título.
- **Antiabuso mínimo**: apenas `throttle:10,1` na rota `POST /ouvidoria/store` (10 requisições
  por minuto por IP). Não há honeypot, *time-trap*, captcha nem rate limit nomeado.
- **Sem SLA**: não há coluna de prazo, data-limite, prorrogação, alerta ou fila de vencimento.
  O status `4` (Prorrogado) da V1 não foi portado.
- **Sem acompanhamento público**: não existe rota do tipo `GET /ouvidoria/{protocolo}`; o
  protocolo aparece uma única vez no *flash* de sucesso e se perde no próximo request.
- **Sem notificação**: o V2 não envia e-mail ao manifestante nem ao setor. A V1 enviava os dois
  (`app/Http/Controllers/Portal/OmbudsmanController.php:166-210`).
- **Sem anexos novos**: `v2_ombudsman_answers.file_url` existe na tabela e no *fillable*, mas
  nenhum action do módulo o preenche; só o importador V1 grava a URL da cópia na galeria. O
  portal e o CMS não têm upload, embora a V1 anexasse arquivos.

---

## 4. Como o Usuário Interage

**Portal (`GET /ouvidoria`).** Página única com:
- quatro `select` (tipo, assunto, origem, forma de resposta), carregados dos dicionários ativos;
- nome, e-mail, telefone e endereço (todos opcionais no servidor);
- descrição obrigatória;
- um *checkbox* de consentimento `user_option` que **não é validado nem persistido**;
- caixa fixa com e-mail e horário de atendimento *hardcoded*.

Ao enviar (`POST /ouvidoria/store`), o cidadão volta para a mesma página com um alerta de
sucesso contendo o protocolo, ou com a lista de erros de validação no topo.

**CMS (`/cms/v2/ouvidoria`).** Índice com busca textual (protocolo/nome/e-mail), ordenação por
`status`, `protocol` ou `created_at`, 20 linhas por página e um único atalho para o detalhe.
O detalhe mostra dicionários, dados pessoais, IP, descrição e respostas; um *select* de status e
um *textarea* de resposta aparecem apenas para quem tem nível `write`.

---

## 5. Detalhes e Particularidades Técnicas

- **Fluxo:** rota → `web` (+ `MaintenanceMiddleware`; no CMS também `v2.authenticate`,
  `v2.permission` e `module.active:Ouvidoria`) → controller → `$r->validate()` inline ou
  `Model::create()` → Blade. Não há Form Request, Service, Repository, Policy, Resource,
  Job, evento ou comando.
- **Descoberta:** `V2ServiceProvider` varre `V2/Modules/<Grupo>/<Modulo>`, registra migrations,
  o namespace de views `v2-ouvidoria` e as rotas `Routes/{web,cms,api}.php`
  (`V2/Core/Providers/V2ServiceProvider.php:88-127`).
- **Autorização em dois níveis:** o middleware `v2.permission` resolve o módulo pelo
  `active_pattern` (`cms/v2/ouvidoria*`) e decide leitura/escrita pelo método HTTP; os
  controllers repetem a checagem com `v2_can('ouvidoria', ...)` e abortam 403.
- **Parâmetros do middleware são ignorados:** a rota declara `v2.permission:ouvidoria,read`,
  mas `V2PermissionMiddleware::handle($request, Closure $next)` não recebe nem usa parâmetros
  (`V2/Core/Middleware/V2PermissionMiddleware.php:22`).
- **Mass assignment:** o portal faz `$data = $r->all()` e `OmbudsmanRequest::create($data)`;
  como `status`, `protocol`, `is_anonymous` e `metadata` estão no `fillable`, o cliente pode
  injetá-los.
- **Busca:** `scopeSearch` encadeia `where(...)->orWhere(...)->orWhere(...)` sem grupo, o que
  impede combinar a busca com qualquer outro filtro (status, tipo, período).
- **Auditoria:** as ações de CMS usam `V2AuditLogger::activity()` e `CmsActionLogger::info()`;
  o portal não registra nada (nem log de aplicação).

---

## 6. Estrutura de Dados

| Tabela | Papel | Chave |
|---|---|---|
| `v2_ombudsman_requests` | manifestação | PK `string(36)`; `protocol` unique; 4 FKs opcionais |
| `v2_ombudsman_answers` | respostas da manifestação | PK `string(36)`; FK `v2_ombudsman_request_id` cascade |
| `v2_ombudsman_manifestation_types` | dicionário de tipo | PK `string(36)`; `title`, `is_active` |
| `v2_ombudsman_subjects` | dicionário de assunto | PK `string(36)`; `title`, `is_active` |
| `v2_ombudsman_origins` | dicionário de origem | PK `string(36)`; `title`, `is_active` |
| `v2_ombudsman_response_methods` | dicionário de forma de resposta | PK `string(36)`; `title`, `is_active` |

Extras na tabela de manifestações: `v1_id` (inteiro, nullable) e `imported_at` (timestamp,
nullable), com índice único parcial `WHERE v1_id IS NOT NULL`, adicionados para a importação
idempotente da V1. Não há views SQL, pivot nem tabela de anexos no V2.

---

## 7. Como Testar

```bash
# suíte dedicada do módulo (Feature, controller)
vendor/bin/phpunit tests/Feature/V2/Modules/Servicos/Ouvidoria/OuvidoriaCmsControllerTest.php

# importação legada (Unit)
vendor/bin/phpunit tests/Unit/V2/Modules/Infraestrutura/MigracaoLegada/OuvidoriaLegacyImporterTest.php

# matriz RBAC que inclui o índice da Ouvidoria via HTTP
vendor/bin/phpunit tests/Feature/V2/Rbac/CommonUserModuleAccessE2ETest.php
```

Cenários mínimos: criação pelo portal com e sem dicionários; validação de descrição e e-mail;
geração do protocolo; busca do índice; alteração de status; resposta forçando status 2;
403 sem permissão; 404 para id inexistente; importação idempotente por `v1_id`.

---

## 8. Migração para Laravel Moderno

O V7 (Laravel 12 / PHP 8.4) deve **portar** a semântica útil — protocolo estável, dicionários
ativos, dicionários como dado, status de workflow e importação idempotente — e **rejeitar** os
defeitos estruturais: mass assignment, status inteiro mágico, protocolo gerado em `boot()` do
model, IP cru, consentimento decorativo e ausência de prazos/acompanhamento.

O desenho-alvo está em `06-MIGRACAO-MODERNA.md` e o julgamento de aproveitamento, com as ADRs
aplicáveis, em `09-APROVEITAMENTO-V7.md`.

---

## 9. Resumo Geral

O V2 da Ouvidoria é uma reescrita mínima do canal: um formulário público que grava em
`v2_ombudsman_requests` e um backoffice que lista, altera status e responde. O que ele resolve
bem é o essencial do protocolo (formato `OUV-{Ymd}-{8}`, unicidade no banco), a separação dos
dicionários (tipo/assunto/origem/forma de resposta) e a importação idempotente da V1. O que ele
não resolve é quase todo o restante do ciclo de ouvidoria: não há prazos nem prorrogação, não há
consulta pública por protocolo, não há e-mail de confirmação ou de resposta, não há anexos, não
há CRUD de dicionários, não há trilha LGPD de consentimento e o operador não consegue indeferir
registrando uma resposta — o fluxo de resposta força o status “Atendido”. Tecnicamente, o
formulário aceita injeção de `status`/`protocol`/`is_anonymous` por *mass assignment*, a busca
usa `orWhere` no topo (quebrando filtros compostos) e o IP cru do cidadão é armazenado e exibido.
A reconstrução no V7 deve preservar o contrato observável do protocolo e dos dicionários, mas
reimplementar o fluxo com enum de status, consentimento persistido, prazos, acompanhamento por
protocolo, auditoria nativa e rate limiting endurecido.

---

## Índice do dossiê

| Arquivo | Conteúdo |
|---|---|
| `00-RESUMO.md` | Este documento |
| `01-ARQUITETURA.md` | Camadas, classes e fluxo das duas superfícies |
| `02-MODELAGEM.md` | Tabelas, colunas, FKs, migrations e seeds |
| `03-ROTAS-E-CONTROLADORES.md` | Rotas, validações, permissões e respostas |
| `04-VIEWS-E-UX.md` | Telas, formulários, partials e acessibilidade |
| `05-REGRAS-E-PORTABILIDADE.md` | Regras, armadilhas, divergências V1→V2 e riscos |
| `06-MIGRACAO-MODERNA.md` | Schema, models, requests, policies e endpoints no V7 |
| `07-TESTES.md` | Estratégia, casos reais, regressões e lacunas |
| `08-GAPS-E-DIVIDAS.md` | Bugs, dívidas, riscos de dados e perguntas abertas |
| `09-APROVEITAMENTO-V7.md` | O que portar, adaptar e rejeitar, com ADRs do V7 |
