# Requerimentos — Arquitetura

> **Versão analisada:** `v6` — módulo **novo na v6** (sem equivalente na v5)
> **Slug:** `requerimentos` (`modulos.id = 41`)

---

## 1. Visão geral das camadas

O módulo não possui service, repository ou model exclusivo. Ele é montado sobre
três pilares já existentes na plataforma:

1. **Conteúdo genérico** (`contents` + `category_contents`) — o CMS publica e o
   portal lista.
2. **View SQL `requerimentos`** — projeção tabulada usada exclusivamente pela
   listagem do CMS (DataTable).
3. **Controller de portal dedicado** — `Portal\RequerimentoController`, único
   código realmente exclusivo do módulo.

```
Navegador
   │
   ├── /requerimentos[...]  ──► routes/web.php (grupo check.site.manutention)
   │                              └── Portal\RequerimentoController
   │                                    ├── CategoryContent (Eloquent)  ──► category_contents
   │                                    ├── Content (Eloquent)          ──► contents
   │                                    └── Archive (Eloquent)         ──► archives
   │                                          └── Blade do tema ativo (modelo1..modelo6)
   │
   └── /cms/...  ──────────────► routes/basepro.php (prefixo cms, before csrf)
                                  ├── ContentController@genericIndex/...  (slug requerimentos)
                                  │     ├── ContentRequest (regras)
                                  │     ├── CityCouncilor (select requerente)
                                  │     └── ActivityLog (auditoria)
                                  ├── CategoryContentController (subcategorias)
                                  └── DatatableController@get_requerimentos
                                        └── DB::table('requerimentos')  ──► VIEW SQL
```

## 2. Fluxo de requisição — Portal

### 2.1 `GET /requerimentos` (`requerimentos.index`)

1. `Route::middleware(['check.site.manutention'])` (`v6/routes/web.php:8`) decide
   se o portal está em manutenção.
2. `RequerimentoController@index` (`v6/app/Http/Controllers/Portal/RequerimentoController.php:26`)
   busca a **primeira categoria filha** da raiz `requerimentos`:

```php
$categoryFirst = CategoryContent::whereHas('categoryContents', function ($q) {
    $q->where('slug', '=', 'requerimentos');
})->orderBy('order')->first();
```

3. Se existir, redireciona para `requerimentos.show` com o slug da categoria;
   senão tenta a raiz; senão `redirect('/')->with('error', ...)`.

### 2.2 `GET /requerimentos/{slug}` (`requerimentos.show`)

1. Carrega **todas** as categorias filhas da raiz (mesma consulta do passo 2.1,
   agora com `->get()`).
2. Define a categoria ativa (`firstWhere('slug', $slug)`), com fallback para a
   primeira; o título da página é o nome da categoria.
3. Monta a query unificada sobre `contents`:

```php
$categoryIds = $categories->pluck('id');

$contentsQuery = Content::query()
    ->select('title', 'text', 'publication_date', 'slug', 'category_contents_id')
    ->whereIn('category_contents_id', $categoryIds)
    ->where('publication_date', '<=', $current_date);
```

4. Aplica filtros (`ano`, `mes`, `texto`) — detalhados em
   `05-REGRAS-E-PORTABILIDADE.md`.
5. `paginate(12)` + `appends($request->query())` (preserva os filtros na
   paginação).
6. Calcula a lista de **anos** (`EXTRACT(YEAR FROM publication_date)`) e, se um
   ano foi escolhido, a lista de **meses** (`EXTRACT(MONTH ...)`).
7. Se `request()->ajax()` → devolve `Response::json(view(...requerimentos.list)->render())`;
   caso contrário → devolve `view(...requerimentos.show)`.

### 2.3 `GET /requerimentos/{slug1}/{slug2}` (`requerimentos.view`)

1. Busca o `Content` **apenas por `slug`**, sem verificar a categoria
   (fragilidade registrada em `08-GAPS-E-DIVIDAS.md`).
2. Se não existir → `redirect('/requerimentos')->with('error', 'Requerimento não encontrado.')`.
3. Se `publication_date` for futura → `redirect('/requerimentos')`.
4. Carrega `archives` com `highlight = null` e o primeiro `archive` com
   `view_pdf = true`.
5. Incrementa `contents.clicks` em 1 e salva.
6. Renderiza `portal.layouts.{template}.requerimentos.view`.

## 3. Fluxo de requisição — CMS

| Passo | Arquivo | O que faz |
|---|---|---|
| Menu do CMS | `v6/app/Http/Controllers/Prefecture/ContentController.php:111` | `returnIndex('Requerimentos', 'requerimentos')`; chama `menuClicks('requerimentos')` (incrementa `menu_orders.clicks`) |
| Formulário | `v6/app/Http/Controllers/Prefecture/ContentController.php:203-224` | Monta `councilmen` (vereadores ativos), `categoryContent` (raiz) e `categoryContentSelect` (filhas) |
| Validação | `v6/app/Http/Requests/ContentRequest.php:240-262` | Regras do slug `requerimentos` (ver `03-ROTAS-E-CONTROLADORES.md`) |
| Persistência | `v6/app/Http/Controllers/Prefecture/ContentController.php:657` e `:968` | Grava `councilmen_id` (ou `null` quando `"0"`/ausente) |
| Listagem | `v6/app/Http/Controllers/Prefecture/DatatableController.php:46-50` | `DB::table('requerimentos')->get()` → DataTable server-side |
| Flags de ação | `v6/app/Http/Controllers/Prefecture/DatatableController.php:504-519` | `struct_views()` anexa `edit`/`delete`/`active`/`inactivate` conforme `has_access()` |
| Subcategorias | `v6/app/Http/Controllers/Prefecture/CategoryContentController.php:149-162`, `:259-274` | CRUD de `category_contents` filhas da raiz `requerimentos` |

## 4. Classes envolvidas

| Classe | Caminho | Papel |
|---|---|---|
| `App\Http\Controllers\Portal\RequerimentoController` | `v6/app/Http/Controllers/Portal/RequerimentoController.php` | Listagem unificada, filtros, detalhe e contador de cliques |
| `App\Http\Controllers\Admin\Controller` | `v6/app/Http/Controllers/Admin/Controller.php:39-49` | `getTemplateActived()` — escolhe o tema do portal (`Configuration.key = 'layout'`) |
| `App\Http\Controllers\Prefecture\ContentController` | `v6/app/Http/Controllers/Prefecture/ContentController.php` | CRUD genérico de conteúdo (listagem, create, edit, store, update, delete) |
| `App\Http\Controllers\Prefecture\CategoryContentController` | `v6/app/Http/Controllers/Prefecture/CategoryContentController.php` | CRUD das subcategorias de requerimento |
| `App\Http\Controllers\Prefecture\DatatableController` | `v6/app/Http/Controllers/Prefecture/DatatableController.php` | DataTable server-side alimentado pela view SQL |
| `App\Http\Requests\ContentRequest` | `v6/app/Http/Requests/ContentRequest.php` | Validação do formulário por slug |
| `App\Prefecture\Content` | `v6/app/Http/Model/Prefecture/Content.php` | Eloquent de `contents`; relações `categoryContents()`, `councilman()`, `archives()` |
| `App\Prefecture\CategoryContent` | `v6/app/Http/Model/Prefecture/CategoryContent.php` | Eloquent de `category_contents` (árvore) |
| `App\Http\Model\Prefecture\CityCouncilor` | `v6/app/Http/Model/Prefecture/CityCouncilor.php` | Eloquent de `councilmen` (tabela declarada no model) |
| `App\Prefecture\Archive` | `v6/app/Http/Model/Prefecture/Archive.php` | Anexos (`view_pdf`, `highlight`, `extension`, `path`) |
| `App\Http\Model\Admin\Modulos` | `v6/app/Http/Model/Admin/Modulos.php` | Liga/desliga o módulo em `/cms/modulos` |

## 5. Dependências externas

| Pacote / recurso | Uso no módulo |
|---|---|
| `cartalyst/sentinel` | Sessão e permissões (`has_access()` em `v6/app/Library/Helper/appHelpers.php:22`) |
| `yajra/laravel-datatables-oracle` | `DataTables::of(...)` no controller do CMS |
| `laracasts/presenter` | `PresentableTrait` em `Content`/`CategoryContent` |
| PostgreSQL 10 | View SQL `requerimentos`, `EXTRACT`, `translate(...)`, `to_char(...)` |
| jQuery + Bootstrap 4 | Ajax de filtragem/paginação no portal |
| Summernote | Editor rico do texto do requerimento no CMS |
| AdminLTE 1.22 | Layout do CMS |

## 6. Dependências entre módulos

| Módulo | Relação |
|---|---|
| `vereadores` | Fornece `councilmen`; `contents.councilmen_id` referencia `councilmen.id` |
| `sessoes` | Compartilha a mesma estrutura de conteúdo/categorias (mesmo padrão de view SQL) |
| `atas-e-pontos` | Mesmo padrão de conteúdo genérico; concentra o padrão de SQL frágil de busca textual |
| Menu do portal | `menu_items.category_contents_id = 37` (raiz `requerimentos`) cria o link “Requerimentos” (`v6/database/seeds/MenuItemsTableSeeder.php:386-394`) |

## 7. Diagrama de fluxo (Mermaid)

```mermaid
flowchart TD
    A[Cidadão GET /requerimentos] --> B[check.site.manutention]
    B --> C[RequerimentoController@index]
    C --> D{Primeira categoria filha?}
    D -- sim --> E[redirect requerimentos.show]
    D -- não --> F[redirect raiz ou / com error]
    E --> G[RequerimentoController@show]
    G --> H[CategoryContent: filhas da raiz]
    H --> I[Content: whereIn categorias + publication_date <= now]
    I --> J[Filtros ano / mes / texto]
    J --> K{paginate 12}
    K -- ajax --> L[Response::json(list.blade.php render)]
    K -- normal --> M[show.blade.php]
    L --> N[JS substitui #include-list]
    M --> O[list.blade.php + nav.blade.php]
    O --> P[Clique no item]
    P --> Q[RequerimentoController@view]
    Q --> R[Content::where(slug) + Archive + clicks++]
    R --> S[view.blade.php + files-list.blade.php]
```

## 8. Auditoria e telemetria

- `menuClicks('requerimentos')` (`v6/app/Helpers/functions.php:865`) incrementa
  `menu_orders.clicks` a cada abertura da listagem no CMS — é o dado usado para
  ordenação de módulos mais acessados.
- `activity()->performedOn(...)->causedBy(Sentinel::getUser())->log(...)`
  registra “Listagem de categoria de requerimentos” e “Acessou criação de
  categoria de Requerimentos” (`v6/app/Http/Controllers/Prefecture/CategoryContentController.php:157-161`, `:267-271`).
