# PRD — Movimentação WMS

> **Status:** Em discussão — eixos de decisão abertos  
> **Tipo:** Nova `operacao_tipo` no sinal AlocacaoWms existente  
> **Relacionado:** `00-prd.md` (visão geral WMS), `02-techplan.md` (arquitetura do sinal)

---

## 1. Contexto e Problema

O módulo WMS já suporta as operações `alocacao`, `inventario_produtos` e `inventario_endereco`. Todas registram *onde* um produto está, mas nenhuma representa o ato de *retirar* um produto de um endereço e *colocar* em outro.

Hoje, uma movimentação física no armazém (ex.: reorganização de corredor, transferência para área de expedição, consolidação de paletes) não tem representação no Genesis. O estoque fica divergindo entre a localização real e a cadastrada no WMS.

**Necessidade:** permitir que o operador registre no celular "tirei X unidades do produto P do endereço A e coloquei no endereço B", com rastreabilidade e integração ao ERP/WMS externo.

---

## 2. Usuários e User Stories

| Persona | User Story | Critério de aceite chave |
|---|---|---|
| **Operador WMS** | Eu quero registrar a movimentação de um produto de um endereço para outro para que o saldo reflita a localização correta | Bipa origem, produto e destino; confirma; o movimento fica registrado com status `confirmada` |
| **Gestor de Armazém** | Eu quero designar missões de movimentação para um operador específico para organizar o armazém | Cria missão `operacao_tipo = movimentacao`; operador vê no App |
| **TI / Integração** | Eu quero que a movimentação chegue ao ERP/WMS externo com o mesmo mecanismo das outras operações | Payload ERP com `operacao_tipo = movimentacao`; retry automático |

---

## 3. Eixos de Decisão

A movimentação introduz uma dificuldade nova: **duas localizações** (origem e destino) por movimento. O framework atual tem uma única coluna `localizacao_codigo` em `wms_movimentos`. As decisões abaixo são independentes e podem ser combinadas.

---

### Eixo A — Modelo de Dados

*Como armazenar `localizacao_origem` e `localizacao_destino` no banco?*

#### Opção A1 — Dois rows (debit / credit)

Cada movimentação gera **dois registros** em `wms_movimentos`:
- Row saída: `localizacao_codigo = origem`, `quantidade = -N`
- Row entrada: `localizacao_codigo = destino`, `quantidade = +N`

Ambos compartilham o mesmo `missao_item_id` (link implícito pelo par).

```mermaid
erDiagram
    wms_movimentos {
        int id
        string localizacao_codigo
        float quantidade
        string operacao_tipo
        int missao_item_id
    }

    wms_movimentos ||--|| wms_movimentos : "par saida/entrada\n(mesmo missao_item_id)"
```

| | |
|---|---|
| **Vantagem** | Sem migration; append-only natural; soma direta de `quantidade` por localização já dá o saldo |
| **Desvantagem** | Atomicidade: os dois inserts podem ser separados; integração ERP precisa interpretar `qty < 0`; difícil cancelar só metade |

---

#### Opção A2 — Colunas extras em `wms_movimentos`

Adicionar duas colunas: `localizacao_codigo_origem` e `localizacao_codigo_destino`. A coluna atual `localizacao_codigo` passa a ser o **destino** (backward-compat com as outras operações).

```mermaid
erDiagram
    wms_movimentos {
        int id
        string localizacao_codigo        "destino (mantida)"
        string localizacao_codigo_origem  "NEW — nullable"
        string localizacao_codigo_destino "NEW — alias explícito"
        float quantidade
        string operacao_tipo
    }
```

Migration necessária (simples `ALTER TABLE`):
```sql
ALTER TABLE wms_movimentos
    ADD COLUMN localizacao_codigo_origem  VARCHAR(64),
    ADD COLUMN localizacao_codigo_destino VARCHAR(64);
```

| | |
|---|---|
| **Vantagem** | Um row por movimento; semântica clara; filtros e joins diretos; payload ERP limpo |
| **Desvantagem** | Migration; as colunas ficam `NULL` para todas as outras operações (`alocacao`, `inventario_*`) |

---

#### Opção A3 — Origem em `dados_adicionais` do item

Guardar `localizacao_codigo_origem` como campo dentro do JSONB/TEXT de `missao_logistica_itens.dados_adicionais`. O `wms_movimentos.localizacao_codigo` recebe só o **destino**.

```mermaid
erDiagram
    missao_logistica_itens {
        int id
        jsonb dados_adicionais "{ localizacao_codigo_origem: 'A-01-01' }"
    }
    wms_movimentos {
        int id
        int missao_item_id
        string localizacao_codigo "destino"
    }
    missao_logistica_itens ||--|{ wms_movimentos : "missao_item_id"
```

| | |
|---|---|
| **Vantagem** | Sem migration em `wms_movimentos`; origem vive no contexto da sessão |
| **Desvantagem** | Origem não aparece no histórico de movimentos; integração ERP precisa cruzar tabelas; inconsistente com o modelo geral |

---

#### Recomendação A

**A2 (colunas extras)** — migration pequena, modelo mais limpo e queryável. O custo de ter duas colunas nullable é baixo; o ganho em clareza e na integração ERP/WMS é alto.

---

### Eixo B — UX / Ordem de Leitura

*Em que ordem o operador interage com origem, produto e destino?*

#### Opção B1 — Origem → Produto → Quantidade → Destino

O operador define contexto (de onde está saindo) primeiro, bipa o produto, informa a quantidade e só então confirma o destino.

```mermaid
sequenceDiagram
    actor Op as Operador
    participant App

    Op->>App: Bipa endereço ORIGEM
    App-->>Op: Origem confirmada (A-01-01)
    Op->>App: Bipa produto (EAN)
    App-->>Op: Produto identificado
    Op->>App: Digita quantidade
    Op->>App: Bipa endereço DESTINO
    App-->>Op: ✅ Movimento confirmado
```

| | |
|---|---|
| **Vantagem** | Operador "está na prateleira origem" — bipa naturalmente o que está diante dele; destino é o último gesto (chega lá e bipa) |
| **Desvantagem** | Operador precisa navegar até o destino depois de já ter digitado tudo; experiência longa antes de ver confirmação |

---

#### Opção B2 — Origem → Produto → Destino → Quantidade

Variante de B1 onde a quantidade vem após o destino (ou pode ser opcional / unitária).

```mermaid
sequenceDiagram
    actor Op as Operador
    participant App

    Op->>App: Bipa endereço ORIGEM
    App-->>Op: Origem confirmada
    Op->>App: Bipa produto (EAN)
    App-->>Op: Produto identificado
    Op->>App: Bipa endereço DESTINO
    App-->>Op: Confirmar quantidade?
    Op->>App: Digita / confirma quantidade
    App-->>Op: ✅ Movimento confirmado
```

| | |
|---|---|
| **Vantagem** | O operador chega no destino e ainda pode ajustar a quantidade antes de confirmar |
| **Desvantagem** | Igual a B1; fluxo longo; quantidade digitada no destino pode ser contra-intuitivo |

---

#### Opção B3 — Produto → Origem → Destino → Quantidade

Começa pelo produto (como `inventario_produtos`). Útil quando a designação pré-define o produto e o operador vai caçar onde ele está.

```mermaid
sequenceDiagram
    actor Op as Operador
    participant App

    Op->>App: Bipa produto (EAN)
    App-->>Op: Produto identificado
    Op->>App: Bipa endereço ORIGEM
    App-->>Op: Origem confirmada
    Op->>App: Bipa endereço DESTINO
    App-->>Op: Confirmar quantidade?
    Op->>App: Digita quantidade
    App-->>Op: ✅ Movimento confirmado
```

| | |
|---|---|
| **Vantagem** | Alinha com fluxo produto-primeiro quando a designação já define o produto |
| **Desvantagem** | Menos natural para reorganização de endereços; operador não está "na origem" quando começa |

---

#### Recomendação B

**B1 (Origem → Produto → Quantidade → Destino)** para sessão livre — o operador está em frente à origem, coleta o que precisa mover e percorre até o destino. A quantidade é informada enquanto ainda está na origem (sabe o que está retirando).

---

### Eixo C — Escopo da Missão

*O que é pré-definido na designação vs. livre no campo?*

#### Opção C1 — Sessão Livre (como alocação hoje)

Nada é pré-definido. O operador pode mover qualquer produto de qualquer endereço para qualquer destino dentro da mesma missão.

```mermaid
flowchart LR
    D[Designação] -->|operacao_tipo=movimentacao| M[Missão WMS]
    M --> Op[Operador]
    Op --> |Livre| Orig(Origem livre)
    Op --> |Livre| Prod(Produto livre)
    Op --> |Livre| Dest(Destino livre)
```

| | |
|---|---|
| **Vantagem** | Máxima flexibilidade; gestor não precisa saber os detalhes antes; igual ao padrão de alocação |
| **Desvantagem** | Sem rastreabilidade de "por que mover"; sem garantia de que o operador moveu o que devia |

---

#### Opção C2 — Produto pré-definido, endereços livres

A designação especifica quais produtos devem ser movimentados. O operador confirma de onde e para onde.

```mermaid
flowchart LR
    D[Designação] -->|produto_id = 16221\noperacao_tipo = movimentacao| M[Missão WMS]
    M --> Op[Operador]
    Op --> |Livre| Orig(Origem livre)
    Op --> |Fixo| Prod(Produto = Água 500ml)
    Op --> |Livre| Dest(Destino livre)
```

| | |
|---|---|
| **Vantagem** | Gestor define "o que mover"; controle sem rigidez de endereços |
| **Desvantagem** | Requer ajuste na tela de designação para selecionar produtos |

---

#### Opção C3 — Produto + Origem pré-definidos, Destino livre

Tanto o produto quanto o endereço de origem são definidos na designação. O operador só define o destino.

```mermaid
flowchart LR
    D[Designação] -->|produto_id = 16221\nloc_origem = A-01-01\noperacao_tipo = movimentacao| M[Missão WMS]
    M --> Op[Operador]
    Op --> |Fixo| Orig(Origem = A-01-01)
    Op --> |Fixo| Prod(Produto = Água 500ml)
    Op --> |Livre| Dest(Destino livre)
```

| | |
|---|---|
| **Vantagem** | Máximo controle; missão de reorganização dirigida |
| **Desvantagem** | Mais complexidade na tela de designação; menos útil para reorganizações ad-hoc |

---

#### Recomendação C

**C1 (Sessão Livre)** para o MVP — alinha com o padrão já implementado e reduz escopo. As opções C2/C3 podem vir numa segunda fase junto com o escopo de designações (F3 do techplan).

---

### Eixo D — Validação de Saldo de Origem

*O sistema deve verificar se há saldo suficiente no endereço de origem antes de confirmar?*

> **Contexto atual:** Genesis não tem read API do WMS externo. O saldo "oficial" vive no WMS. Genesis só empurra movimentos (push-only). Pode-se derivar um saldo local somando `wms_movimentos` por localização, mas ele reflete apenas o que foi registrado no Genesis, não o estoque físico real.

#### Opção D1 — Sem validação (WMS reconcilia)

Genesis registra o movimento, empurra ao WMS. O WMS valida o saldo e retorna erro de integração se houver inconsistência.

```mermaid
flowchart LR
    Op[Operador confirma] --> Reg[Registra wms_movimentos]
    Reg --> Push[Empurra ao WMS/ERP]
    Push -->|OK| Done[✅ Enviado]
    Push -->|Saldo insuficiente| Falha[integracao_erp = falha]
    Falha --> Retry[Retry worker]
```

| | |
|---|---|
| **Vantagem** | Sem complexidade extra; Genesis mantém papel de source of truth de movimentos (não de saldo) |
| **Desvantagem** | Operador não sabe na hora que vai falhar; movimento fica com `integracao_erp = falha` |

---

#### Opção D2 — Saldo local Genesis (aviso, sem bloqueio)

Genesis soma `wms_movimentos` para derivar saldo local. Antes de confirmar, exibe o saldo estimado. Se saldo < quantidade a mover, exibe **aviso** mas permite continuar.

```mermaid
flowchart LR
    Op[Operador confirma] --> Calc[Calcula saldo local\nSUM qty por origem+produto]
    Calc -->|Saldo >= qty| Reg[Registra]
    Calc -->|Saldo < qty| Aviso[⚠️ Exibe aviso\n'Saldo estimado: 5, movendo: 10']
    Aviso --> Op2[Operador decide continuar?]
    Op2 -->|Sim| Reg
    Op2 -->|Não| Cancel[Cancela]
    Reg --> Push[Empurra ao WMS/ERP]
```

| | |
|---|---|
| **Vantagem** | Feedback imediato sem bloquear; útil como sanidade; saldo local pode ficar desatualizado em relação ao WMS real |
| **Desvantagem** | Saldo local diverge do WMS real (Genesis não sabe o que aconteceu diretamente no WMS sem passar por ele); pode gerar falsos positivos e falsos negativos |

---

#### Opção D3 — Saldo local com bloqueio

Igual ao D2, mas **bloqueia** a confirmação se saldo local < quantidade.

| | |
|---|---|
| **Vantagem** | Proteção mais forte contra inconsistência no Genesis |
| **Desvantagem** | Saldo local pode estar errado (WMS fez movimentos diretos); operador pode ficar travado por razão incorreta |

---

#### Recomendação D

**D1 (sem validação)** para o MVP — é coerente com o princípio atual do módulo (Genesis é source of truth de movimentos registrados por ele, não de saldo físico). O WMS externo valida o estoque. D2 pode ser adicionado como feature futura quando tivermos uma API de leitura do WMS.

---

## 4. Quadro de Decisões

| Eixo | Opção Recomendada | Alternativa MVP | Alternativa Futura |
|---|---|---|---|
| **A — Dados** | A2 — colunas extras | A1 — dois rows | — |
| **B — UX** | B1 — Origem→Produto→Qtde→Destino | B2 (Qtde depois do destino) | B3 (produto-primeiro) |
| **C — Escopo** | C1 — Sessão livre | — | C2/C3 na fase de designações |
| **D — Saldo** | D1 — sem validação | — | D2 com read API do WMS |

---

## 5. Impacto Técnico (esboço)

Com as recomendações acima, a implementação requereria:

1. **Migration** — `005_add_movimentacao_fields.sql`: `ALTER TABLE wms_movimentos ADD COLUMN localizacao_codigo_origem VARCHAR(64), ADD COLUMN localizacao_codigo_destino VARCHAR(64)`
2. **Enum** — adicionar `case MOVIMENTACAO = 'movimentacao'` em `OperacaoTipo.php`
3. **Adapter** — novo `MovimentacaoAdapter.php` estendendo `OperacaoAdapterBase`
4. **Factory** — registrar `'movimentacao' => MovimentacaoAdapter::class`
5. **View** — `App/html/operacaowms/movimentacao.php` + `movimentacao.js` (fluxo B1)
6. **Sonda** — `WmsMovimentoSonda::registrarMovimento()` sem mudanças (aceita os campos novos via `montarMovimento`)
7. **Laboratorio** — `ConfirmarAlocacaoLaboratorio` genérico, sem mudanças
8. **i18n** — 3 línguas (`pt-br`, `en`, `es`)

---

## 6. Fora do Escopo (MVP)

- Validação de saldo de origem (D2/D3)
- Missão com produto/origem pré-definidos (C2/C3)
- Rastreio de movimentações em lote (múltiplos produtos em uma única missão orquestrada)
- Dashboard de movimentações (relatório de transferências por período)
- Integração reversa (leitura de saldo do WMS externo)
