# Terceirizados — Resumo Executivo

> **Versão analisada:** `v5` (módulo **ausente** na `v6`)
> **Slug do módulo:** `terceirizados` (tabela `resources`, id 14 no banco v5; `menu_orders`, id 34)
> **Natureza:** Ambos (CMS `/cms/terceirizados` + Portal `/terceirizados`)
> **Estado:** apenas v5 — a v6 não possui controllers, model, request, export, views nem rotas deste módulo

---

## 1. Objetivo

O módulo **Terceirizados** publica a folha de pessoal terceirizado (pessoas contratadas
por empresas privadas para prestar serviços ao município), permitindo ao operador do CMS
cadastrar, por **ano e mês de referência**, o nome do empregado, a empresa contratada,
a função exercida e os anexos que comprovam o vínculo (contratos, aditivos). O cidadão
acessa a página pública `/terceirizados`, filtra por ano, mês, função e texto livre,
consulta a ficha de cada empregado e baixa os arquivos comprovativos. Serve ao município
como instrumento de transparência sobre despesa com terceirização e como fonte de
planejamento e fiscalização contratual.

## 2. Efeito / Impacto

| Faixa | Efeito |
|---|---|
| Cidadão | Página pública com lista paginada (15 por página), filtros por ano/mês/função/busca, ficha individual e download dos anexos (PDF); exportação em XLSX, PDF, CSV e TXT do resultado filtrado. |
| Prefeitura (CMS) | CRUD completo com auditoria de atividade (`spatie/laravel-activitylog`), menu próprio no AdminLTE, exclusão individual e em lote, controle de acesso por permissão `terceirizados.*`. |
| Transparência e legalidade | Publica folha terceirizada por competência (ano/mês), atendendo ao princípio da transparência em folha de pagamento e permitindo a socialização do gasto com pessoal terceirizado. |
| Operação técnica | Tabela `outsourceds` com `SoftDeletes`; arquivos anexos gravados em `storage/app/public/terceirizados` e referenciados por JSON na coluna `file_path`. |

## 3. Comportamento

- Cada registro é IDENTIFICADO por **ano** (2000 até ano corrente + 1) e **mês** (1–12),
  e descreve **um** empregado terceirizado (`employee_name`), a **empresa** (`company`)
  e a **função** (`role`). Todos os campos textuais são obrigatórios e limitados a 255 caracteres.
- Ao criar, o registro nasce com `is_show = true`; o CMS não oferece nenhuma tela para
  desligar a visibilidade — o portal filtra por `is_show = true` e o CMS lista todos os
  registros (inclusive os ocultos, se algum for inserido manualmente).
- Anexos são **opcionais** e restritos a **PDF** (`mimes:pdf`); vários arquivos podem ser
  enviados por registro, cada um com uma descrição livre (`arquivo_desc`).
- O caminho dos anexos é gravado na coluna `file_path` como um **JSON** com a forma
  `[{"path": "terceirizados/xxx.pdf", "description": "..."}]`; o download usa
  `url('storage/' . $path)`.
- Na edição, arquivos **novos** são **somados** aos existentes; os existentes só saem da
  lista por remoção individual (endpoint `DELETE` dedicated), que apaga o vínculo mas
  **não** o arquivo físico.
- Exclusão de registro é *soft delete*; a listagem do CMS e o portal ignoram registros
  com `deleted_at` preenchido.
- Auditoria: abrir a listagem no CMS grava "Listou Terceirizados" no log de atividades;
  o model `Outsourced` registra created/updated/deleted automaticamente.
- Cada visita ao CMS incrementa o contador `menu_orders.clicks` do slug `terceirizados`.

## 4. Como o Usuário Interage

**Operador (CMS, `/cms/terceirizados`)**

1. Menu AdminLTE "Terceirizados" (ícone `handshake-o`) → listagem em DataTable (AJAX) com
   colunas Ano, Mês, Nome do Empregado, Empresa, Função; botões de Excel, PDF, CSV e TXT
   na barra do DataTable; botão "Cadastrar Terceirizado" e botão de exclusão em lote.
2. Tela de cadastro/edição com campos Ano, Mês, Nome, Empresa, Função e um bloco dinâmico
   "Adicionar Arquivo" (input `file` restrito a `.pdf` + descrição).
3. Arquivos já enviados aparecem listados com botões de download, visualizar e excluir
   (chamada AJAX `DELETE`).

**Cidadão (Portal, `/terceirizados`)**

1. Cabeçalho "Terceirizados", card de filtros (Ano, Mês, Função, busca livre) e botão
   "Consultar"/"Limpar campos".
2. Tabela com os registros e link "Detalhes" por linha (ícone de lupa) → ficha com Ano,
   Mês, Nome, Empresa, Função e o card "Arquivos para Download".
3. Paginação e rodapé "Salvar como: xls | pdf | csv | txt" (visível apenas quando há
   registros), que reexecuta a rota com os mesmos filtros + `type=<formato>`.

## 5. Detalhes e Particularidades Técnicas

- Camadas: `Route` → `check.route.permission` → `Prefecture\OutsourcedController` →
  `OutsourcedRequest` → `IOutsourcedRepository`/`OutsourcedRepositoryDefaultImpl`
  → `App\Prefecture\Outsourced` → tabela `outsourceds`; a listagem do CMS usa
  `Prefecture\DatatableController@get_terceirizados` (acesso direto via `DB::table`).
- O portal usa `App\Http\Filter\FilterOutsourceds` (filtros `ilike` sobre
  `employee_name`/`company`/`role`) e renderiza a view do template ativo
  (`modelo1`..`modelo6`).
- Armadilhas reais: **exclusão em lote quebrada** (o JS envia `toDelete`, o controller lê
  `ids`); **arquivos órfãos** no storage quando o anexo é removido do registro; **JSON em
  coluna de texto** com fallback para formato antigo (caminho único); **`$outsourceds_all`
  é materializado em toda requisição do portal**, mesmo sem exportação; **layouts
  `default`/`newsbit` não possuem as views** do módulo (a página pública quebra com 404
  se o layout ativo for um desses).
- Dependências: `laracasts/flash` (mensagens), `spatie/laravel-activitylog` (auditoria),
  `maatwebsite/excel` (XLSX/CSV), `barryvdh/laravel-dompdf` (PDF), `yajra/laravel-datatables`
  (listagem do CMS), `cartalyst/sentinel` (permissões).

## 6. Estrutura de Dados

| Objeto | Descrição |
|---|---|
| `outsourceds` | Tabela principal: `id`, `year`, `month`, `employee_name`, `company`, `role`, `file_path` (text, JSON de anexos), `is_show` (bool, default `true`), `created_at`, `updated_at`, `deleted_at`. |
| `resources` | Linha `id 14 / slug terceirizados / name Terceirizados` — base das permissões `terceirizados.*`. |
| `menu_orders` | Linha `id 34 / slug terceirizados` — entrada do menu do CMS e contador de cliques. |
| `configurations` | Chave `layout` — define qual template do portal renderiza a página. |
| Sem FK | A tabela não possui chaves estrangeiras; `file_path` é desnormalizado (JSON de caminhos). |
| Sem views SQL | O módulo não usa `CREATE VIEW`. |
| Sem seeds de dados | Não há seed com registros de terceirizados; a tabela está vazia no banco v5 (0 linhas). |

## 7. Como Testar

- **CMS:** login no `/cms/terceirizados`; cadastrar um registro com anexo PDF; conferir a
  listagem, o download do anexo, a exclusão do anexo, a edição (preservando o anexo
  anterior) e a auditoria em `activity_log`.
- **Portal:** abrir `/terceirizados`; filtrar por ano, mês, função e texto; verificar a
  paginação; abrir a ficha; baixar o anexo; exportar em xls/pdf/csv/txt.
- **Regressão:** confirmar que a v6 responde 404 em `/terceirizados` e que a tabela
  `outsourceds` não existe no banco v6 (ver `08-GAPS-E-DIVIDAS.md`).

## 8. Migração para Laravel Moderno

Reconstruir como um recurso único `App\Models\Outsourced` com `casts` (`year`/`month`
inteiros, `is_show` booleano) e um **accessor de arquivos** que faça o `json_decode`
do `file_path` (hoje repetido em 3 lugares: controller CMS, form Blade e show do portal).
Trocar `App\Http\Filter\FilterOutsourceds` por `FormRequest` + query builder tipado;
substituir `flash()` por redirect com sessão de flash; migrar `has_access`/`check.route.permission`
para Policies do Laravel 11; transformar o JSON de anexos em tabela pivot `outsourced_attachments`
(ou, no mínimo, um value object); e expor `GET /api/outsourceds` com
`OutsourcedResource` para o portal. Ver `06-MIGRACAO-MODERNA.md`.

## 9. Resumo Geral

Terceirizados é um módulo de transparência folha-terceirizada que existe **somente na v5**.
Uma linha da tabela `outsourceds` representa um empregado terceirizado em uma competência
(ano + mês), com empresa, função e anexos PDF. O operador mantém os dados no CMS
(`/cms/terceirizados`, AdminLTE, permissões `terceirizados.*`, auditoria de atividade,
exclusão em lote) e o cidadão consulta e exporta no portal (`/terceirizados`, templates
`modelo1`..`modelo6`, filtros `ano`/`mes`/`funcao`/`busca`, exportações xls/pdf/csv/txt).
O acoplamento técnico é fraco (nenhuma FK, nenhum service compartilhado), mas o
`file_path` guarda um JSON de caminhos dentro de uma coluna `text` —mudança feita na
migration `2026_05_28_120000` porque a coluna original era `varchar(255)`— e é
re-decodificado em três lugares diferentes. Há defeitos conhecidos: exclusão em lote envia
`toDelete` mas o controller lê `ids`; remover um anexo deixa o arquivo órfão no storage;
o portal materializa a collection completa em toda requisição; e os layouts `default` e
`newsbit` não têm as views do módulo. A v6 **não possui** o módulo: faltam tabela,
model, controllers, request, export, views, rotas e até a linha `terceirizados` em
`resources`/`menu_orders`. A reconstrução na v7 deve tratar o módulo como novo,
preservando os nomes de rota, os nomes de permissão e o formato de exportação.

---

## Índice do dossiê

| Arquivo | Conteúdo |
|---|---|
| `00-RESUMO.md` | Este documento |
| `01-ARQUITETURA.md` | Camadas, classes e o diagrama de fluxo |
| `02-MODELAGEM.md` | Estrutura de dados detalhada |
| `03-ROTAS-E-CONTROLADORES.md` | Rotas, controllers, requests, resources |
| `04-VIEWS-E-UX.md` | Telas, componentes, JS, assets |
| `05-REGRAS-E-PORTABILIDADE.md` | Particularidades e armadilhas |
| `06-MIGRACAO-MODERNA.md` | Plano de reconstrução em Laravel moderno |
| `07-TESTES.md` | Estratégia e casos de teste |
| `08-GAPS-E-DIVIDAS.md` | Pendências, riscos, comparativo v5 vs v6 |
