Pular para conteúdo

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ção IMPORTA_PIED do 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)

  1. O cliente apaga dXpEnvio e dXpRetorno remanescentes no diretório do X-Adm (nomes e diretório configuráveis).
  2. 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 pAbast com 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 (literal Iniciou 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 (literal IMPORTA_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, config zim.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 dXpRetorno e 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 VASTINT do ZIM é um inteiro escalado de até 15 dígitos significativos (capacidade fixa do tipo — só a escala varia por coluna; ver integracao-zim.md do 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 com 00 quando 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 email 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 (espelho NUMERIC(19,3), igual ao itensped); o pied_formula_id (id sequencial gerado no X-Adm) vai como 1º campo. Sem colunas chave_* (o X-Adm gera as chaves; nunca voltam). Ao confirmar o layout real, revisar esta seção e o ArquivoEnvio.linhaFormulas juntos.

# 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 dXpRetorno na 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:

  1. lê o dRels3 se existir e descarta linhas em branco;
  2. loga cada linha (WARN) e, no modo debug, espelha no console;
  3. 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;
  4. 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 006 com 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 o pAbast lê os 18 à direita.
  • estoque.Saldo fora do envio — confirmar.
  • Formato de prazo (texto AAAAMMDD) e de hora_inc (HHMM).
  • Comportamento do pAbast sem 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 o pAbast usa o mesmo texto. A linha de continuação já está fechada: IMPORTA_PIED (fixa em código no ZimExecutor, junto com as irmãs Continuar/abastecimento e ENCERRA_MDFE/MDF-e).
  • Pedidos-kit de 2026-09-08/09 (diagnóstico do maxsul-pied, 2026-09-10) — lado ZIM:
  • estoque do produto-kit (código = número do pedido) respondeu 006 com 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 ("006 só descreve o registro da própria linha").
  • contratos criou contrato novo para pedidos cujo contrato anterior a rotina de estoque encontrou (8 pedidos, possível duplicidade no ERP): as duas rotinas procuram o pedido por chaves diferentes?
  • itensped com 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 valor do 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 pAbast recomeça o lote e acrescenta a rodada nova ao dXpRetorno sem 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 pAbast responde também à linha de continuação (IMPORTA_PIED 999-Conflito de transação no 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.exe abre 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 pAbast escreve 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.