# Terceirizados — Arquitetura

> Módulo **existente apenas na `v5`**. Todos os caminhos citados são da `v5`.

---

## 1. Visão geral

O módulo tem **duas entradas independentes** que compartilham a mesma tabela:

```
CMS (autenticado)                         Portal (público)
─────────────────                         ───────────────
/cms/terceirizados                        /terceirizados
        │                                        │
        ▼                                        ▼
Prefecture\OutsourcedController          Portal\OutsourcedController
        │                                        │
        ▼                                        ▼
OutsourcedRequest (valida)                FilterOutsourceds (filtra)
        │                                        │
        ▼                                        ▼
IOutsourcedRepository                    App\Prefecture\Outsourced
(OutsourcedRepositoryDefaultImpl)                │
        └──────────────┬─────────────────────────┘
                       ▼
                 tabela `outsourceds`
```

A listagem do CMS **não** passa pelo controller de recurso: ela é servida por
`Prefecture\DatatableController@get_terceirizados` (Yajra DataTables), acionado pelo
JavaScript `v5/public/assets/js/custom/contents/terceirizados/datatables.js`.

## 2. Fluxo da requisição

### 2.1 CMS — listagem (GET `/cms/terceirizados`)

1. `v5/routes/basepro.php:395` → `Route::resource('terceirizados', 'Prefecture\OutsourcedController')`
   (dentro do grupo `prefix => cms`, `before => csrf`).
2. Middleware: `check.route.permission` (registrado no construtor do controller,
   `v5/app/Http/Controllers/Prefecture/OutsourcedController.php:21`).
   O grupo do recurso **não** usa `check.logged` — a autenticação é garantida pelo próprio
   middleware de permissão, que redireciona para `login` quando não há sessão Sentinel.
3. `Prefecture\OutsourcedController@index` grava log de atividade, incrementa
   `menu_orders.clicks` e devolve `prefecture.outsourceds.index`.
4. O navegador carrega `v5/public/assets/js/custom/contents/terceirizados/datatables.js`,
   que chama `runDatatables('terceirizados', ...)` → AJAX para
   `GET /cms/datatable/terceirizados` (`v5/routes/basepro.php:451`).
5. `Prefecture\DatatableController@get_terceirizados` lê a tabela com `DB::table`,
   descarta `deleted_at` não nulos, deriva `mes_extenso` e as flags de permissão
   (`edit`, `delete`) via `struct_views()`, e devolve JSON do Yajra DataTables.

### 2.2 CMS — criação/edição (POST/PUT `/cms/terceirizados[/{id}]`)

1. `OutsourcedRequest` valida `year`, `month`, `employee_name`, `company`, `role`,
   `arquivo.*` (mimes:pdf).
2. O controller monta `$data` com `$request->only([...])`, força `is_show = true` e,
   se houver upload, chama `FileTrait::saveUpload($file, 'terceirizados', null, null, false)`
   para cada arquivo, acumulando `{path, description}`.
3. `file_path` recebe `json_encode($items)`.
4. `IOutsourcedRepository::upInsert($data, $id)` → `Repository::upInsert` executa
   `updateOrCreate(['id' => $id], $data)` dentro de uma transação.
5. `flash('Terceirizado cadastrado com sucesso.')->success()` e redirect para
   `terceirizados.index`.

### 2.3 Portal — listagem (GET `/terceirizados`)

1. `v5/routes/web.php:124` (grupo `check.site.manutention`).
2. `Portal\OutsourcedController@index` recebe `Request` + `FilterOutsourceds`.
3. `FilterOutsourceds::filter()` monta a query (`is_show = true` + filtros opcionais).
4. A collection completa é materializada (`->get()`), a ordenação é
   `year desc, month desc, employee_name asc`.
5. Se a requisição tiver `type`, o controller desvia para `export()` (xls/pdf/csv/txt).
6. Caso contrário, `->paginate(15)` e renderização de
   `portal.layouts.<layout>.terceirizados.index` (o layout vem de
   `configurations.key = layout`, lido por `Admin\Controller::getTemplateActived`).

### 2.4 Portal — detalhe (GET `/terceirizados/{id}`)

`Portal\OutsourcedController@indexShow` → `Outsourced::where('id', $id)->where('is_show', true)->firstOrFail()`
→ `portal.layouts.<layout>.terceirizados.show`.

### 2.5 Remoção de anexo (DELETE `/cms/terceirizados/{id}/arquivo/{index}`)

`Prefecture\OutsourcedController@destroyArquivo` decodifica `file_path`, remove o
índice com `array_splice`, reindexa com `array_values` e regrava o JSON (ou `null` se a
lista ficou vazia). Responde JSON `{success}` / `{erro}`.

## 3. Classes envolvidas

| Classe | Caminho | Papel |
|---|---|---|
| `Prefecture\OutsourcedController` | `v5/app/Http/Controllers/Prefecture/OutsourcedController.php` | CRUD do CMS, upload, remoção de anexo, bulk delete, log de atividade. |
| `Portal\OutsourcedController` | `v5/app/Http/Controllers/Portal/OutsourcedController.php` | Listagem pública, filtros, paginação, ficha e exportações. |
| `OutsourcedRequest` | `v5/app/Http/Requests/OutsourcedRequest.php` | Form Request de `store`/`update`. |
| `App\Prefecture\Outsourced` | `v5/app/Http/Model/Prefecture/Outsourced.php` | Model Eloquent (`SoftDeletes`, `LogsActivity`). |
| `IOutsourcedRepository` | `v5/app/Repositories/Contracts/IOutsourcedRepository.php` | Contrato do repositório. |
| `OutsourcedRepositoryDefaultImpl` | `v5/app/Repositories/Eloquent/Prefecture/Outsourced/OutsourcedRepositoryDefaultImpl.php` | Implementação Eloquent (`all`, `find`, `delete`, `structureUpInsert`). |
| `Repository` (base) | `v5/app/Repositories/Repository.php` | `upInsert`, `destroy`, `destroyAll`, `dataTrait`. |
| `FilterOutsourceds` | `v5/app/Http/Filter/FilterOutsourceds.php` | Filtros do portal (`ano`, `mes`, `busca`, `funcao`). |
| `OutsourcedsExport` | `v5/app/Exports/OutsourcedsExport.php` | Export Maatwebsite a partir de uma view Blade. |
| `AppTrait` | `v5/app/Http/Traits/AppTrait.php` | Utilitários (sanitização, `traitRequest`). |
| `FileTrait` | `v5/app/Http/Traits/FileTrait.php` | `saveUpload`, `deleteFile`, `getFileName`. |
| `Prefecture\DatatableController` | `v5/app/Http/Controllers/Prefecture/DatatableController.php` | JSON da listagem do CMS (`get_terceirizados`). |
| `Admin\Controller` | `v5/app/Http/Controllers/Admin/Controller.php` | Base do controller do portal; `getTemplateActived()`. |
| `Prefecture\Controller` | `v5/app/Http/Controllers/Prefecture/Controller.php` | Base do controller do CMS (aplica `check.route.permission`). |
| `CheckRoutePermission` | `v5/app/Http/Middleware/CheckRoutePermission.php` | Autorização por rota/permissão. |

> O model está em `v5/app/Http/Model/Prefecture/Outsourced.php` mas declara o namespace
> `App\Prefecture` — o autoload do `v5/composer.json` cobre `app/` por **classmap**,
> por isso a classe resolve sem `psr-4` na pasta `app/Prefecture`.

## 4. Diagrama de fluxo (portal)

```
Navegador
   │  GET /terceirizados?ano=2026&mes=3&busca=joao&funcao=auxiliar
   ▼
routes/web.php:124  → middleware check.site.manutention
   ▼
Portal\OutsourcedController@index
   ├── FilterOutsourceds::filter($request)
   │      └─ Outsourced::where('is_show', true)
   │            .when(ano) .when(mes) .when(busca → ilike) .when(funcao → ilike)
   ├── query->orderBy(year desc)->orderBy(month desc)->orderBy(employee_name)->get()
   │      └─ (collection completa materializada SEMPRE)
   ├── $request->has('type')  ──true──► export(): xls | pdf | csv | txt
   │      └── false
   ├── query->paginate(15)
   ├── years/roles: Outsourced::where('is_show',true)->distinct()->pluck(...)
   └── view portal.layouts.{layout}.terceirizados.index
            ├── filtro.blade.php  (GET form + botões de exportação)
            └── portal/components/terceirizados/table.blade.php
```

## 5. Diagrama de fluxo (CMS, escrita)

```
Form (prefecture/outsourceds/form.blade.php)
   │  POST /cms/terceirizados  (ou PUT /cms/terceirizados/{id})
   ▼
OutsourcedRequest  ──(falha)──► 422 + view de erros no form.blade.php
   │(ok)
   ▼
Prefecture\OutsourcedController@store/@update
   ├── $request->only(['year','month','employee_name','company','role'])
   ├── $data['is_show'] = true
   ├── hasFile('arquivo') ?
   │      ├── sim → FileTrait::saveUpload(...)  → storage/app/public/terceirizados
   │      │        → file_path = json_encode([{path, description}, ...])
   │      └── não → (update) preserva o file_path existente
   ▼
OutsourcedRepositoryDefaultImpl::upInsert → Repository::upInsert
   └── DB::transaction → updateOrCreate(['id' => $id], $data)
   ▼
flash(...)->success() → redirect route('terceirizados.index')
```

## 6. Dependências externas

| Pacote (`v5/composer.json`) | Uso no módulo |
|---|---|
| `laravel/framework` 5.5.* | Framework base (Eloquent, Validation, Blade). |
| `cartalyst/sentinel` 2.0.* | Sessão e permissões (`Sentinel::getUser()`, `hasAnyAccess`). |
| `laracasts/flash` 3.0.* | `flash('...')->success()/error()/warning()`. |
| `spatie/laravel-activitylog` ^2.8 | `LogsActivity` no model + `activity()->performedOn(...)` no `index`. |
| `maatwebsite/excel` ^3.1 | `Excel::download(new OutsourcedsExport(...), 'terceirizados.xlsx'\|'.csv')`. |
| `barryvdh/laravel-dompdf` ^0.8.7 | `PDF::loadView('exports.outsourceds', ...)`. |
| `yajra/laravel-datatables` ^1.5 | JSON da listagem do CMS. |
| `laravelcollective/html` ^5.5 | `Form::open/number/text/select` no formulário do CMS. |
| `laravellegends/pt-br-validator` ^5.1 | Mensagens de validação em português. |
| `intervention/image` ^2.7 | Usado por `FileTrait::compress` — **não** aplicado a PDF (`$compress = false`). |
| `diglactic/laravel-breadcrumbs` ^4.0 | Trilha `terceirizados` / `terceirizados-show`. |

## 7. Onde o módulo se acopla ao restante do sistema

| Acoplamento | Ponto de contato |
|---|---|
| Menu do CMS | `v5/app/Providers/MenuServiceProvider.php:182` — `case 'terceirizados'` cria o item "Terceirizados" (`/cms/terceirizados`, ícone `handshake-o`) a partir do slug em `menu_orders`. |
| Contador de acessos | `menuClicks('terceirizados')` em `v5/app/Helpers/functions.php:822`. |
| Permissões | `has_access([...])` em `v5/app/Library/Helper/appHelpers.php:22`; resource `terceirizados` (id 14) em `resources`. |
| Auditoria | `activity()` (spatie) gravando em `activity_log`, exibido em `/cms/log`. |
| Transparência Fiscal | `v5/database/seeds/AddTerceirizadosInTransparenciaFiscalSeeder.php` cria `static_pages`/`contents`/`links` com slug `terceirizados` apontando para `https://www.<cidade>.pb.gov.br/terceirizados` (somente quando `configurations.key = estado` é `PB`). |
| Layout do portal | `configurations.key = layout` — se apontar para um template sem as views `terceirizados`, a rota pública quebra. |
