Files
orderPRO/.paul/phases/02-bug-fixes/02-04-PLAN.md
Jacek Pyziak 880ab5933f feat(02-bug-fixes): extract CronHandlerFactory, complete Phase 2
Phase 2 complete (4/4 plans):
- Plan 02-01: Fix dead ZPL page size condition in AllegroShipmentService
- Plan 02-02: Add time-based cursor to AllegroStatusSyncService
- Plan 02-03: Fix ShopproOrdersSyncService using wrong state repository
- Plan 02-04: Extract CronHandlerFactory as single cron composition point

Plan 02-04 specifics:
- New CronHandlerFactory builds complete cron object graph
- Application::maybeRunCronOnWeb() reduced from 80+ to ~20 lines
- bin/cron.php reduced from 123 to 26 lines
- Fixed 2 bugs: AllegroOAuthClient→AllegroTokenManager,
  AllegroOrderSyncStateRepository→ShopproOrderSyncStateRepository

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-13 00:43:04 +01:00

242 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: 02-bug-fixes
plan: 04
type: execute
wave: 1
depends_on: []
files_modified:
- src/Modules/Cron/CronHandlerFactory.php
- src/Core/Application.php
- bin/cron.php
autonomous: true
---
<objective>
## Cel
Wydzielić `CronHandlerFactory` — jedyne miejsce, które buduje graf obiektów dla crona. Zastąpić nią ręczną konstrukcję w `Application::maybeRunCronOnWeb()` oraz `bin/cron.php`.
## Powód
`Application.php` (linie 274353) zawiera 80+ linii ręcznego `new X($this->db)` powielających okablowanie z `bin/cron.php`. Oba miejsca mogą się rozjechać — już teraz `bin/cron.php` przekazuje `AllegroOAuthClient` tam, gdzie powinien być `AllegroTokenManager`, oraz używa `AllegroOrderSyncStateRepository` zamiast `ShopproOrderSyncStateRepository` dla shopPRO.
## Output
- Nowy plik `src/Modules/Cron/CronHandlerFactory.php`
- `Application.php::maybeRunCronOnWeb()` skrócona do ~10 linii
- `bin/cron.php` skrócone do ~15 linii
- Oba błędy w `bin/cron.php` naprawione przy okazji
</objective>
<context>
## Kontekst projektu
@.paul/PROJECT.md
@.paul/ROADMAP.md
@.paul/STATE.md
## Pliki źródłowe
@src/Core/Application.php
@bin/cron.php
@routes/web.php
</context>
<skills>
## Required Skills (from SPECIAL-FLOWS.md)
| Skill | Priority | When to Invoke | Loaded? |
|-------|----------|----------------|---------|
| /code-review | required | Po implementacji, przed UNIFY | ○ |
| sonar-scanner | required | Po APPLY, przed UNIFY | ○ |
**BLOCKING:** Required skills MUST be loaded before APPLY proceeds.
## Skill Invocation Checklist
- [ ] /code-review loaded
- [ ] sonar-scanner run po zakończeniu implementacji
</skills>
<acceptance_criteria>
## AC-1: Jeden punkt definicji handlerow crona
```gherkin
Given codebase z trzema miejscami budującymi graf obiektów crona
When plik CronHandlerFactory.php istnieje
Then Application.php i bin/cron.php nie zawierają już new AllegroIntegrationRepository / new ShopproIntegrationsRepository / new AllegroOrderImportService w kontekście crona wszystkie obiekty buduje fabryka
```
## AC-2: Web cron działa identycznie jak przed zmianą
```gherkin
Given ustawienie cron_run_on_web = true w bazie
When użytkownik odwiedza dowolną stronę aplikacji
Then maybeRunCronOnWeb() uruchamia CronRunner z tymi samymi handlerami co wcześniej brak regresji funkcjonalnej
```
## AC-3: bin/cron.php używa poprawnych zależności
```gherkin
Given bin/cron.php przed zmianą używał AllegroOAuthClient zamiast AllegroTokenManager (arg #2 AllegroOrderImportService i AllegroOrdersSyncService) oraz AllegroOrderSyncStateRepository zamiast ShopproOrderSyncStateRepository dla ShopproOrdersSyncService
When bin/cron.php zostanie zaktualizowany do użycia CronHandlerFactory
Then oba błędy są naprawione fabryka przekazuje AllegroTokenManager i ShopproOrderSyncStateRepository
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Utwórz CronHandlerFactory</name>
<files>src/Modules/Cron/CronHandlerFactory.php</files>
<action>
Utwórz klasę `App\Modules\Cron\CronHandlerFactory` w nowym pliku.
Konstruktor: `__construct(private readonly \PDO $db, private readonly string $integrationSecret)`.
Metoda publiczna: `build(CronRepository $cronRepository, \App\Core\Support\Logger $logger): CronRunner`.
Wewnątrz `build()` skonstruuj dokładnie te same obiekty co w `Application::maybeRunCronOnWeb()` (stan po naprawie z planu 02-03, czyli wersja z `Application.php` — NIE z `bin/cron.php`):
```
$integrationRepository = new AllegroIntegrationRepository($this->db, $this->integrationSecret)
$oauthClient = new AllegroOAuthClient()
$tokenManager = new AllegroTokenManager($integrationRepository, $oauthClient)
$apiClient = new AllegroApiClient()
$statusMappingRepository = new AllegroStatusMappingRepository($this->db)
$orderImportService = new AllegroOrderImportService(
$integrationRepository, $tokenManager, $apiClient,
new OrderImportRepository($this->db), $statusMappingRepository, new OrdersRepository($this->db)
)
$ordersSyncService = new AllegroOrdersSyncService(
$integrationRepository, new AllegroOrderSyncStateRepository($this->db),
$tokenManager, $apiClient, $orderImportService
)
$shopproIntegrationsRepo = new ShopproIntegrationsRepository($this->db, $this->integrationSecret)
$shopproSyncService = new ShopproOrdersSyncService(
$shopproIntegrationsRepo, new ShopproOrderSyncStateRepository($this->db),
new ShopproApiClient(), new OrderImportRepository($this->db),
new ShopproStatusMappingRepository($this->db), new OrdersRepository($this->db)
)
$shopproStatusSyncService = new ShopproStatusSyncService($shopproIntegrationsRepo, $shopproSyncService)
$shopproPaymentSyncService = new ShopproPaymentStatusSyncService(
$shopproIntegrationsRepo, new ShopproApiClient(), new OrdersRepository($this->db), $this->db
)
```
Zwróć `new CronRunner($cronRepository, $logger, [ handlers array ])` — lista handlerów identyczna jak w `Application.php` (allegro_token_refresh, allegro_orders_import, allegro_status_sync, shoppro_orders_import, shoppro_order_status_sync, shoppro_payment_status_sync).
Dodaj wszystkie potrzebne `use` na górze pliku. Klasa powinna być `final`.
UNIKAJ: przyjmowania całego `Application` lub tablicy konfiguracji jako parametru — tylko `$db` i `$integrationSecret` są potrzebne.
</action>
<verify>
Plik istnieje: `src/Modules/Cron/CronHandlerFactory.php`.
Składnia PHP poprawna: `php -l src/Modules/Cron/CronHandlerFactory.php` zwraca "No syntax errors detected".
</verify>
<done>AC-1 częściowo — fabryka istnieje i buduje kompletny graf obiektów</done>
</task>
<task type="auto">
<name>Task 2: Uproszczenie Application::maybeRunCronOnWeb()</name>
<files>src/Core/Application.php</files>
<action>
W metodzie `maybeRunCronOnWeb()` zastąp blok `try { ... $runner = new CronRunner(...) ... }` wywołaniem fabryki:
```php
$factory = new CronHandlerFactory(
$this->db,
(string) $this->config('app.integrations.secret', '')
);
$runner = $factory->build($repository, $this->logger);
$runner->run($webLimit);
```
Zachowaj blok `finally { $this->releaseWebCronLock(); }`.
Dodaj `use App\Modules\Cron\CronHandlerFactory;` do importów na górze pliku.
Usuń z `Application.php` wszystkie `use` instrukcje które były potrzebne wyłącznie dla ręcznej konstrukcji grafu crona (AllegroIntegrationRepository, AllegroOAuthClient, AllegroTokenManager, AllegroApiClient, AllegroStatusMappingRepository, AllegroOrderImportService, AllegroOrdersSyncService, AllegroOrderSyncStateRepository, AllegroStatusSyncService, ShopproApiClient, ShopproIntegrationsRepository, ShopproOrdersSyncService, ShopproOrderSyncStateRepository, ShopproPaymentStatusSyncService, ShopproStatusSyncService, ShopproStatusMappingRepository, OrderImportRepository, OrdersRepository) — TYLKO jeśli nie są używane nigdzie indziej w Application.php.
UNIKAJ usuwania `use` dla CronRunner, CronRepository, AllegroOrdersImportHandler i innych klas Cron, bo mogą być używane.
SPRAWDŹ przed usunięciem każdego `use` czy nie pojawia się w innych metodach lub właściwościach klasy.
</action>
<verify>
`php -l src/Core/Application.php` — brak błędów składni.
Metoda `maybeRunCronOnWeb()` liczy ≤25 linii (było 80+).
Grep na "new AllegroIntegrationRepository" w Application.php — brak wystąpień w metodzie maybeRunCronOnWeb.
</verify>
<done>AC-1 spełnione — Application.php nie buduje już własnego grafu crona; AC-2 spełnione — ta sama fabryka, te same handlery</done>
</task>
<task type="auto">
<name>Task 3: Uproszczenie bin/cron.php z naprawą błędów</name>
<files>bin/cron.php</files>
<action>
Zastąp całą sekcję od `$cronRepository = new CronRepository(...)` do `$runner = new CronRunner(...)` wywołaniem fabryki:
```php
$cronRepository = new CronRepository($app->db());
$factory = new CronHandlerFactory(
$app->db(),
(string) $app->config('app.integrations.secret', '')
);
$runner = $factory->build($cronRepository, $app->logger());
```
Zachowaj blok `$result = $runner->run($limit)` i `fwrite(...)`.
Zachowaj blok parsowania `--limit` z `$argv`.
Usuń wszystkie `use` instrukcje które nie są już potrzebne — zastąp je jednym `use App\Modules\Cron\CronHandlerFactory;` (obok `use App\Core\Application;` i `use App\Modules\Cron\CronRepository;`).
To NAPRAWIA dwa istniejące błędy w bin/cron.php:
- AllegroOAuthClient zamiast AllegroTokenManager w AllegroOrderImportService i AllegroOrdersSyncService
- AllegroOrderSyncStateRepository zamiast ShopproOrderSyncStateRepository dla ShopproOrdersSyncService
</action>
<verify>
`php -l bin/cron.php` — brak błędów składni.
bin/cron.php liczy ≤25 linii łącznie (było 123).
Grep "AllegroOAuthClient" w bin/cron.php — brak (usunięto nieprawidłowe użycie).
Grep "AllegroOrderSyncStateRepository" w bin/cron.php — brak (usunięto nieprawidłowe użycie).
</verify>
<done>AC-3 spełnione — bin/cron.php używa poprawnych zależności przez fabrykę</done>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- Logika wewnątrz handlerów (AllegroOrdersImportHandler, AllegroStatusSyncHandler, itp.)
- Klasa CronRunner — interfejs publiczny pozostaje niezmieniony
- Metody Application poza maybeRunCronOnWeb() i blokiem use
- routes/web.php — nie dotykamy, nie buduje handlerow crona
- Migracje i schemat bazy danych
## SCOPE LIMITS
- Nie wprowadzamy DI container — to osobny temat (CONCERNS.md [MEDIUM] No DI Container)
- Nie naprawiamy innych bugów z CONCERNS.md w tym planie
- Nie przenosimy crona do routes/web.php — fabryka jest prostszym i bardziej przenośnym rozwiązaniem
- Nie zmieniamy publicznego API CronHandlerFactory po jego stworzeniu bez potrzeby
</boundaries>
<verification>
Przed deklaracją planu jako zakończonego:
- [ ] `php -l src/Modules/Cron/CronHandlerFactory.php` — No syntax errors
- [ ] `php -l src/Core/Application.php` — No syntax errors
- [ ] `php -l bin/cron.php` — No syntax errors
- [ ] Grep "new AllegroIntegrationRepository" w Application.php — 0 wyników w maybeRunCronOnWeb
- [ ] Grep "new ShopproIntegrationsRepository" w Application.php — 0 wyników w maybeRunCronOnWeb
- [ ] Grep "AllegroOrderSyncStateRepository" w bin/cron.php — 0 wyników
- [ ] Grep "AllegroOAuthClient" w bin/cron.php — 0 wyników
- [ ] Wszystkie kryteria akceptacji AC-1, AC-2, AC-3 spełnione
</verification>
<success_criteria>
- CronHandlerFactory istnieje i buduje kompletny, poprawny graf obiektów crona
- Application::maybeRunCronOnWeb() liczy ≤25 linii (skrócone z 80+)
- bin/cron.php liczy ≤25 linii (skrócone ze 123)
- Dwa błędy w bin/cron.php naprawione (AllegroOAuthClient → AllegroTokenManager, AllegroOrderSyncStateRepository → ShopproOrderSyncStateRepository)
- Brak błędów składni PHP we wszystkich zmienionych plikach
- Brak duplikacji okablowania crona między Application.php a bin/cron.php
</success_criteria>
<output>
Po zakończeniu utwórz `.paul/phases/02-bug-fixes/02-04-SUMMARY.md`
</output>