# Suporte — Regras e Portabilidade

> Regras, acoplamentos e armadilhas do módulo de suporte.

---

## 1. Regras de negócio não óbvias

### 1.1 "Suporte" significa "fornecedor do sistema", não "chamado"

A única rota viva do módulo (`GET /cms/suporte`) mostra os dados da
**empresa que mantém o portal** (o fornecedor cadastrado no módulo
`fornecedores` com `fornecedor_ativo = true`). Não há canal de
chamados, SLA, fila ou triagem.

### 1.2 O fornecedor exibido é o "ativo"

`suppliers.fornecedor_ativo` é um booleano nullable. A troca é feita por
`POST /cms/fornecedores_switch` (módulo `fornecedores`), que desativa o
anterior e ativa o selecionado. Se **dois** estiverem ativos, a query
(`->first()`) devolve o primeiro encontrado — sem ordenação explícita, o
que significa "ordem do índice", não determinística.

### 1.3 A página fica em branco sem fornecedor ativo

O `@if ($fornecedor)` em `v6/resources/views/admin/layouts/about.blade.php`
não tem ramo `else`. Não há mensagem, nem link para cadastrar um
fornecedor. O operador não sabe se o problema é "módulo sem fornecedor"
ou "fornecedor sem imagem".

### 1.4 O protocolo do ticket é derivado da categoria

```php
$protocolo = strtoupper(substr(str_slug($categoria->name), 0, 2)) . $id . date('Y');
```

Categorias do seeder -> prefixos: `Dúvida` -> `DU`, `Erro` -> `ER`,
`Sugestão` -> `SU`, `Elogío` -> `EL`. O sufixo é o id previsto
(`Support::max('id') + 1`) e o ano corrente. Exemplo: `DU25A2026`.

### 1.5 O ticket dispara **dois** e-mails

Um para o suporte (`email_suporte`) e um para o usuário que abriu. Se
qualquer um dos endereços for inválido, o controller **interrompe** o
fluxo e redireciona com erro — mas **o ticket já foi gravado** no banco
(o `DB::transaction` ocorre antes). Resultado: ticket criado sem
notificação, com mensagem de erro para o usuário.

### 1.6 O módulo não tem permissão própria

Não há recurso `suporte` em `resources` nem item em `menu_orders`. A
página é acessível a qualquer operador autenticado.

---

## 2. Acoplamentos

### 2.1 Dependências de saída

| Dependência | Tipo | Onde |
|---|---|---|
| módulo `fornecedores` (`Supplier`) | interno | `admin.layouts.about` |
| `check.logged` | interno | rota `/cms/suporte` |
| `Configuration` (`email`, `email_suporte`, `titulo_site`) | interno | sistema de tickets (órfão) |
| `Mail` | Laravel | sistema de tickets (órfão) |
| `Sentinel` | externo | sistema de tickets (órfão) |
| `spatie/laravel-activitylog` | externo | sistema de tickets (órfão) |

### 2.2 Dependências de entrada

| Consumidor | Dependência | Risco |
|---|---|---|
| Menu do AdminLTE | `route('informacoes-instituicao')` | erro 500 se o nome mudar |
| `admin.layouts.about` | `Supplier` (ativo) | página vazia sem fornecedor |
| views `prefecture/support/**` | `route('suporte.*')`, `route('email')` | `RouteNotFoundException` |
| views `prefecture/support/{support,tutorial}.blade.php` | `Breadcrumbs::render('static-ouvidoria', ...)` e conteúdo estático | — |

> O acoplamento com `fornecedores` é **de mão dupla**: o rodapé do CMS
> (`v6/resources/views/vendor/adminlte/page.blade.php:419-437`) também
> usa o fornecedor ativo. Uma mudança no módulo `fornecedores` afeta
> o "Suporte" e o rodapé.

---

## 3. Armadilhas

### 3.1 [CRÍTICO] O sistema de tickets é completamente inalcançável

Nenhuma rota `suporte.*` existe. Consequências:

- `Prefecture\SupportController` (155 linhas), os models
  `Support`/`SupportCategory`, as tabelas `supports`/`supports_categories`,
  o seeder de categorias, as 6 views e os 2 templates de e-mail são
  **código morto**.
- As views referenciam `route('suporte.index')`, `route('suporte.create')`,
  `route('suporte.show')` e `route('email')` — todos inexistentes.
- O `SupportController` redireciona para `route('suporte.index')` em
  **todos** os caminhos de retorno (linhas 93, 110, 118 e 126) — ou seja,
  **mesmo que a rota existisse**, o fluxo quebraria.

### 3.2 [CRÍTICO] `catch (Exception $ex)` sem `use Exception;`

`v6/app/Http/Controllers/Prefecture/SupportController.php:119`. Dentro do
namespace `App\Http\Controllers\Prefecture`, `Exception` resolve para
`App\Http\Controllers\Prefecture\Exception`, que **não existe**. O PHP
não encontra a classe e o `catch` **não captura nada**: as exceções
escapam sem tratamento (HTTP 500 com stack trace em debug).

Correção: `catch (\Exception $ex)`.

### 3.3 [ALTO] `GET /init` cria usuário e perfil administrativos

`v6/routes/basepro.php:9`:

```php
Route::get('/init', 'Admin\MainController@initApp')->name('init');
```

A rota **não tem** `check.logged`. E `MainController@initApp`
(`v6/app/Http/Controllers/Admin/MainController.php:45`) cria:

- o perfil `members`;
- o perfil `administrators` com as permissões
  `user.create`, `user.delete`, `user.update`, `user.view`,
  `role.create`, `role.delete`, `role.update`, `role.view`;
- o usuário **`admin@admin.com`** com senha **`123123`**, ligado ao perfil
  `administrators`.

Ou seja: **qualquer visitante que abra `/init` obtém credenciais
administrativas conhecidas**. A rota está no mesmo arquivo e ao lado da
rota de suporte — é preciso pelo menos registrá-la aqui.

> Não é uma falha do módulo `suporte` propriamente dito, mas é o item de
> maior severidade encontrado na área.

### 3.4 [ALTO] A configuração `email_suporte` pode não existir

`Configuration::where('key','email_suporte')->first()->value` — sem
checagem de null. Se a chave não estiver cadastrada, o erro é
"Call to a member function value() on null" (Erro 500).

### 3.5 [MÉDIO] O ticket é gravado antes do envio do e-mail

Se o e-mail falhar (endereço inválido, SMTP fora), o registro em
`supports` **já existe** e o usuário recebe "Sua mensagem não foi
enviada. Tente novamente." — uma inconsistência entre banco e
mensagem.

### 3.6 [MÉDIO] Condição de corrida na geração do protocolo

`Support::max('id')` + 1 é calculado antes do INSERT. Duas requisições
simultâneas obtêm o mesmo id previsto. Não há índice único em
`supports.protocolo`, então a duplicidade passaria despercebida.

### 3.7 [MÉDIO] N+1 na listagem de tickets

`Support::all()` seguido de `$content->users->name` e
`$content->category->name` em cada linha gera 2 queries por ticket
(sem `with()`).

### 3.8 [MÉDIO] Inconsistência de grafia do slug de permissão

A view usa `has_access(['support.create'])` (com "c"), enquanto o resto
do módulo usa "suporte". Mesmo que houvesse um recurso `support`, ele
não bateria com o slug do módulo `suporte` da tabela `modulos`.

### 3.9 [BAIXO] Variáveis mortas no `enviarEmail`

`$copyright` e `$copyright2` são atribuídos (`date('Y')`) e nunca usados.
`$prefecture` e `$prefecture2` são usados; `$from`/`$from2` e
`$to`/`$to2` são usados. `use App\Admin\User;` é importado e não usado.

### 3.10 [BAIXO] `SupportCategory` não usa `SoftDeletes`

A coluna `deleted_at` existe na tabela, mas o model não usa o trait
(`v6/app/Http/Model/Prefecture/SupportCategory.php`) — as categorias
apagadas continuam aparecendo.

### 3.11 [BAIXO] `Support` usa `hasOne` para relações 1:N

`users()` e `category()` são `hasOne` com a FK local. Funciona, mas
`belongsTo` expressaria melhor a cardinalidade.

### 3.12 [BAIXO] `Supplier::$fillable` com colunas inexistentes

`telefone` e `endereco` não existem na tabela `suppliers` (os dados
atuais estão em `phone` e `address`). São residuais de um esquema
anterior e não causam erro (o Eloquent só tenta gravar os campos
presentes no `$fillable` **e** na tabela, mas o `fill()` gravação de um
campo inexistente geraria erro de coluna — na prática, `upInsert` usa
`filter_request` com a lista explícita de campos, o que evita o
problema).

### 3.13 [BAIXO] `SupportController` estende o `Controller` do Admin

`use App\Http\Controllers\Admin\Controller;` — o controller do CMS **não**
registra middleware, ao contrário de `Prefecture\Controller`. Se as
rotas forem reativadas, é preciso adicionar `check.logged` e/ou
`check.route.permission` explicitamente.

---

## 4. Código duplicado entre v5 e v6

| Arquivo | v5 vs v6 |
|---|---|
| `app/Http/Controllers/Prefecture/SupportController.php` | **idêntico** |
| `app/Http/Model/Prefecture/Support.php` | **idêntico** |
| `app/Http/Model/Prefecture/SupportCategory.php` | **idêntico** |
| `resources/views/prefecture/support/**` (6 arquivos) | **idênticos** |
| `database/seeds/SupportCategorySeeder.php` | **idêntico** |
| `database/migrations/2018_12_26_140420_create_supports_categories_table.php` | **idêntica** |
| `database/migrations/2018_12_26_140430_create_supports_table.php` | **idêntica** |
| `app/Http/Controllers/Admin/MainController.php` | **idêntico** |

Única diferença no escopo do módulo: o **nome** da rota.

| | v5 | v6 |
|---|---|---|
| Rota | `GET /cms/suporte` — `informacoes-prefeitura` | `GET /cms/suporte` — `informacoes-instituicao` |

---

## 5. Onde a v6 divergiu (e por quê)

| Mudança | Efeito |
|---|---|
| Renomeou a rota `informacoes-prefeitura` -> `informacoes-instituicao` | alinhamento com a unificação de produtos (Prefeitura, Câmara, IPM) descrita no changelog "Public V6" |
| O menu do AdminLTE passou a ter mais itens de módulo com guarda `$modulos[...]` | o item "Suporte" **não** recebeu guarda — permanece visível mesmo com o módulo desativado |

---

## 6. Riscos de migração de schema

| Risco | Detalhe | Mitigação |
|---|---|---|
| Decisão pendente sobre o futuro do módulo | manter as duas metades ou remover o sistema de tickets | decisão de negócio antes de iniciar a migração |
| Tabelas `supports`/`supports_categories` vazias ou com dados | perda de histórico de chamados | exportar antes de remover |
| `supports.protocolo` sem índice único | protocolos duplicados | adicionar `unique` após limpar duplicatas |
| Dependência de `fornecedor_ativo` | sem fornecedor ativo, a página fica vazia | tornar o estado vazio explícito na UI |
| Referências a `route('suporte.*')` em views órfãs | se as views forem migradas sem as rotas, erro 500 | migrar as rotas e as views juntas, ou remover as views |

---

## 7. Perguntas em aberto para o negócio

1. **O que o módulo "Suporte" deve ser na v7?** Página de contato do
   fornecedor (mantendo) ou sistema de chamados (reativando)? As duas
   coisas não têm relação nenhuma hoje.
2. **Se for o sistema de chamados, quem atende?** Não há papel, fila nem
   SLA. Falta o conceito de "atendente".
3. **A página do fornecedor deveria ser pública?** Hoje é interna ao CMS.
4. **O que fazer com as tabelas `supports` e `supports_categories` se o
   sistema de tickets for descontinuado?**
5. **O módulo `suporte` deve continuar no registro de módulos** se for
   fundido com `fornecedores`?
6. **A rota `/init` pode ser removida?** Ela cria credenciais
   administrativas fixas e é acessível publicamente.
