# Fornecedores — Resumo Executivo

> **Versão analisada:** `v6` (e `v5`, com diferenças)
> **Slug do módulo:** `fornecedores` (tabela `modulos`, id 6, `status = true`)
> **Natureza:** CMS (painel administrativo)
> **Estado:** migrado v5 -> v6 (com ajustes de lógica no "fornecedor ativo" e de sanitização de telefone)

---

## 1. Objetivo

Cadastrar as **empresas fornecedoras do sistema** (no caso, a empresa que
desenvolveu e mantém o portal). Cada fornecedor tem nome, e-mail, site,
telefone, WhatsApp, endereço, mapa (Google Maps), logotipo e ícone.

Um único fornecedor pode ser marcado como **ativo**, e é esse fornecedor
que aparece em dois lugares do CMS:

1. na página **Suporte** (`/cms/suporte`);
2. no **rodapé** do CMS (nome e site).

## 2. Efeito / Impacto

| Faixa | Efeito |
|---|---|
| Cidadão | Nenhum efeito direto. |
| Prefeitura (CMS) | Centraliza os dados de contato de quem mantém o sistema, para que o operador saiba a quem recorrer. |
| Transparência e legalidade | O rodapé do CMS e o portal público exibem o nome do fornecedor responsável — é a identificação técnica de quem opera o portal. |
| Operação técnica | O rodapé do CMS exibe o nome e o site do fornecedor ativo. |

## 3. Comportamento

- **Listagem**: DataTable servida pela view SQL `fornecedores`, com colunas
  Nome, E-mail, Telefone, Endereço, Ícone, Imagem e Ações.
- **Cadastro/edição**: `name` (5-120), `email` (e-mail válido), `site`,
  `phone` (>= 5), `address` (>= 5), `locale` (URL do mapa, obrigatório),
  `image` e `icon` (imagens, opcionais mas validadas quando enviadas).
- O `slug` é gerado automaticamente a partir do `name`
  (`Str::slug($request->name)`).
- Telefone e WhatsApp têm os **caracteres não numéricos removidos**
  (`preg_replace('/\D/', '', ...)`) antes de gravar.
- Imagens: `image` vai para `storage/app/public/img/images_fornecedores/`
  e `icon` para `storage/app/public/img/icons_fornecedores/`, com nome
  aleatório (`uniqid() . str_random(5) . '.' . extensao`).
- **"Fornecedor ativo"**: um único fornecedor pode estar ativo. A troca é
  feita por `POST /cms/fornecedores_switch`, que desativa o anterior e
  ativa o selecionado.
- A exclusão é **definitiva por soft delete** (`Supplier` usa
  `SoftDeletes`).

## 4. Como o Usuário Interage

- Menu de engrenagem do AdminLTE -> item **"Fornecedores"**
  (`v6/resources/views/vendor/adminlte/page.blade.php:275-282`), com guarda
  `has_access(['supplier.index']) && $modulos['fornecedores']`.
- `/cms/fornecedores`: tabela com botão "Cadastrar" (visível apenas com
  `supplier.create`).
- Botão de alternância na linha: ativa/desativa o fornecedor
  (`fornecedores_switch`).
- Botões de editar (`fornecedores/editar/{id}`) e excluir
  (`fornecedores/excluir/{id}`).
- Formulário: nome, e-mail, site, telefone, WhatsApp, endereço, mapa
  (URL), logotipo e ícone.

## 5. Detalhes e Particularidades Técnicas

- Camadas: `Route` -> `Prefecture\SupplierController` ->
  `SupplierRequest` -> `ISupplierRepository`/`SupplierRepository` ->
  `App\Http\Model\Prefecture\Supplier` -> `suppliers`.
- O `SupplierController::__construct()` **não** chama
  `parent::__construct()`; logo, o middleware `check.route.permission` da
  classe base `Prefecture\Controller` **não** é registrado. Somadas às
  rotas que não declaram `check.logged`, as actions de **escrita**
  (`store`, `update`, `destroy`, `switchFornecedorAtivo`) ficam
  **sem qualquer autenticação** — protegidas apenas pelo CSRF do grupo
  `web`.
- A listagem usa a **view SQL** `fornecedores` (alias de `phone` para
  `telefone` e de `address` para `endereco`), não o model.
- Não há auditoria (`activity()`) no módulo.
- O módulo **não tem recurso** em `resources` -> `supplier.index` e
  `supplier.create` não podem ser concedidas a nenhum perfil; apenas
  `super-admin` passa.

## 6. Estrutura de Dados

| Tabela | Papel |
|---|---|
| `suppliers` | Fornecedores do sistema |
| `resources` | Deveria ter o recurso `supplier` (modulo_id 6) — **ausente** |
| `modulos` | Registro de módulos (id 6 = `fornecedores`) |
| `configurations` | Usada indiretamente (o rodapé do portal) |

Views SQL: `fornecedores`.

Seeds: `v6/database/seeds/SuplierTableSeeder.php` insere três
fornecedores de exemplo (Máxima Tecnologia, PBSoft, PublicSoft) — com
`DB::table('suppliers')->truncate()`.

## 7. Como Testar

- `/cms/fornecedores` deve listar os fornecedores (via view SQL).
- Cadastrar um fornecedor com imagem deve gravar o arquivo em
  `storage/app/public/img/images_fornecedores/` e o caminho relativo em
  `suppliers.image`.
- Ativar um fornecedor deve desativar o anterior (apenas um ativo).
- Um `POST /cms/fornecedores` **sem sessão** deve ser aceito (comportamento
  atual — falha de segurança a ser corrigida na v7).
- O telefone deve ser gravado só com dígitos.

## 8. Migração para Laravel Moderno

- Model `Supplier` enxuto, com `casts` (`fornecedor_ativo => boolean`),
  accessor de telefone formatado e escopo `scopeAtivo`.
- `StoreSupplierRequest` / `UpdateSupplierRequest` com as mesmas
  regras, mais `Rule::unique` no `slug`.
- `SupplierPolicy` no lugar do middleware ausente.
- `POST/PUT/DELETE` autenticados (`auth` + Policy) — corrigir a falha
  de segurança.
- `SupplierResource` para a API; endpoint `GET /api/suppliers/active`.
- Upload com `Storage::disk('public')->putFile()` (sem
  `Intervention` para reescala desnecessária).
- Consolidação das colunas duplicadas `phone`/`telefone` e
  `address`/`endereco` (o model ainda lista as antigas no `$fillable`).

## 9. Resumo Geral

"Fornecedores" cadastra as empresas que fornecem o sistema (no caso, a
empresa que mantém o portal), com nome, contatos, endereço, mapa e duas
imagens (logotipo e ícone). A listagem é servida por uma **view SQL**
(`fornecedores`) e o CRUD usa o padrão Repository do legado
(`ISupplierRepository`/`SupplierRepository`), com upload das imagens para
`img/images_fornecedores/` e `img/icons_fornecedores/`. Um fornecedor pode
ser marcado como **ativo**, e é esse que aparece na página de Suporte, no
rodapé do CMS e no rodapé do portal. Os problemas: o controller não
registra o middleware `check.route.permission` da classe base e as rotas
de escrita (`store`, `update`, `destroy`, `fornecedores_switch`) não
declaram `check.logged`, o que as deixa **sem autenticação** (protegidas
apenas pelo CSRF); o módulo não tem recurso em `resources`, o que impede
conceder `supplier.index`/`supplier.create`; o model ainda lista no
`$fillable` as colunas `telefone` e `endereco`, que não existem mais; e
a migration que tornava `email` e `site` nullable, presente na v5, **não
foi portada para a v6**.

---

## Índice do dossiê

| Arquivo | Conteúdo |
|---|---|
| `00-RESUMO.md` | Este documento |
| `01-ARQUITETURA.md` | Camadas, classes e o diagrama de fluxo |
| `02-MODELAGEM.md` | Estrutura de dados detalhada |
| `03-ROTAS-E-CONTROLADORES.md` | Rotas, controllers, requests, resources |
| `04-VIEWS-E-UX.md` | Telas, componentes, JS, assets |
| `05-REGRAS-E-PORTABILIDADE.md` | Particularidades e armadilhas |
| `06-MIGRACAO-MODERNA.md` | Plano de reconstrução em Laravel moderno |
| `07-TESTES.md` | Estratégia e casos de teste |
| `08-GAPS-E-DIVIDAS.md` | Pendências, riscos, comparativo v5 vs v6 |
