# Implementação backend

## Arquivos criados

- `componentes/Constelacoes/Logistica/MissaoLogistica/Strategies/Execucao/SeparacaoWmsStrategy.php` — variante `SEPARACAO:WMS`: ordena a fila puramente pelo código do endereço (natural sort), itens `legado` sempre ao fim (agrupados, não intercalados), zero I/O novo (lê só o snapshot já carregado no item).
- `componentes/Missoes/Logistica/sinais/AlocacaoWms/Laboratorios/ResolverEnderecoProdutoLaboratorio.php` — batch `produto_id → endereço WMS confirmado`, wrap fino de `WmsMovimentoSonda::mapaLocalizacaoConfirmadaPorProduto()`; expõe `normalizar()` (trim+upper) como ponto único de normalização reusado na validação da execução.
- `componentes/Missoes/Logistica/sinais/App/Laboratorios/ValidarEnderecoSeparacao.php` — Laboratório satélite do `App`: valida o bip de endereço no card de um item `origem=wms` (endereço ativo no catálogo + match com o snapshot do item), registra `ENDERECO_VALIDADO` no extrato em caso de match; NÃO bloqueia conclusão do item (gate de front + auditoria, não trava a fila nem o vínculo missão-operador).

## Arquivos alterados

- `componentes/Constelacoes/Logistica/MissaoLogistica/Strategies/Execucao/ExecucaoStrategyFactory.php` — registrou `'SEPARACAO:WMS' => SeparacaoWmsStrategy::class`; em `createFromMissao()`, quando o caller não passa `$ordenacao` e o tipo é `SEPARACAO`, deriva a variante de `missao.configuracoes.variante_separacao` (novo método privado `resolverVarianteSeparacao()`) — isso é o que faz o modo escolhido na criação dirigir o hot path (`ordenarCandidatosStrategy`) sem tocar nos callers.
- `componentes/Missoes/Logistica/sinais/Designacoes/Laboratorios/GestaoMissoesBase.php` — adicionado hook `protected function enriquecerLocalizacaoWms(array $itens, array $data): array` (default no-op) chamado em `processarEnvio()` logo após `enriquecerCodigosBarras()` (passo 5.1.5), antes do `getConfiguracoesAdicionais()`.
- `componentes/Missoes/Logistica/sinais/Designacoes/Laboratorios/GestaoMissoesSeparacao.php` — sobrescreveu `enriquecerLocalizacaoWms()` (só age quando `$data['variante_separacao'] === 'WMS'`; resolve endereço via `ResolverEnderecoProdutoLaboratorio`, grava `dados_adicionais.endereco_wms` / `dados_adicionais.origem_localizacao` em cada item, computa cobertura); `getConfiguracoesAdicionais()` agora grava `configuracoes['variante_separacao'] = 'WMS'` quando o gestor escolhe o modo (ausente = comportamento atual/andar); novo override de `processarEnvio()` que anexa `cobertura_wms` (`total`, `sem_endereco`, `itens_sem_endereco`) à resposta de criação quando a variante WMS rodou.
- `componentes/Constelacoes/Logistica/MissaoLogistica/Enums/AcaoExtrato.php` — novo case `ENDERECO_VALIDADO`, com entradas em `descricao()`, `icon()` e `categoria()` (os três `match()` são exaustivos — cobertos para não quebrar em runtime).
- `componentes/Missoes/Logistica/sinais/App/App.php` — novo ramo em `start()` para `sateliteGxData === 'ValidarEnderecoSeparacao'`, instancia `App\Laboratorios\ValidarEnderecoSeparacao` e devolve via `galaxiaResposta(...)->atualizarPagina()->feito()`, espelhando exatamente os ramos `SolicitarAcessoRetira` / `SolicitarAcessoPesagem` já existentes.

## Desvios do techplan

Nenhum desvio de arquitetura. Um ponto que o techplan deixou implícito e eu decidi de forma consistente com o padrão do repo:

- O techplan pede para "expor a contagem de cobertura na resposta de criação" mas não especifica o mecanismo exato. `processarEnvio()` da Base termina delegando a mensagem de sucesso para `$strategy->getSuccessMessage(...)` (Message::render()), então não havia um hook dedicado para anexar dados extras. Resolvi com o menor desvio possível: `GestaoMissoesSeparacao` sobrescreve `processarEnvio()`, chama `parent::processarEnvio($data)` e só faz `array_merge` de `cobertura_wms` na resposta quando a variante WMS rodou (`$this->coberturaWms !== null`). Não duplica nenhuma lógica da Base.
- Não escrevi testes (TDD unit/integration listados no techplan) — por escopo do meu papel, ficam para o Tester. Só rodei `php -l` em todos os arquivos tocados/criados.
- Não toquei em nenhum arquivo de `forms/`, `html/`, JS ou `linguagens/*.json` — são explicitamente frontend/i18n (fora do meu escopo). Isso inclui os itens do techplan: `App/html/separacao/endereco_wms.js`, `endereco_wms.php`, alterações em `separacao/item.php`/`item.js`, `Designacoes/html/designar_unificado.php`, `Designacoes/forms/enviar_unificado.php` e as chaves i18n novas em `linguagens/Distribuicao/{pt-br,en,es}.json`. Ficam para o dev frontend, com o contrato descrito abaixo.

## Comandos de verificação executados

- `php -l componentes/Constelacoes/Logistica/MissaoLogistica/Strategies/Execucao/ExecucaoStrategyFactory.php` → OK
- `php -l componentes/Constelacoes/Logistica/MissaoLogistica/Strategies/Execucao/SeparacaoWmsStrategy.php` → OK
- `php -l componentes/Missoes/Logistica/sinais/AlocacaoWms/Laboratorios/ResolverEnderecoProdutoLaboratorio.php` → OK
- `php -l componentes/Missoes/Logistica/sinais/Designacoes/Laboratorios/GestaoMissoesBase.php` → OK
- `php -l componentes/Missoes/Logistica/sinais/Designacoes/Laboratorios/GestaoMissoesSeparacao.php` → OK
- `php -l componentes/Constelacoes/Logistica/MissaoLogistica/Enums/AcaoExtrato.php` → OK
- `php -l componentes/Missoes/Logistica/sinais/App/Laboratorios/ValidarEnderecoSeparacao.php` → OK
- `php -l componentes/Missoes/Logistica/sinais/App/App.php` → OK

## Próximos passos para o frontend

### 1. Seletor de modo na criação (Designacoes)

Em `designar_unificado.php` + `enviar_unificado.php`, adicionar um campo (ex.: radio/select) que submete `variante_separacao` no payload de `enviarMissoes` para o satélite `GestaoMissoesSeparacao`:
- Ausente ou qualquer valor diferente de `'WMS'` → comportamento atual (andar), sem diferença nenhuma.
- `variante_separacao = 'WMS'` (string literal, exatamente essa) → ativa a variante.

A resposta da criação (`GestaoMissoesSeparacao::processarEnvio`) inclui, quando a variante rodou, a chave extra:

```json
{
  "notification_class": "success",
  "status": "ok",
  "notification_html": "...",
  "notification_title": "...",
  "cobertura_wms": {
    "total": 42,
    "sem_endereco": 7,
    "itens_sem_endereco": [
      { "produto_id": 123, "produto_nome": "Produto X" }
    ]
  }
}
```
Use isso para o aviso de cobertura (US3) — é só informativo, nunca bloqueia.

### 2. Card do item (App/html/separacao)

Cada item de missão em `dados_adicionais` (já vem decodificado no card server-side, mas cuidado: pode chegar como string JSON dependendo do ponto de leitura — ver nota `sonda_configuracoes_json_string` na memória) carrega, quando a missão é `variante_separacao=WMS`:
- `dados_adicionais.origem_localizacao` → `'wms'` ou `'legado'`.
- `dados_adicionais.endereco_wms` → código do endereço (ex.: `A03-P2-N1`), presente só quando `origem_localizacao === 'wms'`.
- Fallback legado continua em `dados_adicionais.localizacao` (mesmo campo de sempre, `codigo_enderecamento`).

Emitir `data-origem-localizacao` e `data-endereco-wms` no HTML do card para a state machine JS ler.

### 3. State machine de bipagem (item.js)

Espelhar o roteamento por ESTADO do card de `alocacaowms/item.js` (não por prefixo do código):
- Item `origem=legado` → não precisa bipar endereço; bip vai direto para a mecânica atual de produto.
- Item `origem=wms`, endereço ainda não confirmado → o bip do scanner dispara a chamada abaixo. Sucesso → muda o estado do card para "aguardando produto" e libera a bipagem normal (mecânica de códigos/fator intocada). Erro → feedback visual, mantém travado no bip de endereço.

### 4. Endpoint de validação de endereço

Dispatch via Galaxia (mesmo padrão de `SolicitarAcessoRetira`/`SolicitarAcessoPesagem`, ver `App/html/estrutura.php` para o padrão de `acaoElemento`/`dadosG`):

```
setVersao(2)->setConstelacao('Logistica')->setEstrela('App')
  ->setSatelite('ValidarEnderecoSeparacao')->setOpcao('validar')
```

Payload esperado:
```json
{ "item_id": 123, "codigo": "A03-P2-N1" }
```
(`codigo` aceita também a chave `endereco` como alias.)

Resposta (`Message::render()` + campos extras, mesmo padrão de qualquer Laboratório do App):
```json
{
  "notification_class": "success" | "error",
  "status": "ok" | "no",
  "notification_html": "...",
  "notification_title": "...",
  "endereco_confirmado": true | false,
  "item_id": 123,
  "endereco_esperado": "A03-P2-N1"   // presente só em mismatch, para exibir no feedback de erro
}
```
Casos de erro possíveis: item não encontrado, código vazio, endereço inativo/inexistente no catálogo, mismatch com o endereço esperado do item. Itens `legado` (ou sem `endereco_wms` no snapshot) sempre retornam sucesso imediato (`endereco_confirmado: true`) — não é obrigatório chamar este endpoint para eles, mas é seguro chamar (não quebra).

### 5. i18n

Strings novas a embrulhar com `show()` (chave = md5 do texto PT literal, harvest automático):
- Badge/selo de origem (`WMS` vs `Legado`) no card.
- Mensagens de erro da bipagem de endereço ("Endereço inválido ou inativo.", "Endereço não confere com o esperado para este item.").
- Rótulo do seletor de modo na criação ("Endereço WMS" vs modo atual).
- Aviso de cobertura na criação (contagem de itens sem endereço WMS).

### 6. Não regressão

Nada muda para missões sem `variante_separacao` — a chave simplesmente não existe em `configuracoes`, o factory cai no mesmo default de sempre (`SeparacaoEnderecamentoStrategy`, por andar), e `GestaoMissoesSeparacao::enriquecerLocalizacaoWms()` retorna os itens inalterados (early-return).

## Correções pós-review (ciclo 1)

Aplicados os 3 majors da parte backend apontados em `06-review.md` (M1, M2, M3). Nenhum minor/nit foi tocado (fora de escopo deste ciclo).

### M1 — `ValidarEnderecoSeparacao` migrado de `Inteligencias\MissaoLogisticaItem` (V1) para Sonda V2

- **Criado** `componentes/Constelacoes/Logistica/MissaoLogistica/Sondas/MissaoLogisticaItemSonda.php` — Sonda V2 (`extends ModelInteligencia`) sobre `missao_logistica_itens`, mesmo `propeties` do model V1 legado (`customerControl: true`, `db: gx_goldie`), com um único método público `buscarPorId(int $itemId): ?object`. Não substitui o model V1 nos consumidores legados (`MissoesBipagem`, `MissoesExecucao`, `PedidosHandler`, `SeparacaoHandler`, `PesagemHandler` etc. permanecem intocados — fora de escopo) — só serve o consumo novo do Laboratório desta feature, alinhando com a memória `feedback_sondas_v2`.
- **Alterado** `componentes/Missoes/Logistica/sinais/App/Laboratorios/ValidarEnderecoSeparacao.php` — troca do `use ... Inteligencias\MissaoLogisticaItem` pelo `use ... Sondas\MissaoLogisticaItemSonda`; a leitura do item passou de `(new MissaoLogisticaItem())->find('id = :id', ...)->fetch()` para `(new MissaoLogisticaItemSonda())->buscarPorId($itemId)`. Comportamento idêntico (mesmo SQL/boundary de tenant) — só troca o wrapper.

### M2 — `App.php` não força mais reload no ramo `ValidarEnderecoSeparacao`

- **Alterado** `componentes/Missoes/Logistica/sinais/App/App.php` — removido `->atualizarPagina()` do ramo `sateliteGxData === 'ValidarEnderecoSeparacao'`; a resposta agora é só `galaxiaResposta($laboratorio->response)->feito()`, igual ao padrão de qualquer satélite que não quer `location.reload()`. Comentário adicionado explicando o porquê (este endpoint é chamado a cada bip de endereço — reload quebraria a UX de US5/AC5.3).
- Isso viabiliza o frontend (fora do meu escopo tocar) migrar de volta pro dispatcher padrão (`SendGalaxiaPost`/`dispararAcaoGalaxia`) em vez do `$.ajax` manual documentado em `endereco_wms.js:117-179` — mas essa migração do JS não foi feita aqui (é frontend, e o `06-review.md` trata como aceitável entrar com esse débito).

### M3 — `origem_localizacao`/`endereco_wms` expostos no topo do `jsItensData` (modo livre)

- **Alterado** `componentes/Constelacoes/Logistica/MissaoLogistica/Presenters/MissaoPresenter.php` — em `montarJsonFrontendBase()`, além de `dados_adicionais` (já presente por inteiro), passou a expor também no topo de cada item: `origem_localizacao` e `endereco_wms` (lidos do mesmo `$dadosAdicionais` já decodificado, `null` quando ausentes). +2 linhas, sem mudança de assinatura nem de comportamento para os demais Presenters que reusam este método (`CarregamentoPresenter`, `PesagemPresenter`, `RetornoPresenter`) — as chaves novas só ficam preenchidas quando o item realmente carrega esses campos no snapshot (missões `SEPARACAO:WMS`), caso contrário vêm `null` e são inofensivas.
- Isso resolve só o lado servidor do gap: o dado agora chega no `data-itens-json` consumido pelo modo livre (`separacao/item.js`/`SeparaçãoV2`). Ligar o `EnderecoWmsGate` a esse fluxo (o JS ainda lê hidden inputs `#data-origem-localizacao`/`#data-endereco-wms` renderizados só na fila única) é trabalho de frontend, não incluído neste ciclo — consistente com a instrução de só aplicar os majors do lado backend.

### Comandos de verificação executados (ciclo 1)

- `php -l componentes/Constelacoes/Logistica/MissaoLogistica/Sondas/MissaoLogisticaItemSonda.php` → OK
- `php -l componentes/Missoes/Logistica/sinais/App/Laboratorios/ValidarEnderecoSeparacao.php` → OK
- `php -l componentes/Missoes/Logistica/sinais/App/App.php` → OK
- `php -l componentes/Constelacoes/Logistica/MissaoLogistica/Presenters/MissaoPresenter.php` → OK
- `php componentes/Missoes/Logistica/sinais/App/Testes/Laboratorios/ValidarEnderecoSeparacaoTddTest.php` (integration existente, dev_gx_goldie) → 17/17 PASS, 0 FAIL (confirma que a troca para `MissaoLogisticaItemSonda` não quebrou nenhum caso: input inválido, item legado, endereço inativo, mismatch, match + extrato, alias `endereco`)
- `php componentes/Constelacoes/Logistica/MissaoLogistica/Testes/Presenters/CarregamentoPresenterCubagemTddTest.php` (unit existente, não exercita `MissaoPresenter` diretamente mas cobre lógica irmã do mesmo Presenter layer) → 14/14 PASS, 0 FAIL

### Desvios do techplan (ciclo 1)

Nenhum. As 3 correções seguem exatamente o "Como corrigir" de cada major no `06-review.md`. Não toquei em `IntegracaoWms/Tradutores`, `Inventariociclico`, nem em minors/nits (m1-m3, n1-n2) — fora do escopo pedido para este ciclo.

### Próximos passos para o frontend (ciclo 1)

- O endpoint `ValidarEnderecoSeparacao` (App/ValidarEnderecoSeparacao/validar) agora devolve JSON limpo sem `atualizarPagina`. Se/quando o frontend migrar do `$.ajax` manual em `endereco_wms.js` para o dispatcher padrão, a resposta não vai mais forçar reload — pode ler `endereco_confirmado` do retorno do próprio dispatcher (mesmo padrão de `RequestGalaxiaAct` já usado para `cobertura_wms`).
- `jsItensData[itemId]` (consumido em `separacao/item.js` via `data-itens-json`) agora tem `origem_localizacao` e `endereco_wms` no nível raiz de cada item (além de dentro de `dados_adicionais`, que já existia). Para ligar o gate no modo livre, falta só o JS: ler esses dois campos por item ao montar o card do `SeparaçãoV2` e reusar/adaptar `EnderecoWmsGate` (hoje acoplado aos hidden inputs `#data-origem-localizacao`/`#data-endereco-wms` da fila única).
