Produkty¶
Produkty umožňují uživatelům nakupovat permanentky, balíčky vstupů, minutové kvóty a slevy. Každý produkt je vázán na konkrétní zóny.
Předpoklad
Produkty musí být povoleny konfigurací PRODUCTS_ENABLED = true.
Typy produktů¶
Denní permanentka (daily_pass)¶
Neomezený přístup po stanovenou dobu.
Parametry:
Chování:
- Po aktivaci platí
duration_daysdní (do 23:59:59 posledního dne) - Všechny relace v přiřazených zónách jsou zdarma
- Není třeba žádný kredit
Příklady:
| Název | Parametry | Popis |
|---|---|---|
| Denní vstup | {"duration_days": 1} |
Neomezený přístup na 1 den |
| Měsíční permanentka | {"duration_days": 30} |
30 dní neomezeného přístupu |
| Roční permanentka | {"duration_days": 365} |
Celoroční přístup |
Balíček vstupů (entry_bundle)¶
Předplacený počet vstupů. Každý vstup spotřebuje 1 vstup z balíčku.
Parametry:
Chování:
- Při každém zahájení relace se odečte 1 vstup
- Vstupy se odečítají až po úspěšném otevření zařízení
- Relace je zdarma dokud jsou vstupy k dispozici
- Po vyčerpání všech vstupů produkt vyprší
Příklady:
| Název | Parametry | Popis |
|---|---|---|
| Jednorázový vstup | {"entries": 1} |
1 vstup zdarma |
| 10 vstupů | {"entries": 10} |
Balíček 10 vstupů |
| 50 vstupů | {"entries": 50} |
Velký balíček |
Vstupní kvóta (entry_quota)¶
Limit počtu vstupů v klouzavém časovém okně (denně, týdně, měsíčně, ročně).
Parametry:
{
"daily_entries": 2,
"weekly_entries": 10,
"monthly_entries": 0,
"yearly_entries": 0,
"duration_days": 365
}
Chování:
- Počítá dokončené a aktivní relace v jednotlivých časových oknech
- Kvóty se automaticky obnovují jak čas plyne (klouzavé okno)
- Hodnota
0= bez limitu pro dané období - Pokud je jakýkoli limit překročen, produkt relaci nepokryje
- Platí po dobu
duration_daysod aktivace
Příklad: Kvóta 2 vstupy denně, 10 týdně. Uživatel může na posilovnu maximálně 2× denně a 10× týdně. Všechny tyto vstupy jsou zdarma.
Minutová kvóta (minute_quota)¶
Poskytuje volné minuty v klouzavém časovém okně. Pokud uživatel překročí kvótu, zaplatí poměrnou část.
Parametry:
{
"daily_minutes": 60,
"weekly_minutes": 300,
"monthly_minutes": 0,
"yearly_minutes": 0,
"duration_days": 365
}
Chování:
- Neomezuje zahájení relace (doba není předem známa)
- Při ukončení relace spočítá zbytek volných minut
- Pokud zbývající volné minuty >= doba relace → zdarma
- Pokud zbývající minuty < doba relace → poměrná sleva
- Hodnota
0= bez limitu pro dané období - Používá se nejpřísnější nenulový limit
Příklad výpočtu:
| Situace | Zbývající minuty | Doba relace | Cena relace | Výsledná cena |
|---|---|---|---|---|
| Plně pokryto | 60 min | 30 min | 50 Kč | 0 Kč |
| Částečně pokryto | 30 min | 60 min | 100 Kč | 50 Kč |
| Vyčerpáno | 0 min | 60 min | 100 Kč | 100 Kč |
Vzorec: výsledná_cena = cena × (1 - zbývající_minuty / doba_relace)
Procentuální sleva (percentage_discount)¶
Fixní procentuální sleva na všechny relace.
Parametry:
Chování:
- Snižuje cenu relace o daný počet procent
- U entry-based zón se sníží potřebný kredit při vstupu
- U time-based zón se sleva aplikuje při ukončení relace
- Pokud
duration_dayschybí nebo je0→ sleva je trvalá (nevyprší) - Sleva 100 % se chová jako permanentka
Příklady:
| Název | Parametry | Popis |
|---|---|---|
| VIP sleva | {"discount_percent": 20, "duration_days": 365} |
20 % sleva na rok |
| Zaměstnanecká sleva | {"discount_percent": 50} |
50 % trvale |
| Promoční akce | {"discount_percent": 30, "duration_days": 30} |
30 % na měsíc |
Kreditová kontrola
Na rozdíl od permanentky a balíčku vstupů, procentuální sleva neobchází kontrolu kreditu. Uživatel musí mít dostatek kreditu na zlevněnou cenu.
Věrnostní program na čas (playtime_loyalty)¶
Odměňuje uživatele volným časem na základě nahraných minut v časových zónách.
Parametry:
Chování:
- Sledování: Systém sleduje minuty strávené v přiřazených zónách (nebo všech, pokud
zonesje prázdné) - Práh: Když
current_played_minutesdosáhnethreshold_minutes, uživatel získáreward_minutes - Převod zbytku: Minuty nad práh se převádějí do dalšího cyklu
- Automatická aplikace: Při ukončení relace se čekající odměny automaticky aplikují jako sleva
Stav produktu (user_products.state):
| Pole | Typ | Popis |
|---|---|---|
current_played_minutes |
int | Nahrané minuty od posledního prahu |
pending_reward_minutes |
int | Nepoužité volné minuty k dispozici |
total_rewards_given |
int | Celkem přidaných volných minut (pro audit) |
Příklady konfigurace:
| Název | Parametry | Popis |
|---|---|---|
| Základní věrnostní program | {"threshold_minutes": 600, "reward_minutes": 120, "duration_days": 365} |
Po 10 hod. získá 2 hod. zdarma |
| Trvalý program | {"threshold_minutes": 600, "reward_minutes": 120} |
Nikdy nevyprší |
Jak se odměny aplikují:
- Relace končí → systém spočítá dobu v minutách
- Minuty se přičtou k
current_played_minutes - Pokud
current_played_minutes >= threshold_minutes: - Spočítej cykly:
cykly = current_played_minutes / threshold_minutes - Přičti
cykly * reward_minuteskpending_reward_minutes - Aktualizuj
total_rewards_given - Převeď zbytek:
current_played_minutes = current_played_minutes % threshold_minutes - Aplikuj čekající odměny jako slevu:
- Spočítej sazbu za minutu:
sazba = relace.celková_cena / relace.doba_minuty - Aplikuj
min(pending_reward_minutes, relace.doba_minuty)minut jako slevu sleva = použité_minuty * sazbavýsledná_cena = relace.celková_cena - sleva
Zobrazení ve frontendu:
Aktivní playtime_loyalty produkty zobrazují:
- Progress bar: current_played_minutes / threshold_minutes
- Čekající odměny: Dostupné volné minuty
- Celkem odměn: Celkové připsané volné minuty
Důležité
- Čekající odměny nepropadají, dokud je produkt aktivní
- Produkt musí být přiřazen k časovým zónám (session_mode = 'time_based')
- Zónově specifické: sleduje pouze čas v přiřazených zónách
- Může být aktivních více věrnostních produktů (např. jeden pro zónu A, druhý pro zónu B)
Sleva na zařízení (device_discount)¶
Sleva (nebo úplné zdarma použití) na konkrétní zařízení — nikdy na čas strávený v zóně, nikdy na jiná zařízení. Na rozdíl od všech ostatních typů produktů nesoutěží o "nejlepší cenu celé relace" — je to samostatná, nezávislá vrstva, která se sčítá s tím, co uplatní jiný produkt (věrnostní program, minutová kvóta, procentuální sleva, permanentka...).
Parametry:
device_ids— povinné, neprázdný seznam ID zařízení, na která se sleva vztahujediscount_percent— nepovinné, výchozí 100 (plně zdarma), rozsah 1–100duration_days— nepovinné, chybí/0= sleva je trvalá
Chování:
- Sleva se finálně účtuje až při ukončení relace, ale zobrazuje se živě i během probíhající relace: dashboard ukazuje skutečnou (netto) částku k úhradě za dané zařízení, ne surovou narůstající cenu — pokud je zařízení kryto na 100 %, částka k úhradě zůstává 0 po celou dobu relace, ne až na konci
- Pokud jmenované zařízení nebylo během relace vůbec použito, produkt neuplatní žádnou slevu (zůstává aktivní pro příště)
- Více
device_discountproduktů pokrývajících stejné zařízení se sčítá, ale nikdy nemůže slevu na jedno zařízení přesáhnout 100 % jeho vlastního poplatku - Vždy se sčítá s efektem jiného produktu (viz příklad níže) — nikdy jej nenahrazuje ani s ním nesoutěží
Příklady:
| Název | Parametry | Popis |
|---|---|---|
| Osvětlení zdarma | {"device_ids": ["<id světla>"], "discount_percent": 100} |
Trvale zdarma použití daného zařízení |
| Sleva na skříňku | {"device_ids": ["<id skříňky>"], "discount_percent": 50, "duration_days": 30} |
50 % sleva na měsíc |
Kombinace produktů: fotbalový kroužek
Člen fotbalového kroužku má dva samostatné produkty zakoupené nezávisle na sobě:
minute_quota— 60 volných minut hřiště dennědevice_discount— osvětlení zdarma (device_ids= ID reflektorů)
Při ukončení relace se uplatní oba současně: minutová kvóta pokryje čas na hřišti, sleva na zařízení pokryje osvětlení — nezávisle na tom, který produkt "vyhraje" soutěž o nejlepší celkovou cenu (tu soutěží jen produkty typu minute_quota/percentage_discount/daily_pass/atd., device_discount se jí nikdy neúčastní).
Vytvoření produktu v administraci¶
V Administrace → Produkty → Přidat definici vyberte Typ produktu. Místo ručního psaní JSON se zobrazí formulář s poli přesně podle zvoleného typu (např. Počet vstupů u balíčku, Zařízení u slevy na zařízení). Povinná pole jsou označena *, prázdné pole u limitů znamená „bez limitu“. Při změně typu se pole nastaví na výchozí hodnoty nového typu.
- Začít z předvolby (jen při vytváření): vyplní název, cenu, typ a parametry hotovou šablonou — např. měsíční/roční/denní permanentka, balíček 10 nebo 50 vstupů, týdenní limit vstupů, měsíční limit minut, sleva 20 % na rok, věrnostní program 600/120, přístup údržby nebo sleva na zařízení. Cena je jen příklad v Kč, upravte ji. U slevy na zařízení nejsou předvybrána žádná zařízení — vyberte skutečná. Seznam předvoleb viz Předvolby a kontrola konfigurace.
- Pokročilé (JSON): přepínač zobrazí surový JSON parametrů. Je to totéž, co formulář upravuje — změny se promítnou oběma směry.
- Živá kontrola: pod formulářem se průběžně zobrazují problémy s cestou k poli (např.
discount_percent — must be <= 100). Chyby uložení zablokují, upozornění ne.
Pravidla (které klíče jsou povoleny, zda je pole povinné, rozsahy) určuje backend a stejně se kontrolují při každém uložení, i přes API. Nové klíče se nepovolují — překlep jako {"minutes": 60} místo {"daily_minutes": 60} se odmítne, protože by jinak vznikl produkt bez limitů. Beze změny platí: {} u vstupní/minutové kvóty = bez limitu, percentage_discount bez discount_percent je povolená, balíček vstupů vyžaduje entries > 0, sleva na zařízení vyžaduje neprázdný seznam unikátních device_ids, procenta musí být v rozmezí (0, 100].
Starší produkty
Produkt uložený před zavedením kontroly, jehož parametry dnešním pravidlům neodpovídají, lze dál upravovat (přejmenovat, změnit cenu, deaktivovat) — kontrola se spustí, až když změníte typ nebo parametry. Takové záznamy vypíše GET /api/v1/admin/config/health.
Nákup produktů¶
Produkty lze nakoupit dvěma způsoby:
1. Bankovním převodem¶
- Vyberte produkt v katalogu
- Systém vygeneruje QR kód pro platbu
- Zaplaťte přes bankovní aplikaci
- Po připsání na účet se produkt automaticky aktivuje
2. Kreditem¶
- Vyberte produkt v katalogu
- Zvolte platbu kreditem
- Částka se strhne z kreditu
- Produkt se ihned aktivuje
Přiřazení k zónám¶
Každý produkt je vázán na konkrétní zóny. Produkt pokrývá relace pouze v přiřazených zónách. Pokud uživatel zahájí relaci v jiné zóně, produkt se neuplatní.
Životní cyklus produktu¶
| Stav | Popis |
|---|---|
pending_payment |
Čeká na zaplacení |
active |
Aktivní, uplatňuje se na relace |
expired |
Vypršel (čas nebo vstupy vyčerpány) |
cancelled |
Zrušen administrátorem |