Files
orderPRO/.paul/docs/TECH_CHANGELOG.md
2026-04-22 22:54:26 +02:00

7.8 KiB

TECH_CHANGELOG

Chronologiczny log zmian technicznych — co i dlaczego.

2026-04-22 - Alert klienta z historia zwrotow (Phase 106)

Powod: Operator wysylkowy nie widzial wczesniej, ze kupujacy juz raz nie odebral przesylki (delivery_status='returned') zanim wyslal kolejna paczke — generowalo to kolejne koszty wysylki i magazynowania.

Zmiany:

  • src/Modules/Orders/OrdersRepository.php:
    • nowa metoda prywatna customerReturnedCountSubquerySql(orderAlias, addressAlias) — generuje correlated subquery zliczajaca inne zamowienia klienta biezacego wiersza z paczka returned.
    • buildListSql() — dodana kolumna customer_returned_count (EXISTS-style COUNT DISTINCT) do SELECT listy zamowien.
    • transformOrderRow() — przekazuje customer_returned_count do wiersza.
    • findDetails() — JOIN order_addresses typu customer + subquery customer_returned_count; zwraca rowniez buyer_email, buyer_phone, buyer_name w $order.
  • src/Modules/Shipments/ShipmentPackageRepository.php:
    • nowa metoda findReturnedByCustomer(array customer, int excludeOrderId, int limit=10) — lista zwroconych paczek klienta (match OR: email lower+trim, phone tylko cyfry >=6, name lower+trim), sortowana po dacie malejaco.
  • src/Modules/Orders/OrdersController.php:
    • toTableRow() — dodano badge zwroty: N w kolumnie buyer + klasa is-risk-return na <tr> gdy customer_returned_count >= 1 (kompozycja z klasa aged orders z Phase 101).
    • show() — oblicza $customerRiskInfo i przekazuje do widoku.
    • nowe metody prywatne: buildCustomerRiskInfo(order, orderId), composeCustomerRiskText(count, email, phone, name) — budowa tresci alertu zaleznie od dostepnosci pol (phone+email / email / phone / name).
  • resources/views/orders/show.php — banner customer-risk-banner u samej gory karty szczegolow (pod naglowkiem, nad flash messages i status change), z <details> rozwijajacym liste zamowien ze zwrotem (order_id, data, tracking, provider).
  • resources/scss/modules/_customer-risk-alert.scss (nowy modul):
    • .customer-risk-banner + __icon, __body, __text, __list, __table — czerwony banner z pastelowym tlem i lewym paskiem 4px.
    • .risk-return-badge — maly inline badge przy buyer name.
    • tr.is-risk-return — lewy pasek wiersza w tabeli zamowien.
  • resources/scss/app.scss@use "modules/customer-risk-alert".
  • public/assets/css/app.css — rebuild przez npm run build:css.

Wymagania:

  • MySQL 8.0+ (REGEXP_REPLACE w subquery matching phone).
  • Wynik licznika wyliczany on-the-fly (brak migracji DB, brak materializacji). Indeksy na order_addresses(order_id, address_type) i shipment_packages(order_id, delivery_status) sugerowane jesli lista zamowien przekroczy ~50k wierszy — zglosic w kolejnym planie.

Anti-fraud/false positive:

  • Self-exclusion: sp.order_id != o.id — biezace zamowienie nie wlicza sie do licznika.
  • Minimum phone length 6 cyfr — eliminuje match na "", "+48", krotkich fragmentach.
  • OR matching po email/phone/name moze dac fałszywe pozytywy dla popularnych imion; swiadome odstepstwo (user wymagal szerokiego matchingu).

2026-04-22 - Mapowanie statusow dostawy: wykrywanie niezmapowanych + runtime overrides

Powod: zamowienia (np. OP000000357, OP000000638) pokazywaly delivery_status=unknown, mimo ze apaczka API zwracala RETURNED_TO_SHIPPER. UI /settings/delivery-status-mappings pokazywalo wylacznie statusy obecne w defaultach kodu — nowe raw statusy kuriera nie mialy gdzie zostac przypisane bez zmiany kodu.

Faza A — quick fix (defaulty + backfill):

  • src/Modules/Shipments/DeliveryStatus.php: dodano 3 brakujace mapowania:
    • apaczka RETURNED_TO_SHIPPER -> returned
    • apaczka PICKUP -> in_transit
    • allegro_wza collected_from_sender -> in_transit
  • database/migrations/20260422_000101_backfill_delivery_status_unknowns.sql: backfill 11 paczek (3 + 7 + 1). Bez trigger shipment.status_changed (backfill starych rekordow, nie runtime event).

Faza B — rozwiazanie systemowe:

  • DeliveryStatusMappingRepository::listUnmappedRawStatuses(provider, knownKeys) — zwraca raw statusy z shipment_packages ktore nie wystepuja w defaultach ani overrides DB; z licznikiem paczek i ostatnim wystapieniem.
  • DeliveryStatusMappingController::index — przekazuje unmappedRawStatuses do widoku; rowniez wlacza do listy overrides, ktore nie maja odpowiadajacego defaultu (user moze dodac completely custom raw statusy).
  • resources/views/settings/delivery-status-mappings.php — nowa sekcja „Niezmapowane statusy wykryte w systemie (N)" z form submit do save-bulk. Pomaranczowy akcent aby wyroznic od defaultow.
  • ShipmentTrackingHandler — dostal ?DeliveryStatusMappingRepository w konstruktorze; po kazdym service->getDeliveryStatus() wywoluje DeliveryStatus::normalizeWithOverrides(provider, raw, overrides) jesli overrides istnieja. Dzieki temu override z UI dziala runtime bez zmian kodu.
  • CronHandlerFactory — przekazuje DeliveryStatusMappingRepository do ShipmentTrackingHandler.

Faza C — badge w menu:

  • Application — dodano statyczny holder self::$instance + Application::instance(), aby layout mial dostep do kontenera.
  • DeliveryStatusMappingRepository::countAllUnmappedForBadge() — zlicza niezmapowane raw statusy dla wszystkich providerow UI (inpost, apaczka, allegro_wza); cache per-request.
  • resources/views/layouts/app.php — badge pomaranczowy przy linku „Mapowanie statusow dostawy" z liczba niezmapowanych statusow (jesli > 0). Try/catch — brak badge'a nie psuje layoutu.
  • resources/scss/app.scss — klasa .sidebar__badge.

Efekt: user nie musi modyfikowac kodu przy kazdym nowym statusie kuriera. Badge sygnalizuje pojawienie sie nieznanych statusow; sekcja na stronie mapowan pozwala przypisac je do znormalizowanych kategorii. Cron po nastepnym tick'u automatycznie przeliczy istniejace paczki zgodnie z override.

2026-04-19 - Statystyki zamowien (menu + raport dzienny)

  • Dodano nowy modul Statistics:
    • OrdersStatisticsController (obsluga filtrow i render strony /statistics/orders).
    • OrdersStatisticsRepository (agregacje dzienne po kanalach i grupach statusow).
  • Dodano nowa pozycje menu: Statystyki -> Zamowienia.
  • Dodano widok raportowy z filtrem zakresu dat, multiselectem kanalow i multiselectem grup statusow.
  • Dodano tabele dzienna z metrykami Ilosc, Netto, Brutto per kanal oraz stopka Razem.
  • Dodano tlumaczenia statistics.orders.* i navigation.statistics*.
  • Brak zmian migracyjnych i brak zmian schematu bazy danych.

2026-04-19 - Fix: Statystyki nie pokazywaly zamowien (kolizja collation)

  • OrdersStatisticsRepository::channelSql() generowal wyrazenie CONCAT("shoppro:", CAST(integration_id AS CHAR)), ktore w MySQL dawalo wynik z collation utf8mb4_bin. W zestawieniu z parametrami bindowanymi (utf8mb4_general_ci) MySQL rzucal SQLSTATE[HY000] 1271 Illegal mix of collations for operation 'in'.
  • Blad byl polykany przez try/catch (Throwable) w aggregateByDay(), przez co widok dostawal pusta tablice i nie pokazywal zadnych zamowien.
  • Fix: dodano jawne COLLATE utf8mb4_unicode_ci na CAST(integration_id AS CHAR) oraz na calym wyrazeniu CASE zwracajacym channel_key, tak aby klucz kanalu mial spojne collation zgodne z orders.source.

2026-04-19 - Statystyki: fallback netto 23% VAT

  • OrdersStatisticsRepository::netAmountSql() dostal fallback: jesli orders.total_without_tax jest NULL lub 0, a orders.total_with_tax ma wartosc, netto wyliczane jest jako ROUND(total_with_tax / 1.23, 2).
  • Uzasadnienie: shopPRO nie wysyla netto ani na zamowieniu ani w pozycjach (order_items.original_price_without_tax jest puste), wiec bez fallbacku kolumna Netto w statystykach pokazywala 0.
  • Uwaga: fallback zaklada 23% VAT. Ostateczne rozwiazanie (prawidlowy netto z shopPRO / z order_items.tax_rate) opisane w .paul/TODO.md (tag STAT-NET).