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.ABERTAainda 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
recalcularsai cedo diante dele, e a escrita do derivado virouUPDATE … WHERE status <> 'RESOLVIDA_MANUAL'. O write-back não pega oXADM_LOCK, e sem oWHEREuma 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 porpied_x_pede re-armaria as1XXde um pedido já lançado no X-Adm. O cliente o mandaria de novo. - As linhas não são tocadas. As
1XXseguem1XX, 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 emrequest: oRequestCleanupJobapaga osSUCESSOcom mais de 90 dias. Ooperadorvem 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/powersynctrocastatus <> 'ENTREGUE'por(status = 'ABERTA' OR status = 'TRAVADA')nas duas queries do manifesto, para a resolvida sair do bucket (OR encadeado, nãoIN: a 1.23.3 rejeitaINcom 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 pai1XXfica000, 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 voltou120, lançado à mão depois de cadastrar o cliente. Decisão: oresolver-manualtambém responde409quando algum item da remessa está pendente (EstadoDoItem.pendente(): nem006, nem1XX, 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 (contrato006, itens e fórmulas120). - O backfill de startup ressuscitava a resolvida. O
BackfillRemessaJobroda em toda subida e junta as linhas não-006de cada pedido na remessa viva — que ofindVivajá não acha para a resolvida. Cada restart criava umaTRAVADAnova com as1XX, e com uma viva de novo na chave o409do re-arme deixava de valer. Decisão: o backfill pula a chave que temRESOLVIDA_MANUAL, e, como defesa em profundidade, o re-arme por remessa não reabre linha decontratos/itensped/formulasque 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.