# Revisões — Arquitetura

> Módulo CMS (prefixo `/cms`). Sem rota pública. Análise: `v6` (Laravel 5.5.50 / PHP 7.2.34).

---

## 1. Visão geral das camadas

O módulo é um CRUD clássico de Laravel com três desvios em relação ao padrão do restante do
CMS:

1. **Sem service layer**: os controllers `RatingsController`, `QuestionController` e
   `OptionsController` falam direto com o Eloquent. Só o relatório/PDF passa por repository
   (`EnviosRepository`).
2. **Endpoints AJAX que devolvem string ou model cru**: não há API Resource; o
   `QuestionController` serializa o model inteiro e o `OptionsController` devolve `"true"` /
   `"false"`.
3. **Listagens apoiadas em views SQL**, não em queries Eloquent: `formulario`, `envios`,
   `respostas` e `listagem_clientes` são views Postgres consumidas pelo
   `DatatableController` (Yajra DataTables).

## 2. Classes envolvidas

### 2.1 Controllers (`v6/app/Http/Controllers/Prefecture/`)

| Classe | Papel | Observações |
|---|---|---|
| `RatingsController` | CRUD da avaliação (`ratings`), status, cópia, export/import JSON, exclusão em lote | Aplica `check.route.permission` no construtor; usa `IRatingsRepository` |
| `RatingsClientsController` | Listagem de relatórios do "cliente", PDF do envio, tela de estatísticas | Estende `App\Http\Controllers\Admin\Controller`; **não** aplica `check.route.permission` |
| `QuestionController` | CRUD e reordenação de perguntas e categorias | Só `Request` cru, sem Form Request |
| `QuestionAnswersController` | Aplicação da avaliação, respostas, e-mail, relatórios | O controller mais denso do módulo (~400 linhas) |
| `OptionsController` | CRUD de categorias de pergunta, de opções e de grupos de opções | Só `Request` cru; respostas em string |
| `DatatableController` | Endpoints JSON das DataTables (`/cms/datatable/*`) | Compartilhado por todo o CMS |

### 2.2 Models (`v6/app/Http/Model/Prefecture/`)

| Classe | Tabela | Namespace declarado |
|---|---|---|
| `Ratings` | `ratings` | `App\Prefecture` |
| `SendRating` | `send_ratings` | `App\Prefecture` |
| `AnswerForm` | `answer_forms` | `App\Prefecture` |
| `EmailRevisoes` | `email_revisoes` | `App\Prefecture` |
| `Questions` | `questions` | `App\Prefecture` |
| `QuestionType` | `question_types` | `App\Prefecture` |
| `QuestionCategory` | `question_categories` | `App\Prefecture` (usa `RevisoesPresenter`) |
| `Options` | `options` | `App\Prefecture` |
| `OptionCategory` | `option_categories` | `App\Prefecture` |
| `OptionCategoryHasOption` | `option_category_has_options` | `App\Prefecture` (sem `SoftDeletes`) |

> **Atenção**: `v6/composer.json:52-61` usa `classmap: ["app/"]` + `psr-4: App\ => app/`.
> O `vendor/composer/autoload_classmap.php` é **stale**: ainda mapeia
> `App\Http\Model\Prefecture\SendRating` e `App\Http\Model\Prefecture\EmailRevisoes` para os
> arquivos que hoje declaram `namespace App\Prefecture;`. Ver `05-REGRAS-E-PORTABILIDADE.md`.

### 2.3 Repositories e contratos

| Caminho | Papel |
|---|---|
| `v6/app/Repositories/Contracts/IRatingsRepository.php` | Contrato **vazio** (sem métodos) |
| `v6/app/Repositories/Contracts/IEnviosRepository.php` | Contrato **vazio** (sem métodos) |
| `v6/app/Repositories/Eloquent/Prefecture/Ratings/RatingsRepositoryDefaultImpl.php` | Implementação de `IRatingsRepository`; só define `structureUpInsert()` vazio |
| `v6/app/Repositories/Eloquent/Prefecture/Ratings/Envios/EnviosRepository.php` | Reimplementa `IEnviosRepository`: `generatePDF()`, `returnEnvios()`, `returnResults()` |
| `v6/app/Repositories/Repository.php` | Base com `destroyAll()`, `filter_request()` |

O `RatingsController` injeta `IRatingsRepository` apenas para `destroyAll()` e
`getModel()` (usado no log de atividade). O `RatingsRepositoryDefaultImpl` é, na prática, um
caso legado do padrão "Repository" do projeto.

### 2.4 Requests, Presenters e traits

| Caminho | Papel |
|---|---|
| `v6/app/Http/Requests/RatingRequest.php` | Valida `title`, `subtitle`, `description` (obrigatórios) no `store`/`update` da avaliação |
| `v6/app/Http/Requests/RevisoesRepostas.php` | Valida `dataDeEnvio` e `revisor` no `store` da resposta |
| `v6/app/Http/Presenters/RevisoesPresenter.php` | `pegarQuestoesPDF()`, `pegarQuestoes()`, `pegarFilhos()` — usado por `QuestionCategory` |
| `v6/app/Http/Traits/AppTrait.php` | `toBRDataFormat()` usado na formatação das datas de envio |
| `v6/app/Library/Helper/appHelpers.php` | `has_access()` (autorização por permissão Sentinel) |
| `v6/app/Helpers/functions.php` | `slug_fix()`, `clean_slug()`, `menuClicks()`, `formatarDataPorExtenso()` |

### 2.5 Middleware

| Middleware | Onde incide | Efeito |
|---|---|---|
| `check.logged` | grupo de `v6/routes/basepro.php:265-344` | exige login no CMS para o resource `revisoes`, `respostas`, `questions`, `options` |
| `check.route.permission` | construtor de `RatingsController` | compara o nome da rota com as permissões do usuário (super-admin passa direto) |
| `check.site.manutention` | não aplicado neste módulo | — |

## 3. Diagrama de fluxo

### 3.1 Montagem do questionário (CMS)

```mermaid
flowchart TD
    A[Operador: GET /cms/revisoes] --> B[RatingsController@index]
    B --> C[(view SQL formulario)]
    C --> D[DataTable /cms/datatable/formulario]
    D --> E{ação}
    E -->|Cadastrar| F[GET /cms/revisoes/create]
    F --> G[POST /cms/revisoes  RatingRequest]
    G --> H[(ratings)]
    E -->|Editar| I[GET /cms/revisoes/{id}/edit]
    I --> J[revisoesv2.js + modais]
    J --> K[POST /cms/questions/saveQuestion]
    K --> L[(questions)]
    J --> M[POST /cms/options/saveCategories]
    M --> N[(question_categories)]
    J --> O[POST /cms/options/saveOptions]
    O --> P[(options + option_category_has_options)]
    J --> Q[POST /cms/reorder / categories-reorder]
    Q --> R[(questions.order / question_categories.order)]
```

### 3.2 Aplicação da avaliação e relatório

```mermaid
flowchart TD
    A[GET /cms/respostas/create/{ratings_id}] --> B[QuestionAnswersController@createDeEnvios]
    B --> C[(questions + question_categories + options)]
    C --> D[Operador preenche e envia]
    D --> E[POST /cms/respostas  RevisoesRepostas]
    E --> F[(send_ratings: protocolo, revisor, pts)]
    E --> G[(answer_forms: option_id + observation)]
    E --> H[QuestionAnswersController::pontuacao]
    H --> I[EnviosRepository::generatePDF]
    I --> J[(storage/app/public/content/revisoes/{id}/pdf/revisoes_{data}.pdf)]
    J --> K[send_ratings.path]
    K --> L[POST /cms/resposta/enviar_email]
    L --> M[(email_revisoes)]
    L --> N[Mail::send com anexo]
    N --> O[send_ratings.status = true]
    O --> P[GET /cms/resposta/enviadas/{id}  gráficos]
    P --> Q[EnviosRepository::returnResults]
```

### 3.3 Importação/exportação do questionário

```mermaid
flowchart TD
    A[GET /cms/download_revisao/{id}] --> B[RatingsController@baixar_revisao]
    B --> C[public/cache/revisao_{slug}.json]
    C --> D[POST /cms/import_revisao]
    D --> E[RatingsController@importar_revisao]
    E --> F[(ratings + question_categories + question_types + option_categories + options + questions)]
```

## 4. Dependências externas (composer)

| Pacote | Uso no módulo |
|---|---|
| `barryvdh/laravel-dompdf` | Geração do PDF da avaliação (`EnviosRepository::generatePDF`) |
| `intervention/image` | `AppTrait::createIMGraph()` (`v6/app/Http/Traits/AppTrait.php:795`) — imagens dos gráficos embutidas no PDF |
| `laracasts/presenter` | `RevisoesPresenter` ligado a `QuestionCategory` |
| `cartalyst/sentinel` | Sessão, papéis e permissões (`Sentinel::getUser()`, `has_access`) |
| `spatie/laravel-activitylog` | Log de "Exclusão de revisões" em `deleteByLote` |
| `yajra/laravel-datatables` | Endpoints `/cms/datatable/*` |
| `laravel/framework` | Eloquent, Blade, Mail, Storage |

## 5. Onde o módulo encosta em outros módulos

| Dependência | Tipo | Onde |
|---|---|---|
| `Configuration` (`titulo_site`, `nome_prefeito`, `logo`, `favicon`, `cor_1`, `estado`) | leitura | `EnviosRepository::generatePDF`, `QuestionAnswersController::enviar_email` |
| `Supplier` (`fornecedor_ativo`) | leitura | `EnviosRepository::generatePDF` (fornecedor no PDF) |
| `Admin\User` | leitura | `QuestionAnswersController::createDeEnvios` / `enviar_email` |
| `Status` / `StatusCategory` | **não usado** | Tabelas `statuses`/`status_categories` existem e têm seed (`v6/database/seeds/StatusesTableSeeder.php`), mas o módulo de Revisões **não** as consome — o status aqui é o booleano `ratings.status` / `send_ratings.status` |
| `modulos` (id 34) / `resources` (43, 44) / `menu_orders` (28) | registro | Aparição no menu do CMS e contagem de cliques |
| Formulários Dinâmicos | **nenhum** | Apenas coincidência de nomenclatura ("formulário") |
| `DatatableController` | infraestrutura | Listagens |

## 6. Persistência e efeitos colaterais

| Operação | Efeito colateral |
|---|---|
| `POST /cms/revisoes` | `reverseAllStates()` desativa todas as avaliações antes de ativar a nova |
| `POST /cms/setStatus` | Idem, alternando o estado de uma avaliação |
| `POST /cms/revisoes/delete-por-lote` | Grava log de atividade (`activity()->performedOn()->causedBy()->log('Exclusão de revisões')`) e apaga em lote |
| `POST /cms/respostas` | Cria envio + respostas, recalcula pontos, **gera o PDF** e grava `path` |
| `POST /cms/resposta/enviar_email` | Cria destinatários, envia e-mail, muda `status` do envio e **recarrega `data`** com `new DateTime()` (a data original do envio é sobrescrita!) |
| `GET /cms/download_revisao/{id}` | Escreve arquivo em `public/cache/` (artefato não versionado) |
| `POST /cms/import_revisao` | Insere avaliação nova + perguntas + tipos + opções; não é transacional |
