---
phase: 98-order-imported-first-only
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - src/Modules/Settings/AllegroOrderImportService.php
  - src/Modules/Settings/ShopproOrdersSyncService.php
  - bin/backfill_shipped_status_98.php
autonomous: true
delegation: off
---

<objective>
## Goal
Zdarzenie automatyzacji `order.imported` ma się uruchamiać wyłącznie przy PIERWSZYM imporcie zamówienia (gdy rekord jest tworzony), a nie przy każdej aktualizacji z marketplace'u. Dodatkowo: jednorazowy backfill — zamówienia obecnie w statusie "w realizacji", które już mają utworzoną przesyłkę, zostaną przestawione na status "wysłane".

## Purpose
Aktualnie reguły automatyzacji typu "Pobranie zamówienia" reagują też na powtórne pobranie tego samego zamówienia z Allegro/shopPRO. Skutek: zamówienia oznaczone wcześniej jako "wysłane" są przez regułę cofane do "w realizacji". Trzeba przerwać tę regresję i posprzątać dane historyczne.

## Output
- 2 punkty trigger (`AllegroOrderImportService`, `ShopproOrdersSyncService`) emitują `order.imported` tylko dla `$wasCreated === true`.
- Skrypt `bin/backfill_shipped_status_98.php` jednorazowo poprawia statusy uszkodzonych zamówień.
</objective>

<context>
@.paul/PROJECT.md
@.paul/STATE.md
@CLAUDE.md
@DOCS/DB_SCHEMA.md
@src/Modules/Settings/AllegroOrderImportService.php
@src/Modules/Settings/ShopproOrdersSyncService.php
@src/Modules/Automation/AutomationService.php
@src/Modules/Orders/OrdersRepository.php
@.paul/phases/84-order-imported-automation-event/84-01-SUMMARY.md
</context>

<acceptance_criteria>

## AC-1: Allegro re-import nie odpala order.imported
```gherkin
Given zamówienie Allegro istnieje już w bazie (np. status "wysłane")
When cron Allegro ponownie pobiera to zamówienie ($wasCreated === false)
Then AutomationService::trigger('order.imported', ...) NIE jest wywoływane
And żadna reguła z eventem "Pobranie zamówienia" się nie uruchamia
```

## AC-2: ShopPRO re-import nie odpala order.imported
```gherkin
Given zamówienie shopPRO istnieje już w bazie
When ShopproOrdersSyncService importuje je ponownie ($wasCreated === false, brak payment transition)
Then AutomationService::trigger('order.imported', ...) NIE jest wywoływane
```

## AC-3: Pierwszy import nadal triggeruje event
```gherkin
Given nowe zamówienie nieznane lokalnie
When import zapisuje rekord ($wasCreated === true)
Then AutomationService::trigger('order.imported', ...) jest wywoływane jeden raz z payloadem zawierającym created=true
```

## AC-4: Backfill statusów
```gherkin
Given w bazie istnieją zamówienia, których aktualny status to "w realizacji"
And zamówienia te mają co najmniej jedną przesyłkę w `shipment_packages`
When uruchomię `php bin/backfill_shipped_status_98.php`
Then status każdego takiego zamówienia zostanie zmieniony na "wysłane" przez OrdersRepository::updateOrderStatus
And w `order_status_history` powstanie wpis o zmianie z autorem "system/backfill-98"
And w `order_activity_log` powstanie wpis informacyjny
And skrypt wypisze podsumowanie: liczba przeanalizowanych, liczba zmienionych, liczba pominiętych
```

</acceptance_criteria>

<tasks>

<task type="auto">
  <name>Task 1: Gate order.imported na pierwszy import (Allegro + shopPRO)</name>
  <files>src/Modules/Settings/AllegroOrderImportService.php, src/Modules/Settings/ShopproOrdersSyncService.php</files>
  <action>
    1. `AllegroOrderImportService.php` ~linie 99-106: warunek
       `if ($this->automationService !== null) { ... }`
       zmień na
       `if ($wasCreated && $this->automationService !== null) { ... }`
       Pozostaw payload bez zmian (`created => $wasCreated` zostaje, ale teraz zawsze true).
    2. `ShopproOrdersSyncService.php` ~linie 273-280: warunek
       `if ($savedOrderId > 0 && !$wasPaymentTransition && $this->automationService !== null)`
       rozszerz o `$wasCreated`:
       `if ($savedOrderId > 0 && $wasCreated && !$wasPaymentTransition && $this->automationService !== null)`
    3. NIE zmieniaj nic w `AutomationService` ani w samych regułach — semantyka eventu pozostaje, tylko trigger jest węższy.
    4. Zachowaj istniejącą logikę `recordActivity` (ona ma się wykonywać dla każdego importu — bez zmian).
    Avoid: dodawania nowych pól do payloadu, zmiany list eventów, modyfikacji ShopproPaymentStatusSyncService (payment transition flow nieobjęty).
  </action>
  <verify>
    1. `php -l src/Modules/Settings/AllegroOrderImportService.php`
    2. `php -l src/Modules/Settings/ShopproOrdersSyncService.php`
    3. Manualny test: w panelu Ustawienia > Zadania automatyczne stworzyć regułę z eventem "Pobranie zamowienia" i akcją "update_order_status -> realizacja". Wymusić ponowny import istniejącego zamówienia (cron Allegro / przycisk synchronizacji). Status zamówienia NIE zmienia się. W tabie Historia automatyzacji brak nowego wpisu dla tej reguły.
    4. Drugi test: nowe zamówienie pobrane po raz pierwszy — reguła odpala się dokładnie raz.
  </verify>
  <done>AC-1, AC-2, AC-3 spełnione</done>
</task>

<task type="auto">
  <name>Task 2: Skrypt backfill — orders w realizacji z przesyłką -> wysłane</name>
  <files>bin/backfill_shipped_status_98.php</files>
  <action>
    Utwórz CLI skrypt jednorazowy po wzorze innych skryptów w `bin/` (bootstrap przez `bin/bootstrap.php` lub odpowiednik używany przez aktualne skrypty crona — sprawdź jak to robią `bin/cron.php` i `bin/randomize_order_statuses.php`).

    Logika:
    1. Pobierz `OrdersRepository` przez `CronHandlerFactory` lub bezpośrednio (zgodnie z konwencją bin/).
    2. Z `order_statuses` odczytaj id statusu o `code = 'realizacja'` (lub odpowiednim — dopasuj do faktycznych kodów w bazie; jeżeli kod jest inny, np. `processing`/`in_progress`, użyj tego, który widoczny jest w UI jako "W realizacji"). Analogicznie id statusu "Wysłane" (`code = 'wyslane'` / `shipped`).
    3. Query: `SELECT o.id FROM orders o WHERE o.order_status_id = :realizacja_id AND EXISTS (SELECT 1 FROM shipment_packages sp WHERE sp.order_id = o.id)`.
    4. Dla każdego id wywołaj `OrdersRepository::updateOrderStatus($orderId, $shippedStatusId, 'system/backfill-98', 'Backfill 98: zamowienie w realizacji posiadalo przesylke')` (sygnaturę dopasuj do istniejącej metody używanej np. w akcji automatyzacji `update_order_status`).
    5. Licz: total / updated / skipped (skipped gdy update zwrócił false albo rzucił wyjątek — złapać i zalogować, kontynuować).
    6. Na końcu wypisz podsumowanie: `Backfill 98: total={N} updated={U} skipped={S}`.
    7. Skrypt ma być idempotentny — drugie uruchomienie na czystej bazie nie zrobi nic.

    Avoid: bezpośredniego `UPDATE orders SET order_status_id=...` (omija historię i activity log); hardcodowania liczbowych ID statusów (zawsze przez `code`); używania innego mechanizmu zmiany statusu niż `OrdersRepository::updateOrderStatus`.
  </action>
  <verify>
    1. `php -l bin/backfill_shipped_status_98.php`
    2. Najpierw DRY: zakomentowany update + var_dump kandydatów — zweryfikować na 1-2 zamówieniach.
    3. `php bin/backfill_shipped_status_98.php` — sprawdzić output, sprawdzić w UI że wybrane zamówienia są na "Wysłane", w detalach mają wpis w historii statusu i activity logu.
    4. Drugie uruchomienie: `total=0 updated=0 skipped=0`.
  </verify>
  <done>AC-4 spełnione</done>
</task>

</tasks>

<boundaries>

## DO NOT CHANGE
- `AutomationService` (logika dispatch eventów stabilna — phase 84/96)
- `ShopproPaymentStatusSyncService` (oddzielny flow payment_status)
- Definicja `ALLOWED_EVENTS` w `AutomationController`
- Reguły automatyzacji w bazie (żadnych migracji danych poza backfillem statusów)
- Mapowania pull statusów Allegro/shopPRO

## SCOPE LIMITS
- Nie dodajemy nowego eventu (`order.first_imported`) — gating po stronie producenta wystarczy
- Backfill jest jednorazowy: skrypt CLI w `bin/`, bez UI ani crona
- Nie zmieniamy semantyki `created` w payloadzie (pozostaje `true`)
- Brak refaktoryzacji `recordActivity` / dedupe logiki
</boundaries>

<verification>
- [ ] `php -l` na obu zmodyfikowanych plikach źródłowych
- [ ] `php -l` na nowym skrypcie bin
- [ ] Manualny test: re-import zamówienia nie odpala reguły "Pobranie zamowienia"
- [ ] Manualny test: pierwszy import nowego zamówienia odpala regułę dokładnie raz
- [ ] Backfill wykonany na produkcyjnej (lokalnej) bazie, statusy zmienione, historia + activity log obecne
- [ ] Drugie uruchomienie backfillu: 0 zmian
- [ ] Wszystkie AC potwierdzone
</verification>

<success_criteria>
- Re-import zamówienia nie regresuje statusów wysłanych zamówień
- Historyczne uszkodzone dane są naprawione przez backfill
- Brak zmian w niepowiązanych modułach
- Zero nowych warningów PHP
</success_criteria>

<output>
Po ukończeniu utwórz `.paul/phases/98-order-imported-first-only/98-01-SUMMARY.md`
</output>
