Skip to main content

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

  1. 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.
  2. 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".
  3. 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).
  4. 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.
  5. 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:

  1. Transakcje, które zostaną pokryte w całości, otrzymują status paid_wallet.
  2. Jedna transakcja może zostać pokryta częściowo (kwota transakcji zostaje pomniejszona o pozostałą kwotę z portfela).
  3. Pozostałe transakcje pozostają w stanie pending z 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, kwota wallet_amount (jeśli jest większa od 0) jest zwracana na portfel klienta, a płatność zmienia status na cancelled.
  • Dla płatności opłaconych (np. paid_wallet, paid_partial_wallet, paid_mixed), jeśli parametr refundToWallet jest ustawiony na true, zwracana jest cała kwota płatności (payment.amount). Jeśli refundToWallet jest ustawiony na false, zwracana jest jedynie kwota faktycznie pobrana z portfela (payment.wallet_amount). Płatność zmienia status na refunded.

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:

  1. Wymuszenie Dynamicznego Renderingu na Serwerze: Endpointy /api/clients/[email]/payments, /api/clients/[email]/payments/summary, /api/players/[id]/payments oraz /api/players/[id]/payments/summary posiadają dyrektywy export const dynamic = 'force-dynamic'; oraz export const revalidate = 0;.
  2. 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.
  3. Klient: Każde wywołanie fetch z 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:
    1. Wybierając płatności z historii i przechodząc do standardowej bramki płatniczej Przelewy24.
    2. 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/initialize oraz /api/payments/wallet-initialize filtrują i zezwalają na płatności posiadające status = 'pending' LUB status = 'overdue'.
  • Blokady rezerwacji: Helpery takie jak isCurrentUserOverdueBlocked, hasPlayerOverduePayments, hasOwnerOverduePayments oraz getOwnerOverdueTotal weryfikują sumaryczną kwotę zaległości, odpytując bazę z warunkiem p.status IN ('pending', 'overdue').
  • Widok w tabelach i akcje:
    • Klasa AdminActionsCell w columns.tsx renderuje kontrolkę ręcznego rozliczenia dla transakcji o statusie 'overdue'.
    • Checkbox w kolumnie select umoż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 DESC co gwarantuje, że nieuregulowane zaległości zawsze wyświetlają się jako pierwsze.
  • System powiadomień i przypomnień: Skrypt lib/reminders.ts odpytuje 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'.