# Tech Changelog ## 2026-04-03 (Phase 64 - Receipt Datetime Precision, Plan 01) - Migracja `20260403_000076_alter_receipts_issue_date_to_datetime.sql`: ALTER TABLE receipts MODIFY issue_date DATETIME NOT NULL. - `ReceiptController::store()`: zapis issue_date jako Y-m-d H:i:s, konwersja z formatu datetime-local (T separator). - `AccountingController::toTableRow()` i `export()`: formatowanie issue_date do YYYY-MM-DD HH:MM (substr 0-16). - `receipt-create.php`: input type=datetime-local z domyslna biezaca data+czas. - `receipts/show.php`, `receipts/print.php`, `orders/show.php`: wyswietlanie issue_date z godzina (substr 0-16). ## 2026-04-01 (Phase 63 - Order Item Personalization, Plan 01) - Migracja `20260401_000075_add_personalization_to_order_items.sql`: kolumna `personalization TEXT NULL` w `order_items`. - `ShopproOrderMapper::extractPersonalization()`: ekstrakcja `custom_fields` z odpowiedzi shopPRO API, konwersja HTML na czysty tekst (strip_tags, html_entity_decode, br->newline). - `ShopproOrderMapper::mapItems()`: dodanie klucza `personalization` do mapowanego itemu. - `OrderImportRepository::replaceItems()`: zapis `personalization` do INSERT query. - `resources/views/orders/show.php`: warunkowe wyswietlanie personalizacji pod nazwa produktu (div.item-personalization z etykieta i liniami tekstu, escape via `e()`). - `resources/scss/app.scss`: style `.item-personalization` (kompaktowy blok, border-left, muted colors). ## 2026-03-31 (Phase 60 - Order Status Aged Event, Plan 01) - Migracja `20260331_000074_seed_order_status_aged_cron.sql`: seed cron schedule `order_status_aged` co 3600s. - `OrderStatusAgedService`: skanuje zamowienia w danym statusie od X dni (query HAVING MAX(changed_at) na `order_status_history`), limit 100/regule, trigger `order.status_aged`. - `OrderStatusAgedHandler`: cron handler delegujacy do `OrderStatusAgedService::scan()`. - `CronHandlerFactory`: rejestracja handlera `order_status_aged`. - `AutomationController`: dodano `order.status_aged` do `ALLOWED_EVENTS`, `days_in_status` do `ALLOWED_CONDITION_TYPES`, `parseConditionValue` + branch `days_in_status`. - `AutomationService::evaluateDaysInStatusCondition()`: ewaluacja `context.days_in_status >= value.days`. - `resources/views/automation/form.php`: opcja "Minelo X dni od zmiany statusu" w zdarzeniach, "Liczba dni w statusie" w warunkach z polem numerycznym. - `public/assets/js/modules/automation-form.js`: `buildDaysInStatusInput()`, opcja `days_in_status` w `addCondition()` i `onConditionTypeChange()`. ## 2026-03-31 (Phase 59 - Order Status Automation Event, Plan 01) - `AutomationController`: dodano `order.status_changed` do `ALLOWED_EVENTS`, `order_status` do `ALLOWED_CONDITION_TYPES`, `parseConditionValue` + branch `order_status` (walidacja kodow vs DB), `buildRuleFromRequest` + branch `order_status`. - `AutomationService::evaluateOrderStatusCondition()`: ewaluacja warunku order_status — porownanie `new_status` z context vs dozwolone `order_status_codes`. - `AutomationService::handleUpdateOrderStatus()`: pobiera old_status przed zmiana, emituje chain event `order.status_changed` przez `emitEvent()` (tylko przy realnej zmianie statusu). - `OrdersController::changeStatus()`: pobiera old_status przed `updateOrderStatus()`, emisja `order.status_changed` po udanej zmianie (try/catch, non-blocking). - `resources/views/automation/form.php`: opcja "Zmiana statusu zamowienia" w zdarzeniach, "Status zamowienia" w warunkach z checkboxami aktywnych statusow. - `resources/views/automation/index.php`: dodano etykiety `payment.status_changed` i `order.status_changed` do `$eventLabels`. - `public/assets/js/modules/automation-form.js`: `buildOrderStatusCheckboxes()`, opcja `order_status` w `addCondition()` i `onConditionTypeChange()`. ## 2026-03-30 (Phase 58 - Automation Form Preserve, Plan 01) - `AutomationController::store()` i `update()`: re-render formularza z danymi z request zamiast redirect przy bledzie walidacji. - `AutomationController::buildRuleFromRequest()`: buduje tablice `$rule` z POST data w formacie oczekiwanym przez `form.php`. - `AutomationController::renderForm()`: nowy parametr `$errorMessage` — priorytet nad Flash. - `resources/views/automation/form.php`: `$isEdit` sprawdza `isset($rule['id'])` (nie `$rule !== null`), conditions/actions wyciagane z `$rule` niezaleznie od `$isEdit`. ## 2026-03-30 (Phase 57 - Payment Automation Event, Plan 01) - `AutomationController`: dodano `payment.status_changed` do `ALLOWED_EVENTS`, `payment_status` do `ALLOWED_CONDITION_TYPES`, stala `PAYMENT_STATUS_OPTIONS` (0/1/2), parseConditions dla `payment_status_keys`. - `AutomationService::evaluatePaymentStatusCondition()`: ewaluacja warunku payment_status — porownanie `new_payment_status` z context vs dozwolone status_keys. - `OrdersController::addPayment()`: emisja `payment.status_changed` po zapisie platnosci (try/catch, non-blocking). - `ShopproPaymentStatusSyncService::syncSingleOrderPayment()`: emisja `payment.status_changed` po zmianie payment_status w cron sync (tylko przy realnej zmianie). - `CronHandlerFactory`: przeniesiono tworzenie `$automationService` przed `$shopproPaymentSyncService`, przekazano jako zależność. - `resources/views/automation/form.php`: opcja "Zmiana statusu platnosci" w zdarzeniach, "Status platnosci" w warunkach z checkboxami (Nieoplacone/Czesciowo oplacone/Oplacone). - `public/assets/js/modules/automation-form.js`: `buildPaymentStatusCheckboxes()`, opcja w `addCondition()` i `onConditionTypeChange()`. ## 2026-03-30 (Phase 56 - Order Payments, Plan 01) - Migracja `20260330_000073_create_order_payments_table.sql`: tabela `order_payments` (id, order_id, source_payment_id, external_payment_id, payment_type_id, payment_date, amount, currency, comment, payload_json) + idempotentne dodanie kolumn `total_with_tax`, `total_paid`, `external_payment_type_id` do `orders`. - `OrdersRepository::addPayment()`: INSERT do `order_payments`, przeliczenie `total_paid` i `payment_status` na `orders`. - `OrdersRepository::findOrderSourceInfo()`: pobiera `source`, `integration_id`, `source_order_id` dla push do shopPRO. - `OrdersController::addPayment()`: POST `/orders/{id}/payment/add` — walidacja, zapis platnosci, activity log, push do shopPRO. - `OrdersController::pushPaymentToShoppro()`: po dodaniu platnosci pokrywajacej calosc — PUT `set_paid` do shopPRO API. - `ShopproApiClient::setOrderPaid()`: PUT `/api.php?endpoint=orders&action=set_paid&id={sourceOrderId}`. - `resources/views/orders/show.php`: przycisk "Dodaj platnosc", formularz inline (kwota, typ, data, komentarz), AJAX submit z reload. - Style: `.payment-add-form` w `resources/scss/app.scss`. ## 2026-03-28 (Phase 51 - Email HTML Layout, Plan 01) - Migracja `20260328_000001_add_html_layout_to_email_mailboxes.sql`: kolumny `header_html` TEXT NULL i `footer_html` TEXT NULL w `email_mailboxes`. - `EmailMailboxRepository::save()`: zapis `header_html`/`footer_html` w INSERT i UPDATE. - `EmailMailboxController::save()`: pobiera `header_html`/`footer_html` z POST i przekazuje do repozytorium. - `resources/views/settings/email-mailboxes.php`: sekcja "Szablon wiadomosci" z dwoma edytorami Quill.js (email-safe toolbar: bold, italic, underline, kolor, wyrownanie, listy, link, image, naglowki h1-h3). Sync innerHTML do hidden inputs przy submit. - `EmailSendingService`: nowa metoda `composeBody()` — sklada header + body + footer. Uzywa variableResolver na header/footer. Uzyta w `send()` i `preview()`. NULL/pusty header/footer = pomijany. ## 2026-03-28 (Phase 50 - Allegro Shipment Waybill Push, Plan 01) - `AllegroShipmentService`: - po sukcesie `checkCreationStatus(...)` (gdy jest `tracking_number`) probuje dopiac przesylke do checkout form Allegro, - wykorzystuje `AllegroApiClient::addShipmentToOrder(...)` (`POST /order/checkout-forms/{id}/shipments`), - push wykonywany tylko dla zamowien `orders.source='allegro'` i niepustego `source_order_id`, - retry pushu po `ALLEGRO_HTTP_401` z ponownym `tokenManager->resolveToken()`, - bledy pushu traktowane jako niekrytyczne (lokalna paczka pozostaje utworzona). - `AllegroShipmentService::downloadLabel(...)`: - przy fallbackowym dopelnieniu trackingu (gdy brak numeru w rekordzie paczki) wykonuje ten sam warunkowy push waybilla do Allegro. - Testy: - dodano `tests/Unit/AllegroShipmentServiceTest.php` (scenariusze: push dla Allegro, brak pushu dla nie-Allegro, fallback przy bledzie API, retry po 401). ## 2026-03-28 (Public HTTPS cron endpoint) - Dodano publiczny endpoint triggera crona: - `GET /cron?token=` - dodatkowo kompatybilny wariant sciezki: `GET /cron/token=`. - Token jest walidowany przez `hash_equals` i pochodzi z nowej zmiennej srodowiskowej `CRON_PUBLIC_TOKEN`. - Endpoint uruchamia `CronRunner` z limitem z ustawienia `cron_web_limit`. - `Application::maybeRunCronOnWeb(...)` ignoruje teraz sciezki `/cron` i `/cron/*`, aby uniknac podwojnego triggera. - Zaktualizowano `.env.example` o `CRON_PUBLIC_TOKEN`. ## 2026-03-28 (Hotfix - Apaczka pickup day fallback) - `ApaczkaShipmentService`: - dodano automatyczny retry `order_send` dla bledu API `Pickup not available for selected day`, - rozszerzono detekcje bledu o wariant komunikatu: `We're sorry, you can't place an order today. Change its date to another working day.`, - fallback dotyczy tylko `pickup.type=COURIER`, - kazdy retry przesuwa `pickup.date` na kolejny dzien roboczy (`normalizeCourierPickupDate`) i ponawia wysylke, - limit fallbacku: do 7 kolejnych dni, potem zwracany jest oryginalny blad API. ## 2026-03-28 (Phase 49 - Automation History Tab, Plan 01 - rozszerzenie akcji) - Rozszerzono automatyzacje o nowy typ akcji `update_order_status` (UI: `Zmiana statusu zamowienia`). - `AutomationController`: - `ALLOWED_ACTION_TYPES` zawiera `update_order_status`, - waliduje i parsuje `order_status_code` tylko do aktywnych statusow z `order_statuses`. - `AutomationRepository`: - nowa metoda `listActiveOrderStatuses()` zwracajaca aktywne statusy (`code`, `name`) sortowane rosnaco po nazwie. - `resources/views/automation/form.php` i `public/assets/js/modules/automation-form.js`: - nowa opcja akcji z wyborem docelowego statusu zamowienia. - `AutomationService`: - nowy handler `handleUpdateOrderStatus(...)`, - wykonanie zmiany przez `OrdersRepository::updateOrderStatus(...)` z aktorem systemowym `Automatyzacja: `, - fallback log aktywnosci `automation_order_status_failed` gdy zmiana nie powiedzie sie. ## 2026-03-28 (Phase 49 - Automation History Tab, Plan 01) - `Ustawienia > Zadania automatyczne` (`/settings/automation`) rozdzielone na 2 taby: - `Ustawienia` - obecne zarzadzanie regulami, - `Historia` - log wykonan automatyzacji z filtrowaniem i paginacja. - Dodano migracje `20260328_000072_create_automation_execution_logs_table.sql`: - nowa tabela `automation_execution_logs` (event, regula, order, status, wynik, context, timestamp), - indeksy pod filtry historii, - seed harmonogramu crona `automation_history_cleanup` (co 24h). - Nowe klasy backend: - `AutomationExecutionLogRepository` - zapis/listowanie/paginacja/licznik historii + purge retencji, - `AutomationHistoryCleanupHandler` - usuwanie wpisow starszych niz konfigurowalna liczba dni (domyslnie 30). - `AutomationService::trigger(...)` zapisuje wpis historii per wykonana regula: - `success` po poprawnym wykonaniu akcji, - `failed` przy wyjatku w wykonaniu reguly. - `AutomationController::index(...)` obsluguje filtry historii (`history_*`) i paginacje (`history_page`), zachowujac kompatybilnosc listy regul. - UI historii wykorzystuje kompaktowy formularz filtrow i paginacje z zachowaniem aktywnych parametrow. ## 2026-03-28 (Phase 48 - Email Template Shipment Variables, Plan 01) - Email templates (`/settings/email-templates`): - dodano zmienne `{{przesylka.numer}}` i `{{przesylka.link_sledzenia}}` w `EmailTemplateController::VARIABLE_GROUPS`, - rozszerzono `SAMPLE_DATA` do preview o przykladowy numer i URL sledzenia. - `VariableResolver`: - otrzymuje zaleznosc `ShipmentPackageRepository`, - pobiera najnowsza paczke (`findLatestByOrderId(order_id)`), - mapuje `przesylka.numer` i `przesylka.link_sledzenia`, - link jest liczony przez `DeliveryStatus::trackingUrl(provider, tracking_number, carrier_id)`. - DI: - `routes/web.php` i `CronHandlerFactory` przekazuja `ShipmentPackageRepository` do `VariableResolver`. - Zachowanie brzegowe: - brak paczki lub brak numeru trackingowego nie psuje renderu - zmienne przesylki zwracaja pusty string. ## 2026-03-28 (Phase 47 - Shipment Creation Automation, Plan 01) - Automatyzacja: - dodano nowe zdarzenie `shipment.created` (UI: `Utworzenie przesylki`), - trigger jest uruchamiany natychmiast po sukcesie `ShipmentController::create()` oraz `ShipmentController::createManual()`, - kontekst triggera zawiera m.in. `package_id`, `provider`, `tracking_number`, `package_status`, `delivery_status`. - Dodano nowy typ akcji automatyzacji `update_shipment_status` (UI: `Zmiana statusu przesylki`): - walidacja konfiguracji przez `AutomationController` (`shipment_status_key` -> `status_key`), - wykonanie przez `AutomationService::handleUpdateShipmentStatus(...)`, - aktualizacja `delivery_status` tylko przy realnej zmianie (bez falszywych triggerow), - po zmianie statusu emitowane jest `shipment.status_changed` z kontekstem zmiany. - Rozszerzono `AutomationService` o zaleznosc `ShipmentPackageRepository`: - nowa metoda pomocnicza `ShipmentPackageRepository::findLatestByOrderId(int): ?array` (fallback wyboru paczki dla akcji). - UI automatyzacji (`resources/views/automation/form.php`, `resources/views/automation/index.php`, `public/assets/js/modules/automation-form.js`) rozszerzono o: - event `Utworzenie przesylki`, - akcje `Zmiana statusu przesylki` z wyborem docelowego statusu biznesowego. - Aktualizacja DI: - `routes/web.php` i `src/Modules/Cron/CronHandlerFactory.php` przekazuja `ShipmentPackageRepository` do `AutomationService`, - `ShipmentController` otrzymuje `AutomationService` jako zaleznosc konstruktora. ## 2026-03-28 (Allegro Status Push - orderPRO -> Allegro) - Zaimplementowano kierunek synchronizacji statusow `orderpro_to_allegro` w `AllegroStatusSyncService`. - `AllegroStatusSyncService`: - pobiera reczne zmiany statusow (`order_status_history.change_source=manual`) dla zamowien Allegro, - buduje reverse mapping (`orderpro_status_code -> allegro_status_code`) na podstawie `allegro_order_status_mappings`, - pushuje statusy do API Allegro i raportuje `pushed/skipped/failed`, - aktualizuje kursor `integration_order_sync_state.last_status_pushed_at`. - Nowa metoda `AllegroApiClient::updateCheckoutFormFulfillment()`: - PUT `/order/checkout-forms/{id}/fulfillment`, - body JSON: `{"status":""}`. - `AllegroOrderSyncStateRepository`: dodano obsluge kursora push (`getLastStatusPushedAt`, `updateLastStatusPushedAt`). - `AllegroStatusMappingRepository`: dodano reverse map builder `buildOrderproToAllegroMap()`. - UI `Ustawienia > Integracje > Allegro > Ustawienia`: - odblokowano wybor opcji `orderPRO -> Allegro` (usunieto `disabled` i dopisek `(wkrotce)`), - zaktualizowano opis hintu kierunku synchronizacji. - Dodano testy jednostkowe `tests/Unit/AllegroStatusSyncServiceTest.php` dla scenariuszy push/skip/fail/retry-401. ## 2026-03-27 (ShopPRO Status Push — orderPRO -> shopPRO) - Zaimplementowano kierunek synchronizacji statusow `orderpro_to_shoppro` w `ShopproStatusSyncService`. - Nowa metoda `ShopproApiClient::updateOrderStatus()` — PUT `/api.php?endpoint=orders&action=change_status&id={id}`. - Wydzielono wspolna metode `executeRequest()` w `ShopproApiClient` (reuse GET/PUT). - `ShopproStatusSyncService::syncPushDirection()`: - buduje reverse mapping (orderpro_status_code -> shoppro_status_code), - query `order_status_history` po zmianach `change_source=manual` po kursore, - pushuje status do shopPRO API, aktualizuje kursor `last_status_pushed_at`. - Migracja: `20260327_000071_add_last_status_pushed_at_to_sync_state.sql` — kolumna `last_status_pushed_at` w `integration_order_sync_state`. - `ShopproOrderSyncStateRepository`: nowe metody `getLastStatusPushedAt()`, `updateLastStatusPushedAt()`. - `CronHandlerFactory`: zaktualizowana kompozycja `ShopproStatusSyncService` z nowymi zaleznosciami. ## 2026-03-25 (Automation - new action "Wystaw paragon") - Dodano nowy typ akcji automatyzacji: `issue_receipt` (Wystaw paragon). - Konfiguracja akcji wymaga kompletu parametrow: - `receipt_config_id` (aktywna konfiguracja paragonu), - `issue_date_mode` (`today` / `order_date` / `payment_date`), - `duplicate_policy` (`skip_if_exists` / `allow_duplicates`). - `AutomationController`: - rozszerzono `ALLOWED_ACTION_TYPES`, - dodano walidacje i parsowanie configu akcji `issue_receipt`, - formularz dostaje aktywne konfiguracje paragonow i slowniki opcji. - `resources/views/automation/form.php` i `public/assets/js/modules/automation-form.js`: - nowa pozycja akcji `Wystaw paragon`, - dynamiczne pola dla parametrow akcji. - `AutomationService`: - wykonuje automatyczne wystawienie paragonu przez `ReceiptRepository`, - zapisuje activity log sukcesu/pominiecia/bledu, - ma ochrone przed petla dla eventu `receipt.created` (akcja `issue_receipt` jest pomijana i logowana), - obsluguje polityke duplikatow. - Aktualizacja DI: - `routes/web.php` i `CronHandlerFactory` przekazuja do `AutomationService` zaleznosci `ReceiptRepository` i `ReceiptConfigRepository`. - Dodano systemowy mechanizm chain automation dla obecnych i przyszlych zdarzen: - wspolny kontekst lancucha (`__automation_chain`) propagowany miedzy kolejnymi triggerami, - `emitEvent(...)` jako bezpieczny mechanizm emitowania kolejnych eventow z akcji, - ochrona anty-petla przez deduplikacje wykonania `event_type + rule_id` w jednym lancuchu, - limit glebokosci lancucha (`MAX_CHAIN_DEPTH`) i limit historii wykonan (`MAX_CHAIN_EXECUTIONS`). ## 2026-03-25 (Phase 43 - Print Queue Entry Removal, Plan 01) - Dodano usuwanie wpisu kolejki wydruku: - `PrintJobRepository::deleteById(int): bool`, - `PrintSettingsController::deleteJob(Request): Response`, - route `POST /settings/printing/jobs/delete` w `routes/web.php`. - Widok `resources/views/settings/printing.php` ma przycisk `Usun` dla wpisu kolejki z potwierdzeniem `window.OrderProAlerts.confirm(...)`. - Dodano style `print-queue-actions` i `print-queue-delete-form` w `resources/scss/modules/_printing.scss` + rebuild `public/assets/css/app.css`. ## 2026-03-25 (Phase 42 - Automation Shipment Status Event, Plan 01) - Rozszerzono automatyzacje o event `shipment.status_changed` i condition type `shipment_status`: - `AutomationController` dopuszcza nowe event/condition i waliduje `status_keys`, - `AutomationService::trigger()` przyjmuje kontekst triggera i ocenia warunki statusowe. - UI automatyzacji (`resources/views/automation/form.php`, `index.php`, `public/assets/js/modules/automation-form.js`) obsluguje: - wybor eventu `Zmiana statusu przesylki`, - nowy warunek biznesowy statusu przesylki: - zarejestrowana -> `created|confirmed`, - do odbioru -> `ready_for_pickup`, - nadana w punkcie -> `confirmed|in_transit`, - odebrana -> `delivered`, - anulowana -> `cancelled`, - nieodebrana -> `problem`, - odebrana (zwrot) -> `returned`. - `ShipmentTrackingHandler` triggeruje `AutomationService->trigger('shipment.status_changed', orderId, context)` tylko gdy `delivery_status` realnie sie zmieni. - `CronHandlerFactory` buduje `AutomationService` dla procesu cron i przekazuje go do `ShipmentTrackingHandler`; konstruktor dostaje teraz dodatkowo `basePath` (zmiany w `bin/cron.php` i `Application::maybeRunCronOnWeb`). ## 2026-03-25 (Phase 41 - Allegro Import Log Rationalization, Plan 01) - `AllegroOrderImportService::importSingleOrder(...)` przyjmuje trigger (`manual_import`, `orders_sync`, `status_sync`) i zapisuje go w `details_json` logu `import` (`trigger`, `trigger_label`, `source_updated_at`). - `AllegroOrdersSyncService` i `AllegroStatusSyncService` przekazuja jawny trigger do importu. - `OrdersRepository::shouldSkipDuplicateImportActivity(...)` deduplikuje kolejne wpisy `import`, gdy ostatni wpis ma ten sam `source_order_id`, `source_updated_at` i `trigger` (z wyjatkiem nowo utworzonego zamowienia). - Widok historii zamowienia (`resources/views/orders/show.php`) pokazuje przy wpisie `import` skondensowany kontekst triggera i `source_updated_at`. ## 2026-03-25 (Phase 40 - Remove Order List Bulk Print, Plan 01) - Usunieto akcje bulk print z listy zamowien: - OrdersController::index() nie wystawia juz przycisku Drukuj etykiety w header_actions. - resources/views/orders/list.php nie zawiera juz skryptu wysylajacego bulk request do drukarki. - Wycofano endpoint i logike backendowa bulk print: - usunieto trase POST /api/print/jobs/bulk z routes/web.php, - usunieto metode PrintApiController::bulkCreateJobs(...), - usunieto nieuzywana metode PrintJobRepository::findPackagesWithLabelsByOrderIds(...). - Pozostawiono bez zmian druk pojedynczej etykiety przez POST /api/print/jobs oraz API klienta Windows (pending/download/complete). ## 2026-03-25 (Phase 30 - Button Primary Color Distinction, Plan 01) - Rozdzielono palete kolorow naglowkow i przyciskow akcji w warstwie SCSS: - `resources/scss/shared/_ui-components.scss` ma nowe tokeny `--c-action-primary`, `--c-action-primary-dark`, `--focus-ring-action`, - `btn--primary` i `btn:focus-visible` korzystaja z tokenow akcji, bez zmiany koloru `section-title` opartego o `--c-primary`. - `resources/scss/modules/_printing.scss`: `btn--outline-primary` zostal przepiety z twardego niebieskiego (`#4a90d9`) na token `--c-action-primary`. - Przebudowano assety frontendowe komenda `npm run build:css`, co zaktualizowalo `public/assets/css/app.css`. ## 2026-03-23 (Phase 26 — Manual Tracking Number, Plan 01) - Nowa metoda `ShipmentPackageRepository::createManual(int, string, ?string): int` — INSERT do `shipment_packages` z `provider='manual'`, `status='created'`. - Nowa metoda `ShipmentController::createManual(Request): Response` — endpoint `POST /orders/{id}/shipment/manual`, walidacja CSRF + tracking_number, activity log `shipment_manual`. - Nowa route w `routes/web.php`: `POST /orders/{id}/shipment/manual`. - `resources/views/orders/show.php` — formularz inline do dodawania recznego numeru przesylki w zakladce Przesylki; zmienione wyswietlanie przesylek manualnych (status "Dodana recznie", przewoznik z carrier_id, brak etykiety). - `resources/scss/app.scss` — nowa klasa `.manual-tracking-form` (flex, gap 8px). ## 2026-03-22 (Phase 21 — Order Source Display, Plan 01) - `OrdersRepository::buildListSql()` — LEFT JOIN `integrations ig ON ig.id = o.integration_id`, nowa kolumna `ig.name AS integration_name`. - `OrdersRepository::findDetails()` — analogiczny LEFT JOIN dla strony szczegolow zamowienia. - `OrdersRepository::transformOrderRow()` — nowe pole `integration_name`. - `OrdersController::toTableRow()` — wyswietla `integration_name` gdy niepuste, fallback na `sourceLabel()` (Allegro/Erli/default). - `resources/views/orders/show.php` — naglowek zamowienia: dwa osobne spany (nazwa integracji + "ID: identyfikator") zamiast jednego. ## 2026-03-18 (Phase 16 — Zadania automatyczne, Plan 02: Watcher/Executor) - Nowa klasa `App\Modules\Automation\AutomationService` — trigger + ewaluacja warunkow (AND) + wykonanie akcji. - Flow: `ReceiptController::store()` -> `AutomationService::trigger('receipt.created', orderId)` -> sprawdzenie warunkow (integration_id) -> `EmailSendingService::send()`. - Rozszerzenie `EmailSendingService::send()` o opcjonalne parametry `$recipientEmailOverride` i `$recipientNameOverride` (kompatybilnosc wsteczna). - 3 tryby odbiorcy: 'client' (kupujacy), 'company' (e-mail firmy z company_settings), 'client_and_company' (oba). - Trigger w try/catch — blad automatyzacji nie blokuje sukcesu tworzenia paragonu. - Activity log: automation_email_sent / automation_email_failed z actor_type='system'. ## 2026-03-18 (Phase 16 — Zadania automatyczne, Plan 01: DB + CRUD) - Nowe tabele: `automation_rules`, `automation_conditions`, `automation_actions` (migracja 000057). - Nowy modul `App\Modules\Automation` z 2 klasami: - `AutomationController` — CRUD regul automatyzacji (index, create, store, edit, update, destroy, toggleStatus). - `AutomationRepository` — operacje DB z transakcjami (create/update atomowe z conditions+actions), findActiveByEvent dla watchera. - 7 nowych route'ow: `/settings/automation/*`. - Widoki: `resources/views/automation/index.php` (lista regul), `resources/views/automation/form.php` (formularz z dynamicznymi warunkami/akcjami). - Nowy JS: `public/assets/js/modules/automation-form.js` (dodawanie/usuwanie wierszy warunkow i akcji). - Nowy SCSS: `resources/scss/modules/_automation.scss` (style formularza dynamicznego). - Menu nawigacji: dodany link "Zadania automatyczne" w sekcji Ustawienia. ## 2026-03-17 (Phase 15 — Wysylka e-mail z zamowien) - Nowa zaleznosc: `phpmailer/phpmailer` v7.0.2 (SMTP transport). - Nowy modul `App\Modules\Email` z 3 klasami: - `EmailSendingService` — wysylka e-mail (send, preview), logowanie do email_logs, resolwer skrzynki (mailboxId → template → default). - `VariableResolver` — zamiana `{{grupa.zmienna}}` na dane zamowienia/kupujacego/adresu/firmy. - `AttachmentGenerator` — generowanie PDF paragonu (dompdf) jako zalacznik in-memory (addStringAttachment). - `OrdersController`: nowe metody `sendEmail()`, `emailPreview()`, `loadEmailLogs()`. - Nowe route'y: `POST /orders/{id}/send-email`, `POST /orders/{id}/email-preview`. - Widok `orders/show.php`: przycisk "Wyslij e-mail" + modal (wybor szablonu/skrzynki, podglad, wysylka AJAX). - Zakladka Dokumenty: sekcja "Wysylki e-mail" z historia wyslanych maili (status, podglad body). - Nowy partial: `resources/views/orders/partials/email-send-modal.php`. - Nowy SCSS: `resources/scss/modules/_email-send.scss` (modal overlay, podglad, style). ## 2026-03-15 (Phase 13 — DB + Skrzynki pocztowe) - Dodano 3 migracje email: `000054_create_email_mailboxes_table`, `000055_create_email_templates_table`, `000056_create_email_logs_table`. - Nowe klasy: `EmailMailboxController` (index, save, delete, toggleStatus, testConnection), `EmailMailboxRepository` (listAll, findById, save, delete, toggleStatus, listActive). - Test polaczenia SMTP przez natywny `stream_socket_client` z pelnym handshake (EHLO → STARTTLS → AUTH LOGIN) — bez zewnetrznych bibliotek. - Hasla SMTP szyfrowane przez `IntegrationSecretCipher` (AES-256-CBC + HMAC-SHA256). - Widok `settings/email-mailboxes.php` — lista skrzynek + formularz CRUD + AJAX test polaczenia. - Nawigacja: link "Skrzynki pocztowe" w sidebar Settings. - 5 nowych route'ow: GET/POST `/settings/email-mailboxes/*`. ## 2026-03-14 - Zoptymalizowano zapytanie listy zamowien (`OrdersRepository::buildListSql()`): - 4 correlated subqueries (items_count, items_qty, shipments_count, documents_count) zastapiono aggregating LEFT JOINami — eliminuje N+1 na kazdym wierszu listy. - `OrdersRepository::canResolveMappedMedia()` — zamiana instance property na `static` — `information_schema` odpytywany co najwyzej raz na cykl PHP zamiast raz per instancja. - Dodano migracje `20260314_000048_add_orders_performance_indexes.sql` — indeksy na `orders`: `source`, `external_status_id`, `ordered_at`, composite `(source, external_status_id)`. - Dodano SSL verification (`CURLOPT_SSL_VERIFYPEER => true`, `CURLOPT_SSL_VERIFYHOST => 2`, `CURLOPT_CAINFO`) do 4 klas ApiClient: AllegroApiClient (3 metody), AllegroOAuthClient, ShopproApiClient, ApaczkaApiClient. Fallback: `$_ENV['CURL_CA_BUNDLE_PATH']` → XAMPP cacert.pem → system CA bundle. - Cron web throttle (`isWebCronThrottled()`) przeniesiony z `$_SESSION` do `app_settings` (klucz `cron_web_last_run_at`) — eliminuje wielokrotne uruchamianie crona przy wielu aktywnych sesjach. - Deduplikacja migracji `000014` → `000014b` (kolizja z `create_product_integration_translations`). - `AllegroStatusSyncService::sync()` zwraca `ok:false` dla kierunku `orderpro_to_allegro` (wczesniej false-positive `ok:true`). Opcja UI oznaczona jako `disabled` z `(wkrotce)`. - Lista zamowien: source wyswietlany przed ID z prefixem `ID:`; `sourceLabel()` mapuje shoppro→shopPRO, allegro→Allegro. - Statusy zamowien na liscie kolorowane kolorem grupy z konfiguracji (`statusColorMap()` → inline `background-color`). - Ciemniejsze obramowanie pol formularzy: `--c-border` zmieniony z `#e2e8f0` na `#b0bec5`. - Hotfix SSL: `getCaBundlePath()` zwraca `null` gdy zaden CA bundle nie znaleziony — `CURLOPT_CAINFO` ustawiany warunkowo, cURL uzywa systemowego CA na serwerze. ## 2026-03-08 - Poprawiono date podjazdu kuriera w payloadzie Apaczka: - `pickup.date` dla trybu `COURIER` jest normalizowane tak, aby nie wypadal w niedziele (niedziela -> poniedzialek), - `pickup.hours_to` jest ograniczane do `16:00` (wg limitu API), a okno godzinowe jest korygowane do poprawnej relacji `hours_from < hours_to`, - eliminuje blad API `Field PickupLocation->MaxPickupDate cannot be set to Sunday`. - Dodano pole firmy `sender_contact_person` (osoba kontaktowa nadawcy): - nowa migracja `20260308_000046_extend_company_settings_contact_person.sql`, - UI `Ustawienia > Dane firmy` ma nowe pole `Osoba kontaktowa nadawcy`, - `ApaczkaShipmentService` wysyla `contact_person` dla nadawcy z ustawien firmy. - Uzupelniono kontakt odbiorcy w payloadzie Apaczka: - `ApaczkaShipmentService` wysyla `receiver.contact_person` (fallback na dane odbiorcy/klienta z zamowienia), - eliminuje blad API: `Osoba kontaktowa nadawcy/odbiorcy: Pole jest wymagane`. - Dla tworzenia przesylek Apaczka dodano jawne pole `pickup.type` w payloadzie `order_send`: - gdy podano `sender_point_id` ustawiane jest `SELF`, - gdy brak punktu nadania i usluga dopuszcza odbior kurierem ustawiane jest `COURIER`. - Poprawiono obsluge punktow nadania/odbioru w payloadzie Apaczka: - adres `sender` i `receiver` wysyla teraz komplet aliasow punktu (`point`, `foreign_address_id`, `point_id`), - rozszerzono diagnostyke dla bledu API `Niepoprawny sposob nadania przesylki` o wskazowke dot. `sender_point_id` / doboru uslugi. - Wzmocniono fallback adresu odbiorcy dla przesylek punktowych Apaczka (Orlen/InPost): - `ApaczkaShipmentService` probuje teraz dodatkowo uzupelnic adres punktu (`street`, `postal_code`, `city`) przez API `points` na podstawie `receiver_point_id`, - jezeli zamowienie punktowe dalej nie ma kompletu danych adresowych, serwis uzupelnia techniczne minimum (`line1`, `city`, `postal_code`) zamiast blokowac tworzenie przesylki bledem walidacji lokalnej. - Ujednolicono i wzmocniono naglowki sekcji UI (`h2/h3/h4.section-title`) w widokach: - naglowki sekcyjne maja teraz wyrazniejszy styl (akcent lewy, delikatne tlo, obramowanie), - zmiana jest globalna (SCSS), obejmuje m.in. `Zamowienia > Przygotuj przesylke` oraz pozostale ekrany korzystajace z `section-title`. - Poprawiono fallback danych odbiorcy dla przesylek punktowych Apaczka (w tym Orlen Paczka): - `ApaczkaShipmentService::buildReceiverAddress(...)` uzupelnia brakujace pola z wielu zrodel w kolejnosci: - dane z formularza, - adres `delivery` z zamowienia, - dane punktu z `parcel_name` (`ulica`, `kod`, `miasto`), - dane klienta (`name`, `phone`, `email`, `country`), - adres ulicy sklada teraz `street_name + street_number`, - naprawiono fallback `receiver_point_id` (uzywa `delivery.parcel_external_id`, usunieto bledne odwolanie do niezdefiniowanej zmiennej). - Poprawiono obsluge usunietych/nieosiagalnych etykiet Apaczka: - gdy API zwraca `Label is not available for this order`, rekord paczki jest oznaczany jako `error` i zapisywany jest `error_message`, - dzieki temu przycisk etykiety nie jest ponownie pokazywany dla tego rekordu. - Ujednolicono flow etykiet w `Zamowienia > Przygotuj przesylke` (dla wszystkich providerow): - po statusie `created` etykieta jest generowana automatycznie przez cykliczne sprawdzanie statusu (backend probuje `downloadLabel`), - UI nie pokazuje juz przycisku `Generuj etykiete`; do czasu gotowosci widoczny jest stan `Generowanie etykiety...`, - przycisk `Pobierz` pojawia sie dopiero po zapisaniu pliku etykiety (`label_path`). - Uzgodniono payload `order_send` Apaczka z dokumentacja API v2: - `ApaczkaShipmentService` wysyla teraz strukture `address.sender/receiver` + `shipment[]` (`dimension1/2/3`, `weight` w kg), - `cod` jest przekazywany jako obiekt (`amount`, `currency`), a wartosc ubezpieczenia przez `shipment_value` + `shipment_currency`, - punkty sa przekazywane jako `foreign_address_id` (z aliasami diagnostycznymi), - naprawilo to blad wyceny dla zamowienia testowego `#21` (utworzenie przesylki zakonczone sukcesem). - Dodano techniczny skrypt diagnostyczny `tools/apaczka_probe_order.php`: - wykonuje automatyczne proby `order_send` dla wskazanego zamowienia (bez klikania w UI), - testuje kombinacje uslug i wariantow pol punktu, raportujac sukces/blad per proba. - Rozszerzono payload punktow w tworzeniu przesylki Apaczka: - oprocz `point` przekazywane sa teraz aliasy `foreign_address_id` i `point_id` (dla odbiorcy i nadawcy), - cel: kompatybilnosc z wariantami API wymagajacymi innej nazwy pola punktu. - Korekta walidacji punktu odbioru Apaczka: - usunieto twarde blokowanie tworzenia przesylki na podstawie prefiksu punktu (`POP-`), - informacja o prefiksie punktu pozostaje tylko jako sugestia diagnostyczna. - Poprawiono prefill danych odbiorcy na ekranie `Zamowienia > Przygotuj przesylke`: - `ShipmentController` dla dostaw do punktu (`parcel_external_id`/`parcel_name`) ustawia `receiver_name` na dane klienta (`address_type=customer`), - eliminuje przypadek podstawiania nazwy punktu/metody dostawy w polu `Imie i nazwisko`. - Poprawiono diagnostyke bledow tworzenia przesylki Apaczka: - `ApaczkaShipmentService` przekazuje teraz `receiver_point_id` do payloadu `receiver.point` (oraz `sender_point_id` do `sender.point`), - dodano walidacje wymagan uslugi na podstawie `service_structure` (np. wymagany punkt odbioru/nadania), - dla bledu API `Brak wyceny dla podanych parametrów zamówienia` komunikat zawiera rozszerzona diagnostyke (service_id/nazwa/supplier, punkt odbioru/nadania, gabaryt/waga) i hint o niedopasowaniu uslugi do typu punktu. - Poprawiono import danych faktury z shopPRO: - `ShopproOrdersSyncService` wykrywa fakture nie tylko po `is_invoice`/`invoice.required`, ale takze po danych firmowych (`firm_name`/`firm_nip`), - `ShopproOrdersSyncService::mapAddresses(...)` zapisuje adres `invoice` (firma, NIP, adres) na podstawie pol `invoice`/`billing*`/`firm_*`, - widok `orders/show` wyswietla teraz `company_name` i `company_tax_number` dla adresu faktury. - Fix prewyboru uslugi Apaczka na ekranie `Zamowienia > Przygotuj przesylke`: - widok `resources/views/shipments/prepare.php` odczytuje ID uslugi Apaczka z fallbackiem `service_id -> id`, - naprawia przypadek, gdy przewoznik (`Apaczka`) byl wybierany poprawnie, ale usluga dostawy pozostawala pusta mimo mapowania. - Poprawiono diagnostyke mapowania form dostawy na ekranie `Zamowienia > Przygotuj przesylke`: - `ShipmentController` zwraca teraz komunikat diagnostyczny, gdy brak mapowania metody dostawy, - komunikat rozroznia brak mapowan dla instancji `shopPRO` (`source_integration_id`) od braku mapowania konkretnej metody, - widok `resources/views/shipments/prepare.php` wyswietla ten komunikat bezposrednio pod informacja o metodzie z zamowienia. - Poprawiono UX wyszukiwania w selectach mapowania form dostawy (zakladki `Formy dostawy`): - `resources/views/settings/allegro.php` przeszlo z przebudowy opcji `