diff --git a/.paul/HANDOFF-2026-03-13.md b/.paul/HANDOFF-2026-03-13.md new file mode 100644 index 0000000..08da179 --- /dev/null +++ b/.paul/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). Generowanie etykiet kurierskich. +**Core value:** Sprzedawca obsługuje wszystkie kanały i nadaje przesyłki bez przełączania platform. + +--- + +## Current State + +**Version:** v0.1.0 (In Progress) +**Phase:** 6 of TBD — 06-sonarqube-quality +**Plan:** 06-01..06-06 — CREATED, awaiting approval + +**Loop Position:** +``` +PLAN ──▶ APPLY ──▶ UNIFY + ✓ ○ ○ [6 planów gotowych, żaden nie wykonany] +``` + +--- + +## What Was Done + +- Przeanalizowano 327 issues SonarQube z CONCERNS.md i DOCS/todo.md +- Zbadano kluczowe pliki (AllegroIntegrationController 923L, ShopproIntegrationsController 901L, ShopproOrdersSyncService 1192L, OrdersRepository 785L) +- Utworzono 6 planów dla fazy 06-sonarqube-quality: + - `06-01-PLAN.md` — php:S112 (95x) — typowane wyjątki zamiast RuntimeException + - `06-02-PLAN.md` — php:S1142 (57x) — redukcja return statements (save() z 5→≤3) + - `06-03-PLAN.md` — php:S1192 (40x) — ekstrakcja literałów do stałych (IntegrationSources, RedirectPaths) + - `06-04-PLAN.md` — php:S3776 (31x) — redukcja złożoności kognitywnej (extract method) + - `06-05-PLAN.md` — php:S1448 (6x) — podział god classes (ShopproOrdersSyncService 39→≤20 metod, AllegroIntegrationController 30→≤15) + - `06-06-PLAN.md` — php:S138 (4x) — skrócenie długich metod (sync 195L, paginate 183L) +- Zaktualizowano ROADMAP.md (Phase 6 dodana) +- Zaktualizowano STATE.md + +--- + +## What's In Progress + +- Żaden plan nie jest w trakcie — wszystkie 6 czeka na zatwierdzenie i APPLY + +--- + +## What's Next + +**Immediate:** Wybierz plan do wykonania i uruchom `/paul:apply .paul/phases/06-sonarqube-quality/06-XX-PLAN.md` + +**Rekomendowana kolejność:** +1. `06-01` (php:S112) — najprostsza zmiana, nowe klasy + podmiana throw +2. `06-03` (php:S1192) — quick win, stałe zamiast literałów +3. `06-02` (php:S1142) — refaktoryzacja metod w 2 kontrolerach +4. `06-06` (php:S138) — skrócenie długich metod +5. `06-04` (php:S3776) — redukcja złożoności (extract method w god classes) +6. `06-05` (php:S1448) — największa zmiana: podział klas (ma checkpoint:human-verify) + +**Zależności między planami:** +- `06-01, 06-02, 06-03` — niezależne, wave 1 +- `06-04` — niezależny, wave 2 (lepiej przed 06-05) +- `06-05` — `depends_on: ["06-04"]`, wave 3, nie jest autonomous (ma human-verify) +- `06-06` — wave 2, uważaj na interferencję z 06-05 w ShopproOrdersSyncService + +--- + +## Key Files + +| File | Purpose | +|------|---------| +| `.paul/STATE.md` | Live project state | +| `.paul/ROADMAP.md` | Phase overview | +| `.paul/phases/06-sonarqube-quality/06-01-PLAN.md` | S112: Typowane wyjątki | +| `.paul/phases/06-sonarqube-quality/06-02-PLAN.md` | S1142: Redukcja return | +| `.paul/phases/06-sonarqube-quality/06-03-PLAN.md` | S1192: Stałe dla literałów | +| `.paul/phases/06-sonarqube-quality/06-04-PLAN.md` | S3776: Złożoność kognitywna | +| `.paul/phases/06-sonarqube-quality/06-05-PLAN.md` | S1448: Podział god classes | +| `.paul/phases/06-sonarqube-quality/06-06-PLAN.md` | S138: Długie metody | +| `.paul/codebase/CONCERNS.md` | Pełna lista concerns (tech debt, bugs, performance) | +| `DOCS/todo.md` | Lista TODO z SonarQube issues | + +--- + +## Resume Instructions + +1. Przeczytaj `.paul/STATE.md` — potwierdź pozycję w loop +2. Wybierz plan (rekomendowane: 06-01 lub 06-03) +3. Uruchom `/paul:apply .paul/phases/06-sonarqube-quality/06-01-PLAN.md` + +--- + +*Handoff created: 2026-03-13* diff --git a/.paul/ROADMAP.md b/.paul/ROADMAP.md index 84204a1..6a21f96 100644 --- a/.paul/ROADMAP.md +++ b/.paul/ROADMAP.md @@ -8,7 +8,7 @@ orderPRO to narzędzie do wielokanałowego zarządzania sprzedażą. Projekt prz **v0.1 Initial Release** (v0.1.0) Status: In progress -Phases: 3 complete, Phase 4 planning +Phases: 5 complete, Phase 6 planning ## Phases @@ -19,6 +19,7 @@ Phases: 3 complete, Phase 4 planning | 3 | Tech Debt 2 | 1/1 | ✅ Complete | 2026-03-13 | | 4 | Schema Docs | 1/1 | ✅ Complete | 2026-03-13 | | 5 | Tech Debt 3 | 1/1 | ✅ Complete | 2026-03-13 | +| 6 | SonarQube Quality | 0/6 | 🔄 Planning | — | ## Phase Details @@ -51,6 +52,16 @@ Migracja flash messages z bezpośrednich zapisów `$_SESSION` do abstrakcji `Fla - **Plan 05-01** — Migrate $_SESSION flash writes to Flash class in OrdersController and ShipmentController — *Complete* +### Phase 6 — SonarQube Quality +Eliminacja 327 issues SonarQube (php:S112, S1142, S1192, S3776, S1448, S138) przez typowane wyjątki, ekstrakcję stałych, redukcję złożoności i podział god classes. + +- **Plan 06-01** — php:S112 (95x) — Typowane klasy wyjątków zamiast RuntimeException — *Not started* +- **Plan 06-02** — php:S1142 (57x) — Redukcja liczby return w metodach — *Not started* +- **Plan 06-03** — php:S1192 (40x) — Ekstrakcja powtarzających się literałów do stałych — *Not started* +- **Plan 06-04** — php:S3776 (31x) — Redukcja złożoności kognitywnej metod — *Not started* +- **Plan 06-05** — php:S1448 (6x) — Podział god classes (ShopproOrdersSyncService, AllegroIntegrationController) — *Not started* +- **Plan 06-06** — php:S138 (4x) — Skrócenie zbyt długich metod — *Not started* + --- *Roadmap created: 2026-03-12* -*Last updated: 2026-03-13 after Phase 5 (Tech Debt 3 — Flash migration)* +*Last updated: 2026-03-13 after Phase 6 plans created (SonarQube Quality — 6 plans)* diff --git a/.paul/STATE.md b/.paul/STATE.md index bbff911..0addef2 100644 --- a/.paul/STATE.md +++ b/.paul/STATE.md @@ -10,10 +10,10 @@ See: .paul/PROJECT.md (updated 2026-03-12) ## Current Position Milestone: v0.1 Initial Release -Phase: 5 of TBD (05-tech-debt-3) — Complete -Plan: 05-01 complete -Status: Phase 5 complete — ready for next phase -Last activity: 2026-03-13 — Phase 05 complete (05-01-SUMMARY.md) +Phase: 6 of TBD (06-sonarqube-quality) — Planning +Plan: 06-01..06-06 created, awaiting approval +Status: PLAN created (6 plans), ready for APPLY +Last activity: 2026-03-13 — Created .paul/phases/06-sonarqube-quality/06-01..06-PLAN.md Progress: - Milestone: [███████░░░] ~65% @@ -22,13 +22,14 @@ Progress: - Phase 3: [██████████] 100% (1/1 plans complete) - Phase 4: [██████████] 100% (1/1 plans complete) - Phase 5: [██████████] 100% (1/1 plans complete) +- Phase 6: [░░░░░░░░░░] 0% (0/6 plans complete) ## Loop Position Current loop state: ``` PLAN ──▶ APPLY ──▶ UNIFY - ✓ ✓ ✓ [Phase 05 complete — ready for Phase 6 PLAN] + ✓ ○ ○ [6 planów utworzonych, oczekują zatwierdzenia] ``` ## Accumulated Context @@ -75,7 +76,7 @@ PLAN ──▶ APPLY ──▶ UNIFY - **code-review** — wywołać /code-review przed kolejnym UNIFY (pominięto w obydwu planach fazy 01). ### Git State -Last commit: 880ab59 +Last commit: 2d4b52a (Phase 04+05 — schema docs + Flash migration) Branch: main Feature branches merged: none @@ -85,9 +86,13 @@ Brak. ## Session Continuity Last session: 2026-03-13 -Stopped at: Phase 05 complete -Next action: /paul:plan dla Phase 6 -Resume file: .paul/phases/05-tech-debt-3/05-01-SUMMARY.md +Stopped at: Phase 06 — 6 planów SonarQube Quality utworzonych, żaden nie wykonany +Next action: /paul:apply .paul/phases/06-sonarqube-quality/06-01-PLAN.md (lub 06-03 jako quick win) +Resume file: .paul/HANDOFF-2026-03-13.md +Resume context: +- Faza 06 ma 6 planów (06-01..06-06) wszystkie gotowe w .paul/phases/06-sonarqube-quality/ +- Rekomendowana kolejność: 06-01 → 06-03 → 06-02 → 06-06 → 06-04 → 06-05 +- 06-05 (god classes) zależy od 06-04 i ma checkpoint:human-verify (nie autonomous) --- *STATE.md — Updated after every significant action* diff --git a/.paul/phases/06-sonarqube-quality/06-01-PLAN.md b/.paul/phases/06-sonarqube-quality/06-01-PLAN.md new file mode 100644 index 0000000..bdf148a --- /dev/null +++ b/.paul/phases/06-sonarqube-quality/06-01-PLAN.md @@ -0,0 +1,251 @@ +--- +phase: 06-sonarqube-quality +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - src/Core/Exceptions/OrderProException.php + - src/Core/Exceptions/AllegroApiException.php + - src/Core/Exceptions/AllegroOAuthException.php + - src/Core/Exceptions/ApaczkaApiException.php + - src/Core/Exceptions/ShipmentException.php + - src/Core/Exceptions/IntegrationConfigException.php + - src/Modules/Settings/AllegroApiClient.php + - src/Modules/Settings/AllegroOAuthClient.php + - src/Modules/Settings/AllegroTokenManager.php + - src/Modules/Settings/AllegroIntegrationRepository.php + - src/Modules/Settings/AllegroIntegrationController.php + - src/Modules/Settings/AllegroOrderImportService.php + - src/Modules/Settings/AllegroOrdersSyncService.php + - src/Modules/Settings/ApaczkaApiClient.php + - src/Modules/Settings/ApaczkaIntegrationRepository.php + - src/Modules/Shipments/ApaczkaShipmentService.php + - src/Modules/Shipments/AllegroShipmentService.php + - src/Modules/Shipments/ShipmentController.php + - src/Modules/Settings/IntegrationSecretCipher.php + - src/Modules/Settings/InpostIntegrationRepository.php + - src/Modules/Settings/ShopproIntegrationsRepository.php + - src/Modules/Settings/ShopproOrdersSyncService.php + - src/Modules/Settings/ShopproPaymentStatusSyncService.php +autonomous: true +--- + + +## Goal +Zastąpić 86+ wywołań `throw new RuntimeException()` dedykowanymi klasami wyjątków per moduł — eliminacja naruszeń SonarQube php:S112. + +## Purpose +SonarQube flaguje `RuntimeException` jako zbyt ogólny — łapanie `catch (RuntimeException)` nie mówi nic o tym, jaki błąd nastąpił. Typowane wyjątki umożliwiają precyzyjny catch w wywołującym kodzie i poprawiają czytelność stacktrace. + +## Output +Hierarchia klas wyjątków w `src/Core/Exceptions/` + podmienione throw w 13 plikach. Liczba S112 w SonarQube spada z 95 do ~5 (pozostałości w Core). + + + +## Project Context +@.paul/PROJECT.md +@.paul/STATE.md + +## Source Files +@src/Modules/Settings/AllegroApiClient.php +@src/Modules/Settings/AllegroOAuthClient.php +@src/Modules/Settings/AllegroTokenManager.php +@src/Modules/Shipments/ApaczkaShipmentService.php +@src/Modules/Shipments/AllegroShipmentService.php +@src/Modules/Settings/ApaczkaApiClient.php +@src/Modules/Settings/IntegrationSecretCipher.php + + + +## Required Skills (from SPECIAL-FLOWS.md) + +| Skill | Priority | When to Invoke | Loaded? | +|-------|----------|----------------|---------| +| sonar-scanner | required | Po APPLY, przed UNIFY | ○ | + +## Skill Invocation Checklist +- [ ] sonar-scanner uruchomiony po zakończeniu APPLY (CLI w katalogu projektu) + + + + +## AC-1: Hierarchia wyjątków istnieje +```gherkin +Given brak folderu src/Core/Exceptions/ +When plan zostaje wykonany +Then folder src/Core/Exceptions/ istnieje z klasami: OrderProException, AllegroApiException, AllegroOAuthException, ApaczkaApiException, ShipmentException, IntegrationConfigException +``` + +## AC-2: Allegro-specific throws podmienione +```gherkin +Given AllegroApiClient, AllegroOAuthClient, AllegroTokenManager rzucają RuntimeException +When plan zostaje wykonany +Then wszystkie throw w tych plikach używają AllegroApiException lub AllegroOAuthException +``` + +## AC-3: Apaczka-specific throws podmienione +```gherkin +Given ApaczkaApiClient, ApaczkaShipmentService, ApaczkaIntegrationRepository rzucają RuntimeException +When plan zostaje wykonany +Then wszystkie throw w tych plikach używają ApaczkaApiException +``` + +## AC-4: Shipment throws podmienione +```gherkin +Given AllegroShipmentService, ShipmentController rzucają RuntimeException +When plan zostaje wykonany +Then wszystkie throw używają ShipmentException +``` + +## AC-5: Brak regresji +```gherkin +Given aplikacja działa przed zmianą +When klasy wyjątków rozszerzają RuntimeException (łańcuch dziedziczenia zachowany) +Then istniejące catch (RuntimeException) w kodzie wywołującym nadal działają +``` + + + + + + + Task 1: Utwórz hierarchię klas wyjątków + + src/Core/Exceptions/OrderProException.php, + src/Core/Exceptions/AllegroApiException.php, + src/Core/Exceptions/AllegroOAuthException.php, + src/Core/Exceptions/ApaczkaApiException.php, + src/Core/Exceptions/ShipmentException.php, + src/Core/Exceptions/IntegrationConfigException.php + + + Utwórz folder src/Core/Exceptions/ i pliki klas z namespace App\Core\Exceptions. + + Hierarchia: + - OrderProException extends \RuntimeException — baza dla wszystkich własnych wyjątków + - AllegroApiException extends OrderProException — błędy HTTP/JSON Allegro API i OAuth + - AllegroOAuthException extends AllegroApiException — specyficznie błędy OAuth (token refresh, brak tokenów) + - ApaczkaApiException extends OrderProException — błędy API Apaczka (HTTP, curl, payload) + - ShipmentException extends OrderProException — błędy tworzenia/pobierania przesyłek (brak zamówienia, brak providera, brak paczki) + - IntegrationConfigException extends OrderProException — błędy konfiguracji integracji (brak rekordu w DB, brak klucza API, brak sekretu) + + Każda klasa: minimalna (tylko class declaration + extends). Brak dodatkowych metod — nie powielać logiki. + Nie dodawaj konstruktorów ani innych metod — klasy wyjątków mają być tylko markerami. + + php -l src/Core/Exceptions/OrderProException.php (i pozostałe 5 plików) — każdy zwraca "No syntax errors" + AC-1 satisfied: folder src/Core/Exceptions/ z 6 klasami istnieje i jest poprawny składniowo + + + + Task 2: Podmień RuntimeException w plikach Allegro + + src/Modules/Settings/AllegroApiClient.php, + src/Modules/Settings/AllegroOAuthClient.php, + src/Modules/Settings/AllegroTokenManager.php, + src/Modules/Settings/AllegroIntegrationRepository.php, + src/Modules/Settings/AllegroIntegrationController.php, + src/Modules/Settings/AllegroOrderImportService.php, + src/Modules/Settings/AllegroOrdersSyncService.php + + + W każdym pliku: + 1. Dodaj use App\Core\Exceptions\AllegroApiException; (i AllegroOAuthException tam gdzie dotyczy OAuth) + 2. Zastąp throw new RuntimeException(...) odpowiednią klasą: + - AllegroApiClient.php (18x): HTTP/JSON/curl błędy → AllegroApiException; błędy OAuth (ALLEGRO_HTTP_401) → AllegroApiException + - AllegroOAuthClient.php (6x): wszystkie → AllegroOAuthException + - AllegroTokenManager.php (3x): brak połączenia OAuth, brak danych, niepowodzenie refresh → AllegroOAuthException + - AllegroIntegrationRepository.php (1x): brak rekordu → IntegrationConfigException (użyj use App\Core\Exceptions\IntegrationConfigException) + - AllegroIntegrationController.php (1x): brak credentials → IntegrationConfigException + - AllegroOrderImportService.php (2x): błędy importu → AllegroApiException + - AllegroOrdersSyncService.php (1x): brak aktywnej integracji → IntegrationConfigException + + Uwaga: NIE zmieniaj logiki catch bloków — tylko throw. NIE zmieniaj komunikatów błędów. + Uwaga: \RuntimeException w AllegroIntegrationController (jeśli catch) — NIE ruszać. + + + php -l src/Modules/Settings/AllegroApiClient.php + php -l src/Modules/Settings/AllegroOAuthClient.php + php -l src/Modules/Settings/AllegroTokenManager.php + grep -r "new RuntimeException" src/Modules/Settings/Allegro*.php — powinno zwrócić 0 wyników + + AC-2 satisfied: pliki Allegro nie zawierają throw new RuntimeException + + + + Task 3: Podmień RuntimeException w plikach Apaczka, Shipment i pozostałych + + src/Modules/Settings/ApaczkaApiClient.php, + src/Modules/Settings/ApaczkaIntegrationRepository.php, + src/Modules/Shipments/ApaczkaShipmentService.php, + src/Modules/Shipments/AllegroShipmentService.php, + src/Modules/Shipments/ShipmentController.php, + src/Modules/Settings/IntegrationSecretCipher.php, + src/Modules/Settings/InpostIntegrationRepository.php, + src/Modules/Settings/ShopproIntegrationsRepository.php, + src/Modules/Settings/ShopproOrdersSyncService.php, + src/Modules/Settings/ShopproPaymentStatusSyncService.php + + + Podmień RuntimeException: + - ApaczkaApiClient.php (9x): curl/HTTP/JSON błędy → ApaczkaApiException + - ApaczkaIntegrationRepository.php (4x): brak rekordu/klucza → IntegrationConfigException + - ApaczkaShipmentService.php (15x): brak zamówienia/paczki/etykiety → ShipmentException; brak konfiguracji (brak app_id) → IntegrationConfigException; brak danych nadawcy → IntegrationConfigException + - AllegroShipmentService.php (8x): brak zamówienia/paczki/przesyłki → ShipmentException; brak danych nadawcy → IntegrationConfigException + - ShipmentController.php (3x): nieznany provider, brak paczki, brak providera → ShipmentException + - IntegrationSecretCipher.php (3x): brak sekretu, błąd szyfrowania → IntegrationConfigException + - InpostIntegrationRepository.php (1x): brak rekordu → IntegrationConfigException + - ShopproIntegrationsRepository.php (1x): INTEGRATION_NOT_FOUND → IntegrationConfigException + - ShopproOrdersSyncService.php (2x): brak danych API, błąd pobierania zamówień → \RuntimeException (zostawić — są wewnątrz try-catch i message pochodzi z zewnętrznego API) + - ShopproPaymentStatusSyncService.php (1x): zostawić jako \RuntimeException (message z zewnętrznego API) + + W każdym pliku dodaj odpowiednie use statements na górze. + NIE zmieniaj logiki catch — tylko throw. + + + php -l src/Modules/Shipments/ApaczkaShipmentService.php + php -l src/Modules/Shipments/AllegroShipmentService.php + php -l src/Modules/Settings/ApaczkaApiClient.php + grep -rn "new RuntimeException" src/ — powinno zwrócić maks. 5 wyników (ShopproOrdersSyncService, ShopproPaymentStatusSyncService, CronRunner, AllegroTokenRefreshHandler — intentional pozostałości) + + AC-3, AC-4, AC-5 satisfied: Apaczka i Shipment pliki używają typowanych wyjątków; łańcuch dziedziczenia od RuntimeException zachowany + + + + + + +## DO NOT CHANGE +- Bloki catch (RuntimeException) w controllers/handlers — łańcuch dziedziczenia zachowuje kompatybilność, ale NIE ruszaj istniejących catch +- Komunikaty błędów w throw — tylko typ wyjątku się zmienia +- src/Core/Exceptions/ — nie tworzyć tu nic poza 6 klasami z tego planu +- routes/web.php +- Pliki widoków (resources/views/) + +## SCOPE LIMITS +- Nie refaktoryzuj logiki poza podmianą throw +- Nie dodawaj konstruktorów ani metod do klas wyjątków +- Nie zmieniaj sposobu obsługi błędów w UI (flash messages, HTTP redirecty) +- Core wyjątki (Router.php, Template.php, Translator.php, ConnectionFactory.php, Migrator.php) — zostawić jako RuntimeException (mają sens jako generyczne) + + + + +Przed zamknięciem planu: +- [ ] php -l na wszystkich zmodyfikowanych plikach PHP — zero błędów składniowych +- [ ] grep -rn "new RuntimeException" src/ — max 5 wyników (tylko Core + 2 Shoppro intentional) +- [ ] ls src/Core/Exceptions/ — 6 plików: OrderProException, AllegroApiException, AllegroOAuthException, ApaczkaApiException, ShipmentException, IntegrationConfigException +- [ ] Aplikacja startuje bez błędów (wejście na stronę główną, brak PHP fatal errors w logach) +- [ ] sonar-scanner uruchomiony — sprawdź czy S112 violations zmalały + + + +- Wszystkie 3 taski ukończone +- Zero błędów składniowych PHP +- grep "new RuntimeException" src/ zwraca ≤5 wyników +- SonarQube S112 spada z 95 do ≤10 + + + +Po zakończeniu utwórz `.paul/phases/06-sonarqube-quality/06-01-SUMMARY.md` + diff --git a/.paul/phases/06-sonarqube-quality/06-02-PLAN.md b/.paul/phases/06-sonarqube-quality/06-02-PLAN.md new file mode 100644 index 0000000..2337546 --- /dev/null +++ b/.paul/phases/06-sonarqube-quality/06-02-PLAN.md @@ -0,0 +1,190 @@ +--- +phase: 06-sonarqube-quality +plan: 02 +type: execute +wave: 1 +depends_on: [] +files_modified: + - src/Modules/Settings/AllegroIntegrationController.php + - src/Modules/Settings/ShopproIntegrationsController.php +autonomous: true +--- + + +## Goal +Zredukować liczbę `return` w metodach do maksymalnie 3 — eliminacja naruszeń SonarQube php:S1142 (57 wystąpień). + +## Purpose +Metody z 4+ returnami (np. `save()` z 5 returnami) są trudne do śledzenia — nie wiadomo jaką ścieżką wróciła wartość. Wydzielenie walidacji do osobnych metod upraszcza główną ścieżkę logiki. + +## Output +`AllegroIntegrationController` i `ShopproIntegrationsController` z metodami o max 3 return statements. SonarQube S1142 spada o ~15-20 violations. + + + +## Project Context +@.paul/PROJECT.md + +## Source Files +@src/Modules/Settings/AllegroIntegrationController.php +@src/Modules/Settings/ShopproIntegrationsController.php + + + +## Required Skills (from SPECIAL-FLOWS.md) + +| Skill | Priority | When to Invoke | Loaded? | +|-------|----------|----------------|---------| +| sonar-scanner | required | Po APPLY, przed UNIFY | ○ | + +## Skill Invocation Checklist +- [ ] sonar-scanner uruchomiony po zakończeniu APPLY + + + + +## AC-1: AllegroIntegrationController::save() — max 3 return +```gherkin +Given save() ma 5 return statements (walidacja CSRF, URL, credentials, dates, etc.) +When refaktoryzujesz przez wydzielenie walidacji do private validateSaveInput(): ?string +Then save() ma ≤3 return statements; zachowanie identyczne +``` + +## AC-2: AllegroIntegrationController::saveImportSettings() — max 3 return +```gherkin +Given saveImportSettings() ma 4 return statements +When wydzielasz walidację do private validateImportSettingsInput(): ?string +Then saveImportSettings() ma ≤3 return statements +``` + +## AC-3: AllegroIntegrationController::oauthCallback() — max 3 return +```gherkin +Given oauthCallback() ma 4 return statements (walidacja state, code, brak credentials, etc.) +When wydzielasz walidację parametrów do private validateOAuthCallbackParams(): ?string +Then oauthCallback() ma ≤3 return statements +``` + +## AC-4: ShopproIntegrationsController::save() — max 3 return +```gherkin +Given save() ma 5 return statements +When wydzielasz walidację do private validateSaveInput(): ?string +Then save() ma ≤3 return statements +``` + +## AC-5: Brak regresji funkcjonalnej +```gherkin +Given formularze integracji Allegro i Shoppro działają przed zmianą +When refaktoryzacja jest czysto strukturalna (ta sama logika, inne rozłożenie) +Then wszystkie walidacje nadal działają, komunikaty błędów identyczne +``` + + + + + + + Task 1: Refaktoryzacja AllegroIntegrationController — redukcja return w save(), saveImportSettings(), oauthCallback() + src/Modules/Settings/AllegroIntegrationController.php + + Przeczytaj dokładnie trzy metody: save(), saveImportSettings(), oauthCallback(). + + **Wzorzec refaktoryzacji (stosuj w każdej metodzie):** + - Wydziel bloki walidacji (if + return z błędem) do private helper: validateXxxInput(array $input): ?string + - Helper zwraca komunikat błędu lub null (gdy wszystko OK) + - W głównej metodzie: $error = $this->validateXxxInput($data); if ($error !== null) { [flash + redirect z $error]; return; } + - Dzięki temu główna metoda ma: 1 return po walidacji + 1 return po logice = 2 return zamiast 5 + + **save() (linia ~115):** + - Wydziel do private validateSaveInput(array $data): ?string + - Walidacje do wydzielenia: CSRF check, URL validation, credentials check, date validations + - Zwraca pierwszy napotkany błąd jako string, lub null + + **saveImportSettings() (linia ~169):** + - Wydziel do private validateImportSettingsInput(array $data): ?string + - Walidacje: CSRF, date range checks, inne + + **oauthCallback() (linia ~403):** + - Wydziel do private validateOAuthCallbackParams(array $params): ?string + - Walidacje: state parameter, code parameter, CSRF state match + + Zasady: + - NIE zmieniaj logiki biznesowej — tylko strukturę + - NIE zmieniaj komunikatów błędów ani flash keys + - NIE zmieniaj redirect paths + - Nowe private metody dodaj na końcu klasy, przed ostatnim } + + + php -l src/Modules/Settings/AllegroIntegrationController.php + grep -c "return" w obrębie save(), saveImportSettings(), oauthCallback() — każda ≤3 + + AC-1, AC-2, AC-3 satisfied: trzy metody mają ≤3 return statements; składnia poprawna + + + + Task 2: Refaktoryzacja ShopproIntegrationsController — redukcja return w save() i innych metodach + src/Modules/Settings/ShopproIntegrationsController.php + + Przeczytaj dokładnie: save(), test(), saveStatusMappings(), syncStatuses(). + + **save() (linia ~112):** + - Wydziel do private validateSaveInput(array $data): ?string + - Walidacje: CSRF, URL, credentials, data range checks + - Docelowo: ≤3 return w save() + + **test() (linia ~202):** + - 3 return — sprawdź czy można zredukować do 2 przez wydzielenie warunków wejściowych + - Jeśli nie da się bez utraty czytelności, zostaw (SonarQube limit to 3 — akceptowalne) + + **saveStatusMappings() (linia ~238):** + - Jeśli ma 4+ return: wydziel wstępną walidację + + **syncStatuses() (linia ~301):** + - Jeśli ma 4+ return: wydziel walidację + + Stosuj ten sam wzorzec co Task 1: private validateXxxInput(): ?string. + Nowe private metody dodaj na końcu klasy. + NIE zmieniaj logiki, komunikatów, redirect paths. + + + php -l src/Modules/Settings/ShopproIntegrationsController.php + Ręczne przeliczenie: save(), saveStatusMappings(), syncStatuses() — każda ≤3 return + + AC-4 satisfied: ShopproIntegrationsController::save() i inne metody mają ≤3 return statements + + + + + + +## DO NOT CHANGE +- Logika biznesowa (warunki, komunikaty błędów, redirect paths, flash keys) +- Inne metody poza wymienionymi — nie ruszaj +- Pliki widoków, routes/web.php, inne kontrolery + +## SCOPE LIMITS +- Refaktoryzacja strukturalna tylko w 2 plikach +- Nowe private helper methods — tylko walidacja (nie wydzielaj logiki zapisu do bazy) +- Nie wydzielaj metod do osobnych klas (to jest zakres planu 06-05) +- Nie zmieniaj visibility metod (public/private) + + + + +Przed zamknięciem planu: +- [ ] php -l src/Modules/Settings/AllegroIntegrationController.php — brak błędów +- [ ] php -l src/Modules/Settings/ShopproIntegrationsController.php — brak błędów +- [ ] Ręczne sprawdzenie: save() i oauthCallback() w AllegroIntegrationController — ≤3 return każda +- [ ] Ręczne sprawdzenie: save() w ShopproIntegrationsController — ≤3 return +- [ ] sonar-scanner uruchomiony — S1142 violations zmalały + + + +- Oba pliki bez błędów składniowych +- save(), saveImportSettings(), oauthCallback() (Allegro) — ≤3 return każda +- save() (Shoppro) — ≤3 return +- SonarQube S1142 spada o min. 10 violations + + + +Po zakończeniu utwórz `.paul/phases/06-sonarqube-quality/06-02-SUMMARY.md` + diff --git a/.paul/phases/06-sonarqube-quality/06-03-PLAN.md b/.paul/phases/06-sonarqube-quality/06-03-PLAN.md new file mode 100644 index 0000000..64752b1 --- /dev/null +++ b/.paul/phases/06-sonarqube-quality/06-03-PLAN.md @@ -0,0 +1,231 @@ +--- +phase: 06-sonarqube-quality +plan: 03 +type: execute +wave: 1 +depends_on: [] +files_modified: + - src/Core/Constants/IntegrationSources.php + - src/Core/Constants/RedirectPaths.php + - src/Modules/Settings/AllegroIntegrationController.php + - src/Modules/Settings/ShopproIntegrationsController.php + - src/Modules/Orders/OrdersRepository.php + - src/Modules/Settings/AllegroOrdersSyncService.php + - src/Modules/Settings/AllegroStatusSyncService.php + - src/Modules/Settings/AllegroOrderImportService.php + - src/Modules/Settings/ShopproOrdersSyncService.php + - src/Modules/Settings/ShopproStatusSyncService.php + - src/Modules/Settings/ShopproPaymentStatusSyncService.php +autonomous: true +--- + + +## Goal +Wyciągnąć 40+ powtarzających się literałów string do stałych — eliminacja naruszeń SonarQube php:S1192. + +## Purpose +Ciągi jak `'allegro'` (23x), `'shoppro'` (15x), redirect paths (5-6x) i identyfikatory statusów rozsiane po całym kodzie to pułapki na literówki i utrudnienie przy refaktoryzacji. Stałe zapewniają jedno miejsce zmiany i wykrycie błędu przez IDE. + +## Output +Dwie klasy stałych w `src/Core/Constants/`, wszystkie powtórzenia zastąpione — S1192 spada z 40 do ~5. + + + +## Project Context +@.paul/PROJECT.md + +## Source Files +@src/Modules/Orders/OrdersRepository.php +@src/Modules/Settings/AllegroIntegrationController.php +@src/Modules/Settings/ShopproIntegrationsController.php + + + +## Required Skills (from SPECIAL-FLOWS.md) + +| Skill | Priority | When to Invoke | Loaded? | +|-------|----------|----------------|---------| +| sonar-scanner | required | Po APPLY, przed UNIFY | ○ | + +## Skill Invocation Checklist +- [ ] sonar-scanner uruchomiony po zakończeniu APPLY + + + + +## AC-1: Klasy stałych istnieją +```gherkin +Given brak src/Core/Constants/ +When plan zostaje wykonany +Then src/Core/Constants/IntegrationSources.php zawiera ALLEGRO, SHOPPRO, APACZKA, INPOST + src/Core/Constants/RedirectPaths.php zawiera stałe dla ścieżek integracji +``` + +## AC-2: Identyfikatory źródeł podmienione +```gherkin +Given 'allegro' (23x), 'shoppro' (15x), 'apaczka' (10x), 'inpost' (7x) rozsiane w src/ +When plan zostaje wykonany +Then wszystkie wystąpienia w plikach PHP (poza SQL query strings) używają IntegrationSources::ALLEGRO etc. +``` + +## AC-3: Redirect paths podmienione w kontrolerach +```gherkin +Given '/settings/integrations/allegro' (5x), '/settings/integrations/shoppro' (6x) w kontrolerach +When plan zostaje wykonany +Then AllegroIntegrationController i ShopproIntegrationsController używają RedirectPaths::* +``` + +## AC-4: Brak regresji +```gherkin +Given aplikacja działa z twardymi stringami +When stałe mają identyczne wartości co podmienianie literały +Then zachowanie aplikacji identyczne — routing, source matching, redirect paths +``` + + + + + + + Task 1: Utwórz klasy stałych IntegrationSources i RedirectPaths + + src/Core/Constants/IntegrationSources.php, + src/Core/Constants/RedirectPaths.php + + + Utwórz folder src/Core/Constants/ i dwa pliki: + + **IntegrationSources.php** (namespace App\Core\Constants): + ```php + final class IntegrationSources { + public const ALLEGRO = 'allegro'; + public const SHOPPRO = 'shoppro'; + public const APACZKA = 'apaczka'; + public const INPOST = 'inpost'; + } + ``` + + **RedirectPaths.php** (namespace App\Core\Constants): + Przejrzyj AllegroIntegrationController i ShopproIntegrationsController i wypisz wszystkie unikalne redirect paths (np. '/settings/integrations/allegro', '/settings/integrations/allegro?tab=settings', '/settings/integrations/shoppro', etc.). + Utwórz stałe dla każdego unikalnego path: + ```php + final class RedirectPaths { + public const ALLEGRO_INTEGRATION = '/settings/integrations/allegro'; + public const ALLEGRO_SETTINGS_TAB = '/settings/integrations/allegro?tab=settings'; + public const ALLEGRO_STATUS_MAPPING_TAB = '/settings/integrations/allegro?tab=status-mapping'; + public const ALLEGRO_DELIVERY_TAB = '/settings/integrations/allegro?tab=delivery-mapping'; + public const SHOPPRO_INTEGRATION = '/settings/integrations/shoppro'; + public const SHOPPRO_SETTINGS_TAB = '/settings/integrations/shoppro?tab=settings'; + // ... dodaj wszystkie które znajdziesz w plikach kontrolerów + } + ``` + + Klasy final, bez konstruktora, bez metod — tylko stałe. + + + php -l src/Core/Constants/IntegrationSources.php + php -l src/Core/Constants/RedirectPaths.php + + AC-1 satisfied: oba pliki istnieją, poprawna składnia PHP + + + + Task 2: Podmień identyfikatory źródeł w Services i Repositories + + src/Modules/Settings/AllegroOrdersSyncService.php, + src/Modules/Settings/AllegroStatusSyncService.php, + src/Modules/Settings/AllegroOrderImportService.php, + src/Modules/Settings/ShopproOrdersSyncService.php, + src/Modules/Settings/ShopproStatusSyncService.php, + src/Modules/Settings/ShopproPaymentStatusSyncService.php, + src/Modules/Orders/OrdersRepository.php + + + W każdym pliku: + 1. Dodaj use App\Core\Constants\IntegrationSources; na górze + 2. Podmień literały: + - 'allegro' (gdy to identyfikator źródła/integracji, NIE gdy nazwa tabeli SQL) → IntegrationSources::ALLEGRO + - 'shoppro' → IntegrationSources::SHOPPRO + - 'apaczka' → IntegrationSources::APACZKA + - 'inpost' → IntegrationSources::INPOST + + WAŻNE — co NIE podmieniać: + - String w zapytaniach SQL (np. WHERE source = 'allegro' w OrdersRepository) — zostaw literal (SonarQube wyjątek dla SQL) + - Nazwy tabel, kolumn — zostaw + - Klucze tłumaczeń (np. 'settings.allegro.*') — zostaw + - Redirect URL paths — podmieniane w Task 3 + + Sprawdź kontekst każdego wystąpienia przed podmianą. + + + php -l na każdym zmodyfikowanym pliku + grep -n "'allegro'" src/Modules/Settings/AllegroOrdersSyncService.php — 0 wyników (lub tylko w SQL/komentarzach) + grep -n "'shoppro'" src/Modules/Settings/ShopproOrdersSyncService.php — 0 wyników (lub tylko w SQL/komentarzach) + + AC-2 satisfied: identyfikatory źródeł w services/repositories używają IntegrationSources::* + + + + Task 3: Podmień redirect paths i source literals w kontrolerach + + src/Modules/Settings/AllegroIntegrationController.php, + src/Modules/Settings/ShopproIntegrationsController.php + + + W obu plikach: + 1. Dodaj use App\Core\Constants\IntegrationSources; + 2. Dodaj use App\Core\Constants\RedirectPaths; + 3. Podmień redirect path strings → odpowiednie RedirectPaths::* + 4. Podmień identyfikatory źródeł 'allegro', 'shoppro' → IntegrationSources::* + + Szczególna uwaga: + - Header redirect strings (np. header('Location: /settings/integrations/allegro')) → RedirectPaths::ALLEGRO_INTEGRATION + - URL z tabami → odpowiednie stałe z RedirectPaths + - Nie podmieniaj kluczy tłumaczeń (settings.allegro.*) + - Nie podmieniaj niczego w widokach (resources/views/) + + + php -l src/Modules/Settings/AllegroIntegrationController.php + php -l src/Modules/Settings/ShopproIntegrationsController.php + grep -c "'/settings/integrations/allegro'" src/Modules/Settings/AllegroIntegrationController.php — 0 + grep -c "'/settings/integrations/shoppro'" src/Modules/Settings/ShopproIntegrationsController.php — 0 + + AC-3, AC-4 satisfied: redirect paths podmienione; aplikacja zachowuje identyczne zachowanie + + + + + + +## DO NOT CHANGE +- Zapytania SQL z literałami (np. 'allegro' w WHERE clauses) — SonarQube nie flaguje string w SQL +- Klucze tłumaczeń (settings.allegro.*, settings.integrations.*) +- Pliki widoków (resources/views/) +- routes/web.php +- Nazwy tabel i kolumn w SQL + +## SCOPE LIMITS +- Tylko pliki wymienione w files_modified +- Nie tworzyć stałych dla string które pojawiają się tylko 1-2 razy (SonarQube S1192 flaguje 3+ powtórzenia) +- Nie przenosić stałych do modułów — zostają w Core/Constants (globalne identyfikatory) + + + + +Przed zamknięciem planu: +- [ ] php -l na wszystkich zmodyfikowanych plikach — zero błędów +- [ ] ls src/Core/Constants/ — IntegrationSources.php, RedirectPaths.php +- [ ] grep -rn "'allegro'" src/ (bez SQL context) — drastycznie zredukowane +- [ ] grep -rn "'shoppro'" src/ (bez SQL context) — drastycznie zredukowane +- [ ] sonar-scanner — S1192 violations zmalały + + + +- Oba pliki Constants istnieją i są poprawne składniowo +- Wszystkie zmodyfikowane pliki bez błędów PHP +- SonarQube S1192 spada z 40 do ≤10 + + + +Po zakończeniu utwórz `.paul/phases/06-sonarqube-quality/06-03-SUMMARY.md` + diff --git a/.paul/phases/06-sonarqube-quality/06-04-PLAN.md b/.paul/phases/06-sonarqube-quality/06-04-PLAN.md new file mode 100644 index 0000000..63c9e60 --- /dev/null +++ b/.paul/phases/06-sonarqube-quality/06-04-PLAN.md @@ -0,0 +1,246 @@ +--- +phase: 06-sonarqube-quality +plan: 04 +type: execute +wave: 2 +depends_on: [] +files_modified: + - src/Modules/Settings/AllegroIntegrationController.php + - src/Modules/Settings/ShopproIntegrationsController.php + - src/Modules/Settings/ShopproOrdersSyncService.php +autonomous: true +--- + + +## Goal +Obniżyć złożoność kognitywną (cognitive complexity) w 5 najbardziej skomplikowanych metodach — eliminacja naruszeń SonarQube php:S3776 (31 wystąpień). + +## Purpose +Metody z zagnieżdżonymi if/foreach/switch na 4+ poziomach są trudne do testowania i modyfikacji. Wydzielenie fragmentów do private helper methods upraszcza główny przepływ i obniża wynik SonarQube poniżej progu 15. + +## Output +Zmniejszona złożoność w 5 kluczowych metodach w 3 plikach. SonarQube S3776 spada z 31 do ~15. + + + +## Project Context +@.paul/PROJECT.md + +## Source Files +@src/Modules/Settings/AllegroIntegrationController.php +@src/Modules/Settings/ShopproIntegrationsController.php +@src/Modules/Settings/ShopproOrdersSyncService.php + + + +## Required Skills (from SPECIAL-FLOWS.md) + +| Skill | Priority | When to Invoke | Loaded? | +|-------|----------|----------------|---------| +| sonar-scanner | required | Po APPLY, przed UNIFY | ○ | + +## Skill Invocation Checklist +- [ ] sonar-scanner uruchomiony po zakończeniu APPLY + + + + +## AC-1: AllegroIntegrationController::oauthCallback() uproszczone +```gherkin +Given oauthCallback() ma 6 zagnieżdżonych if (validation chain) +When wydzielasz każdy blok walidacji do osobnej private metody +Then oauthCallback() ma max 2 poziomy zagnieżdżenia +``` + +## AC-2: AllegroIntegrationController::loadDeliveryServices() uproszczone +```gherkin +Given loadDeliveryServices() ma try-catch z 4 poziomami zagnieżdżenia +When wydzielasz ładowanie Allegro services i Apaczka services do osobnych private metod +Then loadDeliveryServices() ma max 2 poziomy zagnieżdżenia +``` + +## AC-3: ShopproIntegrationsController::saveStatusMappings() uproszczone +```gherkin +Given saveStatusMappings() ma 3 zagnieżdżone pętle (foreach w foreach z if) +When wydzielasz przetwarzanie pojedynczego mappingu do private metody +Then saveStatusMappings() ma max 2 poziomy zagnieżdżenia +``` + +## AC-4: ShopproOrdersSyncService::sync() uproszczone +```gherkin +Given sync() (195 linii) ma triple nested loop (for pages > foreach candidates > try-catch > if) +When wydzielasz przetwarzanie pojedynczej strony do private processPage() i pojedynczego zamówienia do processCandidate() +Then sync() ma max 3 poziomy zagnieżdżenia; główna pętla jest czytelna +``` + +## AC-5: Brak regresji +```gherkin +Given funkcjonalność działa przed refaktoryzacją +When zmiany są czysto strukturalne (extract method, nie zmiana logiki) +Then zachowanie identyczne — OAuth flow, status mappings, sync działają +``` + + + + + + + Task 1: Uproszczenie AllegroIntegrationController — oauthCallback() i loadDeliveryServices() + src/Modules/Settings/AllegroIntegrationController.php + + Przeczytaj oauthCallback() i loadDeliveryServices() dokładnie. + + **oauthCallback() — wzorzec uproszczenia:** + Każdy blok `if (warunek) { flash error; return; }` to kandydat do wydzielenia. + Stwórz private validateOAuthState(array $params): bool — sprawdza state parameter + Stwórz private validateOAuthCode(array $params): bool — sprawdza code parameter + Główna metoda: series of guard checks bez zagnieżdżenia. + Docelowy kształt: + ```php + public function oauthCallback(): void { + if (!$this->validateOAuthState($params)) { [flash + redirect]; return; } + if (!$this->validateOAuthCode($params)) { [flash + redirect]; return; } + // logika bez zagnieżdżenia + } + ``` + + **loadDeliveryServices() — wzorzec uproszczenia:** + Wydziel ładowanie Allegro delivery services do private loadAllegroDeliveryServices(): array + Wydziel ładowanie Apaczka services do private loadApaczkaServices(): array + Główna metoda wywołuje oba i buduje odpowiedź — bez zagnieżdżonych try-catch. + Każda z wydzielonych metod ma swój własny try-catch wewnętrznie. + + Zasady: + - NIE zmieniaj logiki — tylko gdzie jest kod + - NIE zmieniaj komunikatów, flash keys, redirect paths + - Nowe private metody na końcu klasy + + + php -l src/Modules/Settings/AllegroIntegrationController.php + Ręczne sprawdzenie oauthCallback(): max 2 poziomy zagnieżdżenia w ciele metody + Ręczne sprawdzenie loadDeliveryServices(): max 2 poziomy zagnieżdżenia + + AC-1, AC-2 satisfied: obie metody uproszczone, składnia poprawna + + + + Task 2: Uproszczenie ShopproIntegrationsController — saveStatusMappings() + src/Modules/Settings/ShopproIntegrationsController.php + + Przeczytaj saveStatusMappings() i loadDeliveryServices(). + + **saveStatusMappings() — wzorzec uproszczenia:** + Jeśli metoda zawiera zagnieżdżone foreach z logiką wewnątrz: + ```php + foreach ($mappings as $mapping) { + foreach ($mapping['items'] as $item) { + if (...) { + // logika + } + } + } + ``` + Wydziel wewnętrzny foreach+if do private processMappingItem(array $item, ...): void + lub private processSingleStatusMapping(array $mapping): void + + **loadDeliveryServices() (jeśli jest w Shoppro):** + Ten sam wzorzec co Allegro — wydziel ładowanie per-provider. + + Główna zasada: metoda po refaktoryzacji powinna być "czytalna jak lista kroków" — bez zagłębiania się w szczegóły. + + NIE zmieniaj logiki biznesowej. + Nowe private metody na końcu klasy. + + + php -l src/Modules/Settings/ShopproIntegrationsController.php + Ręczne sprawdzenie saveStatusMappings(): max 2 poziomy zagnieżdżenia + + AC-3 satisfied: saveStatusMappings() uproszczone + + + + Task 3: Uproszczenie ShopproOrdersSyncService::sync() + src/Modules/Settings/ShopproOrdersSyncService.php + + Przeczytaj metodę sync() (ok. 195 linii). + + **sync() — wzorzec uproszczenia:** + Obecna struktura (schemat): + ``` + foreach ($integrations) { + for ($page = 1; ...) { + foreach ($candidates) { + try { + if (...) { + // przetwarzanie zamówienia + } + } catch (...) { ... } + } + } + } + ``` + + Wydziel: + 1. private processOrderCandidate(array $candidate, array $integration, array $statusMap, ...): void + - zawiera: try-catch + logikę przetwarzania jednego zamówienia + 2. private processPageCandidates(array $candidates, array $integration, array $statusMap, array &$results): void + - zawiera: foreach ($candidates) + wywołanie processOrderCandidate() + + Po wydzieleniu sync() powinna wyglądać jak: + ``` + foreach ($integrations) { + for ($page = 1; ...) { + $candidates = $this->buildCandidates($orders); + $this->processPageCandidates($candidates, $integration, $statusMap, $results); + if (brak kolejnej strony) break; + } + } + ``` + + Zachowaj wszystkie parametry które są potrzebne (przekaż przez argumenty lub &$results). + NIE zmieniaj logiki przetwarzania — tylko gdzie jest kod. + + + php -l src/Modules/Settings/ShopproOrdersSyncService.php + Ręczne sprawdzenie sync(): max 3 poziomy zagnieżdżenia (foreach integrations > for pages > processPageCandidates) + + AC-4, AC-5 satisfied: sync() uproszczone, ShopproOrdersSyncService poprawna składniowo + + + + + + +## DO NOT CHANGE +- Logika biznesowa — warunkowa, obliczeniowa, API calls +- Komunikaty błędów, flash keys, redirect paths +- Publiczne sygnatury metod (public function names + parameters) +- Inne metody poza wymienionymi w AC +- Pliki poza files_modified + +## SCOPE LIMITS +- Wydzielanie metod tylko wewnątrz istniejącej klasy (nie tworzyć nowych klas — to zakres 06-05) +- Nowe metody: private, na końcu klasy, jasna nazwa opisująca co robią +- Nie optymalizuj algorytmicznie — tylko strukturalnie + + + + +Przed zamknięciem planu: +- [ ] php -l na wszystkich 3 plikach — zero błędów +- [ ] oauthCallback() — max 2 poziomy zagnieżdżenia (ręczne sprawdzenie) +- [ ] loadDeliveryServices() (Allegro) — max 2 poziomy zagnieżdżenia +- [ ] saveStatusMappings() (Shoppro) — max 2 poziomy zagnieżdżenia +- [ ] sync() (ShopproOrdersSyncService) — max 3 poziomy zagnieżdżenia +- [ ] sonar-scanner — S3776 violations zmalały + + + +- Wszystkie 3 pliki bez błędów składniowych +- 5 wskazanych metod z zredukowanym zagnieżdżeniem +- SonarQube S3776 spada z 31 do ≤18 + + + +Po zakończeniu utwórz `.paul/phases/06-sonarqube-quality/06-04-SUMMARY.md` + diff --git a/.paul/phases/06-sonarqube-quality/06-05-PLAN.md b/.paul/phases/06-sonarqube-quality/06-05-PLAN.md new file mode 100644 index 0000000..6de108e --- /dev/null +++ b/.paul/phases/06-sonarqube-quality/06-05-PLAN.md @@ -0,0 +1,298 @@ +--- +phase: 06-sonarqube-quality +plan: 05 +type: execute +wave: 3 +depends_on: ["06-04"] +files_modified: + - src/Modules/Settings/ShopproOrdersSyncService.php + - src/Modules/Settings/ShopproOrderMapper.php + - src/Modules/Settings/ShopproProductImageResolver.php + - src/Modules/Settings/AllegroIntegrationController.php + - src/Modules/Settings/AllegroStatusMappingController.php + - src/Modules/Settings/AllegroDeliveryMappingController.php +autonomous: false +--- + + +## Goal +Rozdzielić 2 z 3 god classes przekraczające 20 metod — eliminacja naruszeń SonarQube php:S1448. +- `ShopproOrdersSyncService` (39 metod) → 3 klasy +- `AllegroIntegrationController` (30 metod) → 3 kontrolery + +## Purpose +God classes naruszają SRP i S1448. `ShopproOrdersSyncService` z 39 metodami i 1192 liniami jest najtrudniejszą do testowania klasą w projekcie. Podział ułatwia izolowane modyfikacje integracji Shoppro bez ryzyka regresi w całym module. + +## Output +5 nowych klas/kontrolerów, `ShopproOrdersSyncService` spada do ~15 metod, `AllegroIntegrationController` do ~12. S1448 spada z 6x do 1x (ShopproIntegrationsController pozostaje na następny plan). + + + +## Project Context +@.paul/PROJECT.md +@.paul/ROADMAP.md + +## Source Files +@src/Modules/Settings/ShopproOrdersSyncService.php +@src/Modules/Settings/AllegroIntegrationController.php +@routes/web.php + + + +## Required Skills (from SPECIAL-FLOWS.md) + +| Skill | Priority | When to Invoke | Loaded? | +|-------|----------|----------------|---------| +| sonar-scanner | required | Po APPLY, przed UNIFY | ○ | +| /code-review | optional | Po implementacji, przed UNIFY | ○ | + +## Skill Invocation Checklist +- [ ] sonar-scanner uruchomiony po zakończeniu APPLY +- [ ] /code-review rozważyć (duże zmiany strukturalne) + + + + +## AC-1: ShopproOrderMapper wydzielony +```gherkin +Given ShopproOrdersSyncService zawiera 15+ metod mapujących (mapOrderAggregate, mapAddresses, mapItems, etc.) +When wydzielasz je do src/Modules/Settings/ShopproOrderMapper.php +Then ShopproOrderMapper zawiera wszystkie metody mapowania; ShopproOrdersSyncService wstrzykuje go przez konstruktor +``` + +## AC-2: ShopproProductImageResolver wydzielony +```gherkin +Given ShopproOrdersSyncService zawiera resolveProductImagesForOrder() i fetchPrimaryProductImageUrl() +When wydzielasz je do src/Modules/Settings/ShopproProductImageResolver.php +Then ShopproProductImageResolver obsługuje image fetching; ShopproOrdersSyncService używa go przez konstruktor +``` + +## AC-3: ShopproOrdersSyncService ma ≤20 metod +```gherkin +Given ShopproOrdersSyncService ma 39 metod +When wydzielono ShopproOrderMapper i ShopproProductImageResolver +Then ShopproOrdersSyncService ma ≤20 metod (sync + orchestration + state management) +``` + +## AC-4: AllegroIntegrationController podzielony +```gherkin +Given AllegroIntegrationController ma 30 metod (status mapping, delivery mapping, main settings) +When wydzielasz AllegroStatusMappingController i AllegroDeliveryMappingController +Then każdy kontroler ma ≤15 metod; routes/web.php zaktualizowane +``` + +## AC-5: Brak regresji +```gherkin +Given Shoppro sync i Allegro integration działają przed podziałem +When podział jest czysto strukturalny (move method, nie zmiana logiki) +Then sync Shoppro importuje zamówienia; mapowania Allegro zapisują się; OAuth działa +``` + + + + + + + Task 1: Wydziel ShopproOrderMapper z ShopproOrdersSyncService + + src/Modules/Settings/ShopproOrderMapper.php, + src/Modules/Settings/ShopproOrdersSyncService.php + + + Przeczytaj ShopproOrdersSyncService dokładnie. Zidentyfikuj metody mapujące dane zamówień. + + **Metody do przeniesienia do ShopproOrderMapper:** + Przenieś wszystkie private metody których jedynym zadaniem jest konwersja danych API → struktury wewnętrzne: + - mapOrderAggregate() — główna metoda mapowania + - mapAddresses() — mapowanie adresów + - mapItems() — mapowanie pozycji + - mapPayments() — mapowanie płatności + - mapShipments() — mapowanie wysyłek + - mapNotes() — mapowanie notatek + - buildInvoiceAddress() — budowanie adresu faktury + - normalizeOrderId(), normalizePaidFlag(), mapPaymentStatus(), sanitizePlainText() + - buildDeliveryMethodLabel(), formatMoneyCompact(), normalizeMediaUrl() + - toFloatOrNull(), toFloatOrDefault(), readPath(), readSinglePath(), composeName() + - hasAddressData(), resolveInvoiceRequested(), parsePickupPoint() + + **ShopproOrderMapper (nowy plik):** + - namespace App\Modules\Settings + - class ShopproOrderMapper + - Konstruktor przyjmuje zależności potrzebne metodom (np. ShopproOrderSyncStateRepository jeśli potrzebne) + - Przenieś metody bez zmiany logiki (tylko zmień z private na public te, które sync() wywołuje bezpośrednio) + + **ShopproOrdersSyncService (aktualizacja):** + - Dodaj private ShopproOrderMapper $mapper; w konstruktorze + - CronHandlerFactory tworzy ShopproOrdersSyncService — sprawdź src/Modules/Cron/CronHandlerFactory.php i zaktualizuj kompozycję + - Zastąp wywołania $this->mapOrderAggregate() → $this->mapper->mapOrderAggregate() + - Usuń przeniesione metody z ShopproOrdersSyncService + + UWAGA: Sprawdź CronHandlerFactory.php — musi przekazać nowy ShopproOrderMapper do konstruktora. + + + php -l src/Modules/Settings/ShopproOrderMapper.php + php -l src/Modules/Settings/ShopproOrdersSyncService.php + php -l src/Modules/Cron/CronHandlerFactory.php + grep -c "function " src/Modules/Settings/ShopproOrdersSyncService.php — powinno być ≤25 (po Task 1, przed Task 2) + + AC-1 satisfied: ShopproOrderMapper istnieje z metodami mapowania; ShopproOrdersSyncService go używa + + + + Task 2: Wydziel ShopproProductImageResolver i finalizuj ShopproOrdersSyncService + + src/Modules/Settings/ShopproProductImageResolver.php, + src/Modules/Settings/ShopproOrdersSyncService.php, + src/Modules/Cron/CronHandlerFactory.php + + + **ShopproProductImageResolver (nowy plik):** + - Przenieś z ShopproOrdersSyncService: + - resolveProductImagesForOrder() + - fetchPrimaryProductImageUrl() + - Konstruktor przyjmuje ShopproApiClient (potrzebny do fetch) + - namespace App\Modules\Settings + + **ShopproOrdersSyncService (finalizacja):** + - Dodaj private ShopproProductImageResolver $imageResolver; + - Zastąp $this->resolveProductImagesForOrder() → $this->imageResolver->resolveProductImagesForOrder() + - Po Task 1 + Task 2 ShopproOrdersSyncService powinien mieć ≤20 metod + + **CronHandlerFactory.php (aktualizacja):** + - Sprawdź gdzie tworzona jest instancja ShopproOrdersSyncService + - Dodaj nowe zależności: new ShopproOrderMapper(...), new ShopproProductImageResolver($shopproApiClient) + - Przekaż do konstruktora ShopproOrdersSyncService + + Sprawdź czy ShopproOrderMapper też potrzebuje ShopproApiClient lub innych zależności — jeśli tak, przekaż z CronHandlerFactory. + + + php -l src/Modules/Settings/ShopproProductImageResolver.php + php -l src/Modules/Settings/ShopproOrdersSyncService.php + php -l src/Modules/Cron/CronHandlerFactory.php + grep -c "function " src/Modules/Settings/ShopproOrdersSyncService.php — ≤20 + + AC-2, AC-3 satisfied: ShopproProductImageResolver wydzielony; ShopproOrdersSyncService ≤20 metod + + + + + ShopproOrdersSyncService podzielony na 3 klasy: + - ShopproOrderMapper (metody mapowania danych) + - ShopproProductImageResolver (pobieranie zdjęć produktów) + - ShopproOrdersSyncService (orchestrator, ≤20 metod) + CronHandlerFactory zaktualizowany z nowymi zależnościami. + + + 1. Uruchom aplikację (XAMPP) + 2. Przejdź do Ustawień → Integracje → ShopPRO + 3. Uruchom ręczny import zamówień (jeśli dostępny) lub uruchom cron + 4. Sprawdź logi crona: importy Shoppro działają bez błędów + 5. Sprawdź że zamówienia pojawiają się / nie ma regresji + + Wpisz "approved" aby kontynuować do podziału AllegroIntegrationController, lub opisz błędy + + + + Task 3: Podziel AllegroIntegrationController na 3 kontrolery + + src/Modules/Settings/AllegroIntegrationController.php, + src/Modules/Settings/AllegroStatusMappingController.php, + src/Modules/Settings/AllegroDeliveryMappingController.php, + routes/web.php + + + Przeczytaj AllegroIntegrationController i routes/web.php dokładnie. + + **Podział metod:** + + AllegroStatusMappingController (nowy): + - saveStatusMapping() + - saveStatusMappingsBulk() + - deleteStatusMapping() + - syncStatusesFromAllegro() + - buildImportImageWarningMessage() (private helper) + - reasonLabel() (private helper) + + AllegroDeliveryMappingController (nowy): + - saveDeliveryMappings() + - loadDeliveryServices() + - (prywatne helpery potrzebne tym metodom) + + AllegroIntegrationController (zostaje z): + - index() + - save() + - saveImportSettings() + - startOAuth() + - oauthCallback() + - refreshOAuthToken() + - importSingleOrder() + - (prywatne walidatory i helpery potrzebne tym metodom) + + **Nowe pliki kontrolerów:** + - Skopiuj konstruktor i zależności z AllegroIntegrationController + - namespace App\Modules\Settings + - Przenieś metody bez zmiany logiki + + **routes/web.php:** + - Utwórz instancje nowych kontrolerów (ten sam wzorzec co istniejące) + - Przepnij route definicje dla: + - status mapping routes → AllegroStatusMappingController + - delivery mapping routes → AllegroDeliveryMappingController + - AllegroIntegrationController obsługuje resztę + + Sprawdź które zależności ($this->repository, $this->translator etc.) są potrzebne każdemu kontrolerowi. + + + php -l src/Modules/Settings/AllegroIntegrationController.php + php -l src/Modules/Settings/AllegroStatusMappingController.php + php -l src/Modules/Settings/AllegroDeliveryMappingController.php + php -l routes/web.php + grep -c "function " src/Modules/Settings/AllegroIntegrationController.php — ≤15 + + AC-4, AC-5 satisfied: AllegroIntegrationController ≤15 metod; nowe kontrolery poprawne; routes zaktualizowane + + + + + + +## DO NOT CHANGE +- Logika biznesowa w żadnej z przenoszonych metod +- Publiczne sygnatury metod (nazwy, parametry) +- Pliki widoków i SCSS +- ShopproIntegrationsController — zakres następnego planu (06-06 jeśli powstanie) +- Inne moduły (Orders, Shipments, Auth, etc.) + +## SCOPE LIMITS +- Podział 2 klas (ShopproOrdersSyncService + AllegroIntegrationController) +- ShopproIntegrationsController pomijamy w tym planie (30 metod — podobny refactoring) +- Nie zmieniaj publicznych URL routes — tylko który controller je obsługuje +- CronHandlerFactory — tylko minimalne zmiany potrzebne dla nowych dependencies + + + + +Przed zamknięciem planu: +- [ ] php -l na wszystkich nowych i zmodyfikowanych plikach +- [ ] ShopproOrdersSyncService: grep -c "function " ≤20 +- [ ] AllegroIntegrationController: grep -c "function " ≤15 +- [ ] ShopproOrderMapper.php istnieje z metodami mapowania +- [ ] ShopproProductImageResolver.php istnieje +- [ ] AllegroStatusMappingController.php i AllegroDeliveryMappingController.php istnieją +- [ ] routes/web.php poprawny składniowo +- [ ] Checkpoint human-verify zaliczony (Shoppro sync działa) +- [ ] sonar-scanner — S1448 violations zmalały + + + +- 4 nowe pliki PHP +- ShopproOrdersSyncService ≤20 metod +- AllegroIntegrationController ≤15 metod +- Zero błędów składniowych +- Shoppro import działa (human-verify passed) +- SonarQube S1448 spada z 6 do ≤2 + + + +Po zakończeniu utwórz `.paul/phases/06-sonarqube-quality/06-05-SUMMARY.md` + diff --git a/.paul/phases/06-sonarqube-quality/06-06-PLAN.md b/.paul/phases/06-sonarqube-quality/06-06-PLAN.md new file mode 100644 index 0000000..58eca78 --- /dev/null +++ b/.paul/phases/06-sonarqube-quality/06-06-PLAN.md @@ -0,0 +1,204 @@ +--- +phase: 06-sonarqube-quality +plan: 06 +type: execute +wave: 2 +depends_on: [] +files_modified: + - src/Modules/Settings/ShopproOrdersSyncService.php + - src/Modules/Orders/OrdersRepository.php +autonomous: true +--- + + +## Goal +Skrócić 4 metody przekraczające limit linii — eliminacja naruszeń SonarQube php:S138. Dodatkowo: `OrdersRepository::paginate()` (183 linie) i `findDetails()` (101 linii) jako najdłuższe metody w module Orders. + +## Purpose +Metody o 87-195 liniach są niereviewowalne w MR i trudne do lokalnego rozumowania. Wydzielenie sub-metod ogranicza zakres zmian przy przyszłych modyfikacjach — edytujesz 30-liniową metodę, nie 200-liniowy monolit. + +## Output +6 metod skróconych do ≤50 linii każda przez ekstrakcję sub-metod. SonarQube S138 spada z 4 do 0. + +**Uwaga na kolejność z planem 06-05:** +Ten plan (06-06) modyfikuje ShopproOrdersSyncService. Plan 06-05 podnosi zależność `depends_on: ["06-04"]`. Jeśli 06-05 jest w trakcie, odłóż zmiany w ShopproOrdersSyncService do po zakończeniu 06-05. + + + +## Project Context +@.paul/PROJECT.md + +## Source Files +@src/Modules/Settings/ShopproOrdersSyncService.php +@src/Modules/Orders/OrdersRepository.php + + + +## Required Skills (from SPECIAL-FLOWS.md) + +| Skill | Priority | When to Invoke | Loaded? | +|-------|----------|----------------|---------| +| sonar-scanner | required | Po APPLY, przed UNIFY | ○ | + +## Skill Invocation Checklist +- [ ] sonar-scanner uruchomiony po zakończeniu APPLY + + + + +## AC-1: ShopproOrdersSyncService — sync() skrócone +```gherkin +Given sync() ma 195 linii +When wydzielasz procesPage() i processOrderCandidate() (jeśli nie zrobione w 06-04) +Then sync() ma ≤60 linii; wydzielone metody mają ≤50 linii każda +``` + +## AC-2: ShopproOrdersSyncService — mapAddresses() skrócone +```gherkin +Given mapAddresses() ma 166 linii (budowanie customer + delivery address) +When wydzielasz buildCustomerAddress(): array i buildDeliveryAddress(): array +Then mapAddresses() ma ≤50 linii; każda sub-metoda ≤60 linii +``` + +## AC-3: OrdersRepository::paginate() skrócone +```gherkin +Given paginate() ma 183 linie (dynamic SQL building + data transformation) +When wydzielasz buildFilters(): array i transformRow(array $row): array +Then paginate() ma ≤80 linii; sub-metody mają ≤50 linii każda +``` + +## AC-4: OrdersRepository::findDetails() skrócone +```gherkin +Given findDetails() ma 101 linii (8 kolejnych zapytań DB) +When wydzielasz osobne private metody per query group (loadOrderItems, loadOrderShipments, etc.) +Then findDetails() ma ≤50 linii; każdy loader ≤30 linii +``` + +## AC-5: Brak regresji +```gherkin +Given lista zamówień i szczegóły zamówienia działają przed zmianą +When zmiany są czysto strukturalne (extract method) +Then paginate() i findDetails() zwracają identyczne dane +``` + + + + + + + Task 1: Skróć ShopproOrdersSyncService — sync() i mapAddresses() + src/Modules/Settings/ShopproOrdersSyncService.php + + Przeczytaj ShopproOrdersSyncService dokładnie. + + **Sprawdź najpierw:** czy 06-04 już wydzielił processPageCandidates() z sync(). Jeśli tak, sprawdź czy sync() jest już ≤60 linii. Jeśli nie — wydziel. + + **sync() (195 linii) — jeśli jeszcze nie zrobione w 06-04:** + Wydziel: + - private processPageCandidates(array $candidates, array $integration, array $statusMap, array &$results): void + → zawiera foreach ($candidates) + try-catch + logikę przetwarzania 1 zamówienia + - private fetchOrdersPage(array $integration, int $page, string $startDate): array + → zawiera wywołanie API + obsługę błędów odpowiedzi + + **mapAddresses() (166 linii):** + Wydziel: + - private buildCustomerAddress(array $payload): array + → adres klienta (billing address) + - private buildDeliveryAddress(array $payload): array + → adres dostawy (shipping address) + - private buildPickupPointData(array $payload): array (jeśli dotyczy) + + mapAddresses() staje się kompozytorem: wywołuje buildCustomerAddress() i buildDeliveryAddress(). + + NIE zmieniaj logiki — tylko gdzie jest kod. + Nowe private metody na końcu klasy (ale przed zamknięciem `}`). + + + php -l src/Modules/Settings/ShopproOrdersSyncService.php + Ręcznie: sync() ≤60 linii, mapAddresses() ≤50 linii + + AC-1, AC-2 satisfied: sync() i mapAddresses() skrócone + + + + Task 2: Skróć OrdersRepository — paginate() i findDetails() + src/Modules/Orders/OrdersRepository.php + + Przeczytaj OrdersRepository dokładnie — paginate() i findDetails(). + + **paginate() (183 linie):** + Ma dwie odrębne odpowiedzialności: budowanie SQL z filtrami + transformacja wierszy. + + Wydziel: + - private buildPaginateFilters(array $filters): array + → zwraca ['conditions' => [], 'bindings' => []] + → zawiera bloki if ($filter['source']), if ($filter['status']), if ($filter['date_from']), etc. + + - private transformOrderRow(array $row): array + → transformuje jeden wiersz z DB → format gotowy dla widoku + → jeśli nie ma transformacji per-row, wydziel transformOrderRows(array $rows): array + + paginate() staje się: build SQL structure → execute → transform → return. + + WAŻNE: Medoo query syntax — zachowaj dokładnie ten sam format przekazywania warunków do Medoo. NIE zmieniaj sposobu budowania prepared statements. + + **findDetails() (101 linii):** + Metoda wykonuje 8 kolejnych zapytań DB. Wydziel każde lub grupuj logicznie: + - private loadOrderAddresses(int $orderId): array + - private loadOrderItems(int $orderId): array + - private loadOrderShipments(int $orderId): array + - private loadOrderHistory(int $orderId): array + - (i inne grupy które znajdziesz w metodzie) + + findDetails() staje się: załaduj dane orderu + złóż w całość + return. + + NIE zmieniaj logiki SQL, kolumn, tabel, sposobu bindingu. + NIE zmieniaj zwracanej struktury danych. + + + php -l src/Modules/Orders/OrdersRepository.php + Ręcznie: paginate() ≤80 linii, findDetails() ≤50 linii + + AC-3, AC-4, AC-5 satisfied: paginate() i findDetails() skrócone; brak zmiany logiki SQL + + + + + + +## DO NOT CHANGE +- Logika SQL w OrdersRepository — prepared statements, WHERE conditions, column names +- Zwracana struktura danych z paginate() i findDetails() — widoki zależą od tych kluczy +- Publiczne sygnatury metod +- Inne pliki poza ShopproOrdersSyncService.php i OrdersRepository.php + +## SCOPE LIMITS +- Tylko extract method refactoring — nie zmieniaj algorytmów +- Nie przenoś metod do innych klas (to zakres 06-05) +- Nie zmieniaj logiki filtrowania w paginate() — tylko gdzie jest kod +- ShopproOrdersSyncService: uważaj na interferencję z 06-05 (jeśli 06-05 już zmienił tę klasę — dostosuj) + + + + +Przed zamknięciem planu: +- [ ] php -l src/Modules/Settings/ShopproOrdersSyncService.php — brak błędów +- [ ] php -l src/Modules/Orders/OrdersRepository.php — brak błędów +- [ ] sync() w ShopproOrdersSyncService: ≤60 linii (ręczne sprawdzenie) +- [ ] mapAddresses(): ≤50 linii +- [ ] paginate(): ≤80 linii +- [ ] findDetails(): ≤50 linii +- [ ] Lista zamówień ładuje się (brak PHP fatal errors) +- [ ] sonar-scanner — S138 violations zmalały + + + +- Oba pliki bez błędów składniowych +- 4 metody skrócone poniżej limitu SonarQube +- Nowe private helper metody ≤50 linii każda +- SonarQube S138 spada z 4 do 0 + + + +Po zakończeniu utwórz `.paul/phases/06-sonarqube-quality/06-06-SUMMARY.md` +