Przejdź do treści

Dokumenty (Document)

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

Automatyczne generowanie dokumentów — szablony DOCX wypełniane danymi z systemu.


Co to jest?

Domena Dokumentów zarządza szablonami dokumentów i ich generowaniem. To jak drukarka z gotowymi formularzami — wgrywasz szablon umowy z polami do wypełnienia (np. reservation.first_name lub rezerwacja.imie), a system automatycznie podstawia dane i generuje gotowy dokument.

System obsługuje: - Szablony DOCX z placeholderami (format PhpWord: ${klucz}) - Automatyczne wypełnianie danymi z kontekstu (rezerwacja, obiekt, organizacja, użytkownik, system) - Dwujęzyczne klucze — angielskie (reservation.first_name) i polskie (rezerwacja.imie) - Przypisanie szablonów do obiektów (Item) i organizacji (Organization) - Generowanie dokumentów dla rezerwacji, obiektów i organizacji - Walidację kluczy podczas wgrywania szablonu - Zarządzanie stanem aktywności szablonów


Jakie problemy rozwiązuje?

Korzyści dla JST

  • Oszczędność czasu — dokumenty generują się automatycznie
  • Spójność — wszystkie umowy wyglądają tak samo
  • Brak błędów — dane pobierane automatycznie z systemu
  • Łatwa aktualizacja — zmiana szablonu = zmiana wszystkich przyszłych dokumentów
  • Walidacja — system sprawdza poprawność kluczy przy wgrywaniu szablonu

Struktura domeny

app/Domain/Document/
├── Action/
│   ├── Attr/
│   │   └── GetFullFilePathAttr.php          # Resolves full file path from storage disk
│   ├── Controller/
│   │   ├── IndexDocumentController.php       # Lista dokumentów (filtr assignable)
│   │   ├── ShowDocumentController.php        # Szczegóły dokumentu
│   │   ├── StoreDocumentController.php       # Tworzenie + przypisanie
│   │   ├── UpdateDocumentController.php      # Aktualizacja metadanych
│   │   ├── AssignDocumentController.php      # Przypisanie do modelu
│   │   ├── UnassignDocumentController.php    # Odpisanie od modelu
│   │   ├── GenerateDocumentController.php    # Generowanie wypełnionego dokumentu
│   │   ├── DownloadDocumentController.php    # Pobieranie szablonu źródłowego
│   │   ├── IndexDocumentKeyController.php    # Lista kluczy placeholderów
│   │   └── ShowDocumentKeyController.php     # Szczegóły klucza
│   └── Other/
│       ├── AssignDocumentToModel.php         # Przypisanie z deduplikacją
│       ├── BuildContext.php                  # Budowanie kontekstu danych
│       ├── CreateDocument.php                # Upload + walidacja kluczy + zapis
│       ├── CreateDocumentAssignable.php      # Batch tworzenie przypisań
│       ├── DocumentGenerator.php             # Generowanie DOCX (PhpWord)
│       ├── DocumentKeyResolver.php           # Mapowanie kluczy EN↔PL + rozwiązywanie wartości
│       ├── DocumentParser.php                # Ekstrakcja placeholderów + walidacja
│       ├── UnassignDocumentFromModel.php     # Usuwanie przypisania
│       └── UpdateDocument.php                # Aktualizacja metadanych
├── Controller/
│   ├── DocumentController.php                # Główny kontroler (9 metod)
│   └── DocumentKeyController.php             # Kontroler kluczy (2 metody)
├── Model/
│   ├── Document.php                          # Szablon dokumentu
│   ├── DocumentAssignable.php                # Polimorficzne przypisanie
│   └── DocumentKey.php                       # Klucz placeholdera (baza danych)
├── Observer/
│   └── DocumentObserver.php                  # Pusty observer (zarejestrowany via ObservedBy)
├── Request/
│   ├── AssignDocumentControllerRequest.php   # Walidacja przypisania
│   ├── GenerateDocumentControllerRequest.php # Walidacja generowania
│   ├── IndexDocumentControllerRequest.php    # Walidacja listy dokumentów
│   ├── IndexDocumentKeyControllerRequest.php # Walidacja listy kluczy
│   ├── StoreDocumentControllerRequest.php    # Walidacja uploadu + klucze DOCX
│   ├── UpdateDocumentControllerRequest.php   # Walidacja aktualizacji
│   └── Rule/
│       └── GeneratedForExistsRule.php        # Walidacja istnienia modelu docelowego
├── Resource/
│   ├── DocumentResource.php                  # Zasób dokumentu
│   ├── DocumentAssignableResource.php        # Zasób przypisania
│   └── DocumentKeyResource.php               # Zasób klucza
└── Service/
    ├── DocumentEnumService.php               # Typy dokumentów + konteksty kluczy
    ├── TraitDocument.php                     # Metody domenowe
    └── TraitControllerDocument.php           # Delegacja do Action/Controller

Kluczowe funkcjonalności

Dla administratora / supervisora / pracownika

Funkcja Opis Status
Wgrywanie szablonów Upload plików DOCX (max 10 MB) z walidacją kluczy Dostępne
Zarządzanie szablonami Edycja metadanych, aktywacja/dezaktywacja Dostępne
Przypisanie do obiektów Szablon per obiekt (Item) lub organizacja (Organization) Dostępne
Generowanie dokumentu Dla rezerwacji, obiektu lub organizacji Dostępne
Pobieranie szablonu Download oryginalnego pliku DOCX Dostępne
Podgląd kluczy API z listą dostępnych placeholderów Dostępne
Zmiana stanu Toggle is_active szablonu Dostępne

Jak to działa?

Scenariusz 1: Tworzenie szablonu dokumentu

flowchart LR
    A[Admin tworzy szablon DOCX] --> B[Wgrywa do systemu]
    B --> C{Walidacja kluczy}
    C -->|OK| D[Przypisuje do obiektu/organizacji]
    C -->|Błąd| E[Odrzucenie z listą<br/>nieznanych kluczy]
    D --> F[Szablon gotowy do użycia]
  1. Administrator tworzy dokument Word z placeholderami w formacie ${klucz}
  2. Klucze mogą być w języku angielskim (np. reservation.first_name) lub polskim (np. rezerwacja.imie)
  3. Wgrywa szablon do systemu — system automatycznie wyciąga placeholdery z DOCX
  4. System waliduje klucze — sprawdza czy istnieją w tabeli document_keys (kolumna key lub key_pl)
  5. Jeśli klucze są nieznane → plik odrzucony z listą nieprawidłowych kluczy (HTTP 422)
  6. Przypisuje szablon do konkretnego obiektu (Item) i/lub organizacji (Organization)

Scenariusz 2: Generowanie dokumentu

sequenceDiagram
    participant P as Pracownik
    participant S as System
    participant BC as BuildContext
    participant DKR as DocumentKeyResolver
    participant DG as DocumentGenerator

    P->>S: POST /documents/{id}/generate
    Note over P,S: generated_for_type + generated_for_id
    S->>BC: Buduje kontekst danych
    BC-->>S: {reservation, item, organization, user}
    S->>DG: Generuje dokument
    DG->>DG: Wyciąga placeholdery z szablonu
    DG->>DKR: Rozwiązuje wartości kluczy
    DKR-->>DG: Mapowane wartości
    DG->>DG: Wypełnia szablon (PhpWord)
    DG-->>S: Wygenerowany plik DOCX
    S-->>P: BinaryFileResponse (download)
  1. Pracownik wskazuje typ i ID obiektu, dla którego generuje dokument
  2. BuildContext buduje kontekst danych:
    • Reservation → ładuje rezerwację + reservationable (Item lub ActivityItem) + organizację
    • Item → ładuje obiekt + organizację
    • Organization → ładuje organizację
    • Zawsze dodaje zalogowanego użytkownika (auth()->user())
  3. DocumentGenerator wyciąga placeholdery z szablonu DOCX
  4. DocumentKeyResolver mapuje klucze (obsługuje polskie i angielskie nazwy) i rozwiązuje wartości z kontekstu
  5. Nierozwiązane wartości zastępowane są symbolem {...}
  6. Wygenerowany plik zapisywany w storage/app/private/generated/{uuid}.docx
  7. Nazwa pliku do pobrania: {slug-nazwy-dokumentu}_{YmdHis}.docx

Modele danych

Document

Pole Typ Opis
id UUID Identyfikator
name string Nazwa szablonu (max 255 znaków)
type enum Typ dokumentu (contract, regulation, information, correspondence, other)
file_path string Ścieżka do pliku w storage
file_name string Oryginalna nazwa pliku
file_size unsignedBigInteger Rozmiar pliku w bajtach
description string/null Opis szablonu
is_active boolean Czy szablon jest aktywny
created_at datetime Data utworzenia
updated_at datetime Data aktualizacji

Model: Domain\Document\Model\Document — rozszerza CustomModel (logowanie aktywności via Spatie), HasUuids, TraitDocument, #[ObservedBy(DocumentObserver)].

Relacje: assignables() → HasMany DocumentAssignable

Computed: full_file_path — accessor rozwiązujący pełną ścieżkę pliku (obsługuje URLe HTTP i ścieżki dyskowe).

DocumentKey

Pole Typ Opis
id UUID Identyfikator
key string Klucz w języku angielskim (np. reservation.first_name), unique
key_pl string/null Klucz w języku polskim (np. rezerwacja.imie)
label string Etykieta klucza
description text/null Opis klucza
context enum Kontekst klucza (reservation, item, organization, user, global)
value_type enum Typ wartości (string, date, datetime, number, boolean)
example_value string/null Przykładowa wartość

Model: Domain\Document\Model\DocumentKey — rozszerza CustomModel, HasUuids. Brak ObservedBy — nie ma observera.

Tabela: document_keys — przechowuje definicje kluczy do walidacji.

DocumentAssignable

Pole Typ Opis
id UUID Identyfikator
document_id UUID ID dokumentu
assignable_type string Typ modelu (Item lub Organization)
assignable_id UUID ID modelu
created_at datetime Data utworzenia
updated_at datetime Data aktualizacji

Model: Domain\Document\Model\DocumentAssignable — rozszerza CustomModel, HasUuids. Relacja polimorficzna (MorphTo).

Relacje: document() → BelongsTo Document, assignable() → MorphTo (Item lub Organization).


Struktura tabel w bazie danych

Migracja: 0001_01_01_000029_create_documents.php

Tabela documents

Kolumna Typ Nullable Default Opis
id uuid Nie Klucz główny
name string Nie Nazwa szablonu
type enum(contract, regulation, information, correspondence, other) Nie Typ dokumentu
file_path string Nie Ścieżka pliku w storage
file_name string Nie Oryginalna nazwa pliku
file_size unsignedBigInteger Nie Rozmiar pliku w bajtach
description text Tak null Opis szablonu
is_active boolean Nie true Czy aktywny
created_at timestamp Tak Data utworzenia
updated_at timestamp Tak Data aktualizacji

Indeksy: (type, is_active)

Tabela document_assignables

Kolumna Typ Nullable Default Opis
id uuid Nie Klucz główny
document_id uuid Nie FK → documents.id (CASCADE)
assignable_type string Nie Typ modelu (Item/Organization)
assignable_id uuid Nie ID modelu
created_at timestamp Tak Data utworzenia
updated_at timestamp Tak Data aktualizacji

Klucz obcy: document_iddocuments.id ON DELETE CASCADE

Constraint unique: (document_id, assignable_type, assignable_id)

Indeksy: document_id, (assignable_type, assignable_id)

Tabela document_keys

Kolumna Typ Nullable Default Opis
id uuid Nie Klucz główny
key string Nie Klucz EN (unique)
key_pl string Tak null Klucz PL
label string Nie Etykieta
description text Tak null Opis
context enum(reservation, item, organization, user, global) Nie Kontekst
value_type enum(string, date, datetime, number, boolean) Nie Typ wartości
example_value string Tak null Przykładowa wartość
created_at timestamp Tak Data utworzenia
updated_at timestamp Tak Data aktualizacji

Indeksy: key (unique), context, key_pl

Seeding kluczy w migracji

Migracja zawiera seed 32 predefiniowanych kluczy placeholderów (12 rezerwacja, 8 obiekt, 6 organizacja, 2 użytkownik, 4 system). Klucze są tworzone via DocumentKey::firstOrCreate().


Typy dokumentów

Enum Wartość Opis
CONTRACT contract Umowa
REGULATION regulation Regulamin
INFORMATION information Informacja
CORRESPONDENCE correspondence Korespondencja
OTHER other Inny

Źródło: DocumentEnumService::getDocumentTypes().


Konteksty kluczy

Enum Wartość Opis
RESERVATION reservation Dane rezerwacji
ITEM item Dane obiektu
ORGANIZATION organization Dane organizacji
USER user Dane użytkownika
GLOBAL global Dane systemowe

Źródło: DocumentEnumService::getDocumentKeyContexts().


Dostępne placeholdery

System udostępnia 32 placeholdery do wykorzystania w szablonach. Każdy klucz ma wersję angielską i polską — w szablonie DOCX można użyć dowolnej z nich.

Dane rezerwacji (12 kluczy)

Klucz EN Klucz PL Opis Przykład
reservation.id rezerwacja.id ID rezerwacji 550e8400-...
reservation.uuid rezerwacja.uuid UUID rezerwacji abc-123-def
reservation.total_price rezerwacja.cena_calkowita Cena całkowita 250,00
reservation.status rezerwacja.status Status rezerwacji RES_COMPLETED
reservation.first_name rezerwacja.imie Imię rezerwującego Jan
reservation.last_name rezerwacja.nazwisko Nazwisko rezerwującego Kowalski
reservation.email rezerwacja.email Email rezerwującego jan@example.com
reservation.mobile_phone rezerwacja.telefon Telefon rezerwującego +48 123 456 789
reservation.created_at rezerwacja.data_utworzenia Data utworzenia 2024-02-15 10:00
reservation.date_time_from rezerwacja.data_od Data rozpoczęcia 2024-02-15 10:00
reservation.date_time_to rezerwacja.data_do Data zakończenia 2024-02-15 12:00
reservation.days_time_array rezerwacja.wszystkie_dni_rezerwacji Wszystkie dni rezerwacji (tablica → tekst)

Dane obiektu (8 kluczy)

Klucz EN Klucz PL Opis Przykład
item.name obiekt.nazwa Nazwa obiektu Sala konferencyjna A
item.full_address obiekt.pelny_adres Pełny adres ul. Sportowa 5, 00-001 Warszawa
item.street obiekt.ulica Ulica ul. Sportowa
item.street_number obiekt.numer Numer 5
item.postal_code obiekt.kod_pocztowy Kod pocztowy 00-001
item.city obiekt.miasto Miasto Warszawa
item.capacity obiekt.pojemnosc Pojemność 50
item.area obiekt.powierzchnia Powierzchnia 120

Dane organizacji (6 kluczy)

Klucz EN Klucz PL Opis Przykład
organization.name organizacja.nazwa Nazwa organizacji Gmina Przykładowo
organization.nip organizacja.nip NIP 123-456-78-90
organization.regon organizacja.regon REGON 123456789
organization.email organizacja.email Email kontakt@gmina.pl
organization.mobile_phone organizacja.telefon Telefon +48 12 345 67 89
organization.full_address organizacja.pelny_adres Pełny adres ul. Główna 1, 00-001 Warszawa

Dane użytkownika (2 klucze)

Klucz EN Klucz PL Opis Przykład
user.name uzytkownik.imię_i_nazwisko Imię i nazwisko zalogowanego użytkownika Anna Nowak
user.email uzytkownik.email Email zalogowanego użytkownika anna@gmina.pl

Dane systemowe (4 klucze)

Klucz EN Klucz PL Opis Przykład
system.name system.nazwa Nazwa aplikacji Zajmij.to
system.current_date system.aktualna_data Aktualna data 2024-02-20
system.current_datetime system.aktualna_data_czas Aktualna data i czas 2024-02-20 14:30
system.current_year system.aktualny_rok Aktualny rok 2024

Walidacja kluczy

Klucze zdefiniowane są w tabeli document_keys w bazie danych. Podczas uploadu szablonu system wyciąga wszystkie placeholdery z pliku DOCX i waliduje je — zarówno po kolumnie key (EN) jak i key_pl (PL). Nieznane klucze powodują odrzucenie pliku z HTTP 422.


Powiązania z innymi domenami

Domena Powiązanie
Zasoby (Item) Szablony przypisywane do obiektów, dane obiektu w placeholderach
Organizacje (Organization) Szablony przypisywane do organizacji, dane organizacji w placeholderach
Rezerwacje (Reservation) Generowanie dokumentów dla rezerwacji, dane rezerwacji w placeholderach
Aktywności (Activity) Kontekst ActivityItem przy generowaniu dokumentu dla rezerwacji aktywności
Użytkownicy (User) Dane zalogowanego użytkownika w placeholderach

API Endpoints

Dokumenty (/api/documents)

Metoda Endpoint Opis Request
GET /documents Lista dokumentów IndexDocumentControllerRequest
GET /documents/{documentId} Szczegóły dokumentu
GET /documents/{documentId}/download Pobierz szablon DOCX
POST /documents Wgraj nowy szablon StoreDocumentControllerRequest
PATCH /documents/{documentId} Aktualizuj metadane UpdateDocumentControllerRequest
POST /documents/{documentId}/assign Przypisz do modelu AssignDocumentControllerRequest
DELETE /documents/{documentId}/unassign/{assignableId} Odpisz od modelu
POST /documents/{documentId}/generate Generuj wypełniony dokument GenerateDocumentControllerRequest
PATCH /documents/{documentId}/change-state Toggle is_active

Klucze dokumentów (/api/document-keys)

Metoda Endpoint Opis Request
GET /document-keys Lista kluczy placeholderów IndexDocumentKeyControllerRequest
GET /document-keys/{keyId} Szczegóły klucza

Brak middleware na DocumentKeyController

Kontroler DocumentKeyController nie ma middleware z ograniczeniem ról — endpointy kluczy są dostępne dla wszystkich zalogowanych użytkowników.

Parametry żądań

Store (POST /documents)

Pole Walidacja Opis
name required, string, max:255 Nazwa szablonu
type required, string, in:DocumentEnumService Typ dokumentu
file required, file, mimes:docx, max:10240 Plik DOCX (max 10 MB)
description nullable, string Opis
is_active sometimes, boolean Czy aktywny (domyślnie: true)
assignables required, array Przypisania do modeli
assignables.*.assignable_type required, string, in:[Item, Organization] Typ modelu
assignables.*.assignable_id required_with, uuid ID modelu

Walidacja kluczy przy uploadzie

Request StoreDocumentControllerRequest wykonuje dodatkową walidację (withValidator): wyciąga placeholdery z wgranego pliku DOCX i sprawdza je w tabeli document_keys. Nieprawidłowe klucze dodawane jako błąd walidacji na polu file.

Update (PATCH /documents/{id})

Pole Walidacja Opis
name sometimes, string, max:255 Nazwa szablonu
type sometimes, string, in:DocumentEnumService Typ dokumentu
description nullable, string Opis
is_active sometimes, boolean Czy aktywny

Update nie pozwala na zmianę pliku

Endpoint aktualizacji zmienia jedynie metadane — nie ma możliwości ponownego uploadu pliku DOCX. Aby zmienić plik, należy utworzyć nowy dokument.

Generate (POST /documents/{id}/generate)

Pole Walidacja Opis
generated_for_type required, string, in:[Reservation, Item, Organization] Typ modelu docelowego
generated_for_id required, uuid, GeneratedForExistsRule ID modelu docelowego

GeneratedForExistsRule — niestandardowa reguła walidacji sprawdzająca istnienie rekordu w odpowiedniej tabeli na podstawie generated_for_type.

Assign (POST /documents/{id}/assign)

Pole Walidacja Opis
assignable_type required, string, in:[Item, Organization] Typ modelu
assignable_id required, uuid ID modelu

Index Documents (GET /documents)

Pole Walidacja Opis
per_page sometimes, integer, min:1, max:100 Liczba wyników na stronę
assignable_type sometimes, string, in:[Item, Organization] Filtr po typie przypisania
assignable_id required_with:assignable_type, uuid ID obiektu do filtrowania

Index DocumentKeys (GET /document-keys)

Pole Walidacja Opis
per_page sometimes, integer, min:1, max:100 Liczba wyników na stronę
context sometimes, string, in:DocumentEnumService::keyContexts Filtr po kontekście
value_type sometimes, string Filtr po typie wartości

Odpowiedzi API

DocumentResource

{
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Umowa najmu sali",
    "type": "contract",
    "type_lang": "Contract",
    "file_path": "documents/contract/abc123.docx",
    "file_name": "umowa_najmu_sali.docx",
    "file_size": 45678,
    "description": "Szablon umowy najmu sali konferencyjnej",
    "is_active": true,
    "created_at": "2024-02-15 10:00:00",
    "updated_at": "2024-02-15 10:00:00",
    "can": {
        "view": true,
        "create": true,
        "update": true
    },
    "assignables": [
        {
            "id": "...",
            "document_id": "550e8400-...",
            "assignable_type": "Domain\\Item\\Model\\Item",
            "assignable_id": "...",
            "created_at": "2024-02-15 10:00:00",
            "updated_at": "2024-02-15 10:00:00"
        }
    ]
}

Relacje: RELATIONS = ['assignables'].

Pole can

Pole can zawiera uprawnienia aktualnie zalogowanego użytkownika (na podstawie DocumentPolicy). Jest usuwane z odpowiedzi dla niezalogowanych użytkowników (UNALLOWED_NO_AUTH_KEYS = ['can']).

DocumentKeyResource

{
    "id": "660e8400-...",
    "key": "reservation.first_name",
    "key_pl": "rezerwacja.imie",
    "label": "Imię rezerwującego",
    "description": "Imię osoby dokonującej rezerwacji",
    "context": "reservation",
    "context_pl": "Reservation",
    "value_type": "string",
    "example_value": "Jan",
    "created_at": "2024-02-15 10:00:00",
    "updated_at": "2024-02-15 10:00:00"
}

Odpowiedzi na Store/Update

Endpointy store i update zwracają komunikat sukcesu (nie zasób):

{
    "data": "The document has been successfully added"
}

Tworzenie szablonu

Krok 1: Sprawdź dostępne klucze

Użyj endpointu GET /api/document-keys aby zobaczyć listę dostępnych kluczy. Filtruj po kontekście: ?context=reservation.

Krok 2: Przygotuj dokument Word

Stwórz dokument .docx z placeholderami w formacie ${klucz} (format PhpWord TemplateProcessor):

UMOWA NAJMU

zawarta dnia ${system.current_date} (lub ${system.aktualna_data})

pomiędzy:
${organization.name} z siedzibą ${organization.full_address}
NIP: ${organization.nip}, REGON: ${organization.regon}
reprezentowaną przez ${user.name}

a

${reservation.first_name} ${reservation.last_name}
email: ${reservation.email}, tel: ${reservation.mobile_phone}

Przedmiot najmu: ${item.name}
Adres obiektu: ${item.full_address}
Pojemność: ${item.capacity} osób

Termin: od ${reservation.date_time_from} do ${reservation.date_time_to}
Kwota: ${reservation.total_price} zł

Klucze po polsku

Można używać kluczy w języku polskim — system je automatycznie rozpozna:

${rezerwacja.imie} ${rezerwacja.nazwisko}
zamieszkałym: ${obiekt.miasto}

Krok 3: Wgraj szablon

POST /api/documents
Content-Type: multipart/form-data

name: "Umowa najmu sali"
type: "contract"
file: [plik.docx]
description: "Szablon umowy najmu"
assignables[0][assignable_type]: "Domain\Item\Model\Item"
assignables[0][assignable_id]: "uuid-obiektu"

System automatycznie: 1. Wyciągnie placeholdery z pliku DOCX 2. Zwaliduje klucze w tabeli document_keys 3. Zapisze plik w storage/app/private/documents/{type}/ 4. Utworzy rekord Document i przypisania DocumentAssignable

Krok 4: Generuj dokument

POST /api/documents/{documentId}/generate

{
    "generated_for_type": "Domain\\Reservation\\Model\\Reservation",
    "generated_for_id": "uuid-rezerwacji"
}

Odpowiedź: BinaryFileResponse — plik DOCX do pobrania.


Budowanie kontekstu (BuildContext)

Kontekst danych budowany jest automatycznie na podstawie generated_for_type:

flowchart TD
    A[generated_for_type] --> B{Typ}
    B -->|Reservation| C[Ładuj rezerwację<br/>+ reservationable<br/>+ reservationSlots]
    B -->|Item| D[Ładuj obiekt<br/>+ organizację]
    B -->|Organization| E[Ładuj organizację]

    C --> F{reservationable type}
    F -->|Item| G["context: reservation, item, organization"]
    F -->|ActivityItem| H["context: reservation, activity_item, activity, organization"]

    D --> I["context: item, organization"]
    E --> J["context: organization"]

    G --> K[+ user z auth]
    H --> K
    I --> K
    J --> K
generated_for_type Kontekst Źródło danych
Reservation (Item) reservation, item, organization, user Rezerwacja → Item → Organization
Reservation (ActivityItem) reservation, activity_item, activity, organization, user Rezerwacja → ActivityItem → Activity → Organization
Item item, organization, user Obiekt → Organization
Organization organization, user Organizacja

Użytkownik w kontekście

Zalogowany użytkownik (auth()->user()) jest zawsze dodawany do kontekstu — dzięki temu klucze user.name i user.email wypełniają się danymi aktualnie zalogowanej osoby (np. pracownika generującego dokument).


Mechanizm rozwiązywania kluczy (DocumentKeyResolver)

flowchart LR
    A["Klucz z szablonu<br/>(np. rezerwacja.imie)"] --> B[normalizeKey]
    B --> C{"Czy klucz PL<br/>w keyMappings?"}
    C -->|Tak| D["Zamień na EN<br/>(reservation.first_name)"]
    C -->|Nie| E[Użyj oryginalnego]
    D --> F[resolveKey]
    E --> F
    F --> G{Prefix klucza}
    G -->|reservation.| H[resolveReservationKey]
    G -->|item.| I[resolveItemKey]
    G -->|organization.| J[resolveOrganizationKey]
    G -->|user.| K[resolveUserKey]
    G -->|system.| L[resolveSystemKey]
    H --> M[Wartość z modelu]
    I --> M
    J --> M
    K --> M
    L --> M

Kluczowe szczegóły: - reservation.total_price — formatowany: number_format($value, 2, ',', ' ') (np. 250,00) - reservation.created_at — format: Y-m-d H:i - item.full_address / organization.full_address — składany z pól: "{street} {street_number}, {postal_code} {city}" - system.name — pobierany z config('app.name', 'Zajmij.to') - reservation.days_time_array — zwraca tablicę → konwertowana na tekst via implode("\n", ...)


Przechowywanie plików

Lokalizacja Opis
storage/app/private/documents/{type}/ Szablony źródłowe (per typ: contract, regulation, etc.)
storage/app/private/generated/ Wygenerowane dokumenty (nazwa: {uuid}.docx)

Dysk: private (Laravel Storage).

Limit rozmiaru: max 10 MB (10240 KB) — walidacja w StoreDocumentControllerRequest.

Format: wyłącznie DOCX (mimes:docx, mimetypes:application/vnd.openxmlformats-officedocument.wordprocessingml.document).


Wyjątki

Wyjątek Kod Opis
InvalidDocumentKeysException 422 Plik zawiera nieznane klucze placeholderów
NotFoundModelException 404 Dokument/klucz/model docelowy nie znaleziony

InvalidDocumentKeysException (Infrastructure\Exceptions) — rzucany przez CreateDocument gdy walidacja kluczy w DOCX nie przejdzie. Wiadomość zawiera listę nieprawidłowych kluczy.


Wartość biznesowa

Argumenty dla decydentów

  1. Automatyzacja — koniec ręcznego wypełniania umów
  2. Standaryzacja — wszystkie dokumenty zgodne z wzorem
  3. Elastyczność — każdy obiekt może mieć własny szablon
  4. Dwujęzyczność kluczy — szablony mogą używać kluczy polskich lub angielskich
  5. Walidacja — system nie pozwoli wgrać szablonu z błędnymi kluczami
  6. Audyt — logowanie aktywności na modelu Document (Spatie ActivityLog via CustomModel)

Technologia

Element Technologia
Format szablonów DOCX (Microsoft Word)
Biblioteka PhpWord TemplateProcessor
Przechowywanie Laravel Storage (dysk private) + baza danych
Format wyjściowy DOCX
Walidacja kluczy Tabela document_keys (klucze EN + PL)
Logowanie aktywności Spatie ActivityLog (via CustomModel, tylko model Document)

Uprawnienia

Rola Podgląd Wgrywanie Edycja Generowanie Przypisanie Zmiana stanu Pobieranie
Administrator Tak Tak Tak Tak Tak Tak Tak
Supervisor Tak Tak Tak Tak Tak Tak Tak
Employee Tak Nie Nie Tak Nie Nie Tak

Dwupoziomowa autoryzacja

Uprawnienia działają na dwóch poziomach:

  1. Middleware (role:admin|supervisor|employee) — ogranicza dostęp do wszystkich endpointów tylko dla tych trzech ról.
  2. Policy (DocumentPolicy) — dodatkowo ogranicza operacje zapisu:
    • create() → wymaga roli Supervisor (Admin ma dostęp przez before() w BasePolicy)
    • update() → wymaga roli Supervisor (Admin ma dostęp przez before() w BasePolicy)

W efekcie Employee może jedynie przeglądać (index, show), pobierać (download) i generować (generate) dokumenty — nie może ich tworzyć, edytować, przypisywać, odpisywać ani zmieniać stanu.


Policy (DocumentPolicy)

DocumentPolicy rozszerza BasePolicy (która w before() nadaje Adminowi pełny dostęp).

Metoda Logika Dostęp
view(User, Document) return true Każdy zalogowany użytkownik
create(User) $this->isSupervisor($user) Admin (via before) + Supervisor
update(User, Document) $this->isSupervisor($user) Admin (via before) + Supervisor

Brak metody delete

Policy nie definiuje metody delete — usuwanie dokumentów nie jest zaimplementowane w kontrolerze.


Observer

DocumentObserver — zarejestrowany via #[ObservedBy(DocumentObserver::class)] na modelu Document. Klasa jest pusta — nie implementuje żadnych hooków lifecycle.


Diagram komponentów

flowchart TB
    subgraph Controllers
        DC[DocumentController<br/>9 metod]
        DKC[DocumentKeyController<br/>2 metody]
    end

    subgraph "Action/Controller"
        IDC[IndexDocumentController]
        SDC[ShowDocumentController]
        STDC[StoreDocumentController]
        UDC[UpdateDocumentController]
        ADC[AssignDocumentController]
        UADC[UnassignDocumentController]
        GDC[GenerateDocumentController]
        DDC[DownloadDocumentController]
        IDKC[IndexDocumentKeyController]
        SDKC[ShowDocumentKeyController]
    end

    subgraph "Action/Other"
        CD[CreateDocument]
        UD[UpdateDocument]
        ADM[AssignDocumentToModel]
        UADM[UnassignDocumentFromModel]
        CDA[CreateDocumentAssignable]
        DG[DocumentGenerator]
        DP[DocumentParser]
        DKR[DocumentKeyResolver]
        BC[BuildContext]
    end

    subgraph Models
        D[Document]
        DK[DocumentKey]
        DA[DocumentAssignable]
    end

    DC --> IDC & SDC & STDC & UDC & ADC & UADC & GDC & DDC
    DKC --> IDKC & SDKC

    STDC --> CD --> DP
    STDC --> CDA
    UDC --> UD
    ADC --> ADM
    UADC --> DA
    GDC --> BC & DG
    DG --> DP & DKR

    CD --> D & DA
    DP --> DK
    ADM --> DA
    BC --> D

Znane ograniczenia i TODO

Ograniczenie Opis
Brak usuwania dokumentów Kontroler nie implementuje metody delete — usunięcie szablonu jest możliwe jedynie bezpośrednio w bazie danych
Brak aktualizacji pliku Endpoint update zmienia jedynie metadane — nie pozwala na ponowny upload pliku DOCX
Pusty observer DocumentObserver jest zarejestrowany, ale nie implementuje żadnej logiki
Wygenerowane pliki nie są czyszczone Pliki w storage/app/private/generated/ nie są automatycznie usuwane — brak CRON joba do czyszczenia
Policy view niewykorzystana Metoda view() w DocumentPolicy istnieje, ale nie jest wywoływana przez żaden endpoint kontrolera