# Carta de Serviços — Views SQL, Migrations e Seeds

> Complemento de `02-MODELAGEM.md` (tabelas e relacionamentos).

## 7. Views SQL

Criadas em `v6/database/migrations/2026_01_20_221003_create_service_letters_views.php` (confirmadas em `pg_views`).

### 7.1 `listagem_servicos_carta_de_servicos`

```sql
CREATE OR REPLACE VIEW listagem_servicos_carta_de_servicos AS
SELECT
    row_number() OVER (ORDER BY sl.created_at DESC) AS value,
    sl.nome,
    c.title AS secretaria,
    (select
        ARRAY_TO_STRING(array_agg(sls.nome), ', ')
     from service_letter_subjects sls
     join service_letters_subjects_rel slsr on slsr.subject_id = sls.id and slsr.service_letter_id = sl.id
     group by slsr.service_letter_id) AS assuntos,
    sl.menu_acesso_rapido as menu,
    case when sl.ativo then 'ATIVO' else 'INATIVO' end as status,
    case when sl.destaque then 'SIM' else 'NÃO' end as destaque,
    sl.created_at AS data_criacao,
    sl.updated_at AS data_atualizacao,
    sl.id
FROM service_letters sl
LEFT JOIN contents c ON c.id = sl.secretaria_id
ORDER BY sl.ordem ASC, sl.nome ASC
```

Consumida por `v6/app/Http/Controllers/Prefecture/DatatableController.php:492-496` (`get_service_letter_services_list`).

> **Risco verificado:** a view **não filtra `sl.deleted_at IS NULL`**. Serviços excluídos (soft delete) continuam aparecendo na listagem do CMS.

### 7.2 `listagem_assuntos_carta_de_servicos`

```sql
CREATE OR REPLACE VIEW listagem_assuntos_carta_de_servicos AS
SELECT
    row_number() OVER (ORDER BY sls.created_at DESC) AS value,
    sls.tipo_visual as visual,
    sls.nome,
    sls.ordem,
    sls.ativo as status,
    sls.created_at AS data_criacao,
    sls.updated_at AS data_atualizacao,
    sls.id
FROM service_letter_subjects sls
ORDER BY sls.ordem ASC, sls.nome ASC
```

Consumida por `v6/app/Http/Controllers/Prefecture/DatatableController.php:498-502`.

> Mesmo risco: **não filtra `deleted_at`**.

### 7.3 `down()` inconsistente

O `down()` da migration derruba `service_letters_view` e `service_letters_subjects_view` — nomes que **não são os criados** (`listagem_servicos_carta_de_servicos` / `listagem_assuntos_carta_de_servicos`). Rollback não remove as views reais. Ver `08-GAPS-E-DIVIDAS.md`.

## 9. Migrations do módulo (13 arquivos, todas em 2026)

| Ordem | Arquivo | Efeito |
|---|---|---|
| 1 | `2026_01_20_160648_create_service_letter_configs_table.php` | cria `service_letter_configs` |
| 2 | `2026_01_20_160714_create_service_letter_subjects_table.php` | cria `service_letter_subjects` |
| 3 | `2026_01_20_160719_create_service_letters_table.php` | cria `service_letters` **+ insere o módulo em `modulos`/`menu_orders`/`resources`** |
| 4 | `2026_01_20_160720_service_letters_subjects_rel.php` | cria o pivot |
| 5 | `2026_01_20_160723_create_service_letter_channels_table.php` | cria canais |
| 6 | `2026_01_20_160726_create_service_letter_steps_table.php` | cria etapas |
| 7 | `2026_01_20_160729_create_service_letter_documents_table.php` | cria documentos |
| 8 | `2026_01_20_160732_create_service_letter_attachments_table.php` | cria anexos |
| 9 | `2026_01_20_160736_create_service_letter_pdfs_table.php` | cria PDFs |
| 10 | `2026_01_20_160739_create_service_letter_access_logs_table.php` | cria logs de acesso |
| 11 | `2026_01_20_221003_create_service_letters_views.php` | cria as 2 views SQL |
| 12 | `2026_01_20_231045_create_service_letters_responsaveis_table.php` | cria responsáveis |
| 13 | `2026_03_30_000000_add_soft_deletes_to_service_letters_tables.php` | adiciona `deleted_at` a 8 tabelas |

### 9.1 Registro do módulo (dentro da migration 3)

```php
$moduloId = DB::table('modulos')->insertGetId([
    'name' => 'Carta de Serviços',
    'slug' => 'carta-de-servicos',
    'status' => true,
    'created_at' => now(),
    'updated_at' => now(),
]);

DB::table('menu_orders')->updateOrInsert([
    'slug' => 'carta-de-servicos',
    'modulo_id' => $moduloId,
], ['clicks' => 0]);

DB::table('resources')->updateOrInsert([
    'slug' => 'carta-de-servicos',
    'modulo_id' => $moduloId,
], [
    'name' => 'Carta de Serviços',
    'description' => 'Formulários de Cartas de Serviço',
]);
```

Estado no banco `prefeituras_v5`:

| Tabela | id | slug | vínculo |
|---|---|---|---|
| `modulos` | 57 | `carta-de-servicos` | — |
| `resources` | 4 | `carta-de-servicos` | `modulo_id = 57` |
| `menu_orders` | 3 | `carta-de-servicos` | `modulo_id = 57` |

> **Correção ao enunciado do assignment:** o slug **existe** na tabela `modulos` (id 57) — foi criado pela própria migration do módulo, não por `2024_11_01_150206_create_modulos_table.php`.

## 10. Seeds

`v6/database/seeds/ServiceLetterSeeder.php` — único seed do módulo. Comportamento:

1. Localiza a primeira `Content` da categoria `secretarias`; se não houver, aborta com aviso.
2. `ServiceLetterSubject::firstOrCreate(['slug' => 'assunto-exemplo'], [...])`.
3. `ServiceLetter::firstOrCreate(['slug' => 'servico-exemplo'], [...])`.
4. `subjects()->syncWithoutDetaching([$assunto->id])`.
5. `ServiceLetterResponsavel::firstOrCreate(['email' => 'joao.silva@example.com', 'service_letter_id' => ...], [...])`.
6. `ServiceLetterConfig::firstOrCreate(['nome' => 'Configuração Padrão'], [...])`.

> **Dois desvios verificados no seed:**
> 1. `'menu_acesso_rapido' => true` — o campo espera `cidadao|empresa|servidor` (string). Um booleano `true` gravado em `varchar(20)` produz a string `"1"`/ `"t"` no Postgres e **viola a validação** do controller (que exige `in:cidadao,empresa,servidor`) em qualquer edição posterior.
> 2. `'ordenacao_servicos' => 'ordem'` — valor fora do domínio (`secretaria`|`assunto`); o controller PDF trata qualquer valor ≠ `secretaria` como "ordenar por nome".
>
> O seed também **não** popula canais, etapas, documentos nem anexos — o módulo nasce sem exemplares desses quatro recursos.
