Phase 112 / Plan 112-01 complete (v3.6): - OrderImportRepository::upsertOrderAggregate split into create vs re-import paths - replaceAddresses/Items/Notes/Shipments/StatusHistory invoked only on first import - new updateOrderDelta() narrows UPDATE to status_code (cond.), payment_status, total_paid, is_canceled_by_buyer, source_updated_at, payload_json, fetched_at - source-side cancellation override (is_canceled_by_buyer=1 OR pull status_code='anulowane') - identical-payload no-op guard via normalizePayloadJson() - fixes case #882: order_items.id stable, project_generated (Phase 97) preserved - Phase 111 payment.status_changed emit retained without regression Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
95 lines
9.0 KiB
Markdown
95 lines
9.0 KiB
Markdown
# Technical Changelog
|
|
|
|
## 2026-05-07 - Phase 112 Plan 01: Re-import Data Protection
|
|
|
|
**Co zrobiono:**
|
|
- `OrderImportRepository::upsertOrderAggregate` - rozdzielono sciezke `created` (pierwszy import) od `else` (re-import). `replaceAddresses`, `replaceItems`, `replaceNotes`, `replacePayments`, `replaceShipments`, `replaceStatusHistory` wywolywane sa teraz wylacznie przy pierwszym imporcie. Logika `paymentTransition` / `statusOverwriteAllowed` (Phase 111) i preservacja `status_code` (Phase 62) bez zmian.
|
|
- `OrderImportRepository::updateOrderDelta()` - nowa prywatna metoda zastepujaca `updateOrder()`. Aktualizuje wylacznie `status_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=1` ze zrodla LUB zmapowany pull `status_code='anulowane'` (Phase 75/83), wymuszone jest `orders.status_code='anulowane'` niezaleznie od `statusOverwriteAllowed`.
|
|
- Identical-payload guard: porownanie znormalizowanego `payload_json` (nowy vs aktualny w DB). Identyczny payload + brak `paymentTransition`/`statusOverwriteAllowed`/`cancelledBySource` -> commit transakcji bez wywolywania UPDATE; `fetched_at` i `updated_at` pozostaja niezmienione.
|
|
- `OrderImportRepository::getCurrentOrderState()` - rozszerzenie `getCurrentStatusAndPaymentStatus()` o kolumne `payload_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 na `order_items` przy kazdym re-imporcie, co (a) zerowalo flagi `project_generated`/`project_generated_at` (Phase 97) na default 0/NULL i (b) zmienialo `order_items.id`, co lamie referencje skryptu batch `tools/generowanie/_batch_run.sh` (`UPDATE ... WHERE id IN (...)`).
|
|
- Phase 111 dodala emisje `payment.status_changed` przy 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_at` przy cyklicznym imporcie tych samych zamowien.
|
|
|
|
**BREAKING:**
|
|
- Brak zmian breaking dla istniejacego API/UI/automatyzacji. Wewnetrznie usunieto `updateOrder()` i `getCurrentStatusAndPaymentStatus()` (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 detekcja `payment_transition`. Teraz porownuje poprzedni `payment_status` z nowym (warunek `0/1 -> 2`) zamiast polegac wylacznie na `status_code='nieoplacone'`. Logika preservacji status_code z Phase 62 (`statusOverwriteAllowed`) zostala wydzielona jako osobna decyzja.
|
|
- `OrderImportRepository::getCurrentStatusAndPaymentStatus()` - nowa metoda pomocnicza zastepujaca `getCurrentStatus()`, zwraca i status_code, i payment_status w jednym SELECT.
|
|
- `AllegroOrderImportService::importSingleOrder` - dodaje emit `payment.status_changed` gdy `payment_transition && !$wasCreated`.
|
|
- `ShopproOrdersSyncService::importOneOrder` - analogiczny emit `payment.status_changed`.
|
|
- `bin/backfill_payment_transition_111.php` - jednorazowy CLI dla zamowien z `payment_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.imported` jest 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 w `ShopproOrdersSyncService` (flaga `payment_transition` byla wykrywana, ale nie emitowala eventu).
|
|
- Regula automatyzacji #7 (`payment.status_changed` -> `update_order_status` na `w_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 i `payment_transition=false`. Brak duplikatow eventow.
|
|
|
|
## 2026-04-28 - Phase 110 Plan 01: Statistics Summary
|
|
|
|
**Co zrobiono:**
|
|
- `/statistics/summary` - nowy widok podsumowania w menu `Statystyki -> 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_groups` i `order_statuses`, wiec migracja DB nie jest potrzebna.
|
|
- Seria `Razem` jest 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 przez `filemtime()`.
|
|
- `resources/views/statistics/orders.php` - filtry `channels[]` i `status_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_status` i akcji `update_shipment_status` ładuje statusy z DB przez `DeliveryStatus::getAllOptions()`
|
|
- Walidacja w `parseConditionValue()` i `parseActionConfig()` używa `DeliveryStatus::getAllStatuses()`
|
|
- `AutomationService` — usunięto stałą `SHIPMENT_STATUS_OPTION_MAP`; ewaluacja `evaluateShipmentStatusCondition()` porównuje klucze bezpośrednio
|
|
- `resolveStatusFromActionKey()` — 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-statuses` i jest on od razu dostępny w dropdownach automatyzacji bez deploymentu
|
|
- Eliminacja kolizji semantycznej: stary klucz grupowy `picked_up` mapował na `delivered` (paczka odebrana przez klienta), nowy klucz DB `picked_up` to "Odebrana przez kuriera" (od nadawcy)
|
|
- BREAKING: stare reguły z grupowymi kluczami (`registered`, `courier_pickup`, `dropped_at_point`, `unclaimed`, `picked_up_return`, oraz `picked_up`/`ready_for_pickup`/`cancelled` w 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_statuses` z seedem 11 statusów (migracja `20260427_000103`)
|
|
- `DeliveryStatusRepository` — CRUD + per-request cache
|
|
- `DeliveryStatus.php` — dynamiczne ładowanie statusów z DB (`setRepository()`)
|
|
- Panel `/settings/delivery-statuses` z 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-color` dla 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
|