API HTTP

Driverul FisCool încorporează un server HTTP RESTful pentru operațiuni de dispozitiv și interogări de sistem. API-ul este stateless și ușor de integrat din orice limbaj care poate face cereri HTTP.

Configurare de Bază

  • URL implicit: http://127.0.0.1:10123
  • Host: implicit 127.0.0.1 (doar localhost); configurabil din setările FisCool (ex: 0.0.0.0 pentru a expune API-ul în LAN).
  • Port: implicit 10123, configurabil.
  • Activare: serverul API poate fi dezactivat complet din setări (implicit este activat).
  • Content-Type: application/json pentru toate cererile POST.
  • Autentificare: niciuna. CORS este deschis, dar vezi Accesul din browser mai jos.

Accesul din browser (verificarea Origin)

Cererile care au un header Origin (adică vin dintr-o pagină web) sunt filtrate: prima dată când un origin nou apelează API-ul, FisCool afișează operatorului un dialog de confirmare („Permiteți <origin> să interacționeze cu dispozitivele fiscale?"). Dacă este acceptat, originul este salvat în lista de permisiuni și nu se mai întreabă; dacă este respins, cererea eșuează cu HTTP 403. Cererile fără header Origin (curl, servicii backend) nu sunt filtrate.

Endpoint-uri de Sistem

GET /

Descriere: Verificare de funcționare (health check).

Răspuns: text simplu

FisCool API Server running.

GET /status

Descriere: Returnează snapshot-ul complet al stării aplicației: dispozitive fiscale, terminale POS, configurația API, configurația aplicației, imprimantele active, sumarul licențelor. Array-urile fiscal_devices și pos_terminals sunt identice cu endpoint-urile dedicate de mai jos.

GET /devices/fiscal

Descriere: Listează toate dispozitivele fiscale configurate.

Răspuns:

{
  "fiscal_devices": [
    {
      "config": {
        "id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",   // UUID de configurare - se folosește ca device_id
        "name": "Casa 1 - DATECS DP25",
        "manufacturer": "DATECS",
        "model": "DP25",
        "connection_details": {
          "type": "Serial",
          "details": { "port": "COM3", "baud_rate": 115200 }
        },
        "notes": "Casa principală",
        "enabled": true,
        "scan_folder": "C:\\FisCool\\INP",               // opțional: folderul monitorizat pentru INP
        "auto_send_card_to_pos": true,                    // funcții opționale
        "linked_pos_device_id": "b2c3d4e5-f6a7-5b8c-9d0e-1f2a3b4c5d6e",
        "is_system_device": false
      },
      "status": "Connected",             // Disconnected | Connecting | Connected | Disabled | {"Error": "..."}
      "status_text": "Conectat",
      "busy_external": false,            // true cât timp o aplicație externă ține dispozitivul ocupat
      "vat_rates": {                     // grupele de TVA citite de pe dispozitiv (sutimi de procent)
        "A": 1900, "B": 900, "E": 0
      },
      "connected_device_id": "DT123456", // SERIA HARDWARE - null cât timp e deconectat
      "connected_device_data": {
        "id": "DT123456",                // seria hardware (identică cu connected_device_id)
        "cif": "RO12345678",             // codul fiscal al firmei programat în dispozitiv
        "print_width": 32                // caractere pe linie ale imprimantei (poate lipsi)
      },
      "scheduled_vat_rate_changes": []   // programări schedule_vat_rate_change în așteptare
    }
  ]
}

Notă: connected_device_id/connected_device_data sunt populate doar cât timp dispozitivul este conectat; altfel sunt null. config.id este UUID-ul folosit pentru operațiuni - nu seria hardware.

GET /devices/pos

Descriere: Listează toate terminalele POS configurate. Dispozitivele fiscale hibride cu POS integrat apar și aici (marcate "is_virtual": true) după ce seria POS devine cunoscută.

Răspuns:

{
  "pos_terminals": [
    {
      "config": {
        "id": "b2c3d4e5-f6a7-5b8c-9d0e-1f2a3b4c5d6e",
        "name": "Terminal POS 1",
        "manufacturer": "PAX",
        "model": "A920 Pro",
        "bank": "BCR",
        "connection_details": {
          "type": "Network",
          "details": { "ip_address": "192.168.1.100", "port": 8080 }
        },
        "enabled": true
      },
      "status": "Connected",
      "status_text": "Conectat",
      "connected_device_id": "PAX001",
      "connected_device_data": { "id": "PAX001", "cif": null },
      "is_virtual": false
    }
  ]
}

Operațiuni Dispozitive

POST /devices/operation

Descriere: Execută o operațiune pe un dispozitiv fiscal sau pe un terminal POS. Este endpoint-ul principal pentru toate interacțiunile cu dispozitivele. Toate operațiunile și payload-urile lor sunt documentate pe pagina Operațiuni Dispozitive.

Headere de Cerere
HeaderObligatoriuDescriere
Content-TypeDaapplication/json
Idempotency-KeyNuCheie unică ce previne execuția duplicată (vezi Idempotență).
Structura Corpului Cererii
{
  "device_id": "UUID",         // UUID-ul dispozitivului țintă (config.id)
  "operation": {
    "type": "nume_operatiune",
    // ... parametri specifici operațiunii
  }
}
Exemple Complete
Exemplul 1: Tipărirea unui bon simplu
POST /devices/operation
Content-Type: application/json

{
  "device_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
  "operation": {
    "type": "print_receipt",
    "lines": [
      {
        "type": "item",
        "description": "Cafea Espresso",
        "quantity_thousandths": 1000,
        "unit_price_cents": 850,
        "vat": "19%",
        "um": "buc"
      },
      {
        "type": "text",
        "text": "Multumim pentru cumparaturi!"
      }
    ],
    "payments": [
      { "method": "cash", "amount_cents": 850 }
    ],
    "close_action": "close"
  }
}
Exemplul 2: Bon cu codul fiscal al cumpărătorului și plată cu cardul
POST /devices/operation
Content-Type: application/json

{
  "device_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
  "operation": {
    "type": "print_receipt",
    "lines": [
      {
        "type": "item",
        "description": "Servicii Consultanta IT",
        "quantity_thousandths": 1000,
        "unit_price_cents": 120000,
        "vat": "19%"
      }
    ],
    "payments": [
      { "method": "card", "amount_cents": 120000 }
    ],
    "buyer": { "vat_number": "RO12345678" },
    "flags": { "is_invoice": true },
    "close_action": "close"
  }
}
Exemplul 3: Tipărirea unui raport Z
{
  "device_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
  "operation": { "type": "print_z" }
}
Exemplul 4: Introducere numerar
{
  "device_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
  "operation": { "type": "deposit_cash", "amount": 5000 }
}
Exemplul 5: Vânzare POS
{
  "device_id": "b2c3d4e5-f6a7-5b8c-9d0e-1f2a3b4c5d6e",
  "operation": { "type": "sale", "amount": 2500, "suppress_print": false }
}
Exemplul 6: Raport din jurnalul electronic
{
  "device_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
  "operation": {
    "type": "je_report",
    "range": { "type": "date", "start": "2024-01-01", "end": "2024-01-31" },
    "doc_type": "fiscal_receipts",
    "output_mode": "read_line_by_line"
  }
}
Formatul Răspunsului
Succes (HTTP 200)
{
  "status": 0,
  "msg": "Receipt printed",
  "slip_number": 1234,
  "z_report_number": 45,
  "fiscal_receipt_count": 1050
}

Câmpurile exacte depind de operațiune - vezi Operațiuni Dispozitive.

Eroare (HTTP 4xx/5xx)

Orice eșec - validare, conexiune sau o eroare raportată de dispozitiv - este returnat cu un status HTTP non-2xx și acest corp JSON:

// HTTP 400 Bad Request - dispozitivul a raportat „lipsă hârtie" (cod 500)
{
  "status": 500,
  "error": "Lipsă hârtie în imprimantă."
}

// HTTP 404 Not Found - UUID de dispozitiv necunoscut
{
  "status": 404,
  "error": "Dispozitivul nu a fost găsit: a1b2c3d4-..."
}

// HTTP 400 Bad Request - vânzare POS refuzată, cu chitanța de refuz atașată
{
  "status": 600,
  "error": "Tranzacție POS refuzată.",
  "receipt": "...textul chitanței de refuz..."
}

Important: câmpul status din corp este codul de eroare al aplicației (întotdeauna un întreg pozitiv - vezi Coduri Eroare); nu este statusul HTTP. Folosiți status/error din corp pentru tratarea programatică și considerați orice răspuns HTTP non-200 un eșec.

Maparea Statusurilor HTTP
Status HTTPCând
200Operațiune executată cu succes (corpul are status: 0).
400Cerere/configurare invalidă (cod 400) sau o eroare operațională raportată de dispozitiv (majoritatea codurilor fiscale 5xx și POS 6xx).
403Origin respins de operator.
404Dispozitiv negăsit (cod 404).
408Timeout (cod 408).
501Operațiune nesuportată de acest dispozitiv (cod 1015).
503Dispozitiv inaccesibil/hardware indisponibil (cod 1004 și codurile fiscale de tip hardware, ex: 500, 502, 520…).
500Erori interne (coduri 1000-1003, …).
Idempotență

Pentru operațiuni critice (tipărire bonuri, vânzări POS) folosiți header-ul Idempotency-Key pentru a preveni execuția duplicată la reîncercări:

POST /devices/operation
Content-Type: application/json
Idempotency-Key: bon-2024-01-15-001

{ "device_id": "...", "operation": { ... } }

Toate cererile cu aceeași cheie partajează o singură execuție - duplicatele concurente și ulterioare primesc același rezultat fără a re-executa operațiunea.

Endpoint-uri de Tipărire

GET /printers

Descriere: Listează imprimantele de sistem activate în FisCool, cu detalii de driver.

Răspuns:

{
  "status": 0,
  "printers": [
    { "name": "POS-80", "paper_width_mm": 80, "dpi": 203, "render_width_px": 576 }
  ]
}

POST /printers/print_html

Descriere: Randează HTML și îl tipărește ca bitmap pe o imprimantă de sistem activată (pipeline pentru imprimante termice). Tipărirea către o imprimantă neactivată în FisCool returnează eroarea 400.

Corpul Cererii:

{
  "printer_name": "POS-80",
  "html": "<h1>Salut</h1><p>Comanda #42</p>"
}

Răspuns:

{
  "status": 0,
  "msg": "Print job completed"
}

Endpoint-uri de Administrare

Driverul poate fi administrat și programatic (salvare/ștergere dispozitive, interogarea jurnalului, actualizări) prin endpoint-urile /manage/* - vezi pagina API Headless.

Exemple de Integrare

cURL

# Tipărește un bon simplu
curl -X POST http://127.0.0.1:10123/devices/operation \
  -H "Content-Type: application/json" \
  -d '{
    "device_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
    "operation": {
      "type": "print_receipt",
      "lines": [{"type": "item", "description": "Test", "quantity_thousandths": 1000, "unit_price_cents": 100, "vat": "19%"}],
      "payments": [{"method": "cash", "amount_cents": 100}],
      "close_action": "close"
    }
  }'

# Listează dispozitivele fiscale
curl http://127.0.0.1:10123/devices/fiscal

JavaScript / Node.js

const axios = require('axios');

async function printReceipt(deviceId, items, payments) {
  try {
    const response = await axios.post('http://127.0.0.1:10123/devices/operation', {
      device_id: deviceId,
      operation: {
        type: 'print_receipt',
        lines: items,
        payments: payments,
        close_action: 'close'
      }
    });
    // HTTP 200 => succes
    console.log('Bon tipărit, număr:', response.data.slip_number);
  } catch (error) {
    // non-2xx => eșec; corpul conține {status, error}
    const body = error.response?.data;
    console.error('Eșec:', body?.status, body?.error);
  }
}

Python

import requests

def print_receipt(device_id, items, payments):
    url = "http://127.0.0.1:10123/devices/operation"
    payload = {
        "device_id": device_id,
        "operation": {
            "type": "print_receipt",
            "lines": items,
            "payments": payments,
            "close_action": "close",
        },
    }
    response = requests.post(url, json=payload)
    if response.ok:
        result = response.json()
        print(f"Bon tipărit: {result.get('slip_number')}")
    else:
        err = response.json()
        print(f"Eșec ({err.get('status')}): {err.get('error')}")

items = [{
    "type": "item",
    "description": "Cafea",
    "quantity_thousandths": 1000,
    "unit_price_cents": 250,
    "vat": "19%",
}]
payments = [{"method": "cash", "amount_cents": 250}]
print_receipt("a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d", items, payments)