Pular para conteúdo

Cenário 1 — dados saindo do X-Adm em tempo real

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

Aplica-se a: equipe X-Adm (ERP/ZIM) e quem opera a nuvem.

O que é

A cada gravação no X-Adm (nota, pedido, BL, cadastro), o ERP gera um arquivo .json com os registros alterados. Um agente Java no servidor do cliente envia esse JSON ao integrador do cliente (int.<cliente>.xadm.biz), que valida e grava no banco do cliente na nuvem. O PowerSync (ps.<cliente>.xadm.biz) entrega o dado aos apps (celular e web) em segundos.

Clientes neste cenário hoje: Sulplata e OnPetro Trading. A Thoms usa o mesmo integrador, mas por enquanto o dado chega por CSV (ver cenário 2 §Thoms).

%% lint-mermaid: LR-ok — duas colunas verticais (cliente | nuvem), leitura de cima para baixo
flowchart LR
    subgraph cliente["Servidor do cliente"]
        direction TB
        u["Usuário do X-Adm"]
        erp["ERP X-Adm (ZIM)"]
        ag["Agente Java<br/>(pasta monitorada)"]
        u -- "1. grava nota / pedido / BL" --> erp
        erp -- "2. gera .json" --> ag
    end
    subgraph nuvem["Nuvem X-Adm"]
        direction TB
        int["int.&lt;cliente&gt;<br/>integrador"]
        pg[("banco do cliente")]
        ps["ps.&lt;cliente&gt;<br/>PowerSync"]
        app["Apps Web e Mobile"]
        int -- "4. valida e grava<br/>(upsert pela chave)" --> pg
        pg -- "5. replicação" --> ps
        ps -- "6. sincroniza (segundos)" --> app
    end

    cliente -- "3. agente envia<br/>PUT /api/v1/xadm (Bearer)<br/>resposta: SUCESSO | ERRO" --> nuvem

Pré-requisitos

  • Cliente com a base na nuvem pronta — banco, integrador e PowerSync (Cliente novo na nuvem, passos 1 a 5).
  • O INTEGRADOR_API_TOKEN do cliente, entregue à equipe X-Adm por canal seguro.
  • Saída HTTPS liberada do servidor do cliente para int.<cliente>.xadm.biz (porta 443).

Passos

1. Definir o banco-espelho

O banco na nuvem é uma cópia fiel de parte do X-Adm: mesmos nomes de tabela, mesmas chaves. Ele é o mesmo para todos os clientes — o integrador cria todas as tabelas ao subir. O que muda por cliente é o que o ERP envia e o que o PowerSync distribui (passo 2).

Tabelas-espelho disponíveis hoje (identidade = a chave do X-Adm):

Tabela (chave no JSON) Chave do X-Adm Conteúdo
CONTRATOS ChaveCP cabeçalho do pedido/contrato
ITENSPED ChaveItem itens do pedido
ITENSPEDAMX SeqN alocação de item (BL, quantidade, retirada)
ITPEDAMXDI SeqNItAmx datas de desembaraço/DI do item alocado
ITENS / ITENSLOTE ChaveItemNF itens da nota e por lote
NOTACOMPL ChaveNota nota complementar
ESTOQUE ChaveEst (ou CodProd) cadastro de produto, saldo, preço
LOTEEST ChaveLoteEst lote de estoque
TBPCOEST ChaveEst + período tabela de preço por vigência
PROPRIEDADES ChaveProp cliente/fornecedor
VEICULOS SeqVeic veículo/navio
MUNICIPIO CodMun municípios
MDF, MDFCOMPL, NFEEVENTO, MDFITENS, NFMDF, NFCOMPLMDF, FRETES chaves do MDF-e ciclo do MDF-e (origem PMDFE, usado com a Sascar)

Regras do espelho:

  • Cada linha ganha um id próprio da nuvem, mas quem identifica o registro é a chave do X-Adm. Reenviar o mesmo registro atualiza, nunca duplica.
  • Apagar no X-Adm (DELETE) não apaga na nuvem: marca deleted = true. Reenviar com PUT revive a linha.
  • Tipos do ZIM: VastInt(n) vira número decimal; data AAAAMMDD vira data.
  • Colunas de nome curto (NomeProdCurto, NomePropCurto, DescricaoCurta) são editadas no app e o X-Adm nunca sobrescreve.

Precisa de tabela ou coluna nova? A equipe X-Adm define o que falta (tabela do ZIM, campos, chave) e passa para a nuvem. Na nuvem, o trabalho é no repo integrador-server — migration nova, entidade, leitura do JSON e o gravador da tabela — seguido de publicação do integrador para todos os clientes. Coluna nova que o ERP ainda não manda não quebra nada: fica vazia até o ERP enviar. O roteiro do código está no guia do integrador; o modelo consolidado, na modelagem.

2. Escolher o que sincroniza

O banco recebe tudo o que o ERP manda; os apps recebem só o que está nas sync rules do cliente, no powersync.yaml do repo <cliente>-powersync. Sulplata e OnPetro Trading sincronizam as 13 tabelas de negócio:

streams:
  global:
    auto_subscribe: true
    queries:
      - SELECT * FROM contratos    WHERE deleted = false
      - SELECT * FROM estoque      WHERE deleted = false
      - SELECT * FROM itens        WHERE deleted = false
      # … itenslote, itensped, itenspedamx, itpedamxdi, loteest,
      #   municipio, notacompl, propriedades, tbpcoest, veiculos

Incluir tabela: acrescente a linha SELECT, publique o repo do PowerSync e confira a publicação do banco (Cliente novo — banco do cliente). Mudar sync rule faz todos os aparelhos baixarem tudo de novo — agende fora do horário de pico.

Os apps desses clientes entram com login Google (projeto Firebase navarro-app); o PowerSync aceita o token do Google direto. Detalhe em PowerSync.

3. Lado do X-Adm: gerar e enviar o JSON

Este trecho é da equipe X-Adm: o ERP gera o .json a cada gravação numa pasta do servidor do cliente, e o agente X-Adm Integração Java monitora a pasta e envia cada arquivo ao integrador.

A preencher pela equipe X-Adm

Instalação e configuração do agente Java — <PLACEHOLDER>:

  • pasta monitorada e padrão de nome dos arquivos: <PLACEHOLDER>;
  • arquivo de configuração e chaves (URL, token): <PLACEHOLDER>;
  • o que o agente faz com o arquivo depois de enviar (apaga, move para enviados/, erros/): <PLACEHOLDER>;
  • política de nova tentativa (intervalo, limite): <PLACEHOLDER>;
  • como ligar o agente no início do Windows e onde fica o log: <PLACEHOLDER>.

Enquanto não for preenchido, o contrato abaixo é tudo o que a nuvem exige do agente.

O contrato que o agente tem de cumprir:

Item Valor
Endereço https://int.<cliente>.xadm.biz/api/v1/xadm
Gravar / atualizar PUT
Apagar DELETE (mesmo corpo; apaga pelas chaves do X-Adm)
Cabeçalhos Authorization: Bearer <INTEGRADOR_API_TOKEN> · Content-Type: application/json
Corpo objeto com Origem (a rotina do X-Adm) e uma chave por tabela, cada uma com uma lista de registros
{
  "Origem": "PPEDAMX",
  "CONTRATOS": [ { "ChaveCP": " 1 0P  242D     3451", "ChaveProp": " 1 0  242 0", "DtAprov": "20260116", "SeqVeic": "    81" } ],
  "ITENSPED":  [ { "ChaveItem": "…", "ChaveCP": " 1 0P  242D     3451", "…": "…" } ]
}

Exemplos reais completos, por rotina: PUT e DELETE de um pedido (PPEDAMX); os demais (ex2 a ex7) ficam na mesma pasta.

Regras que o agente tem de seguir:

  1. Ler o corpo da resposta, não só o status. O integrador responde 200 mesmo quando falhou:

    { "requestId": 1234, "resultado": "SUCESSO", "mensagem": null }
    
    Resposta Significa O que o agente faz
    200 + SUCESSO gravado segue para o próximo
    200 + ERRO recebido, mas não gravou (a mensagem diz tabela, registro e motivo) registra o erro e não repete sem corrigir — repetir dá o mesmo erro
    400 JSON malformado corrigir a geração do arquivo
    401 token ausente ou errado conferir o token com a nuvem
    500, sem resposta, tempo esgotado falha de rede ou da nuvem tentar de novo mais tarde — é seguro
  2. Enviar na ordem em que os arquivos foram gerados, um de cada vez. A ordem importa (um DELETE seguido de PUT do mesmo registro tem de chegar nessa ordem).

  3. Reenviar é seguro. O mesmo registro enviado duas vezes é atualizado, não duplicado.
  4. Usar as origens conhecidas: PPEDAMX, PPEDIDO, PBLDI, PNOTAI, PNOTAD, PCSTPROD, PREQUIS (e INT-PIED, do cenário 3). Origem fora dessa lista (hoje, inclusive a PMDFE, do MDF-e) funciona, mas os erros dela não disparam alarme até ser cadastrada no integrador — avise a nuvem antes de usar uma origem nova.
  5. Nome da tabela em MAIÚSCULAS (CONTRATOS, não Contratos). Tabela com nome errado é ignorada — se nenhuma tabela do corpo for reconhecida, a resposta é ERRO com "payload sem nenhuma tabela reconhecida".
  6. Valores como o ZIM gera, inclusive os espaços das chaves: " 1 0P 242D 3451" e "1 0P 242D 3451" são registros diferentes.

Contrato completo, com todas as tabelas e campos: Contrato de ingestão do integrador.

4. Na nuvem: o que o integrador faz ao receber

  1. Guarda o JSON recebido, como veio, na tabela de auditoria request (com a origem e o horário).
  2. Lê as tabelas do corpo e grava tudo numa transação só: ou grava o arquivo inteiro, ou nada.
  3. Para cada registro: procura pela chave do X-Adm; se existe, atualiza; se não, cria; se estava apagado, revive.
  4. Marca o request como SUCESSO ou ERRO (com a mensagem) e responde ao agente.
  5. Erro de origem conhecida vira alerta no GlitchTip (bug.xadm.biz).
  6. Nota de venda (PNOTAI) e BL liberado (PBLDI) disparam notificação no celular, onde o cliente tem notificação ligada.

Registros descartados sem erro

Algumas linhas incompletas são ignoradas em silêncio (o arquivo volta SUCESSO): ITENS sem DtEst, Qtde ou Valor; ITENSLOTE sem Qtde ou Saldo; TBPCOEST sem ValIni, ValFin ou VlProd; ITPEDAMXDI sem SeqNItAmx. Se um registro "não aparece" no app, confira esses campos primeiro.

5. Distribuição aos apps

O PowerSync lê as mudanças do banco e entrega a cada app conectado. O app funciona sem internet e atualiza quando reconecta.

Quando o usuário edita um campo permitido no app (por exemplo o nome curto de um produto), o app devolve a alteração ao integrador (POST /api/v1/powersync), que grava no banco; o PowerSync então distribui a todos.

Verificação

Conferir Como Esperado
O arquivo chegou no banco do cliente: SELECT id, origem, tipo, resultado, mensagem, criado_em FROM request ORDER BY id DESC LIMIT 20; a linha do envio com SUCESSO
Os erros recentes SELECT * FROM request WHERE resultado = 'ERRO' ORDER BY id DESC; vazio; senão, a mensagem diz a tabela, o registro e o motivo
O registro está no espelho SELECT * FROM contratos WHERE chave_cp = '<chave como o ZIM gera>'; a linha, com deleted = false
O banco está recebendo GET https://int.<cliente>.xadm.biz/api/v1/powersync/lastchange horário da última mudança recente
O app recebeu abrir o app do cliente o registro aparece em segundos
Alertas GlitchTip (bug.xadm.biz), projeto integrador-server sem erro novo da origem

Problemas comuns

Sintoma Causa provável O que fazer
Todo envio volta 401 token do agente diferente do INTEGRADOR_API_TOKEN do integrador conferir os dois lados; trocar ao mesmo tempo
200 mas o dado não aparece o agente trata 200 como sucesso e o corpo dizia ERRO olhar request com resultado = 'ERRO'
SUCESSO mas o registro não aparece linha descartada por campo obrigatório vazio (ver o aviso do passo 4) ou tabela fora das sync rules conferir os campos; conferir o powersync.yaml
Registro duplicado no app a chave veio com espaçamento diferente em envios diferentes padronizar a geração da chave no ZIM
App parado, banco recebendo PowerSync fora do ar ou sync rule sem a tabela Incidentes comuns