# Implementação backend — alocacao-wms

## Arquivos criados

### Enums
- `componentes/Missoes/Logistica/sinais/AlocacaoWms/Enums/StatusAlocacao.php` — enum `confirmada | cancelada` para estado de cada linha em `wms_alocacoes`
- `componentes/Missoes/Logistica/sinais/AlocacaoWms/Enums/OrigemLeitura.php` — enum `codigo_barras | manual` registrado por alocação (AC3.3)
- `componentes/Missoes/Logistica/sinais/AlocacaoWms/Enums/EstadoIntegracao.php` — enum `pendente | enviado | falha` para `integracao_erp` e `integracao_wms`

### Migrations
- `componentes/Missoes/Logistica/sinais/AlocacaoWms/Migrations/001_create_wms_localizacoes.sql` — tabela de referência de localizações WMS (catálogo consultado na bipagem)
- `componentes/Missoes/Logistica/sinais/AlocacaoWms/Migrations/002_create_wms_alocacoes.sql` — tabela de alocações 1:N produto↔localização com índices parciais de retry
- `componentes/Missoes/Logistica/sinais/AlocacaoWms/Migrations/003_seed_tipo_missao_alocacao_wms.sql` — seed `tipos_missao_logistica` id=13 `ALOCACAO_WMS`

### Sondas
- `componentes/Missoes/Logistica/sinais/AlocacaoWms/Sondas/WmsLocalizacaoSonda.php` — Sonda de leitura de `wms_localizacoes`; expõe `buscarPorCodigo()` e `existeAtiva()`; `customerControl=true`
- `componentes/Missoes/Logistica/sinais/AlocacaoWms/Sondas/WmsAlocacaoSonda.php` — Sonda de escrita/leitura de `wms_alocacoes`; expõe `registrarAlocacao()`, `listarPorMissao()`, `marcarIntegracaoErp/Wms()`, `buscarPendentesIntegracaoErp/Wms()`; `customerControl=true`

### Validator
- `componentes/Missoes/Logistica/sinais/AlocacaoWms/Validators/ConfirmarAlocacaoValidator.php` — valida AC4.1 (missão ativa) → AC4.2 (permissão operador) → AC4.3 (produto na missão) → AC4.4 (localização existe) → quantidade condicional (decisão #2)

### Laboratórios
- `componentes/Missoes/Logistica/sinais/AlocacaoWms/Laboratorios/ConfirmarAlocacaoLaboratorio.php` — orquestra Validator → WmsAlocacaoSonda::registrarAlocacao → concluir item → extrato; retorna resposta imediata (integração assíncrona)
- `componentes/Missoes/Logistica/sinais/AlocacaoWms/Laboratorios/ListarAlocacoesLaboratorio.php` — consulta itens da missão enriquecidos com última alocação; `listarHistoricoProduto()` para histórico 1:N

### Strategies de Execução
- `componentes/Constelacoes/Logistica/MissaoLogistica/Strategies/Execucao/AlocacaoWmsExecucaoStrategy.php` — ordenação livre (pendentes → localização WMS ASC → produto ASC); anota `quantidade_obrigatoria` nos itens

### Strategy de Lista
- `componentes/Missoes/Logistica/sinais/App/Strategies/Lista/AlocacaoWmsListaStrategy.php` — tema indigo, rota `alocacaowms`, estatísticas (Pendentes/Andamento/Concluídas/Total)

### Handler e Dispatcher
- `componentes/Missoes/Logistica/sinais/App/Handlers/AlocacaoWmsHandler.php` — handler fino (≤90 linhas efetivas); rotas `alocacaowms` (lista) e `alocacaowms/{id}` (tela de trabalho); Luz delegates `Luz_AlocacaoWmsLayout`, `Luz_AlocacaoWmsItemCard`
- `componentes/Missoes/Logistica/sinais/AlocacaoWms/AlocacaoWms.php` — dispatcher JSON do sinal; ações `confirmar | listar | historico`

### Hooks de integração assíncrona
- `componentes/Constelacoes/Logistica/MissaoLogistica/Strategies/ErpAlocacaoWmsHookStrategy.php` — extends HookSincronizadorBase; envia payload com `importacao_id` (AC5.4); atualiza `integracao_erp` via WmsAlocacaoSonda
- `componentes/Constelacoes/Logistica/MissaoLogistica/Strategies/WmsAlocacaoWmsHookStrategy.php` — extends HookSincronizadorBase; estado `integracao_wms` independente do ERP (AC5.1/5.2)
- `crons/wms_alocacao_integracao.php` — worker cron que varre `wms_alocacoes` com índices parciais de retry; processa filas ERP e WMS independentemente; frequência recomendada: a cada 2 minutos

### Views (placeholder — UI para o Dev Frontend)
- `componentes/Missoes/Logistica/sinais/App/html/alocacaowms/menu.php` — placeholder com documentação de variáveis disponíveis
- `componentes/Missoes/Logistica/sinais/App/html/alocacaowms/item.php` — placeholder; documenta variáveis da tela de bipagem e a ação `MissoesBipagem::alocarLocalizacao`
- `componentes/Missoes/Logistica/sinais/App/html/alocacaowms/lista.php` — placeholder; documenta variáveis da lista unificada

---

## Arquivos alterados

- `componentes/Constelacoes/Logistica/MissaoLogistica/Enums/TipoMissao.php` — adicionado `case ALOCACAO_WMS = 'ALOCACAO_WMS'` com id=13 em `name()`, `id()`, `label()`, `fromId()`
- `componentes/Constelacoes/Logistica/MissaoLogistica/Strategies/Execucao/ExecucaoStrategyFactory.php` — adicionada chave `'ALOCACAO_WMS' => AlocacaoWmsExecucaoStrategy::class`
- `componentes/Missoes/Logistica/sinais/App/Strategies/Lista/MissaoListaStrategyFactory.php` — adicionada chave `'ALOCACAO_WMS' => AlocacaoWmsListaStrategy::class`
- `componentes/Constelacoes/Logistica/MissaoLogistica/Satelites/MissoesBipagem.php` — adicionado `'alocarLocalizacao' => 'alocarLocalizacao'` no mapa de métodos + método privado `alocarLocalizacao()` delegando ao `ConfirmarAlocacaoLaboratorio`; import do Laboratorio adicionado
- `componentes/Missoes/Logistica/sinais/App/App.php` — registrado `AlocacaoWmsHandler` em `$handlerClasses` e `$routeMap` (`alocacaowms`, `alocacaowms/lista`); adicionado branch `alocacaowms` em `start()`; delegates `alocacaowms()`, `AlocacaoWms_item()`, `Luz_AlocacaoWmsLayout()`, `Luz_AlocacaoWmsItemCard()`; import `AlocacaoWmsHandler`
- `componentes/Missoes/Logistica/sinais/App/Handlers/BaseHandler.php` — adicionado `TipoMissao::ALOCACAO_WMS => "missao/logistica/app/alocacaowms"` no `match` de redirecionamento de `finalizarMissao`

---

## Correções de bugs (pós-entrega — 2026-06-08)

### BUG #1 — Migration 002: colunas do framework adicionadas

`Migrations/002_create_wms_alocacoes.sql` — adicionadas as colunas `usuario_cadastro VARCHAR(100)`, `data_atualizacao TIMESTAMP`, `usuario_atualizacao VARCHAR(100)`, `block_data TIMESTAMP`, `block_quem VARCHAR(100)` que `ModelInteligencia::create()` injeta automaticamente.

No banco `dev_gx_goldie` a tabela já existia sem essas colunas; aplicado `ALTER TABLE wms_alocacoes ADD COLUMN ...` para cada uma via script de fixup.

### BUG #2 — `ConfirmarAlocacaoValidator::operadorTemPermissao()` TypeError corrigido

`Validators/ConfirmarAlocacaoValidator.php` linha 151 — trocado `array_map('intval', $operadores)` por `array_map('intval', $operadores ?? [])`. Evita `TypeError` quando `$missao->operadores` não existe (coluna ausente na tabela retorna `null` após `json_decode`).

### BUG #3 — Migration 003: coluna e id corrigidos

`Migrations/003_seed_tipo_missao_alocacao_wms.sql` — corrigido para usar `codigo` (não `nome`), removida coluna `ativo` (inexistente na tabela), e alterado de `id=13` para `id=14` porque `id=13` já pertence a `CONFERENCIA_ALOCACAO` no banco.

`componentes/Constelacoes/Logistica/MissaoLogistica/Enums/TipoMissao.php` — `id()` e `fromId()` atualizados de 13 para 14 para `ALOCACAO_WMS`.

Testes atualizados para refletir id=14:
- `tests/integration/AlocacaoWms/MigrationSchemaTest.php` — assertion `id() == 14`
- `componentes/Missoes/Logistica/sinais/AlocacaoWms/Testes/Enums/StatusAlocacaoTddTest.php` — assertions `id() == 14` e `fromId(14)`

### Resultado após correções

| Suíte | Antes | Depois |
|---|---|---|
| StatusAlocacaoTddTest | 32/34 (2 FAIL) | 34/34 |
| MigrationSchemaTest | 39/39 | 39/39 |
| WmsLocalizacaoSondaTddTest | 9/9 | 9/9 |
| WmsAlocacaoSondaTddTest | 17/17 | 17/17 |
| ConfirmarAlocacaoValidatorTddTest | 20/21 (1 FAIL) | 21/21 |
| ConfirmarAlocacaoLaboratorioTddTest | 10/11 (1 FAIL) | 11/11 |
| AlocacaoFluxoTest | 27/27 | 27/27 |
| **Total** | **154/158** | **158/158** |

---

## Correções de review (pós-entrega — 2026-06-08, round 2)

### BLOCKER B1 — Cron multi-tenant

`crons/wms_alocacao_integracao.php` — reescrito seguindo o padrão de `dashboard_expedicao_diario.php`:
1. Bootstrap CLI (`php_sapi_name() !== 'cli'`, `error_reporting`, `$_SERVER['HTTP_HOST']`, `autoload.php`).
2. Query SQL direta (leitura) para `SELECT DISTINCT block FROM wms_alocacoes WHERE ... pendente/falha`.
3. Loop por tenant: configura `$_SESSION['galaxiaUserInfo']` e `$_SESSION['dashboard']['galaxiaUserInfo']` com `customerData->id = $block` antes de instanciar a `WmsAlocacaoSonda`. Isso resolve `customerControl=true` corretamente sem sessão de usuário real.
4. Falha de um tenant não interrompe os demais — `try/catch` isolado por bloco.

### BLOCKER B2 — View Luz ausente

`componentes/Missoes/Logistica/sinais/App/html/luz/alocacaowms/item_card.php` — criado. Renderiza card compacto de item WMS (produto + localização + status concluído/pendente). Espelha `html/luz/inventario/item_card.php`.

### MAJOR M1 — Progressão automática de status (AC6.1/6.2/6.3)

`componentes/Missoes/Logistica/sinais/AlocacaoWms/Laboratorios/ConfirmarAlocacaoLaboratorio.php`:
- Adicionado método privado `atualizarStatusMissaoAposConfirmacao()` que:
  - Conta itens pendentes com `MissaoLogisticaItem::findAny()` (mesma lógica do `finalizar()` de `MissoesStatus`)
  - `0 pendentes` → transita para `CONCLUIDA` (AC6.3) — grava `finalizado_em` + `finalizada_por`
  - `PENDENTE com pendentes` → transita para `EM_ANDAMENTO` (AC6.1) — grava `iniciado_em`
  - Usa `StatusMissao::podeTransicionarPara()` antes de gravar (sem `MissoesStatus` Satélite, que requer `$galaxia`)
- `componentes/Constelacoes/Logistica/MissaoLogistica/Satelites/MissoesBipagem.php` — removida chamada redundante `garantirMissaoIniciada` após delegação ao Laboratório; atualizado docblock para refletir que o lab cobre AC6.1/6.2/6.3.

### MAJOR M2 — Contaminação de escopo

Restaurados para o estado de `dev_team` via `git checkout dev_team -- <arquivo>`:
- `componentes/Missoes/Logistica/sinais/Central/html/central.php`
- `componentes/Missoes/Logistica/sinais/Edicaorota/Sondas/EdicaoRotaSonda.php`
- `componentes/Missoes/Logistica/sinais/Edicaorota/html/_layout.php`
- `componentes/Missoes/Logistica/sinais/Edicaorota/html/editar_rota.php`

As mudanças de Edicaorota (countdown/podeAprovar) não pertencem a esta feature.

### MAJOR M3 — i18n

Verificado: todas as 19 chaves de `item.php` já estavam presentes com `traducao` preenchida em `linguagens/pt-br.json`, `linguagens/en.json` e `linguagens/es.json`. M3 estava resolvido na entrega anterior — confirmado por varredura manual de cada `show()` em `item.php` contra os 3 arquivos.

### MAJOR M4 — Criação de missão ALOCACAO_WMS no Central

Implementado passo 12 do techplan:

**Novos arquivos:**
- `componentes/Missoes/Logistica/sinais/Central/Laboratorios/CriarAlocacaoWmsLaboratorio.php` — laboratório com `buscarOperadores()` e `criar()` que chama `MissoesPersistencia::criacao` com `tipo=ALOCACAO_WMS` e `configuracoes={'quantidade_obrigatoria':bool}`
- `componentes/Missoes/Logistica/sinais/Central/forms/criar_alocacao_wms.php` — form inline com: descrição, select de operador (opcional/FILA_ABERTA), checkbox `quantidade_obrigatoria`

**Arquivos alterados:**
- `componentes/Missoes/Logistica/sinais/Central/Central.php` — adicionado método `criarAlocacaoWms()`, registro em `start()` (`sinalGxData === 'criarAlocacaoWms'`), e handler no `Observatorio` para pós-submit
- `componentes/Missoes/Logistica/sinais/Central/html/central.php` — adicionado card "Alocação WMS" (tema indigo `#6366f1`) no modal `modal-escolher-designacao`; default `habilitadosDesignacao['alocacao_wms'] = 1`

---

## Desvios do techplan

**Passo 10 (Views/JS/i18n)**: O techplan instrui executar passos 1–9 e 11. As views foram criadas como placeholders documentados (variáveis disponíveis + contrato da ação `alocarLocalizacao`) para o Dev Frontend. Conteúdo HTML/JS e chaves de i18n são responsabilidade do Dev Frontend conforme instrução do orquestrador.

**`RegistraExtratoTrait` no Laboratório**: O trait é usado por Satélites (MissoesBipagem), não por Laboratórios no padrão canônico. A alternativa seria não registrar extrato no Laboratório e deixá-lo no Satélite. Optei por manter o extrato no Laboratório (coesão — quem confirma registra) já que o trait é self-contained (não precisa de `$this->galaxia`). Se o time preferir mover o extrato para o Satélite, é um refactor de 3 linhas.

**Worker cron**: O techplan §Riscos item 1 menciona decidir o runner com o time de infra. Implementado como (b) — cron periódico em `crons/wms_alocacao_integracao.php`. A opção (a) (`register_shutdown_function`) pode ser adicionada como otimização futura sem alterar o worker.

---

## Comandos de verificação executados

Todos os 28 arquivos PHP passaram em `php -l` (21 originais + 7 de correções de review):

- `php -l componentes/Missoes/Logistica/sinais/AlocacaoWms/Enums/StatusAlocacao.php` → OK
- `php -l componentes/Missoes/Logistica/sinais/AlocacaoWms/Enums/OrigemLeitura.php` → OK
- `php -l componentes/Missoes/Logistica/sinais/AlocacaoWms/Enums/EstadoIntegracao.php` → OK
- `php -l componentes/Constelacoes/Logistica/MissaoLogistica/Enums/TipoMissao.php` → OK
- `php -l componentes/Missoes/Logistica/sinais/AlocacaoWms/Sondas/WmsLocalizacaoSonda.php` → OK
- `php -l componentes/Missoes/Logistica/sinais/AlocacaoWms/Sondas/WmsAlocacaoSonda.php` → OK
- `php -l componentes/Missoes/Logistica/sinais/AlocacaoWms/Validators/ConfirmarAlocacaoValidator.php` → OK
- `php -l componentes/Missoes/Logistica/sinais/AlocacaoWms/Laboratorios/ConfirmarAlocacaoLaboratorio.php` → OK
- `php -l componentes/Missoes/Logistica/sinais/AlocacaoWms/Laboratorios/ListarAlocacoesLaboratorio.php` → OK
- `php -l componentes/Constelacoes/Logistica/MissaoLogistica/Strategies/Execucao/AlocacaoWmsExecucaoStrategy.php` → OK
- `php -l componentes/Constelacoes/Logistica/MissaoLogistica/Strategies/Execucao/ExecucaoStrategyFactory.php` → OK
- `php -l componentes/Missoes/Logistica/sinais/App/Strategies/Lista/AlocacaoWmsListaStrategy.php` → OK
- `php -l componentes/Missoes/Logistica/sinais/App/Strategies/Lista/MissaoListaStrategyFactory.php` → OK
- `php -l componentes/Constelacoes/Logistica/MissaoLogistica/Satelites/MissoesBipagem.php` → OK
- `php -l componentes/Missoes/Logistica/sinais/App/Handlers/AlocacaoWmsHandler.php` → OK
- `php -l componentes/Missoes/Logistica/sinais/AlocacaoWms/AlocacaoWms.php` → OK
- `php -l componentes/Missoes/Logistica/sinais/App/App.php` → OK
- `php -l componentes/Missoes/Logistica/sinais/App/Handlers/BaseHandler.php` → OK
- `php -l componentes/Constelacoes/Logistica/MissaoLogistica/Strategies/ErpAlocacaoWmsHookStrategy.php` → OK
- `php -l componentes/Constelacoes/Logistica/MissaoLogistica/Strategies/WmsAlocacaoWmsHookStrategy.php` → OK
- `php -l crons/wms_alocacao_integracao.php` → OK

**Correções de review (round 2):**
- `php -l crons/wms_alocacao_integracao.php` → OK (reescrito)
- `php -l componentes/Missoes/Logistica/sinais/App/html/luz/alocacaowms/item_card.php` → OK (novo)
- `php -l componentes/Missoes/Logistica/sinais/AlocacaoWms/Laboratorios/ConfirmarAlocacaoLaboratorio.php` → OK (M1)
- `php -l componentes/Constelacoes/Logistica/MissaoLogistica/Satelites/MissoesBipagem.php` → OK (M1)
- `php -l componentes/Missoes/Logistica/sinais/Central/Central.php` → OK (M4)
- `php -l componentes/Missoes/Logistica/sinais/Central/Laboratorios/CriarAlocacaoWmsLaboratorio.php` → OK (M4 novo)
- `php -l componentes/Missoes/Logistica/sinais/Central/html/central.php` → OK (M4)
- `php -l componentes/Missoes/Logistica/sinais/Central/forms/criar_alocacao_wms.php` → OK (M4 novo)

---

## Próximos passos para o frontend

### Rotas disponíveis no App

| Rota | Handler | Descrição |
|---|---|---|
| `/missao/logistica/app/alocacaowms` | `AlocacaoWmsHandler::alocacaowms` | Lista de missões ativas |
| `/missao/logistica/app/alocacaowms/{id}` | `AlocacaoWmsHandler::AlocacaoWms_item` | Tela de execução da missão |

### Ação de bipagem (confirmação de alocação)

Via `MissoesBipagem` (padrão Galaxia — mesmo mecanismo que `biparItem`):

```
Satélite: MissoesBipagem
Opção:    alocarLocalizacao
POST campos:
  missao_id         int       (obrigatório)
  produto_id        int       (obrigatório — ID resolvido; pode usar resolverProduto antes)
  localizacao_codigo string   (obrigatório — código bipado/digitado)
  origem_leitura    string    'codigo_barras' | 'manual'  (padrão: 'codigo_barras')
  quantidade        float     (opcional; obrigatório se missao.configuracoes.quantidade_obrigatoria=true)
```

**Resposta de sucesso**: `{status: 'success', alocacao_id: int, item_id: int, missao_id: int}` + Galaxia `atualizarPagina`

**Respostas de erro** (AC4.5 — cada rejeição tem mensagem específica):
- Missão não ativa: "Esta missão não está ativa..."
- Sem permissão: "Você não tem permissão para executar esta missão..."
- Produto fora da missão: "Este produto não pertence à missão..."
- Localização inválida: "Localização WMS '{codigo}' não encontrada na base de validação..."
- Quantidade obrigatória: "Quantidade é obrigatória para esta missão..."

### Dispatcher JSON direto (gestão/AJAX)

```
Sinal:  AlocacaoWms
Ações:  confirmar | listar | historico
```

`listar`: GET `missao_id=int` → `{ok: true, itens: [...], alocacoes_por_produto: {...}}`

`historico`: GET `missao_id=int&produto_id=int` → `{ok: true, alocacoes: [...]}`

### Variáveis injetadas nas views

`html/alocacaowms/item.php` recebe:
- `$estado` — `'iniciar' | 'em_andamento' | 'concluido' | 'erro'`
- `$missao` — objeto missão completo
- `$itens` — array de `MissaoLogisticaItem` com `$item->ultima_alocacao` (objeto `wms_alocacoes` ou null)
- `$quantidadeObrigatoria` — bool
- `$rotaVoltar` — string rota de retorno
- `$acaoIniciarMissao`, `$acaoFinalizarMissao`, `$acaoLiberarMissao` — arrays `{opcao, params}`

### Worker de integração

Registrar no crontab (a cada 2 min):
```
*/2 * * * * php /caminho/genesis/crons/wms_alocacao_integracao.php >> /tmp/genesis_wms_worker.log 2>&1
```

Logs de cada tentativa de hook em `/tmp/genesis_webhook.log`.

### Migrations

Executar em ordem no banco `gx_goldie` (e `dev_gx_goldie` para o ambiente de dev):
1. `001_create_wms_localizacoes.sql`
2. `002_create_wms_alocacoes.sql`
3. `003_seed_tipo_missao_alocacao_wms.sql`

### Criação de missão pelo gestor (passo 12 do techplan — implementado no round 2)

No modal "Nova Designação" do Central, o gestor vê agora o card **Alocação WMS** (indigo).
Ao clicar: abre form modal com:
- Campo **Descrição** (obrigatório)
- Select **Operador responsável** (opcional — FILA_ABERTA se vazio)
- Checkbox **Quantidade obrigatória** — grava em `missao_logistica.configuracoes` como `{"quantidade_obrigatoria": true}`

O submit chama `MissoesPersistencia::criacao` com `tipo=ALOCACAO_WMS`.

Produtos são adicionados via edição de missão pós-criação (mesmo fluxo dos outros tipos).
