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