# Ouvidoria — Rotas e Controllers

## 1. Rotas do CMS — `v6/routes/basepro.php`

Todas dentro do grupo `Route::group(['prefix' => 'cms', 'before' => 'csrf'], ...)` (linha 35), com middleware `web` aplicado em `RouteServiceProvider::mapBaseproRoutes`.

### 1.1 Chamados (linhas 89-96)

| HTTP | URI | Nome | Controller@ação | `check.route.permission` |
|---|---|---|---|---|
| GET | `/cms/chamado-da-ouvidoria/{id}` | `dados_do_chamado` | `Prefecture\OmbudsmanRequestController@dados_do_chamado` | **não** |
| GET | `/cms/chamado-da-ouvidoria/responder/{id}` | `responder_chamado` | `Prefecture\OmbudsmanAnswerController@createByRequest` | **sim** (herdado) |
| GET | `/cms/chamado-da-ouvidoria/indeferir/{id}` | `indeferir_chamado` | `Prefecture\OmbudsmanAnswerController@createRejection` | **sim** (herdado) |
| POST | `/cms/chamado-da-ouvidoria/responder` | `enviar_resposta` | `Prefecture\OmbudsmanAnswerController@store` | **sim** (herdado) |
| POST | `/cms/chamado-da-ouvidoria/enviar` | `enviar_chamado` | `Prefecture\OmbudsmanRequestController@enviar_chamado` | **não** |
| GET | `/cms/chamado-da-ouvidoria/prorrogar/{id}` | `prorrogar_chamado` | `Prefecture\OmbudsmanRequestController@prorrogar_chamado` | **não** |
| resource | `/cms/chamados-da-ouvidoria` | `chamados-da-ouvidoria.{index,create,store,show,edit,update,destroy}` | `Prefecture\OmbudsmanRequestController` | **não** |

### 1.2 CRUDs de apoio (linhas 97-104)

| HTTP | URI | Nome | Controller@ação |
|---|---|---|---|
| POST | `/cms/tipos-de-chamado/set-status` | `tipos-de-chamado.set-status` | `Prefecture\OmbudsmanManifestationTypeController@setStatus` |
| resource | `/cms/tipos-de-chamado` | `tipos-de-chamado.*` | `Prefecture\OmbudsmanManifestationTypeController` |
| resource | `/cms/assuntos` | `assuntos.*` | `Prefecture\OmbudsmanSubjectController` |
| resource | `/cms/setores-orgaos` | `setores-orgaos.*` | `Prefecture\OmbudsmanSectorController` |
| GET | `/cms/respostas-prontas/api` | `respostas-prontas.api` | `Prefecture\OmbudsmanCannedResponseController@respostas_prontas` |
| resource | `/cms/respostas-prontas` | `respostas-prontas.*` | `Prefecture\OmbudsmanCannedResponseController` |
| resource | `/cms/formas-de-resposta` | `formas-de-resposta.*` | `Prefecture\OmbudsmanResponseMethodController` |
| resource | `/cms/origens-de-chamado` | `origens-de-chamado.*` | `Prefecture\OmbudsmanOriginController` |

> `Route::resource` gera 7 rotas por recurso (`index`, `create`, `store`, `show`, `edit`, `update`, `destroy`). O controller `OmbudsmanSectorController` **não** implementa `create` de forma útil — faz `redirect()->route('setores-orgaos.index')`, tratando o formulário como a própria listagem.

### 1.3 Rotas AJAX (linhas 118-120)

| HTTP | URI | Nome | Controller@ação |
|---|---|---|---|
| POST | `/cms/ajax/get/ombudsman/by/protocol` | `ajax.get.ombudsman.by.protocol` | `Portal\OmbudsmanController@getOmbudsmanByProtocol` |
| POST | `/cms/ajax/get/answer/by/ombudsman` | `ajax.get.answer.by.ombudsman` | `Portal\OmbudsmanController@getAnswerByOmbudsman` |

### 1.4 Exclusão em lote

| HTTP | URI | Nome | Controller@ação |
|---|---|---|---|
| POST | `/cms/ajax/tipos-de-chamado/deleteAll` | `ajax.manifestation-type.deleteAll` | `Prefecture\OmbudsmanManifestationTypeController@deleteAll` |
| POST | `/cms/ajax/assuntos/deleteAll` | `ajax.subject.deleteAll` | `Prefecture\OmbudsmanSubjectController@deleteAll` |
| POST | `/cms/ajax/setores-orgaos/deleteAll` | `ajax.sector.deleteAll` | `Prefecture\OmbudsmanSectorController@deleteAll` |
| POST | `/cms/ajax/respostas-prontas/deleteAll` | `ajax.canned-response.deleteAll` | `Prefecture\OmbudsmanCannedResponseController@deleteAll` |
| POST | `/cms/ajax/formas-de-resposta/deleteAll` | `ajax.response-method.deleteAll` | `Prefecture\OmbudsmanResponseMethodController@deleteAll` |
| POST | `/cms/ajax/origens-de-chamado/deleteAll` | `ajax.origin.deleteAll` | `Prefecture\OmbudsmanOriginController@deleteAll` |

### 1.5 DataTables (linhas 463-468)

| HTTP | URI | Controller@ação |
|---|---|---|
| GET | `/cms/datatable/assuntos` | `Prefecture\DatatableController@get_assuntos` |
| GET | `/cms/datatable/setores-orgaos` | `Prefecture\DatatableController@get_ouvidoria_setores_orgaos` |
| GET | `/cms/datatable/tipos-de-chamado` | `Prefecture\DatatableController@get_ouvidoria_tipos_chamado` |
| GET | `/cms/datatable/formas-de-resposta` | `Prefecture\DatatableController@get_ouvidoria_formas_resposta` |
| GET | `/cms/datatable/respostas-prontas` | `Prefecture\DatatableController@get_ouvidoria_respostas_prontas` |
| GET | `/cms/datatable/origens-de-chamado` | `Prefecture\DatatableController@get_ouvidoria_origens_chamado` |
| GET | `/cms/datatable/chamados-da-ouvidoria` | `Prefecture\DatatableController@get_chamados_ouvidoria` |

> Essas rotas ficam no grupo `Route::group(['prefix' => 'datatable', 'as' => 'datatable.'], ...)` (linha 396), **sem** `check.logged`. O `DatatableController` herda o construtor de `Prefecture\Controller`, que registra `check.route.permission` — mas para rotas cujo nome contém `datatable`, a checagem de acesso é liberada. Permanece a exigência de **login**.

## 2. Rotas do Portal — `v6/routes/web.php`

Dentro do grupo `Route::middleware(['check.site.manutention'])` (linha 7), middleware `web`.

| HTTP | URI | Nome | Controller@ação |
|---|---|---|---|
| ANY | `/relatorio-estatistico-ouvidoria` | `relatorio-estatistico-ouvidoria` | `Portal\OmbudsmanController@relStatitics` |
| POST | `/ajax/get-answers/ouvidoria` | `get.ombudsman.answers` | `Portal\OmbudsmanController@getOmbudsmanAnswers` |
| POST | `/ajax/new-message/ouvidoria` | `new.message.ombudsman` | `Portal\OmbudsmanController@newMessage` |
| ANY | `/ouvidoria` | `ouvidoria.index` | `Portal\OmbudsmanController@getIndex` |
| ANY | `/ouvidoria/store` | `ouvidoria.store` | `Portal\OmbudsmanController@store` |

> `Route::any` aceita **qualquer** método HTTP, incluindo GET — um `GET /ouvidoria/store` criaria uma manifestação. Impacto de segurança: ver `08-GAPS-E-DIVIDAS.md`.

## 3. Form Requests e validações

### 3.1 Form Requests do CMS (`v6/app/Http/Requests/`)

| Classe | `authorize()` | Regras |
|---|---|---|
| `OmbudsmanSubjectRequest` | `true` | `title` `required\|string\|max:255`<br>`sector_id` `required\|exists:ombudsman_sectors,id`<br>`email` `required\|email\|max:255`<br>`responsible` `required\|string\|max:255` |
| `OmbudsmanSectorRequest` | `true` | `title` `required\|string\|min:2\|max:255`<br>`active` `nullable` |
| `OmbudsmanOriginRequest` | `true` | `title` `required\|min:5\|max:255` |
| `OmbudsmanResponseMethodRequest` | `true` | `title` `required\|min:5\|max:255` |
| `OmbudsmanManifestationTypeRequest` | `true` | `title` `required\|min:5\|max:255`<br>`status` `nullable` |
| `OmbudsmanCannedResponseRequest` | `true` | `title` `required\|min:5\|max:255`<br>`message` `required` |
| `OmbudsmanAnswerRequest` | `true` | Condicional por `type`:<br>`type == 1` → `ombudsman_request_id` `required`, `type` `required\|boolean`, `answer` `required`<br>`type == 0` → idem + `rejected` `required`<br>caso contrário → apenas id + `type` |

Mensagens customizadas (exemplo do `OmbudsmanAnswerRequest`):
```php
'ombudsman_request_id.required' => 'Não foi possível recuperar o número da solicitação.',
'type.required' => 'Não foi possível recuperar o tipo de resposta.',
'answer.required' => 'O campo resposta é obrigatório.',
'rejected.required' => 'O campo justificativa é obrigatório.',
```

### 3.2 `OmbudsmanPortalRequest` — classe **não utilizada**

Existe `v6/app/Http/Requests/OmbudsmanPortalRequest.php` com `authorize()` retornando **`false`** (o que, se fosse usada, **bloquearia** todos os requests). Suas regras referenciam a tabela `ombudsman_types`, que **não existe** no banco (a real é `ombudsman_manifestation_types`). O portal **não** a usa — o controller tem um método próprio `validated()`.

### 3.3 Validação do portal — `Portal\OmbudsmanController::validated()`

```php
'modality'            => 'required|integer|not_in:0',
'type_id'             => 'required|integer|not_in:0',
'subject_id'          => 'required|integer|not_in:0',
'response_method_id'  => 'required|integer|not_in:0',
'origin_id'           => 'required|integer|not_in:0',
'name'                => 'nullable|string|max:255',
'email'               => 'nullable|email|max:255',
'phone'               => 'nullable|string|max:20',
'description'         => 'required|string',
'number'              => 'nullable|string|max:20',
'neighborhood'        => 'nullable|string|max:255',
'complement'          => 'nullable|string|max:255',
'address'             => 'nullable|string|max:255',
'reference_point'     => 'nullable|string|max:255',
'user_option'         => 'nullable|string|max:255',
'token'               => 'required|string',
// 'g-recaptcha-response' => 'required|recaptcha',   ← COMENTADO
```

> **Inconsistências verificadas:**
> 1. As regras dizem `not_in:0` e `integer`, mas o formulário envia **strings** (ex.: `modality=identificado`). A validação `integer` **reprovaria** o envio real. [INFERÊNCIA] o formulário em produção envia valores numéricos diferentes dos do array `$modalities` — o array do controller é `['identificado' => 'Identificado', 'sigiloso' => 'Sigiloso', 'anonimo' => 'Anônimo']`. Este é um ponto que exige teste em ambiente real.
> 2. A validação do **captcha está comentada** (`// 'g-recaptcha-response' => 'required|recaptcha'`), embora o formulário renderize `{!! Recaptcha::render() !!}`. O captcha é exibido mas **não validado** — proteção apenas visual.
> 3. `name`, `email` e `phone` são `nullable` no controller, mas o formulário do portal **desabilita** esses campos quando a modalidade é anônima (via `public/assets/js/custom/portal/ouvidoria.js`). Campos desabilitados não são enviados.
> 4. `OmbudsmanPortalRequest` usaria `required_unless:anonymous,true` — a lógica correta, hoje não aplicada.

## 4. Formato de saída (JSON)

| Endpoint | Formato |
|---|---|
| `GET /cms/chamado-da-ouvidoria/{id}` | JSON do model **completo** (inclui `name`, `email`, `phone`, `description`, `ip`, `browser`, `token`) — **sem mascaramento** |
| `POST /cms/ajax/get/ombudsman/by/protocol` | JSON do model **completo** — **sem mascaramento** |
| `POST /cms/ajax/get/answer/by/ombudsman` | `[{answer: {id, mask_answer, created_at}}]` ou `[]` com HTTP 404 |
| `POST /ajax/get-answers/ouvidoria` | `{request: {protocol (mascarado), mask_description, status_id}, answers: [{id, mask_answer, created_at}]}` — **mascarado** |
| `POST /cms/respostas-prontas/api` | JSON com todos os registros de `ombudsman_canned_responses` |
| `POST /cms/chamado-da-ouvidoria/enviar` | `{success: true, message: '…'}` ou `{success: false, message: '…', error: '…'}` com 500 |

> **Inconsistência de exposição de dados verificada:** os endpoints usados pelo **CMS** (`dados_do_chamado`, `getOmbudsmanByProtocol`) devolvem o model **completo**, incluindo `token` (hash de contestação), `ip` e `browser`. Já os endpoints públicos (`getOmbudsmanAnswers`, `getAnswerByOmbudsman`) devolvem apenas campos mascarados. O `token` em claro no JSON do CMS é um hash (`Hash::make`), mas o `ip` e o `browser` do cidadão são expostos sem necessidade.

## 5. Permissões exigidas

### 5.1 Middleware `check.route.permission`

`v6/app/Http/Middleware/CheckRoutePermission.php` possui uma seção específica para este módulo:

```php
if (in_array($access, ['responder_chamado', 'enviar_resposta'])) {
    return Sentinel::check()->hasAnyAccess(['ouvidoria.edit']);
}
```

Ou seja: as rotas de responder/indeferir o chamado exigem a permissão **`ouvidoria.edit`** — e não `chamados-da-ouvidoria.edit`. É a única permissão com nome próprio do módulo em todo o middleware.

### 5.2 Permissões nos blades

| Chave | Onde |
|---|---|
| `ombudsman.create` | botões "Cadastrar" em `canned-response/index`, `manifestation-type/index`, `origin/index`, `response-method/index` |
| `ombudsman.delete` | botões de exclusão em lote e individual nos mesmos índices + `request/index` |
| `ombudsman.create` **ou** `ouvidoria.index` | `sector/index` e `subject/index` |
| `ombudsman.delete` **ou** `ouvidoria.index` | `sector/index` |
| `feriados-municipais.delete` | `ombudsman/index.blade.php` (view **órfã**, copiada de Feriados Municipais) |

> **Estado atual verificado:** nenhum papel do banco possui as chaves `ouvidoria.*` ou `ombudsman.*` em `roles.permissions`. Na prática, **apenas `super-admin`** acessa o CMS do módulo.

## 6. Auditoria (`activity()`)

| Controller | Evento registrado |
|---|---|
| `OmbudsmanRequestController@index` | `'Listou as solicitações de ouvidoria'` |
| `OmbudsmanRequestController@prorrogar_chamado` | `'Chamado da ouvidoria respondido'` (com detalhe do status `prorrogada`) |
| `OmbudsmanAnswerController@store` | `'Chamado de Ouvidoria respondida'` |
| `OmbudsmanSubjectController@index` | `'Listou os Assuntos de ouvidoria'` |
| `OmbudsmanSectorController@index` | `'Listou os Setores/Órgãos de ouvidoria'` |
| `OmbudsmanManifestationTypeController@index` | `'Listou as Tipos de Chamado de ouvidoria'` |
| `OmbudsmanOriginController@index` | `'Listou as Origens de Chamado de ouvidoria'` |
| `OmbudsmanCannedResponseController@index` | `'Listou as Respostas Prontas de ouvidoria'` |
| `OmbudsmanCannedResponseController@store` | `'Cadastro de Resposta Pronta de ouvidoria'` |

## 7. Breadcrumbs — `v6/routes/breadcrumbs.php:18-21,42-45`

```php
// Home > Ouvidoria
Breadcrumbs::register('ouvidoria', function ($breadcrumbs, $content) {
    $breadcrumbs->parent('home');
    $breadcrumbs->push($content);
});

// Home > Ouvidoria > [pagina]
Breadcrumbs::register('static-ouvidoria', function ($breadcrumbs, $post) {
    $breadcrumbs->parent('ouvidoria');
    $breadcrumbs->push($post->title, route('static.show', $post->slug));
});
```

## 8. Contrato das respostas de e-mail

O `OmbudsmanAnswerController@store` escolhe o template conforme o tipo e a presença de anexo:

| Condição | Template |
|---|---|
| `$emailAttach && $emailAnswer` | `prefecture.emails.ombudsman-answer.email_answer_attach` |
| `!$emailAttach && $emailAnswer` | `prefecture.emails.ombudsman-answer.email_answer_no_attach` |
| `$emailRejected` | `prefecture.emails.ombudsman-answer.email_rejected` |

O `prorrogar_chamado` usa `prefecture.emails.ombudsman-answer.email_to_extend`.
O `enviar_chamado` usa `prefecture.emails.ombudsman-send-requests.email_admin`.
O `store` do portal usa `prefecture.emails.ombudsman-requests.email_user` e `.email_admin`.

Variáveis passadas ao template: `title`, `browser`, `ip`, `protocol`, `welcome` (saudação por hora: "Bom dia" < 12h, "Boa tarde" < 18h, "Boa noite"), `ombudsmanRequest`, `copyright`, `token` (só no e-mail ao cidadão).

> **Risco de exposição verificado:** o e-mail ao cidadão inclui `ip` e `browser` — dados pessoais do solicitante devolvidos ao próprio titular. Em caso de encaminhamento (`enviar_chamado`), o `ip` e o `browser` do **cidadão** são enviados ao **setor** de destino.
