Przejdź do głównej zawartości

Kasa klubowa

Widok Kasa (/dashboard/cash) oraz kafelek „Kasa klubowa" w panelu bocznym grafiku pokazują, ile pieniędzy powinno znajdować się w szufladzie kasowej danej lokalizacji.

👤 Instrukcja dla pracownika

Co oznacza „Aktualny stan kasy klubowej"

Jest to suma narastająca od początku działalności lokalizacji, a nie stan dnia czy zmiany. System liczy ją tak:

sprzedaż sklepowa gotówką + transakcje opłacone gotówką − wypłaty z kasy + wpłaty do kasy

Analogicznie liczony jest stan dla płatności kartą. Nie ma tu żadnego „zamknięcia dnia" — jeśli ktoś wyjmie pieniądze z szuflady bez zarejestrowania wypłaty, saldo w systemie przestanie zgadzać się ze stanem faktycznym.

Kasa jest zawsze przypisana do lokalizacji

Stan kasy dotyczy tej lokalizacji, którą masz wybraną w selektorze u góry ekranu:

  • wybrane konkretne miasto i ulica → widzisz kasę tej jednej lokalizacji,
  • wybrane miasto bez podziału na ulice (np. Lublin) → widzisz kasę całego miasta,
  • wybrane Wszystkie miasta → widzisz sumę zbiorczą ze wszystkich lokalizacji.

Wypłata i wpłata zawsze trafiają do lokalizacji widocznej na ekranie. Jedyny wyjątek: gdy masz wybrane „Wszystkie miasta", system nie wie, o którą kasę chodzi, i zapisuje operację do lokalizacji domyślnej. Przed rejestrowaniem wypłaty upewnij się więc, że masz wybrane konkretne miasto.

Częste pytania

Dlaczego kwota na grafiku różni się od tej w zakładce Kasa? Nie powinna — oba miejsca liczą to samo dla tej samej lokalizacji. Jeśli się różnią, sprawdź, czy w obu miejscach masz wybrane to samo miasto.

Dlaczego „Ostatnia aktualizacja" pokazuje bieżącą godzinę? To znacznik momentu pobrania danych, a nie ostatniej operacji na kasie. Saldo jest przeliczane przy każdym wejściu na stronę.

🛠 Dokumentacja techniczna

Przepływ danych

Źródło lokalizacji

resolveReadLocation(city, street) w lib/actions/court.ts jest jedynym miejscem, w którym selekcja z URL zamienia się na filtr odczytu:

  • city puste lub 'ALL'{ city: 'ALL', street: 'ALL' }, czyli brak filtrowania,
  • brak street w URL → getDefaultStreetForCity(city), które zwraca konkretną ulicę wyłącznie dla miast wielolokalizacyjnych,
  • miasto jednolokalizacyjne → street: 'ALL', bo takie miasta nie mają wymiaru ulicy.

Każdy odczyt musi przechodzić przez ten helper. CashManagementWrapper dokleja rozwiązaną lokalizację do zapytań /api/cash-management (query dla GET, body dla POST), dzięki czemu odświeżenie po wypłacie trafia dokładnie w tę samą lokalizację, którą wyrenderował SSR.

Ciasteczka selected-city / selected-street pozostają wyłącznie fallbackiem dla wywołań bez jawnej lokalizacji. Są globalne dla przeglądarki, więc przy dwóch kartach z różnymi lokalizacjami nie są wiarygodne — nie opieraj na nich nowych odczytów.

Filtrowanie w SQL

locationFilter(loc, alias?) (lib/utils/location-filter.ts) buduje fragment AND city = ? AND street = ?, pomijając wymiar o wartości 'ALL' lub pustej. getCashBalance używa trzech wariantów:

WariantTabelaAlias
salesFiltersalesbrak
payFilterpaymentp
cwFiltercash_withdrawalbrak

linked_payment celowo nie ma własnych kolumn lokalizacji — płatności dzielone są filtrowane przez JOIN do płatności nadrzędnej (p), z której dziedziczą lokalizację.

Zapis lokalizacji

  • resolveWriteLocation(location?) — dla operacji inicjowanych z UI. 'ALL' zwija się do lokalizacji domyślnej, bo nie jest poprawnym celem zapisu.
  • getWriteStreetForCity(city) — dla rekordów, których miasto wynika z danych (terminy obozów i kursów, płatności pochodne). Zwraca ulicę domyślną tylko dla miast wielolokalizacyjnych, a dla pozostałych pusty string.

Kolumny city / street w payment, sales i cash_withdrawalNOT NULL DEFAULT 'Opole' / 'Spokojna'. Pominięcie ich w INSERT nie powoduje błędu — rekord po cichu wyląduje w lokalizacji domyślnej. Każdy nowy INSERT do tych tabel musi jawnie ustawiać lokalizację.

Uwagi do interpretacji salda

  • Saldo jest sumą narastającą — brak zakresu dat i brak zamknięcia dnia.
  • last_updated to new Date() z momentu zapytania, nie znacznik ostatniej operacji.
  • Płatności ze statusem refunded lub funds_retained = 1 z metodą gotówkową powiększają stan kasy: środki zostają w klubie (zwrot idzie na portfel), więc gotówka fizycznie nie opuszcza szuflady. Ta sama konwencja obowiązuje w lib/actions/finances.ts.