Kihagyás
v26.3

Diszkó-API (készpénzmentes klub)

A Disco modul egy teljesen készpénzmentesen működő klub-/diszkóüzemet képez le: minden vendég a beléptetéskor kap egy kártyát (chip/RFID/NFC), amely a teljes ott-tartózkodás alatt számlaként szolgál. Mindent — belépőt, italokat, ruhatárat, asztali rendeléseket — a kártyára könyvelnek, és csak távozáskor (vagy előre fizetett feltöltéssel) számolnak el. Az API egy kártya teljes életciklusát lefedi: beléptetés → könyvelés → feltöltés → elszámolás → távozás.

Minden végpont a közös api/v1/disco előtag alatt található (három vezérlő osztozik ezen az előtagon: vendégek/kártyák, statisztikák, konfiguráció — ezen felül a forgókapu-hardver API a api/v1/disco/turnstile alatt). Mint minden területen: JWT-Bearer, válaszboríték { "success": true, "data": { ... } }, camelCase mezők, átlátszatlan azonosítók.

A Disco modult igényli

A Disco modult igényli; aktív modullicenc nélkül a végpontok HTTP 403 választ adnak (errorCode: "MODULE_NOT_LICENSED").

Kártyaazonosító és belső azonosító

Minden adatrekordnak van egy belső id értéke (adatbázis-kulcs) és egy guestId értéke — ez a kártyaszám, amellyel a chip fizikailag fel van címkézve. Minden {cardId} útvonal a guestId értéket várja, nem a belső id értéket.

Vendégek és kártyák

DiscoController — alap: api/v1/disco:

Metódus Végpont Cél
POST /enter Vendég beléptetése a forgókapunál (kártyabeolvasás a bejáratnál)
POST /leave Vendég kiléptetése fizetéssel (beolvasás a kijáratnál)
POST /leave-combined Több kártya („pár") közös elszámolása és lezárása egy bizonylaton
POST /book Termék könyvelése a kártyára (bár/pult)
POST /book/batch Több termék könyvelése egyszerre a kártyára
POST /book/{guestId}/{bonIndex}/void Könyvelt tétel sztornózása
POST /table/book-on-card Asztali rendelés könyvelése a kártyára (kiváltja az asztali fizetést)
POST /wardrobe/return/{guestId}/{bonIndex} Ruhatári kiadás (eltávolítja a #számot, az összeg megmarad)
GET /guest/{cardId} Vendég/kártya az aktuális forgalommal együtt (csak nyitott kártyák)
GET /guest/{cardId}/status Kártyaállapot, letiltott/sztornózott/elszámolt kártyák esetén is
GET /guest/{cardId}/checkout Elszámolási nézet: nyitott összeg, előre fizetett fedezet, távozhat-e
GET /guests Minden jelenleg jelen lévő (beléptetett) vendég
POST /guest/{cardId}/topup Előre fizetett egyenleg feltöltése
POST /guest/{cardId}/transfer Kártya/egyenleg átvitele másik kártyára (kártyacsere)
POST /guest/{cardId}/lock Kártya letiltása (további könyvelés nem lehetséges)
POST /guest/{cardId}/unlock Kártya feloldása
POST /guest/{cardId}/storno Teljes kártya sztornózása + nem fizetőként való megjelölése
POST /guest/{cardId}/collect-storno Sztornózott kártya utólagos beszedése a pénztárnál
POST /guest/{cardId}/image Kártyafotó rögzítése
POST /terminal/pay Önkiszolgáló bankkártyás fizetés indítása az információs terminálon
POST /personal/{cardId}/close Egy személyzeti kártya elszámolása (saját fogyasztás)
POST /personal/close-all Minden nyitott személyzeti kártya elszámolása saját fogyasztásként
POST /close-all Minden nyitott kártya lezárása (üzemzárás/napi zárás)

Beléptetés opcionális egyenlegfeltöltéssel

A POST /api/v1/disco/enter egy névtelen vendégkártya esetén a belépőt és a kártya további előre fizetett egyenlegét egyetlen folyamatban szedheti be. A topUpAmount opcionális, alapértéke 0.

POST /api/v1/disco/enter
Authorization: Bearer eyJ...
Content-Type: application/json

{
  "guestId": "crd_10042",
  "gender": 1,
  "enterGroupId": "eg_std",
  "workplaceId": "wp_entry",
  "topUpAmount": 20.00,
  "payments": [
    { "method": "Cash", "amount": 30.00 }
  ]
}

A fizetéseknek együtt kell fedezniük a belépőt és a feltöltést. Egy bizonylat keletkezik, külön tételekkel: a belépő normál adózású forgalomként, a feltöltés 0 %-os kötelezettségként. Az uploadAccount csak a bizonylat sikeres könyvelése után nő. Megengedett érték 0 és 10000 között; ügyfél- és személyzeti kártyák esetén a beléptetéskori feltöltést a rendszer elutasítja:

Hibakód Jelentés
DISCO_ENTRY_TOPUP_CUSTOMER_NOT_ALLOWED Az ügyfélkártyákat az ügyfélszámlán keresztül kell feltölteni
DISCO_ENTRY_TOPUP_PERSONAL_NOT_ALLOWED A személyzeti kártyák beléptetéskor nem kapnak névtelen kártyaegyenleget

Könyvelés a kártyára

A POST /api/v1/disco/book egy terméket könyvel egy nyitott kártyára (bár/pult értékesítés fizetés nélkül — a fizetés csak a leave/checkout során történik).

POST /api/v1/disco/book
Authorization: Bearer eyJ...
Content-Type: application/json

{
  "guestId": "crd_10042",
  "articleId": "art_cola",
  "articleName": "Cola 0,3l",
  "count": 2,
  "price": 3.50,
  "workplaceId": "wp_bar1",
  "workplaceName": "Bar 1"
}

Válasz (200 OK, DiscoGuestResponse):

{
  "success": true,
  "data": {
    "id": "dgu_9f8e7d",
    "guestId": "crd_10042",
    "gender": 1,
    "enterDate": "2026-07-12T22:05:00Z",
    "amount": 7.00,
    "payedAmount": 0,
    "uploadAccount": 20.00,
    "iou": 0,
    "isOpen": true,
    "isLocked": false,
    "openBons": [
      { "articleId": "art_cola", "articleName": "Cola 0,3l", "price": 3.50, "count": 2, "total": 7.00,
        "workplaceId": "wp_bar1", "workplaceName": "Bar 1", "bookDate": "2026-07-12T23:10:00Z", "isVoided": false }
    ]
  }
}

Kártyaállapot

A GET /api/v1/disco/guest/{cardId}/status az állapotot attól függetlenül adja vissza, hogy a kártya még nyitott-e — ellentétben a GET /guest/{cardId} hívással, amely csak nyitott kártyákat talál. Így a bár, a ruhatár és a pénztár a letiltott vagy sztornózott kártyákat is felismeri.

GET /api/v1/disco/guest/crd_10042/status
Authorization: Bearer eyJ...
{
  "success": true,
  "data": {
    "found": true,
    "isOpen": true,
    "isLocked": false,
    "stornoed": false,
    "guest": { "id": "dgu_9f8e7d", "guestId": "crd_10042", "amount": 7.00, "uploadAccount": 20.00 }
  }
}

Aktuális forgalom

A GET /api/v1/disco/guest/{cardId} ugyanazt a DiscoGuestResponse választ adja, mint fent, a folyamatban lévő nyitott könyvelésekkel (openBons) és az előre fizetett egyenleggel (uploadAccount) együtt. A válasz 404 GUEST_NOT_FOUND, ha a kártya nem (már nem) nyitott.

Elszámolás/checkout

A GET /api/v1/disco/guest/{cardId}/checkout semmit sem módosít — puszta előnézet arról, mennyivel tartozik még a kártya, mennyit fedez az előre fizetett egyenleg, és automatikusan távozhat-e (önkiszolgáló checkout, mobil kijárati pénztár, forgókapu).

{
  "success": true,
  "data": {
    "cardId": "crd_10042",
    "found": true,
    "isOpen": true,
    "isLocked": false,
    "consumption": 7.00,
    "netOwed": 7.00,
    "prepaid": 20.00,
    "amountToPay": 0,
    "refund": 13.00,
    "canAutoSettle": true,
    "status": "ok",
    "message": "Karte kann raus — Restguthaben 13.00 €"
  }
}

A status értéke a következők egyike: ok | pay | locked | closed | notfound.

Ha egy névtelen kártyán több a prepaid, mint a netOwed, a többletet a kiszolgáló pénztár készpénzben kifizeti. A lezáró kérés ehhez payoutExcess: true értéket ad meg; a fizetési lista üres lehet:

{
  "cardId": "crd_10042",
  "payments": [],
  "payoutExcess": true
}

Beállított kifizetési módban a háttérrendszer ezen jelölés nélkül DISCO_PAYOUT_REQUIRED hibával megakadályozza a normál lezárást. A kártya ekkor nyitva marad, és nem keletkezik bizonylat. A bankkártyás kifizetés nem része a Disco-checkoutnak; a payoutExcess egy készpénzes kifizetést dokumentál és könyvel a feltöltés ellenkönyveléseként.

Asztal a kártyára

A POST /api/v1/disco/table/book-on-card egy asztal nyitott tételeit egy Disco-kártyára helyezi át (Disco-asztalmód) — kiváltja az asztalnál történő készpénzes/bankkártyás fizetést, a kártyalimitek továbbra is érvényesek, az asztal felszabadul.

{ "tableId": "tbl_12", "cardId": "crd_10042", "openBonIds": null }

Az openBonIds: null minden nyitott tételt átvisz; egy lista csak egy „azonnali" kört visz át.

Feltöltés

A POST /api/v1/disco/guest/{cardId}/topup feltölti a kártya előre fizetett egyenlegét (uploadAccount) — az összeg szabadon választható, a maradék egyenleget checkoutkor visszatérítik.

{ "amount": 20.00, "paymentMethod": "Bar" }

A válasz ismét a frissített DiscoGuestResponse (megnövelt uploadAccount).

Összetéveszthető: Disco-kártyaegyenleg és ügyfélszámla-egyenleg

Ez itt egyetlen Disco-látogatás kártyás előre fizetett egyenlege (távozáskor lejár/visszatérül). Az állandó ügyfélszámla-egyenleg önálló fogalom — lásd Ügyfélszámla: egyenleg és pontok.

Statisztikák

DiscoStatsController — alap: api/v1/disco:

Metódus Végpont Cél
GET /stats Élő mutatók (forgalom, jelen lévő vendégek, kihasználtság csoport/munkaállomás/személyzet/óra szerint)
GET /search Kártyák/vendégek keresése (kártyaszám, név, időszak, nem, sztornószűrő, lapozás)
GET /personal/{ownerId}/activity Egy személyzeti kártya tevékenysége (fogyasztás, sztornó, törés)
GET /daylog/{date} Napi napló egy adott napra

Példa: GET /api/v1/disco/stats

{
  "success": true,
  "data": {
    "totalGuests": 214,
    "currentGuests": 87,
    "totalMale": 96,
    "totalFemale": 118,
    "totalRevenue": 4211.50,
    "averageSpend": 19.68,
    "byGroup": [ { "groupId": "eg_std", "groupName": "Standard", "count": 180, "revenue": 3500.00 } ],
    "byWorkplace": [ { "workplaceId": "wp_bar1", "workplaceName": "Bar 1", "count": 400, "revenue": 2200.00 } ],
    "byStaff": [ { "personalId": "per_1", "personalName": "Mo", "count": 120, "revenue": 900.00 } ],
    "byHour": [ { "hour": 23, "count": 60, "revenue": 800.00 } ]
  }
}

A GET /search többek között a következőket támogatja: cardId, name, dateFrom/dateTo, gender, timeFrom/timeTo, workplaceId, enterGroupId, wardrobe, stornoed, offset/limit, excludeImages.

Konfiguráció

DiscoConfigController — alap: api/v1/disco, klasszikus CRUD a belépő-/árcsoportokhoz („Entergroups" — ezek határozzák meg pl. a belépő árát, a kártyalimitet, az ingyenes italokat, a minimális fogyasztást):

Metódus Végpont Cél
GET /entergroups Minden belépőcsoport
GET /entergroups/{id} Egy belépőcsoport
POST /entergroups Belépőcsoport létrehozása
PUT /entergroups/{id} Belépőcsoport frissítése
DELETE /entergroups/{id} Belépőcsoport törlése

Forgókapu-hardver

api/v1/disco/turnstile/{cardId} (GET/POST) az X-Dikas-Turnstile-Key fejléccel — ez az ajánlott mód. A régebbi api/v1/disco/turnstile/{key}/{cardId} út (kulcs az útvonalban) továbbra is működik; ha egy eszköz mindkettőt küldi, a fejléc érvényes.

Nincs JWT — a hitelesítés API-kulccsal történik, hogy egy egyszerű forgókapu-vezérlő bejelentkezési folyamat nélkül is elérhesse az útvonalat. 200 = a kártya távozhat, minden más kód okot hordoz (letiltva/nyitott/már elszámolva/ismeretlen/hibás kulcs).

→ Részletek, beállítás és állapotkód-táblázat: Disco-forgókapu.

Teljes folyamat az elejétől a végéig

Egy tipikus vendégút, minden lépés a crd_10042 kártyára:

TOKEN="eyJ..."
API="https://ihre-instanz.dikas.de/api/v1"

# 1. Einlass am Drehkreuz (Kartenscan Eingang), hier mit 20 € Guthaben
curl -X POST "$API/disco/enter" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "guestId": "crd_10042", "gender": 1, "enterGroupId": "eg_std", "topUpAmount": 20.00, "payments": [ { "method": "Cash", "amount": 30.00 } ] }'

# 2. Status prüfen (z. B. Wiedereinlass nach kurzem Verlassen)
curl "$API/disco/guest/crd_10042/status" -H "Authorization: Bearer $TOKEN"

# 3. Prepaid aufladen
curl -X POST "$API/disco/guest/crd_10042/topup" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "amount": 20.00, "paymentMethod": "Bar" }'

# 4. An der Bar buchen (einzeln oder als Batch)
curl -X POST "$API/disco/book" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "guestId": "crd_10042", "articleId": "art_cola", "articleName": "Cola 0,3l", "count": 2, "price": 3.50 }'

# 5. Laufenden Umsatz abfragen
curl "$API/disco/guest/crd_10042" -H "Authorization: Bearer $TOKEN"

# 6. Tischbestellung auf die Karte buchen
curl -X POST "$API/disco/table/book-on-card" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "tableId": "tbl_12", "cardId": "crd_10042" }'

# 7. Vor dem Ausgang: Checkout-Vorschau
curl "$API/disco/guest/crd_10042/checkout" -H "Authorization: Bearer $TOKEN"

# 8. Verlassen mit Zahlung (falls noch etwas offen ist)
curl -X POST "$API/disco/leave" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "cardId": "crd_10042", "payments": [], "payoutExcess": true }'

A pénztárnál történő kezelést a Felhasználói kézikönyv Disco modulja írja le.

Üzemzavar — a kártyával a kijáratnál nem tudnak fizetni (nem fizető vendég):

# Karte stornieren: als Nichtzahler markieren, Verzehr als Schwund verbuchen
curl -X POST "$API/disco/guest/crd_10042/storno" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "reason": "Karte verloren" }'

# Später an der Kasse nachkassiert
curl -X POST "$API/disco/guest/crd_10042/collect-storno" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "payments": [ { "method": "Cash", "amount": 7.00 } ] }'