# Outras Configurações — Modelagem

> Módulo `outras-configuracoes` (`modulos.id = 4`). O módulo **não possui
> tabela própria**: everything é gravado em `configurations`, na categoria
> `outros` (`category_configurations.id = 4`).

---

## 1. Tabelas envolvidas

### 1.1 `configurations` — tabela real do módulo

Criada por `v6/database/migrations/2018_05_11_131302_create_configurations_table.php`.

| Coluna | Tipo | Null | Default | Observação |
|---|---|---|---|---|
| `id` | `integer` | não | — | PK (`configurations_pkey`) |
| `title` | `varchar(191)` | não | — | Título exibido na listagem |
| `description` | `text` | sim | — | Texto de apoio no formulário |
| `small_description` | `text` | sim | — | Descrição curta |
| `placeholder` | `varchar(191)` | sim | — | Placeholder do input |
| `key` | `varchar(191)` | não | — | **Sem índice único** (ver riscos) |
| `value` | `text` | sim | — | Valor; para `type = file` guarda o caminho relativo |
| `type` | `varchar(191)` | não | — | `text`, `textarea`, `file`, `checkbox` (o seed também usa `password`) |
| `order` | `integer` | sim | — | Posição dentro da categoria |
| `max_input` | `integer` | sim | — | Tamanho máximo do valor |
| `min_input` | `integer` | sim | — | Tamanho mínimo do valor |
| `category_configurations_id` | `integer` | não | — | FK → `category_configurations.id` (`configurations_category_configurations_id_foreign`) |
| `notnull` | `boolean` | sim | — | Marca a configuração como obrigatória |
| `status_create` | `boolean` | sim | — | Quando verdadeiro, a `key` fica imutável no formulário |
| `created_at` | `timestamp` | sim | — | |
| `updated_at` | `timestamp` | sim | — | |
| `deleted_at` | `timestamp` | sim | — | Soft delete (`SoftDeletes` no model) |

Índices existentes: **apenas** `configurations_pkey`. Não há índice único em
`key` nem em `(category_configurations_id)`.

### 1.2 `category_configurations` — categorias

Criada por `v6/database/migrations/2018_04_10_131301_create_category_configurations_table.php`.

| Coluna | Tipo | Null | Default |
|---|---|---|---|
| `id` | `integer` | não | — (PK) |
| `name` | `varchar(191)` | não | — |
| `slug` | `varchar(191)` | não | — (sem índice único) |
| `status_create` | `boolean` | sim | — |
| `created_at` / `updated_at` / `deleted_at` | `timestamp` | sim | — |

Categorias cadastradas (seed `v6/database/seeds/CategoryConfigurationsTableSeeder.php`) e
situação real no banco `prefeituras_v5`:

| id | slug | nome | ativas | total |
|---|---|---|---|---|
| 1 | `site` | Site | 57 | 57 |
| 2 | `prefeitura` | Prefeitura | 0 | 0 |
| 3 | `temas` | Temas | 9 | 9 |
| **4** | **`outros`** | **Outros** | **0** | **0** |
| 5 | `layout` | Layout | 0 | 0 |
| 6 | `covid` | Landing Covid | 11 | 11 |
| 7 | `barra-privacidade` | Barra de Privacidade | 4 | 4 |
| 8 | `dashboard` | Dashboard | 6 | 6 |

**A categoria deste módulo (`outros`, id 4) não possui nenhuma linha em
`configurations`** — nem no seed, nem no banco.

### 1.3 `modulos` e `resources` (ligação do módulo)

| Tabela | Colunas relevantes | Registro do módulo |
|---|---|---|
| `modulos` | `id`, `name`, `slug`, `status`, timestamps | `id = 4`, `name = 'Outras Configurações'`, `slug = 'outras-configuracoes'`, `status = 1` (migration `v6/database/migrations/2024_11_01_150206_create_modulos_table.php`) |
| `resources` | `slug`, `name`, `modulo_id` | `slug = 'outros'`, `name = 'Outras Configurações'`, `modulo_id = 4` (seed `v6/database/seeds/ResourcesTableSeeder.php`, linha 342) |

`configurations` **não** possui FK para `modulos`: a ligação módulo ↔ chave é
 indireta (categoria de configuração ↔ recurso ↔ módulo).

## 2. Relacionamentos (Eloquent)

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

```php
class Configuration extends Model
{
    use SoftDeletes;
    protected $table = 'configurations';

    protected $fillable = [
        'key',
        'value',
        'status_create',
        'type',
        'title',
        'category_configurations_id',
        'max_input',
        'min_input',
        'notnull',
        'order',
        'description',
        'placeholder',
        'small_description'
    ];

    protected $primaryKey = 'id';

    public function categoryConfiguration()
    {
        return $this->hasOne(CategoryConfiguration::class, 'id', 'category_configurations_id');
    }
}
```

Observações de modelagem relevantes:

| Item | Realidade |
|---|---|
| Namespace | Classe `App\Prefecture\Configuration`, mas o arquivo está em `v6/app/Http/Model/Prefecture/Configuration.php` (autoload por classmap) |
| Relação | `hasOne` (não `belongsTo`) para `CategoryConfiguration` — funcional, mas semanticamente incorreto |
| Casts | Nenhum cast declarado: `notnull`, `status_create`, `max_input`, `min_input`, `order` são lidos como `int`/`string` cru |
| Guard | `SoftDeletes` — `destroy()` marca `deleted_at`, não apaga a linha |
| `$fillable` | Inclui `status_create`, que **não** é gravado por `store()` nem por `update()` |

Consulta usada pelo módulo (`v6/app/Http/Traits/GetInformationsPrefectureTrait.php`):

```php
return Configuration::whereHas('categoryConfiguration', function ($query) use ($reference) {
    $query->where('slug', '=', $reference);
})->orderBy('order')->get();
```

## 3. View SQL

`v6/database/migrations/2023_04_17_144615_create_view_to_configuracoes.php`:

```sql
CREATE OR REPLACE view configuracoes
AS SELECT row_number() OVER (ORDER BY (c.status_create IS TRUE) DESC, c.created_at  ASC) AS value,
    c.title AS titulo,
    cc.slug as categoria,
    c.id,
    c.type as tipo
FROM configurations c
    join category_configurations cc on c.category_configurations_id = cc.id
WHERE c.deleted_at IS NULL
ORDER BY (c.created_at) DESC;
```

| Campo da view | Origem | Uso na UI |
|---|---|---|
| `value` | `row_number()` | Coluna "ID" (numeração sequencial, não o `id` real) |
| `titulo` | `configurations.title` | Coluna "Título" |
| `categoria` | `category_configurations.slug` | Coluna "Categoria" |
| `id` | `configurations.id` | Usado nas URLs de edição/exclusão |
| `tipo` | `configurations.type` | Coluna "Tipo" |

**A view não é filtrável por categoria**: expõe todas as configurações de todas
as categorias. É exatamente ela que faz a tela do módulo mostrar as 87
configurações do sistema, e não apenas as da categoria `outros`.

## 4. Chaves reais do módulo

### 4.1 Categoria `outros` (id 4) — conjunto vazio

Não existe nenhuma chave cadastrada. A tabela abaixo é a **fonte de verdade do
que o módulo pretende agrupar** e onde ela realmente está:

| Chave real | Categoria real | Título | Tipo | notnull | max_input | min_input | order | Valor no banco |
|---|---|---|---|---|---|---|---|---|
| `recaptcha_public_key` | `site` (1) | Chave pública do Recaptcha | `text` | false | 150 | 0 | 32 | (vazio) |
| `recaptcha_private_key` | `site` (1) | Chave privada do Recaptcha | `text` | false | 150 | 0 | 33 | (vazio) |
| `app_env` | `site` (1) | Modo desenvolvimento | `checkbox` | false | — | — | 36 | `prod` |
| `app_debug` | `site` (1) | Ativar debugbar | `checkbox` | false | — | — | 37 | `0` |
| `mail_driver` | `site` (1) | Driver | `text` | false | 50 | 0 | 38 | `smtp` |
| `mail_host` | `site` (1) | Host | `text` | false | 50 | 0 | 39 | `mail.maximatecnologia.com.br` |
| `mail_port` | `site` (1) | Porta | `text` | false | 50 | 0 | 40 | `465` |
| `mail_username` | `site` (1) | Usuário | `text` | false | 80 | 0 | 41 | (endereço do servidor SMTP) |
| `mail_password` | `site` (1) | Senha | `password` | false | 80 | 0 | 42 | (segredo — **não reproduzido aqui**) |
| `mail_encryption` | `site` (1) | Criptografia | `text` | false | 80 | 0 | 43 | `ssl` |
| `mail_from_address` | `site` (1) | Endereço | `text` | false | 80 | 0 | 44 | (endereço de remetente) |
| `mail_from_name` | `site` (1) | Nome do E-mail | `text` | false | 80 | 0 | 45 | `Email Contato` |
| `gemini_api_key` | `site` (1) | Chave da API do Gemini | `text` | false | 255 | 0 | 71 | (vazio) |
| `portal_transparencia_ativo` | `site` (1) | Ativar Busca por Transparência Fiscal (Portal Externo) | `checkbox` | false | — | — | 80 | `0` |
| `portal_transparencia_codigo_ente` | `site` (1) | Código do Ente/Município | `text` | false | 50 | 0 | 81 | (vazio) |
| `portal_transparencia_url_portal` | `site` (1) | URL do Portal Público | `text` | false | 255 | 0 | 82 | (URL do portal externo) |

### 4.2 Categoria `dashboard` (id 8) — também fora de `outros`

| Chave real | Título | Tipo | notnull | max_input | min_input | order | Valor no banco |
|---|---|---|---|---|---|---|---|
| `dashboard_enhanced_enabled` | (dashboard aprimorado) | `checkbox` | false | 1 | 0 | 0 | `0` |
| `google_analytics_property_id` | (GA) | `text` | false | 50 | 0 | 1 | (vazio) |
| `google_analytics_measurement_id` | (GA) | `text` | false | 50 | 0 | 2 | (vazio) |
| `google_analytics_sa_json` | (GA) | `textarea` | false | 10000 | 0 | 3 | (vazio) |
| `dashboard_widget_refresh_minutes` | (dashboard) | `text` | false | 4 | 1 | 4 | (vazio) |
| `dashboard_widgets_enabled` | (dashboard) | `text` | false | 10000 | 0 | 5 | (vazio) |

> Fonte: consulta direta ao banco `prefeituras_v5` (somente leitura) e
> `v6/app/Http/Controllers/Prefecture/ConfigurationController.php:444-451`
> (lista de chaves de dashboard carregada em `editByCategory('configuracoes')`).

**Conclusão de modelagem**: o rótulo "Outras Configurações" não corresponde a
nenhum conjunto real de chaves. As chaves técnicas (servidor, e-mail, API keys,
Gemini) estão na categoria `site`; as chaves de analytics/dashboard, em
`dashboard`. A categoria `outros` é um **slot vazio**, disponível para uso futuro.

## 5. Seeds

| Seed | Caminho | Efeito |
|---|---|---|
| `CategoryConfigurationsTableSeeder` | `v6/database/seeds/CategoryConfigurationsTableSeeder.php` | Cria as 8 categorias por `slug` (`updateOrCreate`), incluindo `outros` (id 4) |
| `ConfigurationsTableSeeder` | `v6/database/seeds/ConfigurationsTableSeeder.php` | Popula **apenas** `site` (47), `temas` (9), `covid` (11) e `barra-privacidade` (4). **Não popula `outros`** |
| `ResourcesTableSeeder` | `v6/database/seeds/ResourcesTableSeeder.php` | Cria o recurso `outros` com `modulo_id = 4` |
| `DefaultSettingSeeder` | `v6/database/seeds/DefaultSettingSeeder.php` | Defaults de ambiente (chaves `mail_*`, `app_*`) |
| `AddPortalTransparenciaConfigurationsSeeder` | `v6/database/seeds/AddPortalTransparenciaConfigurationsSeeder.php` | Chaves `portal_transparencia_*` (categoria `site`) |
| `AddToConfigurationsGeminiApiKeyValue` | `v6/database/seeds/AddToConfigurationsGeminiApiKeyValue.php` | Chave `gemini_api_key` (categoria `site`) |

Distribuição por categoria no seed `ConfigurationsTableSeeder` (contagem real
das linhas com `category_configurations_id`):

| Categoria | Linhas no seed |
|---|---|
| `site` | 47 |
| `temas` | 9 |
| `covid` | 11 |
| `barra-privacidade` | 4 |
| `outros` | **0** |
| `prefeitura`, `layout`, `dashboard` | 0 (populadas por outros seeds) |

## 6. Migrations do módulo

| Data | Migration | Efeito |
|---|---|---|
| 2018-04-10 | `v6/database/migrations/2018_04_10_131301_create_category_configurations_table.php` | Cria `category_configurations` |
| 2018-05-11 | `v6/database/migrations/2018_05_11_131302_create_configurations_table.php` | Cria `configurations` |
| 2023-04-17 | `v6/database/migrations/2023_04_17_144615_create_view_to_configuracoes.php` | Cria a view SQL `configuracoes` |
| 2024-11-01 | `v6/database/migrations/2024_11_01_150206_create_modulos_table.php` | Cria `modulos` e insere o módulo `outras-configuracoes` (id 4, status 1) |
| 2024-11-05 | `v6/database/migrations/2024_11_05_104026_add_modulo_id_to_resources_table.php` | Liga `resources` ao módulo (`outros` → `modulo_id = 4`) |
