Pular para conteúdo

0025 — Remessa resolvida à mão

Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-09-10 · Decidido em: 2026-09-10

Contexto

A 0024 derivou o estado da remessa dos cod_retorno das linhas, por uma função só. TRAVADA sai de lá por um caminho só, o re-arme, que parte do pressuposto de que o operador corrige o dado na origem e reenvia.

O incidente dos 15 pedidos-kit (2026-09-10) mostrou um travamento que não se resolve assim. O contrato está em 006 e os itens e fórmulas em 120, porque o ERP não criou o produto-kit e o estoque ficou com um 006 falso. Parte desses pedidos só se resolve lançando à mão no X-Adm. O maxsul-pied ganhou o botão "Importado manualmente" (ERRO_XADM → IMPORTADO_MANUAL), mas a remessa continuava TRAVADA no Integrador para sempre:

  • no bucket do PowerSync (status <> 'ENTREGUE');
  • no aviso de travada do integrador-client;
  • no manifesto, acumulando lixo.

Nenhum caminho a fechava. O re-arme a reabriria e reenviaria as linhas, que é o oposto do que se quer.

Decisão

Um estado terminal por comando: RESOLVIDA_MANUAL, gravado por POST /api/v1/integracao/remessas/resolver-manual?origem=INT-PIED&chave=, com observação obrigatória no corpo.

  • Só a partir de TRAVADA. ABERTA ainda está em entrega, e fechá-la enquanto o cliente manda as linhas arrisca duplicar no ERP: 409.
  • A derivação não o escreve nem o sobrescreve. O recalcular sai cedo diante dele, e a escrita do derivado virou UPDATE … WHERE status <> 'RESOLVIDA_MANUAL'. O write-back não pega o XADM_LOCK, e sem o WHERE uma derivação que leu a remessa antes do comando a reabriria depois dele.
  • Viva passa a ser a lista fechada ('ABERTA','TRAVADA'), no índice único e nas consultas, e não mais <> 'ENTREGUE'. Com o filtro aberto, o próximo push reusaria a remessa resolvida e a derivação a reabriria.
  • O re-arme da chave responde 409. Sem remessa viva, ele cairia no fallback legado por pied_x_ped e re-armaria as 1XX de um pedido já lançado no X-Adm. O cliente o mandaria de novo.
  • As linhas não são tocadas. As 1XX seguem 1XX, o cliente não as coleta, e a mão única da 0013/0023 segue valendo.
  • A auditoria mora na remessa (resolvida_em, resolucao_obs, resolucao_operador), e não em request: o RequestCleanupJob apaga os SUCESSO com mais de 90 dias. O operador vem do corpo, porque o Bearer é estático por instalação e não identifica pessoa.

Alternativas descartadas

Marcar os itens resolvido = true para a derivação cair em ENTREGUE. Perde a distinção "o ERP aceitou" × "o operador resolveu por fora". Além disso, o resolvido é derivado do cod_retorno a cada derivação: a próxima desfaria a marcação, a menos que ele virasse estado persistido.

Gravar ENTREGUE e marcar a resolução manual em colunas à parte. Não exigiria mudança fora deste repo, e o bucket limparia na hora. Mas ENTREGUE deixaria de significar "todas as linhas 006", que é o que o contrato publicado afirma. O maxsul-pied e qualquer métrica contariam como entregue um pedido que o ERP recusou.

Aceitar também ABERTA. Dá mais poder ao operador do que o problema pede, e abre a janela de duplicidade.

Consequências

Três repos acompanham, mas em qualquer ordem. O integrador-client trata status desconhecido como não travado: a remessa sai do aviso, e as linhas, em 1XX, continuam sem sair. O maxsul-pied já ignora IMPORTADO_MANUAL. O que falta é limpeza:

  • a sync rule do maxsul/powersync troca status <> 'ENTREGUE' por (status = 'ABERTA' OR status = 'TRAVADA') nas duas queries do manifesto, para a resolvida sair do bucket (OR encadeado, não IN: a 1.23.3 rejeita IN com lista literal);
  • o integrador-client alinha o filtro local de remessa viva e passa a conhecer a constante;
  • o maxsul-pied chama o comando no clique "Importado manualmente".

A exceção à derivação única é nomeada, não difusa. A 0024 continua em pé. Um estado só escapa da função, e escapa por uma porta só, sob comando e com auditoria.

Deferido: reabrir o estoque 006 falso (incluir=estoque no re-arme). Só vale se a Maxsul preferir reprocessar a lançar à mão, e hoje a escolha é lançar à mão.

Revisão (2026-09-10) — dois caminhos de duplicidade achados na revisão cruzada

Antes do primeiro deploy, a revisão dos quatro repos (este, integrador-client, powersync e maxsul-pied) achou dois jeitos de a resolução à mão duplicar o pedido no ERP, o oposto do que ela promete:

  • Linha pendente numa TRAVADA. O filho de um pai 1XX fica 000, bloqueado. Resolver tirava a remessa do manifesto (sync rule e cliente só leem as vivas), e o cliente passava a mandar esse filho por presença — ex. o contrato de um pedido cujo cliente voltou 120, lançado à mão depois de cadastrar o cliente. Decisão: o resolver-manual também responde 409 quando algum item da remessa está pendente (EstadoDoItem.pendente(): nem 006, nem 1XX, nem deletado, nem órfão). Resolver à mão fica para o que nada mais vai mandar; o resto se resolve corrigindo o dado e re-armando. Os 15 kits passam (contrato 006, itens e fórmulas 120).
  • O backfill de startup ressuscitava a resolvida. O BackfillRemessaJob roda em toda subida e junta as linhas não-006 de cada pedido na remessa viva — que o findViva já não acha para a resolvida. Cada restart criava uma TRAVADA nova com as 1XX, e com uma viva de novo na chave o 409 do re-arme deixava de valer. Decisão: o backfill pula a chave que tem RESOLVIDA_MANUAL, e, como defesa em profundidade, o re-arme por remessa não reabre linha de contratos/itensped/ formulas que também seja item de uma resolvida. Os pais (propriedades/fones/estoque) ficam de fora dessa exclusão de propósito: são compartilhados entre pedidos, e excluí-los travaria o re-arme de outro pedido do mesmo cliente.

Verificação

RemessaResolverManualTest, contra Postgres real, cobre: TRAVADA vira RESOLVIDA_MANUAL com auditoria e sem tocar as linhas; a derivação não reabre, nem com a linha indo a 006; ABERTA dá 409; sem remessa viva dá 200 com zero, idempotente; a validação dá 400; o re-arme depois da resolução dá 409 e deixa as 120 intactas; uma remessa nova da mesma chave não colide no índice; a consulta traz a resolução. RemessaAuthTest cobre o 401.