This commit is contained in:
2026-03-13 01:00:28 +01:00
parent 7b29fd9e02
commit 9a0dbe050b
13 changed files with 650 additions and 199 deletions

39
.claude/sessionstate.md Normal file
View File

@@ -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

View File

@@ -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

View File

@@ -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*

0
.scannerwork/.sonar_lock Normal file
View File

View File

@@ -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

4
.vscode/ftp-kr.json vendored
View File

@@ -14,6 +14,8 @@
".git",
"/.vscode",
"/.claude",
".gitignore"
".gitignore",
"/.scannerwork",
"/.paul"
]
}

View File

@@ -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
}
}
},

6
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"sonarlint.connectedMode.project": {
"connectionId": "sonar-project-pro-pl",
"projectKey": "orderPRO"
}
}

View File

@@ -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`,

View File

@@ -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;

View File

@@ -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,

13
sonar-project.properties Normal file
View File

@@ -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

View File

@@ -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<string, string>
*/
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<string, string> $oauth
* @return array{0:string, 1:array<string, string>}
*/
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<string, string> $oauth
* @return array{0:string, 1:array<string, string>}
*/
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)));