# VIAP — Arquitetura

> Módulo novo na `v6`, com **duas faces**: CMS (`/cms/viap`) e Portal público
> (`/viap`). Segue o padrão MVC clássico do portal legado (Laravel 5.5),
> com camada de *Repository* entre controller e model.

---

## 1. Fluxo da requisição

### 1.1 Face CMS (escrita e listagem)

```
POST /cms/viap  (ou PUT /cms/viap/{id})
        │
        ├─ web (Kernel: EncryptCookies, StartSession, ShareErrorsFromSession,
        │        VerifyCsrfToken, SubstituteBindings)
        ├─ check.route.permission  (App\Http\Middleware\CheckRoutePermission)
        │        ├─ Sentinel::check() == false  → redirect route('login')
        │        └─ Sentinel::inRole('super-admin') == false
        │                 └─ hasAccess()  → redirect()->back() + flash de aviso
        │
        ▼
Prefecture\ViapController@store / @update
        │
        ├─ ViapRequest (App\Http\Requests\ViapRequest)
        │        └─ rules(): publication_date, reference_month, reference_year,
        │           link_type (in:gabinete,outro_orgao),
        │           councilman_id (required_if link_type=gabinete),
        │           organ_id     (required_if link_type=outro_orgao)
        │
        ▼
App\Repositories\Eloquent\Prefecture\Viap\ViapRepositoryDefaultImpl
        │   ├─ Repository::upInsert()  → DB::transaction + updateOrCreate
        │   └─ structureUpInsert()     → toUSDataFormat(), slugify(), generateSlug()
        │
        ▼
App\Http\Model\Prefecture\Viap  (tabela `viap`, SoftDeletes, LogsActivity)
        │
        ▼
ViapController::extractedToArchive()
        ├─ App\Http\Traits\FileTrait::getFileName()
        └─ IArchiveRepository::upInsert()  → App\Prefecture\Archive  + Storage
                └─ $viap->archives()->attach($insertArchive)   (pivot viap_has_archives)
```

### 1.2 Face CMS — DataTable da listagem

```
GET /cms/datatable/viap
        ├─ web + check.route.permission
        ▼
Prefecture\DatatableController@get_viap()
        ├─ DB::table('view_viap')->get()            ← view SQL (não o model)
        ├─ struct_views($data, 'viap')               ← injeta edit/delete/active/inactivate
        │       └─ has_access('viap.edit' | 'viap.delete' | 'viap.restore' | 'viap.inactivate')
        └─ Yajra\DataTables\Facades\DataTables::of(...)->make(true)
```

### 1.3 Face Portal (público)

```
GET /viap  (?link_type&councilman_id&organ_id&month&year)
GET /viap/{slug}
GET /viap/export?type=xls|pdf|csv
        │
        ├─ web + check.site.manutention
        ▼
Portal\ViapPortalController@index / @show / @export
        ├─ Viap::with(['councilman','organ','archives'])->active()
        ├─ filtros where() por link_type / councilman_id / organ_id /
        │   reference_month / reference_year
        ├─ export:  Maatwebsite\Excel\Facades\Excel + App\Exports\ViapExport
        │            barryvdh/laravel-dompdf (PDF::loadView)
        │            App\Exports\ViapExport::gerarTxt() (TXT)
        └─ view('portal.layouts.' . getTemplateActived() . '.viap.index')
```

## 2. Classes envolvidas

| Classe | Caminho | Papel |
|---|---|---|
| `App\Http\Controllers\Prefecture\ViapController` | `v6/app/Http/Controllers/Prefecture/ViapController.php` | CRUD do CMS; monta o formulário; dispara upload de anexos; registra atividade no `index` |
| `App\Http\Controllers\Portal\ViapPortalController` | `v6/app/Http/Controllers/Portal/ViapPortalController.php` | Listagem pública, filtros, detalhe por slug e exportação (XLSX/PDF/CSV/TXT) |
| `App\Http\Controllers\Prefecture\DatatableController` | `v6/app/Http/Controllers/Prefecture/DatatableController.php` | Alimenta o DataTable do CMS a partir da view `view_viap` (`get_viap()`, linhas 480-489) |
| `App\Http\Controllers\Prefecture\Controller` | `v6/app/Http/Controllers/Prefecture/Controller.php` | Controller base do CMS; registra `check.route.permission` no construtor |
| `App\Http\Controllers\Admin\Controller` | `v6/app/Http/Controllers/Admin/Controller.php` | Controller base do portal; fornece `getTemplateActived()` |
| `App\Http\Model\Prefecture\Viap` | `v6/app/Http/Model/Prefecture/Viap.php` | Model Eloquent (`viap`); `SoftDeletes`, `LogsActivity`, relações `councilman`, `organ`, `archives`; `scopeActive`; `referenceMonths()`; accessor `display_title` |
| `App\Http\Model\Prefecture\CityCouncilor` | `v6/app/Http/Model/Prefecture/CityCouncilor.php` | Parliamentary (tabela `councilmen`), usado como lista de gabinetes |
| `App\Prefecture\Content` | `v6/app/Http/Model/Prefecture/Content.php` | Conteúdo do portal (tabela `contents`); nesta módulo representa o **órgão** |
| `App\Prefecture\CategoryContent` | `v6/app/Http/Model/Prefecture/CategoryContent.php` | Categorias de conteúdo; o módulo usa a categoria de `slug = 'orgaos'` |
| `App\Prefecture\Archive` | `v6/app/Http/Model/Prefecture/Archive.php` | Metadados do arquivo anexado (tabela `archives`) |
| `App\Prefecture\MenuOrder` | `v6/app/Http/Model/Prefecture/MenuOrder.php` | Posição do módulo no menu do CMS (criado pelo seed) |
| `App\Admin\Resource` | `v6/app/Http/Model/Admin/Resource.php` | Recurso/permissão (tabela `resources`); criado pelo seed |
| `App\Http\Requests\ViapRequest` | `v6/app/Http/Requests/ViapRequest.php` | Validação do formulário de cadastro/edição |
| `App\Repositories\Contracts\IViapRepository` | `v6/app/Repositories/Contracts/IViapRepository.php` | Contrato do repositório (vazio; herda `IRepository`) |
| `App\Repositories\Eloquent\Prefecture\Viap\ViapRepositoryDefaultImpl` | `v6/app/Repositories/Eloquent/Prefecture/Viap/ViapRepositoryDefaultImpl.php` | Implementação: normaliza datas, monta o slug, herda `upInsert`/`destroy`/`destroyAll` |
| `App\Repositories\Repository` | `v6/app/Repositories/Repository.php` | Base: `upInsert` (transação + `updateOrCreate`), `destroy`, `destroyAll` |
| `App\Repositories\Eloquent\Prefecture\Archive\ArchiveRepository` | `v6/app/Repositories/Eloquent/Prefecture/Archive/ArchiveRepository.php` | Grava o arquivo em disco e devolve o model `Archive` |
| `App\Exports\ViapExport` | `v6/app/Exports/ViapExport.php` | Exportação `FromView` (maatwebsite/excel) a partir de `exports.viap` |
| `App\Http\Middleware\CheckRoutePermission` | `v6/app/Http/Middleware/CheckRoutePermission.php` | Autorização por nome de rota/permissão do Sentinel |
| `App\Http\Middleware\CheckLogged` | `v6/app/Http/Middleware/CheckLogged.php` | Exige sessão ativa (usado por outros módulos, **não** pelo VIAP) |
| `App\Http\Traits\AppTrait` | `v6/app/Http/Traits/AppTrait.php` | `toUSDataFormat()`, `traitRequest()`, sanitizadores |
| `App\Http\Traits\FileTrait` | `v6/app/Http/Traits/FileTrait.php` | `getFileName()`, `saveUpload()` |
| `App\Library\Helper\appHelpers` | `v6/app/Library/Helper/appHelpers.php` | Helper global `has_access()` |

## 3. Diagrama do fluxo (Mermaid)

```mermaid
flowchart TD
    A[Browser] -->|GET /cms/viap| B[Route: viap.index]
    A -->|POST /cms/viap| C[Route: viap.store]
    A -->|GET /viap| D[Route: portal.viap.index]

    B --> E[check.route.permission]
    C --> E
    E -->|sem sessão| F[redirect /login]
    E -->|sem permissão| G[flash aviso + redirect back]
    E --> H[ViapController]

    H --> I[ViapRequest]
    I -->|falha| J[422 com erros]
    I -->|ok| K[ViapRepositoryDefaultImpl]
    K --> L[(tabela viap)]
    H --> M[extractedToArchive]
    M --> N[(archives + viap_has_archives)]
    H --> O[view prefecture.viap.index]

    D --> P[check.site.manutention]
    P --> Q[ViapPortalController]
    Q --> R[(tabela viap via Eloquent)]
    Q --> S[view portal.layouts.modeloN.viap.index]
    Q --> T[Excel / dompdf / TXT]
```

## 4. Dependências externas (composer)

Pacotes de `v6/composer.json` realmente usados pelo módulo:

| Pacote | Versão | Uso no VIAP |
|---|---|---|
| `cartalyst/sentinel` | `2.0.*` | Autenticação e permissões (`Sentinel::check()`, `hasAnyAccess`) |
| `maatwebsite/excel` | `^3.1` | `Excel::download(new ViapExport(...), 'viap.xlsx' / 'viap.csv')` |
| `barryvdh/laravel-dompdf` / `dompdf/dompdf` | `^0.8.7` / `^0.8.3` | `PDF::loadView('exports.viap', ...)` |
| `yajra/laravel-datatables` | `^1.5` | `DataTables::of(...)` no endpoint do CMS |
| `spatie/laravel-activitylog` | `^2.8` | `LogsActivity` no model + `activity()->performedOn()` no `index` |
| `laracasts/presenter` / `laravel/framework` | — | `PresentableTrait` (usado por `Content`, dependência indireta) |

Não há dependência de API externa (OpenData), Redis ou fila no VIAP.

## 5. Onde o módulo se encaixa no menu do CMS

```
MenuServiceProvider::boot()
  └─ Evento BuildingMenu (JeroenNoten\LaravelAdminLte)
       └─ MenuOrder::with('modulo')->orderBy('clicks','DESC')->orderBy('id','ASC')
            └─ if (has_access("$menu->slug.index") && $menu->modulo->status)
                 └─ case 'viap':  →  ['text' => 'VIAP', 'url' => '/cms/viap', 'icon' => 'file-text-o']
```
Fonte: `v6/app/Providers/MenuServiceProvider.php:40-46` e `:412-413`.
