Pular para o conteúdo
Softeum PedidosIntegração com o ERP

Guia de integração do ERP com o Softeum Pedidos

Para a equipe de TI: como o ERP recebe, confirma e recusa os pedidos e envia os cadastros, com exemplos prontos.

docs.softeum.com.br/guia/

Seções desta página

Para a equipe de TI da indústria (o cliente do Softeum). O Softeum lê os pedidos de compra que chegam por e-mail, confere e deixa o pedido pronto para o ERP. O ERP recebe o pedido de um de dois jeitos — o Softeum avisa (um POST assinado no endereço do ERP) ou o ERP busca (na API REST, com uma chave de acesso) —, lança o pedido de venda e confirma com o número dele. O ERP também pode enviar os cadastros (produtos, compradores, vendedores e tabelas de preço) pela mesma API. Este guia tem tudo o que é preciso, com exemplos prontos para rodar.

Serve para qualquer ERP: SAP, TOTVS (Protheus, Winthor, Datasul, RM), Sankhya, Senior, Omie, Bling, Tiny/Olist, Microsoft Dynamics, Oracle NetSuite, Odoo ou um ERP próprio. A seção 14 traz o caminho de cada um.

Suporte da Softeum: diiego.martins@softeum.com.br. Nunca mande por e-mail a chave de acesso, o segredo ou a senha do SFTP: quem digita é o administrador, no painel.

Situação (25/10/2026)

Recurso Situação
Aviso de pedido pronto (pedido.aprovado), assinado com HMAC, com reenvio automático e confirmação pela resposta Disponível
Aviso de teste (botão Enviar teste do painel) Disponível
Pedido de teste para a busca (botão Criar pedido de teste do painel) Disponível (12/11/2026)
API REST de pedidos: listar, buscar um, confirmar, recusar, baixar o documento original No ar — https://api.softeum.com.br/v1
API REST de cadastros: produtos, compradores, vendedores, tabelas de preço No ar — mesmo endereço

O formato completo de cada campo também está no arquivo OpenAPI 3.1 (no painel: botão Arquivo OpenAPI, no topo deste guia e da tela Integração com o ERP). Abre no Swagger Editor, Postman ou Insomnia, e geradores como o OpenAPI Generator criam as classes a partir dele. Se este guia e o arquivo discordarem, vale o arquivo — e avise a Softeum.

Sumário

  1. Comece aqui: aviso ou busca
  2. Como funciona
  3. Configurar no painel
  4. O aviso pedido.aprovado
  5. Os campos do pedido e onde lançar cada um
  6. Conferir a assinatura do aviso
  7. Responder ao aviso e não lançar duas vezes
  8. Exemplos prontos e simulador
  9. Aviso de teste e pedido de teste
  10. O que o painel mostra
  11. Problemas comuns
  12. Antes de ir para produção
  13. Perguntas frequentes
  14. API REST: referência
  15. Por ERP: o caminho de cada um
  16. Entrega por arquivo (SFTP)

0. Comece aqui: aviso ou busca

Aviso (o Softeum entrega) Busca (o ERP vai buscar)
Como chega POST assinado no endereço do ERP, assim que o pedido fica pronto O ERP chama GET /v1/pedidos?situacao=pronto de tempos em tempos
O ERP precisa de Um endereço HTTPS público que receba POST Só sair para a internet (o ERP pode ficar dentro da rede da empresa)
Demora Segundos O intervalo da busca (1 a 5 minutos é o comum)
Confirmação Na resposta ao aviso ({"numero_erp": "..."}) ou depois, pela API POST /v1/pedidos/{id}/confirmacao
Comece por receptor.mjs / .py / .php / Program.cs (seção 7) buscar-pedidos.mjs / buscar_pedidos.py (seção 7)

Os dois podem conviver: o aviso entrega, e a API serve para confirmar depois, recusar com o motivo ou buscar o que ficou para trás. O pedido entregue pelo aviso deixa de aparecer em situacao=pronto.

ERP que importa pedidos de uma pasta (SAP pela interface de arquivos, Protheus, ERPs que leem XML, CSV ou TXT): há um terceiro caminho, sem programar nada. O Softeum grava cada pedido aprovado como um arquivo, no layout do ERP, na pasta de um servidor SFTP. Veja a seção 15.

Pelo aviso, em 4 passos

  1. Suba um endereço HTTPS público no ERP que receba POST com JSON. Comece por um dos receptores prontos da seção 7: eles já conferem a assinatura, ignoram o aviso de teste, não lançam o mesmo pedido duas vezes e devolvem o número do ERP. Falta só escrever o lançamento no seu ERP.
  2. Cadastre o endereço no painel (Integração → Como o ERP recebe → Aviso no endereço do ERP) e guarde o segredo que aparece.
  3. Teste com o simulador da seção 7 (no seu ambiente, sem depender do painel) e depois com o botão Enviar teste do painel.
  4. Lance um pedido de verdade e devolva o número: o pedido aparece como confirmado no painel.

Pela busca, em 5 passos

  1. Crie a chave de acesso no painel (Integração → Como o ERP recebe → O ERP busca os pedidos). Ela começa com sp_live_ e aparece uma vez só: guarde no cofre de senhas do ERP.
  2. Comece pelo buscar-pedidos.mjs ou buscar_pedidos.py (seção 7): eles buscam os prontos, chamam o seu lançamento e confirmam (ou recusam com o motivo). Falta só escrever o lançamento no seu ERP.
  3. Agende a busca a cada 1 a 5 minutos (cron, Agendador de Tarefas do Windows, job do ERP).
  4. Teste com um pedido de teste, antes do primeiro pedido de verdade: o administrador clica em Criar pedido de teste no painel (no mesmo lugar da chave) e um pedido pronto, montado com os compradores e os produtos do cadastro da indústria, aparece na busca com "teste": true. Lance no ERP de homologação e confirme; crie outro e recuse, para testar a recusa (seção 8). O ERP de produção recusa o pedido de teste.
  5. Lance um pedido de verdade: ele aparece como confirmado no painel e some de situacao=pronto.

Sem endereço de entrega cadastrado, o pedido pronto fica esperando a busca do ERP (no painel: "Pronto para enviar").

Palavras deste guia

Palavra O que é
Aviso O POST que o Softeum faz no endereço do ERP quando um pedido fica pronto (um webhook).
Busca O ERP chama a API REST para pegar os pedidos prontos.
Chave de acesso Texto sp_live_... que identifica a sua empresa na API REST (cabeçalho Authorization: Bearer).
Endereço de entrega A URL HTTPS do ERP que recebe os avisos. Uma por empresa.
Segredo Texto whsec_... que só o painel e o ERP conhecem; com ele o ERP confere que o aviso veio do Softeum.
Pedido pronto Lido, conferido e aprovado (direto ou pelo time de faturamento). Só pedido pronto vai ao ERP.
Confirmado O ERP lançou e devolveu o número do pedido de venda.
Comprador O cliente da indústria que fez o pedido.
Vendedor O representante da indústria que encaminhou o e-mail do pedido.

1. Como funciona

Comprador ──e-mail──▶ Vendedor ──encaminha──▶ Contas do Outlook da indústria
                                                       │
                                     Softeum lê (XML, planilha, PDF, texto)
                                     e confere: comprador, produtos (de-para),
                                     preços, campos obrigatórios, duplicados
                                                       │
                          ┌────────────────────────────┴───────────────────┐
                    tudo certo                                     tem pendência
                          │                                               │
                          │                               time de faturamento revisa
                          │                               no painel e aprova (ou recusa)
                          ▼                                               │
                   pedido PRONTO ◀───────────────────────────────────────┘
                          │
        ┌─────────────────┴──────────────────────────────┐
     AVISO (com endereço de entrega)                 BUSCA (pela API)
        │                                                │
  POST assinado no endereço do ERP          GET /v1/pedidos?situacao=pronto
        │                                                │
  2xx com {"numero_erp": "..."} → CONFIRMADO   POST /v1/pedidos/{id}/confirmacao → CONFIRMADO
  2xx sem número               → ENVIADO      POST /v1/pedidos/{id}/recusa      → ERRO (com o motivo)
  sem 2xx → reenvio em 1, 5, 30,              (sem confirmar, o pedido continua PRONTO
  120 e 360 min → ERRO no painel               e volta na próxima busca)
  • O ERP só recebe pedido aprovado. Pedido com pendência (preço fora da tolerância, produto sem de-para, CNPJ desconhecido, valor acima do limite configurado etc.) fica com o time de faturamento até ser resolvido. Pedido recusado pelo time nunca chega ao ERP.
  • Todo item já vem com o produto do catálogo da indústria identificado (itens[].produto). O ERP não faz de-para.
  • Um e-mail pode gerar mais de um pedido: cada anexo com pedido (PDF, planilha, XML, foto) vira um pedido, com id próprio e aviso próprio. Um arquivo só com vários pedidos dentro ainda não é separado: vira um pedido.
  • O Softeum não envia nada ao comprador. Quem fala com o comprador é a indústria.

2. Configurar no painel

Com um usuário administrador da indústria:

  1. Integração → Como o ERP recebe → Aviso no endereço do ERP: informe o endereço HTTPS do ERP.
    • Tem que começar com https:// e ser público: endereço de rede interna, localhost, IP privado, *.local e *.internal são recusados — ao salvar e de novo a cada envio.
    • Não coloque usuário e senha no endereço. Para identificar a origem, use um caminho ou um parâmetro (https://erp.suaempresa.com.br/softeum/pedidos?origem=softeum).
    • Não seguimos redirecionamento: 301, 302 e 307 contam como falha. Informe o endereço final.
    • Qualquer porta é aceita (ex.: https://erp.suaempresa.com.br:8443/...).
    • O certificado TLS precisa ser válido, com a cadeia completa, emitido por uma autoridade pública.
  2. Ao salvar pela primeira vez, o painel mostra o segredo de assinatura (whsec_ + 48 caracteres). Guarde no cofre de senhas do ERP. Ele pode ser revisto (Mostrar, Copiar) e trocado (Gerar novo segredo); ao trocar, o anterior para de valer na hora — atualize o ERP antes.
  3. Clique em Enviar teste (seção 8) e confira se o ERP respondeu 2xx.

Depois disso, todo pedido aprovado vai para o endereço. Trocar o endereço mantém o mesmo segredo.

Para a API REST, crie a chave em Integração → Como o ERP recebe → O ERP busca os pedidos (só o administrador). A chave começa com sp_live_, aparece uma vez só (guarde no cofre de senhas do ERP) e pode ser revogada a qualquer momento — revogar corta o acesso na hora e não tem volta. Use uma chave por sistema (produção, homologação) para poder revogar uma sem parar a outra. Quem só busca pela API não precisa cadastrar endereço de entrega.


3. O aviso pedido.aprovado

3.1 A requisição

POST https://erp.suaempresa.com.br/softeum/pedidos
Content-Type: application/json
User-Agent: Softeum-Pedidos
Softeum-Evento: pedido.aprovado
Softeum-Entrega: 0199a3c4-5f10-7a2b-8c3d-4e5f60718294
Softeum-Assinatura: t=1759261325,v1=5d1c...e9a0

{ "id": "...", "tipo": "pedido.aprovado", "criado_em": "...", "pedido": { ... } }
Cabeçalho O que é
Softeum-Evento O tipo do aviso: pedido.aprovado (ou teste, no aviso de teste).
Softeum-Entrega O id do aviso. É o mesmo em todos os reenvios do mesmo pedido.
Softeum-Assinatura t=<segundos desde 1970, UTC>,v1=<HMAC-SHA256 em hexadecimal minúsculo> (seção 5).

O corpo é JSON em UTF-8. Leia o corpo como texto bruto antes de converter: a assinatura é calculada sobre os bytes exatos que chegaram.

3.2 O corpo

{
  "id": "0199a3c4-5f10-7a2b-8c3d-4e5f60718294",
  "tipo": "pedido.aprovado",
  "criado_em": "2026-09-30T16:42:05-03:00",
  "pedido": {
    "id": "0199a3c4-5f10-7a2b-8c3d-4e5f60718293",
    "numero_comprador": "45882",
    "situacao": "pronto",
    "origem": "pdf",
    "recebido_em": "2026-09-30T16:13:00-03:00",
    "aprovado_em": "2026-09-30T16:42:00-03:00",
    "comprador": { "cnpj": "12345678000195", "razao_social": "Distribuidora Rio Claro Ltda", "codigo_erp": "C001" },
    "vendedor": { "nome": "Carlos Menezes", "email": "carlos.menezes@valeazul.com.br", "codigo_erp": null },
    "condicao_pagamento": "28/42/56 dias",
    "data_pedido": "2026-09-29",
    "data_entrega": "2026-10-07",
    "endereco_entrega": "CD Rio Claro, Av. Brasil, 2150",
    "observacoes": null,
    "itens": [
      {
        "linha": 4,
        "codigo_comprador": "004359",
        "descricao_comprador": "LIMPA VIDROS REFIL 500ML CX12",
        "produto": { "sku": "VA-LVD-500R", "codigo_erp": "4359", "descricao": "Limpa-vidros refil 500 ml", "ean": "7896543000415" },
        "quantidade": 30,
        "unidade": "CX",
        "quantidade_erp": 360,
        "unidade_erp": "UN",
        "preco_unitario": 76.04,
        "valor_total": 2281.2,
        "data_entrega": null
      }
    ],
    "valor_total": 2281.2,
    "revisao": { "revisado_por": "Marina Costa", "revisado_em": "2026-09-30T16:40:00-03:00", "motivos": ["preco"] },
    "numero_erp": null,
    "arquivo_original": "https://.../pedido-45882.pdf?token=..."
  }
}

Pedido aprovado direto, sem revisão, vem com "revisao": null. Vendedor sem e-mail cadastrado vem com "vendedor": null.


4. Os campos do pedido e onde lançar cada um

Datas e horas vêm em ISO 8601 com o fuso da empresa (2026-09-30T16:42:00-03:00); datas sem hora, aaaa-mm-dd. Números usam ponto decimal. Todo campo listado vem sempre no JSON; "ou null" quer dizer que o valor pode ser nulo.

4.1 Onde cada campo entra no pedido de venda

No pedido de venda do ERP Use Observação
Chave de integração (evita duplicar) pedido.id Grave numa coluna própria com restrição de unicidade.
Cliente comprador.codigo_erp Sem código, procure pelo comprador.cnpj.
Nº do pedido do cliente numero_comprador Pode vir vazio. Não é único entre clientes.
Vendedor / representante vendedor.codigo_erp vendedor pode ser null.
Condição de pagamento condicao_pagamento Texto livre: traduza para o código do ERP (seção 12).
Data do pedido (emissão) data_pedido null = o documento não traz: use recebido_em.
Data de entrega data_entrega null = sem data pedida. Com uma data por item, a de cada item vem em itens[].data_entrega.
Endereço de entrega endereco_entrega null = endereço do cadastro do cliente.
Observação observacoes
Produto do item itens[].produto.codigo_erp Sem código, procure pelo sku.
Quantidade e unidade itens[].quantidade + itens[].unidade Ou quantidade_erp + unidade_erp, com o preço convertido (veja o alerta em 4.4).
Preço unitário itens[].preco_unitario Líquido, por unidade.
Total do item itens[].valor_total quantidade × preco_unitario.
Total do pedido valor_total Sem frete e sem impostos.

4.2 Identificação e datas

Campo Tipo O que é e como usar
id uuid Identificador do pedido no Softeum. Use como chave única para não lançar o mesmo pedido duas vezes.
teste true (só no pedido de teste) Vem só no pedido de teste, criado no painel para a TI testar a busca (seção 8); nos outros pedidos a chave nem aparece. Lance no ERP de homologação; no de produção, recuse com o motivo, e ele sai da fila (os exemplos da seção 7 recusam sozinhos). Novo em 12/11/2026.
numero_comprador texto (pode ser "") Número do pedido no documento do comprador. Vem vazio quando o documento não trazia número e o time aprovou assim.
situacao texto No aviso é sempre pronto.
origem texto De onde foi lido: pdf (inclusive digitalizado), word (documento Word, .docx), excel, xml, imagem (foto ou print do pedido, anexado ou colado no e-mail), corpo (texto do e-mail) ou importado. A lista pode crescer: trate um valor que você não conhece como genérico, sem recusar o pedido.
recebido_em data e hora Quando o e-mail chegou à conta do Outlook (a indústria pode ter mais de uma).
aprovado_em data e hora Quando o pedido ficou pronto. Igual em todos os reenvios.
data_pedido aaaa-mm-dd ou null A data em que o comprador fez o pedido, como o documento traz (a emissão: "Data Emissão", "Data do pedido"). O e-mail encaminhado dias depois não muda esta data. null quando o documento não traz: use recebido_em. Novo em 11/11/2026.

4.3 Comprador, vendedor e condições

Campo Tipo O que é e como usar
comprador.cnpj 14 dígitos Só números. Do cadastro de compradores do Softeum (o comprador ligado ao pedido), e não o lido do documento: um dígito lido errado não chega ao ERP. Mudou em 02/11/2026 (antes vinha o CNPJ lido).
comprador.razao_social texto Do cadastro de compradores do Softeum.
comprador.codigo_erp texto ou null Código do cliente no seu ERP. Cadastrar no Softeum (Compradores ou planilha) evita a busca por CNPJ.
vendedor objeto ou null Quem encaminhou o e-mail. null quando o vendedor não tem e-mail cadastrado.
vendedor.nome / email texto Nome e e-mail do vendedor.
vendedor.codigo_erp texto ou null Código do vendedor/representante no ERP.
condicao_pagamento texto ou null Como o comprador escreveu (28/42/56 dias, 30 DDL).
data_entrega aaaa-mm-dd ou null Data de entrega pedida. Quando o documento traz uma por item e todas iguais, é ela. Com datas diferentes nos itens, o pedido passou pela revisão, e a de cada item vem em itens[].data_entrega.
endereco_entrega texto ou null Como veio no documento, numa linha só.
observacoes texto ou null Observações do pedido.

4.4 Itens (itens[], pelo menos um)

Campo Tipo O que é e como usar
linha inteiro Posição do item no documento do comprador. Pode pular números: item tirado na revisão não vem.
codigo_comprador texto ou null Código do produto como o comprador escreveu. Só para conferência.
descricao_comprador texto ou null Descrição como o comprador escreveu. Só para conferência.
produto.sku texto SKU do produto no catálogo da indústria (já identificado pelo de-para).
produto.codigo_erp texto ou null Código do produto no seu ERP: use para lançar. null = produto sem código do ERP no cadastro; procure pelo sku e corrija o cadastro.
produto.descricao texto Descrição do catálogo da indústria.
produto.ean texto ou null EAN/GTIN do catálogo.
quantidade número, até 4 casas Quantidade na unidade do comprador (unidade).
unidade texto ou null Unidade do comprador (CX, FD, UN).
quantidade_erp número A mesma quantidade convertida para unidade_erp pelo fator do de-para (30 CX de 12 → 360 UN). Sem conversão, igual a quantidade.
unidade_erp texto Unidade de quantidade_erp, do cadastro do produto, nunca do texto do comprador: a embalagem do produto vendido por medida (KG, L, M) ou com 1 unidade na embalagem (CX); UN na embalagem com mais de uma. É a do cadastro de quando a conta de quantidade_erp foi feita (na leitura, ou na revisão quando o produto ou a conversão mudou), gravada junto com ela: o cadastro corrigido depois não muda a unidade de uma quantidade já calculada (o produto em CX com 1 unidade, corrigido para CX com 12 depois da leitura, segue como 10 CX, e não 10 UN). Mudou em 02/11/2026: antes vinha do texto do comprador ou um UN fixo (25 CX de frango vendido por KG, 250 em quantidade_erp, saíam como 250 UN).
preco_unitario número, até 4 casas Preço líquido por unidade do comprador, já com o desconto do pedido (4.7) e decidido na revisão quando houve.
valor_total número, 2 casas quantidade × preco_unitario.
data_entrega aaaa-mm-dd ou null A data de entrega deste item, quando o documento do comprador traz uma por item (o pedido do Protheus). null: vale a data_entrega do pedido. Novo em 11/11/2026.
desconto objeto, null ou ausente Só na indústria que lê o desconto de cada item (a equipe Softeum liga): o desconto escrito no próprio item, { "percentual": 10.5, "tipo": "comercial" } (percentual maior que 0 e até 100; tipo comercial, especial ou promocional), e null no item sem desconto. Nas outras indústrias a chave não aparece. O preco_unitario já vem com esse desconto: não aplique de novo.

Atenção à unidade do preço. preco_unitario é por unidade (a do comprador), não por unidade_erp. Se o ERP lança o item em quantidade_erp/unidade_erp, o preço por unidade do ERP é valor_total ÷ quantidade_erp (no exemplo: 2.281,20 ÷ 360 = 6,3367 por UN). Lançar quantidade_erp com preco_unitario multiplica o valor do item pelo fator da embalagem — 12 vezes, no exemplo.

4.5 Totais, revisão e arquivo

Campo Tipo O que é
valor_total número Soma de itens[].valor_total, já com as decisões da revisão. Sem frete e sem impostos: o ERP calcula IPI, ICMS-ST etc.
revisao objeto ou null Preenchido quando o pedido passou pela revisão: revisado_por, revisado_em e motivos — códigos preco, depara, cnpj, faltam, catalogo, duplicado, valor, leitura, sempre_revisar (o comprador da lista "sempre revisar" ou bloqueado, o vendedor reconhecido por quem encaminhou o e-mail, e não pelo remetente, ou o e-mail recebido há 7 dias ou mais). Só o que mandou o pedido para a revisão: os avisos (a regra em "só avisar", o produto sem preço na tabela do comprador) não entram. Útil para auditoria.
numero_erp null No aviso é sempre null (o ERP ainda não lançou).
arquivo_original URL Documento original do comprador (PDF, planilha, XML ou o e-mail .eml). Vale 1 hora a partir do envio: para guardar o arquivo, baixe na hora. Os arquivos ficam guardados conosco por 12 meses a partir do recebimento. Sem arquivo (ou depois dos 12 meses), vem um endereço que não baixa nada.

Prepare o ERP para campos novos: ignore campos que não conhece e não dependa da ordem dos campos — o formato pode ganhar campos com o tempo.

4.6 O que o pedido não traz: o ERP completa

O pedido traz o que o comprador pediu. O pedido de venda do ERP pede também dados internos da indústria, que nenhum comprador informa. Toda integração precisa decidir estes, e o time do ERP vai perguntar por eles:

Dado do pedido de venda Como se chama em alguns ERPs Como preencher
Empresa ou filial que fatura Filial (Protheus, Winthor), empresa (Sankhya), estabelecimento (Datasul), organização de vendas (SAP) Fixo na integração. Indústria que fatura por mais de uma filial: defina a regra (por exemplo, pela UF do cliente) e fale com a Softeum se precisar de um campo no pedido.
Tipo de operação TES (Protheus), tipo de venda (Winthor), TOP (Sankhya), tipo de ordem (SAP), natureza de operação (Tiny/Olist, Datasul), etapa (Omie) Fixo: a operação de venda normal da indústria.
Tabela de preço Tabela de preço (Protheus, Tiny/Olist), região de preço (Winthor) Do cadastro do cliente no ERP. O preço do item já vem pronto (4.7).
Forma de cobrança e plano de pagamento Cobrança e plano de pagamento (Winthor), forma de pagamento Traduza condicao_pagamento (seção 12) ou use a do cadastro do cliente.
Transportadora e frete (CIF/FOB) Transportadora, tipo de frete Do cadastro do cliente ou fixo.
Canal, setor, natureza financeira, centro de custo, depósito Canal de distribuição e setor de atividade (SAP), natureza e centro de custo (Sankhya), armazém (Protheus) Fixos na integração.
Data de emissão A data do lançamento no ERP. aprovado_em diz quando o pedido ficou pronto.

Guarde esses valores numa configuração da integração (arquivo ou tabela), não no meio do código: muda a filial ou a operação, troca-se o valor sem programar.

4.7 Preço: lance o preço que veio

  • preco_unitario é o preço que vale para o pedido. Quando a indústria tem tabelas de preço no Softeum, ele já foi conferido contra elas, e a indústria escolhe em Configurações → Regras de conferência → Preço fora da tolerância qual preço segue:
    • Revisão (padrão): dentro da tolerância segue o preço do comprador; fora dela, o time de faturamento decide o preço na revisão;
    • Preço do pedido: segue sempre o preço que o comprador mandou;
    • Preço da tabela: segue sempre o preço da tabela de preço do comprador, no jeito da linha do pedido (o preço da embalagem, da unidade ou da conversão da linha, como a conferência decidiu; com ou sem IPI). Produto sem preço na tabela segue com o preço do comprador. Nas três, com o produto na tabela do comprador, a linha cujo preço não está na base da quantidade (o da unidade numa linha em caixas, ou o contrário) passa antes pela revisão: o pedido não chega com a quantidade numa base e o preço na outra. Sem o preço do produto na tabela, não há com o que comparar: a linha segue na unidade escrita no pedido, com o preço do comprador (a linha sem unidade, de produto com mais de uma unidade na embalagem, passa pela revisão).
  • O desconto do pedido já está no preço. O desconto dado no próprio item vem no preco_unitario dele. O desconto dado sobre o total do pedido (o documento traz "Desconto: R$ 345,92" no rodapé, e não em cada item) também vem no preço: ele é distribuído nos itens em proporção ao valor de cada linha, e a soma dos itens dá o total do documento. Não lance o desconto de novo no cabeçalho do pedido do ERP: ele seria aplicado duas vezes. Por isso o preço pode vir com até 4 casas decimais (76.0412).
  • Desconto de cada item (só na indústria que liga). Para o ERP que lança o desconto item a item (o percentual e o tipo, e não o preço já com o desconto), a equipe Softeum liga a leitura do desconto de cada item da indústria. Os pedidos lidos depois disso trazem, em cada item, o desconto escrito no documento: "desconto": { "percentual": 10.5, "tipo": "comercial" }, ou null no item sem desconto (4.4). O preco_unitario continua com o desconto: o preço de tabela do pedido é preco_unitario ÷ (1 − percentual ÷ 100) (11,0712 com 10,5% → 12,37). O ERP que lança o desconto lança o preço de tabela com ele; o que lança o preco_unitario não lança o desconto: nunca os dois. Nas outras indústrias, e nos pedidos lidos antes de ligar, a chave não vem.
  • Lance o pedido com o preço informado, sem recalcular pela tabela do ERP. Recalcular troca o preço que o comprador aceitou e o pedido sai diferente do que foi conferido.
  • Se o ERP bloqueia pela política comercial dele (desconto acima do permitido, preço abaixo do mínimo, cliente com crédito bloqueado), não lance pela metade: recuse pela API (POST /v1/pedidos/{id}/recusa) com o motivo. O pedido volta para o time com o seu texto.
  • O preço vem sem impostos: o ERP calcula IPI, ICMS-ST e os demais (seção 12). Atenção também à unidade do preço (4.4).

4.8 Cliente e produto com código composto

codigo_erp é texto livre de até 60 caracteres, igual para compradores, produtos e vendedores. Quando o ERP identifica o registro por mais de uma parte, cadastre as partes juntas no Softeum e separe na integração:

  • Protheus: cliente é código + loja (000123 + 01). Cadastre 000123-01 e separe pelo hífen. O modelo pronto do arquivo também aceita 00012301 (a loja depois das 6 posições do A1_COD).
  • SAP: o número do cliente tem 10 dígitos, com zeros à esquerda (0000100001). Cadastre já com os zeros, ou complete na integração.
  • ERP com o id interno diferente do código visível (Omie, Bling, Tiny/Olist): cadastre o código que o seu lançamento usa e, se a API do ERP pede o id interno, busque o id pelo código antes de lançar.

4.9 Nota fiscal: pedido e item do comprador (xPed e nItemPed)

Muitos compradores (redes, indústrias, grandes distribuidores) exigem na NF-e o número do pedido de compra deles (xPed) e o número do item nesse pedido (nItemPed), nos campos próprios do XML, e recusam a nota sem eles.

  • xPed: use numero_comprador. A NF-e aceita até 15 caracteres; alguns compradores pedem só números. Siga a regra do comprador.
  • nItemPed: linha é a posição do item no documento do comprador (1, 2, 3...). Serve quando o pedido dele numera os itens em sequência. Quando numera de outro jeito (10, 20, 30, comum em pedido gerado por SAP), o Softeum ainda não traz o número impresso no item: fale com a Softeum antes de faturar para esse comprador.
  • Guarde os dois no pedido de venda do ERP: é de lá que a nota fiscal sai.

4.10 O que ainda não vem no pedido

Para planejar a integração sem surpresa:

  • Endereço de entrega em partes (rua, número, bairro, cidade, UF, CEP): vem numa linha só, como o comprador escreveu (endereco_entrega). null = o endereço do cadastro do cliente no ERP.
  • Comprador pessoa física (CPF): ainda não é aceito; o comprador é sempre uma empresa (CNPJ).
  • Número do item impresso no pedido do comprador (4.9).
  • Transportadora e tipo de frete pedidos pelo comprador (4.6).
  • Aviso de cancelamento: não existe (seção 12).
  • Vários pedidos dentro do mesmo arquivo: chegam como um pedido só (seção 1).

5. Conferir a assinatura do aviso (obrigatório)

Sem conferir a assinatura, qualquer pessoa que descubra o endereço consegue criar pedidos falsos no ERP.

  1. Leia o cabeçalho Softeum-Assinatura e separe t e v1.
  2. Recuse se t estiver a mais de 5 minutos do relógio do servidor (para mais ou para menos). Mantenha o relógio sincronizado (NTP).
  3. Monte o texto "<t>.<corpo bruto>": o valor de t, um ponto e o corpo exatamente como chegou, sem reformatar o JSON.
  4. Calcule HMAC-SHA256 desse texto com o segredo inteiro como chave (incluindo o prefixo whsec_), em UTF-8, e escreva em hexadecimal minúsculo.
  5. Compare com v1 em tempo constante. Diferente → responda 401 e não lance nada.

Vetor de teste

Para conferir a implementação antes de ligar no painel (desligue a checagem dos 5 minutos ou fixe o "agora" em 1759261500):

segredo:   whsec_exemplo0123456789abcdef0123456789abcdef01234567
t:         1759261500
corpo:     {"id":"0199a3c4-5f10-7a2b-8c3d-4e5f60718295","tipo":"teste","criado_em":"2026-09-30T16:45:00-03:00"}
cabeçalho: t=1759261500,v1=7c088d8e8b8e9253568c05345d02d1110c91b1169ad13541859bdab7bffa5f83

O código, em quatro linguagens

A mesma função está dentro dos exemplos completos da seção 7.

Node.js / TypeScript

import { createHmac, timingSafeEqual } from "node:crypto";

export function assinaturaValida(cabecalho, corpoBruto, segredo, agora = Math.floor(Date.now() / 1000)) {
  if (!cabecalho) return false;
  const partes = Object.fromEntries(cabecalho.split(",").map((p) => p.trim().split("=", 2)));
  const t = Number(partes.t);
  if (!Number.isInteger(t) || !partes.v1 || Math.abs(agora - t) > 300) return false;
  const esperado = createHmac("sha256", segredo).update(`${t}.${corpoBruto}`, "utf8").digest("hex");
  const a = Buffer.from(esperado), b = Buffer.from(partes.v1);
  return a.length === b.length && timingSafeEqual(a, b);
}
// Express: express.raw({ type: "application/json" }) e req.body.toString("utf8") — nunca express.json() antes.

Python

import hashlib, hmac, time

def assinatura_valida(cabecalho, corpo_bruto: bytes, segredo: str, agora=None) -> bool:
    if not cabecalho:
        return False
    partes = dict(p.strip().split("=", 1) for p in cabecalho.split(",") if "=" in p)
    try:
        t = int(partes["t"])
    except (KeyError, ValueError):
        return False
    agora = int(time.time()) if agora is None else agora
    if abs(agora - t) > 300 or "v1" not in partes:
        return False
    esperado = hmac.new(segredo.encode(), f"{t}.".encode() + corpo_bruto, hashlib.sha256).hexdigest()
    return hmac.compare_digest(esperado, partes["v1"])
# Flask: request.get_data() · Django: request.body · FastAPI: await request.body()

C# (.NET)

using System.Security.Cryptography;
using System.Text;

static bool AssinaturaValida(string? cabecalho, string corpoBruto, string segredo, long? agora = null)
{
    if (string.IsNullOrEmpty(cabecalho)) return false;
    var partes = new Dictionary<string, string>();
    foreach (var parte in cabecalho.Split(','))
    {
        var kv = parte.Trim().Split('=', 2);
        if (kv.Length == 2) partes[kv[0]] = kv[1];
    }
    if (!partes.TryGetValue("t", out var tTexto) || !long.TryParse(tTexto, out var t)) return false;
    if (!partes.TryGetValue("v1", out var v1)) return false;
    var now = agora ?? DateTimeOffset.UtcNow.ToUnixTimeSeconds();
    if (Math.Abs(now - t) > 300) return false;
    using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(segredo));
    var esperado = Convert.ToHexString(hmac.ComputeHash(Encoding.UTF8.GetBytes($"{t}.{corpoBruto}"))).ToLowerInvariant();
    return CryptographicOperations.FixedTimeEquals(Encoding.UTF8.GetBytes(esperado), Encoding.UTF8.GetBytes(v1));
}

PHP

function assinatura_valida(?string $cabecalho, string $corpoBruto, string $segredo, ?int $agora = null): bool {
    if (!$cabecalho) return false;
    $partes = [];
    foreach (explode(',', $cabecalho) as $p) {
        [$k, $v] = array_pad(explode('=', trim($p), 2), 2, null);
        $partes[$k] = $v;
    }
    if (!isset($partes['t'], $partes['v1']) || !ctype_digit($partes['t'])) return false;
    $t = (int) $partes['t'];
    if (abs(($agora ?? time()) - $t) > 300) return false;
    $esperado = hash_hmac('sha256', $t . '.' . $corpoBruto, $segredo);
    return hash_equals($esperado, $partes['v1']);
}
// $corpoBruto = file_get_contents('php://input');  ·  cabeçalho: $_SERVER['HTTP_SOFTEUM_ASSINATURA']

6. Responder ao aviso e não lançar duas vezes

O ERP responde O que acontece no Softeum
2xx com corpo {"numero_erp": "584460"} Pedido confirmado com esse número.
2xx sem corpo, ou sem numero_erp Pedido enviado (chegou ao ERP, ainda sem número).
Qualquer outro código (3xx, 4xx, 5xx), nada em 10 s, erro de conexão ou de TLS Falha. Reenviamos depois de 1, 5, 30, 120 e 360 minutos (6 tentativas em cerca de 8h36). Esgotadas, o pedido fica com erro no painel e o time reenvia quando o ERP voltar.
  • Prazo: 10 segundos para a resposta inteira (cabeçalho e corpo). Se lançar demora mais, grave o aviso numa fila do ERP, responda 2xx na hora e lance depois.
  • numero_erp é texto de até 60 caracteres (um número JSON também é aceito). Lemos só os primeiros 4.000 bytes.
  • Pedido repetido também recebe 2xx, de preferência com o mesmo numero_erp. Responder erro para repetido faz o Softeum reenviar até esgotar.
  • 401 para assinatura inválida e 400 para corpo que o ERP não entende. Os dois contam como falha. O painel mostra cada tentativa com o código e o tempo; o começo do corpo da resposta (até 500 caracteres) fica no histórico.
  • Para recusar por regra de negócio (cliente bloqueado, produto inativo), responda 2xx ao aviso e recuse pela API (POST /v1/pedidos/{id}/recusa com o motivo): o pedido volta na hora para o time, com o seu texto. Responder 4xx também acaba em erro, mas só depois das 6 tentativas e sem o motivo à vista do time.
  • Se o lançamento demora, responda 2xx sem número e confirme depois pela API (POST /v1/pedidos/{id}/confirmacao).

Não lançar duas vezes

O mesmo pedido pode chegar de novo quando o ERP lançou mas respondeu depois dos 10 s (o Softeum conta como falha e reenvia), quando o time reenvia um pedido com erro que o ERP já tinha lançado, ou quando dois avisos chegam ao mesmo tempo. Por isso:

  1. Antes de lançar, procure pedido.id no ERP. Já existe → não lance; responda 2xx com o número existente.
  2. Grave pedido.id com uma restrição de unicidade no banco do ERP — procurar antes não basta quando dois avisos chegam ao mesmo tempo.
  3. Softeum-Entrega (o id do aviso) também é estável por pedido e serve para registrar o recebimento.

Quantos avisos ao mesmo tempo

Os pedidos de uma empresa chegam ao ERP um de cada vez (o próximo sai quando o anterior teve resposta). Para quem recebe muitos pedidos por hora, a Softeum combina com a empresa até 32 avisos ao mesmo tempo: o ERP precisa aguentar as requisições em paralelo, e os pedidos podem chegar fora da ordem em que ficaram prontos. O mesmo pedido nunca vai em dois avisos ao mesmo tempo (nem quando o Softeum é atualizado no meio de uma entrega: a outra tentativa espera a primeira terminar). Vale também para a entrega por arquivo (seção 15): até esse número de conexões SFTP ao mesmo tempo.


7. Exemplos prontos e simulador

Para baixar no painel, ao lado deste guia (Integração com o ERP → Guia completo de integração → Arquivos de exemplo); a Softeum também os envia junto com o guia:

Arquivo O que é Como rodar
receptor.mjs Endereço de entrega em Node.js 18+, sem dependências SOFTEUM_SEGREDO=whsec_... node receptor.mjs
receptor.py O mesmo em Python 3.9+, só biblioteca padrão SOFTEUM_SEGREDO=whsec_... python3 receptor.py
receptor.php O mesmo em PHP 8, guardando os lançados em SQLite SOFTEUM_SEGREDO=whsec_... php -S localhost:8080 receptor.php
Program.cs O mesmo em C# (.NET 8, minimal API) dotnet new web, troque o Program.cs, dotnet run
simular-aviso.mjs Simulador: manda ao seu endereço um aviso assinado igual ao do Softeum ver abaixo
simular-aviso.sh Simulador do aviso de teste só com curl e openssl ./simular-aviso.sh <endereço> <segredo>
buscar-pedidos.mjs Busca pela API em Node.js 18+: lista os prontos, lança, confirma ou recusa SOFTEUM_CHAVE=sp_live_... node buscar-pedidos.mjs
buscar_pedidos.py O mesmo em Python 3.9+, só biblioteca padrão SOFTEUM_CHAVE=sp_live_... python3 buscar_pedidos.py
kit-para-o-ti.md O kit para a TI: o jeito de receber, o caminho de cada ERP, o teste, os acessos e quem testa o quê Leia primeiro

Os quatro receptores fazem a mesma coisa, na ordem certa:

  1. leem o corpo bruto e conferem a assinatura (401 se não bate);
  2. respondem 2xx sem lançar nada ao aviso de teste e a tipos desconhecidos;
  3. respondem com o mesmo número a um pedido já lançado;
  4. chamam lancarNoErp — a única parte a escrever — e devolvem {"numero_erp": "..."}; se o lançamento falha, respondem 503 e o Softeum tenta de novo.

Eles escutam HTTP na porta 8080 (PORTA muda). Em produção, deixe o processo atrás do proxy ou balanceador que já faz o HTTPS do ERP (nginx, IIS, load balancer da nuvem).

Os dois clientes de busca listam os prontos, chamam lancarNoErp e confirmam (ou recusam com o motivo). O pedido de teste ("teste": true, seção 8) só é lançado com SOFTEUM_HOMOLOGACAO=1, que você liga só no ERP de homologação: sem ela, ele é recusado com o motivo "Pedido de teste: não lançado no ERP de produção." e sai da fila. Mantenha essa regra no cliente de busca que você escrever.

Testar sem o painel

Com o receptor rodando, num outro terminal:

# 1. aviso de teste: espere "teste aprovado"
node simular-aviso.mjs http://localhost:8080/softeum/pedidos whsec_SEU_SEGREDO

# 2. pedido de exemplo: espere "CONFIRMADO com o número ..."
node simular-aviso.mjs http://localhost:8080/softeum/pedidos whsec_SEU_SEGREDO --pedido

# 3. o mesmo pedido de novo: espere o MESMO número (o ERP não lançou duas vezes)
node simular-aviso.mjs http://localhost:8080/softeum/pedidos whsec_SEU_SEGREDO --pedido

# 4. outro pedido (id novo): espere um número novo
node simular-aviso.mjs http://localhost:8080/softeum/pedidos whsec_SEU_SEGREDO --pedido --novo

# 5. segredo errado: espere 401 e "falha"
node simular-aviso.mjs http://localhost:8080/softeum/pedidos whsec_errado --pedido

O simulador usa o mesmo prazo de 10 s e também não segue redirecionamento, e diz o que o Softeum faria com a resposta (confirmado, enviado ou falha com reenvio). Rode também contra o endereço HTTPS público, antes de cadastrá-lo.


8. Aviso de teste

O botão Enviar teste (Integração → Como o ERP recebe → Aviso no endereço do ERP) manda ao endereço:

POST <seu endereço>
Softeum-Evento: teste
Softeum-Entrega: <uuid novo a cada teste>
Softeum-Assinatura: t=...,v1=...

{"id": "...", "tipo": "teste", "criado_em": "2026-09-30T16:45:00-03:00"}

Confira a assinatura e responda 2xx sem lançar nada. O painel mostra o código e o tempo da resposta. Trate o campo tipo (ou o cabeçalho Softeum-Evento) e responda 2xx para tipos que o ERP não conhece: novos tipos de aviso podem ser criados no futuro.

Pedido de teste (busca pela API)

Para testar a busca sem esperar um pedido chegar por e-mail. O administrador clica em Criar pedido de teste (Integração → Como o ERP recebe → O ERP busca os pedidos) e o Softeum cria, na hora, um pedido pronto:

  • o número TESTE-0001, TESTE-0002... (nunca se repete) e "teste": true no JSON, logo depois do id;
  • um comprador ativo do cadastro (de preferência com o codigo_erp), até 3 produtos ativos (de preferência com o codigo_erp), as quantidades 1, 2 e 3 e o preço da tabela de preço que vale para o comprador (R$ 10,00 no produto sem tabela). Com os códigos do ERP preenchidos, o ERP de homologação consegue lançar o pedido de verdade;
  • a data do pedido de hoje, a entrega para daqui a 7 dias e a observação "PEDIDO DE TESTE: não faturar. Criado no Softeum Pedidos para a TI testar a integração com o ERP." (o aviso vem no começo, e fica mesmo se o ERP cortar o campo);
  • sem documento: arquivo_original vem com o endereço que não baixa nada (como no pedido sem arquivo, seção 4.5), e GET /v1/pedidos/{id}/arquivo responde 404.

Ele sai em GET /v1/pedidos?situacao=pronto como qualquer pronto. Lance e confirme (POST /v1/pedidos/{id}/confirmacao), ou recuse com o motivo (POST /v1/pedidos/{id}/recusa), e confira no painel: confirmado com o número do ERP, ou com erro e o motivo.

Só com o ERP de homologação, antes do primeiro pedido de verdade. A busca não separa a homologação da produção: o pedido de teste sai para todo ERP que busca os pedidos da empresa, com qualquer chave, e o ERP de produção o lançaria como um pedido de verdade. Por isso:

  • o painel pede confirmação antes de criar;
  • o Softeum não cria o pedido de teste quando o ERP já recebeu um pedido de verdade da empresa nos últimos 30 dias (entregue pelo aviso ou pelo arquivo, ou confirmado pela API, inclusive no ERP de homologação): o painel mostra a data e o contato do suporte. Faça o teste com o pedido de teste antes de mandar o primeiro pedido de verdade. Para testar de novo depois disso, fale com o suporte da Softeum (diiego.martins@softeum.com.br);
  • no ERP de produção, recuse o pedido de teste com o motivo: ele sai da fila. Os clientes de busca da seção 7 já fazem isso (sem SOFTEUM_HOMOLOGACAO=1);
  • antes de apontar o ERP de produção, confirme ou recuse no ERP de homologação os pedidos de teste que ainda estão prontos. O que sobrar sem confirmar é apagado na primeira hora depois de um pedido de verdade chegar ao ERP.

O pedido de teste nunca vai pelo aviso nem pelo arquivo: com o endereço de entrega ou o SFTP ligados, o painel não cria (use o Enviar teste do aviso ou o Testar conexão do SFTP), e ligar um dos dois apaga os pedidos de teste que ainda não foram confirmados. Ele não entra na cobrança, aparece em Pedidos com o selo Teste e é apagado sozinho em até 7 dias depois de criado. No máximo 20 por dia por empresa. Sem nenhum comprador ou produto ativo no cadastro, o painel pede para cadastrar antes.


9. O que o painel mostra

No alto da tela Integração com o ERP, uma faixa diz numa frase como a entrega está agora (os pedidos chegando, a entrega parada, o ERP recusando, pedidos esperando o ERP há mais de 30 minutos) e leva direto ao que resolve; ao lado, quantos pedidos voltaram com erro do ERP.

Na aba Integração → Entregas, cada pedido enviado aparece com as tentativas (hora, código, tempo) e uma situação (os mesmos nomes da lista de Pedidos):

Situação no painel Quer dizer O que fazer
Pronto para enviar Pronto: esperando o aviso (primeira tentativa ou próximo reenvio) ou a busca do ERP pela API. Pelo aviso, nada: o Softeum tenta sozinho. Pela busca, o pedido sai na próxima busca do ERP.
Enviado ao ERP O ERP respondeu 2xx ao aviso sem numero_erp (no arquivo, o arquivo está na pasta do ERP). Confirme pela API (POST /v1/pedidos/{id}/confirmacao) ou devolva o número na resposta ao aviso.
Confirmado pelo ERP O ERP lançou e devolveu o número (aparece na linha). Nada.
Com erro As 6 tentativas falharam, o ERP recusou pela API (o motivo aparece) ou o pedido não pôde sair (seção 10). Corrija a causa e use Reenviar ao ERP. A agenda de reenvios recomeça. Só pedido com erro pode ser reenviado.
Em revisão O pedido voltou para a revisão antes de sair (ex.: produto sem o código do ERP no arquivo que usa o código). O time de faturamento confere e aprova de novo; aprovado, ele vai ao ERP.

10. Problemas comuns

Mensagens que o painel mostra na entrega (ou no Enviar teste), com a causa e o que fazer:

Mensagem no painel Causa provável O que fazer
O ERP respondeu com o código 401. O ERP recusou a assinatura. Use o corpo bruto (não o JSON reconvertido); use o segredo inteiro, com whsec_; confira se o segredo não foi trocado no painel; sincronize o relógio do servidor; compare em hexadecimal minúsculo. Teste com o vetor da seção 5.
O ERP respondeu com o código 404 / 405. Endereço ou caminho errado, ou a rota não aceita POST. Confira o endereço cadastrado e a rota no ERP.
O ERP respondeu com o código 301 / 302 / 307 / 308. O endereço redireciona (ex.: http→https, barra no fim, outro domínio). Cadastre o endereço final, para onde ele redireciona.
O ERP respondeu com o código 413. O proxy do ERP limita o tamanho do corpo. Aumente o limite: pedido com centenas de itens gera um corpo grande.
O ERP respondeu com o código 500 / 502 / 503 / 504. Erro ou indisponibilidade do lado do ERP ou do proxy. Veja o log do ERP. O Softeum reenvia sozinho.
O ERP não respondeu em 10 s. O lançamento demora mais que o prazo, ou o servidor está travado. Responda 2xx logo e lance depois (fila). Atenção: o ERP pode ter lançado mesmo assim — a regra de não duplicar (seção 6) resolve o reenvio.
Não deu para chegar ao endereço do ERP. Confira o endereço e se o ERP está no ar. Nome que não resolve (DNS), porta fechada, firewall, certificado TLS inválido ou com a cadeia incompleta. Teste de fora da rede da empresa (curl -v https://...) e com o simulador. Libere o acesso à porta pela internet.
O endereço do ERP aponta para uma rede interna. Use o endereço público do ERP. O nome resolve para um IP privado (10.x, 172.16–31.x, 192.168.x etc.). Publique o endereço com IP público (proxy reverso, balanceador).
O item da linha N está sem produto do catálogo. … O pedido voltou para a revisão. Um item sem produto do catálogo, com o produto inativo ou, no arquivo por SFTP que usa o código do ERP, com o produto sem esse código. Assunto do time de faturamento: o pedido volta para a revisão (não fica em erro), com a pendência de cada linha; o time troca ou reativa o produto (ou completa o código do ERP no cadastro) e aprova de novo. Nada chegou ao ERP. Por arquivo, depois de uma troca de nome sem resposta, o pedido vai para erro com "o arquivo pode já ter chegado ao ERP": confira no ERP antes de mandar para a revisão.
O comprador deste pedido não está cadastrado. O pedido voltou para a revisão. O comprador não está no cadastro do Softeum. O pedido volta para a revisão; cadastrar o comprador (de preferência com o codigo_erp) e aprovar de novo. Nada chegou ao ERP.
O ERP recusou: motivo O ERP recusou pela API, com esse motivo. Assunto do time de faturamento: corrigir o que o ERP apontou e reenviar.

Respostas da API REST e o que fazer:

Resposta Causa provável O que fazer
401 Chave de acesso inválida Sem o cabeçalho Authorization: Bearer, chave incompleta, revogada ou de outro ambiente. Copie a chave inteira (sp_live_ + 48 caracteres). Revogada não volta: gere outra no painel.
403 Acesso suspenso O acesso da empresa ao Softeum está suspenso. Fale com a Softeum.
404 Pedido não encontrado O id é de outra empresa, o pedido ainda está na leitura ou na revisão, ou foi recusado ou cancelado pelo time (o detail diz qual). Use os ids da lista. Pedido que ainda não ficou pronto aparece quando o time aprovar.
409 em confirmação Já confirmado com outro número, ou o pedido não está pronto nem enviado (com erro, cancelado). Não confirme duas vezes com números diferentes; o detail traz o número que vale.
409 em recusa O pedido já foi confirmado (não se recusa) ou não está pronto nem enviado. Para desfazer um pedido confirmado, fale com o time de faturamento.
409 ao buscar um pedido O pedido não pode ir ao ERP (item sem produto do catálogo, produto inativo, comprador sem cadastro) e voltou para a revisão do time. Assunto do time de faturamento; o pedido volta a aparecer quando o time aprovar de novo.
422 Dados inválidos Campo fora do formato: erros[] traz cada campo e o motivo. Corrija e mande de novo.
429 Muitas chamadas Mais de 120 chamadas por minuto com a mesma chave (ou o limite combinado para a sua empresa), ou mais de 30 tentativas por minuto com chave inválida (ou de empresa suspensa) vindas do mesmo endereço. Espere os segundos do cabeçalho Retry-After; busque a cada 1 a 5 minutos, não em laço. Com muitos pedidos por hora, confirme com Prefer: return=minimal (13.3) e peça à Softeum um limite maior. Se o detail fala de chave inválida, confira a chave antes de tentar de novo.
5xx Instabilidade do lado do Softeum. Tente de novo em instantes; as operações podem ser repetidas sem efeito duplicado.

11. Antes de ir para produção

Pelo aviso

Pela busca

Nos dois casos


12. Perguntas frequentes

Qual campo identifica o pedido? pedido.id. O numero_comprador se repete entre compradores e pode vir vazio.

Como acho o cliente no ERP? Por comprador.codigo_erp; se vier null, pelo comprador.cnpj.

Como acho o produto? Por itens[].produto.codigo_erp; se vier null, pelo sku. Mantenha o código do ERP preenchido no catálogo do Softeum.

A condição de pagamento vem no formato do meu ERP? Não: vem como o comprador escreveu. Mantenha uma tabela de tradução no ERP (28/42/56 dias → condição 023); sem tradução, use a condição do cadastro do cliente ou recuse.

E os impostos? Os preços são líquidos, sem impostos. O ERP calcula IPI, ICMS-ST e os demais pelas regras dele.

O time cancelou um pedido que já chegou ao ERP. O ERP fica sabendo? Hoje não: não existe aviso de cancelamento, e o pedido cancelado some da API (GET responde 404 dizendo que foi cancelado). O time precisa avisar quem cuida do ERP.

E se o ERP ficar fora do ar por um dia? As 6 tentativas cobrem cerca de 8h36. Depois disso o pedido fica com erro no painel e o time usa Reenviar ao ERP quando o ERP voltar; a agenda de reenvios recomeça a cada reenvio.

De que IPs vêm os avisos? Do serviço do Softeum na nuvem, sem IP fixo garantido. Proteja o endereço pela assinatura, não por lista de IPs.

Posso ter dois endereços (produção e homologação)? Hoje é um endereço ativo por empresa. Homologue no endereço de homologação e troque para o de produção depois; o segredo continua o mesmo.

O ERP pode confirmar o pedido depois, sem ser na resposta? Sim: POST /v1/pedidos/{id}/confirmacao com o número, quando quiser (seção 13.3). Vale para o pedido entregue pelo aviso e para o buscado pela API.

Como testo a busca sem esperar um pedido chegar por e-mail? Com o pedido de teste: o administrador clica em Criar pedido de teste no painel (Integração → Como o ERP recebe → O ERP busca os pedidos) e um pedido pronto, com os compradores e os produtos do cadastro, aparece na busca com "teste": true. Ele não entra na cobrança, nunca vai pelo aviso nem pelo arquivo e é apagado em até 7 dias (seção 8). É só para antes do primeiro pedido de verdade: depois que o ERP recebe um, o painel não cria mais o pedido de teste e mostra o contato do suporte.

O ERP de produção pode receber o pedido de teste? Pode, se ele busca enquanto há um pedido de teste pronto: a busca não separa a homologação da produção. Recuse no ERP de produção todo pedido com "teste": true, como os exemplos da seção 7 fazem; ele chega com o número TESTE- e a observação "PEDIDO DE TESTE: não faturar.".

Busquei um pedido e não confirmei. Ele some? Não: continua pronto e volta em toda busca de situacao=pronto até ser confirmado ou recusado. Por isso a regra de não lançar duas vezes (seção 6) vale também na busca.

Posso usar o aviso e a busca juntos? Sim. O aviso entrega na hora; a API confirma depois, recusa com o motivo e busca o que ficou para trás (por exemplo, com o endereço fora do ar).


13. API REST: referência

13.1 O básico

  • Endereço: https://api.softeum.com.br/v1.
  • Chave: toda chamada leva Authorization: Bearer sp_live_... (seção 2). A chave identifica a empresa: a API só mostra e altera os dados dela.
  • Formato: JSON em UTF-8. Em POST e PUT, mande Content-Type: application/json.
  • Repetir não duplica: toda operação pode ser repetida sem efeito em dobro (confirmar com o mesmo número, a mesma recusa, o mesmo lote de cadastro). Diante de timeout ou erro de rede, repita. O cabeçalho Idempotency-Key é aceito, mas não é necessário.
Limite Valor
Chamadas por minuto, por chave 120 (acima disso, 429 com Retry-After). Para quem confirma milhares de pedidos por hora, a Softeum combina um limite maior para a empresa (até 6.000). O minuto conta a partir da primeira chamada da chave.
Tentativas com chave inválida ou de empresa suspensa, por minuto, por endereço 30 (acima disso, 429 com Retry-After até o minuto virar; a chave que já entrou continua passando)
Tamanho do corpo 5 MB
Registros por lote de cadastro 1.000
Preços por tabela 20.000 (acima disso, a tabela inteira é recusada com 422, sem conferir os preços)
Pedidos por página até 500 (padrão 100)

Erros vêm em application/problem+json (RFC 9457): title diz o tipo, detail explica em português e erros[] traz cada campo com problema, até 100: quando passam disso, vão os 100 primeiros e o detail diz quantos são ao todo.

{
  "type": "https://softeum.com.br/erros/validacao",
  "title": "Dados inválidos",
  "status": 422,
  "detail": "Confira o corpo da confirmação.",
  "erros": [{ "campo": "numero_erp", "mensagem": "Pequeno demais: esperava que o texto tivesse >= 1 caracteres" }]
}
Código Quando
200 Deu certo.
302 /arquivo: o Location é o link do documento.
400 O corpo não é um JSON válido ou veio vazio.
401 Sem chave, chave incompleta, inválida ou revogada (vem com WWW-Authenticate: Bearer).
403 O acesso da empresa ao Softeum está suspenso.
404 Rota ou pedido que não existe para a sua empresa (o detail diz o porquê).
409 A situação do pedido não permite (já confirmado com outro número, recusar um confirmado...).
415 Faltou o Content-Type: application/json (corpo com outro tipo vira 422).
422 Dados fora do formato; erros[] diz o quê.
429 Muitas chamadas (ou tentativas demais com chave inválida do mesmo endereço): espere o Retry-After (segundos).
5xx Instabilidade do Softeum: tente de novo em instantes.

13.2 Buscar os pedidos

GET /v1/pedidos devolve os pedidos do mais antigo para o mais novo (pelo momento em que ficaram prontos).

Parâmetro O que é
situacao pronto (esperando o ERP), enviado, confirmado ou erro. Sem ele, todas essas.
desde Só os que ficaram prontos a partir deste momento, em ISO 8601 com fuso (2026-10-01T00:00:00-03:00).
limite De 1 a 500 por página (padrão 100).
cursor O proximo_cursor da página anterior, como veio.
curl -s "https://api.softeum.com.br/v1/pedidos?situacao=pronto&limite=100" \
  -H "Authorization: Bearer $SOFTEUM_CHAVE"
{
  "dados": [ { "id": "0199a3c4-5f10-7a2b-8c3d-4e5f60718293", "numero_comprador": "45882", "situacao": "pronto", "...": "o pedido inteiro, igual ao do aviso (seção 3.2)" } ],
  "proximo_cursor": "eyJlIjoiMjAyNi0xMC0yMSAxMDowMDowMCswMCIsImkiOiIwMTk5Li4uIn0"
}

proximo_cursor nulo = acabou. Cada pedido vem no mesmo formato do aviso (seção 3.2 e campos na seção 4), com situacao e numero_erp como estão agora.

O laço de busca (é o que os exemplos buscar-pedidos.mjs e buscar_pedidos.py fazem):

  1. Peça GET /v1/pedidos?situacao=pronto sem cursor.
  2. Para cada pedido: lance no ERP (sem duplicar: seção 6) e confirme com POST /v1/pedidos/{id}/confirmacao. O que o ERP não aceita, recuse com POST /v1/pedidos/{id}/recusa e o motivo.
  3. Se veio proximo_cursor, peça a página seguinte com ele e repita o passo 2.
  4. Na próxima rodada (1 a 5 minutos depois), comece de novo do passo 1, sem cursor.

Não guarde o cursor de uma rodada para a outra. O pedido sai de pronto quando é confirmado ou recusado, então a fila de prontos sempre recomeça do que falta. Um pedido que volta para pronto depois de um reenvio do time mantém o lugar dele na fila (pelo momento em que ficou pronto da primeira vez): com cursor guardado, ele ficaria para trás e não seria visto. O cursor serve só para ler as páginas da mesma rodada.

O pedido buscado e não confirmado continua pronto e volta na próxima busca — é o ERP que tira o pedido da fila, confirmando ou recusando.

O pedido de teste (seção 8) vem na mesma lista, com "teste": true. No ERP de produção, recuse com o motivo (os exemplos da seção 7 recusam sozinhos, sem SOFTEUM_HOMOLOGACAO=1).

13.3 Um pedido, confirmar, recusar, documento

GET /v1/pedidos/{id} — um pedido, no formato do aviso.

  • 404: o pedido não é da sua empresa, ainda está na leitura ou na revisão, ou foi recusado ou cancelado pelo time.
  • 409: o pedido não pode ir ao ERP (item sem produto do catálogo, produto inativo, comprador sem cadastro) e voltou para a revisão do time, com a pendência de cada linha (antes ia para erro, e o reenvio só o punha em erro de novo). A mesma régua vale para a lista: esse pedido não aparece até o time aprovar de novo.

POST /v1/pedidos/{id}/confirmacao — o ERP lançou o pedido.

curl -s -X POST "https://api.softeum.com.br/v1/pedidos/0199a3c4-5f10-7a2b-8c3d-4e5f60718293/confirmacao" \
  -H "Authorization: Bearer $SOFTEUM_CHAVE" -H "Content-Type: application/json" \
  -d '{"numero_erp": "584460", "lancado_em": "2026-10-25T09:30:00-03:00"}'
Campo Obrigatório O que é
numero_erp sim Número do pedido no ERP, de 1 a 60 caracteres.
lancado_em não Quando o ERP lançou (ISO 8601 com fuso). Sem ele, vale a hora da confirmação.

Responde 200 com o pedido (situacao: "confirmado", numero_erp preenchido). Com o cabeçalho Prefer: return=minimal, responde só o resumo, mais rápido (o pedido não é montado de novo), e o cabeçalho Preference-Applied: return=minimal:

{ "id": "0199a3c4-5f10-7a2b-8c3d-4e5f60718293", "situacao": "confirmado", "numero_erp": "584460" }

Use a resposta curta quando confirmar muitos pedidos por hora: o ERP já tem o pedido. Repetir com o mesmo número não muda nada; com outro número, 409. Só pedido pronto ou enviado pode ser confirmado.

POST /v1/pedidos/{id}/recusa — o ERP não aceita o pedido; ele volta para o time com o motivo.

curl -s -X POST "https://api.softeum.com.br/v1/pedidos/0199a3c4-5f10-7a2b-8c3d-4e5f60718293/recusa" \
  -H "Authorization: Bearer $SOFTEUM_CHAVE" -H "Content-Type: application/json" \
  -d '{"motivo": "Condição de pagamento 28/56 não existe no ERP."}'

motivo tem de 3 a 500 caracteres e aparece no painel como "O ERP recusou: motivo". Com Prefer: return=minimal, responde só o resumo (situacao: "erro"), como na confirmação. Sem ele, responde 200 com o pedido (situacao: "erro"). A mesma recusa de novo não muda nada; recusar um pedido confirmado é 409. Depois que o time corrige e reenvia, o pedido volta a pronto (e aparece de novo na busca ou chega por novo aviso).

GET /v1/pedidos/{id}/arquivo — o documento original do comprador (PDF, planilha, XML ou o e-mail .eml). Responde 302 com o link no Location, válido por 5 minutos; 404 se o pedido não tem documento guardado. Os arquivos ficam guardados por 12 meses a partir do recebimento: depois disso, a resposta é 404.

Os pedidos (com os itens) ficam disponíveis na API por 5 anos a partir do recebimento; depois disso saem e GET /v1/pedidos/{id} responde 404. Guarde no ERP o que precisar por mais tempo.

curl -s -L -o pedido.pdf "https://api.softeum.com.br/v1/pedidos/0199a3c4-5f10-7a2b-8c3d-4e5f60718293/arquivo" \
  -H "Authorization: Bearer $SOFTEUM_CHAVE"

13.4 Enviar os cadastros

O ERP é a fonte da verdade de produtos, compradores, vendedores e tabelas de preço. Mande o que mudou (ou tudo, de tempos em tempos).

As regras

  • Cada registro é criado ou atualizado pelo codigo_erp. Registro que ainda não tem código no Softeum (veio da planilha ou foi feito no painel) é reconhecido pelo SKU (produto), pelo CNPJ (comprador) ou pelo e-mail (vendedor) e passa a ter o código.
  • Campo opcional que não veio fica como está; null apaga. Os obrigatórios vão sempre.
  • ativo que não vem vale true. Para desativar (produto fora de linha, comprador bloqueado, vendedor que saiu), mande "ativo": false. Nada é apagado. Atenção: um registro desativado no painel volta a ativo se o ERP mandá-lo sem ativo: false.
  • Registro com problema não impede os outros: ele volta em erros com o indice (a posição no lote, começando em 0), o campo e a mensagem; o resto é gravado.
  • A ordem importa, porque um cadastro aponta para outro pelo código do ERP: 1. vendedores → 2. produtos →
    1. tabelas de preço padrao, regiao e comprador → 4. compradores → 5. tabelas especifico.

A resposta de todo envio:

{
  "criados": 2,
  "atualizados": 1,
  "erros": [
    { "indice": 1, "campo": "ncm", "mensagem": "NCM com 8 números." },
    { "indice": 3, "campo": "codigo_vendedor", "mensagem": "O vendedor V99 não está cadastrado: envie os vendedores antes dos compradores." }
  ]
}

O lote inteiro só é recusado (422) quando falta a lista, ela vem vazia ou passa de 1.000 registros.

PUT /v1/vendedores — {"vendedores": [...]}

Campo Obrigatório O que é
codigo_erp sim Código do vendedor no ERP.
nome sim Nome.
email não O e-mail que encaminha os pedidos: vira o endereço principal do vendedor. Outros endereços do mesmo vendedor se cadastram no painel. Só e-mails de vendedores ativos cadastrados são lidos pelo Softeum (pelo remetente, ou por quem encaminhou ou redirecionou).
ativo não (padrão true) false desativa: os e-mails dele ficam retidos no painel.

PUT /v1/produtos — {"produtos": [...]}

Campo Obrigatório O que é
codigo_erp sim Código do produto no ERP (é ele que volta em itens[].produto.codigo_erp).
sku sim SKU do catálogo, único na empresa.
descricao sim Descrição.
embalagem sim Embalagem de venda (CX, FD, UN).
unidades_por_embalagem sim Inteiro maior que zero.
ean não EAN/GTIN com 8, 12, 13 ou 14 números.
ncm não NCM com 8 números.
ipi_pct não (padrão 0) IPI em %, de 0 a 100.
linha não Linha de produto.
ativo não (padrão true) false = fora de linha.
curl -s -X PUT "https://api.softeum.com.br/v1/produtos" \
  -H "Authorization: Bearer $SOFTEUM_CHAVE" -H "Content-Type: application/json" \
  -d '{"produtos": [{"codigo_erp": "4359", "sku": "VA-LVD-500R", "descricao": "Limpa-vidros refil 500 ml",
        "ean": "7896543000415", "embalagem": "CX", "unidades_por_embalagem": 12, "ncm": "34022000", "ipi_pct": 0}]}'

PUT /v1/tabelas-de-preco/{codigo} — uma tabela inteira; {codigo} é o código da tabela no ERP.

Campo Obrigatório O que é
nome sim Nome da tabela.
nivel sim especifico, comprador, regiao ou padrao (veja abaixo).
codigo_comprador no nível especifico codigo_erp do comprador dono dos preços.
regiao no nível regiao A região de preço (a mesma do campo regiao dos compradores). Gravada em maiúsculas e sem acento: Sudeste, sudeste e SUDESTE são a mesma região. A tabela de região que vem do ERP é sempre pela região escrita: a tabela pelos estados (as UFs do cadastro do comprador) nasce só pelo painel e pela planilha geral de preços.
vigencia_inicio sim aaaa-mm-dd.
vigencia_fim não aaaa-mm-dd; sem ela, vale sem prazo.
precos sim [{"codigo_produto": "4359", "preco": 76.04}]: preço líquido, sem impostos, por embalagem de venda.

A tabela é substituída inteira: preço que não vier sai dela. Produto que não está no catálogo (ou repetido na lista) volta em erros e os outros preços entram. Em cada item do pedido vale o primeiro nível que tiver preço: especifico (de um comprador) → comprador (a tabela atribuída ao comprador em codigo_tabela_preco) → regiao (a da região escrita do comprador; a tabela pelos estados, criada no painel, vale pelo uf dele, depois da região escrita) → padrao. Duas tabelas do mesmo alcance não podem valer no mesmo dia: a segunda é recusada inteira (422, campo vigencia_inicio). Esta API não mudou com a tabela pelos estados.

curl -s -X PUT "https://api.softeum.com.br/v1/tabelas-de-preco/T-SUDESTE" \
  -H "Authorization: Bearer $SOFTEUM_CHAVE" -H "Content-Type: application/json" \
  -d '{"nome": "Sudeste 2026", "nivel": "regiao", "regiao": "Sudeste", "vigencia_inicio": "2026-01-01",
       "precos": [{"codigo_produto": "4359", "preco": 76.04}, {"codigo_produto": "4360", "preco": 52.80}]}'

PUT /v1/compradores — {"compradores": [...]}

Campo Obrigatório O que é
codigo_erp sim Código do cliente no ERP (é ele que volta em comprador.codigo_erp).
cnpj sim Só os 14 números, com dígitos verificadores válidos. Único na empresa.
razao_social sim Razão social.
nome_fantasia, cidade não
uf não Duas letras maiúsculas (SP). Casa com a tabela de preço pelos estados, criada no painel.
regiao não Região de preço (casa com as tabelas de nível regiao). Gravada em maiúsculas e sem acento, como a da tabela.
codigo_vendedor não codigo_erp do vendedor responsável (envie os vendedores antes). null tira.
codigo_tabela_preco não codigo_erp de uma tabela de nível comprador (envie a tabela antes). null tira. Sem o campo, fica a tabela escolhida no painel; com ele, vale a do ERP, também por cima da escolhida no painel.
ativo não (padrão true) false = bloqueado: os pedidos dele vão para a revisão do time.

14. Por ERP: o caminho de cada um

A integração é a mesma para qualquer ERP: pegar o pedido no Softeum (busca ou aviso), traduzir os códigos e lançar pela porta de entrada de pedidos do ERP. O que muda de um ERP para outro é só essa porta de entrada e os dados internos que ela exige (4.6).

Os nomes abaixo são os mais comuns. Versão, módulos e personalizações mudam nomes e regras: confirme os detalhes com o time ou a consultoria do seu ERP antes de começar.

14.1 O roteiro, para qualquer ERP

  1. Escolha busca ou aviso (seção 0). ERP dentro da rede da empresa, sem endereço público: busca.
  2. Ache a porta de entrada de pedidos de venda do ERP: a API do ERP, a rotina automática de pedido, uma tabela de integração (pré-pedido) ou a importação de arquivo. Use a porta oficial: gravar direto nas tabelas do banco pula as regras do ERP (crédito, impostos, estoque), e alguns ERPs nem reconhecem o registro.
  3. Ponha os códigos do ERP no Softeum: codigo_erp de compradores, produtos e vendedores, pela API (13.4), pela planilha ou na tela. Com eles, o lançamento não precisa procurar nada.
  4. Monte a configuração fixa (4.6) e a tradução da condição de pagamento (seção 12).
  5. Escreva o lançamento: um pedido do Softeum vira um pedido de venda no ERP, com o pedido.id gravado com restrição de unicidade (seção 6) e numero_comprador e linha guardados para a nota (4.9).
  6. Feche o ciclo: confirme com o número do ERP; o que o ERP não aceitar, recuse com o motivo.
  7. Teste com o simulador (seção 7) e um pedido real (seção 11) antes de ligar em produção.

14.2 A porta de entrada em cada ERP

ERP Porta de entrada do pedido de venda O que o lançamento precisa ter em mãos
TOTVS Protheus Rotina automática do pedido de venda (MATA410, por ExecAuto, em ADVPL ou TLPP). Pronto: o modelo pronto do arquivo em CSV (15.6). Também a Mensagem Única de pedido (EAI). Cliente (código + loja: 4.8), TES, condição de pagamento, tabela de preço, filial.
TOTVS Winthor Serviço de pedido de venda do Winthor Anywhere (winthor-pedido-venda) ou a integradora de pedidos que a empresa já usa. RCA (vendedor: vendedor.codigo_erp), plano de pagamento, cobrança, região de preço, filial.
TOTVS Datasul API ou Mensagem Única de pedido de venda, ou o TOTVS Conector. Estabelecimento, natureza de operação, condição de pagamento, tabela de preço.
TOTVS RM Serviços do RM para movimentos (o pedido de venda é um movimento). Coligada e filial, tipo de movimento, condição de pagamento.
SAP S/4HANA e ECC API de ordem de venda (OData API_SALES_ORDER_SRV), BAPI ou IDoc de pedido, normalmente pela ferramenta de integração da empresa (SAP Integration Suite, PI/PO). Pronto: o IDoc ORDERS05 e o odv-mapping por arquivo (15.6). Tipo de ordem, organização de vendas, canal de distribuição, setor de atividade, emissor da ordem (cliente com 10 dígitos: 4.8), pedido do cliente (numero_comprador).
SAP Business One Service Layer: POST /b1s/v2/Orders. CardCode (cliente), DocumentLines com ItemCode e Quantity, data de entrega, referência do pedido do cliente (NumAtCard).
Sankhya API de serviços pelo Gateway da Sankhya, com AppKey; o pedido entra como nota de venda. Gravar direto no banco não é reconhecido. Empresa, parceiro (cliente), TOP, tipo de negociação, vendedor, natureza e centro de custo; o usuário de integração precisa ter acesso às rotinas.
Senior (Gestão Empresarial, Senior X) API de pedido de venda do Senior X (autenticação por chave; o pedido é criado e a situação volta depois) ou os serviços do ERP. Empresa e filial, cliente, condição de pagamento, representante, tabela de preço.
Omie API do Omie (IncluirPedido). codigo_cliente_omie e codigo_produto (os ids do Omie: 4.8), etapa do pedido, parcelas.
Bling API v3 de pedidos de venda (/pedidos/vendas). Contato e produtos pelo id do Bling, natureza de operação, parcelas.
Tiny / Olist ERP API v3 de pedidos (POST /pedidos). Contato, lista de preço, natureza de operação, vendedor, endereço de entrega.
Microsoft Dynamics 365 Business Central API v2.0: salesOrders e salesOrderLines. customerNumber (cliente), externalDocumentNumber (= numero_comprador), condição de pagamento.
Oracle NetSuite REST, registro salesOrder. Cliente (entity), itens, referência do pedido do cliente (otherRefNum).
Odoo API externa (XML-RPC ou JSON-RPC), modelo sale.order. Cliente (partner_id), linhas com produto, quantidade e preço, referência do pedido do cliente.
Outros (Mega, Benner, Viasoft, Nomus, Consinco, CISS, Linx, Alterdata, ERP próprio) A API, a rotina de importação ou a tabela de integração de pedidos do próprio ERP. O roteiro de 14.1 vale igual.

14.3 ERP sem API, sem time de TI ou que só recebe arquivo

  • ERP dentro da rede da empresa e sem ninguém para programar: fale com a Softeum sobre um conector ao lado do ERP. Ele busca os pedidos prontos, lança na porta de entrada do ERP, confirma ou recusa, e traz os cadastros do ERP para o Softeum. Nada precisa ser aberto para a internet.
  • ERP que só recebe pedido por arquivo (XML, CSV, texto de layout fixo): use a entrega por arquivo (seção 15). O Softeum grava cada pedido, no layout do ERP, na pasta do servidor SFTP que o ERP lê.

15. Entrega por arquivo (SFTP)

Para o ERP que importa os pedidos de uma pasta e não recebe aviso nem busca pela API. Cada pedido aprovado vira um arquivo, no layout que o ERP importa, gravado na pasta de um servidor SFTP. Ninguém precisa programar: basta o ERP (ou a rotina de importação dele) ler a pasta.

15.1 Como funciona

  1. O pedido fica pronto (direto ou aprovado na revisão).
  2. O Softeum monta o arquivo do pedido pelo modelo cadastrado no painel (15.3) e conecta ao servidor SFTP.
  3. Grava o arquivo como <nome>.tmp e, no fim, troca para o nome final: o ERP nunca lê um arquivo pela metade. Servidor que não deixa trocar o nome: escolha a gravação direta, em que o arquivo já vai com o nome final (se a conexão cair no meio, o Softeum apaga o pedaço quando consegue, mas o ERP pode ler o arquivo pela metade antes).
  4. O pedido fica enviado no painel. O ERP importa o arquivo no ritmo dele.

O ERP importa cada pedido uma vez só, pelo pedido.id. Como no aviso (seção 6), o mesmo pedido pode chegar de novo em casos raros: a conexão cai logo depois da troca de nome e a resposta do servidor se perde, ou o time reenvia pelo painel um pedido com erro que o ERP já tinha importado. A rotina de importação guarda os pedido.id já importados e ignora o arquivo de um pedido que já entrou. Por isso o nome do arquivo e o conteúdo levam o pedido.id.

O arquivo que já está na pasta com o mesmo nome, antes de gravar:

  • igual ao de agora: a entrega vale, sem gravar de novo;
  • de uma tentativa anterior deste pedido (ela gravou e caiu antes de registrar), mesmo com outro conteúdo (o modelo mudou entre as tentativas): a entrega vale, sem gravar de novo. Vale só a tentativa feita depois da última aprovação do pedido: o que voltou para a revisão e foi aprovado de novo pode estar com outro conteúdo (outro produto, outra quantidade), e o arquivo de antes que ficou na pasta conta como outro arquivo deste pedido (abaixo);
  • o começo do arquivo de agora (sobra de uma gravação interrompida): é apagado e o arquivo é gravado inteiro; a entrega pede para conferir no ERP se o pedido não entrou pela metade;
  • outro arquivo: não é substituído (o ERP pode estar lendo o de outro pedido). A entrega falha, diz o motivo e tenta de novo nos intervalos abaixo. Com o nome do pedido ({{pedido.id}}), a mensagem diz que é um arquivo deste pedido gravado antes com outro conteúdo: confira no ERP se o pedido entrou; se não entrou, apague o arquivo da pasta para ele ser gravado de novo.

A troca de nome sem resposta: a conexão que cai depois de pedir a troca do .tmp para o nome final deixa a dúvida (a troca pode ter acontecido). A tentativa fica marcada e a seguinte confere a pasta: sem o arquivo e sem o .tmp, o ERP já levou o arquivo, e a entrega vale sem gravar de novo (e pede para conferir no ERP); com o .tmp lá, a troca não aconteceu, e o arquivo é gravado e trocado de novo.

O que tenta de novo e o que para as entregas:

  • Tenta de novo depois de 1, 5, 30, 120 e 360 minutos (os intervalos do aviso): servidor fora do ar ou que não responde, conexão recusada ou que cai no meio, endereço que não resolve ou de rede interna, pasta que não existe, usuário sem permissão na pasta e outro arquivo com o mesmo nome. Depois da última tentativa, o pedido fica com erro, com o motivo, para reenviar no painel. Problema da configuração (o endereço, a pasta, a permissão, o nome) termina em "Corrija na tela de Integração e reenvie o pedido."; o servidor fora do ar, em "reenvie pelo painel quando o servidor SFTP voltar.".

  • Para as entregas por arquivo, na hora e sem tentar de novo: o servidor recusou o acesso (usuário e senha ou chave), a chave privada não pôde ser lida ou a identificação do servidor mudou. Insistir só tentaria a senha errada no servidor do cliente. Os pedidos aprovados ficam prontos, esperando, e o painel mostra a parada na tela de Integração; não há prazo para eles irem para erro. As entregas voltam quando o teste da conexão passa, depois de corrigir o acesso ou aceitar a identificação nova. A conexão nova, o acesso trocado (outro servidor, usuário, senha ou chave) e a identificação nova aceita também esperam o teste passar antes da primeira entrega.

  • Vai para erro na hora, sem conectar: o modelo que não gera o arquivo do pedido (uma variável que ele não tem, um código que não cabe nas posições), com a linha do modelo; o arquivo que sai inválido (o JSON que não fecha, o XML mal formado), com a linha e a coluna do arquivo; e o arquivo do pedido maior que 5 MB.

  • Volta para a revisão, sem conectar: o pedido com item sem produto do catálogo, com produto inativo, com o comprador sem cadastro ou, quando o modelo usa item.produto.codigo_erp, com produto sem o código do ERP no cadastro (antes o arquivo saía com o código em branco). A pendência de cada linha diz o motivo, e o time resolve na revisão. Para o arquivo sair mesmo sem o código (o ERP acha o produto pelo SKU), escreva o código dentro de {{#se item.produto.codigo_erp}}…{{/se}} ou com o filtro padrao antes dos de largura ({{item.produto.codigo_erp | padrao:0 | esquerda:6}}): depois de esquerda ou direita o vazio já virou espaços, o padrao não troca nada, e o código conta como necessário. A tela de Integração avisa quando o modelo precisa do código e quando o padrao vem depois de esquerda, direita ou json. A exceção é a tentativa anterior com a troca de nome sem resposta: o arquivo dela pode já estar com o ERP, e o pedido vai para erro (e não para a revisão), com o motivo e o pedido de conferir no ERP se ele entrou; se não entrou, o time manda de volta para a revisão.

  • A rodada de reenvios recomeça quando o pedido é reenviado pelo painel ou aprovado de novo na revisão: as falhas de antes não contam para os intervalos nem levam o pedido aprovado de novo para erro na primeira falha.

  • O nome do arquivo nos reenvios: cada tentativa guarda a versão do layout (15.2) que gerou o arquivo, e os reenvios automáticos usam o nome da versão da primeira tentativa: se uma versão nova for publicada no meio, o mesmo pedido não vai em dois arquivos com nomes diferentes. O conteúdo é o da versão de agora.

  • Confirmação: o arquivo não traz resposta. Se o ERP puder, confirme o pedido com o número dele pela API (POST /v1/pedidos/{id}/confirmacao, seção 13.3): o pedido.id vai no arquivo.

  • Com o SFTP ligado, os pedidos vão por arquivo, mesmo com endereço de entrega (aviso) cadastrado.

15.2 Configurar no painel

Em Integração → Como o ERP recebe → Arquivo na pasta do ERP, o administrador informa:

Campo O que é
Endereço do servidor e porta Ex.: sftp.suaempresa.com.br, porta 22.
Usuário e acesso Senha ou chave privada (as aceitas estão abaixo), com a frase-senha se tiver. Ficam guardadas cifradas, não aparecem de novo e só valem para o servidor e o usuário de quando foram digitadas: trocar o endereço, a porta ou o usuário pede a senha (ou a chave e a frase-senha dela) de novo.
Pasta dos pedidos Onde o ERP lê os pedidos (ex.: /entrada/pedidos). Vazia: a pasta inicial do usuário.
Formato Um modelo (XML, CSV, TXT, JSON com os nomes de campo do ERP) ou o JSON oficial do pedido (o mesmo da API).
Tipo de conteúdo XML, JSON ou texto (CSV, TXT): como os valores entram no modelo (15.4).
Nome do arquivo Também um modelo, ex.: PEDIDO_{{pedido.numero_comprador}}_{{pedido.id}}.xml.
Codificação e quebra de linha UTF-8 ou ISO-8859-1; LF ou CRLF (Windows).
Marca BOM Em UTF-8, o arquivo pode começar com a marca BOM (EF BB BF), que alguns ERPs no Windows pedem para ler os acentos.

Chaves aceitas: as que a entrega lê. OpenSSH (-----BEGIN OPENSSH PRIVATE KEY-----, o padrão do ssh-keygen), PEM de RSA, DSA e EC (-----BEGIN RSA PRIVATE KEY----- e as outras duas) e o .ppk do PuTTY na versão 2 com RSA ou DSA. O painel recusa as outras ao salvar, com o caminho para converter:

  • PKCS#8 (-----BEGIN PRIVATE KEY----- ou -----BEGIN ENCRYPTED PRIVATE KEY-----, o que o OpenSSL grava): regrave no formato OpenSSH com ssh-keygen -p -f <arquivo da chave> e cole o arquivo regravado;
  • .ppk da versão 3 (o padrão do PuTTYgen desde 2021) e .ppk de Ed25519 ou ECDSA: no PuTTYgen, abra a chave, use Conversions > Export OpenSSH key e cole o arquivo exportado.

Chave protegida por frase-senha: o painel pede a frase antes de salvar. Sem ela, a entrega não lê a chave e as entregas param.

A entrega começa desligada. Cadastre o acesso, teste a conexão e ligue a entrega quando o modelo do arquivo estiver pronto: o suporte da Softeum (diiego.martins@softeum.com.br) ajusta o modelo ao layout do seu ERP. Ao ligar, o painel diz qual arquivo vai ao ERP (o modelo pronto, sem ajuste, ele avisa).

O arquivo do pedido tem versões. A equipe Softeum monta o modelo no layout do seu ERP, a partir do arquivo que o ERP importa (de preferência o de um pedido que já passou pelo Softeum) e da especificação do layout, confere com os seus últimos pedidos e publica uma versão. Ela pode publicar antes de a TI cadastrar o servidor: o cadastro já sai com a versão em uso, mesmo antes do primeiro pedido e mesmo que o pedido de exemplo do painel não caiba nela (um código de 4 posições num ERP de 3): o pedido que ela não gerar vai para erro, com o motivo. A aba mostra a versão em uso (quem montou e quando) e o histórico das versões.

  • Mudar o arquivo nesta tela cria uma versão nova, feita no painel, e os próximos pedidos já saem com ela. Quando a versão em uso é da equipe Softeum, o painel pede a confirmação antes; a equipe vê a mudança no histórico e pode voltar a versão dela.
  • Se a equipe Softeum publicar outra versão enquanto a tela está aberta com mudança não salva (também no cadastro), salvar é recusado e a tela pede para recarregar: o arquivo da tela não grava por cima do novo nem é trocado sem aviso.
  • Excluir a conexão apaga a senha e a chave, não as versões: cadastrando de novo, a conexão volta com a versão em uso.

A prévia com os seus pedidos. Além do pedido de exemplo, Usar os pedidos de verdade traz os últimos 20 pedidos que vão ao ERP, montados como a entrega monta: a prévia gera com o escolhido e diz quantos geram o arquivo e quais não (com o motivo). O modelo novo que o pedido de exemplo não gera (um código de 4 posições num ERP de 3) salva quando todos os pedidos de verdade geram.

Depois de salvar, use Testar conexão: o serviço que entrega os pedidos conecta ao servidor, confere a pasta e grava, renomeia e apaga um arquivo de teste (.softeum-teste-*.tmp); com a gravação direta, grava e apaga, sem renomear. Nenhum pedido é enviado. No máximo um teste a cada 30 segundos e 10 por hora, por empresa. O teste que passa libera as entregas: a conexão nova e o acesso trocado esperam por ele (15.1).

  • Identificação do servidor: no primeiro contato, o Softeum grava a impressão digital da chave do servidor (SHA256:...) e confere em todas as conexões seguintes. Se ela mudar, nada é gravado até o administrador conferir com a TI, aceitar a nova no painel e testar a conexão.
  • Servidor que só aceita endereços liberados: a entrega ainda não sai por um endereço fixo, então o servidor SFTP que só aceita conexões de endereços liberados não recebe os arquivos. Fale com o suporte da Softeum. O e-mail do suporte é diiego.martins@softeum.com.br.

15.3 O modelo do arquivo

O modelo é o arquivo como o ERP espera, com marcações entre chaves duplas. O painel traz modelos prontos (XML do pedido, CSV com um item por linha, TXT de largura fixa e o pedido de venda do SAP pela interface odv-mapping), a lista completa das variáveis e filtros e a prévia do arquivo com um pedido de exemplo, que já acusa erro com a linha. A prévia é o arquivo que o Softeum grava, byte a byte: a mesma montagem, a mesma codificação e a mesma quebra de linha.

Marcação O que faz
{{pedido.numero_comprador}} O valor do pedido, com o escape do tipo de conteúdo (15.4).
{{#cada pedido.itens}} ... {{/cada}} Repete o trecho para cada item; o item da vez é item (item.posicao é 1, 2, 3...).
{{#se pedido.data_entrega}} ... {{senao}} ... {{/se}} O trecho só entra quando o valor não está vazio.
{{! comentário }} Não vai para o arquivo.

A linha que só tem uma marcação de bloco sai inteira do arquivo. Para o {{#se}}, vazio é: sem valor, texto vazio, zero (preço 0 é item sem preço) e item.ultimo em todo item menos o último.

Variáveis. Seguem os campos do pedido (seção 4): pedido.*, pedido.comprador.*, pedido.vendedor.*, os itens (item.produto.ean, item.quantidade_erp...), pedido.quantidade_itens, empresa.cnpj (a sua empresa) e agora (quando o arquivo é gerado; não vale no nome do arquivo, que precisa ser o mesmo em todas as tentativas). E também:

Variável O que é
arquivo.linha O número desta linha no arquivo (1, 2, 3...). No rodapé, é o total de linhas, com o cabeçalho e o próprio rodapé. Também serve de sequencial do registro.
item.ultimo Sim só no último item. No JSON, a vírgula entre os itens: {{#se item.ultimo}}{{senao}},{{/se}}.
item.preco_unitario_erp O preço na unidade do ERP, para ir com item.quantidade_erp (76,04 a CX de 12 → 6,336667 a UN). O item.preco_unitario é o preço na unidade do comprador, para ir com item.quantidade. Com 6 casas, a conta quantidade × preço bate com o valor do item.
item.desconto_percentual, item.desconto_tipo Só na indústria que lê o desconto de cada item (a equipe Softeum liga): o desconto escrito no item (10,50% → 10.5, com numero:2) e o tipo (comercial, especial ou promocional); vazios no item sem desconto. O item.preco_unitario já vem com ele. O arquivo que usa essas variáveis não sai do pedido lido sem o desconto de cada item (o pedido fica com erro, sem conectar).

Filtros. Vêm depois da variável, com |, um depois do outro:

Filtro Exemplo Resultado
data {{pedido.data_entrega | data:DD/MM/AAAA}} 07/10/2026 (também DDMMAAAA, AAAAMMDD, HH, mm, ss; com dois-pontos, entre aspas: data:"HH:mm")
numero {{item.preco_unitario | numero:2:,}} 76,04 (casas, separador decimal e de milhar; a metade arredonda para cima)
numero sem separador {{pedido.valor_total | numero:2:}} 325920 (casas implícitas, para TXT)
digitos {{pedido.comprador.cnpj | digitos}} só os números
mascara {{pedido.comprador.cnpj | mascara:##.###.###/####-##}} 12.345.678/0001-95 (cada # recebe um caractere do valor)
trecho {{pedido.comprador.codigo_erp | trecho:7:2}} 2 caracteres a partir do 7º; trecho:-2 pega os 2 últimos
esquerda / direita {{item.quantidade_erp | numero:4: | direita:12:0}} 000003600000 (largura fixa, 15.5)
corta, padrao, troca {{pedido.observacoes | corta:35}} no máximo 35 caracteres
maiusculas, minusculas, sem_acento {{item.produto.descricao | sem_acento | maiusculas}} LIMPA-VIDROS REFIL 500 ML
csv {{item.produto.descricao | csv}} entre aspas quando tem ; ou aspas (csv:, para o separador vírgula)
json {{pedido.observacoes | json}} o valor em JSON: texto entre aspas, número como número e vazio como null (também o vazio que outro filtro devolveu: sem a data, {{pedido.data_entrega | data:DD/MM/AAAA | json}} dá null)

esquerda e direita cortam só texto livre: razão social, nome, descrição, endereço, observações e condição de pagamento. Código, CNPJ, EAN, unidade, número e data que não cabem nas posições não são cortados: o arquivo não é gerado e o pedido fica com erro, dizendo a linha do modelo, porque cortado o valor vira outro (o código de outro produto; 1.440,000 que vira 14,400). Se o ERP aceita o código cortado, ponha corta antes: {{pedido.numero_comprador | corta:15 | esquerda:15}}.

Valor vazio em cada filtro: data, numero, mascara e os de texto devolvem vazio; esquerda completa o campo com espaços e direita:N:0 com zeros; padrao põe o texto dado; json devolve null, também depois de outro filtro. O padrao só troca o vazio onde aparece na cadeia: ponha antes de esquerda, direita e json (| padrao:0 | esquerda:6 sai 0 ; | esquerda:6 | padrao:0 sai em branco).

A conferência avisa do que costuma sair errado sem quebrar o arquivo (o modelo já salvo continua gerando):

  • data com uma letra que não é código (YYYY, hh, SS saem como estão: use AAAA, HH, ss) e MM logo depois da hora (é o mês: para os minutos, mm);
  • digitos num número (3.259,20 vira 32592; para as casas sem separador, numero:2:) e o separador de milhar sem o decimal, ou igual a ele (numero:2:,:. é o certo);
  • esquerda ou direita antes de outro filtro (o outro pode mudar o tamanho do campo: deixe a largura no fim) e o campo com mais de 2.000 posições;
  • no nome do arquivo, pedido.arquivo_original (o link muda a cada tentativa) e um pedaço do começo de pedido.id (o começo é a hora: dois pedidos do mesmo minuto ganhariam o mesmo nome; use o id inteiro ou trecho:-12);
  • no CSV, o campo de texto sem | csv (a unidade CX;99999 que o comprador escreveu abriria uma coluna a mais) e, no JSON, o texto, a data ou o valor vazio fora das aspas e sem | json;
  • o arquivo do pedido de exemplo maior que 100 KB (um pedido com muitos itens passaria de 5 MB e não seria gerado).

15.4 Tipo de conteúdo e codificação

O tipo de conteúdo diz como os valores entram no modelo. O texto fixo do modelo vai como está escrito (em ISO-8859-1, com as mesmas trocas dos valores, abaixo).

Tipo Os valores entram
XML Com & < > " ' trocados por &amp; e os outros. Dentro de <![CDATA[ ... ]]> do modelo, como estão.
JSON Com o escape de texto do JSON (\", \\, \n): o modelo põe as aspas, como em "descricao": "{{item.produto.descricao}}". Número vai sem aspas: "quantidade": {{item.quantidade_erp}}.
Texto (CSV, TXT) Sem quebra de linha nem tabulação (viram espaço), para o valor nunca quebrar o registro.

Cada valor entra já como vai ficar no arquivo, antes dos filtros: com os acentos compostos (o acento separado da letra, comum em texto copiado de PDF, vira a letra acentuada) e, em ISO-8859-1, com o parecido no lugar do que a codificação não tem: ™ vira TM, … vira ..., € vira EUR, — vira -, aspas curvas viram retas e o resto vira ?. Assim esquerda, direita e corta contam as posições que o ERP lê.

Exemplo de ERP que importa JSON com os nomes de campo dele:

{
  "pedido": "{{pedido.numero_comprador}}",
  "cliente": "{{pedido.comprador.codigo_erp}}",
  "observacao": {{pedido.observacoes | json}},
  "itens": [
    {{#cada pedido.itens}}
    { "produto": "{{item.produto.codigo_erp}}", "descricao": "{{item.produto.descricao}}", "quantidade": {{item.quantidade_erp}} }{{#se item.ultimo}}{{senao}},{{/se}}
    {{/cada}}
  ]
}

O arquivo do pedido de exemplo:

{
  "pedido": "45882",
  "cliente": "C001",
  "observacao": null,
  "itens": [
    { "produto": "4359", "descricao": "Limpa-vidros refil 500 ml", "quantidade": 360 },
    { "produto": "4360", "descricao": "Desengordurante ação rápida 500 ml", "quantidade": 144 }
  ]
}

O arquivo de cada pedido é conferido antes de ir ao servidor: o JSON tem de ser válido e o XML bem formado (as marcas abrem e fecham na ordem, o & escapado). O que não passa vai para erro, sem conectar, com a linha e a coluna onde quebrou. A prévia mostra o mesmo, com o pedido de exemplo e com os pedidos de verdade (o caso comum é a vírgula depois do último item, ou um EAN vazio fora das aspas). O modelo novo ou mudado que, com o pedido de exemplo, gera um JSON inválido ou um XML mal formado não é salvo; o modelo que já estava salvo assim continua, com o aviso.

O XML em ISO-8859-1 precisa da declaração na primeira linha (<?xml version="1.0" encoding="ISO-8859-1"?>): sem ela, o leitor de XML entende UTF-8 e recusa o arquivo ou estraga os acentos. A prévia avisa e oferece pôr.

15.5 TXT de largura fixa

  • Codificação: use ISO-8859-1, uma posição por letra. Em UTF-8 a largura conta letras, mas a letra com acento ocupa 2 bytes, e o ERP que conta bytes lê os campos fora do lugar (a prévia avisa, com esquerda ou direita).
  • Todo campo leva esquerda ou direita, até a data opcional (data:AAAAMMDD | esquerda:8): vazia, ela vira 8 espaços e os campos seguintes ficam no lugar.
  • Número: zeros à esquerda e as casas implícitas, numero:2: | direita:15:0 (12,34 vira 000000000001234); com vírgula, numero:2:, | direita:15:0.
  • Quantidade: com 4 casas (numero:4:), as que o pedido pode ter: com 3, o item vendido a peso com 12,5625 KG vai como 12,563.
  • Contagens e totais: pedido.quantidade_itens, pedido.valor_total e arquivo.linha no rodapé (o total de linhas, que já conta o registro opcional quando ele entra).
  • Sequência dos itens: item.posicao (1, 2, 3), com zeros direita:3:0 (001) ou de 10 em 10, como o SAP: {{item.posicao | direita:5:0}}0 (000010, 000020).

Exemplo: cabeçalho (1), um registro por item (2) e o rodapé (9) com a quantidade de itens, o total e o número de linhas do arquivo.

{{! 1 = cabeçalho, 2 = um por item, 9 = rodapé }}
1{{pedido.numero_comprador | esquerda:15}}{{pedido.comprador.cnpj | direita:14:0}}{{pedido.recebido_em | data:DDMMAAAA}}{{pedido.data_entrega | data:AAAAMMDD | esquerda:8}}
{{#cada pedido.itens}}
2{{item.posicao | direita:3:0}}{{item.produto.codigo_erp | esquerda:6}}{{item.produto.descricao | sem_acento | maiusculas | esquerda:30}}{{item.quantidade_erp | numero:4: | direita:11:0}}{{item.preco_unitario_erp | numero:6: | direita:12:0}}
{{/cada}}
9{{pedido.quantidade_itens | direita:3:0}}{{pedido.valor_total | numero:2: | direita:12:0}}{{arquivo.linha | direita:4:0}}

O arquivo do pedido de exemplo (a descrição do item 2 passou de 30 posições e foi cortada):

145882          123456780001953009202620261007
20014359  LIMPA-VIDROS REFIL 500 ML     00003600000000006336667
20024360  DESENGORDURANTE ACAO RAPIDA 5000001440000000006791667
90020000003259200004

15.6 Layouts comuns

  • SAP (odv-mapping): o modelo pronto, com o cliente pelo CNPJ e o produto pelo EAN. Troque os valores fixos pelos do seu SAP. Produto sem EAN vai com o EANCode vazio; se o seu SAP aceita o código do material, use <MaterialCode>{{item.produto.codigo_erp}}</MaterialCode>.
  • SAP (IDoc ORDERS05): o modelo pronto SAP: IDoc de pedido (ORDERS05), um IDoc em XML por pedido para a porta de arquivo do SAP ou para o PI/PO, com os segmentos na ordem do tipo: o controle (EDI_DC40), a organização de vendas (E1EDK14), a data do pedido (E1EDK03 022) e a de entrega (002), o emissor da ordem (E1EDKA1 AG, com comprador.codigo_erp), o pedido do comprador (E1EDK02 001) e, em cada item, a quantidade do ERP (E1EDP01), a entrega do item (E1EDP20) e o material (E1EDP19 002, com produto.codigo_erp). Troque as portas, os parceiros e a organização de vendas pelos do seu SAP; a equipe SAP prepara o perfil de parceiro (ORDERS, código de processo ORDE).
  • TOTVS Protheus: não tem importação de pedido por arquivo pronta; a TI do cliente faz a rotina (ExecAuto do MATA410) que lê o arquivo. O modelo pronto TOTVS Protheus: pedido de venda (CSV) traz um item por linha, com as colunas no nome do campo do Protheus: o cliente e a loja saem do código do comprador no ERP ({{pedido.comprador.codigo_erp | troca:-: | trecho:1:6}} e trecho:7:2, com ou sem o hífen), o CNPJ para achar o cliente sem o código, o produto e o código de barras, a quantidade e o preço na unidade do ERP com ponto decimal (numero:4 e numero:6), a entrega do item, o pedido e o item do comprador para a nota (C6_NUMPCOM e C6_ITEMPC) e o SOFTEUM_ID, que a rotina guarda no C5_XIDSOFT para o mesmo pedido não entrar duas vezes. O C6_ITEM a rotina numera (Soma1, que passa do 99).
  • CSV: o modelo pronto leva | csv em todo campo de texto (o valor com ; ou aspas vai entre aspas) e item.preco_unitario_erp ao lado de item.quantidade_erp (o preço na mesma unidade da quantidade).
  • CSV lido por rotina que só corta no ;: aspas não protegem. Tire o ; do texto: {{item.produto.descricao | troca:";":","}}.
  • EDI de pedido (registros 01, 04, 09): quantidade e preço na unidade do comprador (item.quantidade, item.unidade, item.preco_unitario), EAN com 14 posições (direita:14:0) e datas data:AAAAMMDDHHmm.

O que o modelo não faz: um arquivo leva um pedido só (não junta vários pedidos num arquivo); não faz contas além das variáveis prontas (a soma das quantidades, o total de linhas no cabeçalho); e o que o pedido não traz (o código da condição de pagamento, a TES, a tabela de preço, a transportadora) vai fixo no modelo ou fica com o padrão do cadastro no ERP.

Exemplo: o pedido de venda do SAP pela interface de arquivos (odv-mapping), com o cliente pelo CNPJ e o produto pelo EAN. Troque os valores fixos pelos do seu SAP.

<odv-mapping xmlns="urn:odv-mapping-1.0">
	<Order>
		<OrderType>ZD30</OrderType>
		<SalesOrg>BR10</SalesOrg>
		<DistrChannel>30</DistrChannel>
		<Division>99</Division>
		<CNPJSoldTo>{{pedido.comprador.cnpj | digitos}}</CNPJSoldTo>
		<PurchaseOrder>{{pedido.numero_comprador | corta:35}}</PurchaseOrder>
		<PurchaseOrderDate>{{#se pedido.data_pedido}}{{pedido.data_pedido | data:AAAAMMDD}}{{senao}}{{pedido.recebido_em | data:AAAAMMDD}}{{/se}}</PurchaseOrderDate>
		<RequestDeliveryDate>{{pedido.data_entrega | data:AAAAMMDD}}</RequestDeliveryDate>
		{{#cada pedido.itens}}
		<Item>
			<ItemNumber>{{item.posicao}}</ItemNumber>
			<EANCode>{{item.produto.ean}}</EANCode>
			<Quantity>{{item.quantidade_erp}}</Quantity>
		</Item>
		{{/cada}}
	</Order>
</odv-mapping>

A data do pedido (PurchaseOrderDate) é a que o documento do comprador traz (pedido.data_pedido, a emissão); sem ela, a de quando o e-mail chegou (pedido.recebido_em). A data de entrega de cada item, quando o documento traz uma por item, está em item.data_entrega.

Na indústria que lê o desconto de cada item (4.7), o desconto vai no <Condition> do item, logo depois de <ItemCategory> no modelo pronto do SAP, com os três tipos trocados pelos códigos da condição no SAP (os do exemplo; use os do seu SAP). O item sem desconto não leva o <Condition>, e o pedido sem desconto sai igual ao do modelo pronto:

			{{#se item.desconto_percentual}}
			<Condition>
				<ConditionType>{{item.desconto_tipo | troca:comercial:ZCBR | troca:especial:ZESP | troca:promocional:ZPBR}}</ConditionType>
				<Value>{{item.desconto_percentual | numero:2}}</Value>
				<Currency>%</Currency>
			</Condition>
			{{/se}}

O arquivo que usa o desconto de cada item não sai do pedido lido sem ele (o lido antes de ligar): o pedido fica com erro, sem conectar, para lançar à mão.