# Integração WMS desacoplada — Spec

## Contexto e problema
Hoje o Genesis é o cérebro da operação logística: cria as missões a partir de pedidos/romaneios e **empurra** o resultado para um único sistema externo (`galpaodasembalagens`), com URL e token chumbados no código, sem abstração de provedor, sem configuração por empresa e **só de saída**. A empresa quer plugar um **WMS completo e externo** que passa a ser a fonte da verdade do estoque e a **comandar** o Genesis (dizer o que separar, movimentar, guardar e inventariar), recebendo de volta o resultado da execução. Falta a estrutura para isso ser **isolado e opt-in**: não há provedor plugável de WMS, não há config de integração por empresa e não existe caminho de entrada autenticado — o `/api/v1` atual é interno do frontend e gateado por allowlist de devs.

## Escopo
- ✅ Faz parte:
  - **Saída plugável**: o push para o WMS/ERP deixa de ser soldado a um único destino e passa a ser fornecido por um provedor configurável; adicionar/trocar provedor não exige alterar o núcleo.
  - **Config por empresa**: endpoint, credenciais, liga/desliga e sentido (entrada/saída) da integração saem do código e viram configuração por empresa (e/ou env), sem deploy.
  - **Não-regressão do legado**: o push atual do galpão continua funcionando, virando "mais um" provedor registrado (strangler-fig).
  - **Entrada autenticada (receiver)**: uma superfície externa nova, separada do `/api/v1` interno, que recebe ordens do WMS autenticadas por empresa.
  - **Tradução de ordem → missão**: a ordem do WMS vira uma missão do Genesis do tipo certo (separação, movimentação, alocação/recebimento, inventário), reusando os domínios de missão que já existem.
  - **Mapeamento de identidade** WMS↔Genesis (produto e endereço) e tratamento determinístico de identidade desconhecida.
  - **Idempotência**: reenvio da mesma ordem (mesma referência externa) não duplica missão.
  - **Ciclo fechado**: o resultado da execução do operador é reportado de volta ao WMS, reusando a infra assíncrona de integração (movimentos + estados + cron com retry).
  - **Coexistência e opt-in**: missões nativas (pedidos/romaneios) intactas; missões de origem WMS marcadas como tal; empresa sem WMS plugado não percebe diferença.
  - **Precedência de fonte da verdade**: o estoque oficial é do WMS; em conflito, o WMS prevalece e o Genesis não o sobrescreve.
- ❌ Fora de escopo:
  - Reescrever o app do operador ou a bipagem — a execução é a de hoje; muda só a *origem* da missão e o *report* de volta.
  - Apagar/migrar de imediato o push legado do galpão (segue vivo como provedor).
  - Transformar o Genesis num WMS / gerir estoque contábil — o WMS externo é a fonte da verdade.
  - Abrir o `/api/v1` interno para o WMS — a entrada é superfície própria com auth própria.
  - Definir o schema final do contrato, o mecanismo exato de auth, o esquema de armazenamento da idempotência e o mecanismo de reconciliação de conflito — isso é Tech Lead.
  - A feature `separacao-por-endereco` em si (é um **consumidor** desta camada, não esta camada); ver Dúvidas em aberto.

## User stories

### US1 — TI/Gestor pluga e configura o WMS da empresa
**Como** TI/gestor da empresa **eu quero** configurar qual WMS está plugado (endpoint, credenciais, liga/desliga, sentido) **para** ativar a integração na minha empresa sem depender de deploy.

**Critérios de aceite:**
- AC1.1: A integração é **opt-in por empresa**: existe uma configuração por empresa que define se há WMS plugado e qual provedor.
- AC1.2: Endpoint e credenciais do WMS são lidos da configuração (por empresa e/ou env), **nunca** chumbados no código.
- AC1.3: Alterar destino/credencial do push de uma empresa não exige novo deploy de código.
- AC1.4: É possível ligar/desligar a integração (entrada e saída) por empresa; com ela desligada, nada de origem WMS é criado e o operador não vê diferença.
- AC1.5: Empresa sem WMS configurado segue operando exatamente como hoje (nenhum efeito colateral).

### US2 — Saída deixa de ser soldada (provedor plugável, sem regressão)
**Como** time Genesis **eu quero** que o push de saída seja fornecido por um provedor configurável **para** trocar/adicionar WMS sem reescrever o núcleo.
**Critérios de aceite:**
- AC2.1: O push existente para o galpão passa a ser **um provedor entre vários**, selecionado pela configuração da empresa.
- AC2.2: Adicionar um novo provedor de WMS não exige editar o código do núcleo (apenas registrar/configurar o novo provedor).
- AC2.3: Para uma empresa configurada com o provedor legado, os payloads e o destino entregues continuam equivalentes aos de hoje (baseline de não-regressão passa).
- AC2.4: O report de saída continua usando a infra assíncrona existente (movimentos + estados de integração ERP/WMS + cron), com retry idempotente e estados independentes.
- AC2.5: Falha de integração de saída fica registrada/visível e entra em retry; não some silenciosamente.

### US3 — WMS comanda o Genesis via webhook autenticado
**Como** WMS externo (sistema) **eu quero** enviar ordens ao Genesis por um webhook autenticado por empresa **para** mandar o que separar/movimentar/guardar/inventariar.
**Critérios de aceite:**
- AC3.1: Existe uma superfície de entrada **externa**, separada do `/api/v1` interno, que recebe ordens do WMS.
- AC3.2: Toda ordem é autenticada por empresa; ordem sem credencial válida é **rejeitada** e não cria missão.
- AC3.3: A resposta ao WMS indica de forma inequívoca aceite, rejeição (com motivo) ou já-processada (idempotente).
- AC3.4: A ordem aceita é criada sob a **empresa/tenant correta**; o cruzamento de dados com controles de tenant distintos não pode gerar missão no tenant errado nem falhar silenciosamente.
- AC3.5: O contrato de entrada é versionado, de modo que uma mudança futura de formato não quebre integrações já ativas.

### US4 — Ordem do WMS vira missão do tipo certo
**Como** operador de armazém **eu quero** que a ordem do WMS apareça como uma missão executável no app de hoje **para** executar sem mudança de UX.
**Critérios de aceite:**
- AC4.1: Uma ordem aceita gera a missão correspondente (com seus itens) do tipo adequado: separação/picking, movimentação interna, alocação/recebimento ou inventário/contagem.
- AC4.2: A missão de origem WMS é executada na mesma interface/fluxo do operador de hoje, sem alteração de UX.
- AC4.3: O tipo de ordem do WMS é mapeado para o tipo de missão/operação correspondente do Genesis de forma determinística (mapa explícito, definido no techplan).
- AC4.4: Uma ordem de tipo não suportado pelo release é rejeitada com motivo claro (não cria missão "meio pronta").

### US5 — Mapeamento de identidade WMS↔Genesis
**Como** WMS externo **eu quero** referenciar produtos e endereços pelos meus identificadores **para** que o Genesis crie a missão sobre os itens corretos.
**Critérios de aceite:**
- AC5.1: Produto enviado pelo WMS é resolvido para o produto do Genesis via `importacao_id` (= `codigomd5` do ERP legado).
- AC5.2: Endereço enviado pelo WMS é resolvido via `wms_localizacoes.codigo`.
- AC5.3: Ordem que referencia produto ou endereço **desconhecido** no Genesis tem tratamento determinístico (rejeição ou quarentena com motivo) — nunca cria missão silenciosamente inconsistente. O mecanismo (rejeitar vs quarentenar) é definido no techplan.
- AC5.4: A divergência de namespace de endereço (legado `codigo_enderecamento` vs `wms_localizacoes`) é resolvida na tradução, sem vazar para o operador.

### US6 — Idempotência (o WMS pode reenviar)
**Como** WMS externo **eu quero** poder reenviar uma ordem sem efeito duplicado **para** tolerar reentregas/timeouts.
**Critérios de aceite:**
- AC6.1: Cada ordem carrega uma referência externa única do WMS, usada como chave de deduplicação.
- AC6.2: Reenvio de uma ordem com a mesma referência externa **não cria** uma segunda missão; a resposta é um aceite idempotente.
- AC6.3: O resultado da deduplicação é observável (a ordem é reconhecida como já processada, não silenciosamente ignorada).

### US7 — Ciclo fechado: resultado de volta ao WMS
**Como** WMS externo **eu quero** receber o resultado da execução **para** manter o estoque sincronizado e fechar a ordem.
**Critérios de aceite:**
- AC7.1: Concluída/avançada a execução pelo operador, o resultado é enviado de volta ao WMS (início/conclusão/baixa/divergência conforme o contrato de report).
- AC7.2: O report de volta reusa o pipeline assíncrono existente, com estado de integração rastreável por evento e retry idempotente.
- AC7.3: A ordem do WMS tem um estado de ciclo observável (recebida → em execução → concluída/reportada).
- AC7.4: Falha no report de volta não se perde: fica visível e é reprocessada.

### US8 — Coexistência e marcação de origem
**Como** gestor logístico **eu quero** que as missões nativas e as de origem WMS convivam **para** plugar o WMS sem derrubar a operação atual.
**Critérios de aceite:**
- AC8.1: Missões nativas (pedidos/romaneios) continuam sendo criadas e executadas sem alteração de comportamento.
- AC8.2: A missão de origem WMS é **distinguível** das nativas (marcada com origem distinta).
- AC8.3: As duas origens coexistem para a mesma empresa sem interferência mútua.
- AC8.4: A baseline de regressão das missões nativas e do push legado continua passando.

### US9 — Precedência de fonte da verdade
**Como** gestor logístico **eu quero** que o WMS seja o dono do estoque **para** evitar que o saldo local do Genesis conflite com o oficial.
**Critérios de aceite:**
- AC9.1: Em divergência de estoque/estado entre o dado local do Genesis e o do WMS, o **WMS prevalece** como oficial.
- AC9.2: O Genesis não sobrescreve o estado do WMS com seu saldo local; o papel do Genesis é registrar a execução e reportar.
- AC9.3: Onde hoje o fluxo local calcula "saldo local" (ex.: alocação), com WMS plugado fica explícito que a referência oficial é o WMS. O mecanismo de reconciliação é definido no techplan.

## Personas envolvidas
| Persona | Papel | Ponto de contato com a feature |
|---|---|---|
| WMS externo (sistema) | Fonte da verdade do estoque; emite ordens e consome resultados | Webhook de entrada (envia ordens) + report de saída (recebe resultados) |
| Operador (separador/conferente) | Executa as tarefas, agora também as originadas pelo WMS | App de missões de hoje (sem mudança de UX) |
| Gestor / TI da empresa | Configura o WMS plugado por empresa (endpoint, credenciais, liga/desliga, sentido) | Configuração de integração por empresa |
| Time Genesis | Mantém o núcleo agnóstico ao WMS específico; adiciona/troca provedor | Camada de provedor plugável + config |

## Dados / entidades-chave (alto nível)
- **Configuração de integração WMS por empresa** — qual provedor, endpoint, credenciais, liga/desliga, sentido.
- **Provedor de WMS** — abstração do destino de saída (o legado do galpão é um deles).
- **Ordem do WMS** — comando recebido (tipo, referência externa, produtos/endereços/quantidades, prioridade).
- **Missão de origem WMS** — missão logística criada a partir de uma ordem, marcada com origem e vinculada à referência externa.
- **Movimento WMS / estados de integração** — registro append-only + estados (`integracao_erp` / `integracao_wms`) já existentes, reusados para o report de saída.
- **Identidade**: produto via `importacao_id` (= `codigomd5`), endereço via `wms_localizacoes.codigo`.

## Regras de negócio
1. A integração é **opt-in por empresa**; empresa sem WMS plugado não percebe nenhuma mudança.
2. Missões nativas (pedidos/romaneios) continuam funcionando sem alteração.
3. Missão originada pelo WMS é marcada com **origem distinta** e vinculada à referência externa da ordem.
4. O push legado do galpão continua funcionando como **um provedor entre vários** (strangler-fig), sem regredir.
5. Adicionar ou trocar o provedor de WMS **não** exige alterar o núcleo.
6. Endpoint e credenciais do WMS vivem em configuração (por empresa e/ou env), **nunca** chumbados.
7. A superfície de entrada é **externa, autenticada por empresa e separada** do `/api/v1` interno.
8. Toda ordem recebida é autenticada; ordem não autenticada é rejeitada e não cria missão.
9. Ordens são **idempotentes**: reenvio da mesma referência externa não duplica missão.
10. Identidade de produto mapeia por `importacao_id` (= `codigomd5`); endereço por `wms_localizacoes.codigo`.
11. Ordem com produto/endereço desconhecido tem tratamento **determinístico** (rejeita ou quarentena com motivo), nunca cria missão silenciosamente inconsistente.
12. O WMS é a **fonte da verdade** do estoque; em conflito, o estado do WMS prevalece; o Genesis é execução, não gestão de estoque.
13. O resultado da execução é **reportado de volta** ao WMS (ciclo fechado).
14. O report de saída **reusa** a infra assíncrona existente (movimentos + estados de integração + cron), com retry idempotente e estados independentes.
15. O cruzamento entre tabelas com controles de tenant distintos resolve o **mesmo tenant**; erro silencioso de escopo é inaceitável.

## Métricas de sucesso
- Empresas com WMS plugado (opt-in) operando sem incidentes nas demais empresas.
- % de ordens do WMS traduzidas em missão com sucesso (vs rejeitadas/em quarentena), com motivo registrado para as que falham.
- Zero duplicação de missão por reenvio (idempotência efetiva sob reentrega).
- Zero regressão do push legado do galpão e das missões nativas (baselines passam).
- % de execuções com resultado reportado de volta ao WMS confirmado (sem pendência/falha residual).
- Tempo entre ordem recebida e missão disponível ao operador dentro de um alvo acordado (threshold a definir).

## Dúvidas em aberto
- [ ] **Faseamento do release (decisão de produto):** entregar a visão completa de uma vez ou fatiar (1º saída plugável → 2º receiver de entrada → 3º por tipo de ordem)? *Recomendação: fatiar começando pela saída plugável + config por empresa, depois o receiver de entrada. Definir com o stakeholder.*
- [ ] **Quais tipos de ordem entram primeiro (decisão de produto):** os quatro (separação, movimentação, alocação/recebimento, inventário) na primeira leva da entrada, ou começar por um (qual)?
- [ ] **Atualização/cancelamento de ordem (decisão de produto):** o WMS poder cancelar/repriorizar uma ordem já criada entra nesta fase, ou só criação + dedup de reenvio?
- [ ] **Relação com `separacao-por-endereco` (decisão de produto):** essa feature é consumidora desta camada (a ordem de separação do WMS já traz o endereço). Sai junto ou depois? Hoje não há PRD para ela no repo.
- [ ] **Eventos do report de volta (produto):** quais eventos o WMS espera (início, conclusão por item, baixa, divergência) e por qual sentido (webhook reverso vs polling do WMS)?
- [ ] **Tratamento de identidade desconhecida (produto + TL):** rejeitar a ordem inteira, quarentenar e notificar, ou criar parcial? Confirmar também se `importacao_id`=`codigomd5` cobre todos os casos.
- [ ] **Conflito de fonte da verdade (produto + TL):** quando o WMS diverge do estado local, além de "WMS ganha", como reconciliar o que o Genesis já registrou localmente?
- [ ] **Migração do legado (produto):** quando/como aposentar o push direto do galpão depois do provedor abstrato no ar.
- [ ] **Mecanismo de auth do webhook (técnico — TL):** token estático por empresa, HMAC assinado, mTLS, rotação de credencial e proteção a replay.
- [ ] **Schema e versionamento do contrato (técnico — TL):** envelope comum, campos por tipo de ordem, formato do report.
- [ ] **Onde guardar a chave de idempotência e o estado da ordem (técnico — TL).**
