Receber produtos do Siprinho
Quando o vendedor clica no "+" de um produto no Siprinho — o produto consultado ou uma sugestão de substituto, cross-sell ou upsell —, esse item precisa chegar à cesta do ERP. O Siprinho sabe entregar o item de cinco formas, e cada loja usa uma delas por vez:
| Método | Quem entrega ao ERP | O que o ERP implementa |
|---|---|---|
| Deeplink | o Siprinho, na própria máquina | um protocolo sipru_{NOME_DO_ERP}:// registrado no Windows |
| API do ERP | a nuvem Sipru | um endpoint HTTPS que recebe um POST por item |
| Banco de dados | o Siprinho, na própria máquina | uma tabela (ou procedure) que recebe o item |
| Arquivo | o Siprinho, na própria máquina | a leitura de uma pasta onde cada item vira um arquivo |
| Área de transferência | o vendedor, com Ctrl+V | nada — o vendedor cola o código no ERP |
O método é escolhido na tela de configuração do Siprinho, em Integração com o ERP. Em API (online) o item segue para a nuvem Sipru; em Integrações locais o Siprinho intercepta o clique e entrega o item na própria máquina, pelo método local escolhido.
Seja qual for o método, o ERP lança o item com o preço e as regras do próprio ERP. O preço que vem do Siprinho é informativo.
Visão geral do fluxo
┌─────────┐ 1. abre o canal (POST /channel) ┌───────────┐
│ │ ──────────────────────────────────▶│ │
│ ERP │ ◀──────────────────────────────────│ Sipru API │
│ │ channelId + url │ │
└────┬────┘ └─────┬─────┘
│ 2. abre a url │
▼ │
┌──────────┐◀─────── carrega a sessão ───────────────┘
│ Siprinho │
└────┬─────┘
│ 3. o vendedor clica no "+"
▼
┌────────────────────────────────────────────────────────────┐
│ Integrações locais: deeplink, banco, arquivo ou clipboard │ ── na máquina
│ API (online): Sipru API ── POST no endpoint do ERP │ ── pela nuvem
└────────────────────────────────────────────────────────────┘
A sessão é aberta como descrito em Canal ao vivo. O Siprinho fica aberto o tempo todo, inclusive entre um atendimento e outro — o ERP não deve fechá-lo.
Os dados do item
Nos métodos locais, quem decide o formato da entrega é o modelo configurado no Siprinho: um texto com campos entre chaves duplas, trocados pelos dados do item clicado. Os campos são os mesmos nos quatro métodos locais:
| Campo | O que traz |
|---|---|
{{productCode}} ou {{sku}} | Código do produto |
{{ean}} | Código de barras |
{{quantity}} | Quantidade |
{{name}} | Nome do produto |
{{price}} | Preço unitário (informativo) |
{{source}} | De onde veio o clique: o produto consultado ou uma sugestão |
{{channelId}}, {{tempQuoteId}} | O canal e a cotação temporária da sessão |
{{companyId}}, {{locationId}}, {{pricingPolicy}} | Empresa, loja e política de preço da sessão |
{{vendedor.codigoExterno}}, {{vendedor.nome}} | Vendedor da sessão |
{{data}}, {{hora}}, {{timestamp}} | Data, hora e data e hora completas do clique |
{{eventId}} | Identificador único do clique |
Dois campos merecem atenção do lado do ERP:
{{tempQuoteId}}diz em qual cotação o item entra. Com mais de um atendimento aberto na mesma máquina, é ele que evita lançar o item na cesta errada.{{eventId}}é único por clique. Use-o como chave para não lançar o mesmo clique duas vezes.
Campo que não existir no clique fica vazio. A lista completa, com os valores reais do último clique, está no botão Ver campos disponíveis — ver Campos que podem ser usados.
Deeplink
A cada clique, o Siprinho abre um endereço de protocolo que o ERP registrou no Windows, com os dados do item na query string. O modelo sugerido pelo Siprinho é:
sipru_{NOME_DO_ERP}://carrinho/adicionar?ean={{ean}}&quantity={{quantity}}&productCode={{productCode}}&name={{name}}
E um clique vira, por exemplo:
sipru_meuerp://carrinho/adicionar?ean=&quantity=2&productCode=007136&name=Parafuso%206mm
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
{NOME_DO_ERP} | slug | — | Ex.: sipru_meuerp://. Mantenha igual ao valor enviado no header X-ERP-Name. |
quantity | número | sim | Quantidade a incluir na cesta. Sempre enviado. |
ean / productCode | string | — | Pelo menos um deles vem preenchido. Use o que o seu cadastro reconhecer. |
name | string | — | Só um rótulo, útil na mensagem de erro se o produto não for encontrado no ERP. |
O endereço é configurável no Siprinho: se o ERP precisar de outros parâmetros (por exemplo,
tempQuoteId={{tempQuoteId}}), basta incluí-los no modelo, com os campos acima.
O que o ERP precisa cumprir
- Não abrir uma segunda instância. Sem tratamento, cada clique abre o ERP de novo. A segunda ativação deve repassar o endereço para a instância já em execução (mutex ou lock + IPC local) e encerrar.
- Decodificar a query string (URL-decode) e localizar o produto por
eane/ouproductCode. - Tratar como fire-and-forget. O Siprinho não espera resposta. Se não houver cesta aberta ou o produto não for encontrado, o ERP avisa o usuário na própria tela — não existe retry nem fila.
Se o protocolo não estiver registrado, o Siprinho mostra um aviso vermelho no painel ("Não foi possível abrir o ERP"), para ninguém achar que o item foi lançado.
Exemplos de código por linguagem
Como garantir instância única e tratar o endereço recebido, por linguagem.
C# (.NET)
Exemplo de implementação de "captura" do evento de clique no Siprinho para adicionar o produto na cesta de pedidos no ERP.
Garantir instância única e repassar a URI (Mutex + Named Pipe)
using System.IO.Pipes;
const string NomeMutex = @"Local\MeuErp.SingleInstance";
const string NomePipe = "MeuErp.ActivationPipe";
using var mutex = new Mutex(initiallyOwned: true, NomeMutex, out var souAPrimeira);
if (!souAPrimeira)
{
// Repassa a URI recebida (args[0]) pra instância já aberta, e encerra.
using var cliente = new NamedPipeClientStream(".", NomePipe, PipeDirection.Out);
cliente.Connect(2000);
using var escritor = new StreamWriter(cliente) { AutoFlush = true };
escritor.Write(args[0]);
return;
}
// Só a primeira instância escuta o pipe em loop, em background:
_ = Task.Run(async () =>
{
while (true)
{
await using var servidor = new NamedPipeServerStream(NomePipe, PipeDirection.In, 1);
await servidor.WaitForConnectionAsync();
using var leitor = new StreamReader(servidor);
var uri = await leitor.ReadToEndAsync();
ProcessarUriCarrinho(uri);
}
});
Parsear o callback
static void ProcessarUriCarrinho(string uriTexto)
{
var uri = new Uri(uriTexto);
if (!string.Equals(uri.Host, "carrinho", StringComparison.OrdinalIgnoreCase) ||
!string.Equals(uri.AbsolutePath.Trim('/'), "adicionar", StringComparison.OrdinalIgnoreCase))
return;
var query = System.Web.HttpUtility.ParseQueryString(uri.Query);
var ean = query["ean"];
var productCode = query["productCode"];
var name = query["name"];
if (!int.TryParse(query["quantity"], out var quantity)) return;
AdicionarAoCarrinho(ean, productCode, quantity, name);
}
VB.NET
Os mesmos dois passos do C#, num único bloco contínuo.
Imports System.Diagnostics
Imports System.IO.Pipes
Imports Microsoft.Win32
' Instância única (Mutex) + repasse via Named Pipe
Const NomeMutex As String = "Local\MeuErp.SingleInstance"
Const NomePipe As String = "MeuErp.ActivationPipe"
Dim souAPrimeira As Boolean
Dim mutex As New Mutex(True, NomeMutex, souAPrimeira)
If Not souAPrimeira Then
Using cliente As New NamedPipeClientStream(".", NomePipe, PipeDirection.Out)
cliente.Connect(2000)
Using escritor As New StreamWriter(cliente) With {.AutoFlush = True}
escritor.Write(uriRecebida)
End Using
End Using
Return
End If
' Parsear o callback
Sub ProcessarUriCarrinho(uriTexto As String)
Dim uri As New Uri(uriTexto)
If Not String.Equals(uri.Host, "carrinho", StringComparison.OrdinalIgnoreCase) Then Return
If Not String.Equals(uri.AbsolutePath.Trim("/"c), "adicionar", StringComparison.OrdinalIgnoreCase) Then Return
Dim query = System.Web.HttpUtility.ParseQueryString(uri.Query)
Dim quantity As Integer
If Not Integer.TryParse(query("quantity"), quantity) Then Return
AdicionarAoCarrinho(query("ean"), query("productCode"), quantity, query("name"))
End Sub
VB6
VB6 não tem Named Pipe nativo — usa-se um loopback TCP local (via controle Winsock) para repassar
a URI entre instâncias, já que subclassar a janela para WM_COPYDATA é bem mais trabalhoso em
VB6.
Instância única + repasse via Winsock loopback
' Instância principal — Form_Load
Private Sub Form_Load()
If App.PrevInstance Then
' nunca deveria chegar aqui: a mensageira já finaliza sozinha
End
End If
Winsock1.LocalPort = 51837
Winsock1.Listen
End Sub
Private Sub Winsock1_ConnectionRequest(ByVal requestID As Long)
Winsock1.Close
Winsock1.Accept requestID
End Sub
Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
Dim uriTexto As String
Winsock1.GetData uriTexto
ProcessarUriCarrinho uriTexto
Winsock1.Close
Winsock1.Listen
End Sub
' Instância mensageira — Sub Main (App.PrevInstance = True)
Sub EnviarParaInstanciaExistente(uriTexto As String)
Dim cliente As Winsock
Set cliente = New Winsock
cliente.Connect "127.0.0.1", 51837
Do While cliente.State <> sckConnected: DoEvents: Loop
cliente.SendData uriTexto
End Sub
Parsear o callback
Sub ProcessarUriCarrinho(uriTexto As String)
Dim partes() As String, parQuery() As String, i As Integer
Dim ean As String, productCode As String, nome As String, quantity As Long
Dim posInterrogacao As Long
posInterrogacao = InStr(uriTexto, "?")
If posInterrogacao = 0 Then Exit Sub
partes = Split(Mid$(uriTexto, posInterrogacao + 1), "&")
For i = 0 To UBound(partes)
parQuery = Split(partes(i), "=")
Select Case LCase$(parQuery(0))
Case "ean": ean = UnescapeURL(parQuery(1))
Case "productcode": productCode = UnescapeURL(parQuery(1))
Case "name": nome = UnescapeURL(parQuery(1))
Case "quantity": quantity = CLng(parQuery(1))
End Select
Next i
If quantity <= 0 Then Exit Sub
AdicionarAoCarrinho ean, productCode, quantity, nome
End Sub
Delphi
Padrão clássico Delphi: CreateMutex + FindWindow/
SendMessage(WM_COPYDATA) para instância única e repasse — é o equivalente mais idiomático ao
Named Pipe do .NET nesse ecossistema.
Instância única + repasse via WM_COPYDATA
const
NomeMutex = 'Local\MeuErp.SingleInstance';
NomeJanela = 'MeuErpJanelaPrincipal'; // Form1.Caption/Name único, usado pelo FindWindow
procedure TFormPrincipal.FormCreate(Sender: TObject);
var
Mutex: THandle;
JanelaExistente: HWND;
Dados: TCopyDataStruct;
UriRecebida: string;
begin
Mutex := CreateMutex(nil, True, NomeMutex);
if GetLastError = ERROR_ALREADY_EXISTS then
begin
JanelaExistente := FindWindow(nil, NomeJanela);
if (JanelaExistente <> 0) and (ParamCount >= 1) then
begin
UriRecebida := ParamStr(1);
Dados.dwData := 0;
Dados.cbData := (Length(UriRecebida) + 1) * SizeOf(Char);
Dados.lpData := PChar(UriRecebida);
SendMessage(JanelaExistente, WM_COPYDATA, 0, LPARAM(@Dados));
end;
Application.Terminate;
Exit;
end;
if ParamCount >= 1 then
ProcessarUriCarrinho(ParamStr(1)); // ativação direta (app ainda não estava aberto)
end;
procedure TFormPrincipal.WMCopyData(var Msg: TWMCopyData);
begin
ProcessarUriCarrinho(PChar(Msg.CopyDataStruct.lpData));
end;
// declarar em TFormPrincipal: procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA;
Parsear o callback
uses System.NetEncoding, System.StrUtils;
procedure ProcessarUriCarrinho(const UriTexto: string);
var
Uri: TUri;
Query: TStringList;
Ean, ProductCode, Nome: string;
Quantity: Integer;
begin
Uri := TUri.Create(UriTexto);
try
if not SameText(Uri.Host, 'carrinho') then Exit;
if not SameText(Trim(Uri.Path, ['/']), 'adicionar') then Exit;
Query := TStringList.Create;
try
Query.Delimiter := '&';
Query.DelimitedText := Uri.Query;
Ean := TNetEncoding.URL.Decode(Query.Values['ean']);
ProductCode := TNetEncoding.URL.Decode(Query.Values['productCode']);
Nome := TNetEncoding.URL.Decode(Query.Values['name']);
if not TryStrToInt(Query.Values['quantity'], Quantity) then Exit;
AdicionarAoCarrinho(Ean, ProductCode, Quantity, Nome);
finally
Query.Free;
end;
finally
Uri.Free;
end;
end;
Java
Instância única com FileLock; e repasse local via ServerSocket em loopback.
Instância única (FileLock) + repasse via socket loopback
import java.io.*;
import java.net.*;
import java.nio.channels.*;
static final int PORTA = 51837;
static boolean souAPrimeiraInstancia() throws IOException {
File lockFile = new File(System.getProperty("java.io.tmpdir"), "meuerp.lock");
FileChannel canal = new RandomAccessFile(lockFile, "rw").getChannel();
FileLock lock = canal.tryLock();
return lock != null; // se null, já existe outra instância rodando
}
// Instância principal: escuta em background
static void iniciarEscuta() {
new Thread(() -> {
try (ServerSocket servidor = new ServerSocket(PORTA, 0, InetAddress.getLoopbackAddress())) {
while (true) {
try (Socket cliente = servidor.accept();
BufferedReader leitor = new BufferedReader(new InputStreamReader(cliente.getInputStream()))) {
processarUriCarrinho(leitor.readLine());
}
}
} catch (IOException e) { /* log */ }
}, "sipru-activation-listener").start();
}
// Instância mensageira: repassa a URI e encerra
static void enviarParaInstanciaExistente(String uriTexto) throws IOException {
try (Socket socket = new Socket(InetAddress.getLoopbackAddress(), PORTA);
PrintWriter escritor = new PrintWriter(socket.getOutputStream(), true)) {
escritor.println(uriTexto);
}
}
Parsear o callback
import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
static void processarUriCarrinho(String uriTexto) {
URI uri = URI.create(uriTexto);
if (!"carrinho".equalsIgnoreCase(uri.getHost())) return;
if (!"adicionar".equalsIgnoreCase(uri.getPath().replace("/", ""))) return;
Map<String, String> query = new HashMap<>();
for (String par : uri.getQuery().split("&")) {
String[] kv = par.split("=", 2);
query.put(kv[0].toLowerCase(), kv.length > 1
? URLDecoder.decode(kv[1], StandardCharsets.UTF_8) : "");
}
int quantity;
try {
quantity = Integer.parseInt(query.get("quantity"));
} catch (Exception e) {
return;
}
adicionarAoCarrinho(query.get("ean"), query.get("productcode"), quantity, query.get("name"));
}
Python
Instância única e repasse via socket loopback (mesma abordagem do Java, portável e sem dependências externas).
Instância única + repasse via socket loopback
import socket
import threading
PORTA = 51837
def sou_a_primeira_instancia() -> bool:
global _socket_trava
_socket_trava = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
_socket_trava.bind(("127.0.0.1", PORTA))
_socket_trava.listen(5)
return True
except OSError:
return False # porta já ocupada por outra instância
def iniciar_escuta() -> None:
def loop():
while True:
cliente, _ = _socket_trava.accept()
with cliente:
uri_texto = cliente.recv(4096).decode("utf-8")
processar_uri_carrinho(uri_texto)
threading.Thread(target=loop, daemon=True, name="sipru-activation-listener").start()
def enviar_para_instancia_existente(uri_texto: str) -> None:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as cliente:
cliente.connect(("127.0.0.1", PORTA))
cliente.sendall(uri_texto.encode("utf-8"))
Parsear o callback
from urllib.parse import urlparse, parse_qs
def processar_uri_carrinho(uri_texto: str) -> None:
uri = urlparse(uri_texto)
if uri.hostname != "carrinho" or uri.path.strip("/") != "adicionar":
return
query = {k.lower(): v[0] for k, v in parse_qs(uri.query).items()}
try:
quantity = int(query["quantity"])
except (KeyError, ValueError):
return
adicionar_ao_carrinho(query.get("ean"), query.get("productcode"), quantity, query.get("name"))
API do ERP
Com o Siprinho em API (online), o clique segue para a nuvem Sipru, e o Sipru faz um POST no
endpoint do ERP com o item. É o único endpoint que o parceiro implementa, e precisa estar
acessível pela internet.
Para onde o Sipru envia
O destino é resolvido nesta ordem de precedência:
- o
cartCallbackUrldo canal, quando enviado na abertura — override por cotação; - a URL de item adicionado cadastrada para a loja pelo time Sipru;
- o endpoint de pedidos da mesma configuração — usado como fallback por quem não separa os dois.
Sem nenhum dos três, o clique não vira POST, e o vendedor vê na tela que o item não foi
enviado ao ERP.
Em produção a URL precisa ser HTTPS. HTTP é aceito apenas em homologação, para você conseguir apontar para um host interno durante a integração.
Autenticação
Configurável por canal ou por loja, com credencial estática:
mode | O que o Sipru envia |
|---|---|
none | Nenhum header de autenticação |
bearer | Authorization: Bearer <token> |
api_key | X-API-Key: <token> |
OAuth client credentials não é suportado.
O payload
{
"idempotencyKey": "erp_cart_4f1c8b2e-...",
"event": "cart_item_added",
"channelId": "8f2b...",
"tempQuoteId": "temp-12345",
"quoteId": null,
"cliente": { "cnpj": "04252011000110", "razaoSocial": "…", "nomeFantasia": "…" },
"vendedor": { "cpf": "12345678909", "externalLogin": "joao.silva", "email": "vendedor@loja.com.br" },
"tenant": { "companyId": "empresa-x", "locationId": "07526557000100" },
"item": {
"productCode": "72411",
"ean": "7891234567890",
"name": "Argamassa AC-III 20kg",
"quantity": 4,
"unitPrice": 34.9,
"coinsReward": 12,
"source": "crossSell"
},
"timestamp": "2026-08-31T18:44:02.117Z"
}
O Idempotency-Key também vai no header, com o mesmo valor.
item.source diz de onde veio o clique: product (o produto que o ERP consultou),
substitutes, crossSell ou upsell. Guarde-o: é o que permite medir quanto da venda veio de
uma sugestão da Sipru.
O que o Sipru espera de volta
Responder 2xx. Qualquer 2xx serve.
O Sipru tenta 2 vezes, com timeout de 8 segundos, e só repete em status transitório —
408, 429 e 5xx. Um 4xx não transitório recusa o item de vez, sem nova tentativa. Se o item
não entrar, o vendedor vê isso na tela.
Como há retry, o mesmo item pode chegar duas vezes. Use o idempotencyKey (header ou corpo) como
chave de deduplicação do seu lado.
O contrato formal está em
webhooks.cartItemAdded na referência da API.
Banco de dados
A cada clique, o Siprinho executa um comando SQL no banco do ERP, direto da máquina do vendedor. O comando é escrito por quem configura o Siprinho, com os campos do item. Bancos suportados: SQL Server, MySQL / MariaDB, PostgreSQL, Oracle, Firebird, SQLite, Microsoft Access e ODBC (qualquer outro banco com driver ODBC instalado).
O caminho mais simples é uma tabela de entrada que o ERP lê:
CREATE TABLE SIPRU_CARRINHO (
EVENT_ID VARCHAR(64) NOT NULL PRIMARY KEY,
TEMP_QUOTE_ID VARCHAR(30),
CODIGO VARCHAR(120),
EAN VARCHAR(60),
QTD VARCHAR(10),
CRIADO_EM VARCHAR(30),
PROCESSADO CHAR(1) DEFAULT 'N'
);
E o comando configurado no Siprinho:
INSERT INTO SIPRU_CARRINHO (EVENT_ID, TEMP_QUOTE_ID, CODIGO, EAN, QTD, CRIADO_EM)
VALUES ({{eventId}}, {{tempQuoteId}}, {{productCode}}, {{ean}}, {{quantity}}, {{timestamp}})
O comando também pode chamar uma procedure do ERP, se o banco aceitar a chamada como um comando comum.
Como o Siprinho monta o comando:
- Cada campo vai como parâmetro, separado do texto do comando. Não precisa de aspas, e um nome com apóstrofo (ex.: D'Ávila) não quebra a query.
- Campos só ocupam o lugar de valores. Nome de tabela ou de coluna vai escrito direto no comando.
- O botão Testar query executa o comando com os dados do último clique e desfaz tudo no final: a tabela precisa aceitar a gravação, mas nada fica gravado no teste.
Do lado do ERP:
- Crie um usuário de banco só para o Siprinho, com permissão apenas de gravar na tabela de entrada. A senha fica criptografada no arquivo de configuração do Siprinho, em cada máquina.
- Libere a rede: cada máquina com o Siprinho conecta direto no servidor do banco.
- Leia os itens novos — por polling, trigger ou o mecanismo de fila do próprio ERP — e lance
cada um na cesta da cotação
TEMP_QUOTE_ID. - Marque o item como processado (ou apague a linha) depois de lançado. Com
EVENT_IDcomo chave, o mesmo clique nunca entra duas vezes.
Se o banco estiver fora do ar ou o comando falhar, o Siprinho mostra o erro em vermelho no painel. Não há retry: o clique se perde, e o vendedor clica de novo.
Arquivo
A cada clique, o Siprinho grava um arquivo numa pasta que o ERP acompanha.
- Pasta: a padrão é
%APPDATA%\SipruWidget\Integracao, do usuário do Windows logado. Se o ERP roda com outro usuário, ou em outra máquina, configure no Siprinho uma pasta que os dois enxerguem. - Nome e conteúdo: o mesmo modelo vira o nome e o conteúdo do arquivo. Com
{{productCode}}, um clique no produto ABC-123 geraABC-123.txtcomABC-123dentro. Caracteres que o Windows não aceita em nome de arquivo (como/e:) viram_no nome, mas ficam intactos no conteúdo. - Gravação atômica: o arquivo nasce com a extensão temporária (padrão
.tmp) e só é renomeado para a extensão final (padrão.txt) depois de totalmente gravado. - Codificação: UTF-8 por padrão, ou ANSI (Windows-1252) para sistemas mais antigos.
Se o modelo gerar o mesmo nome duas vezes, o segundo arquivo substitui o primeiro — dois cliques
no mesmo produto antes de o ERP ler a pasta viram um só. Inclua {{eventId}} no modelo para cada
clique gerar um arquivo próprio:
{{eventId}};{{tempQuoteId}};{{productCode}};{{quantity}}
Do lado do ERP:
- Acompanhe a pasta — por evento do sistema de arquivos ou por polling.
- Leia só a extensão final (
.txt). Arquivo.tmpainda está sendo gravado. - Leia com a mesma codificação configurada no Siprinho.
- Apague ou mova o arquivo depois de lançar o item, para não lê-lo de novo.
var pasta = Environment.ExpandEnvironmentVariables(@"%APPDATA%\SipruWidget\Integracao");
var observador = new FileSystemWatcher(pasta, "*.txt") { EnableRaisingEvents = true };
// O Siprinho grava .tmp e renomeia para .txt quando termina.
observador.Renamed += (_, e) =>
{
var conteudo = File.ReadAllText(e.FullPath, Encoding.UTF8);
var campos = conteudo.Split(';'); // eventId;tempQuoteId;productCode;quantity
AdicionarAoCarrinho(campos[1], campos[2], int.Parse(campos[3]));
File.Delete(e.FullPath);
};
Se a pasta não existir ou não tiver permissão de escrita, o Siprinho mostra o erro em vermelho no painel.
Área de transferência
A cada clique, o Siprinho copia um texto para a área de transferência do Windows, e o vendedor
cola no ERP com Ctrl+V. O padrão copia só o código do produto ({{productCode}}); dá para juntar
mais campos e texto livre, por exemplo {{productCode}};{{quantity}}.
Do lado do ERP não há nada a implementar: o vendedor cola o código no campo de produto, como se tivesse digitado. Se o modelo copiar mais de um campo, o campo do ERP precisa aceitar esse formato.
É o método mais simples de ligar, e o único em que o item depende de uma ação do vendedor para entrar na cesta.
Checklist de implementação
- Combinar com a loja qual método o Siprinho vai usar e configurar em Integração com o ERP.
- Deeplink: registrar
sipru_{NOME_DO_ERP}://, garantir instância única e tratar o endereço como fire-and-forget. - API do ERP: publicar o endpoint HTTPS, combinar a autenticação, responder 2xx e
deduplicar por
idempotencyKey. - Banco de dados: criar a tabela de entrada e um usuário só com permissão de gravar nela,
liberar a rede e deduplicar por
{{eventId}}. - Arquivo: combinar a pasta, ler só a extensão final, apagar depois de lançar e incluir
{{eventId}}no nome. - Em todos: lançar o item na cotação certa (
tempQuoteId), com o preço do cadastro do ERP, nunca o que veio do Siprinho.