Kasse (POS)
Zuletzt geändert: 18.07.2026 19:22

Kasse #

Die Kasse ist voll fernsteuerbar: Lesen über MASTER_KasseBeleg/ MASTER_KasseBelegZeile (Scope cash:read), Schreiben über die originalen Delphi-SP-Ketten (cn_Cash*, Scope cash:write) - exakt dieselben Abläufe wie in der EULANDA-Kasse und in EulandaXpos. Die API implementiert keine eigene Kassenlogik: Preisfindung, Pfand-/ Extra-Artikel, Lagerbuchung, Festschreibung und TSE-Protokollierung macht die Datenbank.

Das Kassenmodell in drei Sätzen #

Pro Kasse gibt es genau einen offenen Bon (Lazy-Modell): POST /receipts liefert den offenen Bon zurück (reused: true), statt einen zweiten zu eröffnen. Ein kassierter Bon ist festgeschrieben - danach hilft nur ein Storno-Bon, kein Ändern (409 CASH_RECEIPT_COMPLETED). Am Tagesende gehört genau ein Z-Abschluss (actions/close), am Tagesanfang typischerweise eine Kasseneinlage (actions/deposit, Wechselgeld).

Endpunkte #

MethodePfadZweckScope
GET/api/v1/cash-registersKassen mit Zustand (offener Bon, Kassierer, hasTse)cash:read
GET/api/v1/cashiersKassierer für die Anmelde-Auswahl (hasPin; die PIN selbst nie)cash:read
GET/api/v1/cash-payment-methodsBuchbare Zahlarten (method, label, isCash)cash:read
POST/api/v1/cash-registers/{id}/actions/loginKassierer anmelden, eröffnet die Abrechnung ({ cashierId, pin? })cash:write
POST/api/v1/cash-registers/{id}/actions/logoutAbmelden ({ parkReceipt? })cash:write
POST/api/v1/cash-registers/{id}/actions/depositKasseneinlage ({ cashierId, amount, note? })cash:write
POST/api/v1/cash-registers/{id}/actions/withdrawEntnahme ({ cashierId, amount, note } - Grund ist Pflicht)cash:write
POST/api/v1/cash-registers/{id}/actions/closeZ-Abschluss, optional { counted: [{ method, amount }] }cash:write
POST/api/v1/cash-registers/{id}/receiptsBon eröffnen bzw. offenen Bon liefern ({ cashierId, customerId? })cash:write
POST/api/v1/cash-receipts/{id}/itemsPosition ({ articleId, quantity?, price?, discount?, text? })cash:write
DELETE/api/v1/cash-receipts/{id}/items/{itemId}Position stornierencash:write
POST/api/v1/cash-receipts/{id}/actions/set-customerKunde zuordnen ({ customerId }, zieht Preisliste)cash:write
POST/api/v1/cash-receipts/{id}/actions/payKassieren (siehe unten)cash:write
POST/api/v1/cash-receipts/{id}/actions/cancelStorno-Bon (Retoure) zu einem kassierten Belegcash:write
DELETE/api/v1/cash-receipts/{id}Offenen Bon verwerfencash:write
GET/api/v1/cash-receiptsBons (Filter z.B. ?number=, ?closingId=, ?registerId=)cash:read
GET/api/v1/cash-receipts/{id}/itemsBon-Positionencash:read
GET/api/v1/cash-receipts/{id}/tseTSE-Signaturdaten inkl. QR-String (Nachdruck)cash:read
GET/api/v1/cash-closingsKassenabschlüsse (Z-Berichte; isClosed = Status >= 20)cash:read

Ohne price greift die Kassen-Preisfindung (cn_Preis_GetVK inklusive Kundenpreisliste nach set-customer); discount ist Prozent.

Kassieren (actions/pay) #

Body: ein bis zwei Zahlungen plus optionales Rückgeld und Bemerkung. Die gültigen Zahlarten liefert GET /cash-payment-methods (Katalog konKasseZahlart mit Anzeigenamen aus der EULANDA-Konfiguration; isCash steuert die Rückgeld-Logik - unbar zahlt exakt):

{
  "payments": [ { "method": "BAR", "amount": 50.00 } ],
  "change":   { "amount": 9.05 },
  "note":     "optional"
}

Die Antwort enthält Beleg-Summen (grossTotal, paidAmount, balance) und completedAt. Kassieren bucht das Lager ab und schreibt den Beleg fest.

Storno-Bon (actions/cancel) #

Kassierte Belege lassen sich nicht ändern - der Ausgleich ist ein Storno-Bon: POST /cash-receipts/{id}/actions/cancel erzeugt über cn_CashBelegStorno einen neuen Bon mit negierten Positionen und Zahlungen, bucht das Lager zurück und schließt sofort ab (TSE inklusive). Original und Storno verweisen aufeinander (StornoId bzw. BasisBelegId in den Lesedaten); ein zweiter Versuch liefert 409. Die Kasse darf dabei keinen Bon in Erfassung haben.

Kassierer-PIN #

Ist am Kassierer eine PIN hinterlegt (hasPin in /cashiers), verlangen actions/login und der Z-Abschluss das Feld pin im Body - sonst 403 CASH_PIN_REQUIRED. Die PIN wird nur geprüft, nie ausgeliefert.

TSE (Technische Sicherheitseinrichtung) #

Die TSE steckt am SQL-Server; dort läuft der TSE-Client (Delphi, EulTseConn.exe), der ausschließlich über SQL-Tabellen kommuniziert und jede Transaktion inklusive Stornos signiert. Die REST-API redet nie direkt mit der TSE - die cn_Cash*-SPs reihen Start und Abschluss der TSE-Transaktion selbst ein, sobald die Kasse eine TSE hat (hasTse in /cash-registers).

actions/pay wartet auf einer TSE-Kasse bis zu 30 Sekunden auf die Signatur und liefert sie im Feld tse mit: Transaktionsnummer, Signaturzähler, Zeitstempel und qrData - der fertige BSI-V0-String für den Prüf-QR auf dem Bon. Fällt der TSE-Client aus, blockiert das Kassieren nicht (Verhalten wie EULANDA.exe); der Bon trägt dann errorMessage und muss mit dem gesetzlichen Fehlerhinweis gedruckt werden. Für den Bon-Nachdruck liefert GET /cash-receipts/{id}/tse dieselben Daten jederzeit erneut.

Z-Abschluss mit Zählung #

actions/close ohne Body schließt die Abrechnung direkt ab. Mit counted übergibt der Client die gezählten Endbestände je Zahlart; der Server stellt sie dem Soll aus der Abrechnung gegenüber (cnf_CashAbrechnung_Zahlart), bucht Differenzen als FEHL-/ UEBERBESTAND (cn_CashBelegKorrektur) und liefert die Gegenüberstellung zurück:

{ "counted": [ { "method": "BAR", "amount": 843.80 } ] }
{
  "id": 1, "closingReceiptId": 22,
  "differences": [ { "method": "BAR", "currency": "EUR",
    "expected": 848.80, "counted": 843.80, "difference": -5.00 } ]
}

Beispiel: kompletter Bon #

PowerShell:

$h = @{ Authorization = "Bearer $key" }
$base = 'http://localhost:8098/api/v1'

# Einlage am Tagesanfang (eröffnet die Abrechnung automatisch)
Invoke-RestMethod -Method Post -Uri "$base/cash-registers/1/actions/deposit" -Headers $h `
    -ContentType 'application/json' -Body '{"cashierId":1,"amount":100,"note":"Wechselgeld"}'

# Bon, Position, kassieren
$bon = Invoke-RestMethod -Method Post -Uri "$base/cash-registers/1/receipts" -Headers $h `
    -ContentType 'application/json' -Body '{"cashierId":1}'
Invoke-RestMethod -Method Post -Uri "$base/cash-receipts/$($bon.id)/items" -Headers $h `
    -ContentType 'application/json' -Body '{"articleId":4,"quantity":2}'
Invoke-RestMethod -Method Post -Uri "$base/cash-receipts/$($bon.id)/actions/pay" -Headers $h `
    -ContentType 'application/json' -Body '{"payments":[{"method":"BAR","amount":309.40}]}'

# Z-Abschluss am Tagesende
Invoke-RestMethod -Method Post -Uri "$base/cash-registers/1/actions/close" -Headers $h `
    -ContentType 'application/json' -Body '{"counted":[{"method":"BAR","amount":389.40}]}'

curl:

curl.exe -s -X POST -H "Authorization: Bearer %KEY%" -H "Content-Type: application/json" ^
  -d "{\"cashierId\":1,\"amount\":100,\"note\":\"Wechselgeld\"}" ^
  http://localhost:8098/api/v1/cash-registers/1/actions/deposit
curl.exe -s -X POST -H "Authorization: Bearer %KEY%" -H "Content-Type: application/json" ^
  -d "{\"cashierId\":1}" http://localhost:8098/api/v1/cash-registers/1/receipts
curl.exe -s -X POST -H "Authorization: Bearer %KEY%" -H "Content-Type: application/json" ^
  -d "{\"articleId\":4,\"quantity\":2}" http://localhost:8098/api/v1/cash-receipts/21/items
curl.exe -s -X POST -H "Authorization: Bearer %KEY%" -H "Content-Type: application/json" ^
  -d "{\"payments\":[{\"method\":\"BAR\",\"amount\":309.40}]}" ^
  http://localhost:8098/api/v1/cash-receipts/21/actions/pay
curl.exe -s -X POST -H "Authorization: Bearer %KEY%" -H "Content-Type: application/json" ^
  -d "{\"counted\":[{\"method\":\"BAR\",\"amount\":389.40}]}" ^
  http://localhost:8098/api/v1/cash-registers/1/actions/close

Fehlercodes #

CodeStatusBedeutung
CASH_RECEIPT_COMPLETED409Bon ist kassiert und festgeschrieben - nur per Storno-Bon (actions/cancel) ausgleichbar
CASH_PIN_REQUIRED403Der Kassierer ist PIN-geschützt; pin im Body übergeben
CASH_ACTION_FAILED409Die Kassen-SP hat abgelehnt; detail trägt den EULANDA-Text (z.B. offene Bons beim Z-Abschluss)
VALIDATION_LOOKUP_INVALID422Unbekannte Zahlart bzw. unbekannter Kunde/Artikel
VALIDATION_FIELD_INVALID422Pflichtfeld fehlt (z.B. note bei einer Entnahme)

Tagesumsätze liefert /statistics/revenue mit source=cash.