# Estagiários — Modelagem de Dados

> Versão analisada: `v5` (PostgreSQL 10 + PostGIS). O módulo não existe na `v6`.

---

## 1. Tabela `estagiarios`

Criada por `v5/database/migrations/2026_05_26_200000_create_estagiarios_table.php`.
Estrutura observada no banco `prefeituras_v5` (container `v5_3f273858-postgres-1`):

| Coluna | Tipo | Nulo | Default | Observação |
|---|---|---|---|---|
| `id` | `integer` (serial) | não | `nextval('estagiarios_id_seq')` | PK `estagiarios_pkey` |
| `nome` | `character varying(255)` | não | — | Nome do estagiário |
| `periodo_inicio` | `date` | não | — | Início do contrato |
| `periodo_fim` | `date` | não | — | Fim do contrato |
| `lotacao` | `character varying(255)` | não | — | **Título** da secretaria (texto livre, não FK) |
| `situacao` | `character varying(50)` | não | `'Ativo'` | `Ativo`, `Encerrado` ou `Rescindido` no formulário |
| `arquivo` | `text` | sim | `NULL` | JSON com a lista `{path, description}` |
| `slug` | `character varying(191)` | sim | `NULL` | `estagiarios_slug_unique` |
| `created_at` | `timestamp(0) without time zone` | sim | `NULL` | |
| `updated_at` | `timestamp(0) without time zone` | sim | `NULL` | |
| `deleted_at` | `timestamp(0) without time zone` | sim | `NULL` | Soft delete |

Índices: `estagiarios_pkey` (btree, `id`) e `estagiarios_slug_unique`
(constraint unique, `slug`). Não há **nenhuma FK** — nem para `secretarias`,
nem para `resources`, nem para `contents`.

Trecho real da migration:

```php
Schema::create('estagiarios', function (Blueprint $table) {
    $table->increments('id');
    $table->string('nome', 255);
    $table->date('periodo_inicio');
    $table->date('periodo_fim');
    $table->string('lotacao', 255);
    $table->string('situacao', 50)->default('Ativo');
    $table->text('arquivo')->nullable();
    $table->string('slug')->nullable()->unique();
    $table->timestamps();
    $table->softDeletes();
});
```

Observação: a migration é **idempotente** (`if (!Schema::hasTable('estagiarios'))`),
mas o `down()` derruba a tabela sem recriar a view.

## 2. View `estagiarios_view`

Criada por `v5/database/migrations/2026_05_26_200001_create_view_estagiarios.php`.
Definição SQL real da migration:

```sql
CREATE OR REPLACE VIEW public.estagiarios_view AS
SELECT
    row_number() OVER (ORDER BY e.created_at DESC) AS value,
    e.id,
    e.nome,
    e.periodo_inicio,
    e.periodo_fim,
    e.lotacao,
    e.situacao,
    e.arquivo
FROM estagiarios e
WHERE e.deleted_at IS NULL
ORDER BY e.nome ASC
```

Definição efetivamente instalada no banco (`pg_get_viewdef`):

```sql
SELECT row_number() OVER (ORDER BY e.created_at DESC) AS value,
       e.id, e.nome, e.periodo_inicio, e.periodo_fim,
       e.lotacao, e.situacao, e.arquivo
FROM estagiarios e
WHERE e.deleted_at IS NULL
ORDER BY e.nome;
```

| Uso | Detalhe |
|---|---|
| Leitura no CMS | `Prefecture\DatatableController@get_estagiario` (`v5/app/Http/Controllers/Prefecture/DatatableController.php:182-186`) |
| Portal | **Não usa a view** — `Portal\EstagiariosController` consulta o model `Estagiario`, que já aplica `SoftDeletes` |

Armadilha: a coluna `value` é um `row_number()` ordenado por `created_at DESC`,
mas as linhas vêm ordenadas por `nome`. O `runDatatables` injeta `value` como
**primeira coluna** (cabeçalho "ID" em
`v5/resources/views/prefecture/estagiarios/index.blade.php`), portanto o "ID"
exibido ao operador **não é** o `estagiarios.id`.

## 3. Tabelas de apoio (fora do módulo)

| Tabela/View | Papel | Registros |
|---|---|---|
| `resources` | Recurso de permissão do CMS. Registro `id = 15`, `slug = 'estagiario'`, `name = 'Estagiários'`, `description = 'Funcionalidade Estagiários'` | 5 recursos novos (ids 13–17) |
| `menu_orders` | Item de menu do AdminLTE. Registro `id = 35`, `slug = 'estagiario'`, `clicks = 0` | — |
| `permissions` | Ações reutilizáveis (`index`, `create`, `edit`, `delete`, `inactive`, `inactivate`, `restore`, `show`) | 8 linhas; **nenhuma** com slug `estagiario.*` |
| `secretarias` | **View** (não tabela) usada como lista de lotações | 13 secretarias |
| `configurations` | `layout = 'modelo1'`, `menu_interativo = '1'`, `pagina_em_manutencao = '0'` | — |

Colunas de `resources` na v5 (sem `modulo_id`, que só existe na v6):

| Coluna | Tipo |
|---|---|
| `id` | `integer` (PK) |
| `slug` | `varchar(191)` |
| `name` | `varchar(191)` |
| `description` | `varchar(191)` |
| `status_create` | `boolean` |
| `created_at` / `updated_at` / `deleted_at` | `timestamp(0)` |

## 4. View `secretarias` (origem das lotações)

Não é uma tabela: é uma view sobre `contents` + `category_contents`.

```sql
SELECT row_number() OVER (ORDER BY c.created_at) AS value,
       c.title AS titulo,
       c.id,
       c.galery AS galeria,
       c.slug
FROM contents c
JOIN category_contents cc ON cc.id = c.category_contents_id
WHERE c.deleted_at IS NULL AND cc.slug::text = 'secretarias'::text
ORDER BY c.created_at DESC;
```

O controller do CMS lê `id` e `titulo as name`:

```php
private function getSecretarias()
{
    return DB::table('secretarias')->orderBy('titulo')->get(['id', 'titulo as name']);
}
```

O portal lê só os títulos:

```php
$secretarias = DB::table('secretarias')->orderBy('titulo')->pluck('titulo');
```

Como `estagiarios.lotacao` guarda o **título** (não o `id`), renomear ou
remover o conteúdo da secretaria no CMS deixa o estagiário com uma lotação
órfã: o filtro do portal deixa de offerer a opção, mas o registro continua
exibindo o texto antigo.

## 5. Migration de menu e resource

`v5/database/migrations/2026_05_26_200002_insert_estagiario_menu_and_resource.php`:

```php
public function up()
{
    if (!DB::table('menu_orders')->where('slug', 'estagiario')->exists()) {
        DB::table('menu_orders')->insert([
            'slug'   => 'estagiario',
            'clicks' => 0,
        ]);
    }

    if (!DB::table('resources')->where('slug', 'estagiario')->exists()) {
        DB::table('resources')->insert([
            'slug'        => 'estagiario',
            'name'        => 'Estagiários',
            'description' => 'Funcionalidade Estagiários',
            'created_at'  => date('Y-m-d H:i:s'),
        ]);
    }
}

public function down()
{
    DB::table('menu_orders')->where('slug', 'estagiario')->delete();
    DB::table('resources')->where('slug', 'estagiario')->delete();
}
```

Pontos de atenção:

- Insere em `menu_orders` **sem** `created_at`/`updated_at` (colunas ficam nulas).
- Insere em `resources` **sem** `status_create` (coluna `boolean` anulável no
  banco) — o registro criado fica com `status_create = NULL`.
- O `down()` apaga as linhas pelo slug, o que pode remover registros criados
  manualmente com o mesmo slug.

O item de menu do AdminLTE é montado em
`v5/app/Providers/MenuServiceProvider.php:370-372`:

```php
case 'estagiario':
    $event->menu->add(['text' => 'Estagiários', 'url' => '/cms/estagiarios', 'icon' => 'id-card-o']);
    break;
```

O item só é exibido se `has_access("estagiario.index")` for verdadeiro (bloco
que envolve o `switch` exige `has_access("$menu->slug.index")`).

## 6. Módulos e o slug `estagiario`

| Item | Valor na v5 | Valor na v6 |
|---|---|---|
| Tabela `modulos` | **inexistente** | existe (`v6/database/migrations/2024_11_01_150206_create_modulos_table.php`) |
| `resources` | 5 registros novos (ids 13–17), sem `modulo_id` | nenhuma resource com esses slugs |
| Relação recurso ↔ módulo | não modelada | `resources.modulo_id` e `menu_orders.modulo_id` |

## 7. Seeds e factories

- **Nenhum** seeder populate `estagiarios` (`v5/database/seeds/` não contém
  nenhuma referência a `estagiario`).
- **Nenhuma** factory para `Estagiario` em `v5/database/factories/`.
- A tabela está vazia no banco de desenvolvimento: `SELECT count(*) FROM
  estagiarios` retorna `0`.

## 8. Dados pessoal (LGPD)

| Dado | Natureza | Onde aparece |
|---|---|---|
| `nome` | dado pessoal (nome completo) | listagem pública, ficha pública, CSV/TXT/PDF exportados |
| `lotacao` | dado profissional, vinculável | listagem pública e filtros |
| `arquivo` (PDFs) | pode conter documento assinado, CPF, endereço | baixável publicamente por `storage/{path}` |

Não há anonimização, mascaramento ou aviso de privacidade no módulo. Ver
`05-REGRAS-E-PORTABILIDADE.md`.
