Skip to content

Vouchery

Ověřený fakt — odpovídá modulu 4. Voucher systém v zadání. Dárkové vouchery na konkrétní službu, uplatnitelné přes QR kód / URL.

Skládá se ze dvou částí:

  • Admin správa/VoucherManagement (src/pages/VoucherManagement.jsx)
  • Veřejné uplatnění/redeem?code=… (src/pages/VoucherRedeem.jsx)

Data: entita Voucher.

Admin: správa voucherů

Cesta: /VoucherManagement · Role: Admin

  • Seznam voucherů (Voucher.list('-created_date', 200)) s filtrem podle stavu: active / redeemed / expired / cancelled (u každého počet).
  • Vytvoření (prodej) voucheru — výběr služby, kupující klient, způsob platby a volitelně platnost a poznámka. Kód se generuje lokálně (12 znaků bez matoucích znaků, formát XXXX-XXXX-XXXX).
  • V seznamu je nově vidět cena a způsob platby voucheru.
  • QR kód — generován v prohlížeči knihovnou qrcode z URL "{origin}/redeem?code=…", s možností stažení.
  • Kopírování odkazu a otevření odkazu.
  • Zrušení aktivního voucheru → status: 'cancelled'.

Změna logiky (export 19. 8. 2026): voucher se stal prodejní položkou, dřív šlo jen o administrativní vystavení. Prodej řeší backendová akce purchase_voucher (funkce clientBooking):

  • na voucher se uloží kupující (buyer_client_id), cena podle výkonu, sazba DPH a purchase_id
  • vystaví se daňový doklad (generatePurchaseDocument) a nastaví invoice_id / invoice_number
  • způsob platby campaign znamená kampaňový voucher — bez dokladu, účtuje se jako marketingový náklad
  • na výkon lze voucher vystavit jen s příznakem Service.voucher_enabled

Odvozeno — snapshot service_name se ukládá na voucher, aby šlo zobrazit název služby bez join dotazu.

Veřejné: uplatnění voucheru

Cesta: /redeem?code=… · Role: Veřejné (bez layoutu)

Stavový automat (state): loading → valid / invalid / expired / redeemed → onboarding / booking → done.

flowchart TD
    A[Načtení voucheru dle code] --> B{Stav voucheru}
    B -- nenalezen / cancelled --> INV[Voucher nenalezen]
    B -- redeemed --> RED[Již uplatněn]
    B -- expired / po platnosti --> EXP[Expiroval]
    B -- active --> V[Zobrazit voucher + přihlášení]
    V --> L{Klient přihlášen?}
    L -- ne --> OTP[OtpLoginForm]
    L -- ano, profil nekompletní --> ONB[ClientOnboarding]
    L -- ano, profil OK --> BOOK[ClientBooking pro službu voucheru]
    OTP --> ONB
    ONB --> BOOK
    BOOK --> DONE[Voucher označen redeemed + rezervace potvrzena]

Kontrola platnosti probíhá i na frontendu: pokud valid_until < dnes, voucher je považován za expirovaný.

Po dokončení rezervace (ClientBooking, předvyplněná služba z voucheru) se voucher označí: status: 'redeemed', redeemed_by_client_id, redeemed_by_client_name, booking_id, redeemed_at.

Nezjištěno / riziko: uplatnění voucheru se zapisuje čistě z prohlížeče klienta (Voucher.update), bez serverové kontroly, že voucher mezitím nebyl uplatněn jinde. Race condition při souběžném uplatnění stejného kódu není ošetřen na backendu.

Související