# A Instituição — Gaps, Dívidas e Mapa v5 → v6

> Versão analisada: `v6` (Laravel 5.5.50) com comparação `v5`.

---

## 1. Mapa v5 → v6

### 1.1 Tabela de correspondências

| Elemento | v5 | v6 | Tipo de mudança |
|---|---|---|---|
| Categoria (raiz) | `category_contents.id = 6`, `name = 'A prefeitura'`, `slug = 'a-prefeitura'` | `category_contents.id = 6`, `name = 'A Instituição'`, `slug = 'a-instituicao'` | **Renomeação** |
| View SQL | `a_prefeitura` (`value, titulo, id, galeria`) | `a_instituicao` (`value, titulo, id, galeria, slug`) | **Renomeação + coluna nova** |
| Migration da view | `v5/database/migrations/2023_04_10_145149_create_view_to_a_prefeitura.php` | `v6/database/migrations/2024_10_16_145149_create_view_to_a_instituicao.php` | Recriada |
| Coluna `slug` na view | `v5/database/migrations/2026_03_09_000000_update_views_to_include_slug.php` | Incluída já em `v6/database/migrations/2024_11_29_100640_update_view_a_instituicao.php` | Adiantada na v6 |
| Rota do portal | `GET /a-prefeitura/{slug}` — nome `a-prefeitura.index` | `GET /a-instituicao/{slug}` — nome `a-instituicao.index` | Renomeação |
| Rota de secretarias | `/a-prefeitura/secretarias/{slug}` — nome `a-prefeitura-secretarias.index` | `/a-instituicao/secretarias/{slug}` — nome **`a-instituicao.index`** (duplicado) | **Renomeação quebrada** |
| Rota de órgãos | `/a-prefeitura/orgaos/{slug}` — nome `a-prefeitura-orgaos.index` | `/a-instituicao/orgaos/{slug}` — nome `a-instituicao-orgaos.index` | Renomeação OK |
| Diretório de views do portal | `portal/layouts/modeloN/informacoes-prefeitura/a-prefeitura/` | `portal/layouts/modeloN/informacoes-instituicao/a-instituicao/` | Renomeação |
| Componente de view | `.../informacoes-prefeitura/components/components-a-prefeitura/` | `.../informacoes-instituicao/components/components-a-prefeitura/` | Diretório pai renomeado; **nome do componente mantido** |
| View legada do layout `default` | `portal/layouts/default/pages/contents/a-prefeitura/show.blade.php` | `portal/layouts/default/pages/contents/a-prefeitura/show.blade.php` (com o **nome antigo**) | Órfã em ambas |
| Controller do portal | `Portal\PrefectureController@getIndex` | idem | Sem mudança de papel |
| Controller do CMS | `Prefecture\ContentController@genericIndex` (`case 'a-prefeitura'`) | `Prefecture\ContentController@genericIndex` (`case 'a-instituicao'`) | Renomeação do `case` |
| Form Request | `case 'a-prefeitura'` | `case 'a-instituicao'` | Renomeação; **regras idênticas** |
| Grade do CMS | `DatatableController@get_a_prefeitura` → `DB::table('a_prefeitura')->get()` | `DatatableController@get_a_instituicao` → `DB::table('a_instituicao')->get()` | Renomeação |
| JS da grade | `public/assets/js/custom/contents/aprefeitura/datatable.js` | `public/assets/js/custom/contents/a-instituicao/datatable.js` | Renomeação do diretório |
| Item de menu do CMS | `MenuServiceProvider` — "A Prefeitura" | `MenuServiceProvider` — "A Instituição" (ícone `institution` mantido) | Texto |
| `menu_orders` | `slug = 'a-prefeitura'` | `slug = 'a-instituicao'` (`modulo_id = 12`) | Renomeação por seed |
| `resources` | `slug = 'a-prefeitura'`, `name = 'A Prefeitura'` | `slug = 'a-instituicao'`, `name = 'A Instituição'` (`modulo_id = 12`) | Renomeação por seed |
| Permissões (papel `administrador`) | `a-prefeitura.index/.create/.edit/.delete/.inactive/.inactivate/.restore/.show` | `a-instituicao.*` | Renomeação |
| Busca do portal | array fixo com `a-prefeitura` | array fixo com `a-instituicao` | Renomeação |
| Agrupamento da busca | `v5/app/Helpers/functions.php:443-447` → "A Prefeitura" | `v6/app/Helpers/functions.php:472-473` → `a-instituicao` | Renomeação (id 6 **hardcoded**) |

### 1.2 Onde a renomeação foi feita

```php
// v6/database/seeds/CategoryContentTableSeeder.php:41-53
// ID 6
$category = CategoryContent::where('slug', 'a-prefeitura')->whereNull('category_contents_id')->first();

if ($category) {
    $category->update([
        'name' => 'A Instituição',
        'slug' => 'a-instituicao',
    ]);
} else {
    CategoryContent::updateOrCreate(['slug' => 'a-instituicao'], [
        'name' => 'A Instituição',
        'status_create' => 1
    ]);
}
```

```php
// v6/database/seeds/AjustesMigracao.php:25-39
$menu = MenuOrder::where('slug', 'o-instituto')->first();
if ($menu) {
    $menu->update(['slug' => 'a-instituicao']);
}

$category = CategoryContent::where('slug', 'o-instituto')->whereNull('category_contents_id')->first();

if ($category) {
    $category->update([
        'name' => 'A Instituição',
        'slug' => 'a-instituicao',
    ]);
}
```

```php
// v6/database/seeds/MenuOrderSeeder.php:76-87
$menu = MenuOrder::where('slug', 'a-prefeitura')->first();

if ($menu) {
    $menu->update(['slug' => 'a-instituicao']);
} else {
    MenuOrder::updateOrCreate(
        ['slug' => 'a-instituicao'],
        ['clicks' => 0, 'modulo_id' => 12]
    );
}
```

```php
// v6/database/seeds/ResourcesTableSeeder.php:89-105
$resource = Resource::where('slug', 'a-prefeitura')->first();

if ($resource) {
    $resource->update([
        'slug' => 'a-instituicao',
        'name' => 'A Instituição',
        'modulo_id' => 12
    ]);
} else {
    Resource::updateOrCreate(['slug' => 'a-instituicao'], [
        'name' => 'A Instituição',
        'description' => 'Funcionalidade A Instituição',
        'modulo_id' => 12,
        'created_at' => date("Y-m-d H:i:s")
    ]);
}
```

**Consequência crítica:** a renomeação é feita por **seed**, não por migration.
Uma base que rode as migrations da v6 sem rodar os seeds fica com a categoria
`a-prefeitura` e a view `a_instituicao` apontando para uma categoria inexistente
→ a grade do CMS volta **vazia** e a rota `/cms/conteudos/a-instituicao` não
encontra a view de listagem.

### 1.3 Itens que existem na v5 e foram removidos na v6

| Item | v5 | v6 | Consequência |
|---|---|---|---|
| Nome de rota próprio para secretarias | `a-prefeitura-secretarias.index` | não existe (duplicado) | `RouteNotFoundException` em `/portal/secretarias/{slug}`. |
| Proteção `if (data.slug)` no JS da grade | `v5/public/assets/js/custom/contents/aprefeitura/datatable.js:13-16` | removido | Com `slug` NULL, o botão vira `/a-instituicao/undefined`. |
| Validação de `slug` na edição | idem | idem | — |
| `A Prefeitura` como rótulo em todo o portal | presente | substituído por "A Instituição" | Links internos e textos antigos podem permanecer (ex.: `v6/resources/views/portal/layouts/default/pages/contents/a-prefeitura/show.blade.php` e o diretório `components-a-prefeitura`). |

### 1.4 Itens que existem na v6 e não existiam na v5

| Item | Observação |
|---|---|
| Tabela `modulos` (56 módulos) com `status` | Introduz a **segunda chave** de exibição do menu do CMS. `a-instituicao` ficou `true`; `a-cidade`, `orgaos` e `secretarias` ficaram `false`. |
| `contents.secretaria` e `contents.orgaos_id` | Ganchos de vínculo notícia↔secretaria/órgão, ambos 100% NULL na base atual. |
| Coluna `slug` na view | Necessária para o botão "Acessar Instituto" apontar para a página real. |
| Busca do portal com `a-instituicao` no array fixo de slugs | Habilita a busca do conteúdo institucional. |

## 2. Bugs e erros conhecidos

### BUG-1 — Formulário do CMS aponta para o módulo A Cidade (severidade: alta)

**Evidência:**

```blade
{{-- v6/resources/views/prefecture/contents/a-instituicao/form.blade.php:21 --}}
<div class="mr-2">{{ url('a-cidade') }}/</div>

{{-- v6/resources/views/prefecture/contents/a-instituicao/form.blade.php:30 --}}
<a href="{{ route('a-cidade.index', $content->slug) }}" target="_blank">
    Acessar <i class="fa fa-external-link fa-1x"></i>
</a>
```

**Efeito:** na tela de edição de A Instituição, o campo "Link Permanente" mostra o
prefixo `https://<dominio>/a-cidade/` e o link "Acessar" abre a página de A
Cidade (com o slug do conteúdo institucional, que provavelmente nem existe lá).
O operador copia um endereço errado para documentos, e-mails e Publications.

**Causa:** copy/paste de `v6/resources/views/prefecture/contents/a-cidade/form.blade.php:21,30`
— os dois arquivos têm exatamente os mesmos trechos.

**Correção:** trocar por `{{ url('a-instituicao') }}/` e
`{{ route('a-instituicao.index', $content->slug) }}` — ou, na v7, usar um
componente Blade único parametrizado pelo slug do módulo.

### BUG-2 — Nome de rota duplicado (severidade: alta)

**Evidência:**

```php
// v6/routes/web.php:78-83
Route::prefix('a-instituicao')->group(function () {
    Route::any('/{slug}', 'Portal\PrefectureController@getIndex')->name('a-instituicao.index');
    Route::any('secretarias/{slug}', 'Portal\PrefectureController@getIndex')->name('a-instituicao.index');
```

**Efeito:** o nome `a-instituicao.index` resolve para a **segunda** rota
(`/a-instituicao/secretarias/{slug}`). Consequências:

- `route('a-instituicao.index', 'sobre')` → `/a-instituicao/secretarias/sobre`;
- o breadcrumb do conteúdo institucional aponta para essa URL
  (`v6/routes/breadcrumbs.php:203`);
- o redirecionamento de `/portal/a-instituicao/sobre` cai em
  `/a-instituicao/secretarias/sobre`
  (`v6/app/Http/Controllers/Portal/PortalController.php:331`).

Na v5 os nomes eram distintos (`a-prefeitura.index` e
`a-prefeitura-secretarias.index`), o que prova que a v6 introduziu a regressão.

**Correção:** renomear a segunda rota para `a-instituicao-secretarias.index`.

### BUG-3 — Referência a nome de rota inexistente (severidade: alta)

**Evidência:**

```php
// v6/app/Http/Controllers/Portal/PortalController.php:332
case 'secretarias': return redirect(route('a-instituicao-secretarias.index', $slug2));
```

O nome `a-instituicao-secretarias.index` **não existe** em nenhum arquivo de
rotas da v6 (verificado por busca em `v6/`). O acesso a
`/portal/secretarias/{slug}` resulta em `RouteNotFoundException` (HTTP 500).

**Correção:** criar a rota com esse nome (e-mail/github) ou corrigir a
referência.

### BUG-4 — `modulos.status` inconsistente oculta entradas do menu do CMS (severidade: média)

**Evidência:**

```php
// v6/app/Providers/MenuServiceProvider.php:46
if (has_access("$menu->slug.index") && isset($menu->modulo->status) && $menu->modulo->status) {
```

Estado atual (consulta de leitura):

| `modulos.id` | nome | slug | `status` |
|---|---|---|---|
| 12 | A Instituição | a-instituicao | **true** |
| 13 | A Cidade | a-cidade | false |
| 17 | Órgãos | orgaos | false |
| 18 | Secretarias | secretarias | false |

**Efeito:** as entradas "A Cidade", "Órgãos" e "Secretarias" **não aparecem** no
menu lateral do CMS, embora as rotas existam e as permissões estejam concedidas
(verificado: o papel `administrador` tem todas as permissões `*.index/.create/
.edit/.delete/.show/.inactive/.inactivate/.restore` desses quatro módulos). O
operador só chega às telas digitando a URL.

**Correção:** decisão de negócio — ligar os módulos que devem ser operáveis, ou
remover a segunda chave de exibição.

### BUG-5 — Coluna `slug` ausente na primeira migration da view (severidade: média, já corrigido)

`v6/database/migrations/2024_10_16_145149_create_view_to_a_instituicao.php` cria
a view **sem** `slug`; o botão "Acessar Instituto" só funciona depois de
`v6/database/migrations/2024_11_29_100640_update_view_a_instituicao.php`.
Bancos instalados entre as duas versões tiveram a grade sem `slug` — o botão
apontava para `/a-instituicao/undefined`.

### BUG-6 — Coluna `contents.secretaria` nunca preenchida (severidade: média)

`contents.secretaria` está 100% NULL em toda a base. A relação
`Content::secretarias()` liga pelo **slug** e, portanto, nunca resolve. O
efeito é que a página institucional **nunca** exibe as "Últimas notícias"
(`news_secretary` fica vazio) e o slot `noticias` do `show.blade.php` não é
renderizado.

**Efeito colateral positivo:** o `paginate(6)` nunca é exercitado nesse caminho —
mas o custo da query continua sendo pago.

### BUG-7 — `modulo2` inclui o componente do `modelo1` (severidade: baixa)

o arquivo `orgaosNews.blade.php` dentro de `v6/resources/views/portal/layouts/modelo2/layout/sidebar/`
**não existe**; o componente do modelo2 inclui o do modelo1:

```blade
{{-- v6/resources/views/portal/layouts/modelo2/informacoes-instituicao/components/components-a-prefeitura/index.blade.php:97 --}}
@include('portal.layouts.modelo1.layout.sidebar.orgaosNews')
```

Além disso, `ModulosServiceProvider` registra `view()->composer` para
`portal.layouts.modelo2.layout.sidebar.orgaosNews` — um caminho que nunca é
renderizado.

### BUG-8 — Slug gerado pode colidir com um id futuro (severidade: baixa)

O sufixo de colisão é `slug-<maxid+1>`, calculado no momento da criação. Se,
logo depois, outro conteúdo for inserido com o mesmo `maxid+1`, o slug colide de
novo. Não há índice único em `contents.slug`.

### BUG-9 — Breadcrumb com link morto (severidade: baixa)

`v6/routes/breadcrumbs.php:202` empurra "A Instituição" com `url('/#')`. O
primeiro nível do breadcrumb não navega para a listagem do módulo.

### BUG-10 — `row_number()` como identificador (severidade: baixa)

A coluna `value` da view `a_instituicao` é recalculada a cada consulta: se um
conteúdo for excluído, todos os números seguintes mudam. Não serve de chave,
apenas de ordenação visual.

## 3. Funcionalidades incompletas ou mortas

| Item | Situação |
|---|---|
| API de dados abertos | **Inexistente** para A Instituição. `v6/routes/api.php` expõe apenas `v1/secretarias`, `v1/orgaos`, `v1/legislacoes`, `v1/noticias` e `v1/publicacoes`. |
| Layouts `default` e `newsbit` | **Inalcançáveis**: `getTemplateActived()` exige `portal.layouts.<layout>.home.index`, que só existe em `modelo1..modelo6`. As views de A Instituição nesses layouts (`v6/resources/views/portal/layouts/default/pages/contents/a-prefeitura/show.blade.php`, `v6/resources/views/portal/layouts/newsbit/informacoes-instituicao/components/components-a-prefeitura/index.blade.php`) são órfãs. |
| Nome do diretório de views | `components-a-prefeitura` e `a-prefeitura` (no layout `default`) sobrevivem à renomeação — confuso para quem lê o código. |
| Agenda de eventos na página institucional | `$eventsList` é consultado no controller mas **não** é repassado à view no ramo da categoria 6: a query é feita e descartada. |
| Contador `menu_orders.clicks` | Incrementado na abertura da tela do CMS (`menuClicks()`), o que mistura navegação administrativa com uso do portal. |
| Bloco "Notícias de &lt;título&gt;" | Só aparece quando o conteúdo tem `sidenews` — isto é, quando a categoria é `orgaos`. Para a categoria institucional, o bloco é renderizado mas fica vazio. |
| Exportação CSV | Não existe (só Excel e PDF, gerados no cliente). |
| Galeria de PDFs | `archiveGallery()` não filtra `view_pdf`; o filtro por extensão está na view e descarta PDFs silenciosamente. |
| Log de auditoria | `generateLog()` registra "Listado por" mas não há trilha por conteúdo específico. |

## 4. Riscos de dados

| # | Risco | Evidência | Mitigação na v7 |
|---|---|---|---|
| D-1 | **Seed, não migration**: a coerência entre `category_contents.slug`, `contents.category_contents_id` e a view `a_instituicao` depende de o seed ser executado. | `v6/database/seeds/CategoryContentTableSeeder.php:41-53`; `v6/database/seeds/AjustesMigracao.php:25-39` | Migration idempotente que faz o `UPDATE` de slug. |
| D-2 | **View órfã `a_prefeitura`**: bases migradas da v5 para a v6 mantêm a view antiga, que passa a consultar `cc.slug = 'a-prefeitura'` (sem resultados). | `v5/database/migrations/2023_04_10_145149_create_view_to_a_prefeitura.php` | `DROP VIEW IF EXISTS a_prefeitura` na migration de troca. |
| D-3 | **Coluna `slug` duplicada semanticamente**: o mesmo slug aparece em `contents.slug`, `category_contents.slug`, `menu_orders.slug` e `resources.slug`. Qualquer um deles pode divergir. | 4 tabelas | Constraint de unicidade por tabela + seeder de reconciliação. |
| D-4 | **Conteúdos órfãos** se `category_contents_id` apontar para uma categoria inexistente. | FK `contents_category_contents_id_foreign` protege a integridade referencial, mas o `soft delete` da categoria **não** remove o conteúdo. | Política de cascata explícita. |
| D-5 | **Slugs de conteúdo podem conflitar** ao criar o índice único na v7 (hoje não há unicidade). | `v6/app/Http/Controllers/Prefecture/ContentController.php:676-679` | Backfill aplicando o sufixo legado antes de criar o índice. |
| D-6 | **Chaves de vínculo por texto** (`contents.secretaria` = slug) quebram silenciosamente quando o slug da secretaria muda. | `v6/app/Http/Model/Prefecture/Content.php:72-75` | Converter para FK real. |
| D-7 | **Links de menu** (`menu_items.contents_id`) podem apontar para conteúdos em soft delete — a view SQL já os esconde, mas o link continua no menu. | `v6/app/Http/Controllers/Prefecture/ContentController.php:1320-1367` (a limpeza só ocorre na exclusão via CMS) | Limpeza no job de soft delete ou verificação na renderização do menu. |
| D-8 | **Views SQL dentro de migrations** — reverter a migration recria a view antiga sem `slug` (a `down()` de `2024_11_29_100640` é exatamente isso). | `v6/database/migrations/2024_11_29_100640_update_view_a_instituicao.php:35-46` | `down()` coerente com o estado alvo. |

## 5. Riscos de performance (resumo — detalhes em `05-REGRAS-E-PORTABILIDADE.md` §3)

| # | Risco | Gravidade para este módulo |
|---|---|---|
| P-1 | Grade do CMS carrega a view SQL inteira em memória e pagina no cliente | Alta |
| C-2 | View sem índice próprio, com `row_number()` reordenando a cada consulta | Média |
| C-3 | `updateClicks()` faz SELECT + UPDATE por visita | Média |
| C-5 | Galeria completa carregada em memória só para renderizar | Baixa |
| C-6 | `getTemplateActived()` consulta `configurations` a cada requisição | Baixa |
| R-P6 | `$eventsList` consultado e descartado no ramo institucional | Baixa |

## 6. Perguntas em aberto ao negócio

1. **Escopo do módulo:** "A Instituição" agora hospeda também as secretarias e os
   órgãos (mesmo controller, mesmas rotas com prefixo `a-instituicao/`). A
   nomenclatura confunde o público? Convém uma URL própria para secretarias
   (`/secretarias/{slug}`) mantendo redirecionamento do endereço antigo?
2. **Conteúdo institucional × secretaria:** o bloco de notícias é controlado por
   `contents.secretaria`, hoje 100% NULL. O negócio quer que a página
   institucional liste notícias de **todas** as secretarias? Nesse caso, o
   vínculo atual (por slug) está errado e precisa ser redesenhado.
3. **Escopo do texto:** o campo `text` é HTML livre e é renderizado sem escape
   (`{!! !!}`). A prefeitura quer rich text completo (com vídeo e tabelas
   embutidas) ou texto sanitizado com um conjunto definido de tags?
4. **Galeria:** o limite é 20 MB por imagem, sem limite de quantidade nem de
   dimensional. Existe um limite de itens para a galeria institucional?
5. **Agendamento:** o conteúdo institucional tem `publication_date` e `status`,
   mas a página pública **não** os filtra. Deve uma página institucional poder
   ser agendada para publicação futura?
6. **Exposição em dados abertos:** faz sentido expor o conteúdo institucional em
   `v1/instituicoes`? Quais campos entram no JSON público (o `text` completo? a
   galeria?).
7. **Status dos módulos:** `a-cidade`, `orgaos` e `secretarias` estão com
   `modulos.status = false`. É desativação intencional (módulos em construção) ou
   resíduo de configuração? Se for intencional, a entrada de menu do CMS para
   esses módulos deve continuar oculta em produção?
8. **Multi-município:** o `id = 6` da categoria e os ids 7/12/32 usados pelo
   `MenuItemPresenter` são globais? Há previsão de multi-prefeitura (várias
   bases com ids diferentes)?
9. **Contador de cliques:** é métrica relevante? Se sim, aceita-se a imprecisão
   do `SELECT`+`UPDATE` ou deve-se usar evento de analytics (o módulo já tem
   integração de GA em `v6/tests/Feature/GaConfigurationTest.php`)?
10. **Migração do texto:** houve conteúdo escrito no editor Summernote com
    imagens uploadadas em `storage/summernote-files/content/a-prefeitura/…`?
    O caminho de storage muda com o slug da categoria — precisa de migração de
    arquivos junto com a de dados.

## 7. Itens de verificação pendente

| Item | Situação |
|---|---|
| Papel `administrador` — conjunto completo de permissões `a-instituicao.*` | Verificado em base (JSON de `roles.permissions`). |
| `modulos.status` dos quatro módulos | Verificado em base. |
| Conteúdos da categoria 6 | Verificado em base (5 conteúdos). |
| `contents.secretaria` e `contents.orgaos_id` 100% NULL | Verificado em base. |
| Existência da view `a_instituicao` com 5 colunas | Verificado em base. |
| Efeito do nome de rota duplicado em runtime | Verificado por análise das rotas registradas (`v6/routes/web.php:79-80`); a resolução do nome pelo Laravel ocorre em favor da última rota registrada. |
| Existência de testes automatizados para o módulo | Verificado: **não existem** em `v6/tests/`. |
| Quantidade de arquivos do diretório `components-a-prefeitura` | 7 (modelo1..modelo6 + newsbit). |
| Existência de o arquivo `orgaosNews.blade.php` dentro de `v6/resources/views/portal/layouts/modelo2/layout/sidebar/` | Verificado: **não existe** (origem do BUG-7). |
