Płatności i Historia Płatności (Payments & Payment History)
1. Instrukcja Użytkownika (User Manual)
Cel i Wartość Biznesowa
Moduł płatności w systemie pozwala na kompleksowe monitorowanie i rozliczanie transakcji klientów. Dzięki centralnej historii płatności na profilu klienta ("Historia płatności"), recepcja, menedżerowie oraz pracownicy klubu mają wgląd we wszystkie opłaty powiązane ze wszystkimi podopiecznymi danego klienta w jednym miejscu.
Główne Funkcje
- Zbiorczy Widok Płatności: Kliknięcie przycisku "Historia płatności" na karcie klienta otwiera przejrzyste okno dialogowe zawierające podsumowanie (łączna kwota opłacona oraz oczekująca), wykres miesięczny oraz pełną listę transakcji.
- Aktualność Statusów (Brak Opoźnień): Po opłaceniu zaległości (np. gotówką lub kartą na recepcji), status transakcji w historii klienta natychmiast zmienia się z "Zaległa" / "Oczekująca" na "Opłacona".
- Filtrowanie: Możliwość odfiltrowania płatności według miesiąca, wybranego statusu (w tym płatności mieszane, z portfela czy dzielone) oraz konkretnego uczestnika (podopiecznego).
- Zbiorcze Płatności Portfelem z Rozliczeniem Różnicy: Przy płatnościach zbiorczych (np. za kilka zajęć) system pozwala na częściowe pokrycie kwoty z portfela klienta, a pozostałą część można natychmiast opłacić gotówką, kartą lub online. System automatycznie rozdziela środki i zabezpiecza transakcje w pełni opłacone z portfela przed ponownym pobraniem środków.
- Automatyczny Zwrot do Portfela przy Anulowaniu: W przypadku anulowania rezerwacji lub zajęć, wszelkie środki pobrane wcześniej z portfela klienta (zarówno przy pełnym rozliczeniu portfelem, jak i przy częściowym rozliczeniu w dialogu złożonym) są automatycznie zwracane na jego portfel.
2. Dokumentacja Techniczna (Technical Documentation)
Architektura i Przepływ Danych (Data Flow)
System korzysta z bazy danych Cloudflare D1. Kiedy płatność zostaje oznaczona jako opłacona (poprzez akcję serwerową updatePaymentStatus), rekord w tabeli payment zyskuje odpowiedni status (paid, paid_cash, paid_card, paid_online, paid_wallet, paid_partial_wallet, paid_mixed, paid_split, paid_split_partial) oraz stempel czasowy paid_at.
Zbiorcze Płatności Portfelem i Filtrowanie Statusów na Froncie
Podczas zbiorczej płatności z portfela (payMultipleWithWallet), kwota z portfela jest przypisywana kolejno do wybranych transakcji:
- Transakcje, które zostaną pokryte w całości, otrzymują status
paid_wallet. - Jedna transakcja może zostać pokryta częściowo (kwota transakcji zostaje pomniejszona o pozostałą kwotę z portfela).
- Pozostałe transakcje pozostają w stanie
pendingz pełną oryginalną kwotą.
Serwer zwraca tablicę pendingPaymentIds określającą, które płatności nadal wymagają uregulowania. W interfejsie recepcji (PaymentsTable.tsx, mark-as-paid-button.tsx) oraz klienta (wallet-payment-dialog.tsx), po udanej operacji portfelowej lista aktywnych transakcji jest natychmiast zawężana do tych zawartych w pendingPaymentIds. Zapobiega to nadpisaniu statusu paid_wallet (np. przez paid_cash przy zbiorczym oznaczaniu reszty jako opłaconej gotówką).
Automatyczny Zwrot z Portfela (Automatic Wallet Refund)
Funkcje cancelPaymentsByRelatedId, refundPaymentsByRelatedId oraz refundPaymentsByRelatedIdForEmployeeRemoval pobierają sumę dotychczasowych obciążeń portfela dla danej płatności przy użyciu podzapytania wyliczającego sumę transakcji typu 'debit' z tabeli wallet_transaction:
COALESCE((SELECT SUM(amount) FROM wallet_transaction WHERE related_payment_id = p.id AND transaction_type = 'debit' AND tenant_id = p.tenant_id), 0) AS wallet_amount
Dzięki temu pole wallet_amount jest zawsze aktualne w pobranych rekordach. Podczas anulowania płatności:
- Dla płatności o statusie
pending, kwotawallet_amount(jeśli jest większa od 0) jest zwracana na portfel klienta, a płatność zmienia status nacancelled. - Dla płatności opłaconych (np.
paid_wallet,paid_partial_wallet,paid_mixed), jeśli parametrrefundToWalletjest ustawiony natrue, zwracana jest cała kwota płatności (payment.amount). JeślirefundToWalletjest ustawiony nafalse, zwracana jest jedynie kwota faktycznie pobrana z portfela (payment.wallet_amount). Płatność zmienia status narefunded.
Gdy płatność uzyskuje status refunded (zwrócona), powiązane z nią dokumenty (faktura lub paragon) pozostają dostępne do wglądu i pobrania zarówno w widokach administratora (desktopowa tabela płatności, widok mobilny), jak i w profilu klienta. Zabezpiecza to przed sytuacją, w której po rezygnacji i zwrocie środków użytkownik lub recepcja tracili dostęp do pierwotnego dowodu zakupu.
Zarządzanie Pamięcią Podręczną (Caching Strategy)
W architekturze Next.js App Router, domyślne mechanizmy buforowania zapytań fetch oraz tras API typu GET mogły powodować serwowanie nieaktualnych statusów płatności w oknach dialogowych na froncie.
W celu wyeliminowania problemu nieaktualnych statusów zaległości wdrożono następujące rygorystyczne reguły:
- Wymuszenie Dynamicznego Renderingu na Serwerze: Endpointy
/api/clients/[email]/payments,/api/clients/[email]/payments/summary,/api/players/[id]/paymentsoraz/api/players/[id]/payments/summaryposiadają dyrektywyexport const dynamic = 'force-dynamic';orazexport const revalidate = 0;. - Nagłówki HTTP: Każda odpowiedź zwraca nagłówek
Cache-Control: private, no-cache, no-store, must-revalidate, uniemożliwiający zapisywanie starych statusów w pamięci podręcznej przeglądarki. - Klient: Każde wywołanie
fetchz poziomu komponentów React (ClientPaymentHistoryDialog,PlayerPaymentHistoryDialog) posiada jawną opcję{ cache: 'no-store' }.
Struktura Statusów w UI (Unifikacja Sprawdzania Statusów)
Komponenty UI weryfikują status opłacenia na podstawie zunifikowanej listy SETTLED_PAYMENT_STATUSES zaimportowanej z @/constants/data:
export const SETTLED_PAYMENT_STATUSES = [
'paid',
'paid_cash',
'paid_card',
'paid_online',
'paid_wallet',
'paid_partial_wallet',
'paid_mixed',
'paid_split',
'paid_split_partial'
] as const;
Dzięki zunifikowaniu wszystkich rozproszonych, zahardkodowanych list statusów w kluczowych komponentach takich jak:
columns.tsx(tabela płatności)payments-list-view.tsx(lista mobilna administratora)PaymentsTableAccordion.tsx(akordeon płatności)camp-registrations-table/columns.tsx&cell-action.tsx(rejestracje na półkolonie)paginated-table.tsx(generyczna tabela stronicowana)
Wszystkie nowoczesne formy rozliczeń (np. płatności z portfela, częściowe płatności z portfela, płatności dzielone paid_split, paid_split_partial) są w 100% poprawnie traktowane jako opłacone. Zapobiega to błędom, w których opłacone rezerwacje figurowały w tabelach jako "Zaległe" lub "Oczekujące na płatność" ze względu na brak nowych statusów w lokalnych tablicach sprawdzających.
Zarządzanie Płatnościami w Stanie Zaległości (Status 'overdue')
Instrukcja Użytkownika i Recepcji
- Czym jest status "Zaległa" (
'overdue'): Płatność otrzymuje ten status automatycznie po upływie terminu płatności lub po jej ręcznym oznaczeniu jako zaległość przez pracownika recepcji za pomocą akcji "Oznacz jako zaległość" przy rozliczaniu rezerwacji. - Możliwości uregulowania: Klient może w każdej chwili uregulować zaległe płatności z poziomu swojego konta klienta:
- Wybierając płatności z historii i przechodząc do standardowej bramki płatniczej Przelewy24.
- Opłacając je zbiorczo lub pojedynczo zgromadzonymi środkami ze swojego Portfela.
- Rozliczenie przez recepcję: Pracownik lub administrator klubu może w każdej chwili ręcznie oznaczyć zaległą płatność jako opłaconą (np. przy płatności gotówką lub kartą w recepcji) za pomocą przycisku "Oznacz jako opłacone" bezpośrednio w tabeli płatności.
- Blokady systemowe: W przypadku braku zapłaty w określonym terminie (zgodnie z konfiguracją limitów miasta), zaległości te skutkują automatycznym zablokowaniem możliwości dokonywania nowych rezerwacji przez klienta.
Dokumentacja Techniczna
Płatności zaległe o statusie 'overdue' są technicznie traktowane w zapytaniach SQL oraz logice biznesowej jako podtyp płatności nieopłaconych, równolegle ze statusem 'pending'.
- Inicjalizacja transakcji: Trasy
/api/payments/initializeoraz/api/payments/wallet-initializefiltrują i zezwalają na płatności posiadającestatus = 'pending'LUBstatus = 'overdue'. - Blokady rezerwacji: Helpery takie jak
isCurrentUserOverdueBlocked,hasPlayerOverduePayments,hasOwnerOverduePaymentsorazgetOwnerOverdueTotalweryfikują sumaryczną kwotę zaległości, odpytując bazę z warunkiemp.status IN ('pending', 'overdue'). - Widok w tabelach i akcje:
- Klasa
AdminActionsCellwcolumns.tsxrenderuje kontrolkę ręcznego rozliczenia dla transakcji o statusie'overdue'. - Checkbox w kolumnie
selectumożliwia zbiorcze zaznaczanie wierszy ze statusami'pending'i'overdue'. - Zapytania sortujące rezerwacje na listach korzystają z konstrukcji:
ORDER BY CASE WHEN pay.status IN ('pending', 'overdue') THEN 1 ELSE 0 END DESCco gwarantuje, że nieuregulowane zaległości zawsze wyświetlają się jako pierwsze.
- Klasa
- System powiadomień i przypomnień: Skrypt
lib/reminders.tsodpytuje bazę danych o transakcje wymagające ponaglenia, uwzględniając statusy'pending'i'overdue'. Powoduje to, że klient nie przestaje otrzymywać automatycznych przypomnień e-mail o zaległościach po zmianie ich statusu na'overdue'.