# PLATAFORMA — Gaps, Dívidas e Riscos

> Este documento lista o que está **errado**, **incompleto** ou **em aberto** na
> plataforma legada e o que isso implica para a reconstrução.
> Cada item cita a evidência no repositório.

---

## 1. Bugs e erros conhecidos

### 1.1 `dd()` em produção — 6 pontos

| Arquivo | Linha | Efeito |
|---|---|---|
| `v6/app/Http/Controllers/Prefecture/ContentController.php` | 1294, 1303 | Salvar conteúdo com erro → **drena a requisição** e mostra a exceção crua |
| `v6/app/Http/Controllers/Prefecture/OmbudsmanAnswerController.php` | 230, 234 | Idem, ao responder manifestação |
| `v6/app/Http/Controllers/Prefecture/OmbudsmanRequestController.php` | 220 | Idem, ao registrar manifestação |
| `v6/app/Repositories/Repository.php` | 67 | **Crítico**: `catch (\Throwable)` no `upInsert()` de **todos** os repositórios → qualquer falha de insert/update vira `dd()` |

Em `v6/resources/views/portal/layouts/modelo{1,2,3,4,5,6}/noticias/show.blade.php:39`
há mais 6 `dd()` **comentados** (`{{-- {{ dd(...) }} --}}`) — vestígio de debug.

### 1.2 Funções globais só carregadas em `autoload-dev`

`v6/composer.json` registra `app/Helpers/functions.php` em **`autoload-dev`**:

```json
"autoload-dev": {
    "psr-4": { "Tests\\": "tests/" },
    "files": [ "app/Helpers/functions.php" ]
}
```

Em produção (`composer install --no-dev`), as 27 funções globais
(`mask()`, `getActualLayout()`, `slug_fix()`, `file_exist()`, `getSizeImage()`…)
**não existem**. `v6/resources/views/components/weather-box.blade.php:111` chama
`getActualLayout()` — em produção isso é *undefined function*.

**Gravidade: ALTA.** É um bug latente que só aparece em deploy com `--no-dev`.

### 1.3 Nome de rota duplicado `static.show`

Declarado em `v6/routes/web.php` (`GET /pagina/{slug}`) **e** em
`v6/routes/basepro.php:548` (`GET /{slug}`). Verificado no container:

```
static.show => http://localhost/x
```

O nome resolve para o **catch-all** `/{slug}` (o CMS registra por último).
Consequência: a rota `/pagina/{slug}` fica **inacessível por link gerado**.

### 1.4 Nome de rota duplicado `transparencia.index`

4 URIs em `v6/routes/web.php:184-187` com o **mesmo** nome. Verificado:

```
transparencia.index => http://localhost/portal-transparencia
```

Apenas a última é gerada por `route()`. As outras 3 funcionam por URL direta.

### 1.5 `layout` inválido **derruba o portal inteiro**

`v6/app/Http/Traits/GetInformationsPrefectureTrait.php:96-99`:

```php
if (!View::exists('portal.layouts.' . $templateActived . '.home.index')) {
    DB::table('configurations')->where('key', 'pagina_em_manutencao')->update(['value' => 1]);
    abort(404, 'view_inexistente');
}
```

Salvar `layout = 'modelo9'` no CMS coloca **o portal inteiro** em manutenção —
sem aviso, sem validação no formulário.

### 1.6 `Route::getCurrentRoute()` retorna `null`

`v6/app/Http/Middleware/CheckRoutePermission.php:59`:

```php
$access = Route::getCurrentRoute()->getName();
```

Se a rota não tem nome, `getName()` retorna `null` e
`explode('.', null)` gera erro em PHP 8.1+ (deprecated) e comportamento
indefinido em PHP 7.2 (warning). Rotas sem nome existem na v6 (ex.:
`POST password/reset`, `GET /logout`).

### 1.7 `array_where` — helper do Laravel 5.5, removido no Laravel 6+

`v6/app/Http/Middleware/CheckRoutePermission.php:75` usa `array_where()`.
O helper **existe** no Laravel 5.5
(`v6/vendor/laravel/framework/src/Illuminate/Support/helpers.php:315`), mas
**foi removido do core do Laravel a partir da versão 6** (migrado para o
pacote `laravel/helpers`, e removido do core no Laravel 11).

**Impacto na v7:** a chamada quebra em qualquer Laravel moderno. A semântica
é trivial de reimplementar (`array_filter` com `ARRAY_FILTER_USE_BOTH`), mas
é um **ponto de ruptura obrigatória** na migração do middleware.

### 1.8 `Configuracao` duplicado por nome

`v6/app/Http/Model/Prefecture/Configuration.php` declara
`namespace App\Prefecture;` e é importado como `App\Prefecture\Configuration`.
`v6/app/Http/Model/Prefecture/Layout.php` declara
`namespace App\Http\Model\Prefecture;` **no mesmo diretório**. Dois
namespaces convivem em `v6/app/Http/Model/Prefecture/`.

### 1.9 Arquivo `.orig` versionado

`v6/app/Http/Model/Admin/Person.php.orig` está no repositório junto de
`Person.php` — resíduo de conflito de merge não limpo.

### 1.10 Duas árvores de views de usuário idênticas

`v6/resources/views/admin/user_new/` e
`v6/resources/views/admin/user_new_views/` contêm o **mesmo** conteúdo
(`edit.blade.php`, `roles.blade.php` com os mesmos `data-placeholder`).
Uma das duas é lixo.

### 1.11 `AuthServiceProvider` referencia policy inexistente

`v6/app/Providers/AuthServiceProvider.php:16`:

```php
protected $policies = [
    'App\Model' => 'App\Policies\ModelPolicy',
];
```

Não existe `App\Policies\ModelPolicy` nem o diretório `v6/app/Policies/`. A
política é placeholder do skeleton.

### 1.12 `EventServiceProvider` é placeholder

`v6/app/Providers/EventServiceProvider.php:16-18`:

```php
protected $listen = [
    'App\Events\Event' => [
        'App\Listeners\EventListener',
    ],
];
```

Nenhum `App\Events\Event` nem `App\Listeners\EventListener` existe. Não há
**nenhum** listener real no projeto — toda a reatividade vem de traits
(`NewsTrait`) e do observer do `spatie`.

### 1.13 `ModulosServiceProvider` cacheia sem invalidação

`v6/app/Providers/ModulosServiceProvider.php:22-24`:

```php
$modulos = Cache::rememberForever('modulos', function () {
    return Modulos::all()->pluck('status', 'slug');
});
```

`rememberForever` = sem TTL. Alterar `modulos.status` só surte efeito após
`php artisan cache:clear`. O `ModulosController@update` (rota
`cms/modulos`) **não** invalida o cache.

### 1.14 `Exception Handler` engole a exceção

`v6/app/Exceptions/Handler.php:50-52`:

```php
if ($request->ajax()) {
    return response()->json(['success' => false, 'message' => 'Ocorreu um erro ao tentar enviar a mensagem.' . $exception]);
}
```

A exceção é **anexada à mensagem** enviada ao navegador — vaza stack trace,
caminho de arquivo e credencial de conexão do banco para o cliente.

### 1.15 `TokenMismatchException` vira redirect de logout

`v6/app/Exceptions/Handler.php` converte `TokenMismatchException` em redirect
para `logout` (documentado em `dossie_modules/_01_v5_fix/README.md`, seção 5).
Um POST sem CSRF válido **desloga** o operador em vez de mostrar erro.

### 1.16 `layouts` só tem caminhos do modelo5

Das 41 linhas da tabela `layouts`, **39 com `path` apontam para
`portal.layouts.modelo5.*`**. O editor de layout do CMS **só sabe editar o
modelo5**, mesmo com 6 modelos disponíveis. Se o município trocar
`layout` para `modelo1`, os widgets customizados (títulos, `columns`, `style`,
ordem) **não têm efeito** — os `path` não batem.

### 1.17 Slug de módulo duplicado

`modulos` tem **2 linhas** com `slug = 'logs-do-sistema'` (ids 7 e 32), uma
com `status = true` e outra com `status = false`. A linha 7 é da migration
original; a 32 é um **insert acidental** (id 32, inserido entre "Menus do
site" e "Solicitações LAI").

**Efeito verificado** (`Modulos::all()->pluck('status','slug')` no container
`v6_6f22e1bd-app-1`):

```
logs-do-sistema => false
total chaves: 57
```

O `pluck` com chave duplicada guarda apenas o **último** valor (id 32,
`status = false`). Ou seja: **o módulo "Logs do Sistema" está efetivamente
desligado no menu do CMS**, apesar de a linha id 7 estar com `status = true`.
As 58 linhas produzem só 57 chaves.

### 1.18 Slug com typo

`modulos.id = 16` tem `slug = 'diario-ofifical'` (typo de "oficial"), enquanto
`modulos.id = 37` tem `slug = 'diario-oficial'` (correto). **Dois módulos de
diário oficial** registered, um deles errado e desligado (`status = false`).

### 1.19 Correlação entre `menu_orders.slug` e `modulos.slug`

A migration `v6/database/migrations/2026_09_01_100000_fix_menu_orders_missing_modulo_id.php:27`
associa por **slug exato**. Mas os slugs divergem em vários casos:

| `menu_orders.slug` | `modulos.slug` |
|---|---|
| `enquete` | `enquetes` |
| `agenda-evento` | `agenda-de-eventos` |
| `formulario-dinamico` | `formularios-dinamicos` |
| `lai` | `solicitacoes-lai` |
| `transmissao` | `transmissoes` |
| `formulario` (o portal usa `formularios`) | `formularios-dinamicos` |

A correção foi feita caso a caso, à mão. Qualquer menu novo precisa dessa
correção manual.

### 1.20 `modalidade` e `status` de conteúdo como booleanos

`contents.status` é `boolean` (publicado/rascunho) e `contents.highlights` é
`boolean`. Mas `contents` **também** tem a coluna `order`, `carousel`, `status_create`.
A semântica de "publicado" está espalhada em 3 colunas sem restrição.

### 1.21 SQL com `concat`/`CASE`/`to_char` dentro do PHP

`v6/app/Http/Controllers/Prefecture/ContractController.php:67-74,190,196` e
`v6/app/Http/Controllers/Prefecture/ProposalController.php:68,104` montam SQL
com concatenação de string PHP **dentro** do `DB::raw`. Não há
prepared statement, não há proteção contra injeção, e o `orderByRaw` com
regex (`number ~ '/[0-9]'`) é específico de PostgreSQL.

### 1.22 `orderByRaw` com regex em `biddings.number`

O `number` da licitação é ordenado por um `CASE` que tenta extrair o primeiro
número da string. O resultado depende do **formato textual** do número, que
varia entreaxo o município. Com `orderByRaw` gone na v7, é preciso um campo
numérico derivado.

---

## 2. Funcionalidades incompletas ou mortas

| Item | Evidência | Situação |
|---|---|---|
| `v6/routes/prefecture.php` | 100% comentado | **Morto** — remover |
| `EventServiceProvider::$listen` | placeholder do skeleton | **Morto** |
| `AuthServiceProvider::$policies` | `App\Policies\ModelPolicy` inexistente | **Morto** |
| `Layout` model `$fillable` com **`'kay'`** | `v6/app/Http/Model/Prefecture/Layout.php:19` | **Bug**: o campo real é `key`. O `key` **não é mass-assignable** — por isso todo o código usa `Layout::where('key', …)->update([...])` em vez de `update()`. |
| `getActualLayout()` | usada em 1 lugar | Redundante com `getTemplateActived()` |
| `LayoutController` bloco `else` final | `str_replace('input-','')` | Update silenciosamente ignorado se o `key` não existir |
| `v6/app/Http/Model/Admin/Person.php.orig` | versionado | Lixo |
| `admin/user_new_views/` | duplicata de `admin/user_new/` | Lixo |
| `active.landing` middleware | registrado no Kernel, **nenhuma rota** o usa | **Órfão** — configurado (`configurations.active_landing = 0`) mas inaplicável |
| `phaza/laravel-postgis` | declarado e instalado | **Sem uso** — nenhuma coluna geometry |
| `jadjoubran/laravel-angular` | carregado no layout do CMS | Carregado mas o Angular **não inicializa** nenhuma aplicação (`ng-app` inexistente) — só o peso do JS |
| `barryvdh/laravel-debugbar` | em `require` (não `require-dev`) | Debugbar **ativo em produção** |
| `spatie/laravel-activitylog` `Activity::observe` | `AppServiceProvider::boot()` | Funciona, mas o `ActivityObserver` faz `preg_replace` frágil no User-Agent (7 padrões) |
| Fila de e-mail | `QUEUE_DRIVER=sync` | Envio síncrono; sem retry |
| `modulos.status` sem cache invalidation | `ModulosServiceProvider` | Ver 1.13 |
| `resources.description` `not null` | `\d resources` | Todo recurso precisa de descrição — sem default |
| Tabela `contents."order"` | aspas em tudo que a referencia | `ORDER BY "order"` — colisão com palavra reservada |

---

## 3. Itens que existem na v5 e foram removidos na v6 (e o contrário)

### 3.1 Removidos na v6 (exclusivos da v5)

19 controllers e 15 tabelas — ver `02-MODELAGEM.md` §6 e
`06-MIGRACAO-MODERNA.md` §3.1. Resumo: **Obras**, **Obras Paralisadas**,
**Concursos PSS**, **Estagiários**, **Fila de espera SUS**,
**Licitantes Sancionados**, **Terceirizados**, **Gemini (IA)**,
**Relatório Estatístico Ouvidoria**, **Categorias de Arquivo**.

**A remoção foi intencional?** Não há ADR, ticket ou changelog no repositório
que justifique. O `v6/wiki/modules/DiarioOficial/` e o
`dossie_modules/_01_v5_fix/README.md` não tocam nesse assunto.

### 3.2 Novos na v6

| Item | Evidência |
|---|---|
| **Carta de Serviços** | 12 tabelas `service_letter_*`, 8 controllers, `v6/routes/service_letter.php` (85 linhas) |
| **VIAP** | Tabela `viap`, `viap_has_archives`, `ViapPortalController`, `ViapSeeder.php` |
| **Vereadores / Mandatos** | `councilmen`, `councilmen_mandates`, `mandates`, `mandate_categories`; 4 controllers |
| **TV Câmara** | `tv_camaras`, `TvCamaraController`, `TvCamaraController` (Prefecture) |
| **Comitê de Investimentos** | `comite_de_investimentos` (view), `ComiteDeInvestimentosController` |
| **Atas e Pontos de Frequência** | `atas_e_pontos` (view), `AtasEPontosController` |
| **Pauta do Dia** | `pauta_do_dia` (view), `PautaDoDiaController` |
| **Requerimentos** | `requerimentos` (view), `RequerimentoController` |
| **Unidades de Atendimento** | `unidades_de_atendimento` (view), `ServiceUnitsController` |
| **Sessões** | `sessoes` (view), `SessionsController` |
| **Selos** | `SelosController` + rota `/selos` **fora** do grupo de manutenção |
| **Módulos (registry)** | Tabela `modulos` + `ModulosController` + `ModulosServiceProvider` |
| **Dashboard** | `dashboard_widget_instances`, 16 widgets, `DashboardWidgetController` |
| **Ombudsman sectors** | `ombudsman_sectors` + `OmbudsmanSectorController` |
| **Emendas Parlamentares** | `emendas_parlamentares` + 3 controllers (embora a view `view_emendas_parlamentares` exista desde 2023) |
| **E2E** | `v6/tests-e2e/` (Playwright + sniffCSS) — **não existe na v5** |
| **Swagger/OpenAPI** | `v6/swagger-bootstrap/`, `GenerateSwagger`, `public/swagger.yml` |

### 3.3 Divergências de infraestrutura v5 → v6

| Item | v5 | v6 |
|---|---|---|
| Pacote de breadcrumb | `davejamesmiller` (removido do GitHub) → corrigido para `diglactic` | `diglactic` |
| `v6/composer.lock` | no `.gitignore` (não versionado) | versionado? — **verificar** |
| Migrations | 349 | 358 |
| Rotas (`Route::`) | 578 | 620 |
| Controllers | 147 | 158 |
| Blades | 1.524 | 1.659 |
| Tabelas | 130 | 138 |
| Views SQL | — | 75 |
| Tabela `modulos` | **não existe** | 58 linhas |

---
