Pular para conteúdo

0023 — Exceção sob comando à mão única do write-back

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

Contexto

A decisão 0013 fixou a mão única do fluxo de entrada PIED: o upsert do PUT /api/v1/xadm nunca sobrescreve o que o X-Adm gravou pelo write-back — cod_retorno, msg_retorno, chave_*. A regra existe por um motivo caro: sem ela, todo re-push de rotina apagaria o retorno de um pedido já gravado no ERP, e o integrador-client o mandaria de novo — pedido duplicado.

O efeito colateral só apareceu em produção. 1XX é erro terminal: o integrador-client coleta cod_retorno = '000' OR LIKE '9%' e nada mais. Junte as duas coisas e o resultado é que reimportar o pedido no painel do int-pied nunca destrava um 1XX — os pushes chegam, são aplicados, e o cod_retorno continua o mesmo. Do lado do operador, o botão dizia "ENFILEIRADO" e nada saía: pedido 260079421, 2026-09-08, três cliques, zero reenvios.

A saída era UPDATE na mão no banco de produção, por quem tem acesso — o que não escala e não deixa rastro.

Decisão

Abrir uma exceção sob comando à mão única: POST /api/v1/xadm/retorno/pedido/reenfileirar?xPed= devolve a 000 (e limpa msg_retorno) as linhas do pedido que morreram em 1XX.

Sob comando é o ponto: a mão única continua valendo para todo caminho automático. O que muda é que passa a existir um caminho explícito, autenticado e auditável para o operador destravar o pedido depois de corrigir o dado na origem.

Recorte deliberado:

  • Só 1XX. O filtro cod_retorno LIKE '1%' é o que torna a operação segura. 006 (já gravado no ERP) re-armado faria o cliente reenviar cadastro que o ERP já tem — duplicaria, exatamente o que a mão única evita. 9XX já retenta sozinho. 001 (em processamento, marcado pelo cliente) começa com 0 e não é alcançado.
  • Só as três tabelas chaveadas por pedido — contratos, itensped, formulas. propriedades e fones são chaveadas pelo documento do cliente; estoque, pelo produto. Linha delas presa em 1XX continua sendo SQL na mão — caso raro, e a alternativa (re-armar por chave de cliente) atingiria pedidos que ninguém pediu para mexer.
  • Não toca linha soft-deletada. A contagem devolvida é o que o painel mostra ao operador, e ela não pode incluir linha que, para ele, não existe mais.
  • Uma transação para as três. O PowerSync replica no commit, então o cliente nunca vê as três tabelas em estado intermediário.

Quem chama é o botão Reimportar do painel do int-pied, sempre depois do push do pedido — a linha só volta a ser coletável já com o dado corrigido. O curl do runbook é o mesmo comando na mão, para diagnóstico.

Consequências

Zero não é erro. Pedido inexistente e pedido cujas linhas estão todas em 006 respondem igual: 200 com contagens zeradas. 404 seria pior — obrigaria quem chama a distinguir "não existe" de "nada preso", que para o operador é a mesma frase: não havia o que reabrir.

Uma corrida aceita, não resolvida. O write-back do integrador-client pode carimbar 1XX de novo logo depois do re-armar. A operação roda sob o XADM_LOCK, mas esse lock não cobre esse caminho: ele serializa contra o applyPut, que por definição da mão única não escreve cod_retorno. Quem escreve é o PowersyncController, que não o pega. Estender o lock até lá significaria serializar o write-back de todos os clientes — caminho quente do sistema — para fechar uma janela estreita numa operação idempotente, em que o operador vê a contagem e repete. Trocaríamos uma corrida rara por um gargalo permanente. Fica declarado em vez de escondido.

A 0013 continua em pé. Esta decisão não afrouxa a mão única; nomeia a única porta por onde ela pode ser aberta, e por quem.

Alternativas descartadas

  • Deixar o upsert zerar cod_retorno quando o payload muda. Reabre exatamente o buraco que a 0013 fechou: o X-Adm reenvia payload idêntico de rotina, e "mudou" é frágil de definir sobre campos CHAR com padding.
  • Endpoint que aceita o cod_retorno alvo. Poder mais amplo do que o problema pede — e o único alvo sensato é 000. Parâmetro livre convidaria a re-armar 006.
  • Job que re-arma 1XX sozinho depois de N minutos. 1XX é terminal porque alguém precisa corrigir o dado; re-armar sem correção só recria o mesmo erro em laço.

Revisão (2026-09-17) — re-arme automático de cadastro compartilhado com dado novo

O caso ADEMIR STEIN (pedido 260081441) mostrou o outro lado da mão única: o PUT de 16/09 atualizou o endereço no espelho (frete novo), mas a linha ficou 006 — e o client só coleta 000/9XX, então o ERP nunca viu a correção. Divergência silenciosa espelho × ERP, sem caminho operacional (o re-arme sob comando só toca 1XX).

Exceção automática, mais estreita que a alternativa descartada acima — por isso não reabre o buraco da 0013:

  • Só cadastros compartilhados (propriedades, fones, estoque — chave por documento/produto). Linhas de pedido (contratos, itensped, formulas) mantêm a mão única absoluta: re-armá-las duplicaria pedido no ERP, que é o que a 0013 evita.
  • Só 006 com dado realmente mudado. 000/9XX já são coletáveis; 1XX segue com o operador. "Mudou" é comparação insensível a padding/blank (mesmoTexto) e a escala (compareTo) — re-PUT idêntico sossega no 006 (cobertura em FluxoEntradaIngestIntegrationTest).
  • O reset limpa msg_retorno e carimba updated_at (mesmo gesto do re-arme por remessa).

Acordo com a equipe X-Adm (Tiago): divergência de endereço em cliente existente se resolve com uma linha, código 0, endereço do frete — o que o pAbast faz com ela é combinado lá, não aqui.