# LGPD (Barra de Privacidade) — Modelagem de Dados

> **Versão analisada:** `v6` (banco `prefeituras_v5`, PostgreSQL 10)
> **Slug do módulo:** `lgpd`

---

## 1. Visão geral das entidades

O módulo `lgpd` não tem tabela própria. Ele usa quatro tabelas compartilhadas
com os demais módulos de configuração:

| Tabela | Papel para o módulo LGPD |
|---|---|
| `configurations` | Armazena as 4 chaves da barra de privacidade. |
| `category_configurations` | Agrupa as chaves; a categoria `barra-privacidade` (id 7) é o "perfil" do módulo. |
| `modulos` | Liga o slug `lgpd` (id 3) ao recurso; o `status` controla a exibição no portal. |
| `resources` | Recurso `barra-privacidade` com `modulo_id = 3` (base das permissões do CMS). |

## 2. `configurations`

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 | — | rótulo exibido no CMS |
| `description` | `text` | sim | — | tooltip de ajuda |
| `small_description` | `text` | sim | — | texto auxiliar abaixo do campo |
| `placeholder` | `varchar(191)` | sim | — | placeholder do input |
| `key` | `varchar(191)` | não | — | **sem índice único** |
| `value` | `text` | sim | — | valor bruto (texto HTML no rich text) |
| `type` | `varchar(191)` | não | — | `richtext`, `text`, `checkbox` neste módulo |
| `order` | `integer` | sim | — | ordem de exibição no formulário |
| `max_input` | `integer` | sim | — | usado para gerar regras de validação |
| `min_input` | `integer` | sim | — | idem |
| `category_configurations_id` | `integer` | não | — | FK → `category_configurations.id` |
| `notnull` | `boolean` | sim | — | marca o campo como obrigatório |
| `status_create` | `boolean` | sim | — | usado por `ConfigurationsComposer` (morto) |
| `created_at` / `updated_at` / `deleted_at` | `timestamp` | sim | — | `deleted_at` = soft delete |

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

### 2.1 As 4 chaves reais do módulo (consulta ao banco `prefeituras_v5`)

Categoria `barra-privacidade` (id 7). Consulta usada:

```sql
select c.id, c."key", c.type, c.title, c."order", c.notnull, c.max_input,
       c.min_input, c.value, cc.slug
from configurations c
join category_configurations cc on cc.id = c.category_configurations_id
where cc.slug = 'barra-privacidade' and c.deleted_at is null
order by c."order";
```

| id | order | key | type | title | notnull | max_input | min_input | value (banco) |
|---|---|---|---|---|---|---|---|---|
| 70 | 64 | `barra-privacidade` | `richtext` | Barra de privacidade | `f` | — | — | (vazio) |
| 71 | 65 | `barra-privacidade-expiracao` | `text` | Expiração barra de privacidade | `f` | 999 | 1 | `7` |
| 72 | 66 | `barra-privacidade-exibir` | `checkbox` | Ativar barra de privacidade | `f` | — | — | `0` |
| 73 | 67 | `barra-privacidade-botao` | `text` | Cor do texto do botão | `f` | 999 | 1 | `#ffffff` |

Semântica de cada chave:

| key | Efeito | Consumidor |
|---|---|---|
| `barra-privacidade` | Texto HTML exibido na faixa (saída do Summernote, impresso com `{!! !!}`). | `barra-privacidade.blade.php:14` |
| `barra-privacidade-expiracao` | Prazo em dias de validade do cookie `acceptCookies`. Fallback 7 no JS. | `barra-privacidade.js` + `Portal\HelperController@getPrivacyBar` |
| `barra-privacidade-exibir` | Interruptor que habilita a renderização da faixa. | `barra-privacidade.blade.php:4-5` |
| `barra-privacidade-botao` | Cor de texto do botão "Ok, entendi!" (aplicada via CSS `.botao-privacidade-color`). | `barra-privacidade/head.blade.php:7` |

> Observação: `max_input`/`min_input` = 999/1 em campos de **cor hexadecimal** e
> de **dias** não fazem sentido semanticamente — herdaram do template genérico
> de configurações e não são usados (o campo `text` do colorpicker não é
> `notnull`, portanto as regras dinâmicas não aplicam `max`/`min`).

## 3. `category_configurations`

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

| Coluna | Tipo | Null |
|---|---|---|
| `id` | `integer` (PK) | não |
| `name` | `varchar(191)` | não |
| `slug` | `varchar(191)` | não |
| `status_create` | `boolean` | sim |
| `created_at` / `updated_at` / `deleted_at` | `timestamp` | sim |

Categoria `barra-privacidade`: id 7, nome "Barra de Privacidade".

## 4. `modulos`

Criada por `v6/database/migrations/2024_11_01_150206_create_modulos_table.php`.

| Coluna | Tipo | Null |
|---|---|---|
| `id` | `integer` (PK, `increments`) | não |
| `name` | `string` | não |
| `slug` | `string` | não |
| `status` | `boolean` | sim |
| `created_at` / `updated_at` | `timestamp` | sim |

Inserção do módulo (trecho real da migration):

```php
// ID 3
DB::table('modulos')->insert([
    'name' => 'LGPD',
    'slug' => 'lgpd',
    'status' => 0,
]);
```

> **Ponto central do dossiê:** os módulos `configuracoes-da-instituicao`,
> `temas-e-cores` e `outras-configuracoes` são inseridos com `status => 1`;
> o `lgpd` é inserido com **`status => 0`**. Com o módulo inativo, a barra
> **não é renderizada** no portal, ainda que `barra-privacidade-exibir` esteja
> ligado.

## 5. `resources`

| Coluna relevante | Valor para o módulo |
|---|---|
| `slug` | `barra-privacidade` |
| `name` | "Barra de Privacidade" |
| `description` | "Funcionalidade Barra de Privacidade" |
| `modulo_id` | `3` (apontando para `modulos.slug = 'lgpd'`) |

Trecho real de `v6/database/seeds/ResourcesTableSeeder.php:477`:

```php
Resource::updateOrCreate(['slug' => 'barra-privacidade'],
    [
        'name' => 'Barra de Privacidade',
        'description' => 'Funcionalidade Barra de Privacidade',
        'modulo_id' => 3,
        'created_at' => date("Y-m-d H:i:s")
    ]
);
```

A coluna `modulo_id` foi adicionada por
`v6/database/migrations/2024_11_05_104026_add_modulo_id_to_resources_table.php`.

## 6. Views SQL

| View | Migration | Conteúdo |
|---|---|---|
| `configuracoes` | `v6/database/migrations/2023_04_17_144615_create_view_to_configuracoes.php` | Lista **todas** as configurações de **todas** as categorias com `row_number()`, `titulo`, `categoria`, `id`, `tipo`. Não é filtrável por categoria. |

```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;
```

O datatable do CMS (`v6/app/Http/Controllers/Prefecture/DatatableController.php:384`,
`get_configuracoes`) lê essa view — logo, a listagem de configurações inclui as
4 chaves da LGPD misturadas com as demais, sem filtro por categoria.

## 7. Relacionamentos (Eloquent)

| Relação | Model | Cardinalidade |
|---|---|---|
| `Configuration::categoryConfiguration()` | `hasOne(CategoryConfiguration::class, 'id', 'category_configurations_id')` | 1..1 |
| `Configuration` (categoria `barra-privacidade`) | `whereHas('categoryConfiguration', fn($q) => $q->where('slug', '=', 'barra-privacidade'))` | N |
| `Modulos` (slug `lgpd`) ↔ `Resources` (slug `barra-privacidade`) | `resources.modulo_id = modulos.id` | 1..N |
| `configurations.category_configurations_id` → `category_configurations.id` | FK real no banco | N..1 |

## 8. Migrations relevantes

| Migration | Efeito |
|---|---|
| `v6/database/migrations/2018_04_10_131301_create_category_configurations_table.php` | Cria `category_configurations`. |
| `v6/database/migrations/2018_05_11_131302_create_configurations_table.php` | Cria `configurations`. |
| `v6/database/migrations/2023_04_17_144615_create_view_to_configuracoes.php` | Cria a view `configuracoes`. |
| `v6/database/migrations/2024_11_01_150206_create_modulos_table.php` | Cria `modulos` e insere `lgpd` com `status = 0`. |
| `v6/database/migrations/2024_11_05_104026_add_modulo_id_to_resources_table.php` | Liga `resources` a `modulos`. |
| `v6/database/migrations/2023_02_01_083626_add_itens_to_archives.php` | Itens de menu "Manual da LGPD" / "Cadastro da LGPD" (linhas 512 e 722). |

## 9. Seeds

| Seed | Efeito no módulo |
|---|---|
| `v6/database/seeds/CategoryConfigurationsTableSeeder.php:65` | Cria a categoria `barra-privacidade`. |
| `v6/database/seeds/ConfigurationsTableSeeder.php:1047-1105` | Cria/atualiza as 4 chaves com `order` 64..67, `status_create = 1` e valores default (texto vazio, 7, false, `#ffffff`). |
| `v6/database/seeds/DefaultSettingSeeder.php:92-97` | Aplica os valores padrão de produção: `barra-privacidade-exibir = 0`, `barra-privacidade = 'Ao continuar navegando no nosso portal, você concorda com a nossa Política de Privacidade.'`, `barra-privacidade-expiracao = 7`, `barra-privacidade-botao = '#ffffff'`. |
| `v6/database/seeds/ResourcesTableSeeder.php:477` | Recurso `barra-privacidade` com `modulo_id = 3`. |

Trecho real do seeder de configurações:

```php
$config = Configuration::updateOrCreate(['key' => 'barra-privacidade-exibir'],
    [
        'order' => 66,
        'title' => 'Ativar barra de privacidade',
        'description' => 'Ativa barra de privacidade',
        'placeholder' => 'Marque para ativar',
        'category_configurations_id' => $category['barra-privacidade'],
        'notnull' => 'false',
        'type' => 'checkbox',
        'status_create' => 1
    ]
);
    $config->value ?? $config->update(['value' => false]);
```

## 10. Ausências notáveis no modelo de dados

| Ausência | Consequência |
|---|---|
| Não há tabela de `privacy_bar_consents` | O aceite do cidadão **não é gravado** em banco; só existe o cookie `acceptCookies` no navegador. |
| Não há coluna `enabled` própria | "Ativar" é modelado como uma linha de configuração genérica (`checkbox`). |
| Não há índice único em `configurations.key` | `updateByCategory` usa `Configuration::where('key', $key)->first()`; chaves duplicadas gravariam apenas a primeira. |
| Não há FK de `configurations` para `modulos` | A amarração módulo↔chave é indireta: módulo → recurso → categoria de configuração → chave. |
