Cenniki (Pricing)¶
Ostatnia aktualizacja dokumentacji: 26 lutego 2026 Stan synchronizacji z kodem: Zsynchronizowany
Elastyczne ceny z pelna obsluga VAT — od stawek domyslnych po indywidualne cenniki dla stalych klientow.
1. Opis ogolny¶
Domena Cennikow zarzadza polityka cenowa obiektow i zajec. To jak ksiega cen w recepcji — ale z automatycznym obliczaniem VAT, mozliwoscia ustalania indywidualnych stawek dla klientow i historia zmian cen.
System obsluguje dwa typy cennikow:
- Cennik godzinowy — dla obiektow wynajmowanych na godziny (sale, boiska)
- Cennik za zajecia — dla aktywnosci grupowych (fitness, warsztaty)
2. Architektura domeny¶
2.1 Modele danych¶
| Model | Klasa | Opis |
|---|---|---|
| Pricing | Domain\Pricing\Model\Pricing | Cennik przypisany polimorficznie do obiektu (Item) lub aktywnosci (Activity), opcjonalnie do klienta |
2.2 Diagram relacji¶
erDiagram
Pricing ||--o| Client : "cennik indywidualny"
Pricing }o--|| Item : "priceable (morph)"
Pricing }o--|| Activity : "priceable (morph)"
Reservation ||--o| Pricing : "pricing_id (FK)"
Pricing {
uuid id PK
string priceable_type
uuid priceable_id
decimal value
uuid client_id FK
string vat_rate_override
boolean is_active
datetime created_at
datetime updated_at
} 2.3 Struktura tabeli w bazie danych¶
Tabela: pricings
Migracja: 0001_01_01_000018_create_pricings.php
| Kolumna | Typ | Nullable | Default | Opis |
|---|---|---|---|---|
id | uuid (PK) | Nie | auto | Identyfikator cennika |
priceable_type | string | Nie | — | Typ modelu (FQCN): Domain\Item\Model\Item lub Domain\Activity\Model\Activity |
priceable_id | uuid | Nie | — | ID modelu (Item lub Activity) |
value | decimal(12,2) | Nie | — | Cena netto w PLN |
client_id | uuid (FK) | Tak | NULL | ID klienta (cennik indywidualny); NULL = cennik domyslny |
vat_rate_override | string | Nie | '23' | Nadpisanie stawki VAT |
is_active | boolean | Nie | true | Czy cennik jest aktywny |
created_at | timestamp | Tak | — | Data utworzenia |
updated_at | timestamp | Tak | — | Data ostatniej modyfikacji |
Indeksy:
| Nazwa | Kolumny | Typ |
|---|---|---|
pricings_effective_lookup | priceable_type, priceable_id, client_id, is_active, created_at | Kompozytowy |
Klucze obce:
| Kolumna | Referencja | On Delete |
|---|---|---|
client_id | clients.id | CASCADE |
Referencja z rezerwacji
Tabela reservations zawiera kolumne pricing_id (UUID, nullable) z kluczem obcym do pricings.id i regula onDelete('set null'). Dzieki snapshotowi cennika w reservationable_snapshot dane cenowe sa zachowane nawet po usunieciu cennika.
2.4 Atrybuty wyliczane (Accessors)¶
| Atrybut | Typ | Opis | Logika |
|---|---|---|---|
effective_vat_rate | string | Efektywna stawka VAT | vat_rate_override > klient vat_rate > '23' (domyslna) |
effective_vat_rate_numeric | float\|null | Stawka VAT jako liczba | 23.00, 8.00, 5.00, 0.00, null (dla ZW/NP) |
effective_vat_rate_label | string | Etykieta stawki VAT | "23%", "8%", "Zwolniony", "Nie podlega" |
is_default_pricing | bool | Czy cennik domyslny | true gdy client_id === null |
price_unit | string | Jednostka cenowa | 'hour' dla Item, 'activity' dla Activity |
price_unit_label | string | Etykieta jednostki | 'za godzine' lub 'za zajecia' |
value_gross | float | Cena brutto | value * (1 + stawka/100) lub value dla ZW/NP |
2.5 Scopes (zakresy zapytan)¶
| Scope | Metoda | Opis |
|---|---|---|
default() | whereNull('client_id') | Cenniki domyslne (ogolne) |
forClient($clientId) | where('client_id', $clientId) | Cenniki dla konkretnego klienta |
active() | where('is_active', true) | Tylko aktywne cenniki |
assignedClient() | whereNotNull('client_id') | Cenniki przypisane do klienta |
2.6 Relacje modelu¶
| Relacja | Typ | Model docelowy | Opis |
|---|---|---|---|
priceable() | MorphTo | Item lub Activity | Obiekt/aktywnosc, do ktorej przypisany jest cennik |
client() | BelongsTo | Client | Klient (dla cennika indywidualnego) |
2.7 Traity modelu¶
| Trait | Opis |
|---|---|
HasUuids | Automatyczne generowanie UUID jako klucz glowny |
TraitPricing | Metody domenowe (deaktywacja, obliczenia, observery) |
3. Endpointy API¶
Prefix: /api/pricings Middleware: auth:api, role:admin|supervisor|employee Plik routow: routes/api/auth/pricing.php
3.1 Lista cennikow¶
GET /api/pricings¶
- Opis: Pobiera liste cennikow z filtrami i paginacja
- Autoryzacja: Role:
admin,supervisor,employee - Controller:
PricingController@index->IndexPricingController
Query Parameters (filtry):
| Parametr | Typ | Wymagane | Reguly walidacji | Opis |
|---|---|---|---|---|
filter[priceable_type] | string | Nie | sometimes\|string | Typ modelu (FQCN klasy Item lub Activity) |
filter[priceable_id] | uuid | Nie | sometimes\|uuid | ID obiektu/aktywnosci |
filter[client_id] | uuid\|null | Nie | sometimes\|nullable\|uuid | ID klienta |
filter[is_active] | boolean | Nie | sometimes\|boolean | Status aktywnosci |
filter[assigned_client] | — | Nie | — | Scope: tylko cenniki przypisane do klienta |
page | integer | Nie | Standardowe reguły paginacji | Numer strony |
per_page | integer | Nie | Standardowe reguły paginacji | Elementy na strone |
Dozwolone filtry (Spatie QueryBuilder):
| Filtr | Typ | Opis |
|---|---|---|
id | exact | Filtrowanie po ID cennika |
priceable_type | exact | Filtrowanie po typie modelu |
priceable_id | exact | Filtrowanie po ID modelu |
client_id | exact | Filtrowanie po ID klienta |
is_active | exact | Filtrowanie po statusie |
assigned_client | scope | Cenniki z przypisanym klientem |
Relacje ladowane w odpowiedzi:
priceable— obiekt (Item) lub aktywnosc (Activity)client— klient (jesli cennik indywidualny)
Response (sukces, 200):
{
"data": [
{
"id": "uuid-cennika",
"priceable_type": "Domain\\Item\\Model\\Item",
"priceable_id": "uuid-obiektu",
"value": 100.00,
"value_gross": 123.00,
"client_id": null,
"vat_rate_override": "23",
"effective_vat_rate": "23",
"effective_vat_rate_label": "23%",
"is_active": true,
"is_default": true,
"price_unit": "hour",
"price_unit_label": "za godzine",
"created_at": "2026-02-26 10:00:00",
"updated_at": "2026-02-26 10:00:00",
"can": {
"view": true,
"create": true,
"update": true
},
"priceable": { "..." : "dane obiektu/aktywnosci" },
"client": null
}
],
"links": { "..." : "linki paginacji" },
"meta": { "..." : "metadane paginacji" }
}
3.2 Tworzenie cennika¶
POST /api/pricings¶
- Opis: Tworzy nowy cennik dla obiektu lub aktywnosci
- Autoryzacja: Role:
admin,supervisor,employee+ Policycreate(sprawdza przynaleznosc do organizacji) - Controller:
PricingController@store->StorePricingController
Body (Request):
| Pole | Typ | Wymagane | Reguly walidacji | Opis |
|---|---|---|---|---|
priceable_type | string | Warunkowo | sometimes\|nullable\|required_with:priceable_id\|string\|in:Domain\Item\Model\Item,Domain\Activity\Model\Activity | Typ modelu |
priceable_id | uuid | Warunkowo | sometimes\|nullable\|required_with:priceable_type\|uuid\|CheckExistPriceableIdRule | ID modelu |
value | numeric | Tak | required\|numeric\|min:0 | Cena netto (PLN) |
client_id | uuid | Nie | sometimes\|nullable\|uuid\|exists:clients,id | ID klienta (cennik indywidualny) |
vat_rate_override | string | Tak | required\|in:23,8,5,0,zw,np | Stawka VAT |
is_active | boolean | Nie | sometimes\|boolean | Czy aktywny (domyslnie true) |
Custom Validation Rule — CheckExistPriceableIdRule:
Sprawdza czy:
priceable_typejest podklasaEloquent\Model- Istnieje rekord o podanym
priceable_idw tabeli danego modelu
Przyklad request:
{
"priceable_type": "Domain\\Item\\Model\\Item",
"priceable_id": "550e8400-e29b-41d4-a716-446655440000",
"value": 100.00,
"vat_rate_override": "23",
"client_id": null,
"is_active": true
}
Response (sukces, 200):
Logika biznesowa przy tworzeniu:
- Walidacja danych wejsciowych
- Sprawdzenie policy
create(przynaleznosc do organizacji) - Wywolanie
Pricing::create($validated) - Observer
creatingautomatycznie: - Ustawia
is_active = truejesli nie podano - Dezaktywuje istniejace aktywne cenniki dla tej samej kombinacji (priceable + client)
Custom messages walidacji:
| Pole | Regula | Klucz tlumaczenia |
|---|---|---|
priceable_type | required_with | validation.custom.store_pricing.priceable_type_required_with |
priceable_type | in | validation.custom.store_pricing.priceable_type_invalid |
priceable_id | required_with | validation.custom.store_pricing.priceable_id_required_with |
value | required | validation.custom.store_pricing.value_required |
value | min | validation.custom.store_pricing.value_min |
client_id | exists | validation.custom.store_pricing.client_id_exists |
vat_rate_override | required | validation.custom.store_pricing.vat_rate_override_required |
vat_rate_override | in | validation.custom.store_pricing.vat_rate_override_invalid |
3.3 Zmiana stanu cennika¶
PATCH /api/pricings/{pricingId}/change-state¶
- Opis: Przelacza stan
is_activecennika (aktywacja/dezaktywacja) - Autoryzacja: Role:
admin,supervisor,employee+ Policyupdate - Controller:
PricingController@changeState
Parametry URL:
| Parametr | Typ | Opis |
|---|---|---|
pricingId | uuid | ID cennika do zmiany stanu |
Response (sukces, 200):
Logika biznesowa:
- Sprawdzenie policy
update(przynaleznosc do organizacji) - Przelaczenie pola
is_activena przeciwna wartosc - Observer
updating— jesli cennik jest aktywowany (is_activezmienia sie natrue): - Dezaktywuje inne aktywne cenniki dla tej samej kombinacji (priceable + client), wykluczajac biezacy
4. Logika biznesowa¶
4.1 Glowne procesy¶
Scenariusz 1: Ustawienie cennika dla sali¶
flowchart LR
A[Wybiera obiekt] --> B[Dodaje cennik]
B --> C[Podaje cene netto]
C --> D[Wybiera stawke VAT]
D --> E[Zapisuje]
E --> F[System oblicza brutto] - Administrator otwiera obiekt "Sala konferencyjna"
- Klika "Dodaj cennik"
- Wpisuje cene netto: 100 zl/godz.
- Wybiera stawke VAT: 23%
- Zapisuje — system automatycznie oblicza brutto: 123 zl/godz.
- Cennik jest aktywny od razu
Scenariusz 2: Indywidualna stawka dla stalego klienta¶
- Firma "ABC Sp. z o.o." jest stalym klientem
- Administrator tworzy cennik dla tej firmy
- Cena netto: 80 zl/godz. (rabat 20%)
- VAT: 23%
- Od teraz przy rezerwacjach tej firmy — automatycznie stawka 80 zl
Scenariusz 3: Zmiana ceny obiektu¶
- Administrator tworzy nowy cennik: 120 zl/godz.
- System automatycznie dezaktywuje poprzedni cennik (100 zl/godz.)
- Nowe rezerwacje — po nowej cenie
- Istniejace rezerwacje — zachowuja stara cene (snapshot)
- Historia cen jest zachowana
Scenariusz 4: Obiekt staje sie bezplatny (is_paid = false)¶
- Gdy
is_paidna obiekcie/aktywnosci zmienia sie nafalse - Klasa
SetZeroForIsPaidDirtyFieldautomatycznie tworzy cennik: value = 0is_active = trueclient_id = nullvat_rate_override = '23'- Observer
creatingdezaktywuje poprzednie cenniki
4.2 Reguly biznesowe¶
- Tylko jeden aktywny cennik — dla kazdej kombinacji (obiekt/aktywnosc + klient) moze istniec tylko jeden aktywny cennik
- Automatyczna dezaktywacja — tworzenie nowego cennika lub aktywacja istniejacego automatycznie dezaktywuje pozostale dla tej samej kombinacji
- Hierarchia cennikow — cennik kliencki ma priorytet nad domyslnym
- Cena minimalna — wartosc cennika nie moze byc ujemna (
min:0) - Snapshot w rezerwacji — przy tworzeniu rezerwacji cena jest "zamrazana" w
reservationable_snapshot - Dozwolone typy — cenniki moga byc tworzone tylko dla modeli
ItemiActivity - Kasowanie kaskadowe — usuniecie klienta kasuje jego cenniki (
onDelete: cascade) - Zachowanie referencji — usuniecie cennika ustawia
pricing_idw rezerwacji naNULL(onDelete: set null)
4.3 Klasy akcji (Action)¶
Controllery¶
| Klasa | Opis |
|---|---|
IndexPricingController | Pobiera liste cennikow z filtrami (Spatie QueryBuilder) |
StorePricingController | Tworzy nowy cennik (Pricing::create) |
Atrybuty¶
| Klasa | Opis |
|---|---|
GetValueGrossAttr | Oblicza cene brutto: value * (1 + vatRate/100) lub value dla ZW/NP |
Logika biznesowa (Other)¶
| Klasa | Opis |
|---|---|
CreatingPricingObserver | Hook creating: ustawia is_active = true domyslnie, dezaktywuje inne cenniki |
UpdatingPricingObserver | Hook updating: jesli is_active zmienia sie na true, dezaktywuje inne cenniki (wykluczajac biezacy) |
DeactivateActivePricings | Dezaktywuje aktywne cenniki dla tej samej kombinacji (priceable_type + priceable_id + client_id) |
GetEffectivePricing | Pobiera efektywny cennik: cennik kliencki > cennik domyslny > null |
SetZeroForIsPaidDirtyField | Tworzy cennik z wartoscia 0 gdy obiekt/aktywnosc staje sie bezplatny |
TheLowestModelPricing | Oblicza najnizsza cene domyslna z ostatnich 30 dni (dyrektywa Omnibus) |
4.4 Observer modelu¶
Klasa: Domain\Pricing\Observer\PricingObserver
Rejestracja: Atrybut #[ObservedBy(PricingObserver::class)] na modelu Pricing
| Hook | Delegacja | Logika |
|---|---|---|
creating | CreatingPricingObserver | 1. Ustawia is_active = true jesli null. 2. Dezaktywuje inne aktywne cenniki (bez wykluczenia biezacego) |
updating | UpdatingPricingObserver | Jesli is_active zmienia sie na true — dezaktywuje inne cenniki (wykluczajac biezacy) |
Szczegoly dezaktywacji (DeactivateActivePricings):
Zapytanie:
WHERE priceable_type = ? AND priceable_id = ?
AND is_active = true
AND (client_id = ? | client_id IS NULL) -- w zaleznosci od cennika
[AND id != ? -- jesli excludeCurrent = true]
SET is_active = false
5. Autoryzacja i uprawnienia¶
5.1 Middleware (routy)¶
Wszystkie endpointy cennikow wymagaja:
auth:api— uwierzytelnianie JWTrole:admin|supervisor|employee— dostep tylko dla personelu
5.2 Policy — PricingPolicy¶
Klasa bazowa: Support\Policy\BasePolicy
Metoda before(): Administratorzy (admin) maja pelny dostep do wszystkich operacji — policy nie jest dalej sprawdzane.
| Metoda | Supervisor | Employee | Warunek |
|---|---|---|---|
view | Tak | Tak | Uzytkownik nalezy do organizacji obiektu/aktywnosci |
create | Tak | Warunkowo | Supervisor: nalezy do organizacji. Employee: jest zarzadca obiektu (ItemManager) |
update | Tak | Warunkowo | Supervisor: nalezy do organizacji. Employee: jest zarzadca obiektu |
Szczegoly metody create:
- Sprawdza czy obiekt (
priceable) istnieje - Pobiera
organization_idz obiektu/aktywnosci - Supervisor — sprawdza przynaleznosc do organizacji
- Employee — sprawdza czy jest
ItemManagerdla danego obiektu
Szczegoly metody update:
- Rozwiazuje
organization_idprzez relacje polimorficzna (priceable) - Uzywa
->withoutGlobalScopes()aby ominac ewentualne filtry globalne - Supervisor — sprawdza przynaleznosc do organizacji
- Employee — sprawdza
isItemManagerprzez relacje polimorficzna
6. Stawki VAT¶
System obsluguje wszystkie polskie stawki VAT:
| Stawka | Kod | Wartosc numeryczna | Etykieta | Przyklad uzycia |
|---|---|---|---|---|
| 23% | 23 | 23.00 | "23%" | Wynajem sal, sprzetu |
| 8% | 8 | 8.00 | "8%" | Uslugi sportowe |
| 5% | 5 | 5.00 | "5%" | Ksiazki, czasopisma |
| 0% | 0 | 0.00 | "0%" | Eksport uslug |
| ZW | zw | null | "Zwolniony" | Uslugi edukacyjne |
| NP | np | null | "Nie podlega" | Dotacje, darowizny |
Enum: Domain\Pricing\Service\VatEnumService (backed string enum)
Metody statyczne:
| Metoda | Opis | Zwraca |
|---|---|---|
getNumericRate(?string) | Zwraca wartosc liczbowa stawki | float\|null |
getLabel(string) | Zwraca etykiete stawki | string |
getAllRates() | Zwraca liste wszystkich kodow stawek | string[] |
isVatApplicable(string) | Czy VAT jest naliczany (nie ZW/NP) | bool |
Obliczanie ceny brutto
Dla stawek numerycznych: brutto = netto x (1 + stawka/100)
Dla ZW i NP: brutto = netto (VAT nie jest naliczany)
7. Hierarchia cennikow¶
System stosuje nastepujaca hierarchie przy pobieraniu ceny:
flowchart TD
A[Klient sklada rezerwacje] --> B{Ma cennik indywidualny?}
B -->|Tak| C[Uzyj cennika klienta]
B -->|Nie| D{Jest cennik domyslny?}
D -->|Tak| E[Uzyj cennika domyslnego]
D -->|Nie| F[Obiekt bezplatny] Klasa: GetEffectivePricing
- Cennik kliencki — jesli klient ma przypisany aktywny cennik indywidualny, uzyj go
- Cennik domyslny — jesli nie ma cennika klienckiego, uzyj domyslnego (aktywnego, bez
client_id) - Brak cennika — zwraca
null(obiekt bezplatny)
8. Jednostki cenowe¶
| Typ | price_unit | price_unit_label | Opis |
|---|---|---|---|
| Obiekt (Item) | hour | za godzine | Stawka godzinowa |
| Aktywnosc (Activity) | activity | za zajecia | Stawka za cale zajecia |
Przyklady
- Sala konferencyjna: 100 zl/godz. -> rezerwacja 2h = 200 zl netto
- Zajecia jogi: 30 zl/zajecia -> rezerwacja = 30 zl netto
9. Dyrektywa Omnibus¶
System automatycznie sledzi najnizsza cene z ostatnich 30 dni:
Klasa: TheLowestModelPricing
Enum: PricingEnumService::OMNIBUS_DAYS = '30'
Logika:
SELECT MIN(value) FROM pricings
WHERE priceable_type = ? AND priceable_id = ?
AND client_id IS NULL
AND created_at >= NOW() - 30 DAYS
timeline
title Historia cen obiektu
1 lutego : 100 zl
10 lutego : 90 zl (promocja)
20 lutego : 110 zl
Dzis : Najnizsza cena 30 dni = 90 zl Tylko cenniki domyslne
Obliczanie najnizszej ceny dotyczy wylacznie cennikow domyslnych (client_id IS NULL). Cenniki indywidualne nie sa brane pod uwage.
10. Snapshot cennika w rezerwacji¶
Przy tworzeniu rezerwacji system zapisuje pelny snapshot cennika w polu reservationable_snapshot['_pricing_snapshot']. Dzieki temu:
- Usuniecie cennika nie powoduje utraty danych rezerwacji
- Zmiana ceny nie wplywa na istniejace rezerwacje
- Audyt — zawsze wiadomo jaka byla cena jednostkowa w momencie rezerwacji
Zapisywane dane (w _pricing_snapshot)¶
| Pole | Opis |
|---|---|
id | UUID cennika |
value | Cena netto jednostkowa |
value_gross | Cena brutto jednostkowa |
vat_rate | Efektywna stawka VAT |
vat_rate_override | Nadpisanie stawki VAT (jesli bylo) |
is_client_pricing | true = cennik indywidualny |
client_id | UUID klienta (dla cennika indywidualnego) |
price_unit | hour lub activity |
price_unit_label | "za godzine" lub "za zajecia" |
Zgodnosc FK
Kolumna pricing_id w rezerwacji ma onDelete('set null'), wiec po usunieciu cennika wartosc staje sie NULL. Dzieki _pricing_snapshot w reservationable_snapshot dane sa nadal dostepne.
11. API Resource — struktura odpowiedzi¶
Klasa: Domain\Pricing\Resource\PricingResource
Relacje ladowane: priceable, client
| Pole JSON | Typ | Zrodlo |
|---|---|---|
id | string | $this->id |
priceable_type | string | $this->priceable_type |
priceable_id | string | $this->priceable_id |
value | float | $this->value (cena netto) |
value_gross | float | $this->value_gross (accessor — cena brutto) |
client_id | string\|null | $this->client_id |
vat_rate_override | string\|null | $this->vat_rate_override |
effective_vat_rate | string | $this->effective_vat_rate (accessor) |
effective_vat_rate_label | string | $this->effective_vat_rate_label (accessor) |
is_active | bool | $this->is_active |
is_default | bool | $this->is_default_pricing (accessor) |
price_unit | string | $this->price_unit (accessor) |
price_unit_label | string | $this->price_unit_label (accessor) |
created_at | string\|null | Format: Y-m-d H:i:s |
updated_at | string\|null | Format: Y-m-d H:i:s |
can | object | Uprawnienia uzytkownika (view, create, update) |
priceable | object\|null | Relacja — dane obiektu/aktywnosci |
client | object\|null | Relacja — dane klienta |
Pole can niedostepne publicznie
Dla uzytkownikow nieuwierzytelnionych pole can jest usuwane z odpowiedzi (UNALLOWED_NO_AUTH_KEYS).
12. Enumy¶
PricingEnumService¶
Klasa: Domain\Pricing\Service\PricingEnumService (backed string enum)
| Case | Wartosc | Opis |
|---|---|---|
OMNIBUS_DAYS | '30' | Liczba dni dla obliczania najnizszej ceny (Omnibus) |
ALLOWED_PRICEABLE_TYPE_ITEM | Domain\Item\Model\Item (FQCN) | Dozwolony typ: Obiekt |
ALLOWED_PRICEABLE_TYPE_ACTIVITY | Domain\Activity\Model\Activity (FQCN) | Dozwolony typ: Aktywnosc |
Metoda statyczna: getAllowedPriceableTypes() — zwraca tablice dozwolonych FQCN.
VatEnumService¶
Opisany w sekcji 6. Stawki VAT.
13. Powiazania z innymi domenami¶
Diagram powizan¶
graph LR
Pricing -->|morphTo| Item["Zasoby (Item)"]
Pricing -->|morphTo| Activity["Aktywnosci (Activity)"]
Pricing -->|belongsTo| Client["Klienci (Client)"]
Reservation["Rezerwacje"] -->|pricing_id FK| Pricing
Item -->|morphMany| Pricing
Activity -->|morphMany| Pricing
Client -->|hasMany| Pricing Tabela powizan¶
| Domena | Typ powiazania | Opis |
|---|---|---|
| Zasoby (Item) | Item.pricings() — MorphMany | Cenniki godzinowe dla obiektow. Item ma atrybuty current_pricing i lowest_pricing |
| Aktywnosci (Activity) | Activity.pricings() — MorphMany | Cenniki za zajecia. Activity ma atrybuty current_pricing i lowest_pricing |
| Klienci (Client) | Client.pricings() — HasMany | Cenniki indywidualne dla klientow. Kasowanie klienta kasuje jego cenniki |
| Rezerwacje (Reservation) | Reservation.pricing_id — FK | Cena rezerwacji obliczana z cennika; snapshot cennika zapisywany w reservationable_snapshot |
Wykorzystanie przez inne domeny¶
| Klasa | Domena | Uzywa | Opis |
|---|---|---|---|
GetCurrentPricingAttr | Item, Activity | Pricing model | Pobiera aktywny cennik domyslny |
GetLowestPricingAttr | Item, Activity | Pricing model | Pobiera najnizsza cene z 30 dni |
GetReservationPricing | Reservation | GetEffectivePricing | Pobiera cennik dla rezerwacji (kliencki lub domyslny) |
GenerateReservationItemTotalPrice | Reservation | Pricing model | Oblicza calkowita cene rezerwacji |
BuildReservationSnapshots | Reservation | Pricing model | Tworzy snapshot cennika w rezerwacji |
SlotCreator | Reservation | GetReservationPricing | Oblicza cene slotow rezerwacji |
CreateItem | Item | pricings()->create() | Tworzy cennik przy tworzeniu obiektu |
CreateActivity | Activity | pricings()->create() | Tworzy cennik przy tworzeniu aktywnosci |
14. Konfiguracja¶
Tworzenie cennika¶
| Pole | Opis | Wymagane |
|---|---|---|
| Obiekt/Aktywnosc | Do czego przypisany cennik | Tak |
| Cena netto | Kwota w zlotych | Tak |
| Stawka VAT | Jedna z 6 dostepnych | Tak |
| Klient | Dla cennika indywidualnego | Nie |
| Aktywny | Czy cennik jest uzywany | Nie (domyslnie true) |
Zasady aktywacji¶
Tylko jeden aktywny cennik
Dla kazdej kombinacji (obiekt/aktywnosc + klient) moze byc tylko jeden aktywny cennik.
Gdy aktywujesz nowy cennik, poprzedni zostaje automatycznie dezaktywowany.
Publiczny cennik¶
Obiekty i aktywnosci maja pole is_public_price_list (boolean). Gdy wlaczone, cennik jest widoczny dla niezalogowanych uzytkownikow.
15. Znane ograniczenia i TODO¶
| Ograniczenie | Opis |
|---|---|
| Brak endpointu DELETE | Nie ma mozliwosci calkowitego usuniecia cennika przez API — mozna go tylko dezaktywowac |
| Brak endpointu UPDATE | Nie ma mozliwosci edycji istniejacego cennika — trzeba utworzyc nowy |
| Brak walidacji duplikatow | System pozwala na tworzenie wielu cennikow o tej samej wartosci dla tej samej kombinacji |
| Brak audytu | Model Pricing nie korzysta ze Spatie Activity Log |
Wartosc biznesowa¶
Argumenty dla decydentow
- Zgodnosc z przepisami VAT — automatyczne obliczenia, rozne stawki
- Elastycznosc cenowa — rabaty dla stalych klientow bez recznych korekt
- Przejrzystosc — jedna aktywna cena, brak pomylek
- Zgodnosc z dyrektywa Omnibus — najnizsza cena z 30 dni
Przykladowe metryki¶
| Metryka | Wartosc |
|---|---|
| Obslugiwane stawki VAT | 6 |
| Cennikow na obiekt | Bez limitu |
| Okres Omnibus | 30 dni |