# Comparação: Spatie Model Status vs Model States vs Symfony Workflow

## Fluxo no sistema (implementação atual)

- **Status e Fluxo são por tenant:** as tabelas `status`, `fluxo`, `fluxostatus`, `fluxostatustransicao` e `fluxostatushist` possuem `idtenant` e os models usam `TenantScope`. Cada tenant cadastra seus próprios status e fluxos.

## O que você precisa

1. **Módulo para criar e configurar status** (ex.: admin cadastra novos status, ordem, rótulos).
2. **Status de sistema** que não podem ser removidos e disparam ações (ex.: CONCLUÍDO → atualiza saldo da agência).

Nenhum dos três pacotes atende os dois pontos “prontos”; a escolha define onde você coloca a configuração e onde coloca a lógica de sistema.

---

## Resumo rápido

| Critério | **laravel-model-status** | **laravel-model-states** | **Symfony Workflow** |
|----------|---------------------------|---------------------------|------------------------|
| Lista de status | String/enum, flexível | Uma classe PHP por estado | Places no config (YAML/PHP) |
| Configurável via BD/Admin | ✅ Sim (só o nome) | ❌ Não (código) | ⚠️ Sim, se você montar a definição a partir do BD |
| Regras de transição | ❌ Você implementa | ✅ `allowTransition(De, Para)` | ✅ Transitions no config |
| “Fazer algo na transição” | Você chama antes/depois de `setStatus()` | ✅ **Transition classes** (ex.: atualizar saldo) | ✅ Eventos (entered, completed) |
| Histórico de mudanças | ✅ Tabela `statuses` | ❌ Não incluso | ⚠️ `audit_trail` opcional |
| Status “não removível” | Flag na sua tabela de config | ✅ Classe existe = não “remove” | Marcar no config/BD e não apagar |

---

## 1. spatie/laravel-model-status

- **Ideia:** O modelo tem “statuses”; você chama `$model->setStatus('concluido')` (ou enum). Cada mudança vira um registro em tabela (histórico).
- **Status configuráveis:** Sim. Os status são só nomes (string). Você pode ter uma tabela `status_config` (nome, entidade, ordem, rótulo, `is_system`) e no admin só permitir criar/editar os não-sistema.
- **Ações de sistema (ex.: atualizar saldo):** O pacote **não** tem transição nem eventos. Você centraliza a mudança num serviço, por exemplo:
  - `StatusService::setStatus($parcela, 'CONCLUÍDO')`:
    - Valida se a transição (status atual → CONCLUÍDO) é permitida (consultando sua config ou regras).
    - Se for “entrar em CONCLUÍDO” e tiver agência → chama `NfParcelaService->concluirParcela()`.
    - Chama `$parcela->setStatus('CONCLUÍDO')`.
- **Status de sistema:** Na sua tabela de config, `is_system = true` para CONCLUÍDO, PENDENTE, etc. No CRUD de status, não permite remover nem editar nome desses.

**Prós:** Status 100% configuráveis, histórico incluso, pouca dependência de “estado em código”.  
**Contras:** Você implementa sozinho: transições permitidas e o “hook” que dispara a atualização de saldo.

---

## 2. spatie/laravel-model-states

- **Ideia:** Cada estado é uma **classe** (ex.: `Pendente`, `Concluido`, `Inativo`). Transições são definidas em código: `allowTransition(Pendente::class, Concluido::class, ConcluirParcelaTransition::class)`. A classe de transição faz o que quiser (incluindo atualizar saldo).
- **Status configuráveis:** A **lista de estados** é fixa em código (novo status = nova classe + config). O que você pode “configurar” num módulo:
  - Rótulo, cor, ordem (tabela ou config) para exibir no admin.
  - Quem pode fazer qual transição (permissões).
- **Ações de sistema:** É o forte do pacote. Exemplo:
  - `ConcluirParcelaTransition` estende `Transition`, no `handle()` chama `NfParcelaService->concluirParcela()` e depois altera o estado. Não há como “remover” esse comportamento sem remover a classe (status de sistema “protegido” pelo código).
- **Status de sistema:** Os estados que têm transições com efeito (saldo, etc.) são justamente as classes que você não remove. Não existe “apagar” o estado Concluído no admin porque ele é código.

**Prós:** Transições claras, “ao ir para CONCLUÍDO” fica numa classe só; integração natural com seu `NfParcelaService`.  
**Contras:** Novos status = novo estado + deploy; não dá para criar “só mais um status” 100% pelo admin.

---

## 3. Symfony Workflow

- **Ideia:** Workflow = places (estados) + transitions (de → para). Configuração em YAML/PHP (ou você monta a `Definition` a partir do BD). Quando uma transição ocorre, o componente dispara eventos; você assina e roda a lógica (ex.: atualizar saldo).
- **Status configuráveis:** Os “places” podem vir de config. Dá para ter uma tabela de status e, no boot da app ou num service provider, construir o workflow a partir dela (com regras de transição também no BD).
- **Ações de sistema:** Você registra listeners para eventos (ex.: `workflow.entered.concluido` ou `workflow.completed.to_concluido`). Num listener chama `NfParcelaService->concluirParcela()`. “Status de sistema” = places/transitions que têm listener; na sua config/BD você marca como `system: true` e no admin não permite excluir.
- **Histórico:** O componente tem `audit_trail`; em Laravel você pode ainda persistir em tabela própria.

**Prós:** Workflow muito flexível, configurável (inclusive via BD se você implementar), padrão conhecido.  
**Contras:** Mais conceitos (places, transitions, marking store); integrar no Laravel; construir definição a partir do BD exige um pouco de código.

---

## Recomendações práticas

### Cenário A: Prioridade é “não quebrar saldo” e manter regras claras no código

→ **Use spatie/laravel-model-states.**

- Status que impactam saldo (CONCLUÍDO, etc.) viram estados fixos em código, com **transition classes** que chamam `NfParcelaService`. Esses são seus “status de sistema” (não removíveis).
- O “módulo de configurar status” pode existir para: rótulos, cores, ordem, permissões de quem pode fazer cada transição — sem mudar a lista de estados que têm efeito financeiro.

### Cenário B: Prioridade é ter muitos status criados/editados pelo admin

→ **Use spatie/laravel-model-status** e implemente uma camada sua:

- Tabela de config de status (nome, entidade, ordem, `is_system`). Status de sistema (CONCLUÍDO, PENDENTE, …) com `is_system = true` e não removíveis no CRUD.
- Um **StatusFlowService** (ou similar) que:
  - Valida transições (ex.: de onde → para onde é permitido), usando sua config.
  - Antes/depois de `setStatus()`, executa ações: “se novo status = CONCLUÍDO e entidade = parcela e tem agência → concluir parcela”.
- Assim você tem módulo para criar/configurar status e, ao mesmo tempo, “status de sistema” que não podem ser removidos e que disparam a atualização de saldo.

### Cenário C: Workflow complexo e configurável (muitas entidades, muitas transições)

→ **Use Symfony Workflow** (ou um wrapper Laravel).

- Defina workflows por tipo de entidade (nfparcela, pedido, etc.); podem ser em arquivo ou montados a partir do BD.
- Ações de sistema em **event listeners** (ex.: ao completar transição “to_concluido” em nfparcela → atualizar saldo). Status/transições de sistema = as que têm listener; não removíveis na sua UI de config.

---

## Conclusão

- **Status de sistema que atualizam saldo:** em qualquer opção você precisa de um ponto único que chame `NfParcelaService`: transition class (model-states), serviço antes de `setStatus()` (model-status), ou listener de evento (Symfony Workflow).
- **Módulo para criar/configurar status:**  
  - **model-states:** só “configuração de apresentação” (rótulo, ordem); lista de estados é em código.  
  - **model-status:** lista de status totalmente configurável; transições e ações você implementa.  
  - **Symfony Workflow:** workflow e status configuráveis (inclusive via BD), ações em eventos.

Se o foco agora é **não quebrar o que já existe (saldo, concluir/estornar)** e ter regras explícitas no código, **laravel-model-states** é a opção mais direta. Se o foco é **muitos status definidos pelo negócio no admin**, **laravel-model-status** + sua camada de transições e “status de sistema” atende bem. Symfony Workflow vale quando você quer um motor de workflow reutilizável e configurável para várias entidades.
