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

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, PortName

Anotar 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 DESC

2.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:\REQ
  • port: 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 PrintService

8.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