Skip to main content

Wystawianie Faktur i Paragony

Przewodnik wyjaśniający, jak system zarządza preferencjami klientów dotyczącymi fakturowania oraz jak pomaga pracownikom na recepcji podczas rozliczania opłat za zajęcia.

👤 Instrukcja dla pracownika (Recepcja / Administracja)

System AcePark automatyzuje zarządzanie preferencjami klientów w zakresie dokumentów sprzedażowych (faktury VAT, faktury imienne, paragony fiskalne), ułatwiając i standaryzując proces rozliczania opłat na recepcji.

1. Deklaracja preferencji fakturowania przez klienta

Każdy klient podczas logowania do panelu (lub w procesie rejestracji) ma możliwość trwałego zdefiniowania preferowanego dokumentu księgowego. Ścieżka dla klienta: Portal Klienta ➔ Profil ➔ Dane do faktury

  • Faktura VAT (B2B): Użytkownik wprowadza NIP oraz nazwę przedsiębiorstwa.
  • Faktura Imienna (B2C): Użytkownik wprowadza dane osoby fizycznej.
  • Zastosuj dla wszystkich uczestników: Opcja ta jest wykorzystywana w przypadku kont rodzinnych. Pozwala na automatyczne powielenie danych fakturowych (np. jednego NIP-u) na wszystkie konta podopiecznych (dzieci). Należy pamiętać, że administrator z poziomu panelu pracowniczego również posiada uprawnienia do globalnego ujednolicenia tych danych.

2. Rozliczanie klienta i obsługa paragonów

Podczas przyjmowania płatności stacjonarnej, uruchomienie formularza "Rozliczenie rezerwacji" wyzwala zautomatyzowane procedury weryfikacyjne: Ścieżka dla pracownika: Dashboard ➔ Kalendarz (lub Uczestnicy) ➔ Rozliczenie rezerwacji ➔ Odznacz/zaznacz "Drukuj paragon"

  • Weryfikacja preferencji: Interfejs wyświetla wyraźny, kolorowy komunikat ostrzegawczy (np. "Uwaga, klient wymaga faktury na firmę: ABC Sp. z o.o., NIP 123..."), jeśli profil klienta posiada aktywne dane fakturowe.
  • Zabezpieczenie przed błędem fiskalnym: Jeżeli klient zadeklarował chęć otrzymania faktury VAT na firmę, system automatycznie odznacza opcję wydruku paragonu na zintegrowanej kasie fiskalnej. Blokada ta zabezpiecza przed nieintencjonalnym zarejestrowaniem sprzedaży detalicznej (bez wpisanego NIP-u nabywcy) dla transakcji typu B2B.
  • Funkcjonalność ta eliminuje konieczność manualnej weryfikacji profilu każdego klienta przed dokonaniem transakcji stacjonarnej.

3. Faktury za półkolonie

Rejestracja na turnusy (np. półkolonie) jest w pełni zautomatyzowana pod kątem księgowym. Jeżeli klient w przeszłości zdefiniował profil fakturowy, podczas kolejnych zapisów system domyślnie użyje zachowanych danych przedsiębiorstwa.

4. Gdy Fakturownia odrzuci fakturę

Fakturownia sprawdza nabywcę ostrzej niż formularze zapisu — najczęściej chodzi o NIP, którego nie uznaje (błędny nr NIP nabywcy). Odrzucenie przychodzi już po zapłacie klienta, więc system zachowuje się tak:

  • płatność nie zostaje bez dokumentu — zamiast faktury wystawiany jest paragon,
  • na Dashboard ➔ Powiadomienia krytyczne pojawia się wpis „Fakturownia odrzuciła fakturę dla płatności #…” z numerami płatności.

Zadanie recepcji: poprawić dane do faktury w profilu klienta (najczęściej NIP lub adres) i wystawić fakturę ręcznie. Sam formularz zapisu na próbne i stałe zajęcia nie przepuści już NIP-u z błędną sumą kontrolną ani numeru z jednej powtórzonej cyfry (np. 9999999999), więc ta sytuacja dotyczy głównie danych wprowadzonych wcześniej.

Wycofanie preferencji fakturowych

W przypadku rezygnacji klienta z otrzymywania faktur B2B, modyfikacja wymaga edycji jego profilu. Należy usunąć zaznaczenie z opcji faktur, upewnić się, że funkcja "Zastosuj dla wszystkich uczestników" jest aktywna, a następnie zapisać zmiany. Od tego momentu system zaprzestanie wymuszania faktur dla danego konta rodzinnego.


🛠️ Dokumentacja techniczna

Sekcja opisująca relacyjne aspekty oraz Server Actions w procesie Fakturowania.

Architektura danych bazy

Dane fakturowe są zapisywane w dedykowanej tabeli invoice. Relacja zachodzi z tabelą player (player_id jest kluczem obcym). Dzięki polu owner_email system jest w stanie grupować "podopiecznych" (np. kilkoro dzieci jednego użytkownika Auth0).

Model invoice przechowuje flagi:

  • classic_invoice (BOOLEAN) – Czy wystawiana jest pełna faktura VAT na firmę (wymaga NIP i company name).
  • nominal_invoice (BOOLEAN) – Czy wystawiana jest faktura imienna.

Kluczowe Funkcje w API

Znajdują się głównie w Server Actions:

  • fetchUserInvoiceData: Ciągnie dane logiki i sprawdza, czy wszyscy uczestnicy podpięci pod ten sam e-mail mają dokładnie ten sam stan rekordu invoice (wylicza z tego dynamicznie flagę isAppliedToAll na potrzeby UI).
  • fetchPlayerInvoiceDataInternal: Podstawowa funkcja odczytująca dane invoice używana przez obsługę bez weryfikacji JWT tokenu sesji właściciela (odczyt na uprawnieniach STAFF).
  • upsertInvoicesForUserClient: Zbiorcza operacja dla klienta modyfikująca ustawienia invoice dla wielu player_id w jednej transakcji bazy danych.
  • applyInvoiceDataToAllPlayersByPlayerId: Odpowiednik dla Managera, umożliwiający wzięcie player_id dziecka #1 i nadpisanie jego firmowego NIPu na profil dziecka #2 tego samego opiekuna.

Walidacja NIP-u

lib/utils/nip.ts (isValidPolishTaxId) to jedno źródło prawdy: 10 cyfr, poprawna suma kontrolna (wagi 6 5 7 2 3 4 5 6 7, reszta z dzielenia przez 11) i odrzucenie numerów złożonych z jednej powtórzonej cyfry — te przechodzą sumę kontrolną, ale Fakturownia ich nie przyjmuje. Używają jej validateInvoiceDetails (lib/trial-registration-payment.ts, wspólna dla zapisu na próbne i stałe) oraz oba kroki płatności w interfejsie, więc zły numer zatrzymuje się przed hand-offem do Przelewy24.

Odrzucenie faktury przez Fakturownię

generateInvoiceFromPayment i generateBulkInvoiceFromPayments (lib/actions/invoice-generation.ts) łapią błąd z POST /invoices.json osobno od reszty przetwarzania i wywołują fallbackToReceipt:

  1. reportInvoiceRejection loguje błąd i zakłada powiadomienie krytyczne (type: 'sync', severity: 'critical') z listą paymentIds,
  2. dla tej samej listy pozycji wystawiany jest paragon i zapisywany na płatnościach przez recordReceiptGeneration.

Bez tego wyjątek kończył się samym logError — płatność zostawała z invoice_id i receipt_id równym NULL, a nikt nie dostawał sygnału, że dokumentu nie ma.

Logika synchronizacji interfejsu (Dialogi płatności)

Gdy pracownik otwiera okienko ReservationSettleDialog (lub zmiania cel w selekcie activePlayerId), w tle uruchamiana jest operacja pobrania danych profilu fakturowego:

  1. Component dispatchuje fetchPlayerInvoiceDataInternal.
  2. Jeżeli API zwróci, że classic_invoice === true (oraz nominal_invoice jest false, co wskazuje stricte B2B), stan systemowy zmienia domyślną konfigurację: mutacja wymusza ustawienie checkboxa printReceipt na false.
  3. To zapobiega nieintencjonalnemu wywołaniu żądania do zintegrowanego systemu kasy fiskalnej, chroniąc obiekt przed potrzebą pisania fizycznych zwrotów do raportu dobowego kasy.