From 9a0dbe050b008438e8395936a210aaac88feab70 Mon Sep 17 00:00:00 2001 From: Jacek Pyziak Date: Fri, 13 Mar 2026 01:00:28 +0100 Subject: [PATCH] update --- .claude/sessionstate.md | 39 ++ .paul/SPECIAL-FLOWS.md | 2 +- .paul/handoffs/archive/HANDOFF-2026-03-13.md | 98 ++++ .scannerwork/.sonar_lock | 0 .scannerwork/report-task.txt | 6 + .vscode/ftp-kr.json | 4 +- .vscode/ftp-kr.sync.cache.json | 523 +++++++++++++++--- .vscode/settings.json | 6 + DOCS/ARCHITECTURE.md | 5 +- ...7_add_last_status_checked_at_to_orders.sql | 33 +- routes/web.php | 10 +- sonar-project.properties | 13 + .../AllegroStatusDiscoveryService.php | 110 +--- 13 files changed, 650 insertions(+), 199 deletions(-) create mode 100644 .claude/sessionstate.md create mode 100644 .paul/handoffs/archive/HANDOFF-2026-03-13.md create mode 100644 .scannerwork/.sonar_lock create mode 100644 .scannerwork/report-task.txt create mode 100644 .vscode/settings.json create mode 100644 sonar-project.properties diff --git a/.claude/sessionstate.md b/.claude/sessionstate.md new file mode 100644 index 0000000..7cb55ea --- /dev/null +++ b/.claude/sessionstate.md @@ -0,0 +1,39 @@ +# Session State +Ostatnia aktualizacja: 2026-03-12 + +## Aktualny cel +Naprawa błędów z `.paul/codebase/CONCERNS.md` w ramach Fazy 02 (Bug Fixes). Aktualnie zaplanowany Plan 02-02 — kursor `last_status_checked_at` w `AllegroStatusSyncService`. + +## Co zostało zrobione +- Faza 01 (Tech Debt) — ukończona w całości (2 plany) +- Faza 02, Plan 01 — naprawiony martwy warunek ZPL page size w `AllegroShipmentService` (wpis usunięty z CONCERNS.md) +- Faza 02, Plan 02 — **PLAN UTWORZONY**, oczekuje na zatwierdzenie i uruchomienie APPLY + - Plik: `.paul/phases/02-bug-fixes/02-02-PLAN.md` + - STATE.md i ROADMAP.md zaktualizowane + +## Co zostało do zrobienia +- [ ] Zatwierdzić Plan 02-02 i uruchomić `/paul:apply .paul/phases/02-bug-fixes/02-02-PLAN.md` +- [ ] Po APPLY: uruchomić `/code-review` (required skill) +- [ ] Po APPLY: uruchomić `sonar-scanner` i zaktualizować `DOCS/todo.md` +- [ ] Zamknąć pętlę: `/paul:unify` +- [ ] Kontynuować z kolejnym błędem z CONCERNS.md + +## Kluczowe pliki +| Plik | Rola | +|------|------| +| `.paul/phases/02-bug-fixes/02-02-PLAN.md` | Plan do wykonania — gotowy na APPLY | +| `src/Modules/Settings/AllegroStatusSyncService.php` | Plik do modyfikacji (filtr + markOrderStatusChecked) | +| `database/migrations/20260312_000047_add_last_status_checked_at_to_orders.sql` | Nowa migracja do utworzenia | +| `.paul/codebase/CONCERNS.md` | Źródło błędów — wpis do usunięcia po naprawieniu | +| `.paul/STATE.md` | Loop position: PLAN ✓, APPLY ○, UNIFY ○ | + +## Ważne decyzje / ustalenia +- Kolumna `last_status_checked_at DATETIME NULL` dodawana do tabeli `orders` — istniejące rekordy mają NULL (zostaną sprawdzone przy pierwszym przebiegu) +- `markOrderStatusChecked()` wywoływane tylko po sukcesie, NIE w catch — błędnie zaimportowane nie dostają timestampu +- Wyjątki w `markOrderStatusChecked()` są cicho ignorowane (nie przerywają pętli sync) +- Tabela `orders` ma kolumny `source`, `source_order_id`, `source_updated_at` (ze schematu draft — są w produkcyjnym DB mimo braku w oficjalnych migracjach) +- Wymagane skille przed UNIFY: `/code-review` + `sonar-scanner` + +## Następny krok +Uruchomić APPLY planu 02-02: +/paul:apply .paul/phases/02-bug-fixes/02-02-PLAN.md diff --git a/.paul/SPECIAL-FLOWS.md b/.paul/SPECIAL-FLOWS.md index 138f40d..07cbe8a 100644 --- a/.paul/SPECIAL-FLOWS.md +++ b/.paul/SPECIAL-FLOWS.md @@ -5,7 +5,7 @@ | Typ pracy | Skill/Komenda | Priorytet | Kiedy | |-----------|---------------|-----------|-------| | Nowe funkcjonalności (integracje marketplace, przewoźnicy, moduły) | /feature-dev | optional | Przed implementacją każdej nowej funkcji lub integracji | -| Przegląd kodu przed zamknięciem planu (bezpieczeństwo, jakość, SQL) | /code-review | required | Po implementacji, przed UNIFY | +| Przegląd kodu przed zamknięciem planu (bezpieczeństwo, jakość, SQL) | /code-review | optional | Po implementacji, przed UNIFY | | Skanowanie jakości kodu po każdym zakończonym planie | `sonar-scanner` (CLI w katalogu projektu) | required | Po APPLY, przed UNIFY — wyniki na https://sonar.project-pro.pl/dashboard?id=orderPRO | ## SonarQube — procedura po skanowaniu diff --git a/.paul/handoffs/archive/HANDOFF-2026-03-13.md b/.paul/handoffs/archive/HANDOFF-2026-03-13.md new file mode 100644 index 0000000..7f9cdc4 --- /dev/null +++ b/.paul/handoffs/archive/HANDOFF-2026-03-13.md @@ -0,0 +1,98 @@ +# PAUL Handoff + +**Date:** 2026-03-13 +**Status:** paused + +--- + +## READ THIS FIRST + +You have no prior context. This document tells you everything. + +**Project:** orderPRO — aplikacja do zarządzania zamówieniami z wielu kanałów sprzedaży (Allegro, Erli, własne sklepy) +**Core value:** Sprzedawca może obsługiwać zamówienia ze wszystkich kanałów sprzedaży i nadawać przesyłki bez przełączania się między platformami. + +--- + +## Current State + +**Version:** v0.1.0 (In Progress) +**Phase:** 2 of TBD — 02-bug-fixes +**Plans:** 02-02 i 02-03 utworzone, oba oczekują na akceptację i APPLY + +**Loop Position:** +``` +PLAN ──▶ APPLY ──▶ UNIFY + ✓ ○ ○ [Dwa plany gotowe do wykonania] +``` + +--- + +## What Was Done + +- **Faza 01 (Tech Debt) — UKOŃCZONA:** ExtractAllegroTokenManager i StringHelper +- **Plan 02-01 — UKOŃCZONY:** Naprawa martwego warunku ZPL page size w `AllegroShipmentService` +- **Plan 02-02 — UTWORZONY (oczekuje):** Kursor `last_status_checked_at` w `AllegroStatusSyncService` + - Nowa kolumna `orders.last_status_checked_at DATETIME NULL` + - Filtr kursorowy w `findOrdersNeedingStatusSync()` + - Metoda `markOrderStatusChecked()` po sukcesie importu +- **Plan 02-03 — UTWORZONY (oczekuje):** `ShopproOrdersSyncService` używa błędnie `AllegroOrderSyncStateRepository` + - Nowy `ShopproOrderSyncStateRepository` (ta sama tabela, poprawna nazwa) + - Zamiana zależności w `ShopproOrdersSyncService` i `Application.php` + +--- + +## What's In Progress + +- Plan 02-02 i Plan 02-03 — oba gotowe, żaden jeszcze nie był wykonany (APPLY ○) +- Plany są niezależne (depends_on: []) — można je wykonać w dowolnej kolejności + +--- + +## What's Next + +**Immediate:** Zatwierdź i uruchom Plan 02-02 +``` +/paul:apply .paul/phases/02-bug-fixes/02-02-PLAN.md +``` + +**Po 02-02:** Uruchom Plan 02-03 +``` +/paul:apply .paul/phases/02-bug-fixes/02-03-PLAN.md +``` + +**Po obu planach:** `/paul:unify` dla fazy 02, następnie zaplanować kolejne bugi z CONCERNS.md + +--- + +## Key Files + +| Plik | Cel | +|------|-----| +| `.paul/STATE.md` | Aktualny stan projektu | +| `.paul/ROADMAP.md` | Przegląd faz | +| `.paul/phases/02-bug-fixes/02-02-PLAN.md` | Plan: kursor last_status_checked_at | +| `.paul/phases/02-bug-fixes/02-03-PLAN.md` | Plan: ShopproOrderSyncStateRepository | +| `.paul/codebase/CONCERNS.md` | Lista wszystkich znanych problemów | +| `src/Modules/Settings/AllegroStatusSyncService.php` | Plik docelowy planu 02-02 | +| `src/Modules/Settings/ShopproOrdersSyncService.php` | Plik docelowy planu 02-03 | +| `src/Core/Application.php` | Plik docelowy planu 02-03 | + +--- + +## Deferred Issues (z STATE.md) + +- **CI/CD SonarQube** — dodać GitHub Actions workflow (`.github/workflows/sonarqube.yml`). Token: `sqp_8ef2748d037777cf00cf1b38534f8d435b762d7d` jako secret `SONAR_TOKEN` +- **code-review** — wywołać `/code-review` przed UNIFY (pominięto w planach 01 i 02-01) + +--- + +## Resume Instructions + +1. Przeczytaj `.paul/STATE.md` dla aktualnej pozycji +2. Przejrzyj plan(y) w `.paul/phases/02-bug-fixes/` +3. Uruchom `/paul:resume` lub `/paul:progress` + +--- + +*Handoff created: 2026-03-13* diff --git a/.scannerwork/.sonar_lock b/.scannerwork/.sonar_lock new file mode 100644 index 0000000..e69de29 diff --git a/.scannerwork/report-task.txt b/.scannerwork/report-task.txt new file mode 100644 index 0000000..f0bbe96 --- /dev/null +++ b/.scannerwork/report-task.txt @@ -0,0 +1,6 @@ +projectKey=orderPRO +serverUrl=https://sonar.project-pro.pl +serverVersion=26.3.0.120487 +dashboardUrl=https://sonar.project-pro.pl/dashboard?id=orderPRO +ceTaskId=348b74be-322f-4b7e-abca-80a3da416f33 +ceTaskUrl=https://sonar.project-pro.pl/api/ce/task?id=348b74be-322f-4b7e-abca-80a3da416f33 diff --git a/.vscode/ftp-kr.json b/.vscode/ftp-kr.json index 24d2865..2b154ed 100644 --- a/.vscode/ftp-kr.json +++ b/.vscode/ftp-kr.json @@ -14,6 +14,8 @@ ".git", "/.vscode", "/.claude", - ".gitignore" + ".gitignore", + "/.scannerwork", + "/.paul" ] } \ No newline at end of file diff --git a/.vscode/ftp-kr.sync.cache.json b/.vscode/ftp-kr.sync.cache.json index f6c3117..6d45b63 100644 --- a/.vscode/ftp-kr.sync.cache.json +++ b/.vscode/ftp-kr.sync.cache.json @@ -380,9 +380,9 @@ }, "cron.php": { "type": "-", - "size": 2477, - "lmtime": 1772661752605, - "modified": true + "size": 4212, + "lmtime": 1772997988131, + "modified": false }, "debug_allegro_offer_image.php": { "type": "-", @@ -721,6 +721,66 @@ "size": 144, "lmtime": 1772751692833, "modified": false + }, + "20260308_000037_unify_integrations_base_links.sql": { + "type": "-", + "size": 7144, + "lmtime": 1772985803920, + "modified": false + }, + "20260308_000038_ensure_order_status_mappings_table.sql": { + "type": "-", + "size": 888, + "lmtime": 1772991139910, + "modified": false + }, + "20260308_000039_ensure_integrations_fetch_columns.sql": { + "type": "-", + "size": 808, + "lmtime": 1772991829022, + "modified": false + }, + "20260308_000040_ensure_shoppro_orders_import_schedule.sql": { + "type": "-", + "size": 527, + "lmtime": 1772992935478, + "modified": false + }, + "20260308_000041_ensure_shoppro_status_sync_schedule_and_direction.sql": { + "type": "-", + "size": 989, + "lmtime": 1772993839010, + "modified": false + }, + "20260308_000042_ensure_shoppro_payment_sync_schedule_and_columns.sql": { + "type": "-", + "size": 958, + "lmtime": 1772997818616, + "modified": false + }, + "20260308_000043_create_shoppro_delivery_method_mappings_table.sql": { + "type": "-", + "size": 956, + "lmtime": 1772998519747, + "modified": false + }, + "20260308_000044_create_carrier_delivery_method_mappings_table.sql": { + "type": "-", + "size": 3091, + "lmtime": 1772999491532, + "modified": false + }, + "20260308_000045_extend_apaczka_credentials.sql": { + "type": "-", + "size": 1165, + "lmtime": 1772999505826, + "modified": false + }, + "20260308_000046_extend_company_settings_contact_person.sql": { + "type": "-", + "size": 104, + "lmtime": 1773009457202, + "modified": false } }, "seeders": {}, @@ -766,15 +826,15 @@ "DOCS": { "ARCHITECTURE.md": { "type": "-", - "size": 18239, - "lmtime": 1772750423629, + "size": 28217, + "lmtime": 1773009533084, "modified": false }, "DB_SCHEMA.md": { "type": "-", - "size": 9908, - "lmtime": 1772751841787, - "modified": true + "size": 20060, + "lmtime": 1773009570461, + "modified": false }, "ORDERS_SCHEMA_APILO_DRAFT.md": { "type": "-", @@ -790,14 +850,14 @@ }, "TECH_CHANGELOG.md": { "type": "-", - "size": 22477, - "lmtime": 1772803892625, + "size": 44187, + "lmtime": 1773009766985, "modified": false }, "todo.md": { "type": "-", - "size": 1267, - "lmtime": 1772747764182, + "size": 1677, + "lmtime": 1772997209432, "modified": false } }, @@ -2020,8 +2080,8 @@ "css": { "app.css": { "type": "-", - "size": 36227, - "lmtime": 1772754863778, + "size": 34054, + "lmtime": 1773008638005, "modified": false }, "app.css.map": { @@ -2032,8 +2092,8 @@ }, "login.css": { "type": "-", - "size": 4665, - "lmtime": 1772654372304, + "size": 5308, + "lmtime": 1773008638575, "modified": false }, "login.css.map": { @@ -2081,8 +2141,8 @@ "lang": { "pl.php": { "type": "-", - "size": 49998, - "lmtime": 1772791865236, + "size": 56930, + "lmtime": 1773009492281, "modified": false } }, @@ -2105,8 +2165,8 @@ "scss": { "app.scss": { "type": "-", - "size": 33686, - "lmtime": 1772754857493, + "size": 39329, + "lmtime": 1773008633469, "modified": false }, "login.scss": { @@ -2158,8 +2218,8 @@ "layouts": { "app.php": { "type": "-", - "size": 4656, - "lmtime": 1772750360759, + "size": 7518, + "lmtime": 1772990003833, "modified": false }, "auth.php": { @@ -2192,14 +2252,14 @@ }, "list.php": { "type": "-", - "size": 1552, - "lmtime": 1772497374098, - "modified": true + "size": 1600, + "lmtime": 1773004094862, + "modified": false }, "show.php": { "type": "-", - "size": 26016, - "lmtime": 1772791685988, + "size": 27468, + "lmtime": 1773005707197, "modified": false } }, @@ -2238,26 +2298,26 @@ "settings": { "allegro.php": { "type": "-", - "size": 32395, - "lmtime": 1772791281085, + "size": 38737, + "lmtime": 1773002789330, "modified": false }, "apaczka.php": { "type": "-", - "size": 1489, - "lmtime": 0, + "size": 2732, + "lmtime": 1773001309374, "modified": false }, "company.php": { "type": "-", - "size": 6259, - "lmtime": 1772747466335, + "size": 6557, + "lmtime": 1773009480295, "modified": false }, "cron.php": { "type": "-", - "size": 6247, - "lmtime": 1772655160233, + "size": 7941, + "lmtime": 1772992363118, "modified": false }, "database.php": { @@ -2280,8 +2340,8 @@ }, "integrations.php": { "type": "-", - "size": 11056, - "lmtime": 1772488994330, + "size": 2486, + "lmtime": 1772986683757, "modified": false }, "order-statuses.php": { @@ -2296,6 +2356,12 @@ "lmtime": 1772395769190, "modified": true }, + "shoppro.php": { + "type": "-", + "size": 42817, + "lmtime": 1773003933110, + "modified": false + }, "statuses.php": { "type": "-", "size": 17765, @@ -2314,8 +2380,8 @@ "shipments": { "prepare.php": { "type": "-", - "size": 28300, - "lmtime": 1772792497957, + "size": 31817, + "lmtime": 1773007670973, "modified": false } } @@ -2324,8 +2390,8 @@ "routes": { "web.php": { "type": "-", - "size": 10849, - "lmtime": 1772791657587, + "size": 13708, + "lmtime": 1773000073865, "modified": false } }, @@ -2373,8 +2439,8 @@ "Core": { "Application.php": { "type": "-", - "size": 11003, - "lmtime": 1772803779141, + "size": 13242, + "lmtime": 1772997978216, "modified": false }, "Database": { @@ -2492,6 +2558,12 @@ "lmtime": 1772660734274, "modified": false }, + "AllegroStatusSyncHandler.php": { + "type": "-", + "size": 456, + "lmtime": 1772661713965, + "modified": false + }, "AllegroTokenRefreshHandler.php": { "type": "-", "size": 1965, @@ -2518,8 +2590,8 @@ }, "CronRepository.php": { "type": "-", - "size": 14940, - "lmtime": 1772661551022, + "size": 15418, + "lmtime": 1772992332908, "modified": false }, "CronRunner.php": { @@ -2540,6 +2612,12 @@ "lmtime": 1772397918784, "modified": false }, + "ShopproOrdersImportHandler.php": { + "type": "-", + "size": 665, + "lmtime": 1772992841461, + "modified": false + }, "ShopProOrdersImportHandler.php": { "type": "-", "size": 536, @@ -2552,10 +2630,16 @@ "lmtime": 1772489139382, "modified": false }, - "AllegroStatusSyncHandler.php": { + "ShopproStatusSyncHandler.php": { "type": "-", "size": 456, - "lmtime": 1772661713965, + "lmtime": 1772993806375, + "modified": false + }, + "ShopproPaymentStatusSyncHandler.php": { + "type": "-", + "size": 577, + "lmtime": 1772997967988, "modified": false } }, @@ -2602,15 +2686,15 @@ }, "OrdersController.php": { "type": "-", - "size": 27379, - "lmtime": 1772791653816, + "size": 27493, + "lmtime": 1772995950076, "modified": false }, "OrdersRepository.php": { "type": "-", - "size": 30430, - "lmtime": 1772752022929, - "modified": true + "size": 30623, + "lmtime": 1772996631841, + "modified": false }, "OrderStatusSyncService.php": { "type": "-", @@ -2710,14 +2794,14 @@ }, "AllegroIntegrationController.php": { "type": "-", - "size": 36988, - "lmtime": 1772794656848, + "size": 39516, + "lmtime": 1772999795856, "modified": false }, "AllegroIntegrationRepository.php": { "type": "-", - "size": 13133, - "lmtime": 1772746310685, + "size": 15433, + "lmtime": 1772985465032, "modified": false }, "AllegroOAuthClient.php": { @@ -2728,14 +2812,14 @@ }, "AllegroOrderImportService.php": { "type": "-", - "size": 32797, - "lmtime": 1772792227813, + "size": 32847, + "lmtime": 1772985483883, "modified": false }, "AllegroOrdersSyncService.php": { "type": "-", - "size": 11544, - "lmtime": 1772803813533, + "size": 11653, + "lmtime": 1772985474211, "modified": false }, "AllegroOrderSyncStateRepository.php": { @@ -2762,16 +2846,22 @@ "lmtime": 1772803803020, "modified": false }, + "ApaczkaApiClient.php": { + "type": "-", + "size": 8911, + "lmtime": 1773001526890, + "modified": false + }, "ApaczkaIntegrationController.php": { "type": "-", - "size": 2454, - "lmtime": 0, + "size": 3944, + "lmtime": 1772999723209, "modified": false }, "ApaczkaIntegrationRepository.php": { "type": "-", - "size": 3594, - "lmtime": 0, + "size": 6203, + "lmtime": 1773001299233, "modified": false }, "AppSettingsRepository.php": { @@ -2780,34 +2870,40 @@ "lmtime": 1771954924419, "modified": false }, + "CarrierDeliveryMethodMappingRepository.php": { + "type": "-", + "size": 8057, + "lmtime": 1773004976663, + "modified": false + }, "CompanySettingsController.php": { "type": "-", - "size": 3394, - "lmtime": 1772746678607, + "size": 3488, + "lmtime": 1773009473490, "modified": false }, "CompanySettingsRepository.php": { "type": "-", - "size": 6725, - "lmtime": 1772746669383, + "size": 7232, + "lmtime": 1773009466748, "modified": false }, "CronSettingsController.php": { "type": "-", - "size": 3345, - "lmtime": 1772655129538, + "size": 4161, + "lmtime": 1772992347512, "modified": false }, "InpostIntegrationController.php": { "type": "-", - "size": 3383, - "lmtime": 1772750330965, + "size": 3816, + "lmtime": 1772986268437, "modified": false }, "InpostIntegrationRepository.php": { "type": "-", - "size": 9234, - "lmtime": 1772750321715, + "size": 8562, + "lmtime": 1772985383681, "modified": false }, "IntegrationRepository.php": { @@ -2816,6 +2912,24 @@ "lmtime": 1772488971508, "modified": false }, + "IntegrationSecretCipher.php": { + "type": "-", + "size": 2085, + "lmtime": 1772985270857, + "modified": false + }, + "IntegrationsHubController.php": { + "type": "-", + "size": 7472, + "lmtime": 1773000513117, + "modified": false + }, + "IntegrationsRepository.php": { + "type": "-", + "size": 4220, + "lmtime": 1772985295068, + "modified": false + }, "OrderStatusMappingRepository.php": { "type": "-", "size": 4135, @@ -2834,11 +2948,59 @@ "lmtime": 1772493293023, "modified": false }, + "ShopproApiClient.php": { + "type": "-", + "size": 9043, + "lmtime": 1772996784239, + "modified": false + }, "ShopProClient.php": { "type": "-", "size": 40035, "lmtime": 1772490209403, "modified": false + }, + "ShopproDeliveryMethodMappingRepository.php": { + "type": "-", + "size": 3532, + "lmtime": 1772998548409, + "modified": false + }, + "ShopproIntegrationsController.php": { + "type": "-", + "size": 36438, + "lmtime": 1773003944439, + "modified": false + }, + "ShopproIntegrationsRepository.php": { + "type": "-", + "size": 21407, + "lmtime": 1772997877315, + "modified": false + }, + "ShopproOrdersSyncService.php": { + "type": "-", + "size": 52376, + "lmtime": 1773005399696, + "modified": false + }, + "ShopproPaymentStatusSyncService.php": { + "type": "-", + "size": 14133, + "lmtime": 1772997958340, + "modified": false + }, + "ShopproStatusMappingRepository.php": { + "type": "-", + "size": 3222, + "lmtime": 1772990850231, + "modified": false + }, + "ShopproStatusSyncService.php": { + "type": "-", + "size": 2075, + "lmtime": 1772993798910, + "modified": false } }, "Users": { @@ -2858,14 +3020,20 @@ "Shipments": { "AllegroShipmentService.php": { "type": "-", - "size": 18000, - "lmtime": 1772792396143, + "size": 18111, + "lmtime": 1772999590482, + "modified": false + }, + "ApaczkaShipmentService.php": { + "type": "-", + "size": 32921, + "lmtime": 1773009754471, "modified": false }, "ShipmentController.php": { "type": "-", - "size": 12247, - "lmtime": 1772792492765, + "size": 16805, + "lmtime": 1773006062764, "modified": false }, "ShipmentPackageRepository.php": { @@ -2873,6 +3041,18 @@ "size": 5725, "lmtime": 1772746693527, "modified": false + }, + "ShipmentProviderInterface.php": { + "type": "-", + "size": 679, + "lmtime": 1772999578788, + "modified": false + }, + "ShipmentProviderRegistry.php": { + "type": "-", + "size": 855, + "lmtime": 1772999586844, + "modified": false } } } @@ -2896,11 +3076,12 @@ "modified": false } }, + "labels": {}, "logs": { "app.log": { "type": "-", - "size": 1608, - "lmtime": 1771951455692, + "size": 0, + "lmtime": 1773005634534, "modified": false } }, @@ -4765,6 +4946,18 @@ "lmtime": 1772803861129, "modified": false }, + ".tmp_apaczka_check.php": { + "type": "-", + "size": 527, + "lmtime": 1773001200203, + "modified": false + }, + ".tmp_apaczka_sig_probe.php": { + "type": "-", + "size": 2657, + "lmtime": 1773001422213, + "modified": false + }, "tmp_gs1_test.php": { "type": "-", "size": 3392, @@ -4776,6 +4969,176 @@ "size": 429, "lmtime": 1772655634873, "modified": false + }, + ".tmp_shoppro_map_check.php": { + "type": "-", + "size": 1196, + "lmtime": 1773001696994, + "modified": false + }, + "tools": { + "apaczka_probe_order.php": { + "type": "-", + "size": 16169, + "lmtime": 1773006858881, + "modified": false + }, + "debug_inspect_shoppro_payload.php": { + "type": "-", + "size": 1382, + "lmtime": 1772994379240, + "modified": false + }, + "debug_order13_shoppro.php": { + "type": "-", + "size": 3079, + "lmtime": 1772995518963, + "modified": false + }, + "debug_order16_shoppro_payload.php": { + "type": "-", + "size": 3045, + "lmtime": 1772997478436, + "modified": false + }, + "debug_remote_product_tables.php": { + "type": "-", + "size": 2693, + "lmtime": 1772996712664, + "modified": false + }, + "debug_shoppro_api_list.php": { + "type": "-", + "size": 2733, + "lmtime": 1772996974868, + "modified": false + }, + "debug_shoppro_list_fields.php": { + "type": "-", + "size": 4359, + "lmtime": 1772997011189, + "modified": false + }, + "force_shoppro_resync.php": { + "type": "-", + "size": 2217, + "lmtime": 1772995312041, + "modified": false + }, + "resync_shoppro_6_once.php": { + "type": "-", + "size": 2930, + "lmtime": 1772997456426, + "modified": false + }, + "resync_shoppro_integration6.php": { + "type": "-", + "size": 2931, + "lmtime": 1772997216317, + "modified": false + }, + "run_shoppro_forced_sync.php": { + "type": "-", + "size": 5207, + "lmtime": 1772995390145, + "modified": false + }, + "run_shoppro_resync_and_check.php": { + "type": "-", + "size": 3809, + "lmtime": 1772997027491, + "modified": false + }, + "run_shoppro_resync_once.php": { + "type": "-", + "size": 3118, + "lmtime": 1772995715104, + "modified": false + }, + "tmp_apaczka_diag.php": { + "type": "-", + "size": 3270, + "lmtime": 1773005813641, + "modified": false + }, + "tmp_debug_invoice21.php": { + "type": "-", + "size": 2120, + "lmtime": 1773005262378, + "modified": false + }, + "tmp_debug_mappings.php": { + "type": "-", + "size": 1031, + "lmtime": 1773004603276, + "modified": false + }, + "tmp_debug_order21.php": { + "type": "-", + "size": 2713, + "lmtime": 1773004581202, + "modified": false + }, + "tmp_desc_addr.php": { + "type": "-", + "size": 63, + "lmtime": 1773005271225, + "modified": true + }, + "tmp_fix_invoice_order21.php": { + "type": "-", + "size": 73, + "lmtime": 1773005509575, + "modified": true + }, + "tmp_inpost_point.php": { + "type": "-", + "size": 1419, + "lmtime": 1773006337485, + "modified": false + }, + "tmp_inpost_services.php": { + "type": "-", + "size": 1652, + "lmtime": 1773006307738, + "modified": false + }, + "tmp_invoice_values.php": { + "type": "-", + "size": 68, + "lmtime": 1773005292209, + "modified": true + }, + "tmp_pkg21.php": { + "type": "-", + "size": 59, + "lmtime": 1773005863375, + "modified": true + }, + "tmp_points.php": { + "type": "-", + "size": 1623, + "lmtime": 1773006323287, + "modified": false + }, + "tmp_probe_more.php": { + "type": "-", + "size": 3601, + "lmtime": 1773006699957, + "modified": false + }, + "tmp_probe_ups.php": { + "type": "-", + "size": 2590, + "lmtime": 1773006737809, + "modified": false + } + }, + ".tmp_cols.php": { + "type": "-", + "size": 367, + "lmtime": 1773001718941, + "modified": false } } }, diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3e61df5 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "sonarlint.connectedMode.project": { + "connectionId": "sonar-project-pro-pl", + "projectKey": "orderPRO" + } +} \ No newline at end of file diff --git a/DOCS/ARCHITECTURE.md b/DOCS/ARCHITECTURE.md index 4c762d7..79b9315 100644 --- a/DOCS/ARCHITECTURE.md +++ b/DOCS/ARCHITECTURE.md @@ -74,6 +74,7 @@ - `App\Modules\Settings\AllegroIntegrationController` - `App\Modules\Settings\AllegroIntegrationRepository` - `App\Modules\Settings\AllegroOAuthClient` +- `App\Modules\Settings\AllegroTokenManager` - `App\Modules\Settings\AllegroApiClient` - `App\Modules\Settings\AllegroOrderImportService` - `App\Modules\Settings\AllegroStatusMappingRepository` @@ -291,8 +292,10 @@ - waliduje `state` i `code`, - wymienia `code` na tokeny przez `AllegroOAuthClient::exchangeAuthorizationCode(...)`, - zapisuje tokeny przez `AllegroIntegrationRepository::saveTokens(...)`. +- `AllegroTokenManager`: + - Shared Allegro OAuth token resolver. Checks expiry and refreshes via `AllegroOAuthClient` when needed. Injected into all Allegro service classes (`AllegroOrderImportService`, `AllegroOrdersSyncService`, `AllegroStatusDiscoveryService`, `AllegroShipmentService`). - `AllegroOrderImportService`: - - pilnuje waznosci tokenu (refresh przed requestem lub retry po `401`), + - pilnuje waznosci tokenu przez `AllegroTokenManager::resolveToken()` (refresh przed requestem lub retry po `401`), - pobiera zamowienie `GET /order/checkout-forms/{id}` przez `AllegroApiClient`, - pobiera przesylki zamowienia `GET /order/checkout-forms/{id}/shipments` przez `AllegroApiClient::getCheckoutFormShipments(...)`, - dla pozycji bez obrazka w checkout-form pobiera szczegoly oferty `GET /sale/product-offers/{offerId}` i uzupelnia `order_items.media_url`, diff --git a/database/migrations/20260312_000047_add_last_status_checked_at_to_orders.sql b/database/migrations/20260312_000047_add_last_status_checked_at_to_orders.sql index 632bbab..e54779a 100644 --- a/database/migrations/20260312_000047_add_last_status_checked_at_to_orders.sql +++ b/database/migrations/20260312_000047_add_last_status_checked_at_to_orders.sql @@ -1,8 +1,29 @@ -ALTER TABLE orders - ADD COLUMN last_status_checked_at DATETIME NULL DEFAULT NULL - COMMENT 'Timestamp ostatniego sprawdzenia statusu przez AllegroStatusSyncService' - AFTER source_updated_at; +-- Dodaj kolumnę jeśli nie istnieje +SET @col_exists = ( + SELECT COUNT(*) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'orders' + AND COLUMN_NAME = 'last_status_checked_at' +); +SET @sql = IF(@col_exists = 0, + 'ALTER TABLE orders ADD COLUMN last_status_checked_at DATETIME NULL DEFAULT NULL COMMENT ''Timestamp ostatniego sprawdzenia statusu przez AllegroStatusSyncService'' AFTER source_updated_at', + 'SELECT ''Column last_status_checked_at already exists, skipping.''' +); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; -- Indeks kompozytowy wspiera zapytanie filtrujące po source + sortujące po source_updated_at -ALTER TABLE orders - ADD INDEX orders_source_updated_idx (source, source_updated_at); +SET @idx_exists = ( + SELECT COUNT(*) FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'orders' + AND INDEX_NAME = 'orders_source_updated_idx' +); +SET @sql = IF(@idx_exists = 0, + 'ALTER TABLE orders ADD INDEX orders_source_updated_idx (source, source_updated_at)', + 'SELECT ''Index orders_source_updated_idx already exists, skipping.''' +); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; diff --git a/routes/web.php b/routes/web.php index 73374c0..76b0249 100644 --- a/routes/web.php +++ b/routes/web.php @@ -16,6 +16,7 @@ use App\Modules\Settings\AllegroIntegrationRepository; use App\Modules\Settings\AllegroOAuthClient; use App\Modules\Settings\AllegroOrderImportService; use App\Modules\Settings\AllegroStatusDiscoveryService; +use App\Modules\Settings\AllegroTokenManager; use App\Modules\Settings\AllegroStatusMappingRepository; use App\Modules\Settings\ApaczkaApiClient; use App\Modules\Settings\ApaczkaIntegrationController; @@ -57,6 +58,7 @@ return static function (Application $app): void { $allegroStatusMappingRepository = new AllegroStatusMappingRepository($app->db()); $carrierDeliveryMappings = new CarrierDeliveryMethodMappingRepository($app->db()); $allegroOAuthClient = new AllegroOAuthClient(); + $allegroTokenManager = new AllegroTokenManager($allegroIntegrationRepository, $allegroOAuthClient); $apaczkaApiClient = new ApaczkaApiClient(); $cronRepository = new CronRepository($app->db()); $apaczkaIntegrationRepository = new ApaczkaIntegrationRepository( @@ -74,15 +76,14 @@ return static function (Application $app): void { $allegroOAuthClient, new AllegroOrderImportService( $allegroIntegrationRepository, - $allegroOAuthClient, + $allegroTokenManager, new AllegroApiClient(), new OrderImportRepository($app->db()), $allegroStatusMappingRepository, new OrdersRepository($app->db()) ), new AllegroStatusDiscoveryService( - $allegroIntegrationRepository, - $allegroOAuthClient, + $allegroTokenManager, new AllegroApiClient(), $allegroStatusMappingRepository ), @@ -156,8 +157,7 @@ return static function (Application $app): void { $allegroApiClient = new AllegroApiClient(); $shipmentPackageRepository = new ShipmentPackageRepository($app->db()); $shipmentService = new AllegroShipmentService( - $allegroIntegrationRepository, - $allegroOAuthClient, + $allegroTokenManager, $allegroApiClient, $shipmentPackageRepository, $companySettingsRepository, diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..23a32a6 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,13 @@ +sonar.projectKey=orderPRO +sonar.projectName=orderPRO +sonar.projectVersion=1.0 + +sonar.host.url=https://sonar.project-pro.pl +sonar.token=sqp_8ef2748d037777cf00cf1b38534f8d435b762d7d + +sonar.sources=src,resources/views,routes +sonar.tests=tests +sonar.exclusions=archive/**,node_modules/**,vendor/**,public/assets/**,storage/** + +sonar.php.version=8.1 +sonar.sourceEncoding=UTF-8 diff --git a/src/Modules/Settings/AllegroStatusDiscoveryService.php b/src/Modules/Settings/AllegroStatusDiscoveryService.php index 762b6e9..7c6d2cc 100644 --- a/src/Modules/Settings/AllegroStatusDiscoveryService.php +++ b/src/Modules/Settings/AllegroStatusDiscoveryService.php @@ -3,16 +3,12 @@ declare(strict_types=1); namespace App\Modules\Settings; -use DateInterval; -use DateTimeImmutable; use RuntimeException; -use Throwable; final class AllegroStatusDiscoveryService { public function __construct( - private readonly AllegroIntegrationRepository $integrationRepository, - private readonly AllegroOAuthClient $oauthClient, + private readonly AllegroTokenManager $tokenManager, private readonly AllegroApiClient $apiClient, private readonly AllegroStatusMappingRepository $statusMappings ) { @@ -23,8 +19,7 @@ final class AllegroStatusDiscoveryService */ public function discoverAndStoreStatuses(int $maxPages = 3, int $pageLimit = 100): array { - $oauth = $this->requireOAuthData(); - [$accessToken, $oauth] = $this->resolveAccessToken($oauth); + [$accessToken, $env] = $this->tokenManager->resolveToken(); $unique = []; $safePages = max(1, min(10, $maxPages)); @@ -34,23 +29,13 @@ final class AllegroStatusDiscoveryService for ($page = 0; $page < $safePages; $page++) { try { - $response = $this->apiClient->listCheckoutForms( - (string) ($oauth['environment'] ?? 'sandbox'), - $accessToken, - $safeLimit, - $offset - ); + $response = $this->apiClient->listCheckoutForms($env, $accessToken, $safeLimit, $offset); } catch (RuntimeException $exception) { if (trim($exception->getMessage()) !== 'ALLEGRO_HTTP_401') { throw $exception; } - [$accessToken, $oauth] = $this->forceRefreshToken($oauth); - $response = $this->apiClient->listCheckoutForms( - (string) ($oauth['environment'] ?? 'sandbox'), - $accessToken, - $safeLimit, - $offset - ); + [$accessToken, $env] = $this->tokenManager->resolveToken(); + $response = $this->apiClient->listCheckoutForms($env, $accessToken, $safeLimit, $offset); } $forms = is_array($response['checkoutForms'] ?? null) ? $response['checkoutForms'] : []; @@ -87,91 +72,6 @@ final class AllegroStatusDiscoveryService ]; } - /** - * @return array - */ - private function requireOAuthData(): array - { - $oauth = $this->integrationRepository->getTokenCredentials(); - if ($oauth === null) { - throw new RuntimeException('Brak kompletnych danych OAuth Allegro. Polacz konto ponownie.'); - } - - return $oauth; - } - - /** - * @param array $oauth - * @return array{0:string, 1:array} - */ - private function resolveAccessToken(array $oauth): array - { - $tokenExpiresAt = trim((string) ($oauth['token_expires_at'] ?? '')); - $accessToken = trim((string) ($oauth['access_token'] ?? '')); - if ($accessToken === '') { - return $this->forceRefreshToken($oauth); - } - - if ($tokenExpiresAt === '') { - return [$accessToken, $oauth]; - } - - try { - $expiresAt = new DateTimeImmutable($tokenExpiresAt); - } catch (Throwable) { - return $this->forceRefreshToken($oauth); - } - - if ($expiresAt <= (new DateTimeImmutable('now'))->add(new DateInterval('PT5M'))) { - return $this->forceRefreshToken($oauth); - } - - return [$accessToken, $oauth]; - } - - /** - * @param array $oauth - * @return array{0:string, 1:array} - */ - private function forceRefreshToken(array $oauth): array - { - $token = $this->oauthClient->refreshAccessToken( - (string) ($oauth['environment'] ?? 'sandbox'), - (string) ($oauth['client_id'] ?? ''), - (string) ($oauth['client_secret'] ?? ''), - (string) ($oauth['refresh_token'] ?? '') - ); - - $expiresAt = null; - $expiresIn = max(0, (int) ($token['expires_in'] ?? 0)); - if ($expiresIn > 0) { - $expiresAt = (new DateTimeImmutable('now')) - ->add(new DateInterval('PT' . $expiresIn . 'S')) - ->format('Y-m-d H:i:s'); - } - - $refreshToken = trim((string) ($token['refresh_token'] ?? '')); - if ($refreshToken === '') { - $refreshToken = (string) ($oauth['refresh_token'] ?? ''); - } - - $this->integrationRepository->saveTokens( - (string) ($token['access_token'] ?? ''), - $refreshToken, - (string) ($token['token_type'] ?? ''), - (string) ($token['scope'] ?? ''), - $expiresAt - ); - - $updatedOauth = $this->requireOAuthData(); - $newAccessToken = trim((string) ($updatedOauth['access_token'] ?? '')); - if ($newAccessToken === '') { - throw new RuntimeException('Nie udalo sie zapisac odswiezonego tokenu Allegro.'); - } - - return [$newAccessToken, $updatedOauth]; - } - private function prettifyStatusName(string $statusCode): string { $normalized = str_replace(['_', '-'], ' ', strtolower(trim($statusCode)));