Files
orderPRO/.paul/phases/108-delivery-status-management/108-01-PLAN.md
Jacek Pyziak 0063402897 feat(108): delivery status management
Phase 108 complete (v3.2 milestone):

Plan 108-01 — Delivery Status DB & CRUD:
- Tabela delivery_statuses z seedem 11 statusow systemowych
- DeliveryStatusRepository (CRUD + per-request static cache)
- DeliveryStatus::setRepository() — DB fallback dla static final class
- Panel /settings/delivery-statuses (zakladki Statusy + Mapowanie)
- Sidebar przebudowany: Statusy zamowien + Statusy przesylek

Plan 108-02 — Automation Dropdowns z DB + UI Refactor:
- Dropdowny automatyzacji ladowane z DB (warunek shipment_status + akcja update_shipment_status)
- Walidacja przez DeliveryStatus::getAllStatuses()
- Osobna podstrona formularza CRUD (delivery-status-form.php)
- Lista uproszczona: rename Terminal -> Koncowy, usunieta kolumna Typ
- BREAKING: drop backward compat dla starych grupowych kluczy automatyzacji
- Bug fix: path params w DeliveryStatusesController via \$request->input('id')

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 22:10:24 +02:00

17 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, delegation
phase plan type wave depends_on files_modified autonomous delegation
108-delivery-status-management 01 execute 1
database/migrations/20260427_000103_create_delivery_statuses_table.sql
src/Modules/Shipments/DeliveryStatusRepository.php
src/Modules/Shipments/DeliveryStatus.php
src/Modules/Settings/DeliveryStatusesController.php
src/Modules/Settings/DeliveryStatusMappingController.php
resources/views/settings/delivery-statuses.php
resources/views/settings/_delivery-status-mappings-content.php
resources/views/settings/delivery-status-mappings.php
resources/views/layouts/app.php
resources/scss/settings/_delivery-statuses.scss
resources/scss/app.scss
resources/lang/pl.php
routes/web.php
.paul/docs/DB_SCHEMA.md
.paul/docs/ARCHITECTURE.md
.paul/docs/TECH_CHANGELOG.md
true on
## Goal Wyniesc statusy znormalizowane przesylek do tabeli DB, udostepnic CRUD w nowym panelu ustawien "Statusy przesylek" i dynamicznie ladowac statusy z DB wszedzie tam, gdzie wczesniej uzywano stalych z `DeliveryStatus.php`.

Purpose

Dodanie nowego statusu znormalizowanego wymaga teraz zmiany kodu i deploymentu. Po tej fazie operator moze dodac wlasny status z UI bez ingerencji w kod. Statusy systemowe (delivered, returned, cancelled) pozostaja nieedytowalne.

Output

  • Tabela delivery_statuses z seedem 11 istniejacych statusow
  • DeliveryStatusRepository — odczyt z DB z per-request static cache
  • DeliveryStatus.php — laduje ALL_STATUSES i LABEL_PL z DB (fallback na stale przy bledzie)
  • Nowy panel /settings/delivery-statuses z dwoma zakladkami: CRUD statusow + mapowanie dostawy
  • Sidebar: "Statusy zamowien" (istniejaca pozycja), nowa pozycja "Statusy przesylek"
  • Walidacja w DeliveryStatusMappingController uzywajaca danych z DB
## Project Context @.paul/PROJECT.md @.paul/STATE.md

Source Files

@src/Modules/Shipments/DeliveryStatus.php @src/Modules/Settings/DeliveryStatusMappingController.php @resources/views/settings/delivery-status-mappings.php @resources/views/layouts/app.php @resources/lang/pl.php @routes/web.php @.paul/docs/DB_SCHEMA.md @.paul/docs/ARCHITECTURE.md

## Required Skills
Skill Priority When to Invoke Loaded?
sonar-scanner (CLI) required Po APPLY, przed UNIFY o

<acceptance_criteria>

AC-1: Tabela delivery_statuses istnieje i zawiera seed 11 statusow

Given migracja zostala uruchomiona
Then tabela delivery_statuses zawiera 11 wierszy odpowiadajacych stalym z DeliveryStatus.php
And kolumny: id, key, label_pl, color, sort_order, is_terminal, is_system, created_at
And statusy: delivered, returned, cancelled maja is_system=1, is_terminal=1
And pozostale 8 statusow maja is_system=0

AC-2: CRUD niebedacych systemowymi statusami dziala z panelu

Given operator otwiera /settings/delivery-statuses (zakladka Statusy)
When doda nowy status z kluczem, etykieta, kolorem
Then status pojawia sie na liscie i w dropdownie mapowania statusow
When probuje edytowac status systemowy (delivered/returned/cancelled)
Then formularz jest zablokowany (readonly/disabled fields lub brak przycisku edycji)
When probuje usunac status uzyty w delivery_status_mappings lub shipment_packages
Then otrzymuje blad "Status jest uzywany, nie mozna usunac"

AC-3: Strona /settings/delivery-statuses ma dwie zakladki

Given operator otwiera /settings/delivery-statuses
Then widzi zakladke "Statusy" (CRUD) i "Mapowanie dostawy"
And klikajac "Mapowanie dostawy" widzi te sama tresc co wczesniej na /settings/delivery-status-mappings
And /settings/delivery-status-mappings dalej dziala (backward compat dla zakładek)

AC-4: Sidebar odzwierciedla nowa strukture nawigacji

Given operator jest w ustawieniach
Then widzi "Statusy zamowien" (link do /settings/statuses)
And widzi "Statusy przesylek" (link do /settings/delivery-statuses)
And stara pozycja "Mapowanie statusow dostawy" znika z sidebara
And badge z liczba niezmapowanych statusow widnieje przy "Statusy przesylek"

AC-5: DeliveryStatus.php laduje statusy z DB

Given tabela delivery_statuses istnieje
When kod wywola DeliveryStatus::label($key)
Then zwraca etykiete z DB (nie hardcoded)
And kolejne wywolania w tym samym request uzywaja per-request static cache

AC-6: Badge renderuje sie dla nowych statusow

Given status niebedacy jednym z 11 systemowych ma kolor #ff5500
When badge jest renderowany w liscie zamowien lub szczegolow przesylki
Then badge uzywa inline style="background-color: #ff5500" dla nieznanych kluczy CSS
And istniejace 11 statusow dalej korzysta z hardcoded klas CSS (.delivery-badge--delivered itp.)

</acceptance_criteria>

Task 1: Migracja tabeli delivery_statuses i DeliveryStatusRepository database/migrations/20260427_000103_create_delivery_statuses_table.sql, src/Modules/Shipments/DeliveryStatusRepository.php 1. Utworzyc migracje `20260427_000103_create_delivery_statuses_table.sql`: - CREATE TABLE `delivery_statuses` ( `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `key` VARCHAR(50) NOT NULL UNIQUE, `label_pl` VARCHAR(100) NOT NULL, `color` VARCHAR(7) NOT NULL DEFAULT '#6c757d', `sort_order` TINYINT UNSIGNED NOT NULL DEFAULT 0, `is_terminal` TINYINT(1) NOT NULL DEFAULT 0, `is_system` TINYINT(1) NOT NULL DEFAULT 0, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - INSERT seed: wszystkie 11 statusow z DeliveryStatus.php (unknown, created, confirmed, picked_up, in_transit, out_for_delivery, ready_for_pickup, delivered, returned, cancelled, problem) z odpowiednimi wartosciami label_pl, color, sort_order, is_terminal (delivered/returned/cancelled = 1), is_system (delivered/returned/cancelled = 1). - Kolory bazujac na istniejacych klasach CSS (sprawdzic resources/scss/). 2. Utworzyc `src/Modules/Shipments/DeliveryStatusRepository.php`: - Konstruktor przyjmuje `\Medoo\Medoo $db` - `getAll(): array` — SELECT * FROM delivery_statuses ORDER BY sort_order ASC z per-request static cache (prywatna static zmienna) - `getByKey(string $key): ?array` — szuka w getAll() - `getAllAsOptions(): array` — zwraca [key => label_pl] (do dropdownow) - `create(array $data): int` — INSERT, zwraca id; waliduje unikalnosc key przed insertem - `update(int $id, array $data): void` — UPDATE; blokuje is_system=1 - `delete(int $id): void` — DELETE; sprawdza czy key nie wystepuje w delivery_status_mappings.normalized_status lub shipment_packages.delivery_status; blokuje jesli is_system=1 lub uzywany - `clearCache(): void` — resetuje static cache (do testow i po mutacjach) Uruchom migracje na lokalnej bazie. Sprawdz `SELECT COUNT(*) FROM delivery_statuses` = 11. Sprawdz ze delivered/returned/cancelled maja is_system=1, is_terminal=1. Dane bazodanowe gotowe; repozytorium dostepne do uzycia w pozostalych taskach. Task 2: Dynamiczne ladowanie w DeliveryStatus.php + controller + widok CRUD + routing src/Modules/Shipments/DeliveryStatus.php, src/Modules/Settings/DeliveryStatusesController.php, src/Modules/Settings/DeliveryStatusMappingController.php, resources/views/settings/delivery-statuses.php, resources/views/settings/_delivery-status-mappings-content.php, resources/views/settings/delivery-status-mappings.php, resources/scss/settings/_delivery-statuses.scss, resources/scss/app.scss, routes/web.php 1. Zaktualizowac `DeliveryStatus.php`: - Dodac statyczna metode `setRepository(DeliveryStatusRepository $repo): void` (called once at bootstrap) - Zmodyfikowac `label(string $key): string` — gdy repozytorium jest wstrzykniete, pobiera z DB przez `getByKey($key)['label_pl']`; fallback na `LABEL_PL[$key] ?? $key` - Dodac `getAllStatuses(): array` — zwraca `DeliveryStatusRepository::getAll()` lub fallback `ALL_STATUSES` gdy repo niedostepne - Dodac `getAllOptions(): array` — zwraca `DeliveryStatusRepository::getAllAsOptions()` lub fallback `LABEL_PL` - STALE i TERMINAL_STATUSES/providerowe mapy (INPOST_MAP etc.) pozostaja hardcoded (nie dotyczace listy statusow uzytkownika) - Podpiac `DeliveryStatusRepository` w bootstrapie (app.php lub DI kontener projektu — sprawdzic jak inne repozytoria sa inicjalizowane i powtorzyc ten sam wzorzec) 2. Zaktualizowac `DeliveryStatusMappingController`: - Wstrzyknac `DeliveryStatusRepository` przez konstruktor - W `save()` i `saveBulk()` — walidacja normalizedStatus przez `$this->deliveryStatusRepository->getByKey($normalizedStatus) !== null` zamiast `in_array($normalizedStatus, DeliveryStatus::ALL_STATUSES)` - Zaktualizowac `REDIRECT_PATH` z `/settings/delivery-status-mappings` na `/settings/delivery-statuses?tab=mapping` - Zachowac parametr `?provider=` przy redirectach gdzie byl 3. Wydzielic zawartosc widoku mapowania do include'a: - Przeniesc zawartosc `resources/views/settings/delivery-status-mappings.php` do `resources/views/settings/_delivery-status-mappings-content.php` - W `delivery-status-mappings.php` uzywac `include '_delivery-status-mappings-content.php'` (zachowanie backward compat) 4. Utworzyc `DeliveryStatusesController.php` w `src/Modules/Settings/`: - `index()` — laduje statusy z DeliveryStatusRepository, renderuje delivery-statuses.php; przekazuje `$tab = $_GET['tab'] ?? 'statuses'` i dane mapowania gdy tab=mapping - `store()` — POST create nowego statusu; waliduje key (lowercase, underscored), label_pl, color (#hex); zapisuje przez repozytorium; redirect z Flash - `update(int $id)` — POST edit; blokuje is_system=1; redirect z Flash - `destroy(int $id)` — POST delete; blokuje is_system=1 i uzywane; redirect z Flash - Każda akcja mutujaca: walidacja CSRF `_token`, potem `DeliveryStatusRepository::clearCache()` 5. Utworzyc widok `resources/views/settings/delivery-statuses.php`: - Dwie zakladki: "Statusy" i "Mapowanie dostawy" - Persystencja aktywnej zakladki przez `?tab=` param (nie localStorage — zeby linki z sidebara "Statusy przesylek" i redirect po save trafialy w dobra zakladke) - Zakladka "Statusy": * Tabela statusow: kolor (swatchek), klucz, etykieta, sort_order, is_terminal, akcje (edycja/usun dla nie-systemowych; informacja "systemowy" dla systemowych) * Formularz dodawania nowego statusu (inline pod tabela lub modal) * Formularz edycji (inline row edit lub osobny formularz) * Potwierdzenie usuwania: `window.OrderProAlerts.confirm()` * Formularz dodawania: pola key (slug, lowercase, max 50), label_pl, color (input type=color), sort_order (number), is_terminal (checkbox) - Zakladka "Mapowanie dostawy": * include '_delivery-status-mappings-content.php' 6. Dodac styl `resources/scss/settings/_delivery-statuses.scss`: - `.delivery-status-swatch` — maly kwadrat koloru (16x16px inline-block) - `.delivery-status-system-badge` — np. szary badge "systemowy" - Dodac `@use 'settings/delivery-statuses'` do `app.scss` 7. Dodac trasy w `routes/web.php`: - GET `/settings/delivery-statuses` → `DeliveryStatusesController::index` - POST `/settings/delivery-statuses` → `DeliveryStatusesController::store` - POST `/settings/delivery-statuses/{id}/update` → `DeliveryStatusesController::update` - POST `/settings/delivery-statuses/{id}/delete` → `DeliveryStatusesController::destroy` - GET /settings/delivery-statuses zwraca 200, widac zakladki - Dodanie nowego statusu przez CRUD pojawia sie na liscie - Proba edycji/usuniecia statusu systemowego jest blokowana (HTTP 400 lub redirect z bledem) - GET /settings/delivery-status-mappings dalej dziala (backward compat) - redirect po save mapowania idzie do /settings/delivery-statuses?tab=mapping AC-2, AC-3, AC-5 spelnione. Task 3: Sidebar, jezyk, badge rendering i dokumentacja resources/views/layouts/app.php, resources/lang/pl.php, resources/views/orders/show.php, resources/views/shipments/prepare.php, .paul/docs/DB_SCHEMA.md, .paul/docs/ARCHITECTURE.md, .paul/docs/TECH_CHANGELOG.md 1. Zaktualizowac `resources/lang/pl.php`: - `navigation.statuses` → 'Statusy zamowien' - Dodac `navigation.delivery_statuses` => 'Statusy przesylek' 2. Zaktualizowac sidebar `resources/views/layouts/app.php`: - Zmieniac istniejaca pozycje "Statusy" (href=/settings/statuses) tylko etykiete na $t('navigation.statuses') (juz uzywana, ale tresc sie zmieni po kroku 1) - Podmiana calego bloku sidebar z "Mapowanie statusow dostawy" (linie ~130-147): * Nowy link "Statusy przesylek" href=/settings/delivery-statuses * active gdy $currentSettings === 'delivery-statuses' LUB $currentSettings === 'delivery-status-mappings' (backward compat) * Badge z liczba niezmapowanych (zachowac istniejaca logike z DeliveryStatusMappingRepository) 3. Zaktualizowac badge rendering: - W `resources/views/orders/show.php` i `resources/views/shipments/prepare.php`: Aktualny wzorzec: `` Zmienic na helper lub inline logike: - Jesli `$pkgDeliveryStatus` jest jednym z 11 stalych — uzyj klasy CSS jak dotychczas - W przeciwnym razie: pobierz kolor z DeliveryStatus::getByKey() i dodaj `style="background-color: "` Wzorzec do uzycia (zdefiniowac helper w DeliveryStatus lub inline w widoku): ```php $statusColor = DeliveryStatus::getColor($pkgDeliveryStatus); $isSystemStatus = in_array($pkgDeliveryStatus, DeliveryStatus::ALL_STATUSES); $badgeClass = 'delivery-badge' . ($isSystemStatus ? ' delivery-badge--' . $pkgDeliveryStatus : ''); $badgeStyle = $isSystemStatus ? '' : 'background-color: ' . $statusColor . ';'; ``` `>` - Dodac do `DeliveryStatus.php` metode `getColor(string $key): string` — pobiera kolor z repozytorium lub zwraca '#6c757d' jako fallback 4. Zaktualizowac `.paul/docs/DB_SCHEMA.md` (nowa tabela delivery_statuses) 5. Zaktualizowac `.paul/docs/ARCHITECTURE.md` (DeliveryStatusRepository, DeliveryStatusesController) 6. Zaktualizowac `.paul/docs/TECH_CHANGELOG.md` (entry dla Phase 108 Plan 01) - Sidebar pokazuje "Statusy zamowien" i "Statusy przesylek", brak "Mapowanie statusow dostawy" - Badge dla istniejacych 11 statusow renderuje sie klasa CSS jak wczesniej - Badge dla nowego custom statusu renderuje sie z inline style - Na stronie zamowien i przesylki brak bledow PHP AC-4, AC-6 spelnione; dokumentacja aktualna.

DO NOT CHANGE

  • Logika eventow automatyzacji, reguł i akcji niezwiazana z lista statusow
  • Provider mapy (INPOST_MAP, APACZKA_MAP, ALLEGRO_MAP) — pozostaja hardcoded compile-time
  • TERMINAL_STATUSES — stala pozostaje w kodzie dla logiki biznesowej; DB is_terminal jest dodatkowa informacja dla UI
  • Runtime konfiguracji DB hostow (DB_HOST / DB_HOST_REMOTE)
  • Istniejace CSS klasy .delivery-badge--{status} dla 11 systemowych statusow

SCOPE LIMITS

  • Brak zmian w logice normalizacji statusow dostawcow (normalize(), normalizeWithOverrides())
  • Brak zmian w cronie ani harmonogramie
  • Automatyzacje — dropdown statusow aktualizowany w Plan 02 (nie w tym planie)
Before declaring plan complete: - [ ] `SELECT COUNT(*) FROM delivery_statuses` = 11 - [ ] GET /settings/delivery-statuses zwraca 200 z dwoma zakladkami - [ ] Dodanie nowego statusu przez CRUD jest widoczne na liscie - [ ] Statusy systemowe sa zablokowane przed edycja i usunieciem - [ ] GET /settings/delivery-status-mappings nadal dziala (200) - [ ] Redirect po save mapowania idzie do /settings/delivery-statuses?tab=mapping - [ ] Sidebar pokazuje "Statusy zamowien" i "Statusy przesylek" - [ ] Badge istniejacych statusow bez regresji - [ ] Badge nowego custom statusu renderuje sie z inline style - [ ] Dokumentacja .paul/docs/* zaktualizowana

<success_criteria>

  • Operator moze dodac nowy status znormalizowany z panelu bez deploymentu
  • Status systemowy (delivered/returned/cancelled) jest nieedytowalny z UI
  • Strona /settings/delivery-statuses ma dwie dzialajace zakladki
  • Sidebar jest uporzadkowany zgodnie z nowa struktura
  • Brak regresji w istniejacym wyswietlaniu odznaczen statusow </success_criteria>
After completion, create `.paul/phases/108-delivery-status-management/108-01-SUMMARY.md`