Contrato pAbast¶
Status: Rascunho · Responsável: Gustavo Madruga · Atualizado em: 2026-09-11
Contrato entre o integrador-client (escreve o arquivo de envio, executa o runtime, lê o
retorno) e o programa ZIM pAbast (lê o envio, grava no X-Adm, escreve o retorno).
Este documento é a fonte única do contrato: os dois lados derivam daqui.
Nome do programa: por hora a integração roda sob o nome
pAbast(o mesmo do programa de abastecimento legado — decisão da equipe ZIM). O que identifica ESTA integração (pied-maxsul) na família é a linha de continuaçãoIMPORTA_PIEDdo handshake (§1 passo 5).Um contrato por fluxo: o integrador-client é o cliente coringa da plataforma — este documento cobre o fluxo pied (tabelas do §3) e serve de BASE comum (formato §2, retorno §4, códigos §5, exit codes §6) para os demais. O fluxo abastecimento já tem o seu: contrato-abastecimento.md. Um arquivo de envio carrega SOMENTE as tabelas do fluxo anunciado pelo literal de continuação.
Status: rascunho — validar com a equipe X-Adm antes de fechar: larguras campo a campo, formatos de data e as observações marcadas com ⚠.
1. Fluxo de uma execução (lote)¶
- O cliente apaga
dXpEnvioedXpRetornoremanescentes no diretório do X-Adm (nomes e diretório configuráveis). - O cliente escreve o arquivo de envio completo — todas as linhas do lote (máximo 999 registros; o excedente fica para o lote seguinte — quem garante o limite é o cliente) — ainda sem a linha de continuação do passo 5.
O corte no limite é feito por remessas inteiras, não por posição: uma remessa é o conjunto que o Integrador declarou ter de chegar junto ao ERP, e fatiá-la produziria filho sem pai — o defeito que a decisão 0004 veio matar. Remessa que não couber espera o próximo lote, inteira; o espaço restante é preenchido com linhas que não pertencem a remessa nenhuma, essas sim seguras de cortar. 3. O cliente executa
zimrtmu.exe pAbastcom working directory = diretório do X-Adm. 4. Handshake (restrição do ZIM: o processo não tem stdin/stdout utilizável — a sincronização é por arquivos, herdada da integração de abastecimento do xposto): o programa, assim que sobe, escreve o marcador de início (literalIniciou Zim) como primeira linha do arquivo de retorno e fica esperando. 5. O cliente verifica o arquivo de retorno a cada 100 ms; ao ver o marcador, anexa a linha de continuação (literalIMPORTA_PIED) ao final do arquivo de envio — é o sinal de "arquivo completo, pode processar". Marcador que não vem dentro do timeout de handshake (padrão 3 s, configzim.handshake.timeout) = cliente mata o processo e o lote falha (causa típica: falta de licença ZIM).
Os literais são fixos em código (ZimExecutor) — fazem parte do contrato, não da
instalação. A linha de continuação identifica a integração na família ZIM:
Continuar = abastecimento (xposto) · IMPORTA_PIED = pied-maxsul (este cliente)
· ENCERRA_MDFE = encerramento MDF-e.
6. O pAbast, ao ver a linha IMPORTA_PIED, lê os registros do envio (a linha de
continuação não é registro e é ignorada), grava cada um no X-Adm e escreve o
arquivo de retorno completo antes de encerrar — uma linha de retorno por linha
de envio, na MESMA ordem, inclusive para erros. Registro que falhou também ganha
linha (com o código de erro). O marcador Iniciou Zim era só o sinal de progresso do
passo 4: o ZIM LIMPA o dXpRetorno e regrava o retorno final SEM ele (comportamento de
toda a família ZIM, confirmado pela equipe X-Adm em 2026-08-04) — o retorno final começa
direto nos registros.
7. O cliente só lê o retorno depois de o processo encerrar (timeout de processamento
configurável, padrão 5 minutos); valida (§4) e aplica os códigos; ao final apaga envio e
retorno. Se o marcador Iniciou Zim aparecer na 1ª linha, é descartado; se não (o caso
normal do retorno final), o retorno já começa nos registros — a ausência não invalida.
Falha do lote — o cliente não aplica código nenhum, devolve todas as linhas ao código anterior (pendente) e tenta de novo depois: handshake ausente no prazo, timeout de processamento, exit code ≠ 0, retorno ausente, ou retorno inválido (§4).
O handshake é o ponto de não-retorno. Uma falha antes dele (o marcador não veio, ou o
processo morreu sem escrevê-lo) garante que o ZIM não leu o lote: o reenvio é seguro por
construção. Uma falha depois dele (exit ≠ 0, timeout de processamento, retorno ausente ou
inválido) não garante nada: o ZIM não é transacional e pode ter gravado parte do lote no X-Adm antes
de cair. O cliente reenvia mesmo assim, porque é o único caminho sem intervenção manual, e isso só é
seguro porque toda rotina do pAbast é idempotente (§5). Nesse caso o cliente também:
- registra um aviso (log e GlitchTip) com as chaves do lote, para alguém conferir no ERP — um aviso por lote e motivo, não um por retentativa;
- ecoa no log o
dXpRetornoe a saída do ZIM (pAbast-saida.log), que o lote seguinte sobrescreve.
(Incidente 260077821, 2026-09-09: o ZIM caiu depois de gravar o contrato; no reenvio o contrato
voltou "previamente" e o item, 120.)
2. Formato comum dos arquivos¶
- Texto fixed-width (posições fixas, sem separadores), 1 linha = 1 registro.
- Encoding windows-1252 (ANSI), quebra de linha CRLF.
- Todo registro começa com o contador da linha: inteiro 1..999, largura 3,
alinhado à direita (
1,42,999) — sequencial dentro do arquivo, ecoado no retorno. Em seguida vem o tipo de registro = nome da tabela, texto de largura 20. - Campo texto: alinhado à esquerda, completado com espaço à direita; espaços à esquerda e embutidos são preservados byte a byte; campo não informado = branco (espaços), nunca omitido.
- Campo numérico: largura 18, alinhado à direita (espaços à esquerda), ponto
como separador decimal, escala fixa por campo (arredondamento HALF_UP). Por que 18 para
todos: o
VASTINTdo ZIM é um inteiro escalado de até 15 dígitos significativos (capacidade fixa do tipo — só a escala varia por coluna; verintegracao-zim.mddo int-pied); 15 dígitos + ponto = 16, e 18 dá folga. Ex.:Qtde=2(escala 3) →2.000. - Data:
AAAAMMDD(8). Hora:HHMM(4). Datetime:AAAAMMDDHHMMSS(14) — usado quando o campo representa um instante (data+hora); campos que são só data seguem 8 chars. Segundos são preenchidos com00quando o dado de origem só tem HH:MM.
3. Arquivo de envio (dXpEnvio)¶
Linha = contador(3) + tipo_registro(20) + campos abaixo, na ordem. Offsets contam a
partir de 0 (contador ocupa 0–2, tipo 3–22; o campo 1 de dados começa no offset 23).
3.1 propriedades — cliente/endereço (largura total da linha: 261)¶
| # | Campo | Tipo | Largura | Offset | Observação |
|---|---|---|---|---|---|
| 1 | cgc_cpf | texto | 14 | 23 | CNPJ/CPF, só dígitos |
| 2 | cod_prop | texto | 2 | 37 | código do endereço |
| 3 | nome_prop | texto | 50 | 39 | razão social |
| 4 | fantasia | texto | 30 | 89 | |
| 5 | endereco | texto | 60 | 119 | |
| 6 | cep | texto | 8 | 179 | |
| 7 | bairro | texto | 20 | 187 | |
| 8 | cidade | texto | 30 | 207 | |
| 9 | estado | texto | 3 | 237 | ⚠ gravar com espaço à esquerda (PR) — preservado byte a byte |
| 10 | numero | texto | 6 | 240 | |
| 11 | insc_est | texto | 15 | 246 | IE ou RG |
3.2 fones — contato (largura total: 202)¶
| # | Campo | Tipo | Largura | Offset |
|---|---|---|---|---|
| 1 | cgc_cpf | texto | 14 | 23 |
| 2 | ddd | texto | 5 | 37 |
| 3 | telefone | texto | 10 | 42 |
| 4 | nome | texto | 50 | 52 |
| 5 | texto | 100 | 102 |
3.3 estoque — produto (largura total: 223)¶
| # | Campo | Tipo | Largura | Offset | Observação |
|---|---|---|---|---|---|
| 1 | pied_codigo_alt | texto | 15 | 23 | código do produto no parceiro |
| 2 | ean13 | texto | 13 | 38 | código de barras |
| 3 | cod_prod_alt | texto | 8 | 51 | produto base para o cadastro |
| 4 | nome_prod | texto | 128 | 59 | |
| 5 | venda | numérico 2 dec | 18 | 187 | preço à vista |
| 6 | venda_pz | numérico 2 dec | 18 | 205 | preço a prazo |
⚠ Saldo NÃO vai no envio (é dado que o X-Adm gera) — confirmar com a equipe.
3.4 contratos — cabeçalho do pedido (largura total: 125)¶
| # | Campo | Tipo | Largura | Offset | Observação |
|---|---|---|---|---|---|
| 1 | pied_x_ped | texto | 15 | 23 | código do pedido no parceiro |
| 2 | cgc_cpf_cliente | texto | 14 | 38 | |
| 3 | chave_unid | texto | 5 | 52 | centro de custos — ⚠ espaços à esquerda preservados |
| 4 | cod_mat | texto | 10 | 57 | funcionário — ⚠ espaços à esquerda preservados |
| 5 | dt_em | data | 8 | 67 | emissão (só data) |
| 6 | data_inc | datetime | 14 | 75 | inclusão (data+hora do sistema; junta data_inc e hora_inc do espelho) |
| 7 | vl_tot | numérico 2 dec | 18 | 89 | |
| 8 | nat_op | texto | 5 | 107 | natureza de operação |
| 9 | compl | texto | 2 | 112 | |
| 10 | tp_vda | texto | 1 | 114 | F/C |
| 11 | class_forma | texto | 2 | 115 | forma de pagamento |
| 12 | prazo | data | 8 | 117 | data de entrega |
3.5 itensped — item do pedido (largura total: 125)¶
| # | Campo | Tipo | Largura | Offset |
|---|---|---|---|---|
| 1 | pied_x_ped | texto | 15 | 23 |
| 2 | pied_codigo_alt | texto | 15 | 38 |
| 3 | qtde | numérico 3 dec | 18 | 53 |
| 4 | valor | numérico 2 dec | 18 | 71 |
| 5 | total | numérico 2 dec | 18 | 89 |
| 6 | taxa_frete | numérico 2 dec | 18 | 107 |
3.6 formulas — fórmula (BOM) do kit (largura total: 80) — PROVISÓRIO¶
⚠️ Layout provisório — a reconciliar com a equipe X-Adm (Tiago). O import de FORMULAS no ZIM ainda não teve o layout confirmado. As larguras abaixo herdam o padrão do contrato (numérico 18, §8) e a escala 3 do
qtde(espelhoNUMERIC(19,3), igual aoitensped); opied_formula_id(id sequencial gerado no X-Adm) vai como 1º campo. Sem colunaschave_*(o X-Adm gera as chaves; nunca voltam). Ao confirmar o layout real, revisar esta seção e oArquivoEnvio.linhaFormulasjuntos.
| # | Campo | Tipo | Largura | Offset |
|---|---|---|---|---|
| 1 | pied_formula_id | numérico 0 dec | 18 | 23 |
| 2 | pied_x_ped | texto | 15 | 41 |
| 3 | pied_cod_prod | texto | 6 | 56 |
| 4 | qtde | numérico 3 dec | 18 | 62 |
3.7 Ordem das linhas no lote¶
O cliente escreve o lote na ordem dos fatos: tabelas na sequência
propriedades → fones → estoque → contratos → itensped → formulas (dependência referencial —
formulas por último, pois referencia o produto-kit e o componente) e, dentro de cada tabela,
na ordem de criação (id UUID v7 do espelho — estável mesmo quando um registro falha e é
retentado). O pAbast DEVE processar na ordem do arquivo. O contador (1..999) segue exatamente
essa ordem.
Coerência do lote (não só a ordem). A ordem garante o ARQUIVO; o conteúdo é garantia separada.
O sync grava no banco local do cliente a qualquer momento, inclusive no meio de uma coleta feita
tabela a tabela — o lote sairia com o pedido sem o cliente, e o 1XX que o ZIM devolve é
terminal (incidente do pedido 260079421, 2026-09-08). Por isso o cliente lê os pendentes duas
vezes e só monta o arquivo quando as duas leituras trazem o mesmo conjunto; senão, adia o lote
para o próximo ciclo. Ver decisão 0003.
4. Arquivo de retorno (dXpRetorno)¶
A primeira linha pode ser o marcador de início do handshake (Iniciou Zim, §1 passo 4) —
mas no retorno FINAL o ZIM já o limpou (§1 passo 6), então o normal é o arquivo começar direto
nos registros. O marcador é opcional: se vier, é descartado (não é registro); se não vier,
não invalida. Os registros são uma linha por linha do envio, na MESMA ordem (o casamento é
por posição, conferido pelo contador ecoado). Layout único para todas as tabelas:
| # | Campo | Tipo | Largura | Offset | Conteúdo |
|---|---|---|---|---|---|
| 1 | contador | numérico 1..999 | 3 | 0 | ECO do contador da linha de envio (à direita) |
| 2 | tipo_registro | texto | 20 | 3 | ECO do tipo da linha de envio correspondente |
| 3 | cod_retorno | texto | 3 | 23 | código de retorno (§5) |
| 4 | msg_retorno | texto | até 128 | 26 | mensagem (resto da linha; pode ser branco) |
O pAbast escreve o retorno inteiro antes de encerrar e escreve linha para TODO
registro — sucesso ou erro. O ZIM não devolve chaves (Chave*): só código e mensagem.
Validação no cliente (qualquer falha invalida o lote inteiro — nada é aplicado):
1. se a 1ª linha for o marcador Iniciou Zim (trim), descarta-a; sua ausência não invalida;
2. número de linhas de registro = número de linhas do envio — com duas tolerâncias, as
salvaguardas do reinício e do eco da continuação (abaixo);
3. contador ecoado = posição da linha (1..N);
4. tipo_registro ecoado confere com a linha de envio na mesma posição;
5. linha com pelo menos 26 caracteres e cod_retorno não-branco.
O alinhamento é garantido pelo eco de contador+tipo linha a linha, não pelo marcador; e o arquivo-velho é coberto pelo delete de envio/retorno antes do lote (§1 passo 1) + o handshake, que trunca o
dXpRetornona largada do ZIM. Por isso o marcador pôde deixar de ser exigido.
Salvaguarda do reinício (fora do contrato, tolerada). Em "conflito de transação" o pAbast
recomeça o lote do zero e não limpa o dXpRetorno (confirmado pela equipe X-Adm, Tiago,
2026-09-11): o arquivo sai com o começo da tentativa abortada e, depois, a rodada final completa —
ex. contadores 1,2,1,2,3,…,13 para 13 enviadas. O cliente aceita esse retorno e aplica só as
N últimas linhas (a rodada final) quando, e somente quando, as linhas a mais antes dela:
- formam tentativas que começam no contador 1 e seguem de 1 em 1, sem nenhuma chegar ao fim do envio;
- são idênticas (desprezando espaços no fim) à linha da rodada final de mesmo contador.
Qualquer outro excesso (linha a mais no fim, resposta diferente para o mesmo registro, rodada
inteira repetida) continua invalidando o lote. A rodada final passa pela validação normal (itens 3
a 5). O cliente registra um WARN quando descarta. Por que tolerar: sem isso o lote era reenviado,
e o ERP, que já tinha gravado tudo, respondia 120 no item na segunda execução (a rotina não é
idempotente — §8). O certo continua sendo o pAbast limpar o retorno ao recomeçar.
Salvaguarda do eco da continuação (fora do contrato, tolerada). Em "conflito de transação" o
pAbast responde 999 em todos os registros e escreve também uma resposta para a linha de
continuação — ex. IMPORTA_PIED 999-Conflito de transação no fim —, que não é registro
(§1 passo 6). O cliente descarta a última linha do retorno quando ela é o literal de continuação
do próprio fluxo (sozinho ou seguido de espaço), registra um WARN e valida o resto
normalmente. Em qualquer outra posição, ou com outro literal, o lote segue inválido. Vale também no
retorno casa-por-contador do MDF-e (literal ENCERRA_MDFE). Por que
tolerar: o conflito não grava nada (a tentativa seguinte cadastra do zero), então o certo é aplicar
os 999 e retentar — invalidar deixava as linhas em 000 e disparava o aviso falso de "o ERP pode
ter gravado parte" (§1). O certo é o pAbast não responder à linha de continuação.
4.1 Avisos do processo (dRels3)¶
Quando o X-Adm tem uma crítica sobre o processo (algo que, com tela, apareceria ao usuário — ex.
"lançamento fora da data de fechamento contábil", "XML não copiado para averbação"), o pAbast grava
essas mensagens no arquivo dRels3 (sem extensão, no mesmo diretório do dXpEnvio/dXpRetorno).
O X-Adm o limpa a cada chamada do pAbast, então conteúdo na volta = houve crítica. (Existe também
um dRels sem o 3, menos importante — hoje o cliente não o lê.)
Após executar o pAbast — em qualquer desfecho (é onde a crítica costuma explicar uma falha do
lote) — o cliente:
- lê o
dRels3se existir e descarta linhas em branco; - loga cada linha (
WARN) e, no modo debug, espelha no console; - dispara um aviso no GlitchTip/Sentry (
WARNING) com o conteúdo (agrupado por mensagem) — exceto linhas de conflito (concorrência; o lote retenta sozinho, não é ruído de observabilidade): o filtro é por linha, então a crítica ainda vai ao log/console, mas só as linhas SEM "conflito" alarmam; se nenhuma sobrar, nada é disparado; - apaga o
dRels3(já ficou no log).
5. Códigos de retorno¶
| Código | Significado | Ação do cliente |
|---|---|---|
006 |
gravado com sucesso | encerra o registro |
1XX |
erro terminal de dado (ex. cliente inexistente) | encerra com erro; não retenta |
9XX |
erro de processamento reprocessável (ex. lock) | retenta no próximo ciclo |
(000 = pendente e 001 = em processamento são estados do espelho na nuvem — nunca aparecem
no arquivo de retorno.)
006 só descreve o registro da própria linha. 006 = ESTE registro (desta linha, desta tabela)
está no X-Adm, gravado agora ou já existente. Se o pAbast não gravou nem encontrou o registro da
linha, o código é 1XX, mesmo que outra entidade relacionada exista. Um 006 com a mensagem de
outra entidade esconde o erro para sempre: o re-arme do Integrador só reabre 1XX, e a linha nunca
mais é reprocessada.
Idempotência — obrigatória em toda rotina. O cliente reenvia um registro sempre que não recebeu
a resposta dele: falha depois do handshake (§1), recuperação de linha presa em 001 após queda do
cliente, re-arme de remessa pelo Integrador. Por isso:
- registro que já existe no X-Adm volta
006com a mensagem da própria entidade (ex. "Pedido (X) cadastrado previamente com o código …") — nunca um erro, nunca um segundo cadastro; - o registro que depende dele (item → pedido, fórmula → produto) encontra o pai já existente do mesmo jeito que encontraria o pai gravado na mesma execução.
6. Exit codes do processo¶
| Exit | Significado |
|---|---|
| 0 | pAbast rodou e escreveu o retorno (mesmo que com erros por registro) |
| ≠0 | falha geral (sem licença, erro de runtime) — lote inteiro é retentado |
7. Exemplo¶
Envio (2 registros — larguras encurtadas na exibição, · = espaço; a linha IMPORTA_PIED
é anexada pelo cliente durante o handshake, §1 passo 5):
··1propriedades········11222333000144·01Cliente·Exemplo·Ltda···…
··2contratos···········E2E-PED-1······11222333000144···SB·001······2026072020260720143000···········1234.56VDA··01C0120260725
IMPORTA_PIED
Retorno final correspondente (o ZIM já limpou o marcador Iniciou Zim — §1 passos 6-7 —, então
começa direto nos registros; se o marcador aparecer, o cliente o descarta):
··1propriedades········006Gravado
··2contratos···········112Cliente·nao·encontrado
8. Pontos em aberto (validar com a equipe X-Adm)¶
- Larguras/escalas campo a campo (derivadas de
int-pied/docs/projeto/modelagem.md§1). Numéricos: largura única 18 (VASTINT = até 15 dígitos significativos escalados) — a equipe só precisa confirmar as escalas por campo e que opAbastlê os 18 à direita. estoque.Saldofora do envio — confirmar.- Formato de
prazo(texto AAAAMMDD) e dehora_inc(HHMM). - Comportamento do
pAbastsem licença ZIM disponível: exit ≠ 0 imediato (preferido) ou espera? O cliente cobre os dois (timeout de handshake). - Marcador de início: assumido
Iniciou Zim(mesmo literal do pAbast) para toda a família — confirmar que opAbastusa o mesmo texto. A linha de continuação já está fechada:IMPORTA_PIED(fixa em código noZimExecutor, junto com as irmãsContinuar/abastecimento eENCERRA_MDFE/MDF-e). - Pedidos-kit de 2026-09-08/09 (diagnóstico do maxsul-pied, 2026-09-10) — lado ZIM:
estoquedo produto-kit (código = número do pedido) respondeu006com a mensagem do pedido ("-Pedido (X) cadastrado previamente …") quando já havia contrato com aquele número, e o produto não foi criado. Viola o §5 ("006só descreve o registro da própria linha").contratoscriou contrato novo para pedidos cujo contrato anterior a rotina deestoqueencontrou (8 pedidos, possível duplicidade no ERP): as duas rotinas procuram o pedido por chaves diferentes?itenspedcom o contrato já existente ("previamente") respondeu "-Pedido () não cadastrado": a rotina não acha o pedido pré-existente (viola a idempotência do §5), e a mensagem imprime o valordo item no lugar do número do pedido. O layout do envio (§3.5) é o mesmo dos kits que deram certo.- Retorno não limpo depois de conflito: o
pAbastrecomeça o lote e acrescenta a rodada nova aodXpRetornosem truncar (Tiago confirmou em 2026-09-11; pedidos 260080333, 260077821, 260079756 e 260079648). O cliente tolera o formato (§4, salvaguarda do reinício), mas a correção é do lado ZIM. - Em conflito de transação o
pAbastresponde também à linha de continuação (IMPORTA_PIED 999-Conflito de transaçãono fim do retorno; pedidos 260079567 e 260075187, 2026-09-09/10). O cliente descarta esse eco (§4, salvaguarda do eco da continuação), mas a correção é do lado ZIM. - O
zimrtmu.exeabre processo filho? No timeout o cliente mata só o processo que iniciou (o Java 8 não alcança a árvore); um filho vivo poderia processar o arquivo do lote seguinte. - O
pAbastescreve o retorno aos poucos ou só no fim? Se for aos poucos, o cliente poderia aplicar as linhas já respondidas quando o ZIM cai no meio, em vez de reenviar o lote todo. formulas(§3.6) segue provisório: confirmar o layout.