Files
orderPRO/.paul/codebase/tech_changelog.md
Jacek Pyziak 2382018739 feat(113): fakturownia integration foundation
Phase 113 complete (v3.7 Invoices):
- DB: invoices, invoice_configs, invoice_number_counters, fakturownia_integration_settings + orders.invoice_requested
- FakturowniaIntegrationRepository (multi-account via integrations.type='fakturownia')
- FakturowniaApiClient (testConnection; createInvoice/downloadPdf STUBs)
- IntegrationsRepository::updateTestResult() (reusable test-result writer)
- /settings/integrations/fakturownia (list + edit + test + delete)
- Karta Fakturownia w hubie /settings/integrations

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 22:11:55 +02:00

12 KiB

Technical Changelog

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 + index idx_orders_invoice_requested.
    • 20260510_000106_seed_fakturownia_integration_type.sql - no-op placeholder dokumentujacy uznanie integrations.type='fakturownia' jako oficjalnie wspieranego.
  • FakturowniaIntegrationRepository - CRUD kont Fakturowni z resolved encryption (integrations.api_key_encrypted jako zrodlo prawdy, settings.api_token_encrypted jako cache). findAll/findByIntegrationId/save/delete/getDecryptedToken.
  • FakturowniaApiClient::testConnection() - GET https://{prefix}.fakturownia.pl/account.json?api_token=... z cURL + SslCertificateResolver. createInvoice/downloadPdf jako STUB-y rzucajace RuntimeException (do implementacji w kolejnym planie).
  • IntegrationsRepository::updateTestResult() - nowa publiczna metoda do zapisu last_test_status / last_test_http_code / last_test_message / last_test_at. Wykorzystywana przez FakturowniaIntegrationController::test().
  • FakturowniaIntegrationController - lista (/settings/integrations/fakturownia), edycja (/edit, /new), save, test, delete. CSRF via _token, flash fakturownia.save/.test/.error.
  • Widoki resources/views/settings/fakturownia.php (lista z badge'ami) i resources/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/integrations z 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 + kolumna orders.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_requested z 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_delegated flag w invoice_configs umozliwia w przyszlym planie dwa tryby: lokalna numeracja+PDF dompdf (default) lub delegacja do Fakturowni (numer+PDF z API).
  • STUB-y createInvoice/downloadPdf celowo 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. IntegrationsHubController ma nowy parametr konstruktora (FakturowniaIntegrationRepository) - wszystkie miejsca wywolania zaktualizowane.

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