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 #
| Methode | Pfad | Zweck | Scope |
|---|---|---|---|
| GET | /api/v1/cash-registers | Kassen mit Zustand (offener Bon, Kassierer, hasTse) | cash:read |
| GET | /api/v1/cashiers | Kassierer für die Anmelde-Auswahl (hasPin; die PIN selbst nie) | cash:read |
| GET | /api/v1/cash-payment-methods | Buchbare Zahlarten (method, label, isCash) | cash:read |
| POST | /api/v1/cash-registers/{id}/actions/login | Kassierer anmelden, eröffnet die Abrechnung ({ cashierId, pin? }) | cash:write |
| POST | /api/v1/cash-registers/{id}/actions/logout | Abmelden ({ parkReceipt? }) | cash:write |
| POST | /api/v1/cash-registers/{id}/actions/deposit | Kasseneinlage ({ cashierId, amount, note? }) | cash:write |
| POST | /api/v1/cash-registers/{id}/actions/withdraw | Entnahme ({ cashierId, amount, note } - Grund ist Pflicht) | cash:write |
| POST | /api/v1/cash-registers/{id}/actions/close | Z-Abschluss, optional { counted: [{ method, amount }] } | cash:write |
| POST | /api/v1/cash-registers/{id}/receipts | Bon eröffnen bzw. offenen Bon liefern ({ cashierId, customerId? }) | cash:write |
| POST | /api/v1/cash-receipts/{id}/items | Position ({ articleId, quantity?, price?, discount?, text? }) | cash:write |
| DELETE | /api/v1/cash-receipts/{id}/items/{itemId} | Position stornieren | cash:write |
| POST | /api/v1/cash-receipts/{id}/actions/set-customer | Kunde zuordnen ({ customerId }, zieht Preisliste) | cash:write |
| POST | /api/v1/cash-receipts/{id}/actions/pay | Kassieren (siehe unten) | cash:write |
| POST | /api/v1/cash-receipts/{id}/actions/cancel | Storno-Bon (Retoure) zu einem kassierten Beleg | cash:write |
| DELETE | /api/v1/cash-receipts/{id} | Offenen Bon verwerfen | cash:write |
| GET | /api/v1/cash-receipts | Bons (Filter z.B. ?number=, ?closingId=, ?registerId=) | cash:read |
| GET | /api/v1/cash-receipts/{id}/items | Bon-Positionen | cash:read |
| GET | /api/v1/cash-receipts/{id}/tse | TSE-Signaturdaten inkl. QR-String (Nachdruck) | cash:read |
| GET | /api/v1/cash-closings | Kassenabschlü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 #
| Code | Status | Bedeutung |
|---|---|---|
CASH_RECEIPT_COMPLETED | 409 | Bon ist kassiert und festgeschrieben - nur per Storno-Bon (actions/cancel) ausgleichbar |
CASH_PIN_REQUIRED | 403 | Der Kassierer ist PIN-geschützt; pin im Body übergeben |
CASH_ACTION_FAILED | 409 | Die Kassen-SP hat abgelehnt; detail trägt den EULANDA-Text (z.B. offene Bons beim Z-Abschluss) |
VALIDATION_LOOKUP_INVALID | 422 | Unbekannte Zahlart bzw. unbekannter Kunde/Artikel |
VALIDATION_FIELD_INVALID | 422 | Pflichtfeld fehlt (z.B. note bei einer Entnahme) |
Tagesumsätze liefert /statistics/revenue mit source=cash.