Print Service Unificado — Guia de Implementação
Este é o guia prático de implementação passo a passo. Para contexto, arquitetura e decisões de design, ver contexto-print-service-neofarma.
Visão geral
Um único evento (“pago” → “ag.impressão”) dispara 3 impressões no server1:
VPS detecta mudança de status
│
POST /api/print-order { orderId, reqNumber }
│ via CF tunnel
▼
Server1 (Node + Express)
│
├─ 1. FITA → MP4200 (ESC/POS, rede local)
├─ 2. FICHAS → Brother A4 (PAD gera PDF no ERP → salva em G:\REQ\{req}\ → GS imprime)
└─ 3. RÓTULOS → Epson C6000AU (PDFs já estão em G:\REQ\{req}\ → GS imprime c/ altura variável)
Pasta unificada: G:\REQ\{numReq}\ contém fichas (geradas pelo PAD) e rótulos (gerados pela VPS via Drive).
Quando o evento chega ao server1, todos os PDFs já estão na pasta.
Passo 1 — Infraestrutura base no Server1
O que: preparar o ambiente onde tudo vai rodar.
Por que: Ghostscript, SQLite e SumatraPDF são dependências dos passos seguintes. Sem eles nada imprime.
Ações:
1.1. Instalar Ghostscript 10.x (64-bit) no server1
- Download: https://ghostscript.com/releases/gsdnld.html
- Adicionar
C:\Program Files\gs\gs10.x\binao PATH do sistema - Testar:
gswin64c.exe --versionno terminal
1.2. Instalar SumatraPDF portable (backup/alternativa para Brother)
- Baixar versão portable, salvar em
C:\Tools\SumatraPDF\
1.3. Criar estrutura de pastas
C:\PrintService\
├── print-service.js (o service principal)
├── print-log.db (SQLite, criado automaticamente)
└── temp\ (downloads temporários)
1.4. Verificar nome exato das 3 impressoras no Windows:
Get-Printer | Select-Object Name, DriverName, PortNameAnotar os nomes exatos — vão pro CONFIG do service.
1.5. Configurar driver da Epson C6000AU:
- Painel de Controle → Dispositivos → Epson C6000AU → Preferências
- Paper Source: Roll Paper (rolo contínuo, sem marcas)
- Isso é one-time — o Ghostscript herda essa config
1.6. Confirmar que o Google Drive está montado como G:\ e acessível
- Testar:
dir G:\REQ\deve listar pastas de pedidos existentes
Passo 2 — Firebird ODBC + SQLite (controle do PAD)
O que: conectar o PAD ao banco Firebird do ERP e ao SQLite de controle.
Por que: o PAD precisa saber quais REQs existem (Firebird) e quais já foram processadas (SQLite), para não reimprimir.
Ações:
2.1. Instalar Firebird ODBC 2.5 driver (32-bit se PAD for 32-bit)
2.2. Configurar DSN no ODBC Data Source Administrator:
- Nome:
ERP_Firebird - Database: caminho do .fdb ou IP:porta do servidor Firebird
- User/Password do ERP
2.3. Testar no PAD: ação “Execute SQL statement” com query simples
SELECT FIRST 5 NUMERO_REQ, DATA_INCLUSAO FROM REQUISICAO ORDER BY DATA_INCLUSAO DESC2.4. SQLite de controle — o PAD cria automaticamente se não existir, ou criar manualmente:
CREATE TABLE IF NOT EXISTS processamentos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
numero_req TEXT NOT NULL,
data_inclusao TEXT,
status TEXT DEFAULT 'pendente',
erro TEXT,
processado_em TEXT DEFAULT (datetime('now','localtime'))
);Passo 3 — UI Automation no PAD (gerar PDF da ficha)
O que: gravar a automação que abre a REQ no ERP, clica imprimir, salva PDF.
Por que: o ERP Delphi não tem API de exportação de PDF. A única forma de extrair a ficha é via UI — simulando o que o humano faz hoje.
Ações:
3.1. Gravar o fluxo com o Recorder do PAD usando uma REQ de teste:
- Abrir o ERP
- Navegar até a tela de requisição
- Inserir número da REQ
- Clicar botão de impressão
- No diálogo de impressão, selecionar “Microsoft Print to PDF”
- No “Salvar como”, preencher o path:
G:\REQ\{numReq}\ficha-{numReq}.pdf - Confirmar
3.2. Parametrizar: substituir o número fixo da REQ por variável %NumeroReq%
3.3. Adicionar waits conservadores (2-3s) entre cada ação de UI
- Delphi apps têm latência variável; waits evitam cliques em elementos ainda não renderizados
3.4. Adicionar verificação pós-salvamento:
- Ação “If file exists” em
G:\REQ\{numReq}\ficha-{numReq}.pdf - Se não existe → log de erro no SQLite, pular para próxima REQ
Passo 4 — Impressão da ficha na Brother (dentro do PAD)
O que: após gerar o PDF, imprimir na Brother A4.
Por que: a ficha precisa existir em papel para a triagem farmacêutica trabalhar.
Ações:
4.1. Após o passo 3 confirmar que o PDF existe, rodar via “Run PowerShell script” no PAD:
# Opção A — SumatraPDF (recomendado: silencioso, confiável)
& "C:\Tools\SumatraPDF\SumatraPDF.exe" -print-to "Brother HL-XXXX series" -silent "G:\REQ\%NumeroReq%\ficha-%NumeroReq%.pdf"
# Opção B — Ghostscript (consistente com os rótulos)
& gswin64c.exe -dBATCH -dNOPAUSE -dNOSAFER -sDEVICE=mswinpr2 "-sOutputFile=%printer%Brother HL-XXXX series" -sPAPERSIZE=a4 -dFitPage -dNoCancel "G:\REQ\%NumeroReq%\ficha-%NumeroReq%.pdf"4.2. Atualizar SQLite com status ‘ok’ para essa REQ
Passo 5 — Print Service Node.js (fita + rótulos + orquestração)
O que: o service Express que recebe o webhook da VPS e orquestra as 3 impressões.
Por que: a fita (ESC/POS) e os rótulos (GS com altura variável) precisam de lógica programática que o PAD não faz bem. O service é o ponto central que coordena tudo.
Ações:
5.1. Inicializar o projeto:
cd C:\PrintService
npm init -y
npm install express pdf-lib better-sqlite3(node-fetch não necessário se Node 18+ — usa fetch nativo)
5.2. Copiar o print-service.js entregue anteriormente
5.3. Ajustar CONFIG:
- Nomes exatos das impressoras (anotados no passo 1.4)
driveBasePath:G:\REQport: porta exposta pelo CF tunnel
5.4. Integrar o código ESC/POS existente da fita na função printFita(orderId):
- Hoje é chamado por botão → extrair a lógica para uma função async
- Recebe orderId, busca dados do pedido, monta buffer ESC/POS, envia à MP4200
5.5. Ajustar o job de rótulos (printRotulos):
- Já está no service: lista
G:\REQ\{orderId}\*.pdf, filtra só rótulos - Importante: precisamos distinguir fichas de rótulos na mesma pasta
- Convenção de nomes: fichas =
ficha-{numReq}.pdf, rótulos ={numReq}-a.pdf,{numReq}-b.pdf - O filtro no service lista PDFs que NÃO começam com “ficha-”
5.6. O job de fichas no service não imprime diretamente — ele dispara o PAD:
// Opção A: chamar o PAD via linha de comando
// PAD expõe execução via: "C:\Program Files\Power Automate Desktop\PAD.Console.Host.exe" run --flow "Nome do Fluxo" --input NumeroReq={numReq}
// Opção B: o service só dispara o PAD e confia que ele salva + imprime
// O PAD já faz tudo (salvar PDF + imprimir na Brother)5.7. Sequência no processOrder(orderId):
1. printFita(orderId) → ESC/POS direto na MP4200
2. triggerPAD(reqNumber) → PAD gera PDF da ficha + imprime na Brother
3. printRotulos(reqNumber) → GS imprime cada rótulo na Epson
Passo 6 — Conectar o CF Tunnel
O que: expor o endpoint do service para a VPS.
Por que: o server1 está em rede local. O CF tunnel é o bridge que permite a VPS alcançar o /api/print-order.
Ações:
6.1. O tunnel já existe (mencionado anteriormente). Adicionar rota para a porta do service (ex: 3100)
6.2. Na VPS, configurar o webhook de mudança de status:
POST https://{tunnel}.cfargotunnel.com/api/print-order
Content-Type: application/json
{
"orderId": "PV-12345",
"reqNumber": "123456"
}
(o service precisa de ambos: orderId para a fita, reqNumber para fichas e rótulos)
6.3. Testar com um pedido real em ambiente controlado
Passo 7 — Teste end-to-end e validação
O que: validar a cadeia completa com pedidos reais.
Ações:
7.1. Criar um pedido de teste no ERP, gerar rótulos na VPS, garantir que os PDFs estão em G:\REQ\{numReq}\
7.2. Simular o webhook manualmente:
Invoke-RestMethod -Method POST -Uri "http://localhost:3100/api/print-order" `
-ContentType "application/json" `
-Body '{"orderId":"PV-TEST","reqNumber":"999999"}'7.3. Verificar:
- Fita saiu na MP4200
- PDF da ficha apareceu em
G:\REQ\999999\ficha-999999.pdf - Ficha impressa na Brother A4
- Cada rótulo impresso na Epson com altura correta (conferir corte)
- Log no SQLite registra todos os jobs
7.4. Testar cenários de falha:
- Pasta não existe no Drive
- ERP travado (PAD timeout)
- Impressora offline
- PDF corrompido
7.5. Consultar logs: GET http://localhost:3100/api/print-log/999999
Passo 8 — Produção
O que: colocar pra rodar permanentemente.
Ações:
8.1. Instalar o service como serviço Windows via node-windows ou NSSM:
npm install -g node-windows
# ou baixar NSSM e registrar:
nssm install PrintService "C:\Program Files\nodejs\node.exe" "C:\PrintService\print-service.js"
nssm set PrintService AppDirectory "C:\PrintService"
nssm start PrintService8.2. Configurar restart automático em caso de crash
8.3. Configurar o PAD para rodar como serviço ou ser chamável via CLI pelo service
8.4. Monitoramento: o endpoint GET /api/print-log serve como health check
- Opcional: VPS consulta periodicamente para verificar se jobs estão sendo processados
Resumo da arquitetura final
VPS (nuvem)
│
evento "pago → ag.impressão"
+ rótulos já salvos em G:\REQ\{req}\ via Drive
│
CF Tunnel (HTTPS)
│
▼
┌─────────────────────┐
│ Print Service │
│ (Node + Express) │
│ porta 3100 │
└────────┬────────────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
1. FITA 2. FICHAS 3. RÓTULOS
MP4200 Brother A4 Epson C6000AU
ESC/POS PAD→PDF→GS GS (altura var.)
(rede) (rede) (rede)
│
PAD abre ERP Delphi
UI automation
salva PDF em G:\REQ\{req}\
imprime na Brother
G:\REQ\{numReq}\
├── ficha-{numReq}.pdf ← gerado pelo PAD (server1)
├── {numReq}-a.pdf ← rótulo produto A (gerado pela VPS)
├── {numReq}-b.pdf ← rótulo produto B (gerado pela VPS)
└── {numReq}-c.pdf ← rótulo produto C (gerado pela VPS)
SQLite (C:\PrintService\print-log.db)
print_jobs:
order_id | job_type | file_name | status | detail | paper_height_mm
PV-123 | fita | NULL | ok | NULL | NULL
PV-123 | fichas | ficha-456789.pdf | ok | NULL | NULL
PV-123 | rotulos | 456789-a.pdf | ok | conteúdo: 22.3mm → papel: 25mm| 25
PV-123 | rotulos | 456789-b.pdf | ok | conteúdo: 38.1mm → papel: 40mm| 40
Relação com processos e automações
- contexto-print-service-neofarma — documento de contexto completo (arquitetura, decisões, regras de domínio)
- automacao-req-pad-erp — detalhamento do fluxo PAD isolado
- geracao-req-producao — processo de negócio que gera a REQ
- conferencia-automatica-ag-impressao — conferência automática que gera o JSON consolidado e os PDFs de rótulo
- conferencia-producao — conferências que consomem os documentos impressos