# Servidor — Resumo Executivo

> **Versão analisada:** `v6` (módulo idêntico em `v5`, sem alteração funcional)
> **Slug do módulo:** `servidor` (tabela `modulos`, id 35) — `menu_orders.slug = 'servidor'` (id 29, `modulo_id = 35`), `resources.slug = 'servidor'` (id 47, `modulo_id = 35`)
> **Natureza:** CMS (painel administrativo). O dado é consumido pelo portal apenas de forma indireta, pela busca de requerimentos de diárias.
> **Estado:** OK v6 (copiado da v5 sem alterações)

---

## 1. Objetivo

O módulo cadastra os **servidores públicos** (funcionários e prestadores de serviço) da
prefeitura: CPF, nome, cargo, matrícula, identidade, secretaria de lotação e dados
bancários. O cadastro é a base do módulo **Requerimento de Diárias** — todo requerimento
escolhe um servidor da lista e o banco garante a integridade por FK
(`requerimento_diarias.id_servidor → servidor.id`).

Despite the slug `servidor`, **não se trata de configuração de servidor/host**: é o
cadastro de pessoas (servidores públicos).

## 2. Efeito / Impacto

| Faixa | Efeito |
|---|---|
| Cidadão | Nenhuma tela própria. O portal expõe os servidores apenas de forma indireta, na busca pública de requerimentos de diárias (`/requerimento-de-diarias`), onde o nome e o CPF são filtros. |
| Prefeitura (CMS) | Menu "Servidores" (`/cms/servidor`, ícone `users`) com listagem em DataTable, cadastro, edição, exclusão individual e exclusão em lote. |
| Transparência e legalidade | Dados pessoais sensíveis de servidores (CPF, identidade, matrícula, banco, conta e agência) ficam em um cadastro do CMS, sem registro de auditoria próprio. |
| Operação técnica | A tabela é `servidor` (singular) e a listagem do CMS lê a view `servidores` (plural): dois objetos diferentes com nomes quase idênticos. |

## 3. Comportamento

- **Campos obrigatórios** (`ServidorRequest`): `name`, `cpf`, `identidade`, `matricula`,
  `cargo`. O CPF passa pela regra `Validate_cpf` (dígitos verificadores).
- **Campos opcionais:** `secretaria` (seleção de um conteúdo da categoria `secretarias`),
  `banco`, `conta`, `agencia`.
- **`secretaria` não é uma tabela de secretarias**: é uma FK para `contents(id)`. O select
  do formulário lista os conteúdos da categoria cujo `slug` é `secretarias`.
- **Valores vazios viram `NULL`**: `ServidorRepository::structureUpInsert` usa
  `filter_request()`, que troca qualquer valor falsy por `null` — inclusive a string
  `"0"` enviada pelo select quando nenhuma secretaria é escolhida.
- **Exclusão é lógica** (`SoftDeletes`): a linha permanece em `servidor` com
  `deleted_at` preenchido; a view `servidores` já a oculta.
- **Exclusão em lote** por `POST /cms/servidor/destroyAll`, com os ids no campo `toDelete`.
- **Contador de acessos**: o construtor do controller chama `menuClicks('servidor')`,
  incrementando `menu_orders.clicks` a cada visita a qualquer tela do módulo.
- **Permissões**: o recurso `servidor` existe em `resources` (id 47), de modo que a tela
  de Perfis gera as chaves `servidor.index`, `servidor.create`, `servidor.edit`,
  `servidor.delete`, etc.

## 4. Como o Usuário Interage

**Operador (CMS)**

| Tela | Rota | Ação |
|---|---|---|
| Lista | `/cms/servidor` | DataTable (`tabela_servidor`) com colunas ID, Servidor, CPF e Ações; dados carregados por AJAX em `/cms/datatable/servidor`. |
| Cadastro | `/cms/servidor/create` | Formulário com os campos "Dados cadastrais" (Nome, CPF, Identidade, Matrícula, Cargo, Secretaria) e "Dados Bancários" (Banco, Conta, Agência). |
| Edição | `/cms/servidor/{id}/edit` | **Reutiliza a view de cadastro** (`create.blade.php`) — não existe `edit.blade.php`. |
| Exclusão individual | `DELETE /cms/servidor/{id}` | Botão de lixeira na linha da DataTable; resposta JSON. |
| Exclusão em lote | `POST /cms/servidor/destroyAll` | Botão "Excluir Registro(s)" com seleção múltipla. |

O CPF recebe máscara `cpf` no navegador e a identidade recebe máscara `00.000.000-0`
aplicada por jQuery no `form.blade.php`.

**Cidadão (portal)**

- `/requerimento-de-diarias`: a busca pública aceita `req_name` e `cpf`, que são
  aplicados sobre a tabela `servidor` para filtrar os requerimentos exibidos.

## 5. Detalhes e Particularidades Técnicas

- Camadas: rota (`Route::resource`) → `Prefecture\ServidorController` →
  `App\Http\Requests\ServidorRequest` → `App\Repositories\Eloquent\Prefecture\Servidor\ServidorRepository`
  (implementa `App\Repositories\Contracts\IServidorRepository`) → model
  `App\Http\Model\Prefecture\Servidor` → tabela `servidor`.
- O model declara `protected $table = 'servidor'` (singular), enquanto a migration se
  chama `create_servidors_table.php` e cria a tabela `servidor`.
- A listagem do CMS **não** usa o model: `DatatableController@get_servidor` consulta
  `DB::table('servidores')` — a **view** — que expõe apenas `value`, `name`, `cpf`, `id`.
- A relação `secretarias()` do model (`hasOne(Content::class, 'id', 'secretaria')`) é
  declarada com semântica de `belongsTo` e **não é utilizada** em nenhuma view.
- A interface `IServidorRepository` é **vazia**: não declara nenhum método.
- O `Repository` base (`v6/app/Repositories/Repository.php`) usa `dd($th)` em qualquer
  exceção de `upInsert` — o `catch (Exception)` do controller nunca é alcançado.
- Acoplamento forte com **Requerimento de Diárias** (FK `id_servidor`) e com a busca
  pública do portal, que faz `whereRaw` com concatenação de strings.

## 6. Estrutura de Dados

| Objeto | Tipo | Observação |
|---|---|---|
| `servidor` | tabela | id, cpf, name, cargo, matricula, identidade (todos `NOT NULL`), secretaria (FK `contents.id`, null), banco/conta/agencia (null), deleted_at, created_at, updated_at |
| `servidores` | view SQL | `row_number()` + `name`, `cpf`, `id`; `WHERE deleted_at IS NULL` |
| `requerimento_diarias.id_servidor` | FK | → `servidor(id)`; impede a exclusão física de um servidor com requerimentos |
| `resources` | linha | id 47, `slug = 'servidor'`, `modulo_id = 35` — origina as permissões `servidor.*` |
| `menu_orders` | linha | id 29, `slug = 'servidor'`, `modulo_id = 35`, `clicks` incrementado a cada visita |
| `modulos` | linha | id 35, `slug = 'servidor'`, `status` liga/desliga o item de menu |

## 7. Como Testar

1. CMS: criar um servidor com todos os campos; confirmar a linha em `servidor` e a
   presença na listagem (que lê a view `servidores`).
2. CMS: tentar criar com CPF inválido — deve falhar com "CPF inválido, verifique e tente novamente."
3. CMS: criar sem escolher secretaria — deve gravar `secretaria = NULL` (e não `0`).
4. CMS: excluir em lote e confirmar que as linhas ficam com `deleted_at` e somem da listagem.
5. Portal: `/requerimento-de-diarias` filtrando por nome e por CPF de um servidor
   cadastrado; conferir se o requerimento aparece.
6. Integridade: tentar excluir fisicamente um servidor que tenha requerimentos — a FK
   deve bloquear.

## 8. Migração para Laravel Moderno

- Model `Servidor` com `protected $table = 'servidor'`, `$fillable` explícito, casts e
  relações `belongsTo(Secretaria::class)`.
- Separar `Secretaria` em model próprio (hoje é um `contents` qualquer).
- `FormRequest` com regras tipadas e CPF validado por value object.
- Policy `ServidorPolicy` no lugar do `check.route.permission`.
- Mover os dados bancários para uma tabela separada, com criptografia em repouso e
  acesso restrito (ou removê-los, se a operação não precisar).
- Eliminar a concatenação em `whereRaw` da busca pública (risco de SQL injection).
- Endpoints REST/JSON, API Resource e testes de feature.

## 9. Resumo Geral

O módulo `servidor` é o cadastro de servidores públicos da prefeitura: nome, CPF,
identidade, matrícula, cargo, secretaria e dados bancários. Ele vive na tabela
`servidor` (singular) e é lido no CMS através da view `servidores` (plural), o que
cria uma divergência fácil de não perceber. O cadastro é a base do Requerimento de
Diárias: a coluna `requerimento_diarias.id_servidor` referencia `servidor(id)`, e a
busca pública de diárias filtra a tabela de servidores por nome e CPF. O módulo não tem
interface própria no portal e não possui testes na v6. Os principais problemas são a
rota `servidor.show` gerada pelo `Route::resource` sem método correspondente, a rota
`servidor/destroyAll` registrada duas vezes, a exclusão em lote que sempre reporta
sucesso, o `dd()` em tratamento de erro do repository base, os dados bancários em
claro e a concatenação de strings em `whereRaw` na busca pública.

---

## Í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 e permissões |
| `04-VIEWS-E-UX.md` | Telas do CMS, JS e componentes |
| `05-REGRAS-E-PORTABILIDADE.md` | Regras não óbvias, acoplamentos 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 e comparativo v5 vs v6 |
