# Fornecedores — Gaps, Dívidas e Divergências v5 → v6

---

## 1. Bugs e erros conhecidos

| Sev. | Item | Arquivo / linha |
|---|---|---|
| **CRÍTICO** | As 4 rotas de escrita **não têm autenticação** (`supplier.store`, `supplier.update`, `supplier.destroy`, `/cms/fornecedores_switch`) | `v6/routes/basepro.php:352-355` |
| **CRÍTICO** | `SupplierController::__construct()` não chama `parent::__construct()` -> o middleware `check.route.permission` da classe base `Prefecture\Controller` **não é registrado** | `v6/app/Http/Controllers/Prefecture/SupplierController.php:30-33` |
| **ALTO** | O módulo **não tem recurso** em `resources` -> `supplier.index`/`supplier.create` não podem ser concedidas | (nenhum seeder) |
| **ALTO** | `Repository::upInsert` faz `dd($th)` -> vazamento da exceção | `v6/app/Repositories/Repository.php:64-71` |
| **ALTO** | Telefone vazio (só não dígitos) vira `null` numa coluna NOT NULL -> erro 500 | `v6/app/Http/Traits/AppTrait.php:523-534` + `suppliers.phone` |
| **MÉDIO** | `switchFornecedorAtivo` sem transação e sem validar o id (`null->update()`) | `v6/app/Http/Controllers/Prefecture/SupplierController.php:144-165` |
| **MÉDIO** | Clicar no fornecedor já ativo o **desativa** (sem reativação) — perda de funcionalidade da v5 | mesmo arquivo, linhas 150-158 |
| **MÉDIO** | `fornecedor_ativo` gravado como `null` (nunca `false`) | mesmo arquivo, linha 148 |
| **MÉDIO** | `slug` recalculado em toda edição, sem índice único | `v6/app/Repositories/Eloquent/Prefecture/Supplier/SupplierRepository.php:35` |
| **MÉDIO** | `locale` sem validação de URL, é injetado no `src` de um `<iframe>` | `v6/app/Http/Requests/SupplierRequest.php` |
| **MÉDIO** | Nenhuma auditoria (`activity()`) no módulo | `v6/app/Http/Controllers/Prefecture/SupplierController.php` |
| **MÉDIO** | Arquivos órfãos no disco ao excluir ou trocar a imagem | `v6/app/Http/Controllers/Prefecture/SupplierController.php:129-141` |
| **MÉDIO** | `email` e `site` são NOT NULL na v6, mas nullable na v5 (migration não portada) | `v5/database/migrations/2026_04_06_113000_alter_suppliers_table_nullable.php` |
| **BAIXO** | `Supplier::$fillable` inclui `telefone` e `endereco`, que não existem na tabela | `v6/app/Http/Model/Prefecture/Supplier.php:18-32` |
| **BAIXO** | `use mysql_xdevapi\Exception;` (import de resíduo, classe inexistente) | `v6/app/Http/Controllers/Prefecture/SupplierController.php:12` |
| **BAIXO** | `phone` validado **antes** da remoção dos não dígitos | `v6/app/Http/Requests/SupplierRequest.php` + controller |
| **BAIXO** | Listagem carrega todos os fornecedores (`->get()`) | `v6/app/Http/Controllers/Prefecture/DatatableController.php:159-163` |
| **BAIXO** | `SuplierTableSeeder` faz `truncate` | `v6/database/seeds/SuplierTableSeeder.php` |
| **BAIXO** | A view SQL `fornecedores` não é versionada por migration | — |
| **BAIXO** | `slug` não é único; `fornecedor_ativo` não tem restrição | schema |
| **BAIXO** | `phone` gravado com no máximo 20 caracteres, mas sem `max` na validação | `v6/app/Http/Requests/SupplierRequest.php` |

---

## 2. Funcionalidades incompletas ou mortas

| Item | Situação |
|---|---|
| Recurso `supplier` em `resources` | **Ausente** |
| Auditoria | Inexistente |
| Exportação | Inexistente |
| Histórico de alterações | Inexistente |
| Limpeza de arquivos órfãos | Inexistente |
| Restrição de "um fornecedor ativo" | Inexistente (apenas convenção do código) |
| Botão de **visualizar** (detalhe) na listagem | Inexistente (só editar/excluir) |
| Detalhe do fornecedor | Inexistente |

---

## 3. Mapa v5 → v6

### 3.1 O que mudou

| Aspecto | v5 | v6 |
|---|---|---|
| `SupplierController` | idêntico | **DIFERENTE** (ver 3.2) |
| `Supplier` (model) | idêntico | **idêntico** |
| `SupplierRequest` | idêntico | **idêntico** |
| `SupplierRepository` | idêntico | **idêntico** |
| Views `fornecedor/**` | 4 arquivos | **idênticos** |
| `datatable.js` | idêntico | **idêntico** |
| `SuplierTableSeeder` | idêntico | **idêntico** |
| Migration `create_suppliers_table` | idêntica | **idêntica** |
| Migration `add_column_to_suppliers_table` | idêntica | **idêntica** |
| Migration `update_suppliers_address_values` | idêntica | **idêntica** |
| Migration `alter_suppliers_table_nullable` | **presente** | **AUSENTE** |

### 3.2 Diferenças no `SupplierController`

```diff
+ // store e update: sanitização do telefone e do WhatsApp
+ $request->merge([
+     'phone'    => preg_replace('/\D/', '', $request->phone),
+     'whatsapp' => preg_replace('/\D/', '', $request->whatsapp),
+ ]);
```

```diff
- return redirect()->route('supplier.index');
+ return redirect()->route('supplier.index')->with('success', 'Registro cadastrado com sucesso');
```

```diff
- // v5: TOGGLE
- if ($supplier->fornecedor_ativo) {
-     $supplier->update(['fornecedor_ativo' => null]);
- } else {
-     Supplier::where('fornecedor_ativo', true)->update(['fornecedor_ativo' => null]);
-     $supplier->update(['fornecedor_ativo' => true]);
- }

+ // v6: sempre ativa o clicado
+ $oldSulplier = Supplier::where('fornecedor_ativo', true)->first();
+ if ($oldSulplier) { $oldSulplier->update(['fornecedor_ativo' => null]); }
+ if (!isset($oldSulplier->id)) {
+     $this->supplier->getModel()->find($request->contents_id)->update(['fornecedor_ativo' => true]);
+ } elseif ($oldSulplier->id != $request->contents_id) {
+     $this->supplier->getModel()->find($request->contents_id)->update(['fornecedor_ativo' => true]);
+ }
```

Além disso, a v6 separou os `catch` de `PDOException` e `Exception` no
`destroy` e no `switchFornecedorAtivo`, e o `switch` deixou de retornar
`404` ("Fornecedor não encontrado.") para um id inválido.

### 3.3 Regressão de schema

A migration `2026_04_06_113000_alter_suppliers_table_nullable.php` existe
apenas na **v5** e torna `suppliers.email` e `suppliers.site` nullable.
Ela **não foi portada para a v6** — provavelmente um erro de merge entre
as duas árvores. Na v6, `email` e `site` são `NOT NULL`, e o
`SupplierRequest` exige ambos.

**Consequência:** um fornecedor sem site não pode ser cadastrado na v6,
mas poderia na v5.

---

## 4. Riscos de dados

| Risco | Detalhe | Mitigação |
|---|---|---|
| `email`/`site` NOT NULL | divergência de schema entre v5 e v6 | portar a migration ou unificar na v7 |
| `slug` sem unicidade | dois fornecedores com o mesmo nome colidem | `unique` + desambiguação no backfill |
| `fornecedor_ativo` sem restrição | múltiplos ativos | índice parcial único |
| Colunas `telefone`/`endereco` no `$fillable` | erro se um `fill()` direto as usar | remover do model |
| Arquivos órfãos | disco cresce | apagar o arquivo no `delete` + varredura |
| View SQL fora do versionamento | não é recriada em `migrate:fresh` | versionar como migration |
| `truncate` do seeder | apaga fornecedores reais | remover do fluxo de instalação |
| Caminhos `/storage/...` hardcoded no JS e na view | quebram se o symlink mudar | usar `Storage::disk('public')->url()` |
| `phone`/`whatsapp` sem `max` | valores longos | adicionar `max:20` |

---

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

1. **Pode haver mais de um fornecedor por instalação?** O desenho
   (vários, um ativo) sugere que sim — mas então o que significa
   "fornecedor ativo" em uma instalação com vários?
2. **E-mail e site são obrigatórios?** A v5 já os havia tornado
   nullable; a v6 exige.
3. **O mapa (locale) é obrigatório?** Hoje é `required`, sem validação
   de URL.
4. **Deve haver auditoria de quem trocou o fornecedor ativo?**
5. **O rodapé do portal público deve exibir o fornecedor?** Hoje
   exibe o do CMS.
6. **A functionality de "fornecedor" é genérica?** Em outras
   instalações pode ser o fornecedor de um equipamento ou de um software
   de terceiro — o que muda o significado de `fornecedor_ativo`.
7. **A exclusão deve apagar os arquivos?**

---

## 6. Checklist de correção (priorizado)

| Prioridade | Item | Arquivo |
|---|---|---|
| P0 | Exigir autenticação nas 4 rotas de escrita (`check.logged`) | `v6/routes/basepro.php:352-355` |
| P0 | Registrar autorização no controller (Policy ou `check.route.permission`) | `v6/app/Http/Controllers/Prefecture/SupplierController.php:30-33` |
| P0 | Criar o recurso `supplier` (seeder idempotente) | novo seeder |
| P1 | Corrigir `Repository::upInsert` (não usar `dd($th)`) | `v6/app/Repositories/Repository.php:64-71` |
| P1 | Impedir telefone vazio após a sanitização | `v6/app/Http/Controllers/Prefecture/SupplierController.php:63-68` |
| P1 | Transaction + validação de id em `switchFornecedorAtivo` | `v6/app/Http/Controllers/Prefecture/SupplierController.php:144-165` |
| P1 | Validar `locale` como URL | `v6/app/Http/Requests/SupplierRequest.php` |
| P2 | Restaurar o toggle do fornecedor ativo (ou adicionar "Desativar") | `v6/app/Http/Controllers/Prefecture/SupplierController.php:150-158` |
| P2 | Remover `telefone`/`endereco` do `$fillable` | `v6/app/Http/Model/Prefecture/Supplier.php:18-32` |
| P2 | `unique` em `slug` + índice parcial em `fornecedor_ativo` | nova migration |
| P2 | Registrar auditoria (`activity()`) | `v6/app/Http/Controllers/Prefecture/SupplierController.php` |
| P2 | Apagar os arquivos ao excluir o fornecedor | mesmo arquivo, linhas 129-141 |
| P2 | Portar a migration `alter_suppliers_table_nullable` (v5 -> v6) | `v5/database/migrations/2026_04_06_113000_alter_suppliers_table_nullable.php` |
| P3 | Remover o import `mysql_xdevapi\Exception` | `v6/app/Http/Controllers/Prefecture/SupplierController.php:12` |
| P3 | Remover o `truncate` do `SuplierTableSeeder` | `v6/database/seeds/SuplierTableSeeder.php` |
| P3 | Versionar a view SQL `fornecedores` | nova migration |
| P3 | Usar `Storage::url()` no lugar de `/storage/` hardcoded | `v6/public/assets/js/custom/config/fornecedor/datatable.js` + `about.blade.php` |
