# Ouvidoria — Mapa v5 → v6

> Objetivo deste documento: registrar **exatamente** o que mudou entre `v5` e `v6` no módulo Ouvidoria, isolando as regressões e as adições, para que a reconstrução em Laravel moderno não perca nem reintroduza funcionalidade acidental.

## 1. Inventário comparativo

### 1.1 Controllers

| Artefato | v5 | v6 | Observação |
|---|---|---|---|
| `Prefecture/OmbudsmanRequestController.php` | ✅ | ✅ | v6 acrescenta `enviar_chamado` com troca de assunto/setor + log `forward` e `prorrogar_chamado` |
| `Prefecture/OmbudsmanAnswerController.php` | ✅ | ✅ | v6 acrescenta `mask_answer`, o bloco de histórico de tramitação e o `DataMaskTrait` |
| `Prefecture/OmbudsmanSubjectController.php` | ✅ | ✅ | **mudança de domínio**: v5 vinculava o assunto a uma *secretaria* (`contents`); v6 vincula a um *setor* (`ombudsman_sectors`) |
| `Prefecture/OmbudsmanSectorController.php` | ❌ **ausente** | ✅ **novo** | CRUD de setores/órgãos, exclusivo da v6 |
| `Prefecture/OmbudsmanOriginController.php` | ✅ | ✅ | idem (código quase idêntico) |
| `Prefecture/OmbudsmanResponseMethodController.php` | ✅ | ✅ | idem |
| `Prefecture/OmbudsmanManifestationTypeController.php` | ✅ | ✅ | idem |
| `Prefecture/OmbudsmanCannedResponseController.php` | ✅ | ✅ | idem |
| `Prefecture/RelatorioEstatisticoOuvidoriaController.php` | ✅ | ❌ **REGRESSÃO** | Ver §4 |
| `Portal/OmbudsmanController.php` | ✅ | ✅ | v6 unifica a exportação na rota principal (v5 tinha rota dedicada) |

### 1.2 Models

| Artefato | v5 | v6 | Observação |
|---|---|---|---|
| `OmbudsmanRequest.php` | ✅ | ✅ | v6 acrescenta `modality`, `token`, contestações, `mask_description` ao `$fillable` |
| `OmbudsmanAnswer.php` | ✅ | ✅ | **v6 comenta o método `archives()`** — ver §3 |
| `OmbudsmanSubject.php` | ✅ | ✅ | `$fillable` v6: `title, email, sector_id, secretary_id, responsible` (v5: sem `sector_id`) |
| `OmbudsmanSector.php` | ❌ | ✅ **novo** | `App\Models\OmbudsmanSector` |
| `OmbudsmanRequestLogs.php` | ❌ | ✅ **novo** | `App\Models\OmbudsmanRequestLogs` |
| `OmbudsmanOrigin.php` | ✅ | ✅ | — |
| `OmbudsmanResponseMethod.php` | ✅ | ✅ | — |
| `OmbudsmanManifestationType.php` | ✅ | ✅ | — |
| `OmbudsmanCannedResponse.php` | ✅ | ✅ | — |
| `RelatorioEstatisticoOuvidoria.php` | ✅ | ❌ **REGRESSÃO** | — |

### 1.3 Form Requests

| Artefato | v5 | v6 |
|---|---|---|
| `OmbudsmanSubjectRequest.php` | ✅ | ✅ — **regra alterada**: v5 exigia `secretary_id`; v6 exige `sector_id` |
| `OmbudsmanSectorRequest.php` | ❌ | ✅ **novo** |
| `OmbudsmanAnswerRequest.php` | ✅ | ✅ |
| `OmbudsmanCannedResponseRequest.php` | ✅ | ✅ |
| `OmbudsmanManifestationTypeRequest.php` | ✅ | ✅ |
| `OmbudsmanOriginRequest.php` | ✅ | ✅ |
| `OmbudsmanResponseMethodRequest.php` | ✅ | ✅ |
| `OmbudsmanPortalRequest.php` | ✅ | ✅ (continua **não utilizada** e com `authorize() = false`) |
| `RelatorioEstatisticoOuvidoriaRequest.php` | ✅ | ❌ **REGRESSÃO** |

### 1.4 Migrations

| Migration | v5 | v6 |
|---|---|---|
| `2025_03_31_172524_create_ombudsman_manifestation_types_table.php` | ✅ | ✅ |
| `2025_03_31_172538_create_ombudsman_subjects_table.php` | ✅ | ✅ |
| `2025_03_31_172602_create_ombudsman_canned_responses_table.php` | ✅ | ✅ |
| `2025_03_31_172612_create_ombudsman_response_methods_table.php` | ✅ | ✅ |
| `2025_03_31_172643_create_ombudsman_origins_table.php` | ✅ | ✅ |
| `2025_03_31_182643_create_ombudsman_requests_table.php` | ✅ | ✅ |
| `2025_04_01_155609_create_view_ombudsman_requests.php` | ✅ | ✅ |
| `2025_04_01_155617_create_view_ombudsman_manifestation_types.php` | ✅ | ✅ |
| `2025_04_01_155624_create_view_ombudsman_subjects.php` | ✅ | ✅ |
| `2025_04_01_155631_create_view_ombudsman_response_methods.php` | ✅ | ✅ |
| `2025_04_01_155637_create_view_ombudsman_origins.php` | ✅ | ✅ |
| `2025_04_28_141003_create_ombudsman_answers_table.php` | ✅ | ✅ |
| `2026_02_04_154432_update_ombudsman_requests_table.php` | ❌ | ✅ **nova** — `modality`, `token`, contestações |
| `2026_02_06_083955_add_modality_to_ombudsman_requests_view.php` | ❌ | ✅ **nova** |
| `2026_02_24_163925_create_ombudsman_request_logs_table.php` | ❌ | ✅ **nova** |
| `2026_05_25_000000_add_new_fields_to_ombudsman_requests_table.php` | ✅ **exclusiva da v5** | ❌ |
| `2026_07_22_100000_create_ombudsman_sectors_and_migrate_subjects.php` | ❌ | ✅ **nova** |
| `2026_09_15_100000_add_mask_description_to_ombudsman_requests_table.php` | ✅ | ✅ |
| `2026_08_06_162636_create_ombudsman_answer_archives_table.php` | ✅ **exclusiva da v5** | ❌ |
| `2026_09_21_093024_add_mask_answer_to_ombudsman_answers_table.php` | ❌ | ✅ **nova** |
| `2026_05_27_135849_create_relatorios_estatisticos_ouvidoria_table.php` | ✅ **exclusiva da v5** | ❌ **REGRESSÃO** |

> **Ordem de aplicação:** as migrations da v6 **não** podem ser aplicadas sobre um banco que já rodou a `2026_05_25` (v5), porque ambas tentam adicionar `modality`. A migration da v6 usa `if (!Schema::hasColumn(...))` justamente para ser idempotente nesse ponto. O mesmo vale para `2026_09_15` (`mask_description`), presente nas duas versões com o mesmo efeito.

### 1.5 Views

| Diretório / arquivo | v5 | v6 |
|---|---|---|
| `prefecture/ombudsman/{index,edit,form}.blade.php` | ✅ | ✅ (continuam órfãs nas duas) |
| `prefecture/ombudsman/request/{index,modal_show,modal_send}.blade.php` | ✅ | ✅ |
| `prefecture/ombudsman/request/answer/create.blade.php` | ✅ | ✅ (v6 acrescenta o histórico de tramitação) |
| `prefecture/ombudsman/request/answer/reject.blade.php` | ✅ | ✅ |
| `prefecture/ombudsman/request/answer/_details.blade.php` | ✅ **exclusiva da v5** | ❌ |
| `prefecture/ombudsman/{subject,response-method,origin,manifestation-type,canned-response}/` | ✅ | ✅ |
| `prefecture/ombudsman/sector/` | ❌ | ✅ **novo** |
| `prefecture/relatorio-estatistico-ouvidoria/{index,create,edit,form}.blade.php` | ✅ **exclusiva da v5** | ❌ **REGRESSÃO** |
| `prefecture/emails/ombudsman-{requests,send-requests,answer}/` | ✅ | ✅ |
| `portal/.../informacoes-instituicao/ouvidoria/` (6 modelos) | ✅ | ✅ |
| `portal/.../informacoes-instituicao/acesso-rapido/consulta-solicitacao-ouvidoria.blade.php` (6 modelos) | ✅ | ✅ |
| `portal/.../transparencia-fiscal/relatorio-estatistico-ouvidoria/{index,js}.blade.php` (6 modelos) | ✅ | ✅ |
| `portal/.../transparencia-fiscal/ouvidoria-solicitada.blade.php` (6 modelos) | ✅ | ✅ |
| `portal/layouts/default/pages/statics/ouvidoria/` (6 arquivos) | ✅ | ✅ |
| `portal/layouts/newsbit/.../ouvidoria/index.blade.php` | ❌ | ✅ **nova** |

> **Detalhe de nomenclatura:** a v5 usa `informacoes-instituicao` (com "instituicao"); a v5 também tem `informacoes-prefeitura` em alguns caminhos — a comparação por nome de pasta é enganosa. Verificar por conteúdo, não por nome.

### 1.6 JavaScript

| Arquivo | v5 | v6 |
|---|---|---|
| `custom/contents/ouvidoria/chamados-da-ouvidoria/{datatable,modals,modal_show,modal_send}.js` | ✅ | ✅ |
| `custom/contents/ouvidoria/respostas-da-ouvidoria/{responder_chamado,indeferir_chamado}.js` | ✅ | ✅ |
| `custom/contents/ouvidoria/{assuntos,setores-orgaos,tipos-de-chamado,formas-de-resposta,respostas-prontas,origens-de-chamado}/datatable.js` | ✅ (sem `setores-orgaos`) | ✅ (com `setores-orgaos`) |
| `custom/portal/ouvidoria.js` | ✅ | ✅ |
| `custom/config/relatorio-estatistico-ouvidoria/datatable.js` | ✅ **exclusiva da v5** | ❌ **REGRESSÃO** |

### 1.7 Seeds

| Seed | v5 | v6 |
|---|---|---|
| `OmbudsmanOptionsSeeder.php` | ✅ | ✅ (v6 inclui a chamada a `OmbudsmanSectorSeeder`) |
| `OmbudsmanSectorSeeder.php` | ❌ | ✅ **novo** |
| `OmbudsmanMenuOrderSeeder.php` | ✅ | ❌ |
| `OmbudsmanResourcesSeeder.php` | ✅ | ❌ |
| `ConfigOptionHideOmbudsmanFormSeeder.php` | ✅ | ❌ |
| `UpdateRequestToMaskRequestInOmbudsmanRequest.php` | ✅ | ✅ |
| `AddEmailDenunciaOuvidoriaSeeder.php` | ❌ | ✅ **novo** |
| `OuvidoriaPaginasLinksSeeder.php` | ❌ | ✅ **novo** |
| `UpdateAnswerToMaskAnswerInOmbudsmanAnswer.php` | ❌ | ✅ **novo** |

> O registro de módulo (`modulos` id 56, `resources` id 76) existe na v6. Na v5, o menu e o recurso eram criados pelos seeds `OmbudsmanMenuOrderSeeder` e `OmbudsmanResourcesSeeder`; na v6, o registro foi consolidado em outra migration (ou permanece do banco). [NÃO VERIFICADO] qual migration da v6 cria o registro do módulo Ouvidoria.

## 2. O que a v6 **ganhou**

| Ganho | Descrição | Impacto |
|---|---|---|
| **Setores/Órgãos como entidade** | `ombudsman_sectors` + `OmbudsmanSectorController` + `OmbudsmanSectorRequest` + view `ouvidoria_setores_orgaos` | O assunto deixou de estar preso a uma secretaria e passou a ter um roteamento próprio. O `OmbudsmanSectorSeeder` importa as secretarias preservando os ids. |
| **Histórico de tramitação** | `ombudsman_request_logs` + `OmbudsmanRequestLogs` + exibição na tela de resposta | Permite auditar por onde o chamado passou — visível na v6, ausente na v5. |
| **Encaminhamento entre setores** | `OmbudsmanRequestController@enviar_chamado` | Troca o assunto do chamado, registra o log `forward` e reenvia por e-mail. |
| **Mascaramento por IA** | `mask_description` + `mask_answer` + `DataMaskTrait::maskTextWithAi` | Versão mascarada gravada **no momento da escrita**, não apenas no relatório. |
| **Token de contestação** | `token` + `answer_contestation` + `answer_contestation_2` | Permite ao contestar a resposta. |
| **Modalidade formalizada** | `modality` (`identificado`/`sigiloso`/`anonimo`) | Distinção entre identificação e sigilo — o WhatsApp grava "sigiloso" para não expor o nome. |
| **Coluna `modalidade` na view** | `2026_02_06` | O operador vê a modalidade na listagem. |
| **Seed `AddEmailDenunciaOuvidoriaSeeder`** | — | E-mail do assunto de denúncia configurado. |
| **`mask_answer` na tabela de respostas** | `2026_09_21` | Respostas também mascaradas. |
| **Exportação unificada** | v5 tinha `/relatorio-estatistico-ouvidoria` **e** `/relatorio-estatistico-ouvidoria/exportacao`; v6 usa `?type=` | Menos rotas, mesma capacidade. |

## 3. O que a v6 **perdeu** (regressões)

### 3.1 Módulo de Relatório Estatístico de Ouvidoria (CMS) — **COMPLETO**

Detalhado em `08-GAPS-E-DIVIDAS.md` §1.1. Resumo do que foi perdido:

| Capacidade | v5 | v6 |
|---|---|---|
| Cadastrar relatório anual (ano, descrição, PDF até 20 MB) | ✅ | ❌ |
| Editar / excluir relatórios | ✅ | ❌ |
| Listar relatórios no CMS (DataTable) | ✅ | ❌ |
| Página institucional com o PDF do relatório | ✅ (parcial) | ❌ |
| Tabela `relatorios_estatisticos_ouvidoria` | ✅ | ❌ |

### 3.2 Anexos em respostas

| Item | v5 | v6 |
|---|---|---|
| Método `OmbudsmanAnswer::archives()` | ativo | comentado |
| Tabela `ombudsman_answer_archives` | existe | **não existe** |
| Resultado | anexos funcionam | **`dd($e)` na tela** |

### 3.3 Campos demográficos do solicitante

`cpf_cnpj`, `gender`, `education_level`, `birth_date`, `attachment` — presentes na v5, ausentes na v6.

### 3.4 View `_details.blade.php`

`v5/resources/views/prefecture/ombudsman/request/answer/_details.blade.php` — não existe na v6 (o conteúdo parece ter sido incorporated directly em `create.blade.php`/`reject.blade.php`).

### 3.5 Seeds de menu, recursos e configuração

`OmbudsmanMenuOrderSeeder`, `OmbudsmanResourcesSeeder`, `ConfigOptionHideOmbudsmanFormSeeder` — não existem na v6. O registro do módulo (`modulos` id 56) **existe** na base v6, mas não há seed que o recrie do zero.

## 4. Como recuperar o Relatório Estatístico de Ouvidoria na v7

> **Dossiê complementar:** o módulo v5-only tem dossiê próprio e detalhado em `../relatorio-estatistico-ouvidoria/` (9 arquivos). Esta seção registra apenas o **impacto sobre a Ouvidoria** e o plano de recuperação; o detalhamento do código v5 está no dossiê complementar.

Plano detalhado em `06-MIGRACAO-MODERNA.md` §8. Resumo:

### 4.1 Nova tabela

```php
Schema::create('ombudsman_reports', function (Blueprint $table) {
    $table->id();
    $table->string('year', 10);
    $table->string('description')->nullable();
    $table->string('file_path', 500)->nullable();
    $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
    $table->timestamps();
    $table->softDeletes();
    $table->unique('year');
});
```

### 4.2 Regras do Form Request (idênticas às da v5)

```php
'year'       => 'required|string|max:10',
'description' => 'required|string|max:255',
'file'       => 'nullable|file|mimes:pdf|max:20480',
```

### 4.3 Rotas

| HTTP | URI | Nome |
|---|---|---|
| GET | `/cms/relatorios-estatisticos-ouvidoria` | `relatorio-estatistico-ouvidoria-cms.index` |
| GET | `/cms/relatorios-estatisticos-ouvidoria/criar` | `relatorio-estatistico-ouvidoria-cms.create` |
| POST | `/cms/relatorios-estatisticos-ouvidoria` | `relatorio-estatistico-ouvidoria-cms.store` |
| GET | `/cms/relatorios-estatisticos-ouvidoria/{id}/editar` | `relatorio-estatistico-ouvidoria-cms.edit` |
| PUT | `/cms/relatorios-estatisticos-ouvidoria/{id}` | `relatorio-estatistico-ouvidoria-cms.update` |
| DELETE | `/cms/relatorios-estatisticos-ouvidoria/{id}` | `relatorio-estatistico-ouvidoria-cms.destroy` |
| GET | `/relatorio-estatistico-ouvidoria/{ano}/arquivo` | `relatorio-estatistico-ouvidoria.download` (público) |

### 4.4 Migração de dados

```sql
INSERT INTO ombudsman_reports (year, description, file_path, created_at, updated_at)
SELECT ano, descricao, 'docs/' || arquivo, NOW(), NOW()
FROM relatorios_estatisticos_ouvidoria
WHERE deleted_at IS NULL;
```

> Verificar se `arquivo` já contém o prefixo `docs/` (na v5, `saveUpload($file, 'docs', …)` retorna o nome do arquivo **sem** o diretório, e o controller grava `"docs/{$path}"` — logo o valor gravado **inclui** `docs/`). Ajustar a query conforme o conteúdo real.

### 4.5 Coexistência com o relatório dinâmico

| Página | Comportamento |
|---|---|
| `/relatorio-estatistico-ouvidoria?ano=2026` | Relatório **dinâmico** (indicadores calculados) — mantido |
| `/relatorio-estatistico-ouvidoria?ano=2026&documento=1` | Exibe o PDF institucional cadastrado para o ano, se existir |
| Botão na listagem do CMS | "Baixar PDF do relatório" |

## 5. Regressões de segurança introduzidas na v6 (alerta)

Comparando os controllers `OmbudsmanRequestController` e `OmbudsmanAnswerController` entre v5 e v6:

| Aspecto | v5 | v6 | Nota |
|---|---|---|---|
| `Sentinel::getUser()` nulo | `$user = Sentinel::getUser(); $userName = $user ? $user->name : 'Sistema';` | `Sentinel::getUser()->name` direto | A v6 **removeu** a guarda de nulo — se o usuário não estiver logado, **erro fatal** |
| `dd($e)` | idem | idem | Presente nas duas |
| `update()` sem filtro por id | idem | idem | Presente nas duas |
| Middleware `check.route.permission` | idem (mesmos construtores) | idem | Presente nas duas |

> **A v6 introduziu uma regressão de robustez:** a guarda `Sentinel::getUser()` nulo foi removida em `OmbudsmanRequestController@index`. Combinado com o middleware possivelmente ausente (§2.2 de `08-GAPS-E-DIVIDAS.md`), um acesso anônimo ao CMS da Ouvidoria pode resultar em **erro fatal** (não em um redirecionamento amigável).

## 6. Tabela-resumo

| Dimensão | v5 | v6 | Δ |
|---|---|---|---|
| Controllers CMS | 8 | 8 | 0 (1 novo, 1 removido) |
| Controllers Portal | 1 | 1 | 0 |
| Models | 7 | 9 | +2 |
| Form Requests | 7 | 8 | +1 |
| Migrations do módulo | 16 | 18 | +2 (e 2 exclusivas da v5) |
| Views CMS do módulo | ~22 | ~24 | +2 (setores), −2 (relatório, `_details`) |
| Views de e-mail | 10 | 10 | 0 |
| Views de portal | ~48 | ~50 | +2 (newsbit) |
| Arquivos JS | 12 | 12 | +1 (setores), −1 (relatório) |
| Seeds | 5 | 6 | +1 |
| Tabelas no banco | 8 (+1 relatório +1 pivô) | 9 | +1 (setores), −1 (relatório), −1 (pivô) |
| Views SQL | 6 | 7 | +1 (setores) |
