# Terceirizados — Gaps, Dívidas e Comparativo v5 → v6

> Módulo **existente apenas na `v5`**. A v6 **não possui** o módulo Terceirizados.

---

## 1. Bugs e erros conhecidos (v5)

| # | Severidade | Descrição | Evidência | Efeito para o usuário |
|---|---|---|---|---|
| B1 | **Alta** | **Exclusão em lote não funciona.** O JS envia `toDelete[]`, mas o controller lê `$request->ids`. | `v5/app/Http/Controllers/Prefecture/OutsourcedController.php:157` vs `v5/public/assets/js/custom/utilities/deleteAll.js` | Ao selecionar registros e clicar em "Excluir Registro(s)", o CMS exibe "Registro(s) deletados com sucesso!" mas **nada é excluído** (o repository devolve `false` e o JS só troca a mensagem se `response.success` for truthy). Todos os outros módulos do CMS leem `toDelete`. |
| B2 | **Alta** | **404 na página pública quando o layout ativo não é `modelo1`..`modelo6`.** | `Admin\Controller::getTemplateActived()` monta `portal.layouts.<layout>.terceirizados.index`; só os 6 modelos têm o diretório | Configurar `configurations.key = layout` como `default`, `newsbit`, `barra-privacidade`, `busca-darias`, `calendar-events`, `contratos`, `diario-oficial`, `dynamics-forms` ou `surveys` deixa `/terceirizados` inacessível. |
| B3 | Média | **Arquivos órfãos no storage.** `destroyArquivo` remove o item do JSON, mas não apaga o arquivo do disco. | `v5/app/Http/Controllers/Prefecture/OutsourcedController.php` (`destroyArquivo`) | Crescimento indefinido de `storage/app/public/terceirizados/`. |
| B4 | Média | **Arquivos órfãos no *soft delete*.** O registro é marcado como deletado, mas os anexos permanecem. | Model com `SoftDeletes` | Idem B3. |
| B5 | Média | **`Repository::upInsert` faz `dd($th)` em caso de exceção** — derruba a aplicação com dump HTML em vez de mensagem de erro. | `v5/app/Repositories/Repository.php` (`upInsert`) | Erro de banco vira página de exceção em vez do flash "Erro ao cadastrar terceirizado". |
| B6 | Média | **Coluna completa materializada em toda requisição do portal.** `$outsourceds_all = $query->...->get()` roda mesmo sem exportação, e a query é reexecutada para `paginate(15)`. | `v5/app/Http/Controllers/Portal/OutsourcedController.php` (`index`) | 2 queries + memória O(total) por visita. |
| B7 | Média | **Curingas `%`/`_` não escapados na busca.** O usuário pode forçar varredura total (`busca=%`). | `v5/app/Http/Filter/FilterOutsourceds.php` | Performance e resultado inesperado (não é injeção de SQL: o valor é parametrizado). |
| B8 | Baixa | **`busca=0` não filtra.** `->when($request->busca, ...)` testa truthiness e `"0"` é falsy em PHP. | idem | Busca pela string "0" é ignorada. |
| B9 | Baixa | **Remoção de anexo por índice posicional.** Depois de remover um item, os demais são renumerados; a página não é recarregada, e um segundo clique pode atingir outro arquivo. | `route('terceirizados.destroy-arquivo', [$outsourced->id, $arqIndex])` | Remoção do anexo errado. |
| B10 | Baixa | **`Content-Type: plain/txt` inválido** no download TXT. | `exportTxt()` | Tipo de MIME não registrado. |
| B11 | Baixa | **CSV exportado a partir de uma view HTML.** O Maatwebsite converte o HTML, e o CSV resultante contém marcação residual e quebras de layout. | `v5/app/Exports/OutsourcedsExport.php` (`FromView`) | Arquivo difícil de consumir por planilha. |
| B12 | Baixa | **`<tbody>` dentro do `@foreach`** na tabela do portal — HTML inválido. | `v5/resources/views/portal/components/terceirizados/table.blade.php` | Renderização inconsistente em navegadores/validadores. |
| B13 | Baixa | **`is_show` não é editável** em lugar nenhum, mas é filtrado no portal. | `store`/`update` forçam `true` | Flag sem função prática; risco de registros "ocultos" órfãos. |
| B14 | Baixa | **Limite de tamanho de anexo inexistente.** Só o `php.ini` limita o upload. | `v5/app/Http/Requests/OutsourcedRequest.php` | Erro 500 em vez de mensagem de validação. |
| B15 | Informativo | **A rota `terceirizados.show` do CMS não mostra nada** — redireciona para o portal e pode terminar em 404 se o registro estiver oculto ou deletado. | `show($id)` | Operador clica em "Visualizar" e recebe 404. |

## 2. Funcionalidades incompletas ou mortas

| Item | Situação |
|---|---|
| Controle de `is_show` | **Inexistente** no CMS. A coluna existe e filtra o portal, mas nada a altera. |
| Troca/ordenação de anexos | **Inexistente**. Só há "adicionar" e "remover". |
| Edição da descrição de um anexo já enviado | **Inexistente**. |
| Reordenar/alterar o período (ano/mês) em lote | **Inexistente**. |
| Exportação do CMS pelo backend | **Inexistente** — a barra do DataTable exporta no cliente (HTML→CSV/Excel), o que não respeita o `mimes` nem o volume. O PDF do CMS também é client-side. |
| Filtro do portal por nome do empregado **ou** função na busca livre | **Não implementado** — a busca livre cobre apenas `employee_name` e `company`. |
| Filtro por intervalo de meses/anos | **Não implementado**. |
| `OutsourcedRequest::authorize()` | **Inoperante** (retorna `true`). |
| Documentação do módulo na v5 | Existe apenas `v5/.agents/docs/20260526-Guia-Modulo-Terceirizados.md`, um guia informal; não há dossiê técnico nem testes. |

## 3. Riscos de dados (migração de schema v5 → v7)

| Risco | Detalhe | Ação recomendada |
|---|---|---|
| Coluna `text` com JSON | `file_path` mistura dados de negócio com caminho de storage. | Migrar para `outsourced_attachments` + view de compatibilidade. |
| Truncamento histórico | Registros criados enquanto a coluna era `varchar(255)` podem ter JSON truncado, sendo interpretados como "caminho único" (arquivo invisível). |Auditar `length(file_path) = 255` e tentar reconstruir o JSON. |
| Ausência de índices | Só existe a PK; os filtros do portal fazem varredura sequencial. | Criar índices compostos (`is_show`, `year`, `month`) e GIN trigram. |
| `varchar(191)` | Limite real do banco é 191, enquanto o `FormRequest` valida `max:255`. | Alinhar a validação em 191 ou ampliar a coluna. |
| Arquivos no storage | `storage/app/public/terceirizados/` precisa ser migrado junto com a base. | Inventariar e reimportar para `outsourced_attachments`. |
| Ausência de dados | A tabela está **vazia** no banco v5 (0 linhas) — a migração de dados é trivial, mas o *storage* pode conter arquivos órfãos de registros já removidos. | Inventariar o diretório antes do go-live. |
| Dependência do symlink | `public/storage` não está versionado; sem `artisan storage:link` os downloads dão 404. | Incluir no checklist de deploy. |

## 4. Perguntas em aberto para o negócio

1. O módulo é **obrigatório** na v7? A v6 não o possui — foi uma remoção deliberada (recorte de escopo) ou um descuido? Sem resposta, o módulo entra como "novo".
2. Quem **aprova** a publicação de uma folha terceirizada e por quanto tempo ela permanece pública? Hoje a publicação é imediata e não há campo de vigência.
3. A referência **ano + mês** deve ser única por empregado? Hoje o mesmo empregado pode ser cadastrado ilimitadamente no mesmo mês.
4. É necessário um **mês de referência decrescente** ou uma competência única (ano-mês) para a folha? A mudança afetaria a chave natural.
5. Os anexos têm valor **legal** (contrato, aditivos)? Se sim, é preciso trilha de auditoria de download e integridade do arquivo.
6. Deve existir **duplicidade** (empresa × função × mês) ou o cadastro é livre?
7. O layout do portal da prefeitura usa um template fora de `modelo1`..`modelo6`? Se sim, o B2 é um problema de produção.
8. O "Terceirizados" da Transparência Fiscal (link externo criado pelo seeder) deve virar card interno na v7?
9. Qual o limite aceitável de tamanho por anexo e o número máximo de anexos por registro?
10. A exclusão definitiva (hard delete) deve ser possível? Hoje só existe *soft delete*, e os arquivos nunca são removidos.

## 5. Regressão v5 -> v6

### 5.1 Resumo

O módulo **Terceirizados foi integralmente removido da v6**. Não é uma regressão parcial
(uma view, uma rota ou um campo): **nenhuma** camada do módulo existe na v6.

| Camada | v5 | v6 | Situação |
|---|---|---|---|
| Tabela `outsourceds` | existe (0 linhas) | **não existe** | Removida |
| Migration de criação | `v5/database/migrations/2026_05_26_110000_create_outsourceds_table.php` | ausente | Removida |
| Migration do menu/resource | `v5/database/migrations/2026_05_26_113000_insert_terceirizados_into_menu_and_resources.php` | ausente | Removida |
| Migration `file_path` → `text` | `v5/database/migrations/2026_05_28_120000_change_file_path_to_text_in_outsourceds_table.php` | ausente | Removida |
| Model | `v5/app/Http/Model/Prefecture/Outsourced.php` | ausente | Removido |
| Controller CMS | `v5/app/Http/Controllers/Prefecture/OutsourcedController.php` | ausente | Removido |
| Controller do portal | `v5/app/Http/Controllers/Portal/OutsourcedController.php` | ausente | Removido |
| Form Request | `v5/app/Http/Requests/OutsourcedRequest.php` | ausente | Removido |
| Filtro | `v5/app/Http/Filter/FilterOutsourceds.php` | ausente | Removido |
| Repositório (contrato + impl.) | `v5/app/Repositories/Contracts/IOutsourcedRepository.php`, `v5/app/Repositories/Eloquent/Prefecture/Outsourced/OutsourcedRepositoryDefaultImpl.php` | ausentes | Removidos |
| Exportação | `v5/app/Exports/OutsourcedsExport.php` + `v5/resources/views/exports/outsourceds.blade.php` | ausentes | Removidos |
| Views CMS | `v5/resources/views/prefecture/outsourceds/{index,create,edit,form}.blade.php` | ausentes | Removidas |
| Views do portal | `v5/resources/views/portal/layouts/modelo1..modelo6/terceirizados/{index,show,filtro}.blade.php` + `v5/resources/views/portal/components/terceirizados/table.blade.php` | ausentes | Removidas |
| JS do CMS | `v5/public/assets/js/custom/contents/terceirizados/datatables.js` | ausente | Removido |
| Rota CMS | `v5/routes/basepro.php:395-397` (`terceirizados.*`, `terceirizados.destroyAll`, `terceirizados.destroy-arquivo`) | ausente | Removida |
| Rota do datatable | `v5/routes/basepro.php:451` (`datatable.terceirizados`) | ausente | Removida |
| Rota do portal | `v5/routes/web.php:124-125` (`portal.terceirizados.index`, `portal.terceirizados.show`) | ausente | Removida |
| Breadcrumb | `v5/routes/breadcrumbs.php:213-223` (`terceirizados`, `terceirizados-show`) | ausente | Removido |
| Menu do CMS | `v5/app/Providers/MenuServiceProvider.php:182` (`case 'terceirizados'`) | ausente | Removido |
| Resource de permissão | `resources` id 14, slug `terceirizados` | **ausente** | Removida |
| Entrada de menu | `menu_orders` id 34, slug `terceirizados` | **ausente** | Removida |
| Módulo no catálogo | — (a v5 não tem tabela `modulos`) | `modulos` tem 58 linhas e **nenhuma** com slug `terceirizados` | — |

### 5.2 Evidências no banco

| Verificação | Comando | Resultado |
|---|---|---|
| Tabela na v5 | `\d outsourceds` | existe; colunas `id, year, month, employee_name, company, role, file_path (text), is_show, created_at, updated_at, deleted_at`; **0 linhas**. |
| Tabela na v6 | `select count(*) from information_schema.tables where table_name='outsourceds';` (banco `prefeituras_v5` do container `v6_6f22e1bd-postgres-1`) | **0** — a tabela não existe. |
| Resource na v5 | `select id, slug, name from resources where slug = 'terceirizados';` | `14 | terceirizados | Terceirizados`. |
| Resource na v6 | idem | **nenhuma linha** — a resource foi removida. |
| `menu_orders` na v5 | `select id, slug, clicks from menu_orders where slug='terceirizados';` | `34 | terceirizados | 0`. |
| `menu_orders` na v6 | idem | **nenhuma linha**. |
| `modulos` na v6 | `select id, slug from modulos;` | 58 módulos; **nenhum** `terceirizados`. |

Observação sobre o schema de `resources`: a v5 usa
`id, slug, name, description, status_create, created_at, updated_at, deleted_at`
(sem `modulo_id`), enquanto a v6 usa
`id, slug, name, description, status_create, created_at, updated_at, deleted_at, modulo_id`
com FK para `modulos`
(`v6/database/migrations/2024_11_01_150206_create_modulos_table.php`).
Ao recriar o módulo na v7, a resource precisa ser criada **com `modulo_id`** apontando
para um novo registro em `modulos` — não basta o `INSERT` cru da migration da v5.

### 5.3 Efeito funcional da remoção

| Cenário | v5 | v6 |
|---|---|---|
| Operador publica a folha terceirizada | Cadastra em `/cms/terceirizados` | **Não existe** a tela; a rota `/cms/terceirizados` responde 404. |
| Cidadão consulta a folha | `/terceirizados` lista, filtra e exporta | **404**. |
| Card da Transparência Fiscal | Link para `/terceirizados` (criado pelo seeder) | O seeder `v6/database/seeds/AddTerceirizadosInTransparenciaFiscalSeeder.php` **continua existindo** e ainda cria o link — apontando para uma rota inexistente → **link quebrado**. |
| Auditoria | `activity_log` com `log_name = outsourced` | Sem registros desse tipo. |
| Permissão | `terceirizados.index|create|edit|delete` | Recurso inexistente → `has_access` sempre falso para esses slugs. |

### 5.4 Itens que sobraram na v6 (vestígios)

| Vestígio | Caminho | Observação |
|---|---|---|
| Seeder da Transparência Fiscal | `v6/database/seeds/AddTerceirizadosInTransparenciaFiscalSeeder.php` | Cópia do seeder da v5; cria `static_pages`, `contents` e `links` com slug `terceirizados` e um link para `https://www.<cidade>.pb.gov.br/terceirizados`. Referencia rota que não existe na v6. |
| Diretório de views do portal | `v6/resources/views/portal/layouts/` | Contém `modelo1`..`modelo6`, `default`, `newsbit`, `barra-prividade`, `busca-darias`, `calendar-events`, `contratos`, `diario-oficial`, `dynamics-forms` e `surveys` — **nenhum** com `terceirizados/`. |
| Ausência de rotas | `v6/routes/web.php`, `v6/routes/basepro.php`, `v6/routes/breadcrumbs.php` | Nenhuma menção a `terceirizados`/`Outsourced`. |

### 5.5 Plano de reintrodução na v7

1. Criar a migration de `outsourceds` (schema em `06-MIGRACAO-MODERNA.md` §2) **e** a
   tabela de anexos.
2. Criar a entrada em `modulos` (slug `terceirizados`) e a `resources` com `modulo_id`.
3. Criar a `menu_orders` (slug `terceirizados`) e o item no menu do CMS.
4. Recriar rotas com os **mesmos nomes** (`terceirizados.*`, `portal.terceirizados.*`,
   `datatable.terceirizados`) para preservar links e permissões.
5. Migrar os arquivos de `storage/app/public/terceirizados/` para
   `outsourced_attachments`.
6. Recriar as 6 variantes de view do portal ou — preferível — **uma** view única
   consumida por todos os layouts (evita o bug B2).
7. Recriar os 4 formatos de exportação com os mesmos nomes de arquivo.
8. Registrar o breadcrumb `terceirizados` / `terceirizados-show`.
9. Corrigir o bulk delete (ler `toDelete`) **antes** de expor o botão na v7.
10. Corrigir o seeder remanescente na v6 ou removê-lo para não gerar link quebrado.

---

## 6. Mapa v5 → v6 (arquivo a arquivo)

| Artefato v5 | Destino na v6 | Observação |
|---|---|---|
| `v5/app/Http/Controllers/Prefecture/OutsourcedController.php` | — | Ausente. |
| `v5/app/Http/Controllers/Portal/OutsourcedController.php` | — | Ausente. |
| `v5/app/Http/Model/Prefecture/Outsourced.php` | — | Ausente. |
| `v5/app/Http/Requests/OutsourcedRequest.php` | — | Ausente. |
| `v5/app/Http/Filter/FilterOutsourceds.php` | — | Ausente. |
| `v5/app/Repositories/Contracts/IOutsourcedRepository.php` | — | Ausente. |
| `v5/app/Repositories/Eloquent/Prefecture/Outsourced/OutsourcedRepositoryDefaultImpl.php` | — | Ausente. |
| `v5/app/Exports/OutsourcedsExport.php` | — | Ausente. |
| `v5/resources/views/exports/outsourceds.blade.php` | — | Ausente. |
| `v5/resources/views/prefecture/outsourceds/*.blade.php` (4) | — | Ausentes. |
| `v5/resources/views/portal/layouts/modelo{1..6}/terceirizados/*.blade.php` (18) | — | Ausentes. |
| `v5/resources/views/portal/components/terceirizados/table.blade.php` | — | Ausente. |
| `v5/public/assets/js/custom/contents/terceirizados/datatables.js` | — | Ausente. |
| `v5/database/migrations/2026_05_26_110000_create_outsourceds_table.php` | — | Ausente. |
| `v5/database/migrations/2026_05_26_113000_insert_terceirizados_into_menu_and_resources.php` | — | Ausente. |
| `v5/database/migrations/2026_05_28_120000_change_file_path_to_text_in_outsourceds_table.php` | — | Ausente. |
| `v5/database/seeds/AddTerceirizadosInTransparenciaFiscalSeeder.php` | `v6/database/seeds/AddTerceirizadosInTransparenciaFiscalSeeder.php` | **Mantido** — único vestígio; agora aponta para rota inexistente. |
| `v5/routes/basepro.php` (3 rotas + 1 datatable) | — | Ausentes. |
| `v5/routes/web.php` (2 rotas) | — | Ausentes. |
| `v5/routes/breadcrumbs.php` (2 registros) | — | Ausentes. |
| `v5/app/Providers/MenuServiceProvider.php` (`case 'terceirizados'`) | — | Ausente. |
| `resources` id 14 | — | Ausente. |
| `menu_orders` id 34 | — | Ausente. |
| Tabela `outsourceds` | — | Ausente. |
