# Changelog shopPRO Logi zmian z migracji na Domain-Driven Architecture. Najnowsze na gorze. --- ## ver. 0.325 (2026-02-27) - Fix changelog encoding + limit wyświetlania - **FIX**: `updates/changelog.php` — naprawione krzaczki (mojibake) w polskich znakach; dane odbudowane z plików manifest - **NEW**: `updates/changelog-data.html` — czyste dane changelog oddzielone od logiki PHP - **REFACTOR**: `updates/changelog.php` — konwersja ze statycznego HTML na skrypt PHP: `Content-Type: utf-8`, parsowanie wpisów, filtrowanie po wersji - **NEW**: Parametr `?ver=X.XXX` — ogranicza changelog do 5 wersji wstecz od wersji instancji - **UPDATE**: `admin/templates/update/main-view.php` — przekazuje `?ver=` do URL changelog - **UPDATE**: `build-update.ps1` — nowe wpisy dopisywane do `changelog-data.html` zamiast `changelog.php` --- ## ver. 0.324 (2026-02-27) - System kolejki zadań cron - **NEW**: `Domain\CronJob\CronJobType` — stałe typów zadań, priorytetów, statusów, exponential backoff - **NEW**: `Domain\CronJob\CronJobRepository` — CRUD na `pp_cron_jobs` + `pp_cron_schedules` (enqueue, fetchNext, markCompleted, markFailed, hasPendingJob, cleanup, recoverStuck, getDueSchedules, touchSchedule) - **NEW**: `Domain\CronJob\CronJobProcessor` — orkiestracja: rejestracja handlerów, tworzenie scheduled jobs, przetwarzanie kolejki z priorytetami i retry/backoff - **NEW**: Tabele `pp_cron_jobs` i `pp_cron_schedules` — kolejka zadań z priorytetami, exponential backoff, harmonogram cykliczny - **REFACTOR**: `cron.php` — zastąpienie monolitycznego ~550 linii orkiestratorem z CronJobProcessor i zarejestrowanymi handlerami - **REFACTOR**: `OrderAdminService::queueApiloSync()` — kolejkowanie przez `CronJobRepository::enqueue()` zamiast pliku JSON - **REFACTOR**: `OrderAdminService::syncApiloPayment()`, `syncApiloStatus()` — zmiana z private na public (używane przez handlery cron) - **REMOVED**: `OrderAdminService::processApiloSyncQueue()`, `loadApiloSyncQueue()`, `saveApiloSyncQueue()`, `apiloSyncQueuePath()`, stała `APILO_SYNC_QUEUE_FILE` - **NEW**: Jednorazowa migracja JSON queue → DB w cron.php (automatyczna przy pierwszym uruchomieniu) - **SECURITY**: `cron.php` — ochrona endpointu: wymaga `$config['cron_key']` w URL (`?key=...`) lub trybu CLI - **FIX**: `CronJobRepository::fetchNext()` — re-SELECT po UPDATE eliminuje race condition przy równoległych workerach - **FIX**: `cron.php` — null check dla `$mdb->query()` przed `->fetch()` / `->fetchAll()` (3 miejsca) - **FIX**: `cron.php` — walidacja odpowiedzi curl w APILO_PRODUCT_SYNC i APILO_PRICELIST_SYNC (zapobiega zapisaniu null do bazy) - **FIX**: DI wiring — `CronJobRepository` przekazywany do `OrderAdminService` we wszystkich 4 punktach: `admin\App`, `api\ApiRouter`, `front\App`, `cron.php` - **TESTS**: 41 nowych testów CronJob (CronJobTypeTest, CronJobRepositoryTest, CronJobProcessorTest) - **MIGRATION**: `migrations/0.324.sql` --- ## ver. 0.323 (2026-02-24) - Import zdjęć, trwałe usuwanie, fix API upload - **FIX**: `IntegrationsRepository::shopproImportProduct()` — kompletny refactor importu zdjęć: walidacja HTTP response, curl timeouty, bezpieczna budowa URL, szczegółowy log do `logs/shoppro-import-debug.log` i `error_log`, czytelny komunikat z wynikiem - **FIX**: `ProductRepository::saveProduct()` — `saveCustomFields()` wywoływane tylko gdy klucz `custom_field_name` istnieje w danych (partial update przez API nie czyści custom fields) - **FIX**: `ProductRepository::delete()` — usuwanie rekordów z `pp_shop_products_custom_fields` przy kasowaniu produktu - **FIX**: `ProductsApiController::upload_image()` — poprawka ścieżki uploadu (`upload/` zamiast `../upload/` — api.php działa z rootu projektu) - **NEW**: `ProductArchiveController::delete_permanent()` — trwałe usunięcie produktu z archiwum (wraz ze zdjęciami i załącznikami) - **NEW**: Przycisk "Usuń trwale" w liście produktów archiwalnych z potwierdzeniem --- ## ver. 0.318 (2026-02-24) - ShopPRO export produktów + API endpoints - **NEW**: `IntegrationsRepository::shopproExportProduct()` — eksport produktu do zdalnej instancji shopPRO: pola główne, tłumaczenia, custom fields, zdjęcia przez API (base64) - **NEW**: `IntegrationsRepository::sendImageToShopproApi()` — wysyłka zdjęć do remote API shopPRO (endpoint `upload_image`) z base64 - **REFACTOR**: `shopproImportProduct()` — wydzielono `shopproDb()` i `missingShopproSetting()` jako prywatne helpery; dodano import `security_information`, `producer_id`, custom fields i `alt` zdjęcia - **NEW**: `AttributeRepository::ensureAttributeForApi()` i `ensureAttributeValueForApi()` — idempotent find-or-create dla atrybutów i ich wartości (integracje API) - **NEW**: API endpoint `POST /api.php?endpoint=dictionaries&action=ensure_attribute` — utwórz lub znajdź atrybut po nazwie i typie - **NEW**: API endpoint `POST /api.php?endpoint=dictionaries&action=ensure_attribute_value` — utwórz lub znajdź wartość atrybutu po nazwie - **NEW**: API endpoint `POST /api.php?endpoint=products&action=upload_image` — przyjmuje zdjęcie produktu jako base64 JSON, zapisuje plik i rekord w `pp_shop_products_images` - **NEW**: `IntegrationsController::shoppro_product_export()` — akcja admina eksportująca produkt do shopPRO - **NEW**: Przycisk "Eksportuj do shopPRO" w liście produktów (widoczny gdy shopPRO enabled) - **NEW**: Pole "API key" w ustawieniach integracji shopPRO (`shoppro-settings.php`) --- ## ver. 0.317 (2026-02-23) - Klucz API: przycisk generowania + fix zapisu - **FIX**: `SettingsRepository::saveSettings()` — pole `api_key` brakowało w whiteliście zapisywanych pól, przez co wartość była tracona przy każdym zapisie (TRUNCATE + insert) - **NEW**: Pole "Klucz API" w ustawieniach — przycisk "Generuj" do losowego 32-znakowego klucza alfanumerycznego, usunięto "(ordersPRO)" z nazwy - **FIX**: `api.php` — routing API przeniesiony przed ładowanie globalnych settings (wczesne wyjście), obsługa błędów przez `\Throwable` - **FIX**: `ApiRouter` — catch `\Throwable` zamiast `\Exception` dla pełniejszego łapania błędów --- ## ver. 0.316 (2026-02-23) - Migracja brakującej kolumny type w custom fields - **FIX**: Dodanie brakującej kolumny `type` w tabeli `pp_shop_products_custom_fields` — kolumna była używana w kodzie od v0.277 ale nigdy nie miała migracji ALTER TABLE, przez co instancje ze starszą bazą dostawały `PDOException: Column not found: 1054 Unknown column 'type'` przy zapisie produktu --- ## ver. 0.315 (2026-02-23) - Fix listowania atrybutów w admin - **FIX**: `AttributeRepository::listForAdmin()` — zapytanie COUNT dostawało parametr `:default_lang_id` którego nie miało w SQL, powodując `PDOException: SQLSTATE[HY093]: Invalid parameter number`. Parametr potrzebny tylko w głównym SELECT, nie w COUNT --- ## ver. 0.314 (2026-02-23) - Fix wyszukiwarki admin + title zamówienia - **FIX**: Globalna wyszukiwarka w panelu admina przestała zwracać wyniki — dodano `Content-Type: application/json` i `Cache-Control: no-store` (zapobiega cache'owaniu przez proxy/CDN), zmiana AJAX z GET na POST, `fetchAll(PDO::FETCH_ASSOC)`, top-level try/catch z gwarantowaną odpowiedzią JSON - **NEW**: `document.title` w widoku szczegółów zamówienia pokazuje numer zamówienia (np. "Zamówienie ZAM/123 - shopPro") --- ## ver. 0.313 (2026-02-23) - Fix sync płatności Apilo + logowanie - **FIX**: `syncApiloPayment()` i `syncApiloStatus()` — `(int)` cast na `apilo_order_id` (format `"PPxxxxxx"`) dawał `0`, przez co metody pomijały sync z API Apilo. Zmiana na `empty()` - **NEW**: Logowanie w `syncApiloPaymentIfNeeded()` i `syncApiloStatusIfNeeded()` — każda ścieżka decyzyjna (Apilo wyłączone, brak tokenu, brak `apilo_order_id`, sync nieudany) zapisuje wpis do `pp_log` z kontekstem --- ## ver. 0.312 (2026-02-23) - Fix krytycznych bugów integracji Apilo - **FIX**: `curl_getinfo()` wywoływane po `curl_close()` — HTTP code zawsze wynosił 0, uniemożliwiając prawidłową obsługę odpowiedzi Apilo - **FIX**: Nieskończona pętla wysyłania zamówienia — gdy Apilo zwracało błąd serwera, zamówienie nie dostawało `apilo_order_id` i było ponownie wybierane w każdym cyklu crona. Teraz błędne zamówienia oznaczane `apilo_order_id = -1` z powiadomieniem email - **FIX**: Ceny produktów 0.00 PLN w Apilo — string `"0.00"` z MySQL jest truthy w PHP, więc ternary wybierał `price_brutto_promo` (0.00) zamiast `price_brutto`. Zmiana na `(float)... > 0` - **FIX**: Walidacja cen przed wysyłką — zamówienia z zerowymi cenami produktów nie są wysyłane do Apilo (`apilo_order_id = -2`) z powiadomieniem email - **FIX**: Niezainicjalizowana zmienna `$order_message` powodująca PHP warning --- ## ver. 0.311 (2026-02-23) - Fix race condition Apilo + persistence filtrów + poprawki cen - **FIX**: Race condition — callback płatności przed wysłaniem zamówienia do Apilo nie synchronizował płatności (task trafiał w pustkę). Teraz `syncApiloPaymentIfNeeded` i `syncApiloStatusIfNeeded` kolejkują sync do retry gdy `apilo_order_id` jeszcze nie istnieje - **FIX**: `processApiloSyncQueue` — zamówienia bez `apilo_order_id` były usuwane z kolejki bez synchronizacji. Teraz czekają (max 50 prób ~8h) aż cron wyśle zamówienie do Apilo - **FIX**: Drugie wywołanie `processApiloSyncQueue` w cronie po wysyłce zamówień — sync płatności/statusów w tym samym cyklu - **FIX**: Ceny w szczegółach zamówienia (admin + frontend) — gdy `price_brutto_promo` = 0 lub >= ceny regularnej, wyświetla cenę regularną zamiast 0 zł - **NEW**: Persistence filtrów tabel w panelu admin — localStorage zapamiętuje ostatni widok (filtry, sortowanie, paginacja) i przywraca go przy powrocie do listy. Przycisk "Wyczyść" resetuje zapisany stan --- ## ver. 0.310 (2026-02-23) - Logi integracji w panelu admin - **NEW**: Zakładka "Logi" w sekcji Integracje — podgląd tabeli `pp_log` z paginacją, sortowaniem, filtrami (akcja, wiadomość, ID zamówienia) i rozwijalnym kontekstem JSON - **NEW**: `IntegrationsRepository::getLogs()`, `deleteLog()`, `clearLogs()` — metody do obsługi logów - **NEW**: `IntegrationsController::logs()`, `logs_clear()` — akcje kontrolera - **NEW**: Przycisk "Wyczyść wszystkie logi" z potwierdzeniem --- ## ver. 0.309 (2026-02-23) - ApiloLogger + cache-busting CSS/JS + poprawki UI - **NEW**: `ApiloLogger` — logowanie operacji Apilo do tabeli `pp_log` z kontekstem JSON (send_order, resend_order, payment_sync, status_sync, status_poll) - **NEW**: Migracja `pp_log` — kolumny `action`, `order_id`, `context` + indeksy - **NEW**: Cache-busting dla CSS i JS w admin panelu — `?ver=filemtime()` przy wszystkich lokalnych zasobach w `main-layout.php` - **FIX**: Przeniesienie inicjalizacji `$mdb` przed `SettingsRepository` w `admin/index.php` - **FIX**: Rzutowanie na `(string)` w `ShopProductController::escapeHtml()` — zapobiega warningom - **ZMIANA**: Skrocone kategorie produktow na liscie — `text-overflow: ellipsis` z `title` tooltip - **ZMIANA**: `copyToClipboard()` — uzywa `navigator.clipboard` API z fallbackiem na `