Harmonogramy (Schedule)¶
Ostatnia aktualizacja dokumentacji: 2026-02-26 Stan synchronizacji z kodem: Zsynchronizowany
1. Opis ogólny¶
Domena Harmonogramów odpowiada za wizualizację czasu w systemie ZAJMIJ.TO. Jest to centralny punkt agregacji danych z różnych źródeł — rezerwacje, blokady obiektów i sesje zajęć grupowych — prezentowanych w formie kalendarza z możliwością eksportu do PDF.
Model Schedule jest zapisem czasowym o polimorficznym powiązaniu (scheduleable), reprezentującym:
- slot rezerwacji (
ReservationSlot) — zarezerwowany czas przez klienta, - slot oczekującej rezerwacji (
WaitingReservationSlot) — tymczasowa blokada (soft-lock) podczas składania rezerwacji przez niezalogowanego użytkownika, - blokadę obiektu (
ItemBlock) — planowe lub awaryjne wyłączenie obiektu.
Sesje zajęć (ActivityItem) nie są przechowywane w tabeli schedules — są konwertowane do formatu harmonogramu w locie podczas pobierania danych (klasa ConvertActivityItemsToScheduleFormat).
Kluczowe cechy¶
- Harmonogram jest tylko do odczytu — nie posiada endpointów tworzenia, aktualizacji ani usuwania. Wpisy tworzone są automatycznie przez inne domeny (Reservation, Item).
- Obsługuje filtrowanie z uwzględnieniem hierarchii obiektów (obiekt nadrzędny i podrzędne w jednym zapytaniu).
- Eksport PDF obsługuje trzy widoki: miesięczny (kalendarz siatki), tygodniowy (oparty na minutach) i listowy (tabela z podsumowaniem statystyk).
- Dostęp publiczny (bez logowania) do endpointu
indexjest możliwy, lecz z ograniczeniami.
2. Architektura domeny¶
2.1 Modele danych¶
Schedule (Domain\Schedule\Model\Schedule)¶
Główny model harmonogramu. Każdy rekord reprezentuje jedno zdarzenie czasowe.
| Pole | Typ | Opis |
|---|---|---|
id | uuid (PK) | Identyfikator rekordu |
organization_id | uuid (FK, nullable) | Powiązana organizacja |
scheduleable_type | string | Klasa polimorficzna (ReservationSlot, WaitingReservationSlot, ItemBlock) |
scheduleable_id | uuid | ID rekordu polimorficznego |
item_id | uuid (FK) | Powiązany obiekt (items.id, cascade delete) |
start_at | datetime | Czas rozpoczęcia zdarzenia |
end_at | datetime | Czas zakończenia zdarzenia |
created_at | datetime | Data utworzenia rekordu |
updated_at | datetime | Data ostatniej aktualizacji |
Atrybuty wirtualne (computed):
| Atrybut | Typ | Opis |
|---|---|---|
scheduleable_color | string | Kolor heksadecymalny zdarzenia (obliczany przez GetSchedulableColorAttr) |
Relacje:
| Relacja | Typ | Opis |
|---|---|---|
scheduleable() | MorphTo | Polimorficzny właściciel harmonogramu |
organization() | BelongsTo | Organizacja powiązana z harmonogramem |
item() | BelongsTo | Obiekt, którego dotyczy harmonogram |
Publiczne filtry ($publicFilters):
Scope'y modelu:
| Scope | Opis |
|---|---|
scopeDatesBetween(string $dates) | Filtruje rekordy nakładające się z podanym zakresem dat |
scopeClientId(string $clientId) | Filtruje rekordy ReservationSlot powiązane z klientem |
2.2 Diagram relacji (Mermaid)¶
erDiagram
schedules {
uuid id PK
uuid organization_id FK
string scheduleable_type
uuid scheduleable_id
uuid item_id FK
datetime start_at
datetime end_at
}
organizations {
uuid id PK
string name
}
items {
uuid id PK
string name
uuid parent_id FK
}
reservation_slots {
uuid id PK
uuid reservation_id FK
}
waiting_reservation_slots {
uuid id PK
uuid waiting_reservation_id FK
}
item_blocks {
uuid id PK
string reason_type
}
activity_items {
uuid id PK
uuid activity_id FK
date date
time start_time
time end_time
}
schedules }o--|| organizations : "organization_id"
schedules }o--|| items : "item_id (cascade)"
schedules }o--o| reservation_slots : "scheduleable (morph)"
schedules }o--o| waiting_reservation_slots : "scheduleable (morph)"
schedules }o--o| item_blocks : "scheduleable (morph)" flowchart TD
subgraph "Źródła danych harmonogramu"
RS[ReservationSlot\nrezerwacja klienta]
WRS[WaitingReservationSlot\nsoft-lock niezalogowanego]
IB[ItemBlock\nblokada obiektu]
AI[ActivityItem\nsesja zajęć grupowych]
end
subgraph "Tabela schedules"
S[Schedule\n📅 start_at / end_at]
end
subgraph "Źródła wirtualne (bez DB)"
AIV[ActivityItem\nkonwertowane w locie]
end
RS -->|morphOne| S
WRS -->|morphOne| S
IB -->|morphOne| S
AI -->|ConvertActivityItemsToScheduleFormat| AIV
AIV -.->|wynik zapytania PDF| S 2.3 Struktura tabel w bazie danych¶
Tabela schedules (migracja: 0001_01_01_000022_create_schedules.php):
CREATE TABLE schedules (
id UUID PRIMARY KEY,
organization_id UUID NULL,
scheduleable_type VARCHAR(255) NOT NULL,
scheduleable_id UUID NOT NULL,
item_id UUID NOT NULL,
start_at DATETIME NOT NULL,
end_at DATETIME NOT NULL,
created_at DATETIME,
updated_at DATETIME,
INDEX (organization_id),
INDEX (scheduleable_type, scheduleable_id),
INDEX (item_id),
FOREIGN KEY (item_id) REFERENCES items(id) ON DELETE CASCADE,
FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE
);
3. Endpointy API¶
Uwierzytelnianie¶
| Endpoint | Wymaga logowania | Role |
|---|---|---|
GET /api/schedules | Nie (dostępny publicznie) | Brak ograniczenia roli |
GET /api/schedules/export-pdf | Tak | admin, supervisor, employee |
3.1 GET /api/schedules¶
Trasa: no-auth-schedules.index (publiczny) / schedules.index (zalogowany)
Opis: Pobiera listę harmonogramów z paginacją i filtrowaniem. Zwraca kolekcję ScheduleResource. Klucze scheduleable, scheduleable_id i can są usuwane z odpowiedzi dla niezalogowanych użytkowników.
Klasy: ScheduleController::index → TraitControllerSchedule::indexScheduleController → IndexScheduleController::__invoke
Parametry zapytania:
| Parametr | Typ | Opis |
|---|---|---|
filter[id] | string (UUID) | Filtruj po ID harmonogramu |
filter[item_id] | string (UUID) | Filtruj po ID obiektu (dokładne dopasowanie) |
filter[item_with_hierarchy] | string (UUID lub lista UUID) | Filtruj po obiekcie uwzględniając hierarchię (obiekt + rodzic + dzieci) |
filter[dates_between] | string | Zakres dat w formacie YYYY-MM-DD,YYYY-MM-DD lub YYYY-MM-DD\|YYYY-MM-DD |
sort | string | Sortowanie: start_at, end_at, created_at (domyślnie: start_at) |
page | int | Numer strony (paginacja) |
per_page | int | Liczba rekordów na stronę |
include | string | Relacje do załadowania: organization, scheduleable, item, item.childrens |
Ograniczenie dla niezalogowanych: Filtr dates_between ograniczony do maksymalnie 365 dni (klasa ScopeDatesBetweenFunc, stała MAX_DAYS_FOR_GUEST = 365).
Eager loading (domyślne): Poprzez ApplyScheduleAggregatesOrm automatycznie ładowane są: - scheduleable z morphWith: - ReservationSlot → ['refunds', 'reservation.client', 'reservation.latestNote'] - ItemBlock → ['creator.role'] - item.childrens
Odpowiedź (ScheduleResource):
{
"data": [
{
"id": "uuid",
"organization_id": "uuid|null",
"scheduleable_type": "Domain\\Reservation\\Model\\ReservationSlot",
"scheduleable_id": "uuid",
"item_id": "uuid",
"start_at": "2026-02-01 08:00:00",
"end_at": "2026-02-01 10:00:00",
"can_be_edit": false,
"created_at": "2026-01-01 12:00:00",
"updated_at": "2026-01-01 12:00:00",
"scheduleable_color": "#248bc7",
"can": {
"view": true,
"update": false,
"delete": false
},
"scheduleable": { ... },
"item": { ... },
"organization": { ... }
}
],
"meta": { ... },
"links": { ... }
}
Uwaga: Dla niezalogowanych użytkowników pola scheduleable, scheduleable_id i can są usuwane z odpowiedzi (tablica UNALLOWED_NO_AUTH_KEYS).
3.2 GET /api/schedules/export-pdf¶
Trasa: schedules.export-pdf (wymaga logowania)
Opis: Generuje plik PDF z harmonogramem w jednym z trzech widoków. Zwraca plik do pobrania (typ MIME application/pdf). Nazwa pliku: harmonogram_{widok}_{data}.pdf (np. harmonogram_lista_2026-02-26_123456.pdf).
Klasy: ScheduleController::exportPdf → TraitControllerSchedule::exportSchedulePdfController → ExportSchedulePdfController::__invoke
Role wymagane: admin, supervisor, employee
Parametry zapytania:
| Parametr | Typ | Wymagany | Opis |
|---|---|---|---|
view_type | string | Tak | Typ widoku: month, week, list |
month | string | Nie | Miesiąc w formacie YYYY-MM (domyślnie: bieżący miesiąc) |
include_filters_info | boolean | Nie | Czy dołączyć nagłówek z informacją o filtrach (domyślnie: true) |
orientation | string | Nie | Orientacja strony: portrait, landscape (domyślnie: landscape dla month/week, portrait dla list) |
hide_client_data | boolean | Nie | Ukryj dane klientów w PDF (imiona, nazwiska) |
filter[item_id] | string (UUID) | Warunkowo | Filtruj po obiekcie (wymagany co najmniej jeden filtr) |
filter[client_id] | string (UUID) | Warunkowo | Filtruj po kliencie (wymagany co najmniej jeden filtr) |
filter[activity_id] | string (UUID) | Warunkowo | Filtruj po zajęciach (wymagany co najmniej jeden filtr) |
filter[hide_client_data] | boolean | Nie | Alternatywna lokalizacja parametru ukrycia danych klientów |
filter[dates_between] | string | Nie | Zakres dat YYYY-MM-DD,YYYY-MM-DD (max 31 dni) |
Reguła filtrów: Co najmniej jeden z filtrów filter[item_id], filter[client_id] lub filter[activity_id] jest wymagany. Jeśli żaden nie jest podany, walidacja zwraca błąd filter_required dla wszystkich trzech pól.
Reguła zakresu dat: Filtr filter[dates_between] ograniczony do 31 dni (klasa DateRangeBetweenRule i ValidateDateRange).
Priorytet zakresu dat: 1. filter[dates_between] — jeśli podany 2. month — jeśli podany (oblicza pełny miesiąc) 3. Domyślnie: bieżący miesiąc
Szablony Blade (barryvdh/laravel-dompdf):
view_type | Plik szablonu |
|---|---|
month | schedule.pdf.month |
week | schedule.pdf.week |
list | schedule.pdf.list |
Format pliku PDF: - Papier: A4 - Czcionka: DejaVu Sans (obsługa polskich znaków) - Orientacja: konfigurowalna
4. Logika biznesowa¶
4.1 Główne procesy¶
Pobieranie listy harmonogramów (IndexScheduleController)¶
flowchart TD
A[GET /api/schedules] --> B[Walidacja parametrów\nIndexScheduleControllerRequest]
B --> C[IndexScheduleController::__invoke]
C --> D[Budowanie QueryBuilder\nz filtrami i sortowaniem]
D --> E[ApplyScheduleAggregatesOrm\ndefaultSort + eager loading]
E --> F{Filtr dates_between?}
F -->|Tak| G[ScopeDatesBetweenFunc\nwhere start_at <= to\nAND end_at >= from]
F -->|Nie| H[Brak filtra dat]
G --> I{Niezalogowany\ni > 365 dni?}
I -->|Tak| J[DateRangeExceedsLimitException]
I -->|Nie| K[Zwrot ScheduleResource]
H --> K Filtry dostępne w IndexScheduleController:
| Filtr | Implementacja | Opis |
|---|---|---|
id | AllowedFilter::exact('id') | Dokładne dopasowanie UUID |
item_id | AllowedFilter::exact('item_id') | Dokładne dopasowanie ID obiektu |
item_with_hierarchy | AllowedFilter::callback → ScopeItemWithHierarchyFunc | Rozszerzone dopasowanie po hierarchii obiektów |
dates_between | AllowedFilter::scope('dates_between') → ScopeDatesBetweenFunc | Zakres dat (nakładające się zdarzenia) |
Logika ScopeItemWithHierarchyFunc: Gdy podany jest item_id, system wyszukuje: 1. Sam podany obiekt 2. Jego obiekt nadrzędny (jeśli parent_id nie jest null) 3. Wszystkie jego obiekty podrzędne (where parent_id = item_id)
Następnie filtruje schedule po wszystkich znalezionych ID.
Generowanie eksportu PDF (ExportSchedulePdfController)¶
flowchart TD
A[GET /api/schedules/export-pdf] --> B[Walidacja\nExportSchedulePdfControllerRequest]
B --> C[ParseScheduleExportRequest\nrozparsowanie parametrów]
C --> D[ValidateDateRange\nmaks. 31 dni]
D --> E{Podano activity_id?}
E -->|Tak| F[FetchActivityItemsForExport\nAND ConvertActivityItemsToScheduleFormat]
E -->|Nie| G[FetchSchedulesForExport\nschedules + opcjonalnie activity_items dla client_id]
F --> H[BuildSchedulePdfFilterInfo\nnagłówek PDF]
G --> H
H --> I[BuildSchedulePdfData]
I --> J{view_type}
J -->|month| K[BuildMonthViewPdfData\nsiatkowy kalendarz]
J -->|week| L[BuildWeekViewMinuteBasedPdfData\noparty na minutach]
J -->|list| M[BuildListViewPdfData\ntabela + statystyki]
K --> N[GenerateSchedulePdf\nDomPDF → plik .pdf]
L --> N
M --> N Logika FetchSchedulesForExport: Pobiera harmonogramy z tabeli schedules. Gdy podano client_id, dodatkowo pobiera ActivityItem powiązane z rezerwacjami klienta i konwertuje je na obiekty Schedule (poprzez ConvertActivityItemsToScheduleFormat), a następnie scala obie kolekcje i sortuje po start_at.
Konwersja ActivityItem do formatu Schedule¶
ConvertActivityItemsToScheduleFormat tworzy tymczasowe obiekty Schedule z danych ActivityItem bez zapisu do bazy danych: - schedule->id = activityItem->id - schedule->start_at = data + start_time - schedule->end_at = data + end_time - schedule->scheduleable_type = ActivityItem::class - schedule->scheduleable_id = activityItem->id - Relacja organization = pobrana z activityItem->activity->organization
4.2 Reguły biznesowe¶
-
Harmonogram jest tylko do odczytu. Nie istnieją endpointy
POST,PUT,PATCHaniDELETEdla domeny Schedule. Wszystkie wpisy są tworzone przez inne domeny. -
Zdarzenia
WaitingReservationSlotsą widoczne w harmonogramie (kolor pomarańczowy), ale nie są renderowane w eksporcie PDF.FormatScheduleEventzwracanulldlascheduleable_type === WaitingReservationSlot::class. -
Sesje zajęć (
ActivityItem) nie mają wpisów w tabelischedules. Są pobierane bezpośrednio z tabeliactivity_itemsi konwertowane na formatSchedulew locie podczas eksportu PDF. -
Widok tygodniowy PDF obejmuje całą dobę (00:00–24:00). Konfiguracja
WeekViewPdfConfig:DAY_START_HOUR = 0,DAY_END_HOUR = 24,MINUTES_PER_PAGE = 1440. Jeden dzień = jedna strona PDF. -
Eksport PDF wymaga filtra. Co najmniej jeden z
filter[item_id],filter[client_id]lubfilter[activity_id]musi być podany. -
Eksport PDF ograniczony do 31 dni. Walidacja odbywa się na dwóch poziomach:
DateRangeBetweenRule(walidacja requesta) iValidateDateRange(walidacja w kontrolerze akcji). -
Kolory zdarzeń są obliczane przez
GetSchedulableColorAttr: ReservationSlot→ kolor klienta (reservation.client.color) lub domyślny#248bc7WaitingReservationSlot→ stały#F59E0B(pomarańczowy)-
Inne (w tym
ItemBlock) → stały#EF4444(czerwony) -
Nakładające się zdarzenia w widoku tygodniowym są obsługiwane przez
DetectOverlapsAndLayout, który przydziela kolumny (column,total_columns) do każdego zdarzenia, aby uniknąć wizualnego nakrywania.
4.3 Walidacje¶
IndexScheduleControllerRequest¶
Korzysta z paginationDefaultRequestRules() (paginacja) i publicFilterRequestRules(Schedule::class) (filtry: id, item_id, organization_id, dates_between). Dostępna publicznie (authorize(): true).
ExportSchedulePdfControllerRequest¶
| Pole | Reguła | Opis |
|---|---|---|
view_type | required\|string\|in:month,week,list | Wymagany typ widoku |
month | nullable\|string\|regex:/^\d{4}-\d{2}$/ | Opcjonalny miesiąc w formacie YYYY-MM |
include_filters_info | nullable\|boolean | Czy dołączyć sekcję filtrów w PDF |
orientation | nullable\|string\|in:portrait,landscape | Orientacja strony |
hide_client_data | nullable\|boolean | Ukryj dane klientów |
filter.item_id | nullable\|string\|exists:items,id | ID obiektu |
filter.client_id | nullable\|string\|exists:clients,id | ID klienta |
filter.activity_id | nullable\|string\|exists:activities,id | ID zajęć |
filter.hide_client_data | nullable\|boolean | Alternatywna lokalizacja hide_client_data |
filter.dates_between | nullable\|string\|regex:.../DateRangeBetweenRule(31) | Zakres dat maks. 31 dni |
Domyślne wartości (prepareForValidation): - orientation: landscape jeśli view_type === 'month', w przeciwnym wypadku portrait - include_filters_info: true
Reguła niestandardowa DateRangeBetweenRule: - Sprawdza, że zakres dat nie przekracza $maxDays (domyślnie 31) - Sprawdza, że data końcowa jest po dacie początkowej - Format wejściowy: YYYY-MM-DD,YYYY-MM-DD
5. Autoryzacja i uprawnienia¶
Policy SchedulePolicy¶
| Metoda | Warunek | Opis |
|---|---|---|
view(User, Schedule) | belongsToOrganization($user, $schedule->organization_id) | Widoczność tylko dla użytkowników organizacji |
create(User) | false | Tworzenie harmonogramów jest wyłączone |
update(User, Schedule) | false | Modyfikacja harmonogramów jest wyłączona |
Dostęp do endpointów¶
| Endpoint | Niezalogowany | Zalogowany (dowolna rola) | admin / supervisor / employee |
|---|---|---|---|
GET /api/schedules (no-auth) | Tak (ograniczony) | Tak | Tak |
GET /api/schedules (auth) | Nie | Tak | Tak |
GET /api/schedules/export-pdf | Nie | Nie | Tak |
Ograniczenia dla niezalogowanych (ScheduleResource): - Pola scheduleable, scheduleable_id, can są usuwane z odpowiedzi (stała UNALLOWED_NO_AUTH_KEYS). - Filtr dates_between ograniczony do 365 dni (klasa ScopeDatesBetweenFunc).
6. Eventy i efekty uboczne¶
Observer ScheduleObserver¶
Klasa ScheduleObserver (powiązana przez atrybut #[ObservedBy(ScheduleObserver::class)]) jest aktualnie pusta — nie zawiera żadnych hooków cyklu życia modelu.
Brak własnych eventów¶
Domena Schedule nie publikuje własnych eventów domenowych. Wpisy w harmonogramie są tworzone i usuwane przez inne domeny (Reservation, Item) jako efekt uboczny ich operacji.
Tworzenie wpisów (przez inne domeny)¶
Wpisy Schedule są tworzone automatycznie gdy: - Tworzona jest ReservationSlot — morficznie powiązana jako scheduleable - Tworzony jest WaitingReservationSlot — tymczasowa blokada podczas procesu rezerwacji - Tworzony jest ItemBlock — blokada obiektu przez pracownika
Usuwanie wpisów Schedule następuje kaskadowo przez usunięcie powiązanego Item (FK z ON DELETE CASCADE).
7. Notyfikacje¶
Domena Schedule nie generuje własnych notyfikacji. Jest domeną read-only skoncentrowaną na prezentacji danych i eksporcie.
8. Powiązania z innymi domenami¶
graph LR
SCH[Schedule]
RES[Reservation]
RS[ReservationSlot]
WRS[WaitingReservationSlot]
ITM[Item]
IB[ItemBlock]
ACT[Activity]
AI[ActivityItem]
CLI[Client]
ORG[Organization]
TRN[Trainer]
RS -->|morphOne| SCH
WRS -->|morphOne| SCH
IB -->|morphOne| SCH
ITM -->|item_id| SCH
ORG -->|organization_id| SCH
RES --> RS
ITM --> IB
ACT --> AI
AI -.->|konwersja w locie\ndo eksportu PDF| SCH
CLI -.->|filtr client_id\nw eksporcie| SCH
TRN -.->|dane w\nFormatScheduleEvent| SCH | Domena | Typ powiązania | Opis |
|---|---|---|
| Reservation | Pośrednie przez ReservationSlot | Każdy slot rezerwacji tworzy wpis w harmonogramie |
| Item | Bezpośrednie (item_id) | Harmonogram zawsze dotyczy konkretnego obiektu; cascade delete |
| Item (ItemBlock) | Polimorficzne (scheduleable) | Blokady obiektów są jednym z typów zdarzeń |
| Activity (ActivityItem) | Wirtualne (konwersja w locie) | Sesje zajęć pobierane z activity_items i konwertowane dla eksportu |
| Client | Pośrednie przez ReservationSlot.reservation.client | Dane klienta w kolorach i eksporcie PDF; filtr client_id |
| Organization | Bezpośrednie (organization_id) | Harmonogram należy do organizacji |
| Trainer | Pośrednie przez ActivityItem.trainer | Nazwisko trenera w eksporcie PDF jako "prowadzący" |
9. Konfiguracja¶
Enum WeekViewPdfConfig (Domain\Schedule\Enum\WeekViewPdfConfig)¶
Konfiguruje okno czasowe dla widoku tygodniowego PDF:
| Stała | Wartość | Opis |
|---|---|---|
DAY_START_HOUR | 0 | Godzina rozpoczęcia dnia (00:00) |
DAY_START_MINUTE | 0 | Minuta rozpoczęcia dnia |
DAY_END_HOUR | 24 | Godzina zakończenia dnia (24:00 = północ) |
DAY_END_MINUTE | 0 | Minuta zakończenia dnia |
MINUTES_PER_PAGE | 1440 | Liczba minut na jedną stronę PDF (1440 = cała doba) |
Wynikowy efekt: Widok tygodniowy PDF generuje jedną stronę na każdy dzień obejmującą całą dobę (00:00–24:00). Paginacja stron obliczana jest jako ceil(total_minutes / MINUTES_PER_PAGE).
Ograniczenia zakresu dat¶
| Kontekst | Maksymalny zakres | Klasa |
|---|---|---|
| Eksport PDF | 31 dni | ValidateDateRange, DateRangeBetweenRule |
| Pobieranie listy (niezalogowany) | 365 dni | ScopeDatesBetweenFunc::MAX_DAYS_FOR_GUEST |
| Pobieranie listy (zalogowany) | Brak ograniczenia | — |
Kolory zdarzeń¶
| Typ | Źródło koloru | Wartość domyślna |
|---|---|---|
ReservationSlot | reservation.client.color | #248bc7 |
WaitingReservationSlot | Stały | #F59E0B |
ItemBlock i inne | Stały | #EF4444 |
Zajęcia (ActivityItem) w PDF | activity.category.color | #6B7280 |
10. Znane ograniczenia i TODO¶
Ograniczenia¶
-
Brak endpointów zarządzania. Harmonogram jest wyłącznie do odczytu — wpisy można tylko przeglądać i eksportować. Tworzenie/edycja odbywa się wyłącznie przez domeny Reservation i Item.
-
ActivityItem nie jest w tabeli
schedules. Sesje zajęć grupowych są pobierane i konwertowane w locie. Oznacza to, że nie mają przypisanegoitem_idaniorganization_idw strukturze Schedule, co może powodowaćnullw tych polach podczas eksportu PDF. -
Widok tygodniowy PDF = cała doba.
WeekViewPdfConfignie jest konfigurowalny przez parametry API — zakres godzin 00:00–24:00 jest zakodowany na stałe w klasie enum. -
Brak filtra
organization_idw IndexScheduleController. Poleorganization_idjest w$publicFiltersmodelu, ale nie jest zarejestrowane wAllowedFilterkontrolera akcjiIndexScheduleController(dostępny jest tylko przezpublicFilterRequestRulesna poziomie walidacji requesta, nie jako AllowedFilter QueryBuilder). -
Limit eksportu PDF (31 dni) jest stały. Wartość
MAX_DAYS = 31jest zakodowana na stałe w klasieValidateDateRangei nie jest konfigurowalna. -
ScheduleObserverjest pusty. Klasa obserwatora jest zarejestrowana (#[ObservedBy]), ale nie zawiera żadnych hooków — potencjalne miejsce na przyszłą logikę. -
ScheduleEnumServicejest pusty. Enum serwis nie zawiera żadnych wartości.
Historia zmian¶
| Data | Typ | Opis |
|---|---|---|
| 22 lut 2026 | Naprawa | FormatScheduleEvent: wykluczenie rekordów WaitingReservationSlot — FormatScheduleEvent::__invoke zwraca null dla scheduleable_type === WaitingReservationSlot::class. Rekordy soft-lock nie są renderowane w widokach PDF/tygodniowym/miesięcznym. Zaktualizowane klasy: FormatScheduleEvent, ProcessScheduleEvent, BuildMonthViewPdfData, BuildListViewPdfData |
| 20 lut 2026 | Funkcjonalność | Soft-lock: WaitingReservationSlot w harmonogramie — Nowy model WaitingReservationSlot z relacją morphOne do Schedule. Tymczasowe blokady (kolor #F59E0B) dla niezalogowanych użytkowników podczas składania rezerwacji. Usuwane przez CRON reservation:cleanup-waiting |
| 18 lut 2026 | Optymalizacja | Fix N+1: creator.roles → creator.role w ApplyScheduleAggregatesOrm — Korekta eager loading relacji HasOneThrough role (singular) zamiast MorphToMany roles |
| 18 lut 2026 | Optymalizacja | Eliminacja N+1 query w GET /api/schedules (faza 2) — Rozszerzono eager loading w ApplyScheduleAggregatesOrm: ReservationSlot → ['refunds', 'reservation.client', 'reservation.latestNote'], ItemBlock → ['creator.role']. Dodano item.childrens do IndexScheduleController |
| 11 lut 2026 | Optymalizacja | Eliminacja N+1 query w GET /api/schedules (faza 1) — Utworzono ApplyScheduleAggregatesOrm z eager loading polimorficznym via morphWith. Przeniesiono defaultSort z inline closure do dedykowanej klasy |