Phase 115 complete (vertical slice "zamowienie z NIP -> faktura PDF"):
- Task 1: InvoiceRepository + InvoiceService (dual-flow orchestrator) +
InvoiceIssueException + FakturowniaApiClient::createInvoice + buildPdfUrl
- Task 2: InvoiceController + OrdersController::toggleInvoiceRequested +
OrdersRepository::setInvoiceRequested + auto-import invoice_requested z
Allegro (invoice.required) i shopPRO (5-key flexible parser) + show.php
(toggle w zakladce Platnosci + warunkowy przycisk Wystaw fakture)
- Task 3: Lista wystawionych /settings/accounting/invoices/issued z filtrami
+ invoice_preview + invoice_pdf Dompdf template + hub link
- Task 3b (dodany): NIP lookup przez MF Biala Lista (publiczne API, bez
rejestracji) — MfWhitelistApiClient w src/Core/Http/ + /api/nip/lookup +
przycisk "Pobierz z GUS" w formularzu
Auto-fixes podczas smoke testu (5):
- GUS endpoint Fakturowni nie istnial (HTML 404 -> "json is not valid");
switch na MF Biala Liste
- PHP 8.5 curl_close() deprecation wycieka HTML przed JSON; usuniete z
MfWhitelistApiClient i FakturowniaApiClient (3 miejsca)
- Fakturownia 422 payment_to_kind_days (nieistniejace pole) -> usuniete
- Generic "error" w 422 -> parser plaskuje errors: {pole: [...]} +
error_log z 1000 znakow raw body
- Fakturownia security odrzuca seller_*/department_id jako "create new
department"; usuniete z payloadu (Fakturownia uzywa danych konta)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
22 KiB
22 KiB
Technical Changelog
2026-05-10 - Phase 115 Plan 01: Wystawianie faktury z zamowienia
Co zrobiono:
- GUS lookup (Task 3b):
FakturowniaApiClient::lookupClientByNip()— GET/clients/gus.json?nip=...&api_token=...z walidacja NIP (10 cyfr) i parsowaniem{name, tax_no, street, post_code, city, country}.InvoiceController::gusLookup()jako AJAX endpointGET /api/fakturownia/gus-lookup?nip=...&config_id=...— resolwer konta Fakturownia (preferujeinvoice_configs.integration_idgdy delegated, fallback na pierwsze aktywne konto type='fakturownia').invoice_form.phpma przycisk "Pobierz z GUS" obok pola NIP — vanilla JS fetch + auto-fillbuyer_company_name/street/postal_code/city. Czyni "wystaw fakture" jednoklikowym po wpisaniu NIP.InvoiceControllerctor rozszerzony o 2 deps (FakturowniaIntegrationRepository,FakturowniaApiClient). InvoiceRepository(nowy) — CRUD dlainvoices:findByOrderId,findById,insertLocal,insertDelegated,nextLocalNumber(atomowy INSERT ON DUPLICATE KEY UPDATE nainvoice_number_counters),paginatez filtrami search/config/mode/date.InvoiceService(nowy) — orchestrator wystawiania:issue()rozgalezia naissueLocal()(numer lokalny + Dompdf-ready snapshot) lubissueDelegated()(POST do Fakturowni PRZED INSERT lokalnym; na sukcesie zapisexternal_invoice_id/external_pdf_url/invoice_numberz odpowiedzi).- Static
extractBuyerTaxNumber()parsuje NIP z roznych sciezek payload_json (Allegroinvoice.address.taxId, shopPRObuyer.tax_numberi pokrewne). - Snapshot pattern (snapshoty seller/buyer/items z Phase 8 wzorca) + obliczanie netto/brutto z VAT per pozycja.
buildFakturowniaPayload()mapuje na formathttps://app.fakturownia.pl/api.
InvoiceIssueException(nowy) — typed exception dla bledow biznesowych.FakturowniaApiClientrozszerzony — nowecreateInvoice()(POST/invoices.jsonz body{api_token, invoice}, parsuje response naid/number/view_url/pdf_url/raw) ibuildPdfUrl()(helper bez fetcha, do redirect 302). Stary stubdownloadPdf()zastapiony.InvoiceController(nowy) — endpointy dla zamowienia (create,store,show,pdf) i listaissuedList. PDF: lokalna -> Dompdf inline (mirrorReceiptController::pdf); delegowana -> redirect 302 naexternal_pdf_url. Walidacjainvoice_requested=1przed otwarciem formularza.OrdersController::toggleInvoiceRequested(nowy) — AJAX endpoint POST/orders/{id}/invoice-requested/togglez CSRF +recordActivity('invoice_requested_changed').OrdersRepository::setInvoiceRequested(int, bool)(nowy) — UPDATEorders.invoice_requested.OrdersController::show()rozszerzone — przekazujeinvoices+invoiceConfigsdo widoku przez 2 nowe optional ctor params (InvoiceRepository,InvoiceConfigRepository).AllegroOrderImportService::importSingleOrder— przywasCreated=trueipayload.invoice.required-> ustawiaorders.invoice_requested=1(delta-only re-import nie nadpisuje manualnej flagi).ShopproOrdersSyncService::shouldRequestInvoice()(nowy) — flexible parser sprawdzajacywants_invoice,invoice_required,invoice.required,buyer.wants_invoice,buyer.invoicew raw payload. Dziala tylko przy pierwszym imporcie.- 4 nowe widoki:
resources/views/accounting/invoice_form.php— formularz wystawiania (config select z label "Lokalnie/Fakturownia: {prefix}", NIP prefilled z payload, manualne nadpisanie buyer fields, podglad pozycji + sprzedawca; confirm dialog dla powtornego wystawienia uzywaOrderProAlerts.confirmz options-object API).resources/views/accounting/invoice_preview.php— HTML preview faktury z badgem trybu (Lokalnie/Fakturownia: {prefix}) + dual-button PDF.resources/views/accounting/invoice_pdf.php— Dompdf template "FAKTURA VAT" z parties section, items table (netto/brutto/VAT per pozycja), termin platnosci, konto bankowe.resources/views/accounting/invoices_issued_list.php— lista wystawionych faktur z filtrami (search/config/mode/date_from/date_to) + paginacja + badge Tryb (Lokalnie / Fakturownia: {prefix}).
resources/views/orders/show.php— header dostaldata-invoice-button-wrapz przyciskiem "Wystaw fakture" (visible tylko gdyinvoice_requested=1); pod headerem checkboxdata-invoice-requested-toggle(auto-bound przezinvoice-requested-toggle.js); pod tabem "Documents" nowa sekcja "Faktury" z badgem Tryb i akcjami Podglad/PDF.resources/views/settings/accounting.php— link "Faktury wystawione" w karcie Faktury.public/assets/js/modules/invoice-requested-toggle.js(nowy) — vanilla JS, AJAX POST z_tokenprzychange, rollback checkbox state przy bledzie, optimistic show/hidedata-invoice-button-wrap. Idempotent guarddata-bound.resources/views/layouts/app.php— rejestracjainvoice-requested-toggle.jsz cache-busting.- 6 nowych routes:
POST /orders/{id}/invoice-requested/toggle,GET /orders/{id}/invoice/create,POST /orders/{id}/invoice/store,GET /orders/{id}/invoice/{invoiceId},GET /orders/{id}/invoice/{invoiceId}/pdf,GET /settings/accounting/invoices/issued. Wszystkie podAuthMiddleware. - DI wiring w
routes/web.php:InvoiceRepository,InvoiceService,InvoiceController.OrdersControllerinstantiation rozszerzona o 2 params ($invoiceRepository,$invoiceConfigRepository). - Docs:
architecture.md(nowa sekcja "Phase 115"),tech_changelog.md,todo.md(INVOICE-IDEMP-115).
Dlaczego:
- Plan zatwierdzony jako pelny vertical slice — Phase 113 (DB) + 114 (configs CRUD) byly fundamentem; bez wystawiania z zamowienia funkcja byla bezuzyteczna operacjonalnie.
- Kolejnosc "POST do Fakturowni najpierw, INSERT lokalny po sukcesie" (per user decision) gwarantuje ze nie ma orphan rows w
invoicesgdy API padnie. Trade-off: brak idempotencji przy double-POST -> notatka INVOICE-IDEMP-115. - Auto-set
invoice_requestedz importu pozwala operatorowi nie myslec o flagi dla typowych przypadkow (Allegroinvoice.required=true, shopPRO klienci wpisujacy NIP). Manualny toggle dla edge cases / korekty. - Dual PDF flow (Dompdf lokalny / redirect 302 do Fakturowni) — operator dostaje "natywny" PDF z Fakturowni dla delegowanych (ten sam co ksiegowy widzi w Fakturowni), brak ryzyka rozjazdu wygladu/numeracji.
is_delegated=0config zachowany jako pelny tryb lokalny — dla scenariuszy gdzie operator nie chce/nie ma konta Fakturowni.
BREAKING: Brak. OrdersController ctor dostal 2 NEW optional params (default null) — backwards compatible.
Szczegolne uwagi:
invoice-config-form.js(Phase 114) iinvoice-requested-toggle.js(Phase 115) — dwa rozne moduly, oba wlayouts/app.php. Nazwa "invoice-*" ale rozne kontekstowo (jeden formularz config, drugi zamowienie).OrderProAlerts.confirmwinvoice_form.phpuzywa options-object API zgodnie z memory (Phase 114 fix).- Migracje no-op nie potrzebne — Phase 113 dostarczyla
orders.invoice_requested,invoice_*tabele ifakturownia_integration_settings. - Skill audit: brak required skills dla tego planu.
2026-05-10 - Phase 114 Plan 01: Accounting Configs Refactor
Co zrobiono:
- Migracja
20260511_000107_seed_default_invoice_config.sql- idempotentny seedDomyslny VATconfig (formatFV/%N/%M/%Y, monthly, lokalna numeracja, payment_to_days=7) przezNOT EXISTSguard. InvoiceConfigRepository- pelne CRUDinvoice_configsz walidacja serwerowa wszystkich pol + krytyczna regula delegacji:is_delegated=1wymagaintegration_idzintegrations.type='fakturownia'.delete()pre-checkujeinvoiceszeby zwrocic PL komunikat zamiast SQLSTATE.InvoiceConfigController- index/edit/save/toggle/delete dla/settings/accounting/invoices. Flashaccounting.invoices.save/.error. Edycja na osobnej podstronie.ReceiptConfigControllerrefactor - rozdzielenieindex()nahub()+list()+ nowaedit(). Save/toggle/delete redirectuja na/settings/accounting/receipts(nie hub).- 4 nowe widoki:
accounting.php(REFAKTOR) - hub-rozdroze z 2 kartami Paragony/Faktury (usunieta lista i formularz inline).accounting-receipts.php- lista paragonow w stylu spojnym z fakturami (table.table + badge).accounting-receipt-edit.php- formularz paragonu na osobnej podstronie.accounting-invoices.php- lista faktur (7 kolumn z dodatkami Tryb, Konto Fakturowni).accounting-invoice-edit.php- formularz faktury z conditionalintegration_idselect.
invoice-config-form.js- vanilla JS toggle dlais_delegatedcheckbox -> show/hideintegration_idselect wrapper + dynamicznyrequired.layouts/app.php- rejestracja nowego modulu JSinvoice-config-form.js(z cache-busting?ver=mtime).- Routy: 12 nowych endpointow ksiegowosci (6 receipts + 6 invoices) + 3 legacy aliasy starych
/settings/accounting/save|toggle|delete. - Docs:
db_schema.md(notka o seed),architecture.md(nowa sekcja "Phase 114"), tech_changelog.
Dlaczego:
- Phase 113 dostarczyl tabele
invoice_configs- bez UI/CRUD nie da sie operacjonalnie wystawiac faktur. Phase 115 (wystawianie faktury z zamowienia) wymaga gotowych invoice_configs. - Edycja paragonu pod tabela na
/settings/accounting(stary uklad) miala 2 problemy: dlugi scroll przy wielu configach + brak miejsca na rosnacy formularz faktury (z conditional fields). Refaktor na osobne podstrony rozwiazuje oba. - Seed
Domyslny VAT- operator od razu po deployment moze wystawiac faktury bez recznego skonfigurowania (zerowy onboarding). - Legacy aliasy
/settings/accounting/save|toggle|delete- istniejace formularze w cache'u przegladarki / bookmarki / zewnetrzne narzedzia (jesli sa) dalej dzialaja bez 404. - JS toggle - operator nie zaznaczy delegacji bez wybrania konta (UX + serwerowa walidacja jako backup).
BREAKING:
- Brak. Stare endpointy zachowane jako aliasy. Stary widok
/settings/accountingto teraz hub z linkami zamiast listy - operator wie ze trzeba kliknac "Zarzadzaj paragonami".
2026-05-10 - Phase 113 Plan 01: Fakturownia Integration Foundation
Co zrobiono:
- Migracje SQL:
20260510_000104_create_invoices_tables.sql- cztery nowe tabele:invoice_configs,invoices,invoice_number_counters,fakturownia_integration_settings(multi-account,integration_id UNIQUE FK->integrations).20260510_000105_add_invoice_requested_to_orders.sql-orders.invoice_requested TINYINT(1) NOT NULL DEFAULT 0+ indexidx_orders_invoice_requested.20260510_000106_seed_fakturownia_integration_type.sql- no-op placeholder dokumentujacy uznanieintegrations.type='fakturownia'jako oficjalnie wspieranego.
FakturowniaIntegrationRepository- CRUD kont Fakturowni z resolved encryption (integrations.api_key_encryptedjako zrodlo prawdy,settings.api_token_encryptedjako cache).findAll/findByIntegrationId/save/delete/getDecryptedToken.FakturowniaApiClient::testConnection()- GEThttps://{prefix}.fakturownia.pl/account.json?api_token=...z cURL +SslCertificateResolver.createInvoice/downloadPdfjako STUB-y rzucajaceRuntimeException(do implementacji w kolejnym planie).IntegrationsRepository::updateTestResult()- nowa publiczna metoda do zapisulast_test_status / last_test_http_code / last_test_message / last_test_at. Wykorzystywana przezFakturowniaIntegrationController::test().FakturowniaIntegrationController- lista (/settings/integrations/fakturownia), edycja (/edit,/new), save, test, delete. CSRF via_token, flashfakturownia.save/.test/.error.- Widoki
resources/views/settings/fakturownia.php(lista z badge'ami) iresources/views/settings/fakturownia-edit.php(form: name, account_prefix, api_token, department_id, default_kind, default_payment_to_days, is_active). IntegrationsHubController::buildFakturowniaRow()- karta Fakturowni w hubie/settings/integrationsz agregowanym statusem wszystkich kont.- Routy w
routes/web.php(get /index,get /new,get /edit,post /save,post /test,post /delete) + DI wiring ($fakturowniaIntegrationRepository,$fakturowniaApiClient,$fakturowniaIntegrationController). - Dokumentacja:
db_schema.md(sekcja "Invoices" +fakturownia_integration_settings+ kolumnaorders.invoice_requested, total tables 55 -> 59),architecture.md(sekcja "Phase 113").
Dlaczego:
- v3.7 Invoices wprowadza wystawianie faktur dla klientow wymagajacych dokumentu z NIP (clarifications:
orders.invoice_requestedz importera + manual override). Bez fundamentu DB i konfiguracji konta Fakturowni zaden kolejny plan v3.7 (CRUD configs, wystawianie, lista) nie ma sensu. - Multi-account przez
integrations.type='fakturownia'zachowuje spojnosc z Allegro/shopPRO (rozne instancje) i pozwala na rozne konta Fakturowni dla roznych marek/oddzialow. is_delegatedflag winvoice_configsumozliwia w przyszlym planie dwa tryby: lokalna numeracja+PDF dompdf (default) lub delegacja do Fakturowni (numer+PDF z API).- STUB-y
createInvoice/downloadPdfcelowo rzucaja exception zamiast "TODO" - kazda przedwczesna probaba uzycia rzuci jasny blad zamiast cichego no-op. IntegrationsRepository::updateTestResult()jest reusable - przyszle integracje (np. kolejne API) beda mogly korzystac z tej samej metody zamiast inline UPDATE.
BREAKING:
- Brak zmian breaking.
IntegrationsHubControllerma nowy parametr konstruktora (FakturowniaIntegrationRepository) - wszystkie miejsca wywolania zaktualizowane.
2026-05-07 - Phase 112 Plan 01: Re-import Data Protection
Co zrobiono:
OrderImportRepository::upsertOrderAggregate- rozdzielono sciezkecreated(pierwszy import) odelse(re-import).replaceAddresses,replaceItems,replaceNotes,replacePayments,replaceShipments,replaceStatusHistorywywolywane sa teraz wylacznie przy pierwszym imporcie. LogikapaymentTransition/statusOverwriteAllowed(Phase 111) i preservacjastatus_code(Phase 62) bez zmian.OrderImportRepository::updateOrderDelta()- nowa prywatna metoda zastepujacaupdateOrder(). Aktualizuje wylaczniestatus_code,payment_status,total_paid,is_canceled_by_buyer,source_updated_at,payload_json,fetched_at,updated_at. Pozostale kolumny zamowienia nie sa nadpisywane przez re-import.- Propagacja anulowania: gdy
is_canceled_by_buyer=1ze zrodla LUB zmapowany pullstatus_code='anulowane'(Phase 75/83), wymuszone jestorders.status_code='anulowane'niezaleznie odstatusOverwriteAllowed. - Identical-payload guard: porownanie znormalizowanego
payload_json(nowy vs aktualny w DB). Identyczny payload + brakpaymentTransition/statusOverwriteAllowed/cancelledBySource-> commit transakcji bez wywolywania UPDATE;fetched_atiupdated_atpozostaja niezmienione. OrderImportRepository::getCurrentOrderState()- rozszerzeniegetCurrentStatusAndPaymentStatus()o kolumnepayload_json(jeden SELECT zamiast dwoch).OrderImportRepository::normalizePayloadJson()- nowy helper deserializujacy/reserializujacy payload do porownywalnej formy (JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES).
Dlaczego:
- Bug case #882: po re-imporcie zamowienia produkty w
/orders/{id}wyswietlaly badge "Brak projektu" mimo wczesniej wygenerowanych projektow PSD. Przyczyna:replaceItems()wykonywal DELETE+INSERT naorder_itemsprzy kazdym re-imporcie, co (a) zerowalo flagiproject_generated/project_generated_at(Phase 97) na default 0/NULL i (b) zmienialoorder_items.id, co lamie referencje skryptu batchtools/generowanie/_batch_run.sh(UPDATE ... WHERE id IN (...)). - Phase 111 dodala emisje
payment.status_changedprzy re-imporcie z tranzycja platnosci, wiec re-import istniejacych zamowien stal sie regularny - problem #882 stal sie regresja systemowa, nie krawedziem. - Zamowienia (orderPRO jako narzedzie zarzadzania) sa edytowane w aplikacji, nie w zrodle (Allegro/shopPRO). Re-import ze zrodla powinien aktualizowac wylacznie stan platnosci, anulowanie i znaczniki synchronizacji - reszta jest stanem lokalnym.
- Identical-payload guard eliminuje niepotrzebne write'y do binloga/replikacji oraz sztuczne odswiezanie
updated_atprzy cyklicznym imporcie tych samych zamowien.
BREAKING:
- Brak zmian breaking dla istniejacego API/UI/automatyzacji. Wewnetrznie usunieto
updateOrder()igetCurrentStatusAndPaymentStatus()(oba private) - referencje zewnetrzne nie istnialy. - Backfill zamowien z resetowanymi flagami
project_generated(np. #882) wykonywany recznie przez operatora poza zakresem planu.
2026-05-05 - Phase 111 Plan 01: Payment Transition Event
Co zrobiono:
OrderImportRepository::upsertOrderAggregate- rozszerzona detekcjapayment_transition. Teraz porownuje poprzednipayment_statusz nowym (warunek0/1 -> 2) zamiast polegac wylacznie nastatus_code='nieoplacone'. Logika preservacji status_code z Phase 62 (statusOverwriteAllowed) zostala wydzielona jako osobna decyzja.OrderImportRepository::getCurrentStatusAndPaymentStatus()- nowa metoda pomocnicza zastepujacagetCurrentStatus(), zwraca i status_code, i payment_status w jednym SELECT.AllegroOrderImportService::importSingleOrder- dodaje emitpayment.status_changedgdypayment_transition && !$wasCreated.ShopproOrdersSyncService::importOneOrder- analogiczny emitpayment.status_changed.bin/backfill_payment_transition_111.php- jednorazowy CLI dla zamowien zpayment_status=2 && status_code='nieoplacone'(allegro + shoppro), idempotentny, wzorzec z Phase 98.
Dlaczego:
- Zamowienie #864 (Allegro) zaimportowane 10s po zlozeniu, gdy Allegro jeszcze nie potwierdzilo platnosci. Re-import 2 minuty pozniej zaktualizowal payment_status na 2, ale
order.importedjest gated przez$wasCreated(Phase 98), wiec automatyzacja "Zmien status na w realizacji (allegro)" nigdy nie odpalila. - Allegro nie mial odpowiednika
ShopproPaymentStatusSyncService, wiec tranzycja platnosci znikala cicho. ShopPRO mial analogiczna luke wShopproOrdersSyncService(flagapayment_transitionbyla wykrywana, ale nie emitowala eventu). - Regula automatyzacji #7 (
payment.status_changed->update_order_statusnaw_realizacji) nie ma warunku integration_id, wiec po wyemitowaniu eventu obejmie zarowno Allegro jak i shopPRO. - Idempotencja zalatwiona przez logike repo: po pierwszej tranzycji DB ma
payment_status=2, kolejny re-import widzi old=2/new=2 ipayment_transition=false. Brak duplikatow eventow.
2026-04-28 - Phase 110 Plan 01: Statistics Summary
Co zrobiono:
/statistics/summary- nowy widok podsumowania w menuStatystyki -> Podsumowanie.OrdersStatisticsController::summary()- buduje miesieczny view-model dla wykresow liczby i wartosci zamowien.OrdersStatisticsRepository::aggregateByMonth()- agreguje istniejace zamowienia po miesiacu i kanale/integracji.public/assets/js/modules/statistics-summary-charts.js- renderer dwoch interaktywnych wykresow liniowych oparty o Chart.js 4.4.8 CDN.resources/views/statistics/summary.php- filtry zgodne z raportem dziennym, dwa wykresy obok siebie na desktopie oraz dwie tabele fallback pod nimi.- Domyslny poczatek historii ustawiony na
2026-04-01(04-2026) mimo starszych danych.
Dlaczego:
- Operator potrzebuje szybkiego trendu miesiecznego przed przejsciem do szczegolowych dziennych statystyk.
- Wykresy uzywaja obecnych tabel
orders,integrations,order_status_groupsiorder_statuses, wiec migracja DB nie jest potrzebna. - Seria
Razemjest liczona z tych samych danych co serie integracji, co ulatwia sprawdzenie sum miesiecznych.
2026-04-28 - Phase 109 Plan 01: Checkbox Multiselect Filters
Co zrobiono:
public/assets/js/modules/checkbox-multiselect.js- nowy vanilla JS enhancer dla natywnych<select multiple data-checkbox-multiselect>.resources/views/layouts/app.php- globalne podpiecie modulu z cache busting przezfilemtime().resources/views/statistics/orders.php- filtrychannels[]istatus_groups[]oznaczone do progresywnego ulepszenia bez zmiany nazw pol formularza.resources/scss/app.scss- kompaktowe style dropdownu z checkboxami i opcja "Wszystkie".
Dlaczego:
- Natywne selecty multiple byly malo czytelne i zajmowaly za duzo miejsca w filtrach statystyk.
- Zachowanie oryginalnego selecta w DOM utrzymuje obecny kontrakt GET i fallback bez JavaScript.
- Brak zmian w schemacie DB i logice agregacji statystyk.
Chronologiczny log zmian technicznych — co i dlaczego.
2026-04-27 — Phase 108 Plan 02: Automation Dropdowns z DB
Co zrobiono:
AutomationController— usunięto stałąSHIPMENT_STATUS_OPTIONS(8 grupowych kluczy)- Dropdown statusów w warunku
shipment_statusi akcjiupdate_shipment_statusładuje statusy z DB przezDeliveryStatus::getAllOptions() - Walidacja w
parseConditionValue()iparseActionConfig()używaDeliveryStatus::getAllStatuses() AutomationService— usunięto stałąSHIPMENT_STATUS_OPTION_MAP; ewaluacjaevaluateShipmentStatusCondition()porównuje klucze bezpośrednioresolveStatusFromActionKey()— bezpośredni klucz statusu z DB jako target (zamiast pierwszego z grupy)
Dlaczego:
- Zamknięcie integracji z Plan 01 — operator dodaje status w
/settings/delivery-statusesi jest on od razu dostępny w dropdownach automatyzacji bez deploymentu - Eliminacja kolizji semantycznej: stary klucz grupowy
picked_upmapował nadelivered(paczka odebrana przez klienta), nowy klucz DBpicked_upto "Odebrana przez kuriera" (od nadawcy) - BREAKING: stare reguły z grupowymi kluczami (
registered,courier_pickup,dropped_at_point,unclaimed,picked_up_return, orazpicked_up/ready_for_pickup/cancelledw starym znaczeniu) nie matchują — wymagają ręcznego odtworzenia z nowymi kluczami DB
2026-04-27 — Phase 108 Plan 01: Delivery Status Management
Co zrobiono:
- Tabela
delivery_statusesz seedem 11 statusów (migracja20260427_000103) DeliveryStatusRepository— CRUD + per-request cacheDeliveryStatus.php— dynamiczne ładowanie statusów z DB (setRepository())- Panel
/settings/delivery-statusesz CRUD (zakładka "Statusy") i mapowaniem (zakładka "Mapowanie dostawy") - Sidebar: "Statusy" → "Statusy zamówień", nowe "Statusy przesyłek" z badge niezmapowanych
- Badge przesyłek: inline CSS custom property
--status-colordla niestandardowych statusów
Dlaczego:
- Dodanie nowego statusu wymagało zmiany kodu + deploymentu; teraz z UI
- Operator może definiować własne statusy znormalizowane bez ingerencji w kod