Przejdź do treści

Klienci (Client)

Baza stałych klientów — osoby fizyczne i firmy z historią rezerwacji i indywidualnymi cenami.

Ostatnia aktualizacja dokumentacji: 26 lutego 2026 Stan synchronizacji z kodem: Zsynchronizowany


Co to jest?

Domena Klientów zarządza bazą osób i firm korzystających z systemu rezerwacji. To jak kartoteka klientów w recepcji — z pełną historią rezerwacji, preferencjami VAT i możliwością ustalenia indywidualnych cen.

Klient może być: - Osobą fizyczną — Jan Kowalski - Firmą — ABC Sp. z o.o. (z NIP i REGON)


Jakie problemy rozwiązuje?

Korzyści dla JST

  • Historia klienta — wszystkie rezerwacje w jednym miejscu
  • Indywidualne cenniki — rabaty dla stałych klientów
  • Obsługa firm — prawidłowe faktury z NIP
  • Statystyki finansowe — ile klient wydał, ile zwrócono
  • Raporty — ranking najlepszych klientów, nowe rejestracje

Struktura domeny

app/Domain/Client/
├── Action/
│   ├── Attr/
│   │   ├── GetAmountDueAttr.php          # Obliczanie zaległości
│   │   ├── GetTotalPendingAttr.php       # Obliczanie kwot oczekujących
│   │   ├── GetTotalRefundedAttr.php      # Obliczanie zwrotów
│   │   └── GetTotalSpentAttr.php         # Obliczanie wydatków
│   ├── Controller/
│   │   ├── IndexClientController.php     # Listowanie klientów z filtrami
│   │   ├── ShowClientController.php      # Szczegóły klienta
│   │   ├── StoreClientController.php     # Tworzenie klienta
│   │   └── UpdateClientController.php    # Aktualizacja klienta
│   ├── Other/
│   │   ├── ApplyClientAggregatesOrm.php  # Optymalizacja zapytań SQL
│   │   ├── CreateClient.php              # Tworzenie klienta (Client::create)
│   │   ├── FindClientByEmail.php         # Wyszukiwanie po email
│   │   ├── FindClientByNip.php           # Wyszukiwanie po NIP
│   │   ├── FindOrCreateClient.php        # Znajdź lub utwórz klienta
│   │   ├── GenerateClientColor.php       # Losowanie koloru klienta
│   │   ├── UpdateClient.php              # Aktualizacja danych klienta
│   │   └── UpdateClientIfNeeded.php      # Warunkowa aktualizacja pustych pól
│   └── Scope/
│       └── ScopeSearchFunc.php           # Zakres wyszukiwania tekstowego
├── Controller/
│   └── ClientController.php              # Główny kontroler
├── Model/
│   └── Client.php                        # Model Eloquent
├── Observer/
│   └── ClientObserver.php                # Observer (pusty - zarezerwowany)
├── Policy/
│   └── ClientPolicy.php                  # Polityka autoryzacji
├── Request/
│   ├── IndexClientControllerRequest.php  # Walidacja listy
│   ├── StoreClientControllerRequest.php  # Walidacja tworzenia
│   └── UpdateClientControllerRequest.php # Walidacja edycji
├── Resource/
│   └── ClientResource.php                # Transformacja JSON API
└── Service/
    ├── ClientEnumService.php             # Enum (pusty - zarezerwowany)
    ├── TraitClient.php                   # Metody domenowe
    └── TraitControllerClient.php         # Metody kontrolera

Kluczowe funkcjonalności

Dla administratora

Funkcja Opis Status
Lista klientów Przegląd wszystkich klientów z filtrami Dostępne
Tworzenie klientów Ręczne dodawanie klientów Dostępne
Edycja danych Aktualizacja kontaktu, adresu, VAT Dostępne
Aktywacja/dezaktywacja Zmiana statusu is_active klienta Dostępne
Historia rezerwacji Podgląd wszystkich rezerwacji klienta Dostępne
Statystyki finansowe Wydatki, zwroty, saldo Dostępne
Indywidualne cenniki Przypisanie rabatowych stawek Dostępne
Notatki Wewnętrzne komentarze o kliencie Dostępne
Raporty Nowe rejestracje, ranking wydatków Dostępne

Jak to działa?

Scenariusz 1: Automatyczne tworzenie klienta

flowchart LR
    A[Mieszkaniec rezerwuje] --> B{Czy istnieje klient?}
    B -->|NIP znany<br>i firma| C[Aktualizuj puste pola]
    B -->|Email znany| C
    B -->|Nowy| D[Utwórz klienta]
    D --> E[Losuj kolor]
    C --> F[Powiąż z rezerwacją]
    E --> F
  1. Mieszkaniec składa rezerwację podając dane
  2. System sprawdza kolejno:
    • Czy istnieje klient z tym NIP (jeśli firma)?
    • Czy istnieje klient z tym emailem?
  3. Jeśli znaleziono — aktualizuje tylko puste pola (nie nadpisuje istniejących)
  4. Jeśli nie znaleziono — tworzy nowego klienta z automatycznie wylosowanym kolorem
  5. Rezerwacja jest powiązana z klientem

Pola aktualizowane automatycznie

Przy automatycznej aktualizacji (UpdateClientIfNeeded) system uzupełnia tylko puste pola z listy: first_name, last_name, mobile_phone, company_name, nip, regon, street, street_number, postal_code, city, commune, district, voivodeship, teryt_data. Email nie jest aktualizowany — jest używany wyłącznie do identyfikacji. Dodatkowo, jeśli klient nie był firmą, a nowa rezerwacja wskazuje is_company=true, status zostanie zmieniony na firmę.

Scenariusz 2: Indywidualny cennik dla firmy

  1. Firma "ABC Sp. z o.o." regularnie wynajmuje salę
  2. Administrator tworzy dla niej indywidualny cennik: 80 zł/godz. (zamiast 100 zł)
  3. Przy każdej rezerwacji tej firmy — automatycznie stosowana jest niższa cena
  4. Na fakturze widoczna jest stawka 80 zł

Scenariusz 3: Sprawdzenie historii klienta

  1. Administrator otwiera kartę klienta
  2. Widzi wszystkie rezerwacje, płatności, zwroty
  3. Widzi statystyki: łącznie wydał 5000 zł, zwrócono 200 zł
  4. Może dodać notatkę wewnętrzną

Struktura tabeli w bazie danych

Tabela clients

Kolumna Typ Nullable Default Opis
id UUID Nie auto Klucz główny
first_name string Nie Imię klienta
last_name string Nie Nazwisko klienta
email string Nie Adres email (unikalny)
mobile_phone string Tak null Numer telefonu
is_company boolean Nie false Czy klient jest firmą
company_name string Tak null Nazwa firmy
nip string Tak null NIP firmy (unikalny)
regon string Tak null REGON firmy
street string Tak null Ulica
street_number string Tak null Numer budynku/lokalu
postal_code string Tak null Kod pocztowy
city string Tak null Miasto
commune string Tak null Gmina
district string Tak null Powiat
voivodeship string Tak null Województwo
teryt_data JSON Nie {} Dane adresowe z systemu TERYT
vat_rate string(10) Nie '23' Kod stawki VAT
vat_note string(255) Tak null Notatka dotycząca VAT
color string Tak null Kolor HEX do identyfikacji
is_active boolean Nie true Status aktywności
access_pin_hash string(255) Tak null Zahashowany kod PIN
access_pin_created_at timestamp Tak null Data utworzenia PIN
access_pin_request_id UUID Tak null FK do access_pin_requests
access_enabled boolean Nie true Czy dostęp PIN włączony
created_at timestamp Nie auto Data utworzenia
updated_at timestamp Nie auto Data aktualizacji

Klucze obce

Kolumna Referencja On Delete
access_pin_request_id access_pin_requests.id SET NULL

Indeksy

Kolumny Nazwa Typ
first_name INDEX
last_name INDEX
email UNIQUE
nip UNIQUE
is_active INDEX
mobile_phone INDEX
id, first_name, last_name clients_id_name_index INDEX (kompozytowy)
nip, is_active clients_nip_active_index INDEX (kompozytowy)

Tabele powiązane

Tabela Kolumna FK On Delete Opis
reservations client_id SET NULL Rezerwacje klienta
pricings client_id CASCADE Indywidualne cenniki klienta

Endpointy API

Wszystkie endpointy wymagają uwierzytelnienia (JWT) i roli ADMIN, SUPERVISOR lub EMPLOYEE.

Metoda Endpoint Opis
GET /api/clients Lista klientów z filtrami i paginacją
GET /api/clients/{clientId} Szczegóły klienta
POST /api/clients Tworzenie nowego klienta
PATCH /api/clients/{clientId} Aktualizacja danych klienta
PATCH /api/clients/{clientId}/change-state Zmiana statusu is_active

Brak endpointów publicznych

Domena Client nie posiada endpointów publicznych (noAuth). Wszystkie operacje wymagają uwierzytelnienia.

GET /api/clients

Lista klientów z filtrami, paginacją i agregatami finansowymi.

Parametry zapytania:

Parametr Typ Opis
filter[search] string Wyszukiwanie w: first_name, last_name, email, company_name, nip
filter[id] UUID Dokładne dopasowanie po ID
filter[is_company] boolean Filtr: firma / osoba fizyczna
filter[is_active] boolean Filtr: aktywny / nieaktywny
page integer Numer strony
per_page integer Ilość elementów na stronie

Response (sukces — 200):

Paginowana kolekcja ClientResource (patrz sekcja Odpowiedź API).

GET /api/clients/{clientId}

Szczegóły pojedynczego klienta z agregatami finansowymi.

Parametry URL:

Parametr Typ Opis
clientId UUID Identyfikator klienta

Response (sukces — 200): Pojedynczy obiekt ClientResource.

Response (błąd — 404): NotFoundModelException jeśli klient nie istnieje.

POST /api/clients

Tworzenie nowego klienta. Wymaga autoryzacji (ClientPolicy@create).

Body (Request):

Pole Typ Wymagane Walidacja
first_name string Tak required\|string\|max:255
last_name string Tak required\|string\|max:255
email string Tak required\|email\|max:255\|unique:clients
is_company boolean Tak required\|boolean
company_name string Jeśli firma required_if:is_company,true\|string\|max:255
nip string Jeśli firma required_if:is_company,true\|string\|unique:clients\|ValidCorrectNIPRule
mobile_phone string Nie regex:/^[0-9]+$/\|min:7\|max:15
regon string Nie string\|max:20\|ValidCorrectRegonRule
street string Nie string\|max:255
street_number string Nie string\|max:255
postal_code string Nie regex:/^[0-9]{2}-[0-9]{3}$/
city string Nie string\|max:255
commune string Nie string\|max:255
district string Nie string\|max:255
voivodeship string Nie string\|max:255
teryt_data array Nie array
color string Nie hex_color\|not_in:#000,#000000
vat_rate string Nie in: wartości z VatEnumService::getAllRates()
vat_note string Nie string\|max:255

Response (sukces — 200): "The Client has been successfully added"

PATCH /api/clients/{clientId}

Aktualizacja danych klienta. Wymaga autoryzacji (ClientPolicy@update).

Wszystkie pola są opcjonalne (sometimes). Email i NIP zachowują unikalność z wykluczeniem aktualnego rekordu.

Response (sukces — 200): "The Client has been successfully updated"

Response (błąd — 404): NotFoundModelException jeśli klient nie istnieje.

PATCH /api/clients/{clientId}/change-state

Przełączenie statusu is_active klienta. Wymaga autoryzacji (ClientPolicy@update).

Response (sukces — 200): Komunikat o zmianie statusu.


Powiązania z innymi domenami

Domena Powiązanie Typ relacji
Rezerwacje (Reservation) Klient ma wiele rezerwacji HasMany
Rezerwacje (Reservation) FindOrCreateClientForReservation — automatyczne tworzenie klienta przy rezerwacji Cross-domain
Cenniki (Pricing) Klient może mieć indywidualne cenniki HasMany
Notatki (Note) Do klienta można dodawać notatki MorphMany (polimorficzna)
Dostęp (Access) Klient może mieć kod PIN do logowania BelongsTo (AccessPinRequest)
Harmonogramy (Schedule) Filtrowanie harmonogramu po kliencie Pośrednie
Raporty (Report) clients.new_registrations — raport nowych rejestracji Cross-domain
Raporty (Report) clients.top_spenders — ranking klientów wg wydatków Cross-domain

Integracja z domeną Rezerwacji

Klasa FindOrCreateClientForReservation (w domenie Reservation) wyciąga dane klienta z walidowanych danych rezerwacji i deleguje do FindOrCreateClient:

flowchart LR
    A[Dane rezerwacji] --> B[FindOrCreateClientForReservation]
    B --> C{Email lub NIP podany?}
    C -->|Nie| D[Zwróć null]
    C -->|Tak| E[FindOrCreateClient]
    E --> F[Klient]

Pola wyciągane z rezerwacji: first_name, last_name, email, mobile_phone, is_company, company_name, nip, regon, street, street_number, postal_code, city, commune, district, voivodeship.

Warunek konieczny

Jeśli w danych rezerwacji brakuje zarówno email jak i nip, klient nie jest tworzony — metoda zwraca null.

Integracja z domeną Raportów

Domena Client jest źródłem danych dla 2 dedykowanych raportów:

clients.new_registrations — Raport nowych rejestracji klientów:

  • Grupowanie po dacie (DATE(created_at))
  • Filtrowanie po okresie (this_month, last_month, this_year, last_year, custom)
  • Opcjonalne filtrowanie po organization_id
  • Kolumny: Data, Liczba nowych klientów

clients.top_spenders — Ranking klientów wg wydatków:

  • Dane z rezerwacji o statusie RESERVATION_STATUS_CONFIRMED
  • Filtrowanie po okresie i opcjonalnie po organization_id
  • Konfigurowalny limit wyników (domyślnie 10, max 30)
  • Kolumny: Pozycja, Nazwa klienta, Email, Telefon, Liczba rezerwacji, Liczba slotów, Suma godzin, Zapłacone, Zwroty, Przychód razem

Wartość biznesowa

Argumenty dla decydentów

  1. Wiedza o kliencie — pełna historia w jednym miejscu
  2. Lojalizacja — możliwość oferowania rabatów stałym klientom
  3. Prawidłowe faktury — NIP, REGON, dane adresowe
  4. Analityka — ile klient wydał, jaki ma potencjał
  5. Raporty — ranking najlepszych klientów, analiza nowych rejestracji

Typy klientów

Osoba fizyczna

Pole Wymagane Walidacja Opis
Imię (first_name) Tak string, max 255 Imię klienta
Nazwisko (last_name) Tak string, max 255 Nazwisko klienta
Email (email) Tak email, max 255, unikalny Unikalny adres email
Telefon (mobile_phone) Nie regex: /^[0-9]+$/, 7-15 znaków Numer kontaktowy (tylko cyfry)
Adres Nie Ulica, numer, kod pocztowy, miasto
Dane TERYT (teryt_data) Nie array Dane adresowe z systemu TERYT

Firma

Pole Wymagane Walidacja Opis
Nazwa firmy (company_name) Tak (jeśli firma) string, max 255 Pełna nazwa firmy
NIP (nip) Tak (jeśli firma) walidacja sumy kontrolnej (ValidCorrectNIPRule), unikalny 10 cyfr
REGON (regon) Nie walidacja (ValidCorrectRegonRule), max 20 9 lub 14 cyfr
Email (email) Tak email, max 255, unikalny Email kontaktowy
Telefon (mobile_phone) Nie regex: /^[0-9]+$/, 7-15 znaków Numer kontaktowy
Adres Nie kod pocztowy: regex: /^[0-9]{2}-[0-9]{3}$/ Siedziba firmy

Walidacja przy aktualizacji

Przy aktualizacji (PATCH) wszystkie pola są opcjonalne (sometimes). Email i NIP zachowują unikalność z wykluczeniem aktualnego rekordu.


Obsługa VAT

Każdy klient może mieć przypisaną stawkę VAT (domyślnie 23%):

Stawka Kod Wartość numeryczna Opis
23% 23 23.00 Standardowa stawka
8% 8 8.00 Obniżona
5% 5 5.00 Super obniżona
0% 0 0.00 Zerowa
Zwolniony zw null Zwolnienie z VAT
Nie podlega np null Nie podlega opodatkowaniu

Stawki VAT są zarządzane przez VatEnumService z domeny Pricing.

Pole Opis
vat_rate Kod stawki VAT (domyślnie 23)
vat_rate_label Etykieta tekstowa (np. 23%, Zwolniony) — atrybut obliczany
vat_rate_numeric Wartość numeryczna stawki (np. 23.00, null) — atrybut obliczany
vat_note Notatka dotycząca VAT (opcjonalna, max 255 znaków)

Dziedziczenie stawki VAT

Stawka VAT klienta jest używana przy obliczaniu cen rezerwacji, chyba że cennik ma własne nadpisanie.


Statystyki finansowe

System automatycznie oblicza dla każdego klienta:

Metryka Opis Sposób obliczania
total_spent Suma opłaconych płatności Suma amount z ReservationPayment o statusach RES_COMPLETED lub RES_PARTIALLY_REFUNDED
total_refunded Suma zwrotów Suma amount z ReservationPaymentRefund o statusie RES_COMPLETED, powiązanych z płatnościami o statusach RES_COMPLETED / RES_PARTIALLY_REFUNDED
total_balance Saldo (wydane - zwrócone) total_spent - total_refunded
total_pending Kwota oczekująca na płatność Suma (total_price - total_paid) z rezerwacji o statusach RES_PENDING / RESERVATION_STATUS_CONFIRMED i payment_status RES_PENDING / PAYMENT_STATUS_PARTIALLY_PAID
amount_due Zaległości do zapłaty Alias dla total_pending — ta sama wartość
total_reservations Liczba rezerwacji reservations_count lub reservations()->count()
is_in_use Czy klient ma rezerwacje reservations_exists lub reservations()->exists()

Optymalizacja zapytań

Na liście klientów (index) i w widoku szczegółowym (show) statystyki są obliczane za pomocą subzapytań SQL (ApplyClientAggregatesOrm) zamiast osobnych zapytań dla każdego klienta. System dodaje computed_total_spent, computed_total_refunded i computed_total_pending jako podzapytania SELECT, a także withCount('reservations') i withExists('reservations') — co eliminuje problem N+1.

Różnica między GetTotalPendingAttr a GetAmountDueAttr

GetTotalPendingAttr używa withoutGlobalScope(UserAccessesGlobalScope) co pomija ograniczenia dostępu użytkownika. GetAmountDueAttr używa standardowego Reservation::query() bez usuwania scope'ów. W praktyce zwracają tą samą wartość, ponieważ amount_due jest aliasem total_pending w modelu.


Kolory klientów

Każdy klient ma przypisany kolor do identyfikacji w harmonogramie. System posiada paletę 15 kolorów:

Kolor Hex
Niebieski #3B82F6
Czerwony #EF4444
Zielony #10B981
Żółty/Bursztynowy #F59E0B
Fioletowy #8B5CF6
Różowy #EC4899
Turkusowy #06B6D4
Limonkowy #84CC16
Pomarańczowy #F97316
Indygo #6366F1
Morski #14B8A6
Jasny fiolet #A855F7
Jasny zielony #22C55E
Ciepły żółty #EAB308
Jasny niebieski #0EA5E9

Automatyczne przypisanie

Kolor jest automatycznie losowany z palety przy automatycznym tworzeniu klienta (przez FindOrCreateClient podczas rezerwacji). Przy ręcznym tworzeniu przez API kolor nie jest generowany automatycznie — można go podać w żądaniu. Kolor nie może być czarny (#000 / #000000).


Wyszukiwanie i filtrowanie klientów

Dostępne filtry

Filtr Typ Opis
filter[search] Scope (częściowe dopasowanie) Szuka w: first_name, last_name, email, company_name, nip
filter[id] Exact Dokładne dopasowanie UUID
filter[is_company] Exact Tylko firmy (true) lub osoby fizyczne (false)
filter[is_active] Exact Tylko aktywni (true) lub nieaktywni (false)

Brak indywidualnych filtrów tekstowych

Filtry takie jak first_name, last_name, email, company_name, nip nie są dostępne jako osobne filtry. Wszystkie te pola są przeszukiwane łącznie przez filtr search (częściowe dopasowanie LIKE).

Zabezpieczenia wyszukiwania

Scope ScopeSearchFunc automatycznie escapuje znaki specjalne SQL (%, _) w wartości wyszukiwania, co zapobiega nieoczekiwanym wynikom i SQL injection.

Paginacja

Endpoint GET /api/clients obsługuje standardowe parametry paginacji zdefiniowane w paginationDefaultRequestRules().


Dostęp PIN dla klientów

Klienci mogą mieć kod PIN do logowania w systemie:

Pole Typ Opis
access_pin_hash string (ukryty) Zahashowany kod PIN — pole oznaczone jako $hidden w modelu
access_pin_created_at datetime Kiedy PIN został utworzony
access_pin_request_id string (UUID) Powiązanie z żądaniem PIN (AccessPinRequest)
access_enabled boolean Czy dostęp PIN jest włączony
has_access_pin boolean (obliczany) Czy klient posiada PIN (access_pin_hash !== null)

Więcej o PIN

Szczegóły systemu PIN znajdują się w dokumentacji domeny Dostęp (Access).


Automatyczne tworzenie klientów

Klasa FindOrCreateClient zarządza automatycznym tworzeniem i rozpoznawaniem klientów przy rezerwacji:

flowchart TD
    A[Dane klienta z rezerwacji] --> B{is_company=true<br>i NIP podany?}
    B -->|Tak| C{Istnieje klient<br>z tym NIP?}
    C -->|Tak| D[Aktualizuj puste pola]
    C -->|Nie| E{Istnieje klient<br>z tym emailem?}
    B -->|Nie| E
    E -->|Tak| D
    E -->|Nie| F[Utwórz nowego klienta]
    F --> G[Wylosuj kolor]
    G --> H[Zwróć klienta]
    D --> H
  1. Sprawdzenie NIP (dla firm) — jeśli istnieje klient z tym NIP, aktualizuje puste pola
  2. Sprawdzenie email — jeśli istnieje klient z tym emailem, aktualizuje puste pola
  3. Tworzenie nowego — jeśli nie znaleziono dopasowania, tworzy nowego z losowym kolorem

Aktualizacja danych (UpdateClientIfNeeded)

System uzupełnia wyłącznie puste pola z określonej listy:

  • first_name, last_name, mobile_phone
  • company_name, nip, regon
  • street, street_number, postal_code, city
  • commune, district, voivodeship, teryt_data

Email NIE jest aktualizowany — służy wyłącznie do identyfikacji klienta.

Dodatkowo, jeśli klient nie był firmą (is_company=false), a nowe dane wskazują is_company=true, status zostanie zmieniony na firmę.


Odpowiedź API (Resource)

Endpoint zwraca następujące pola w odpowiedzi JSON:

Pole Typ Opis
id string (UUID) Identyfikator klienta
first_name string Imię
last_name string Nazwisko
email string Email
mobile_phone string\|null Telefon
is_company boolean Czy firma
company_name string\|null Nazwa firmy
nip string\|null NIP
regon string\|null REGON
street string\|null Ulica
street_number string\|null Numer budynku/lokalu
postal_code string\|null Kod pocztowy
city string\|null Miasto
commune string\|null Gmina
district string\|null Powiat
voivodeship string\|null Województwo
teryt_data array\|null Dane adresowe TERYT
color string\|null Kolor HEX klienta
is_active boolean Czy aktywny
vat_rate string Stawka VAT (domyślnie 23)
vat_rate_label string Etykieta stawki VAT
vat_note string\|null Notatka VAT
total_reservations integer Liczba rezerwacji
is_in_use boolean Czy ma rezerwacje
access_pin_hash string\|null Hash PIN (pole ukryte w modelu)
access_pin_created_at string\|null Data utworzenia PIN
access_pin_request_id string\|null ID żądania PIN
access_enabled boolean Czy dostęp PIN włączony
created_at string\|null Data utworzenia (format: Y-m-d H:i:s)
updated_at string\|null Data aktualizacji (format: Y-m-d H:i:s)
total_spent float Suma wydatków
total_refunded float Suma zwrotów
total_pending float Kwoty oczekujące
total_balance float Saldo (wydane - zwrócone)
amount_due float Zaległości (alias total_pending)
can object Uprawnienia użytkownika (patrz niżej)

Obiekt can (uprawnienia)

Klucz Typ Opis
view boolean Czy użytkownik może wyświetlić klienta
create boolean Czy użytkownik może tworzyć klientów
update boolean Czy użytkownik może edytować klienta

Ukrywanie uprawnień

Pole can jest usuwane z odpowiedzi dla nieuwierzytelnionych użytkowników (UNALLOWED_NO_AUTH_KEYS). W praktyce domena Client nie ma endpointów publicznych, więc ten mechanizm ma znaczenie tylko w kontekście ewentualnego rozszerzenia.

Relacje

ClientResource::RELATIONS jest pustą tablicą — klient nie ładuje automatycznie żadnych relacji zagnieżdżonych w odpowiedzi.


Uprawnienia

Middleware (role)

Rola Podgląd Tworzenie Edycja Zmiana statusu
Administrator Tak Tak Tak Tak
Supervisor Tak Tak Tak Tak
Employee Tak Tak Tak Tak

Middleware: role:ADMIN|SUPERVISOR|EMPLOYEE — stosowany do wszystkich metod kontrolera (index, show, store, update, changeState).

Policy (ClientPolicy)

Metoda Logika Opis
view(User, Client) Zawsze true Każdy uwierzytelniony użytkownik z odpowiednią rolą może przeglądać
create(User) Zawsze true Każdy uwierzytelniony użytkownik z odpowiednią rolą może tworzyć
update(User, Client) Zawsze true Każdy uwierzytelniony użytkownik z odpowiednią rolą może edytować

Dwupoziomowa autoryzacja

Autoryzacja opiera się na dwóch warstwach: middleware sprawdza role (ADMIN|SUPERVISOR|EMPLOYEE), a policy pozwala na wszystkie operacje w ramach tych ról. Efektywnie — dostęp mają tylko użytkownicy z jedną z trzech ról.


Eventy i efekty uboczne

Brak dedykowanych eventów

Domena Client nie posiada dedykowanych eventów, jobów, notyfikacji ani listenerów. Observer (ClientObserver) jest zarejestrowany, ale pusty — zarezerwowany na przyszłe rozszerzenia.

Model Client używa traitu Notifiable z Laravel, co umożliwia wysyłanie notyfikacji do klienta w przyszłości.


Znane ograniczenia i TODO

  1. Pusty observerClientObserver jest zarezerwowany, ale nie implementuje żadnych hooków
  2. Pusty enumClientEnumService jest zarezerwowany na przyszłe statusy/typy klientów
  3. Brak soft delete — klienci nie są usuwani miękko (brak SoftDeletes), zamiast tego używana jest flaga is_active
  4. Brak endpointu DELETE — klienci nie mogą być usuwani przez API, jedynie dezaktywowani
  5. GetAmountDueAttr bez GlobalScopeGetAmountDueAttr nie używa withoutGlobalScope w przeciwieństwie do GetTotalPendingAttr, co może prowadzić do różnic w wynikach jeśli UserAccessesGlobalScope jest aktywny (w praktyce amount_due w modelu jest aliasem total_pending, więc różnica nie jest widoczna w API)