Files
shopPRO/docs/CHANGELOG.md
Jacek 5b66720f7c fix: scontainers edit saves existing record instead of creating new
Fixes static container admin edit flow by preserving id in hiddenFields and adding route-id fallback during save.
Adds regression tests for edit/create id behavior, updates release docs (changelog/testing/CLAUDE), and appends SonarQube open issues to docs/TODO.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 22:56:14 +02:00

1425 lines
106 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Changelog shopPRO
Logi zmian z migracji na Domain-Driven Architecture. Najnowsze na gorze.
---
## ver. 0.347 (2026-04-18) - Scontainers edit: update zamiast insert
- **FIX**: `autoload/admin/Controllers/ScontainersController.php` - `id` formularza przeniesione do `hiddenFields` w `FormEditViewModel`, dzieki czemu edycja kontenera nie gubi identyfikatora w formularzu tabowanym
- **FIX**: `autoload/admin/Controllers/ScontainersController.php` - dodany fallback `id` z parametru trasy (`/admin/scontainers/save/id={id}`), gdy `id` nie przyjdzie w payloadzie POST
- **NEW**: `tests/Unit/admin/Controllers/ScontainersControllerTest.php` - testy regresyjne:
- `testBuildFormViewModelStoresIdInHiddenFieldsForEdit`
- `testBuildFormViewModelKeepsCreateFlowWithZeroId`
- **SONARQUBE**: wykonano skan i zaktualizowano `docs/TODO.md` o nowe otwarte issues (bez duplikatow)
---
## ver. 0.346 (2026-04-16) - Fix usuwania wszystkich dodatkowych pĂłl produktu
- **FIX**: `autoload/admin/Controllers/ShopProductController.php` — dodany hidden marker `custom_field_name_present` w `renderCustomFieldsBox()`, gwarantujący że sekcja custom fields jest zawsze rozpoznawana w POST nawet gdy wszystkie pola usunięte
- **FIX**: `autoload/Domain/Product/ProductRepository.php` — warunek zapisu custom fields zmieniony z `array_key_exists('custom_field_name')` na `array_key_exists('custom_field_name_present')` — naprawa buga gdzie jQuery `.serialize()` pomijaŠklucz pustej tablicy
- **NEW**: `tests/Unit/Domain/Product/ProductRepositoryTest.php` — test `testSaveCustomFieldsDeletesAllWhenEmpty` potwierdzający poprawne kasowanie wszystkich pól
---
## ver. 0.345 (2026-03-25) - DataLayer GA4 fix + checkout token fix
- **FIX**: `templates/shop-order/order-details.php` — event purchase: idâ†item_id (string), nameâ†item_name, price via normalize_decimal (fix price:0), usuniÄ™ty hardcoded value: 25.42, dodany google_business_vertical
- **FIX**: `templates/shop-basket/summary-view.php` — event begin_checkout: idâ†item_id, nameâ†item_name, dodany google_business_vertical
- **FIX**: `templates/shop-product/product.php` — event view_item: dodany currency PLN, value, price jako number (nie string), google_business_vertical; event add_to_cart: dodany google_business_vertical, parseInt(quantity)
- **NEW**: `templates/shop-basket/basket.php` — nowy event view_cart na stronie koszyka z peĹnym zestawem danych GA4 (item_id, item_name, price, quantity, currency, google_business_vertical)
- **FIX**: `autoload/front/Controllers/ShopBasketController.php` — usuniÄ™ty bĹÄ™dny guard w summaryView() blokujÄ…cy kolejne zamĂłwienia po pierwszym (redirect na stare zamĂłwienie zamiast podsumowanie)
- **FIX**: `autoload/front/Controllers/ShopBasketController.php` — token zamĂłwienia z jednorazowego na TTL 30 min (wiele kart, odĹwieĹĽenie, "wstecz" nie uniewaĹĽniajÄ… formularza)
- **NEW**: `autoload/front/Controllers/ShopBasketController.php` — logowanie bĹÄ™dĂłw zamĂłwieĹ„ do `logs/logs-order-YYYY-MM-DD.log` (double-submit, token invalid, exception, falsy order_id)
- **FIX**: `autoload/front/Controllers/ShopBasketController.php` — redirect przy zĹym tokenie na `/koszyk-podsumowanie` zamiast `/koszyk` (uĹĽytkownik nie traci kontekstu)
---
## ver. 0.344 (2026-03-19) - Edycja personalizacji produktu w koszyku
- **NEW**: `autoload/front/Controllers/ShopBasketController.php` — nowa metoda `basketUpdateCustomFields()`: AJAX endpoint do edycji custom fields w koszyku z walidacją required, przeliczaniem product_code (MD5 hash) i merge duplikatów
- **NEW**: `templates/shop-basket/_partials/product-custom-fields.php` — przycisk "Edytuj personalizacjÄ™" + formularz inline z aktualnymi wartoĹciami
- **NEW**: `templates/shop-basket/basket-details.php` — przekazanie `product_code` do szablonu custom fields
- **NEW**: `templates/shop-basket/basket.php` — JavaScript obsĹugi edycji/zapisu/anulowania personalizacji
---
## ver. 0.343 (2026-03-19) - Custom fields: type + is_required + obsĹuga obrazkĂłw w koszyku
- **FIX**: `autoload/Domain/Product/ProductRepository.php` — kopiowanie custom fields przy duplikacji produktu uwzględnia teraz pola `type` i `is_required`
- **FIX**: `templates/shop-basket/_partials/product-custom-fields.php` — ochrona XSS (htmlspecialchars), obsĹuga pola typu `image`, bezpieczny fallback typu na `text`
---
## ver. 0.342 (2026-03-19) - Apilo: email z danymi zamĂłwienia + infinite retry dla order jobĂłw
- **FIX**: `cron.php` — email notyfikacji Apilo zawiera teraz dane zamówienia (numer, klient, data, kwota) zamiast surowego JSON payload; temat emaila zawiera numery zamówień
- **NEW**: `autoload/Domain/CronJob/CronJobType.php` — `isOrderRelatedApiloJob()` identyfikuje order joby (send_order, sync_payment, sync_status)
- **NEW**: `autoload/Domain/CronJob/CronJobRepository.php` — order-related Apilo joby ponawiane w nieskoĹ„czonoĹć co 30 min zamiast permanent failure po 10 prĂłbach
- **NEW**: `cron.php` — email rozróżnia "PONAWIANY CO 30 MIN" (order joby) vs "TRWAŁY BŁĄD" (inne joby)
- **NEW**: `cron.php` — po udanym wysĹaniu zamĂłwienia do Apilo czyszczone sÄ… stuck joby sync_payment/sync_status
- **TEST**: +2 testy infinite retry w `CronJobRepositoryTest`
---
## ver. 0.341 (2026-03-16) - Bugfix: zamĂłwienia nie wysyĹy siÄ™ do Apilo + retry i powiadomienia
- **FIX**: `cron.php` — dodano brakujÄ…ce `$apiloRepository` do klauzul `use()` w 5 handlerach cron (APILO_TOKEN_KEEPALIVE, APILO_SEND_ORDER, APILO_PRODUCT_SYNC, APILO_PRICELIST_SYNC, APILO_STATUS_POLL); regresja z ver. 0.339 (split IntegrationsRepository → ApiloRepository) powodowaĹa `Call to a member function apiloGetAccessToken() on null`
- **FIX**: `cron.php` — zamówienia z `apilo_order_id = -1` (failed) są teraz automatycznie ponawiane co 1h zamiast trwale pomijane; priorytet: najpierw nowe zamówienia (NULL), potem retry (-1)
- **NEW**: `cron.php` — powiadomienie mailowe na `biuro@project-pro.pl` przy bĹÄ™dzie cURL wysyĹania zamĂłwienia do Apilo
- **NEW**: `cron.php` — powiadomienie mailowe o trwale nieudanych zadaniach Apilo (po wyczerpaniu `max_attempts`)
---
## ver. 0.340 (2026-03-15) - Bugfix: crash przy skĹadaniu zamĂłwienia z kuponem rabatowym
- **FIX**: `autoload/Domain/Order/OrderRepository.php:793` — naprawiono Fatal Error `Call to undefined method stdClass::is_one_time()` przy skĹadaniu zamĂłwienia z kodem rabatowym; zamieniono wywoĹania nieistniejÄ…cych metod na stdClass (`is_one_time()`, `set_as_used()`) na dostÄ™p do wĹciwoĹci + istniejÄ…cÄ… metodÄ™ `CouponRepository::markAsUsed()`
- **SONARQUBE**: Pierwszy skan SonarQube — wyniki zapisane w `docs/TODO.md` (4 bugi, 31 critical code smells, 10 major, 8 minor)
---
## ver. 0.339 (2026-03-12) - Refactoring: wydzielenie ApiloRepository z IntegrationsRepository
- **REFACTOR**: `autoload/Domain/Integrations/ApiloRepository.php` — nowa klasa `\Domain\Integrations\ApiloRepository` z 19 metodami apilo* (sync produktów, zamówień, konfiguracja) wydzielonymi z `IntegrationsRepository`
- **REFACTOR**: `autoload/Domain/Integrations/IntegrationsRepository.php` — usuniÄ™to 19 metod apilo* (~540 linii); klasa zmniejszona z ~875 do ~340 linii, zawiera wyĹÄ…cznie generycznÄ… logikÄ™ integracji (settings, logi, product linking)
- **REFACTOR**: `autoload/admin/Controllers/IntegrationsController.php` — konsumuje `ApiloRepository` przez DI zamiast `IntegrationsRepository` dla operacji apilo
- **REFACTOR**: `autoload/Domain/Order/OrderAdminService.php` — uĹĽywa `ApiloRepository` do wysyĹki zamĂłwieĹ„ do Apilo
- **REFACTOR**: `cron.php` — używa `ApiloRepository` do synchronizacji cron
- **REFACTOR**: `autoload/admin/App.php` — wiring DI dla `ApiloRepository`
- **TEST**: `tests/Unit/Domain/Integrations/ApiloRepositoryTest.php` — nowe testy dla `ApiloRepository`; suite: 818 testów, 2275 asercji
---
## ver. 0.338 (2026-03-12) - Bugfix: duplikaty zamówień + status COD
- **FIX**: `autoload/front/Controllers/ShopBasketController::summaryView()` — guard przed ponownym zĹoĹĽeniem zamĂłwienia: jeĹli sesja zawiera `ORDER_SUBMIT_LAST_ORDER_ID`, uĹĽytkownik jest przekierowywany do istniejÄ…cego zamĂłwienia zamiast widzieć formularz ponownie
- **FIX**: `autoload/front/Controllers/ShopBasketController::basketSave()` — owiniÄ™cie wywoĹania `createFromBasket()` w try-catch; wyjÄ…tek jest logowany przez `error_log()`, uĹĽytkownik widzi komunikat bĹÄ™du, koszyk sesyjny zostaje zachowany
- **FIX**: `autoload/Domain/Order/OrderRepository::createFromBasket()` — usuniÄ™cie hardkodowanego `payment_id == 3` do wykrywania pĹatnoĹci przy odbiorze; zamiast tego uĹĽywana jest flaga `$payment_method['is_cod']`
- **FEATURE**: `autoload/Domain/PaymentMethod/PaymentMethodRepository` — nowa kolumna `is_cod` (normalizacja, zapis w `save()`, kolumna w `forTransport()` SQL)
- **FEATURE**: `autoload/admin/Controllers/ShopPaymentMethodController` — nowe pole "Platnosc przy odbiorze" w formularzu edycji metody pĹatnoĹci
- **MIGRATION**: `migrations/0.338.sql` — `ALTER TABLE pp_shop_payment_methods ADD COLUMN is_cod TINYINT(1) NOT NULL DEFAULT 0`
---
## ver. 0.337 (2026-03-12) - Bezpieczeństwo: ochrona CSRF panelu administracyjnego
- **SECURITY**: `autoload/Shared/Security/CsrfToken.php` — nowa klasa z `getToken()`, `validate()`, `regenerate()` (token 64-znakowy hex, `hash_equals()` przeciw timing attacks)
- **SECURITY**: `admin/templates/components/form-edit.php` — dodano ukryte pole `_csrf_token` we wszystkich formularzach edycji
- **SECURITY**: `autoload/admin/Support/Forms/FormRequestHandler::handleSubmit()` — walidacja CSRF przed przetworzeniem danych formularza
- **SECURITY**: `admin/templates/site/unlogged-layout.php` — token CSRF w formularzu logowania + fix XSS na komunikacie alertu (`htmlspecialchars`)
- **SECURITY**: `admin/templates/users/user-2fa.php` — token CSRF w obu formularzach 2FA (weryfikacja i resend)
- **SECURITY**: `autoload/admin/App::special_actions()` — walidacja CSRF dla ĹĽÄ…daĹ„ POST; regeneracja tokenu po udanym logowaniu (obie ĹcieĹĽki: bezpoĹrednia i przez 2FA)
- **TEST**: `tests/Unit/Shared/Security/CsrfTokenTest.php` — 7 nowych testów; suite: 817 testów, 2271 asercji
---
## ver. 0.336 (2026-03-12) - Poprawki bezpieczeĹ„stwa: error handling w krytycznych ĹcieĹĽkach
- **FIX**: `cron.php` — przywrĂłcono `E_WARNING` i `E_DEPRECATED` (wyciszano je od zawsze, ukrywajÄ…c potencjalne bĹÄ™dy)
- **FIX**: `IntegrationsRepository::apiloAuthorize()` — try-catch po zapisie tokenĂłw Apilo; bĹÄ…d DB logowany i zwraca `false` zamiast cicho kontynuować
- **FIX**: `ProductRepository::safeUnlink()` — `error_log()` gdy ĹcieĹĽka istnieje ale jest poza `upload/`
- **FIX**: `ArticleRepository::safeUnlink()` — to samo
---
## ver. 0.335 (2026-03-12) - Poprawki bezpieczeństwa: path traversal i XSS w szablonach
- **SECURITY**: `ProductRepository` — dodano `safeUnlink()` z walidacją `realpath()` zapobiegającą path traversal; użyta w `cleanupDeletedFiles()`, `cleanupDeletedImages()`, `deleteNonassignedImages()`
- **SECURITY**: `ArticleRepository` — to samo; użyta w `deleteMarkedImages()`, `deleteMarkedFiles()`, `deleteNonassignedFiles()`, `deleteNonassignedImages()`
- **SECURITY**: `templates/articles/article-full.php` — `htmlspecialchars()` na tytule artykuĹu, `$_SERVER['SERVER_NAME']` i `$url` w linkach social media
- **SECURITY**: `templates/articles/article-entry.php` — `htmlspecialchars()` na tytule i `$url` (3 miejsca: href, title, alt)
---
## ver. 0.334 (2026-03-12) - Poprawki bezpieczeństwa: debug log, SQL, RedBeanPHP
- **SECURITY**: `ShopOrderController::paymentStatusTpay()` — usuniÄ™to `file_put_contents('tpay.txt', ...)` ktĂłry logowaĹ peĹne dane POST/GET pĹatnoĹci do publicznego pliku
- **SECURITY**: `ShopOrderController` — hardcoded sekret HotPay `"ProjectPro1916;"` przeniesiony do prywatnej staĹej `HOTPAY_HASH_SEED`
- **SECURITY**: `IntegrationsRepository::getSettings()` — zastąpiono raw `query("SELECT * FROM $table")` metodą Medoo `select()` (spójne z zasadą braku string concatenation w SQL)
- **REFACTOR**: `index.php`, `admin/index.php` — usuniÄ™to RedBeanPHP (`rb.php`): biblioteka byĹa Ĺadowana i inicjalizowana, ale nigdy nie uĹĽywana w ĹĽadnym zapytaniu
- **CLEANUP**: `libraries/rb.php` — usuniÄ™to plik (536 KB zbÄ™dnych zaleĹĽnoĹci)
- **TESTS**: `IntegrationsRepositoryTest` — zaktualizowano 6 testów do nowego API (`select` zamiast `query` dla `getSettings`)
---
## ver. 0.333 (2026-03-10) - Ochrona przed podwĂłjnym skĹadaniem zamĂłwienia (order submit token)
- **NEW**: `ShopBasketController` — mechanizm tokenu CSRF chroniÄ…cy przed podwĂłjnym skĹadaniem zamĂłwienia (generowanie, walidacja, konsumpcja tokenu w sesji)
- **NEW**: `ShopBasketController::basketSave()` — przy duplikacie przekierowanie do istniejącego zamówienia zamiast tworzenia kolejnego
- **FIX**: `templates/shop-basket/summary-view.php` — JS nasĹuchuje na `submit` formularza zamiast `click` przycisku (poprawna obsĹuga walidacji HTML5)
- **FIX**: `templates/shop-basket/address-form.php` — ukryte pole `order_submit_token` z escape XSS
- **TESTS**: `ShopBasketControllerTest` — testy konstruktora i zaleĹĽnoĹci (5 testĂłw)
---
## ver. 0.332 (2026-03-01) - API produktĂłw: nowe pola new_to_date i additional_message
- **NEW**: `ProductRepository::getProductForApi()` — eksportuje 4 nowe pola: `new_to_date`, `additional_message` (int 0/1), `additional_message_required` (int 0/1), `additional_message_text`
- **NEW**: `ProductsApiController` — obsĹuga nowych pĂłl w PUT/PATCH (aktualizacja `new_to_date`, `additional_message`, `additional_message_required`, `additional_message_text`)
- **DOCS**: `docs/API.md` — zaktualizowane przykĹady GET/PUT dla nowych pĂłl produktu
---
## ver. 0.331 (2026-03-01) - Bugfix: strona produktu uĹĽywaĹa layoutu kategorii zamiast domyĹlnego
- **FIX**: `LayoutsRepository::getProductLayout()` — fallback gdy produkt i jego kategorie nie majÄ… przypisanego layoutu zmieniany z `categories_default = 1` na `status = 1`; wczeĹniej produkty bez layoutu pobieraĹy szablon "Podstrony - kategorie" zamiast wĹciwego domyĹlnego
---
## ver. 0.330 (2026-02-27) - Eliminacja htaccess.conf — wszystkie trasy URL w pp_routes
- **REFACTOR**: `Helpers::htacces()` — generowanie `.htaccess` w caĹci z PHP (usuniÄ™ty `file_get_contents('htaccess.conf')` i placeholder `{HTACCESS_CACHE}`)
- **NEW**: 32 statyczne trasy systemowe wstawiane do `pp_routes` z `type='system'` przy kaĹĽdym `htacces()` (koszyk, logowanie, wylogowanie, panel klienta, newsletter, zamĂłwienia, pĹatnoĹci, moduĹy AJAX: shopBasket/shopClient/shopProduct/shopCoupon/search)
- **NEW**: Dynamiczne trasy językowe i producentów (producenci + per-producent z paginacją) przenoszone do `pp_routes` zamiast `.htaccess`
- **NEW**: Kolumna `type VARCHAR(20) NULL` w `pp_routes` — `NULL` dla encji, `'system'` dla tras systemowych
- **REMOVED**: `libraries/htaccess.conf` — plik szablonu usuniÄ™ty, treĹć wbudowana w PHP
- **PERF**: Invalidacja cache Redis `pp_routes:all` po kaĹĽdym `htacces()` — ĹwieĹĽe trasy przy kolejnym ĹĽÄ…daniu
- **MIGRATION**: `migrations/0.329.sql` (dodano `type` column)
- **DOCS**: `docs/DATABASE_STRUCTURE.md` — zaktualizowana sekcja `pp_routes` o kolumnę `type`
---
## ver. 0.329 (2026-02-27) - Routing kategorii, stron i artykuĹĂłw przez pp_routes
- **REFACTOR**: `index.php` — blok routingu przez `pp_routes` przeniesiony PRZED `checkUrlParams()` (poprawna kolejnoĹć: lang/a=page dostÄ™pne w checkUrlParams)
- **PERF**: Cache Redis dla tras (`pp_routes:all`, TTL 86400s) w `index.php` — jeden SELECT na 24h zamiast przy każdym żądaniu
- **NEW**: Kategorie, strony i artykuĹy zapisywane do `pp_routes` zamiast `.htaccess` w `Helpers::htacces()`
- **NEW**: `CategoryRepository::categoryDelete()` — usuwa powiÄ…zane `pp_routes` przed odĹwieĹĽeniem
- **NEW**: `PagesRepository::pageDelete()` — usuwa powiązane `pp_routes`
- **NEW**: `ArticleRepository::archive()` i `deletePermanently()` — usuwa powiązane `pp_routes`
- **MIGRATION**: `migrations/0.329.sql` — `ALTER TABLE pp_routes ADD COLUMN category_id, page_id, article_id`
- **TESTS**: Zaktualizowane `CategoryRepositoryTest` i `ArticleRepositoryTest` (nowe asercje na `pp_routes` delete)
---
## ver. 0.328 (2026-02-27) - Ikona kopiowania wartoĹci atrybutĂłw w szczegĂłĹach zamĂłwienia
- **NEW**: `order-details-custom-script.php` — JS parsuje `.atributes` div i wstrzykuje przycisk `fa-copy` przy kaĹĽdej wartoĹci atrybutu
- **UX**: KlikniÄ™cie kopiuje wartoĹć do schowka (Clipboard API + fallback execCommand), ikona zmienia siÄ™ na `fa-check` z zielonym tĹem przez 1,5s
---
## ver. 0.327 (2026-02-27) - Masowe usuwanie w archiwum produktĂłw
- **NEW**: `ProductArchiveController::bulk_delete_permanent()` — endpoint POST `product_archive/bulk_delete_permanent/`, przyjmuje `ids[]`, usuwa każdy produkt przez `ProductRepository::delete()`, zwraca JSON `{success, deleted, errors[]}`
- **UX**: Kolumna checkboxĂłw w liĹcie archiwum produktĂłw + pasek akcji masowych z licznikiem zaznaczonych
- **UX**: "Zaznacz wszystkie" w nagĹĂłwku tabeli (wstrzykniÄ™ty via JS), dialog potwierdzenia przed masowym usuniÄ™ciem
- **TEST**: 2 nowe testy w `ProductArchiveControllerTest` — weryfikacja istnienia i sygnatury `bulk_delete_permanent`
---
## ver. 0.326 (2026-02-27) - API: endpoint categories/list
- **NEW**: `api\Controllers\CategoriesApiController` — nowy kontroler API z akcją `list`
- **NEW**: Endpoint `GET api.php?endpoint=categories&action=list` — zwraca pĹaskÄ… listÄ™ aktywnych kategorii (id, parent_id, title) w domyĹlnym jÄ™zyku sklepu
- **FIX**: UsuniÄ™to zbÄ™dny parametr w `CategoryRepository`, eliminacja N+1 queries w categories/list przez bulk-fetch tytuĹĂłw
---
## 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Ĺ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 `<textarea>`
- **ZMIANA**: Uproszczenie CSS `.site-content` — usuniety zbedny `.with-menu`, menu widoczne zawsze na desktop
---
## ver. 0.308 (2026-02-22) - Kolory statusow zamowien + poprawki bezpieczenstwa
- **NEW**: Kolorowe badge statusow na liscie zamowien w admin panelu — kolory pobierane z `pp_shop_statuses.color`, kontrast tekstu obliczany automatycznie
- **FIX**: Walidacja formatu hex koloru z bazy (`/^#[0-9a-fA-F]{3,6}$/`) — odrzucanie nieprawidlowych wartosci
- **FIX**: Sanityzacja HTML w kolumnie "Dostawa" — `strip_tags()` + regex usuwajacy atrybuty z dozwolonych tagow (zapobieganie XSS via `onclick` itp.)
- **OPTYMALIZACJA**: Polaczenie dwoch zapytan SQL do `pp_shop_statuses` (nazwy + kolory) w jedno `orderStatusData()`
- **ZMIANA**: Path-based form submit w `table-list.php` — formularze filtrow i per-page uzywaja JS interceptora z `data-path-submit` zamiast natywnego GET, kompatybilne z admin URL routing
- **NEW**: 11 nowych testow jednostkowych (750 total, 2114 assertions)
---
## ver. 0.307 (2026-02-22) - Przycisk sprawdzania aktualizacji + auto-changelog
- **NEW**: Przycisk "Sprawdz aktualizacje" w panelu admina — ikona odswiezenia obok numeru wersji, klik odpytuje serwer AJAX-em i pokazuje/ukrywa badge "aktualizacja" bez przeladowania strony
- **NEW**: `UpdateController::checkUpdate()` — nowa akcja AJAX czyszczaca sesje `new-version` i sprawdzajaca wersje z serwera
- **NEW**: `updates/changelog.php` — auto-generowany z manifestow JSON (`_manifest.json`) + `changelog-legacy.json`, zamiast recznej edycji HTML
- **NEW**: `updates/changelog-legacy.json` — migracja 295 wpisow changelogu (wersje 0.300-0.001) do formatu JSON
- **ZMIANA**: Workflow KONIEC PRACY — usuniety krok recznej edycji `changelog.php` (teraz automatyczny)
---
## ver. 0.306 (2026-02-22) - Ukrywanie form dostawy bez dostepnych platnosci
- **FIX**: Formy dostawy, dla ktorych nie ma zadnej dostepnej formy platnosci (np. wszystkie odfiltrowane przez `min_order_amount`/`max_order_amount`), sa teraz ukrywane z listy w koszyku
- **ZMIANA**: `TransportRepository::transportMethodsFront()` — po filtrze wagowym i korekcie darmowej dostawy, dodano filtr sprawdzajacy dostepnosc form platnosci per transport (z wykorzystaniem `PaymentMethodRepository::paymentMethodsByTransport()` z cache Redis)
- **NEW**: 5 nowych testow jednostkowych (739 total, 2089 assertions)
---
## ver. 0.305 (2026-02-22) - Sortowanie permutacji + pasek darmowej dostawy
- **FIX**: Naprawa kolejnosci atrybutow permutacji — sortowanie po ID atrybutu (`usort` wg pierwszego segmentu przed `-`) we wszystkich miejscach: koszyk, kombinacje AJAX, `getPermutation()`, `BasketCalculator`. Zapewnia zgodnosc z `permutation_hash` generowanym przez `ksort`.
- **NEW**: Pasek postepu darmowej dostawy w koszyku — progress bar z ikona, tekstem i animacja. Pokazuje ile brakuje do progu `free_delivery` z ustawien sklepu.
- **FIX**: icheck CSS/JS przeniesione z szablonu `product-combination` do globalnego layoutu admina (`main-layout.php`)
- **FIX**: `Tpl::__isset()` — obsluga `isset()` na zmiennych szablonu
- **.updateignore** — lokalne style frontendowe dodane do listy ignorowania
---
## ver. 0.304 (2026-02-22) - Konfigurowalne limity kwotowe metod platnosci
- **NEW**: Kolumny `min_order_amount` i `max_order_amount` w `pp_shop_payment_methods` — konfigurowalne limity kwotowe per metoda platnosci
- **NEW**: Pola min/max kwoty zamowienia w formularzu edycji metody platnosci (admin)
- **FIX**: Zastapiono hardcoded warunek PayPo (id=6, 40-1000 PLN) generycznym filtrowaniem na froncie (basket checkout)
- **NEW**: Cache invalidation po zapisie metody platnosci
- **NEW**: 4 nowe testy jednostkowe (734 total, 2080 assertions)
- **MIGRATION**: `migrations/0.304.sql` — ALTER TABLE pp_shop_payment_methods ADD min/max_order_amount
---
## ver. 0.303 (2026-02-22) - Fix: wyswietlanie atrybutow produktu na froncie + podglad produktu w adminie
- **FIX**: Naprawiono wyswietlanie atrybutow produktu na froncie — gdy dwa atrybuty mialy te sama wartosc kolejnosci (`o`), jeden nadpisywal drugi (kolizja kluczy tablicy). Teraz atrybuty sortowane przez `usort()` z unikalnymi kluczami sekwencyjnymi.
- **NEW**: Przycisk "Podglad" w formularzu edycji produktu — otwiera strone produktu w nowej karcie
- **NEW**: `FormAction::preview()` — nowy typ akcji formularza z `target="_blank"`
---
## ver. 0.302 (2026-02-22) - REST API: warianty produktow, atrybuty, filtrowanie
- **NEW**: API wariantow produktow — CRUD: `variants`, `create_variant`, `update_variant`, `delete_variant`
- **NEW**: API slownik atrybutow — `dictionaries/attributes` z wielojezycznymi nazwami i wartosciami
- **NEW**: Filtrowanie produktow po atrybutach w `products/list` — parametry `attribute_{id}={value_id}`
- **NEW**: Wzbogacone atrybuty w `products/get` — nazwy atrybutow i wartosci z tlumaczeniami
- **NEW**: Warianty w odpowiedzi `products/get` — lista wariantow z atrybutami dla produktow nadrzednych
- **NEW**: Walidacja `price_brutto` przy tworzeniu produktu (musi byc nieujemna liczba)
- **NEW**: Batch-loading atrybutow i wartosci (4 zapytania zamiast N+1) w `AttributeRepository::listForApi()`
- **NEW**: 43 nowe testy jednostkowe (730 total, 2066 assertions)
---
## ver. 0.301 (2026-02-22) - Mobile responsive: filtry tabel i szczegoly zamowienia
- **NEW**: Filtry w tabelach admina domyslnie ukryte — przycisk toggle z ikona filtra, badge z liczba aktywnych filtrow
- **NEW**: Stan filtrow zapamietywany w `localStorage` per widok; auto-show gdy filtry aktywne
- **NEW**: Mobilna wersja szczegolow zamowienia — responsywny layout (CSS-only, breakpoint 767px)
- **NEW**: Pasek akcji zamowienia — ikony-only na mobile, flex row bez zawijania
- **NEW**: Tabela produktow zamowienia — kompaktowa lista na mobile (obraz + nazwa + linia `qty x cena = suma`)
- **NEW**: Sekcje informacyjne zamowienia — full-width stacking na mobile
- **NEW**: Dropdown integracji jako bottom-sheet na mobile
---
## ver. 0.300 (2026-02-21) - System aktualizacji oparty na manifestach JSON
- **NEW**: Manifest JSON per wersja — zastępuje osobne pliki `_sql.txt` i `_files.txt`
- **NEW**: Weryfikacja checksum SHA256 pobranych paczek ZIP
- **NEW**: Automatyczny backup plikĂłw przed nadpisaniem (`backups/` directory)
- **NEW**: `build-update.ps1` — automatyczne budowanie paczek z `git diff` między tagami
- **NEW**: `.updateignore` — wzorce plików wykluczonych z paczek aktualizacji
- **NEW**: `migrations/` — folder na wersjonowane pliki SQL
- **NEW**: Panel "Log ostatniej aktualizacji" w widoku aktualizacji admina
- **UPDATE**: `UpdateRepository` — dual-mode dispatcher (manifest + legacy fallback)
---
## ver. 0.299 (2026-02-21) - WidocznoĹć kolumn w tabelach
- **NEW**: Toggle widocznoĹci kolumn w komponentach `table-list` — przycisk z ikonÄ… kolumn, dropdown z toggle switchami
- **NEW**: Stan widocznoĹci kolumn zapisywany w `localStorage` per tabela (klucz na bazie `basePath`)
- **NEW**: Przycisk "PokaĹĽ wszystkie" resetujÄ…cy widocznoĹć
- **UPDATE**: Style z `table-list.php` wyekstrahowane do osobnego pliku `admin/layout/style-css/table-list.css`
- **UPDATE**: `admin/templates/site/main-layout.php` — podĹÄ…czenie `table-list.css`
---
## ver. 0.297 (2026-02-19) - REST API produktĂłw
- **NEW**: Endpoint `products` w REST API — lista, szczegĂłĹy, tworzenie, aktualizacja produktĂłw
- **NEW**: `\api\Controllers\ProductsApiController` — 4 akcje (list, get, create, update)
- **NEW**: `ProductRepository::listForApi()` — lista produktów z filtrowaniem (search/status/promoted), sortowaniem i paginacją
- **NEW**: `ProductRepository::findForApi()` — szczegĂłĹy produktu z jÄ™zykami, zdjÄ™ciami, kategoriami i atrybutami
- **NEW**: Partial update — `update` merguje przesĹane pola z istniejÄ…cymi danymi produktu
- **NEW**: Mapowanie API → format formularza (`mapApiToFormData`) — status/promoted jako checkboxy, languages jako mapy
- **UPDATE**: `ApiRouter` — rejestracja endpointu `products`
- **UPDATE**: `docs/API.md` — dokumentacja 4 akcji produktowych z przykĹadami
- **Tests**: 21 nowych testĂłw (`ProductsApiControllerTest`)
---
## ver. 0.296 (2026-02-19) - REST API zamówień dla ordersPRO
- **NEW**: REST API do zarzÄ…dzania zamĂłwieniami — lista, szczegĂłĹy, zmiana statusu, oznaczanie pĹatnoĹci
- **NEW**: Endpointy sĹownikowe — statusy zamĂłwieĹ„, metody transportu, metody pĹatnoĹci
- **NEW**: Autentykacja API przez header `X-Api-Key` (klucz w `pp_settings`)
- **NEW**: `\api\ApiRouter` — router API z autentykacją, routingiem i helperami odpowiedzi
- **NEW**: `\api\Controllers\OrdersApiController` — 5 akcji (list, get, change_status, set_paid, set_unpaid)
- **NEW**: `\api\Controllers\DictionariesApiController` — 3 akcje (statuses, transports, payment_methods)
- **NEW**: `OrderRepository::listForApi()` — lista zamówień z filtrowaniem i paginacją (z `updated_since` do pollingu)
- **NEW**: `OrderRepository::findForApi()` — szczegĂłĹy zamĂłwienia z produktami i historiÄ… statusĂłw
- **NEW**: `OrderRepository::touchUpdatedAt()` — aktualizacja `updated_at` przy modyfikacji zamówienia
- **NEW**: Kolumna `pp_shop_orders.updated_at` — data ostatniej modyfikacji (polling API)
- **NEW**: Setting `api_key` w `pp_settings` — klucz autentykacji API
- **UPDATE**: `api.php` — skip sesji dla requestów API, routing do `ApiRouter`
- **UPDATE**: Metody `updateOrderStatus`, `setAsPaid`, `setAsUnpaid`, `saveOrderByAdmin`, `saveNotes`, `createFromBasket` wywoĹujÄ… `touchUpdatedAt()`
- **NEW**: `docs/API.md` — dokumentacja REST API
- **Tests**: 30 nowych testĂłw API (ApiRouter, OrdersApiController, DictionariesApiController)
---
## ver. 0.295 (2026-02-19) - Admin: edycja produktĂłw w zamĂłwieniu + wyszukiwanie AJAX + korekta stanĂłw magazynowych
- **NEW**: Edycja produktĂłw w zamĂłwieniu z panelu admina (dodawanie, usuwanie, zmiana iloĹci/cen)
- **NEW**: Wyszukiwarka produktĂłw AJAX w formularzu edycji zamĂłwienia (`search_products_ajax`)
- **NEW**: Automatyczna korekta stanĂłw magazynowych przy edycji produktĂłw zamĂłwienia
- **NEW**: Automatyczne przeliczanie kosztu dostawy (prĂłg darmowej dostawy) po zmianie produktĂłw
- **NEW**: `OrderRepository` — CRUD: `addOrderProduct()`, `updateOrderProduct()`, `deleteOrderProduct()`, `getOrderProduct()`, `updateTransportCost()`
- **NEW**: `OrderAdminService` — `searchProducts()`, `saveOrderProducts()`, `getFreeDeliveryThreshold()`, `adjustStock()`, `recalculateTransportCost()`
- **NEW**: `ShopOrderController` — endpoint `search_products_ajax`, rozszerzony `order_save` o zapis produktów
- **UPDATE**: `admin\App` — rozszerzone DI wiring (ProductRepository, SettingsRepository, TransportRepository)
- **FIX**: `OrderRepository::createOrder()` — promo price = 0 gdy taka sama jak cena bazowa (brak realnej promocji)
- **NEW**: Template `order-edit-custom-script.php` — interaktywny JS do zarządzania produktami zamówienia
- **UPDATE**: Template `order-edit.php` — sekcja edycji produktów z dynamicznym formularzem
- **Tests**: `OrderAdminServiceTest` (nowy), rozszerzony `OrderRepositoryTest` (+132 linii), zaktualizowany `ShopOrderControllerTest`
---
## ver. 0.294 (2026-02-19) - Code review: full codebase review complete (96/96 classes), 27 fixes across all layers
**Code review zakończony — 96 klas, ~1144 metody przejrzane.**
- **Domain layer (16 fixes):**
- FIX: `CategoryRepository` — usunięto martwy kod `updateCategoryDefaultLayoutId()`, `allCategoriesForCacheRefresh()`
- FIX: `ClientRepository` — null guard na `query()->fetchAll()`
- FIX: `DictionariesRepository` — null guard na `query()->fetchAll()` (2 metody)
- FIX: `IntegrationsRepository` — null guard na `query()->fetchAll()` (3 metody)
- FIX: `LanguagesRepository` — null guard na `query()->fetchAll()` (2 metody)
- FIX: `LayoutsRepository` — null guard na `query()->fetchAll()` (4 metody)
- FIX: `OrderAdminService` — null safety `find()` + redundancja DI
- FIX: `OrderRepository` — null guard na `query()->fetchAll()`
- FIX: `ProductRepository` — null guard na `query()->fetchAll()` (8 metod), redundancja DI
- FIX: `PromotionRepository` — redundancja `new ProductRepository` w pętlach (3 metody)
- **Admin layer (11 fixes):**
- FIX: `admin\App` — null guard po `details()` w logowaniu i 2FA
- FIX: `BannerController`, `ScontainersController`, `ShopProducerController` — null guard `find() ?: []` + `save()` null guard
- FIX: `PagesController` — null guard `menuDetails() ?: []`, `pageDetails() ?: []`
- FIX: `ProductArchiveController` — `$this->repository` → `$this->productRepository`
- FIX: `ShopAttributeController` — null guard `findAttribute() ?: []`
- FIX: `ShopCouponController` — null guard `find() ?: []`
- FIX: `ShopProductSetsController`, `ShopPromotionController` — null guard `find() ?: []`
- FIX: `ShopProductController` — null safety `findForAdmin() ?: []` + 3x redundancja DI
- **Front layer (3 fixes):**
- FIX: `LayoutEngine` — undefined `$level` → `null`; `$_GET['permutation_hash']` → `?? null`
- FIX: `ShopBasketController` — brakujące `global $lang_id` w `basketAddProduct()`
- **Shared layer (2 fixes):**
- FIX: `Helpers::generate_webp_image()` — `$_GET['c_w']`/`$_GET['c_h']` → `?? null`
- FIX: `Helpers::generate_webp_image()` — **bug**: `'png'` → `'image/png'` (Imagick lossless WebP nigdy siÄ™ nie aktywowaĹ)
- **CLASS_CATALOG.md** — kompletny katalog z ✅/🔧 przy wszystkich 1144 metodach
- Testy: 614 OK, 1821 asercji
---
## ver. 0.293 (2026-02-19) - Code review: fixes ArticleRepository, AttributeRepository, BannerRepository, BasketCalculator, CategoryRepository, PromotionRepository
- **ArticleRepository** (7 fixes):
- FIX: `articlesByDateAdd()` — SQL injection (addslashesâ†parameterized queries), dodano parametr `$langId`
- FIX: `articleDetailsFrontend()` — uproszczono select()+foreachâ†get()
- FIX: `articlesIds()`, `pageArticlesCount()` — parametryzacja `$langId`
- FIX: `topArticles()`, `newsListArticles()` — DRY refactor via `fetchArticlesByPage()`, parametryzacja
- **AttributeRepository** (1 fix):
- FIX: `clearTempAndCache()` — martwy `class_exists('\S')` blokowaŠczyszczenie cache/temp
- **CategoryRepository** (2 fixes):
- FIX: `refreshCategoryArtifacts()` — martwy `class_exists('\S')` blokowaŠczyszczenie htaccess/temp
- FIX: `normalizeSeoLink()` — **krytyczny bug** — linki SEO kategorii nigdy nie byĹy generowane od usuniÄ™cia `\S`
- **BannerRepository** (2 fixes):
- FIX: `banners()`, `mainBanner()` — parametryzacja `$today` w SQL + null guard na `query()`
- **BasketCalculator** (3 fixes):
- FIX: `checkProductQuantityInStock()` — dodano `is_array()` guard (foreach na nullâ†fatal)
- FIX: `summaryPrice()` — dodano opcjonalne DI params `$langId`, `$productRepo` z fallbackiem do globals
- FIX: `calculateBasketProductPrice()` — dodano opcjonalny `$productRepo` z fallbackiem do globals
- **PromotionRepository** (1 fix):
- FIX: `findPromotion()` — null guard na `$basket` (produkcyjny fatal error)
- **OrderRepository** — zaktualizowano callery BasketCalculator (jawne DI zamiast globals), usunięto redundantne tworzenie ProductRepository w pętli
- **ShopBasketController**, **ajax.php** — zaktualizowano callery summaryPrice (jawne `$lang_id`)
- **CLASS_CATALOG.md** — zaktualizowano katalog dla 5 klas (rzeczywiste metody + znaczniki przeglądu)
- Testy: 614 OK, 1821 asercji (+4 nowe testy BasketCalculator)
---
## ver. 0.292 (2026-02-18) - Usuniecie autoload/shop/ — 12 legacy klas
- **Faza 5.1: class.Order.php (~562 linii) USUNIETA**
- Logika Apilo sync przeniesiona do `OrderAdminService::processApiloSyncQueue()`
- Email zmiany statusu zintegrowany w `OrderAdminService::changeStatus()`
- `cron.php` przepiety na `OrderAdminService`
- `front\Controllers\ShopOrderController` — dodano `OrderAdminService` jako drugie DI
- `\shop\Order::order_statuses()` → `OrderRepository::orderStatuses()` (3 lokalizacje)
- **Faza 5.2: class.Product.php (~952 linii) USUNIETA — NAJWIEKSZA KLASA**
- ~20 metod przeniesionych do `ProductRepository`: `findCached()`, `isProductOnPromotion()`, `productSetsWhenAddToBasket()`, `addVisit()`, `getProductImg()`, `getProductUrl()`, `searchProductsByName()`, `searchProductByNameAjax()`, `searchProductsByNameCount()`, `isStock0Buy()`, `getProductPermutationQuantityOptions()`, `getProductIdByAttributes()`, `getProductPermutationHash()`, `getProductAttributes()`, `generateSkuCode()`, `productMeta()`, `generateSubtitleFromAttributes()`, `getDefaultCombinationPrices()`, `getProductDataBySelectedAttributes()`, `productCategories()`, `arrayCartesian()`
- `calculate_basket_product_price()` przeniesione do `BasketCalculator::calculateBasketProductPrice()`
- `BasketCalculator` przepisany: `summaryPrice()`, `checkProductQuantityInStock()` uzywaja `ProductRepository`
- XML generation (4 metody) — konwersja objectâ†array access
- ~60+ callsite'ow zamienionych w kontrolerach, szablonach, repozytoriach i entry-pointach
- Cache key strings zaktualizowane z `\shop\Product::` na `ProductRepository::`
- **Cleanup:**
- Usunieto dead `\shop\` routing z `front\App::route()`
- Cache key `\shop\Promotion::get_active_promotions` → `PromotionRepository::getActivePromotions`
- Katalog `autoload/shop/` jest teraz pusty
- **Production hotfixes:**
- FIX: `findCached()` — stale Redis cache z obiektami `\shop\Product` powodowal ceny 0,00 zl (obiekty `__PHP_Incomplete_Class` po `(array)` cast maja zmanglowane klucze). Teraz invaliduje stary cache i re-fetchuje z DB
- FIX: `LayoutEngine.php` — 4 niekwalifikowane `Product::getFromCache()` resolwaly do `\front\Product` po usunieciu `use shop\Product`
- FIX: szablony (product-mini, product-search, product, product-warehouse-message, alert-product-sets, basket-details, main-view, promoted-products) — konwersja object access (`$product->field`) na array access (`$product['field']`)
- UPDATE: `AttributeRepository::getAttributeValueById()` — dodano Redis cache (zgodnosc ze starym `\shop\ProductAttribute::get_value_name()`)
- **PODSUMOWANIE CALEJ MIGRACJI:** 12 legacy klas, ~2363 linii kodu usunieto. Zero referencji `\shop\` w aktywnym kodzie.
- Testy: 610 OK, 1817 asercji
---
## ver. 0.293 (2026-02-17) - front\controls\Site + front\view\Site → front\App + front\LayoutEngine
- **front\controls\Site → front\App** — migracja routera na nowy namespace
- Port 1:1 z camelCase: `check_url_params()` → `checkUrlParams()`, `page_title()` → `pageTitle()`
- Wewnętrzna zmiana: `\front\view\Site::contact()` → `\front\LayoutEngine::contact()`
- USUNIETA: `autoload/front/controls/class.Site.php`
- **front\view\Site → front\LayoutEngine** — migracja layout engine na nowy namespace
- Port 1:1 z camelCase: `cookie_information()` → `cookieInformation()`
- Wewnętrzne zmiany: `\front\controls\Site::route()` → `\front\App::route()`, self-referencje na `self::`
- 13 staĹych regex i ~30 tag replacements przeniesione 1:1
- USUNIETA: `autoload/front/view/class.Site.php`
- **Call sites** — 4 miejsca przepięte:
- `index.php:62` — `\front\App::checkUrlParams()`
- `index.php:157` — `\front\LayoutEngine::show()`
- `index.php:160` — `\front\LayoutEngine::cookieInformation()`
- `ajax.php:51` — `\front\App::checkUrlParams()`
- USUNIETE puste foldery: `autoload/front/controls/`, `autoload/front/view/`
- **Pelna migracja frontendu zakonczona** — `autoload/front/` zawiera: `App.php`, `LayoutEngine.php`, `Controllers/`, `Views/`
- Testy: 610 OK, 1816 asercji (bez zmian — czysty rename/restructure)
---
## ver. 0.292 (2026-02-17) - ShopProduct + ShopPaymentMethod + ShopPromotion + ShopStatuses + ShopTransport frontend migration
- **Pelna migracja front\factory\** — USUNIETY caly folder `autoload/front/factory/` (wszystkie 20 klas zmigrowane do Domain)
- **ShopProduct (frontend Stage 1)** — migracja factory + controls na Domain
- NOWE METODY w `ProductRepository`: ~20 metod frontendowych (getSkuWithFallback, getEanWithFallback, isProductActiveCached, productCategoriesFront, getWarehouseMessageZero/Nonzero, topProductIds, newProductIds, promotedProductIdsCached, getMinimalPrice, productImageCached, productNameCached, productUrlCached, randomProductIds, productWp)
- USUNIETA: `front\factory\class.ShopProduct.php` (~410 linii) — logika przeniesiona do `ProductRepository`
- USUNIETA: `front\controls\class.ShopProduct.php` — logika w `ProductRepository` + szablony
- UPDATE: callerzy w szablonach, kontrolerach, index.php, cron.php przepieci na repo
- **ShopPaymentMethod (frontend)** — migracja factory + view + shop facade na Domain
- NOWE METODY w `PaymentMethodRepository`: metody frontendowe z Redis cache (paymentMethodsCached, paymentMethodCached, paymentMethodsByTransportCached)
- USUNIETA: `front\factory\class.ShopPaymentMethod.php`, `front\view\class.ShopPaymentMethod.php`, `shop\class.PaymentMethod.php`
- UPDATE: callerzy w szablonach i kontrolerach przepieci na `PaymentMethodRepository`
- **ShopPromotion (frontend)** — migracja factory na Domain
- NOWE METODY w `PromotionRepository`: 5 metod aplikowania promocji (applyTypeWholeBasket, applyTypeCheapestProduct, applyTypeCategoriesOr, applyTypeCategoriesAnd, applyTypeCategoryCondition)
- USUNIETA: `front\factory\class.ShopPromotion.php` — logika przeniesiona do `PromotionRepository`
- UPDATE: `shop\Promotion::find_promotion()` — 5 wywolan przepietych na repo
- **ShopStatuses (frontend)** — rewiring callerow
- USUNIETA: `front\factory\class.ShopStatuses.php` — 4 callerzy przepieci bezposrednio na `ShopStatusRepository`
- **ShopTransport (frontend)** — migracja factory + view na Domain
- NOWE METODY w `TransportRepository`: 4 metody frontendowe (transportMethodsFront, transportCostCached, findActiveByIdCached, forPaymentMethod)
- USUNIETA: `front\factory\class.ShopTransport.php`, `front\view\class.ShopTransport.php`
- UPDATE: 8 callerow przepietych na `TransportRepository`
- FIX: broken `transports_list()` w ajax.php — nowa metoda `forPaymentMethod()`
- NOWY: `tests/stubs/ShopProduct.php` — stub `shop\Product` dla testow
- Testy: 610 OK, 1816 asercji (+37: 20 ProductRepository, 5 PaymentMethodRepository, 7 PromotionRepository, 5 TransportRepository)
---
## ver. 0.291 (2026-02-17) - ShopProducer frontend migration
- **ShopProducer (frontend)** — migracja controls + shop facade na Domain + Controllers
- NOWA METODA w `ProducerRepository`: `allActiveProducers()` — peĹne dane aktywnych producentĂłw (id, name, img)
- NOWY: `front\Controllers\ShopProducerController` — instancyjny kontroler z DI (`products()`, `list()`)
- USUNIETA: `front\controls\class.ShopProducer.php` — logika przeniesiona do kontrolera + repozytorium
- USUNIETA: `autoload\shop\class.Producer.php` — fasada niepotrzebna, callery przepięte na repo
- UPDATE: `front\view\Site::show()` — `new \shop\Producer(...)` zamienione na `$producerRepo->findForFrontend()`
- UPDATE: `front\controls\Site::getControllerFactories()` — zarejestrowany `'ShopProducer'`
- FIX: bug `shop\Producer::__get()` referowaŠnieistniejące `$this->data` (usunięty z kodem klasy)
- Testy: 573 OK, 1738 asercji (+8: 5 ProducerRepository frontend, 3 ShopProducerController)
---
## ver. 0.290 (2026-02-17) - ShopCoupon + ShopOrder frontend migration
- **ShopCoupon (frontend)** — migracja controls + factory na Domain + Controllers
- NOWE METODY w `CouponRepository`: 4 metody frontendowe (findByName, isAvailable, markAsUsed, incrementUsedCount)
- NOWY: `front\Controllers\ShopCouponController` — instancyjny kontroler z DI (useCoupon, deleteCoupon)
- KONWERSJA: `shop\Coupon` na fasade z dzialajacymi metodami (is_one_time, set_as_used)
- FIX: kupony jednorazowe nigdy nie byly oznaczane jako uzyte (is_one_time zwracalo null)
- USUNIETA: `front\controls\class.ShopCoupon.php`, `front\factory\class.ShopCoupon.php`
- **ShopOrder (frontend)** — migracja controls + factory + view na Domain + Controllers
- NOWE METODY w `OrderRepository`: 5 metod frontendowych (findIdByHash, findHashById, orderDetailsFrontend, generateOrderNumber, createFromBasket ~180 linii)
- NOWY: `front\Controllers\ShopOrderController` — instancyjny kontroler z DI (paymentConfirmation, paymentStatusTpay, paymentStatusPrzelewy24pl, paymentStatusHotpay, orderDetails)
- POPRAWA: webhooks przelewy24/hotpay — zamiana recznych $mdb->update/insert na \shop\Order::set_as_paid() + update_status() (spójnosc z tpay, poprawna obsluga Apilo sync)
- UPDATE: `ShopBasketController` — DI OrderRepository (createFromBasket, findHashById)
- UPDATE: `ClientRepository::clientOrders()` — OrderRepository::orderDetailsFrontend()
- UPDATE: `shop\Order::order_resend_confirmation_email()` — OrderRepository::orderDetailsFrontend()
- UPDATE: `cron-turstmate.php` — OrderRepository::orderDetailsFrontend()
- USUNIETA: `front\controls\class.ShopOrder.php`, `front\factory\class.ShopOrder.php`, `front\view\class.ShopOrder.php`
- Testy: 565 OK, 1716 asercji (+28: 12 CouponRepository frontend, 3 ShopCouponController, 10 OrderRepository frontend, 3 ShopOrderController)
---
## ver. 0.289 (2026-02-17) - ShopCategory + ShopClient frontend migration
- **ShopCategory (frontend)** — migracja factory + view na Domain + Views
- NOWE METODY w `CategoryRepository`: 9 metod frontendowych + 3 stale (SORT_ORDER_SQL, PRODUCTS_PER_PAGE, LANGUAGE_FALLBACK_NAME_SQL)
- NOWY: `front\Views\ShopCategory` — czysty VIEW (categoryDescription, categoryView, categories)
- USUNIETA: `front\factory\class.ShopCategory.php` — logika przeniesiona do `CategoryRepository`
- USUNIETA: `front\view\class.ShopCategory.php` — zastapiona przez `front\Views\ShopCategory`
- UPDATE: `index.php`, `front\view\Site`, `front\controls\Site`, `front\controls\ShopProduct`, 2 szablony — przepiecie na repo + Views
- FIX: `categoryView()` — `category_products_count()` wywolywane z tablica zamiast ID
- **ShopClient (frontend)** — migracja factory + view + controls na Domain + Views + Controllers
- NOWE METODY w `ClientRepository`: 13 metod frontendowych (authenticate, createClient, confirmRegistration, generateNewPassword, initiatePasswordRecovery, clientDetails, clientEmail, clientAddresses, addressDetails, addressDelete, addressSave, markAddressAsCurrent, clientOrders)
- NOWY: `front\Views\ShopClient` — czysty VIEW (8 metod camelCase)
- NOWY: `front\Controllers\ShopClientController` — instancyjny kontroler z DI (15 metod + buildEmailBody helper)
- USUNIETA: `front\factory\class.ShopClient.php`, `front\view\class.ShopClient.php`, `front\controls\class.ShopClient.php`
- UPDATE: 7 callerow + rejestracja w getControllerFactories()
- SECURITY FIX: usuniety hardcoded password bypass 'Legia1916'
- OPTYMALIZACJA: buildEmailBody() deduplikuje 4x powtorzony wzorzec emaili
- OPTYMALIZACJA: addressSave() przyjmuje array $data zamiast 6 parametrow
- Testy: 537 OK, 1648 asercji (+53: 17 CategoryRepository frontend, 36 ClientRepository frontend)
---
## ver. 0.288 (2026-02-17) - BasketCalculator + ShopBasketController + cms\Layout removal
- **ShopBasket (factory → Domain)** — migracja na Domain
- NOWY: `Domain\Basket\BasketCalculator` — 4 statyczne metody (`summaryWp`, `countProductsText`, `summaryPrice`, `countProducts`)
- USUNIETA: `front\factory\class.ShopBasket.php` — logika przeniesiona do `BasketCalculator`
- UPDATE: 18 callerow w 7 plikach przepietych na `\Domain\Basket\BasketCalculator::`
- **ShopBasket (controls → Controllers)** — migracja kontrolera
- NOWY: `front\Controllers\ShopBasketController` — instancyjny kontroler z camelCase metodami
- Wyekstrahowano `jsonBasketResponse()` — wspolna odpowiedz JSON dla 4 metod koszyka
- Zainicjalizowano zmienne `$values`, `$attributes`, `$custom_fields` w `basketAddProduct()`
- USUNIETA: `front\controls\class.ShopBasket.php` — zastapiona przez `ShopBasketController`
- UPDATE: `front\controls\Site::route()` — konwersja `snake_case → camelCase` w dispatch dla nowych kontrolerow
- UPDATE: `front\controls\Site::title()` / `page_title()` — sprawdzanie `front\Controllers\*Controller` przed fallback
- Zarejestrowany `'ShopBasket'` w `getControllerFactories()`
- **cms\Layout removal**
- USUNIETA: `autoload/cms/class.Layout.php` — caly folder `autoload/cms/` (1 klasa, bug w `__get()`)
- UPDATE: `front\view\Site::show()` — `new \cms\Layout(...)` → `$layoutsRepo->find(...)`
- Testy: 484 OK, 1528 asercji (+8 testow: BasketCalculatorTest)
---
## ver. 0.287 (2026-02-17) - Scontainers + ShopAttribute frontend migration
- **Scontainers (frontend)** — migracja na Domain
- NOWA METODA w `ScontainersRepository`: `frontScontainerDetails()` — z Redis cache + fallback
- UPDATE: `clearFrontCache()` — klucz cache dopasowany do nowego wzorca
- NOWY: `front\Views\Scontainers` — czysty VIEW (`scontainer()`)
- USUNIETA: `front\factory\class.Scontainers.php` — logika przeniesiona do `ScontainersRepository`
- USUNIETA: `front\view\class.Scontainers.php` — zastapiona przez `front\Views\Scontainers`
- UPDATE: `front\view\Site::show()` — przepiecie na `$scontainersRepo->frontScontainerDetails()` + `\front\Views\Scontainers::`
- **ShopAttribute (frontend)** — migracja na Domain
- NOWE METODY w `AttributeRepository`: `frontAttributeDetails()`, `frontValueDetails()` — z Redis cache + fallback
- NOWA: `clearFrontCache()` — czyszczenie cache frontowego per atrybut/wartoĹć i jÄ™zyk
- UPDATE: `saveAttribute()`, `deleteAttribute()`, `saveValues()` — wpiete czyszczenie cache frontowego
- USUNIETA: `front\factory\class.ShopAttribute.php` — logika przeniesiona do `AttributeRepository`
- UPDATE: `front\factory\ShopOrder` — przepiecie na `$attributeRepo->frontAttributeDetails()` / `frontValueDetails()`
- UPDATE: 3 szablony (`product-attribute.php`, `summary-view.php`, `basket-details.php`) — przepiecie na `AttributeRepository`
- Testy: 476 OK, 1512 asercji (+6 testow: 2 ScontainersRepository frontend, 4 AttributeRepository frontend)
---
## ver. 0.286 (2026-02-17) - Layouts, Menu, Pages frontend migration
- **Layouts (frontend)** — migracja na Domain
- NOWE METODY w `LayoutsRepository`: `categoryDefaultLayoutId()`, `getDefaultLayout()`, `getProductLayout()`, `getArticleLayout()`, `getCategoryLayout()`, `getActiveLayout()` — z Redis cache, 3-level fallback (productâ†categoryâ†default)
- USUNIETA: `front\factory\class.Layouts.php` — logika przeniesiona do `LayoutsRepository`
- UPDATE: `front\view\Site::show()` — przepiecie na `$layoutsRepo`
- UPDATE: `Shared\Helpers\Helpers::htacces()` — optymalizacja z 3 wywolan `category_default_layout()` do jednej zmiennej
- **Menu + Pages (frontend)** — migracja na Domain + Views
- NOWE METODY w `PagesRepository`: `frontPageDetails()`, `frontPageSort()`, `frontMainPageId()`, `frontLangUrl()`, `frontMenuDetails()`, `frontMenuPages()` — z Redis cache, rekurencja stron
- NOWY: `front\Views\Menu` — czysty VIEW (`pages()`, `menu()`)
- USUNIETA: `front\factory\class.Menu.php` — logika przeniesiona do `PagesRepository`
- USUNIETA: `front\factory\class.Pages.php` — logika przeniesiona do `PagesRepository`
- USUNIETA: `front\view\class.Menu.php` — zastapiona przez `front\Views\Menu`
- USUNIETA: `templates\menu\submenu.php` — martwy kod (wola nieistniejaca metode `Menu::submenu()`)
- UPDATE: `front\controls\Site::check_url_params()` — przepiecie na `$pagesRepo->frontPageDetails()`
- UPDATE: `index.php` — przepiecie na `$pagesRepo->frontPageDetails()`
- UPDATE: `templates/site/languages.php` — przepiecie na `$pagesRepo->frontLangUrl()`
- UPDATE: `templates/menu/menu.php`, `pages.php`, `main-menu.php` — przepiecie na `\front\Views\Menu::`
- FIX: `frontPageDetails()` — usuniety type hint `string` z `$langId` + cast `(string)` (null $lang_id przy wczesnym wywolaniu `check_url_params()`)
- FIX: `front\controls\class.Site.php` — dodano `$lang_id ?? ''` przy przekazywaniu do `frontPageDetails()`
- Testy: 470 OK, 1484 asercji (+16 testow: 8 LayoutsRepository frontend, 8 PagesRepository frontend)
---
## ver. 0.285 (2026-02-17) - Tpl namespace, CurlServer removal, thumb.php fix
- **Shared\Tpl\Tpl** — migracja silnika szablonow do namespace Shared
- NOWY: `autoload/Shared/Tpl/Tpl.php` (namespace `Shared\Tpl`)
- USUNIETA: `autoload/class.Tpl.php` — zastapiona przez `Shared\Tpl\Tpl`
- ZAMIENIONE: ~135 plikow — `\Tpl::` / `new \Tpl` → `\Shared\Tpl\Tpl::` / `new \Shared\Tpl\Tpl`
- DRY: wyciagnieto powtorzony `ob_start/include/ob_get_contents/ob_end_clean` do prywatnej metody `renderFile()`
- FIX: bug w `render()` branch 3 — sprawdzal `../templates_user/` ale ladowal `../templates/`
- CLEANUP: usunieta nieuzywana property `$dir` i konstruktor `__construct($dir)`
- **CurlServer** — usuniecie nieuzywanej klasy
- USUNIETA: `autoload/curl.class.php` — klasa `CurlServer` bez referencji w projekcie (0 uzyc)
- **thumb.php** — naprawa require po migracji Image
- FIX: `libraries/thumb.php` — zmiana `require_once '../autoload/class.Image.php'` na `../autoload/Shared/Image/ImageManipulator.php` + `use`
- FIX: poprawiony short open tag `<?` na `<?php`
- Testy: 454 OK, 1449 asercji
---
## ver. 0.284 (2026-02-16) - DbModel elimination
- **DbModel** — usunięcie klasy base ORM
- USUNIETA: `autoload/class.DbModel.php` — jedyny konsument (`shop\Promotion`) ma teraz wbudowany konstruktor + `__get()`
- UPDATE: `autoload/shop/class.Promotion.php` — usunięto `extends DbModel` + `use DbModel`, wbudowano minimalny konstruktor z `$mdb->get()` i `__get()`
- Testy: 454 OK, 1449 asercji
---
## ver. 0.283 (2026-02-16) - Legacy class cleanup — S, Html, Email, Image, Log, Mobile_Detect → Shared namespace
- **class.S.php → Shared\Helpers\Helpers**
- PRZENIESIONA: `class.S.php` → `autoload/Shared/Helpers/Helpers.php` (namespace `Shared\Helpers`, klasa `Helpers`)
- ZAMIENIONE: ~140 plików — `\S::` → `\Shared\Helpers\Helpers::`
- CLEANUP: usunięto 12 nieużywanych metod (set_array_value, parse_name, clear_redis_cache, get_domain, pre_dump, escape, chmod_r, rrmdir, rcopy, pre, json_to_array, is_empty_dir)
- FIX: `array_cartesian_product()` — iteracja po niezdefiniowanej zmiennej `$array` zamiast parametru `$input`
- NOWY: `tests/stubs/Helpers.php` — stub klasy Helpers dla testów
- USUNIETA: `autoload/class.S.php`
- **Shared\Html\Html** — `autoload/class.Html.php` → `autoload/Shared/Html/Html.php`
- **Shared\Email\Email** — `autoload/class.Email.php` → `autoload/Shared/Email/Email.php`
- **Shared\Image\Image** — `autoload/class.Image.php` → `autoload/Shared/Image/Image.php`
- **Shared\Log\Log** — `autoload/class.Log.php` → `autoload/Shared/Log/Log.php`
- **Mobile_Detect** — USUNIETA: przestarzaĹa detekcja UA (v2.8.16), zastÄ…piona responsive design
- USUNIETA: metoda `S::is_mobile()` i 3 warunki mobilne w `front\view\Site`
- USUNIETE z `LayoutsRepository`: pola `m_html`, `m_css`, `m_js`
- Testy: 454 OK, 1449 asercji
---
## ver. 0.282 (2026-02-16) - Cache cleanup, Shared namespace
- **Shared\Cache namespace** — przeniesienie CacheHandler i RedisConnection do `Shared\Cache\`
- NOWY: `autoload/Shared/Cache/CacheHandler.php` — glowna implementacja
- NOWY: `autoload/Shared/Cache/RedisConnection.php` — glowna implementacja singleton
- USUNIETA: `autoload/class.CacheHandler.php` — wrapper usuniety, 60 odwolan przepietych na `\Shared\Cache\CacheHandler`
- USUNIETA: `autoload/class.RedisConnection.php` — wrapper usuniety, 12 odwolan przepietych na `\Shared\Cache\RedisConnection`
- **Eliminacja legacy Cache** — usuniecie plikowego cache `class.Cache.php`
- USUNIETA: `autoload/class.Cache.php` — legacy file-based cache (store/fetch z base64+serialize)
- UPDATE: `front\factory\ShopProduct` — 5 metod przepietych z `\Cache::` na `CacheHandler` (get_minimal_price, product_name, product_image, random_products, promoted_products)
- UPDATE: `front\factory\ShopPaymentMethod` — 3 metody przepiete (payment_methods_by_transport, payment_method, payment_methods)
- UPDATE: `front\factory\ShopCategory` — 2 metody przepiete (get_category_sort, category_name)
- UPDATE: `front\factory\ShopTransport` — 2 metody przepiete (transport_cost, transport)
- UPDATE: `front\factory\ShopAttribute` — 1 metoda przepieta (attribute_details)
- UPDATE: `Domain\Dictionaries\DictionariesRepository` — prywatne wrappery cacheFetch/cacheStore przepiete na CacheHandler
- FIX: naprawione rozbieznosci kluczy cache (random_products, category_name) — fetch i store uzywaly roznych kluczy
- Testy: 454 OK, 1449 asercji
---
## ver. 0.281 (2026-02-16) - Banners frontend migration
- **Banners (frontend)** — migracja na Domain + Views
- NOWE METODY w `BannerRepository`: `banners(string $langId)`, `mainBanner(string $langId)` — z Redis cache, filtrowanie dat, plaski format `$banner['languages']` (zgodny z szablonami)
- NOWY: `front\Views\Banners` — czysty VIEW (`banners()`, `mainBanner()`)
- USUNIETA: `front\factory\Banners` — logika przeniesiona do `BannerRepository`
- USUNIETA: `front\view\Banners` — zastapiona przez `front\Views\Banners`
- UPDATE: `front\view\Site::show()` — przepiecie linii 62-63 na repo + Views (bezposrednio `$bannerRepo->banners()` / `$bannerRepo->mainBanner()`)
- Testy: 454 OK, 1449 asercji (+4 nowe testy w BannerRepositoryTest)
---
## ver. 0.280 (2026-02-16) - Articles frontend migration
- **Articles (frontend)** — pelna migracja na Domain + Views
- NOWE METODY w `ArticleRepository`: `articleDetailsFrontend()`, `articlesIds()`, `pageArticlesCount()`, `pageArticles()`, `news()`, `articleNoindex()`, `topArticles()`, `newsListArticles()`
- NOWY: `front\Views\Articles` — czysty VIEW + utility (renderowanie, generateTableOfContents, generateHeadersIds, getImage)
- UPDATE: `front\view\Site::show()` — przepiecie 5 sekcji na repo + Views (BOX aktualnosci, article meta, noindex, news list, top articles)
- UPDATE: `front\controls\Site::route()` — przepiecie single article + page_type switch (4 typy) na repo + Views
- UPDATE: 5 szablonow `templates/articles/*` — przepiecie na `\front\Views\Articles::`
- FASADA: `front\factory\Articles` — 10 metod delegujacych do repo + Views
- FASADA: `front\view\Articles` — 5 metod delegujacych do repo + Views
- USUNIETA: `class.Article` — encja + metody statyczne; logika przeniesiona do `ArticleRepository` + `front\Views\Articles`
- FIX: eliminacja `global $lang` z `articleNoindex()` — zamiana na jawny parametr `$langId`
- FIX: eliminacja zaleznosci od `\front\factory\Pages::page_sort()` w `news()` — inline query
- FIX: naprawione kolizje nazw zmiennych w `front\view\Site::show()` ($news_list → $news_list_matches, $top_news_matches)
- Testy: 450 OK, 1431 asercji (+13 nowych testow w ArticleRepositoryTest)
- UPDATE: `tests/bootstrap.php` — dodany stub `S::is_array_fix()`
---
## ver. 0.279 (2026-02-16) - Newsletter + Languages frontend migration, front\Controllers, front\Views
- **Languages (view)** — migracja do nowego namespace
- USUNIĘTA: `front\factory\Languages` — fasada niepotrzebna, wszystkie zaleĹĽnoĹci przepiÄ™te na `Domain\Languages\LanguagesRepository`
- USUNIĘTA: `front\view\Languages` → przeniesiona do `front\Views\Languages` (nowy namespace, bez `class.` prefix)
- UPDATE: 26 plików przepiętych z fasady na repozytorium (kontrolery DI, entry points, szablony, shop classes)
- UPDATE: `admin\App` — DI factory dla `ShopProductController` rozszerzona o `LanguagesRepository`
- **Newsletter (frontend)** — peĹna migracja na Domain
- NOWE METODY w `NewsletterRepository`: `unsubscribe()`, `confirmSubscription()`, `getHashByEmail()`, `removeByEmail()`, `signup()`, `sendQueued()`
- NOWY: `front\Controllers\NewsletterController` — pierwszy frontowy kontroler z DI (nowy namespace `front\Controllers\`)
- NOWY: `front\Views\Newsletter` — czysty VIEW (nowy namespace `front\Views\`)
- USUNIĘTA: `front\factory\Newsletter` — logika przeniesiona do `NewsletterRepository`
- USUNIĘTA: `front\view\Newsletter` → zastąpiona przez `front\Views\Newsletter`
- USUNIĘTA: `front\controls\Newsletter` → zastąpiona przez `front\Controllers\NewsletterController`
- UPDATE: `front\controls\Site::route()` — nowy routing: `getControllerFactories()` (DI) → fallback stare `front\controls\`
- UPDATE: `front\factory\ShopClient` — 4x `get_template()` przepięte na `NewsletterRepository::templateByName()`
- UPDATE: `index.php` — `newsletter_send()` przepięte na `$repo->sendQueued()`
- FIX: `newsletter_unsubscribe()` — bĹÄ™dna skĹadnia medoo `delete()` (3 argumenty zamiast 2)
- UPDATE: `tests/bootstrap.php` — dodane stuby: `S::email_check()`, `S::get_session()`, `S::set_session()`
- Testy: 437 OK, 1398 asercji (+10 nowych testĂłw w NewsletterRepositoryTest)
---
## ver. 0.278 (2026-02-16) - Settings + Languages frontend migration
- **Settings + Languages (frontend)** — pierwszy etap refaktoringu frontendu
- NOWE METODY: `SettingsRepository::allSettings($skipCache)` — pobranie ustawien z cache Redis
- NOWE METODY: `SettingsRepository::getSingleValue($param)` — pobranie pojedynczej wartosci ustawienia
- NOWE METODY: `LanguagesRepository::defaultLanguage()` — domyslny jezyk z cache Redis
- NOWE METODY: `LanguagesRepository::activeLanguages()` — lista aktywnych jezykow z cache Redis
- NOWE METODY: `LanguagesRepository::translations($lang)` — tlumaczenia z cache Redis
- UPDATE: `front\factory\Settings` → fasada delegujaca do `SettingsRepository`
- UPDATE: `front\factory\Languages` → fasada delegujaca do `LanguagesRepository`
- FIX: `get_single_settings_value()` — parametr `$param` poprawnie uzywany (wczesniej hardcoded `'firm_name'`)
- Testy: 427 OK, 1378 asercji (+13 nowych)
---
## ver. 0.277 (2026-02-16) - ShopProduct factory, Dashboard, Update, legacy cleanup, admin\App
- **ShopProduct (factory)** - pelna migracja modulu #29 na Domain + DI
- NOWE: `ProductRepository` rozszerzony o ~40 metod: CRUD (countProducts, listForAdmin, findForAdmin, allProductsList, productCategoriesText, getParentId, productDefaultName), zapis (saveProduct + 9 prywatnych helperow), operacje (delete, duplicate, toggleStatus, updatePriceBrutto/Promo, updateCustomLabel), kombinacje (getPermutations, generateCombinations, deleteCombination, countCombinations, saveCombination*), zdjecia/pliki (deleteImage, updateImageAlt, saveImagesOrder, deleteFile, updateFileName, deleteNonassigned*), Google Feed XML (generateGoogleFeedXml, generateEAN), custom labels (customLabelSuggestions, saveCustomLabel, saveXmlName), updateCombinationPricesFromBase
- NOWE: `ShopProductController` rozszerzony o ~30 akcji: view_list, product_edit, save, duplicate_product, product_archive/unarchive, product_delete, change_product_status, product_change_price_brutto/promo, product_change_custom_label, product_custom_label_suggestions/save, ajax_product_url, generate_sku_code, product_combination, generate_combination, delete_combination, product_combination_*_save, image_delete, images_order_save, image_alt_change, product_file_delete, product_file_name_change, product_image_delete
- UPDATE: przepiecie zaleznosci zewnetrznych: `ProductArchiveController`, `order-details.php`, `cron.php`, `cron-xml.php`, `product-edit.php`, `mass-edit-custom-script.php`
- CLEANUP: usuniete `autoload/admin/controls/class.ShopProduct.php`, `autoload/admin/factory/class.ShopProduct.php`, `admin/ajax/shop.php` + require z `admin/ajax.php`
- **ShopOrder (stabilizacja po migracji)**
- FIX: `Domain\Order\OrderRepository::listForAdmin()` - poprawa zapytan SQL (count/list), bezpieczne fallbacki i poprawne zwracanie listy zamowien w `/admin/shop_order/list/`
- FIX: wyrĂłwnanie wysokoĹci komĂłrek w `components/table-list` (`vertical-align` + lokalny override dla `.text-right` w tabeli)
- **Integrations (cleanup)**
- CLEANUP: usunieta fasada `autoload/admin/factory/class.Integrations.php`
- UPDATE: przepiÄ™cie wywoĹaĹ„ na `Domain\Integrations\IntegrationsRepository` w: `cron.php`, `shop\Order`, `admin\Controllers\ShopPaymentMethodController`, `admin\Controllers\ShopStatusesController`, `admin\Controllers\ShopTransportController`
- **Admin UX**
- NOWE: globalna wyszukiwarka w top-barze (obok "Wyczysc cache") dla produktow i zamowien
- NOWE: endpoint `/admin/settings/globalSearchAjax/` (`SettingsController::globalSearchAjax`)
- FIX: wsparcie wyszukiwania po peĹnym imieniu i nazwisku (np. "Jan Kowalski") + poprawka escapingu SQL w `CONCAT_WS`
- **Dashboard** - migracja modulu #30 na Domain + DI
- NOWE: `Domain\Dashboard\DashboardRepository` (summaryOrders, summarySales, salesGrid, mostViewedProducts, bestSalesProducts, last24MonthsSales, lastOrders, Redis caching)
- NOWE: `admin\Controllers\DashboardController` (DI z DashboardRepository + ShopStatusRepository)
- CLEANUP: usuniete `autoload/admin/controls/class.Dashboard.php`, `autoload/shop/class.Dashboard.php`
- **Update** - migracja modulu #31 na Domain + DI
- NOWE: `Domain\Update\UpdateRepository` (update, runPendingMigrations, downloadAndApply, executeSql, deleteFiles, extractZip, saveLog)
- NOWE: `admin\Controllers\UpdateController` (DI z UpdateRepository)
- UPDATE: template `update/main-view.php` - usunieto `gridEdit` i `$.prompt()`, zastapiono panelami + `$.confirm()`/`$.alert()`
- CLEANUP: usuniete `autoload/admin/controls/class.Update.php`, `autoload/admin/factory/class.Update.php`, `autoload/admin/view/class.Update.php`
- **Legacy cleanup**
- CLEANUP: usunieto `autoload/admin/factory/class.Articles.php` (martwy kod, `articles_by_date_add` przeniesione do `ArticleRepository`)
- UPDATE: `front\factory\Newsletter` przepieta na `Domain\Article\ArticleRepository::articlesByDateAdd()`
- CLEANUP: usunieto `autoload/admin/view/class.Page.php`, logika przeniesiona do `admin\App::render()`
- CLEANUP: usuniete puste foldery `autoload/admin/controls/`, `autoload/admin/factory/`, `autoload/admin/view/`
- **admin\Site -> admin\App**
- UPDATE: klasa `admin\Site` przemianowana na `admin\App` (plik `App.php` bez przedrostka `class.`)
- UPDATE: refaktoring `App` — usunieto martwy fallback na `\admin\controls\`, uproszczono routing, ujednolicony code style
- TEST:
- NOWE: `DashboardControllerTest` (4), `DashboardRepositoryTest` (6), `UpdateControllerTest` (6), `UpdateRepositoryTest` (6)
- Pelny suite: **OK (414 tests, 1335 assertions)**
---
## ver. 0.276 (2026-02-15) - ShopOrder
- **ShopOrder** - migracja `/admin/shop_order/*` na Domain + DI + nowe widoki
- NOWE: `Domain\Order\OrderRepository` (lista admin z filtrowaniem/sortowaniem, szczegĂłĹy, historia statusĂłw, notes, save admin, summary, trustmate, delete)
- NOWE: `Domain\Order\OrderAdminService` (operacje aplikacyjne admin: status/paid/unpaid/resend email/send to apilo/delete)
- NOWE: `admin\Controllers\ShopOrderController` (DI) z akcjami `list/view_list`, `details/order_details`, `edit/order_edit`, `save/order_save`, `notes_save`, `order_status_change`, `order_resend_confirmation_email`, `set_order_as_paid`, `set_order_as_unpaid`, `send_order_to_apilo`, `toggle_trustmate_send`, `delete/order_delete`
- UPDATE: routing DI (`admin\Site`) rozszerzony o modul `ShopOrder`
- UPDATE: menu admin przepiete na kanoniczny URL `/admin/shop_order/list/`
- UPDATE: lista zamówień przepięta z legacy grid na `components/table-list` (`shop-order/orders-list`)
- UPDATE: `shop-order/order-details` i `shop-order/order-edit` przebudowane bez `gridEdit` + wydzielenie JS do `*-custom-script.php`
- UPDATE: `shop\Order::order_statuses()` przepiete na `Domain\ShopStatus\ShopStatusRepository`
- UPDATE: `admin\controls\Dashboard` pobiera statusy przez `Domain\ShopStatus\ShopStatusRepository`
- CLEANUP: usuniete legacy `autoload/admin/controls/class.ShopOrder.php`, `autoload/admin/factory/class.ShopOrder.php`, `admin/templates/shop-order/view-list.php`
- TEST:
- NOWE: `tests/Unit/Domain/Order/OrderRepositoryTest.php`
- NOWE: `tests/Unit/admin/Controllers/ShopOrderControllerTest.php`
- Testy punktowe: **OK (8 tests, 49 assertions)**
---
## ver. 0.275 (2026-02-15) - ShopCategory
- **ShopCategory** - migracja `/admin/shop_category/*` na Domain + DI + nowe endpointy AJAX
- NOWE: `Domain\Category\CategoryRepository` (`sortTypes`, `subcategories`, `categoryDetails`, `categoryProducts`, `save`, `categoryDelete`, `saveCategoriesOrder`, `saveProductOrder`, `categoryTitle`)
- NOWE: `admin\Controllers\ShopCategoryController` (DI) z akcjami `list/view_list`, `edit/category_edit`, `save`, `delete/category_delete`, `products/category_products`, `category_url_browser`, `save_categories_order`, `save_products_order`, `cookie_categories`
- UPDATE: routing DI (`admin\Site`) rozszerzony o modul `ShopCategory`
- UPDATE: menu admin przepiete na kanoniczny URL `/admin/shop_category/list/`
- UPDATE: widoki `shop-category/*` - wydzielenie skryptow do `*-custom-script.php`, ujednolicone strzalki drzewa (`button + caret + aria-expanded`)
- UPDATE: AJAX drzewek przepiety z `/admin/ajax.php?a=*` na `/admin/shop_category/*`
- UPDATE: zaleznosci `ShopProduct` przepiete z `admin\factory\ShopCategory` na `Domain\Category\CategoryRepository`
- CLEANUP: usuniete legacy `autoload/admin/controls/class.ShopCategory.php`, `autoload/admin/factory/class.ShopCategory.php`, `autoload/admin/view/class.ShopCategory.php`
- CLEANUP: usuniety preload `class.ShopCategory.php` z `libraries/grid/config.php`
- TEST:
- NOWE: `tests/Unit/Domain/Category/CategoryRepositoryTest.php`
- NOWE: `tests/Unit/admin/Controllers/ShopCategoryControllerTest.php`
- Testy punktowe: **OK (16 tests, 72 assertions)**
- Pelny suite: **OK (377 tests, 1197 assertions)**
---
## ver. 0.274 (2026-02-15) - ShopProduct mass_edit + UI trees
- **ShopProduct (mass_edit)** - migracja akcji masowej edycji na Domain + DI
- NOWE: `admin\Controllers\ShopProductController` (DI) z akcjami `mass_edit`, `mass_edit_save`, `get_products_by_category`
- UPDATE: routing DI (`admin\Site`) rozszerzony o modul `ShopProduct`
- UPDATE: `Domain\Product\ProductRepository` rozszerzone o metody `allProductsForMassEdit`, `getProductsByCategory`, `applyDiscountPercent` (+ aktualizacja cen kombinacji)
- CLEANUP: usuniete legacy akcje `mass_edit`, `mass_edit_save`, `get_products_by_category` z `admin\controls\ShopProduct`
- **ShopProduct mass_edit UI** - przebudowa widoku i skryptu
- UPDATE: `admin/templates/shop-product/mass-edit.php` przepiety na nowy partial JS `mass-edit-custom-script`
- NOWE: `admin/templates/shop-product/mass-edit-custom-script.php` (nestedSortable + iCheck + stabilizacja drzewka)
- UPDATE: `admin/templates/shop-product/subcategories-list.php` ujednolicone strzalki (button + caret)
- FIX: zaznaczenie kategorii w drzewku nie zaznacza automatycznie produktow na liscie
- **Pages / Articles UI** - ujednolicenie drzewek
- UPDATE: `/admin/pages/list/` - nowe strzalki drzewa + `aria-expanded` + odswiezanie stanu branch/leaf
- UPDATE: `/admin/articles/edit/*` (zakladka wyswietlania) - nowe strzalki i checkboxy (iCheck) dla drzewka stron
- **ShopClients** - migracja `/admin/shop_clients` na Domain + DI + nowe widoki
- NOWE: `Domain\Client\ClientRepository` (`listForAdmin`, `ordersForClient`, `totalsForClient`)
- NOWE: `admin\Controllers\ShopClientsController` (DI) z akcjami `list`, `details` + aliasy legacy `view_list`, `clients_details`
- UPDATE: routing DI (`admin\Site`) rozszerzony o modul `ShopClients`
- UPDATE: menu admin przepiete na kanoniczny URL `/admin/shop_clients/list/`
- UPDATE: widoki `shop-clients/view-list` i `shop-clients/clients-details` przepiete na `components/table-list`
- CLEANUP: usuniete legacy `autoload/admin/controls/class.ShopClients.php`, `autoload/admin/factory/class.ShopClients.php`
- TEST:
- NOWE: `tests/Unit/admin/Controllers/ShopProductControllerTest.php`
- NOWE: `tests/Unit/Domain/Client/ClientRepositoryTest.php`, `tests/Unit/admin/Controllers/ShopClientsControllerTest.php`
- UPDATE: `tests/Unit/Domain/Product/ProductRepositoryTest.php` (nowe przypadki dla mass_edit)
- UPDATE: `tests/bootstrap.php` (stub `S::normalize_decimal()`)
- Testy: **OK (361 tests, 1125 assertions)**
---
## ver. 0.273 (2026-02-15) - ShopProducer
- **ShopProducer** - migracja `/admin/shop_producer` na Domain + DI + nowe widoki
- NOWE: `Domain\Producer\ProducerRepository` (`listForAdmin`, `find`, `save`, `delete`, `allProducers`, `findForFrontend`, `producerProducts`, `allActiveIds`)
- NOWE: `admin\Controllers\ShopProducerController` (DI) z akcjami `list`, `edit`, `save`, `delete`
- UPDATE: modul `/admin/shop_producer/*` dziala na `components/table-list` i `components/form-edit` z zakladkami jezykowymi (Opis + SEO)
- UPDATE: routing i menu admin na kanoniczny URL `/admin/shop_producer/list/`
- UPDATE: `shop\Producer` przepiety na fasade do `Domain\Producer\ProducerRepository`
- UPDATE: `admin\factory\ShopProduct` - 2 wywolania `admin\factory\ShopTransport` przepiete na `Domain\Transport\TransportRepository`
- UPDATE: `admin\controls\ShopProduct` - usuniety fallback do `admin\factory\Layouts`
- CLEANUP: usuniete legacy `autoload/admin/controls/class.ShopProducer.php`, `admin/templates/shop-producer/list.php`, `admin/templates/shop-producer/edit.php`
- CLEANUP: usuniete 6 pustych factory facades: `admin\factory\Languages`, `admin\factory\Newsletter`, `admin\factory\Scontainers`, `admin\factory\ShopProducer`, `admin\factory\ShopTransport`, `admin\factory\Layouts`
- TEST: dodane `tests/Unit/Domain/Producer/ProducerRepositoryTest.php` i `tests/Unit/admin/Controllers/ShopProducerControllerTest.php`
- Testy: **OK (338 tests, 1063 assertions)**
---
## ver. 0.272 (2026-02-15) - ShopProductSets
- **ShopProductSets** - migracja `/admin/shop_product_sets` na Domain + DI + nowe widoki
- NOWE: `Domain\ProductSet\ProductSetRepository` (`listForAdmin`, `find`, `save`, `delete`, `allSets`, `allProductsMap`)
- NOWE: `admin\Controllers\ShopProductSetsController` (DI) z akcjami `list`, `edit`, `save`, `delete`
- UPDATE: modul `/admin/shop_product_sets/*` dziala na `components/table-list` i `components/form-edit` + Selectize multi-select produktow
- UPDATE: routing i menu admin na kanoniczny URL `/admin/shop_product_sets/list/`
- UPDATE: `shop\ProductSet` przepiety na fasade do `Domain\ProductSet\ProductSetRepository`
- CLEANUP: usuniete legacy `autoload/admin/controls/class.ShopProductSets.php`, `autoload/admin/factory/class.ShopProductSet.php`, `admin/templates/shop-product-sets/view-list.php`, `admin/templates/shop-product-sets/set-edit.php`
- TEST: dodane `tests/Unit/Domain/ProductSet/ProductSetRepositoryTest.php` i `tests/Unit/admin/Controllers/ShopProductSetsControllerTest.php`
- Testy: **OK (324 tests, 1000 assertions)**
---
## ver. 0.271 (2026-02-14) - ShopAttribute
- **ShopAttribute** - migracja `/admin/shop_attribute` na Domain + DI + nowe widoki
- NOWE: `Domain\Attribute\AttributeRepository` (`listForAdmin`, `findAttribute`, `saveAttribute`, `deleteAttribute`, `findValues`, `saveValues`, `saveLegacyValues`, `valueDetails`)
- NOWE: `admin\Controllers\ShopAttributeController` (DI) z akcjami `list`, `edit`, `save`, `delete`, `values`, `values_save`, `value_row_tpl`
- UPDATE: modul `/admin/shop_attribute/*` dziala na `components/table-list` i `components/form-edit`
- UPDATE: nowy edytor wartosci cechy (`values-edit`) z walidacja serwerowa i stabilnym `row_key` (bez indeksow do wyboru domyslnej wartosci)
- UPDATE: routing i menu admin na kanoniczny URL `/admin/shop_attribute/list/` (bez aliasow legacy)
- UPDATE: przepiecie zaleznosci kombinacji produktu (`admin\controls\ShopProduct`, `admin\factory\ShopProduct`, `admin/templates/shop-product/product-combination.php`) na `Domain\Attribute\AttributeRepository` i `shop\ProductAttribute`
- CLEANUP: usuniete legacy `autoload/admin/controls/class.ShopAttribute.php`, `autoload/admin/factory/class.ShopAttribute.php`, `autoload/admin/view/class.ShopAttribute.php`, `admin/templates/shop-attribute/_partials/value.php`
- TEST: dodane `tests/Unit/Domain/Attribute/AttributeRepositoryTest.php` i `tests/Unit/admin/Controllers/ShopAttributeControllerTest.php`
- Testy: **OK (312 tests, 948 assertions)**
---
## ver. 0.270 (2026-02-14) - Apilo payment/status sync hardening
- **Shop/Order + Apilo** - utwardzenie synchronizacji platnosci i statusow zamowien
- FIX: `shop\Order::set_as_paid()` wysyla do Apilo mapowany typ platnosci (`payment_method_id` -> `apilo_payment_type_id`) zamiast stalego `type = 1`
- NOWE: retry queue dla chwilowej niedostepnosci Apilo (`temp/apilo-sync-queue.json`) dla sync platnosci i statusu
- NOWE: `shop\Order::process_apilo_sync_queue()` przetwarza zalegle syncy
- UPDATE: `cron.php` uruchamia przetwarzanie kolejki sync Apilo przy aktywnej integracji
- UPDATE: rozszerzone logowanie debug (`logs/apilo.txt`) o HTTP code i bledy cURL dla sync platnosci/statusu
- Testy: **OK (300 tests, 895 assertions)**
---
## ver. 0.269 (2026-02-14) - ShopTransport
- **ShopTransport** - migracja `/admin/shop_transport` na Domain + DI + nowe widoki
- NOWE: `Domain\Transport\TransportRepository` (`listForAdmin`, `find`, `save`, `allActive`, `allForAdmin`, `findActiveById`, `getTransportCost`, `lowestTransportPrice`, `getApiloCarrierAccountId`)
- NOWE: `admin\Controllers\ShopTransportController` (DI) z akcjami `list`, `edit`, `save`
- NOWE: widoki `shop-transport/transports-list.php` i `shop-transport/transport-edit.php` + `transport-edit-custom-script.php`
- UPDATE: routing i menu admin na kanoniczny URL `/admin/shop_transport/list/`
- UPDATE: `admin\factory\ShopTransport`, `front\factory\ShopTransport` przepiete na nowe repozytorium
- FIX: `save()` return type `?int` zamiast `int|bool` (spojnosc z PaymentMethod)
- FIX: `toSwitchValue()` helper zamiast `=== 'on'` (obsluga '1', 'on', 'true', 'yes')
- FIX: `\S::delete_dir()` przeniesione z repozytorium do kontrolera (DDD)
- FIX: Medoo `select()` syntax - ORDER w WHERE zamiast 4-arg form
- CLEANUP: usuniete legacy `autoload/admin/controls/class.ShopTransport.php`, `autoload/admin/view/class.ShopTransport.php`, `admin/templates/shop-transport/view-list.php`
- FIX: `transports-list.php` - zmienna `'viewModel'` zmieniona na `'list'` (zgodnie z `table-list.php` komponentem)
- Testy: **OK (300 tests, 895 assertions)**
---
## ver. 0.268 (2026-02-14) - ShopPaymentMethod + Apilo token keepalive
- **ShopPaymentMethod** - migracja `/admin/shop_payment_method` na Domain + DI + nowe widoki
- NOWE: `Domain\PaymentMethod\PaymentMethodRepository` (`listForAdmin`, `find`, `save`, `allActive`, `allForAdmin`, `findActiveById`, `isActive`, `getApiloPaymentTypeId`, `forTransport`)
- NOWE: `admin\Controllers\ShopPaymentMethodController` (DI) z akcjami `list`, `edit`, `save`
- NOWE: widoki `shop-payment-method/payment-methods-list.php` i `shop-payment-method/payment-method-edit.php`
- UPDATE: routing i menu admin na kanoniczny URL `/admin/shop_payment_method/list/`
- UPDATE: `admin\controls\ShopTransport`, `front\factory\ShopPaymentMethod`, `shop\PaymentMethod` przepiete na nowe repozytorium
- CLEANUP: usuniete legacy `autoload/admin/controls/class.ShopPaymentMethod.php`, `autoload/admin/factory/class.ShopPaymentMethod.php`, `autoload/admin/view/class.ShopPaymentMethod.php`, `admin/templates/shop-payment-method/view-list.php`
- **Integrations/Apilo** - stabilizacja tokenu i lepszy feedback
- NOWE: automatyczne odswiezanie tokenu Apilo przed wygasnieciem (`apiloKeepalive`, refresh lead time)
- UPDATE: cron uruchamia keepalive i odswieza konfiguracje Apilo
- UPDATE: bardziej szczegolowe komunikaty bledow dla przyciskow integracji Apilo (co zrobic dalej)
- Testy: **OK (280 tests, 828 assertions)**
---
## ver. 0.267 (2026-02-14) - ShopStatuses
- **ShopStatuses** - migracja `/admin/shop_statuses` na Domain + DI + nowe widoki
- NOWE: `Domain\ShopStatus\ShopStatusRepository` (`listForAdmin`, `find`, `save`, `getApiloStatusId`, `getByIntegrationStatusId`, `allStatuses`)
- NOWE: `admin\Controllers\ShopStatusesController` (DI) z akcjami `list`, `edit`, `save` (bez aliasow legacy)
- NOWE: typ pola `FormFieldType::COLOR` + `FormField::color()` + `FormFieldRenderer::renderColor()` (color picker HTML5 zsynchronizowany z polem tekstowym)
- UPDATE: modul `/admin/shop_statuses/*` dziala na `components/table-list` i `components/form-edit`
- UPDATE: `front\factory\ShopStatuses` jako fasada delegujaca do `Domain\ShopStatus\ShopStatusRepository`
- UPDATE: menu admin przepiete na kanoniczny URL `/admin/shop_statuses/list/`
- CLEANUP: usuniete legacy `autoload/admin/controls/class.ShopStatuses.php`, `autoload/admin/factory/class.ShopStatuses.php`
- UWAGA: statusy maja ID od 0 - kluczowe dla walidacji (find/save uzywaja `$id < 0`)
- Testy: **OK (254 tests, 736 assertions)**
---
## ver. 0.266 (2026-02-13) - ShopCoupon
- **ShopCoupon** - migracja `/admin/shop_coupon` na Domain + DI + nowe widoki
- NOWE: `Domain\Coupon\CouponRepository` (`listForAdmin`, `find`, `save`, `delete`, `categoriesTree`)
- NOWE: `admin\Controllers\ShopCouponController` (DI) z akcjami `list`, `edit`, `save`, `delete`
- UPDATE: kompatybilnosc aliasow legacy (`view_list`, `coupon_edit`, `coupon_save`, `coupon_delete`) obslugiwana przez nowy kontroler
- UPDATE: modul `/admin/shop_coupon/*` dziala na `components/table-list` i `components/form-edit`
- NOWE: widoki/partiale `shop-coupon/coupons-list`, `shop-coupon/coupon-edit-new`, `shop-coupon/coupon-categories-selector`, `shop-coupon/coupon-categories-tree`, `shop-coupon/coupon-edit-custom-script`
- CLEANUP: usuniete legacy `autoload/admin/controls/class.ShopCoupon.php`, `autoload/admin/factory/class.ShopCoupon.php`, `admin/templates/shop-coupon/view-list.php`, `admin/templates/shop-coupon/coupon-edit.php`
- UPDATE: menu admin przepiete na kanoniczny URL `/admin/shop_coupon/list/`
- FIX: ujednolicone UI drzewek i checkboxow miedzy kuponami i layoutami
- Testy: **OK (235 tests, 682 assertions)**
---
## ver. 0.265 (2026-02-13) - ShopPromotion poprawki
- **ShopPromotion** - stabilizacja po migracji
- UPDATE: dodane `date_from` w `Domain\Promotion\PromotionRepository` (save/find/list/sort)
- UPDATE: `admin\Controllers\ShopPromotionController` rozszerzony o pole `Data od` na formularzu i kolumne `Data od` na liscie
- UPDATE: `shop\Promotion::get_active_promotions()` filtruje aktywnosc po `date_from` i `date_to`
- FIX: zapis edycji promocji nie tworzy nowego rekordu (hidden `id` + fallback `id` z URL)
- TEST: rozszerzono `PromotionRepositoryTest` o asercje `date_from`
- Testy: **OK (222 tests, 614 assertions)**
---
## ver. 0.264 (2026-02-13) - ShopPromotion
- **ShopPromotion** - migracja `/admin/shop_promotion` na Domain + DI + nowe widoki
- NOWE: `Domain\Promotion\PromotionRepository` (`listForAdmin`, `find`, `save`, `delete`, `categoriesTree`, invalidacja cache aktywnych promocji)
- NOWE: `admin\Controllers\ShopPromotionController` (DI) z akcjami `list`, `edit`, `save`, `delete`
- UPDATE: routing DI (`admin\Site`) rozszerzony o modul `ShopPromotion`
- UPDATE: modul `/admin/shop_promotion/*` dziala na `components/table-list` i `components/form-edit`
- NOWE: widoki/partiale `shop-promotion/promotions-list`, `shop-promotion/promotion-edit`, `shop-promotion/promotion-categories-selector`, `shop-promotion/promotion-categories-tree`, `shop-promotion/promotion-edit-custom-script`
- CLEANUP: usuniete legacy `autoload/admin/controls/class.ShopPromotion.php`, `autoload/admin/factory/class.ShopPromotion.php`, `admin/templates/shop-promotion/view-list.php`
- UPDATE: menu admin przepiete na kanoniczny URL `/admin/shop_promotion/list/`
- Testy: **OK (222 tests, 609 assertions)**
---
## ver. 0.263 (2026-02-13) - Integrations + cleanup Sellasist/Baselinker
- NOWE: `Domain\Integrations\IntegrationsRepository` (settings Apilo/ShopPRO, OAuth, product linking, API fetch)
- NOWE: `admin\Controllers\IntegrationsController` (DI) dla akcji Apilo i ShopPRO
- UPDATE: `admin\factory\Integrations` jako fasada delegujaca do repozytorium
- **CLEANUP: usunieto integracje Sellasist i Baselinker z calego projektu:**
- Usuniete klasy: `admin\controls\Integrations`, `admin\controls\Baselinker`, `admin\factory\Baselinker`, `front\factory\Shop`, `shop\ShopStatus`
- Usuniete szablony: `integrations/sellasist-settings.php`, `integrations/baselinker-settings.php`, `admin/templates/baselinker/`
- Wyczyszczone referencje w: `cron.php`, `cron/cron-xml.php`, `shop\Order`, kontrolery/factory/front Shop*
- Testy: **OK (212 tests, 577 assertions)**
---
## ver. 0.262 (2026-02-13) - Pages
- NOWE: `Domain\Pages\PagesRepository` (CRUD menu/stron, drzewo stron, sortowanie, SEO)
- NOWE: `admin\Controllers\PagesController` (DI) dla akcji menu/page/AJAX
- UPDATE: widoki `admin/templates/pages/*` przepiete na dane z kontrolera/repozytorium
- UPDATE: endpointy AJAX przepiete z `admin/ajax.php?a=*` na `/admin/pages/*`
- CLEANUP: usuniete legacy `controls/Pages`, `view/Pages`, `factory/Pages`, `ajax/pages.php`
- Testy: **OK (186 tests, 478 assertions)**
---
## ver. 0.261 (2026-02-13) - Articles (dalsza refaktoryzacja)
- UPDATE: `Domain\Article\ArticleRepository` rozszerzone o metody UI/admin i `saveFilesOrder()`
- UPDATE: `admin\Controllers\ArticlesController` obsluguje AJAX: `article_image_alt_change`, `article_file_name_change`, `article_image_delete`, `article_file_delete`, `filesOrderSave`
- UPDATE: lista artykulow nie korzysta juz z `admin\factory\Articles::article_pages()`
- UPDATE: widok edycji przepiety z `/admin/ajax.php` na `/admin/articles/*`
- UPDATE: drag&drop sortowania listy zalacznikow
- CLEANUP: usuniete `autoload/admin/view/class.Articles.php` i `admin/ajax/articles.php`
- Testy: **OK (178 tests, 443 assertions)**
---
## ver. 0.260 (2026-02-12) - ArticlesArchive
- NOWE: `admin\Controllers\ArticlesArchiveController` (DI)
- UPDATE: `Domain\Article\ArticleRepository` rozszerzone o `listArchivedForAdmin()`, `restore()`, `deletePermanently()`
- UPDATE: `/admin/articles_archive/view_list/` migrowane na `components/table-list`
- CLEANUP: usuniete legacy `controls/ArticlesArchive`, `factory/ArticlesArchive`, `view/ArticlesArchive`
- Testy: **OK (165 tests, 424 assertions)**
---
## ver. 0.259 (2026-02-12) - Scontainers
- NOWE: `Domain\Scontainers\ScontainersRepository` (listForAdmin, find, save, delete, detailsForLanguage)
- NOWE: `admin\Controllers\ScontainersController` (DI)
- UPDATE: `/admin/scontainers/*` migrowane na `components/table-list` i `components/form-edit`
- UPDATE: `admin\factory\Scontainers` i `front\factory\Scontainers` jako fasady
- CLEANUP: usuniete `controls/Scontainers`, `view/Scontainers`
- Testy: **OK (158 tests, 397 assertions)**
---
## ver. 0.258 (2026-02-12) - Newsletter (stabilizacja)
- UPDATE: tymczasowo wylaczono flow `prepare/send/preview` (wymaga przebudowy)
- UPDATE: tymczasowo wylaczono modul `Szablony uzytkownika`
- UPDATE: aktywna obsluga tylko szablonow administracyjnych (`is_admin = 1`)
- CLEANUP: usuniete nieuzywane widoki `prepare.php`, `preview.php`, `email-templates-user.php`
---
## ver. 0.257 (2026-02-12) - Newsletter
- NOWE: `Domain\Newsletter\NewsletterRepository` (subskrybenci, szablony, ustawienia, kolejka wysylki)
- NOWE: `Domain\Newsletter\NewsletterPreviewRenderer` (render podgladu)
- NOWE: `admin\Controllers\NewsletterController` (DI)
- UPDATE: `/admin/newsletter/*` migrowane na `components/table-list` i `components/form-edit`
- UPDATE: `admin\factory\Newsletter` jako fasada; `front\factory\Newsletter` bez `admin\view\Newsletter`
- CLEANUP: usuniete `controls/Newsletter`, `view/Newsletter`
- Testy: **OK (150 tests, 372 assertions)**
---
## ver. 0.256 (2026-02-12) - Layouts
- NOWE: `Domain\Layouts\LayoutsRepository` (find, save, delete, listForAdmin, menusWithPages, categoriesTree)
- NOWE: `admin\Controllers\LayoutsController` (DI)
- UPDATE: lista `/admin/layouts/view_list/` migrowana na `components/table-list`
- UPDATE: widok `layouts/layout-edit` korzysta z danych z repozytorium
- NOWE: partial `admin/templates/layouts/subcategories-list.php`
- UPDATE: `Domain\Languages\LanguagesRepository::defaultLanguageId()` jako wspolna metoda
- UPDATE: `ArticlesController` korzysta z `LayoutsRepository` (DI)
- CLEANUP: usuniete `controls/Layouts`, `view/Layouts`; `factory/Layouts` jako fasada
- Testy: **OK (141 tests, 336 assertions)**
---
## ver. 0.255 (2026-02-12) - Languages DI cleanup
- UPDATE: SettingsController, BannerController, DictionariesController, ArticlesController pobieraja liste jezykow przez `Domain/Languages/LanguagesRepository` (DI)
- UPDATE: router DI przekazuje `LanguagesRepository` do kontrolerow
- UPDATE: legacy `admin/controls`, `admin/factory/Shop*` przepiete na `LanguagesRepository`
- FIX: `admin/factory/class.Languages.php` poprawione na `<?php` (short_open_tag=Off)
- Testy: **OK (130 tests, 303 assertions)**
---
## ver. 0.254 (2026-02-12) - Languages
- NOWE: `Domain\Languages\LanguagesRepository` (languages + translations CRUD/list)
- NOWE: `admin\Controllers\LanguagesController` (DI)
- UPDATE: widoki `languages/*` migrowane na `components/table-list` i `components/form-edit`
- CLEANUP: usuniete `controls/Languages`, `view/Languages`; `factory/Languages` jako fasada
- Testy: **OK (130 tests, 301 assertions)**
---
## ver. 0.253 (2026-02-12) - Users
- NOWE: `Domain\User\UserRepository` (CRUD, logon, 2FA, checkLogin)
- NOWE: `admin\Controllers\UsersController` (DI: view_list, user_edit, user_save, user_delete, login_form, twofa)
- UPDATE: `admin\factory\Users` jako fasada; `admin/ajax/users.php` oparty o `UserRepository`
- UPDATE: widoki users migrowane na `components/table-list` i `components/form-edit`
- UPDATE: walidacja warunkowa: `twofa_email` wymagany gdy `twofa_enabled = 1`
- CLEANUP: usuniete `controls/Users`, `factory/Users`, `view/Users`
- Testy: **OK (119 tests, 256 assertions)**
---
## ver. 0.252 (2026-02-10) - ProductArchive + Filemanager
- UPDATE: `ProductArchiveController` i szablony przepiete na `components/table-list`
- UPDATE: CSS/JS dla list wydzielone do osobnych widokĂłw `*-custom-script.php`
- NOWE: `admin\Controllers\FilemanagerController` - przepieto filemanager na nowy routing
- FIX: naprawiono bĹÄ…d `Invalid Key` w filemanagerze
- CLEANUP: usunieto legacy `controls/Archive`, `controls/Filemanager`, `view/FileManager`, stare szablony
- RENAME: `admin/templates/product_archive/` → `admin/templates/product-archive/`
- Testy: **OK (82 tests, 181 assertions)**
---
## ver. 0.251 (2026-02-09) - Dictionaries
- NOWE: `Domain\Dictionaries\DictionariesRepository` (listForAdmin, find, save, delete, allUnits)
- NOWE: `admin\Controllers\DictionariesController` (lista + formularz na nowych komponentach)
- UPDATE: migracja sĹownikĂłw na `components/table-list` i `components/form-edit`
- FIX: obsĹuga `lang_id` jako string (`pl`, `en`) w zapisie tĹumaczeĹ„
- CLEANUP: usunięto legacy klasy Dictionaries (`admin\controls`, `admin\factory`, `front\factory`)
- Testy: **OK (82 tests, 181 assertions)**
---
## ver. 0.250 (2026-02-09) - Settings (refaktoryzacja)
- UPDATE: `Domain\Settings\SettingsRepository` ma bezpoĹredni dostÄ™p do DB (bez delegacji do `admin\factory\Settings`)
- UPDATE: przepięto użycia `admin\factory\Settings` na `Domain\Settings\SettingsRepository`
- CLEANUP: usunięto legacy klasy Settings (`factory`, `controls`, `view`)
- Testy: **OK (82 tests, 181 assertions)**
---
## ver. 0.246 (2026-02-07) - Articles legacy cleanup
- CLEANUP: usunięto `autoload/admin/controls/class.Articles.php`
- UPDATE: `admin\Controllers\ArticlesController::galleryOrderSave()` uzywa `ArticleRepository::saveGalleryOrder()`
- FIX: sortowanie list admin po reloadzie - `RewriteRule` dla `/admin/...` ma `QSA`
- FIX: generator `\S::htacces()` komentuje dyrektywy `AddHandler|SetHandler|ForceType`
- Testy: **OK (65 tests, 131 assertions)**
---
## ver. 0.245 (2026-02-06) - Articles::archive
- UPDATE: `Domain\Article\ArticleRepository` - dodano `archive()` (status = -1)
- UPDATE: `admin\Controllers\ArticlesController` - nowa akcja `delete()` z DI
- UPDATE: `admin\factory\Articles::articles_set_archive()` deleguje do repozytorium
- Testy: **OK (59 tests, 123 assertions)**
---
## ver. 0.244 (2026-02-06) - Articles::save
- UPDATE: `Domain\Article\ArticleRepository` - dodano `save()` z prywatnych helperow
- UPDATE: `admin\Controllers\ArticlesController` - nowa akcja `save()` z DI
- UPDATE: `tests/bootstrap.php` - dodano stub `S::seo()`
- Testy: **OK (57 tests, 119 assertions)**
---
## ver. 0.243 (2026-02-06) - Articles cleanup
- UPDATE: `Domain\Article\ArticleRepository` - dodano `deleteNonassignedImages()` i `deleteNonassignedFiles()`
- UPDATE: `admin\Controllers\ArticlesController::edit()` uses repository cleanup methods
- Testy: **OK (50 tests, 95 assertions)**
---
## ver. 0.242 (2026-02-06) - Articles::edit
- NOWE: `Domain\Article\ArticleRepository` (find: artykuĹ + relacje)
- UPDATE: `admin\Controllers\ArticlesController` - konstruktor DI + `edit()` uĹĽywa repozytorium
- UPDATE: `admin\factory\Articles::article_details()` deleguje do repozytorium
- Testy: **OK (48 tests, 91 assertions)**
---
## ver. 0.241 (2026-02-06) - ProductArchive
- NOWE: `admin\Controllers\ProductArchiveController` (DI)
- NOWE: `ProductRepository::archive()`, `unarchive()`
- RENAME: `admin/templates/archive/` → `admin/templates/product_archive/`
- FIX: SQL w `ajax_products_list_archive()` (puste wyszukiwanie + brak `archive = 1`)
- Testy: **OK (50 tests, 95 assertions)**
---
## ver. 0.240 (2026-02-05) - Settings + Cache
- NOWE: `Domain\Settings\SettingsRepository` (fasada → factory)
- NOWE: `Domain\Cache\CacheRepository` (dirs + Redis)
- NOWE: `admin\Controllers\SettingsController` (DI: clearCache, save, view)
- FIX: BrakujÄ…cy `id="content"` w main-layout.php
- FIX: `persist_edit = true` w settings.php
- Testy: **OK (29 tests, 60 assertions)**
---
## ver. 0.239 (2026-02-05) - Banner + Product
- NOWE: `Domain\Banner\BannerRepository` (find, delete, save)
- NOWE: `admin\Controllers\BannerController` - pierwszy kontroler z DI
- NOWE: Router z mapÄ… `$newControllers` + fallback na stare kontrolery
- NOWE: Autoloader PSR-4 fallback w 9 entry pointach
- Zmigrowano: `get_product_price()` → `ProductRepository::getPrice()`
- Zmigrowano: `get_product_name()` → `ProductRepository::getName()`
- Testy: **OK (15 tests, 31 assertions)**
---
## ver. 0.238 (2025-02-05) - Product Repository
- NOWE: `Domain\Product\ProductRepository` - pierwsza klasa w nowej architekturze
- NOWE: Dependency Injection zamiast `global $mdb`
- NOWE: Testy jednostkowe (5 testĂłw, 100% pokrycie)
- Zmigrowano: `get_product_quantity()` → `ProductRepository::getQuantity()`
- KompatybilnoĹć: Stara klasa `shop\Product` dziaĹa jako fasada
---
## ver. 0.237 (2025-02-05) - System cache produktĂłw
- Automatyczne czyszczenie cache produktu po aktualizacji przez CRON
- AJAX dla przycisku "WyczyĹć cache" w panelu admin
- Metody `delete()` i `deletePattern()` w CacheHandler
- Metoda `clear_product_cache()` w klasie S
---
*Dokument aktualizowany: 2026-02-17 (ver. 0.293)*