Skip to main content

Dodawanie i edycja (Cenniki)

Przewodnik po dodawaniu i wycenianiu nowych rodzajów zajęć.

👤 Instrukcja dla pracownika (Administracja)

Aby utworzyć nowy rodzaj zajęć w systemie (np. "Tenis Dzieci VIP"): Ścieżka dla pracownika: Dashboard ➔ Rodzaje zajęć ➔ "+ Dodaj nowy rodzaj zajęć" ➔ Sekcja Konfiguracja Cennika (na samym dole)

Poniżej przedstawiono procedurę konfiguracji wariantów cenowych.

:::tip Miasto W formularzu (dodawania i edycji) pole Miasto decyduje, w której lokalizacji zajęcia będą oferowane. Wartość "Wszystkie miasta" udostępnia je w każdej lokalizacji. Szczegóły: Rodzaje zajęć → Miasto. :::

Modele konfiguracji cennika

W sekcji Konfiguracja Cennika formularza kreacji dostępne są dwa modele rozliczeniowe:

Opcja A: Cennik Podstawowy (Zalecany dla regularnych zajęć grupowych)

Należy zastosować ten model w przypadku, gdy obowiązuje jedna stawka godzinowa, niezależnie od dnia tygodnia czy pory odbywania się zajęć. Kwota podana w polu Cena jest stawką za godzinę i jest mnożona przez czas trwania zajęć (np. stawka 50 zł przy zajęciach 90-minutowych daje 75 zł).

Opcja B: Cennik Zaawansowany (Zalecany przy wynajmie infrastruktury)

Model zaawansowany umożliwia różnicowanie opłat za usługę w oparciu o kryteria czasowe (np. stawki szczytowe i pozaszczytowe). Konfiguracja wymaga określenia następujących parametrów:

  1. Cena bazowa: Kwota domyślna, aplikowana w sytuacjach, gdy żadna z dodatkowych reguł (wyjątków) nie ma zastosowania do terminu rezerwacji.
  2. Reguły cenowe (Wyjątki): Mechanizm pozwalający na definiowanie przedziałów czasowych (po kliknięciu "Dodaj Regułę"). Przykłady zastosowania:
    • "Stawka wakacyjna - poranna": Ustalenie ceny 40 zł w okresie od 1 lipca do 31 sierpnia, w przedziale godzinowym 08:00 - 12:00.
    • "Stawka szczytowa": Ustalenie ceny 80 zł (obowiązującej całorocznie) w godzinach 16:00 - 22:00.

Reguły obowiązujące do północy Jeżeli kort jest czynny do północy, w polu Godzina do należy wpisać 00:00 — reguła obejmie wtedy czas aż do końca doby (np. 07:00 - 00:00 to cały dzień pracy kortu od 7 rano do północy). W ten sam sposób zapisuje się reguły nocne przechodzące przez północ, np. 22:00 - 02:00. Jedyny niedozwolony przypadek to identyczna godzina początkowa i końcowa (np. 10:00 - 10:00), bo taki przedział nie wyznacza żadnego czasu.

Ceny z groszami

Każde pole ceny (Cena, Cena bazowa, reguła cenowa, cena niestandardowa w rezerwacji) przyjmuje kwoty z dokładnością do dwóch miejsc po przecinku, np. 75,21. Separatorem może być przecinek lub kropka — obie formy są równoważne. Próba wpisania trzeciego miejsca po przecinku nie zostanie przyjęta (znak nie pojawi się w polu). Puste pole oznacza brak ceny (0 zł dla cenników, brak nadpisania dla ceny niestandardowej).

Ważne! Hierarchia reguł cenowych Algorytm analizuje listę reguł sekwencyjnie (od góry do dołu). Weryfikowana jest zbieżność terminu zajęć z warunkami pierwszej reguły na liście. W przypadku spełnienia warunków, system aplikuje przypisaną cenę i kończy proces iteracji. Jeżeli warunki nie są spełnione, następuje weryfikacja kolejnej reguły. W przypadku braku dopasowania w pełnym cyklu, system aplikuje ustaloną "Cenę bazową".

Modyfikacja modelu cenowego w istniejących zajęciach

Administrator posiada możliwość modyfikacji "Rodzaju zajęć" po jego zapisaniu. Należy jednak mieć na uwadze następujące zasady:

  • Przekształcenie modelu z "Podstawowego" na "Zaawansowany" jest w pełni bezinwazyjne.
  • Odwrócenie tego procesu (powrót do "Podstawowego") skutkuje bezpowrotnym usunięciem skonfigurowanej tabeli reguł wyjątków. System każdorazowo wymaga potwierdzenia tej akcji w oknie dialogowym.
  • Zmiany w konfiguracji cennika (włączając w to usunięcie reguł) mają wpływ wyłącznie na nowo generowane pozycje w harmonogramie. Istniejące rezerwacje, na podstawie których system wygenerował już pozycje płatności u klientów, zachowują swoje pierwotne kwoty, zapewniając pełną spójność danych finansowych.

🛠️ Dokumentacja techniczna

Szczegóły dla deweloperów o formatach i regułach. Model dla zaawansowanego cennika korzysta z obiektów JSON serializowanych do pola w bazie (pricing_rules).

Kluczowe założenia silnika pricing_rules (calculatePrice)

  • System bierze datę startu i końca konkretnej instancji gry (game).
  • Sprawdza przedziały overlap pod kątem timezone (przelicza je na Europe/Warsaw).
  • Reguły aplikowane są sekwencyjnie. Funkcja Array.find() szuka pierwszego warunku true iterując od indexu 0.
  • Walidacja frontowa (isValidPricingRuleRange z lib/utils/pricing.ts) liczona jest z zapisanych pól dateFrom / dateTo przeliczonych na Europe/Warsaw, a nie z pomocniczych pól timeFrom / timeTo formularza. Odrzuca wyłącznie dwa przypadki: datę końcową wcześniejszą niż początkowa (porównanie po samych dniach) oraz identyczną godzinę początkową i końcową. Komunikat formValidation.invalidDateTimeRange pokazywany jest na czerwono pod listą reguł.
  • Przedział z godziną końcową mniejszą lub równą początkowej (07:00 - 00:00, 22:00 - 02:00) traktowany jest jako przechodzący przez północ i przedłużany o dobę — zarówno w calculateActivityPrice, jak i w isTimeInRange (używanym przez getApplicablePricingRule i getPricingRuleId do zapisania applied_pricing_rule_id). Godzina 00:00 jako koniec oznacza więc koniec doby, a nie jej początek.

Pola cenowe (PriceInput)

Wszystkie pola kwotowe korzystają ze wspólnego komponentu components/price-input.tsx.

  • Komponent trzyma wpisywany tekst w lokalnym stanie i przekazuje w górę wartość liczbową (onValueChange). Dzięki temu stany pośrednie (75,) nie są kasowane przez ponowne formatowanie wartości liczbowej — wcześniej pola były kontrolowane bezpośrednio przez parseFloat, co uniemożliwiało wpisanie groszy.
  • Wpisany tekst jest synchronizowany z wartością z formularza wyłącznie wtedy, gdy pole nie jest aktywne (focus), więc form.reset() działa, ale nie przerywa pisania. Po opuszczeniu pola tekst jest normalizowany do postaci wynikającej z wartości liczbowej.
  • emptyValue określa wartość zgłaszaną przy pustym polu i wartość renderowaną jako puste pole: 0 dla cenników, null dla ceny niestandardowej rezerwacji.
  • Dozwolony format wejścia: /^\d*[,.]?\d{0,2}$/. Znaki niepasujące do wzorca są odrzucane bez zmiany stanu.