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
qrcodez 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 apurchase_id - vystaví se daňový doklad (
generatePurchaseDocument) a nastavíinvoice_id/invoice_number - způsob platby
campaignznamená 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í
- On-boarding klienta — vkládá se před rezervaci u nekompletního profilu
- Entita
Voucher