# e-Vacina — Modelagem de Dados

> Estrutura de dados do módulo. Fonte: schema real do banco
> `prefeituras_v5`.

---

## 1. `evacinas`

Criada por `v6/database/migrations/2022_09_06_091951_create_evacinas_table.php`.

| Coluna | Tipo | Null | Default | Observação |
|---|---|---|---|---|
| `id` | integer | não | `nextval('evacinas_id_seq')` | PK |
| `url` | varchar(191) | **não** | — | URL da planilha do Google Sheets |
| `path_imagem` | varchar(191) | sim | — | caminho do banner (`evacina/images/{id}/banner.png`) |
| `primeira_dose` | integer | sim | — | contador |
| `segunda_dose` | integer | sim | — | contador |
| `primeira_dose_reforco` | integer | sim | — | contador |
| `segunda_dose_reforco` | integer | sim | — | contador |
| `populacao` | double precision | **não** | — | população total do município |
| `status` | boolean | sim | — | ativo/desativado |
| `deleted_at` | timestamp(0) | sim | — | `SoftDeletes` |
| `created_at` / `updated_at` | timestamp(0) | sim | — | |

Observações:

- Os 4 contadores são `integer` (população grande caberia em `bigint`).
- `populacao` é `double precision` (o formulário usa `<input type="number">`).
- `status` é nullable e, na prática, recebe **`null`** (ver seção 4).
- A tabela não tem índice; a consulta do módulo é sempre
  `latest()->first()` (última linha).

## 2. O model

`v6/app/Http/Model/Prefecture/Evacina.php`:

```php
namespace App\Http\Model\Prefecture;

use Illuminate\Database\Eloquent\Model;
use illuminate\Database\Eloquent\SoftDeletes;   // <- "illuminate" minúsculo

class Evacina extends Model
{
    use SoftDeletes;

    protected $table = 'evacinas';

    protected $fillable = [
        'primeira_dose', 'segunda_dose', 'primeira_dose_reforco',
        'segunda_dose_reforco', 'path_imagem', 'populacao', 'status', 'url'
    ];
}
```

Pontos:

- O import `use illuminate\Database\Eloquent\SoftDeletes;` está com
  **"illuminate" minúsculo**. Nomes de classe em PHP são
  case-insensitive, então funciona — mas é um **erro de estilo** e
  quebraria ferramentas estáticas.
- `$fillable` **inclui** os 4 contadores e o `path_imagem`, mas o
  `EvacinaRepository::structureUpInsert` **não** os filtra — logo, na
  prática, só `url`, `populacao` e `status` são gravados pelo
  formulário. Os contadores são gravados pelo comando artisan; o
  `path_imagem` é gravado pelo próprio repository após o `upInsert`.
- Não há `$casts`. No Laravel 11, `status` deve ter
  `casts => ['status' => 'boolean']` e `populacao => 'float'`.

## 3. `modulos` e `resources`

| Item | Valor |
|---|---|
| `modulos` | id **5**, name "e-Vacina", slug `e-vacina`, `status = **false**` |
| `resources` | id **46**, slug `e-vacina`, name "eVacina", `modulo_id = 5` |
| `menu_orders` | **NENHUMA LINHA** para `e-vacina` |

Este é um dos **poucos** módulos com recurso registrado: como
`resources.modulo_id = 5` e o módulo tem `status = false`, a linha do
recurso **não aparece** na matriz de permissões
(`v6/resources/views/admin/role/form.blade.php:43`). Mas **ao ligar o
módulo** em `/cms/modulos`, a linha aparece e as permissões
`e-vacina.index`, `e-vacina.create`, `e-vacina.edit`, `e-vacina.delete`
passam a ser concedíveis.

O módulo `e-vacina` está na lista de **presets** do formulário de
módulos (`v6/resources/views/admin/modulos/form.blade.php:74-104`,
bloco `padrao`).

## 4. O que realmente é gravado

| Valor enviado | Valor gravado | Motivo |
|---|---|---|
| `status_evacina` presente (checkbox marcado) | `status = true` | `isset()` é verdadeiro |
| `status_evacina` ausente | `status = **null**` (não `false`) | `filter_request` converte valores falsos em `null` (`v6/app/Http/Traits/AppTrait.php:523-534`) |
| `url` | `url` | — |
| `populacao` | `populacao` | `0` viraria `null` (coluna NOT NULL -> erro) |
| `imagem_evacina` (arquivo) | `path_imagem = 'evacina/images/{id}/banner.png'` | gravado pelo `EvacinaRepository` |

> O comando `reload:evacina` usa `if ($last_evacina && $last_evacina->status)`
> para decidir se atualiza os contadores. Com `status = null`, o PHP
> trata como falso — a semântica pretendida é mantida, mas o estado
> fica ambíguo no banco (três valores: `true`, `null`, e nunca `false`).

## 5. Views SQL

**Nenhuma.** O módulo usa Eloquent diretamente.

## 6. Relacionamentos

Nenhum. `evacinas` é uma tabela isolada.

## 7. Seeds

**Nenhum** seed popula `evacinas`. A migration cria a tabela vazia; os
dados são inseridos pela interface do CMS.

## 8. Migrations

| Migration | Efeito |
|---|---|
| `v6/database/migrations/2022_09_06_091951_create_evacinas_table.php` | cria `evacinas` |

Nenhuma outra migration toca a tabela — não há índice, nem mudança de
tipo, nem seed de dados.

---

## 9. Observações de modelagem para a v7

1. **`status` deve ser `NOT NULL DEFAULT false`** e ter cast `boolean`.
   Elimina a ambiguidade `true`/`null`.
2. **Os contadores devem ser `unsignedBigInteger`** (populações
   grandes) ou `decimal` (percentuais).
3. **Falta um índice**: a consulta `latest()->first()` faz full scan +
   sort. Um índice em `(id DESC)` ou em `created_at` resolve.
4. **Falta uma chave de negócio**: várias linhas podem existir, mas só
   a última é usada. Ou o modelo passa a ter **uma** linha (chave
   singleton), ou ganha `is_current`.
5. **`path_imagem` guardando caminho relativo** e não URL: o mesmo
   problema dos outros módulos (quebrar se o symlink mudar).
6. **A fonte de dados é externa e efêmera**: o link da planilha pode
   mudar de nome, o `gid` pode mudar, e a coluna 28 do CSV pode mudar de
   posição. Nada disso é versionado no sistema.
7. Se o módulo for **descontinuado** (a pandemia acabou), a decisão é:
   remover a tabela, ou mantê-la congelada como registro histórico?
