---
phase: 112-reimport-data-protection
plan: 01
subsystem: orders
tags: [order-import, re-import, data-protection, payload-guard, allegro, shoppro, pdo]

requires:
  - phase: 111-payment-transition-event
    provides: payment_transition flag + payment.status_changed emit
  - phase: 97-project-generation
    provides: order_items.project_generated, project_generated_at columns

provides:
  - Delta-only re-import for existing orders (skip replaceAddresses/Items/Notes)
  - updateOrderDelta() — narrow UPDATE to status_code, payment_status, total_paid, is_canceled_by_buyer, source_updated_at, payload_json, fetched_at
  - Source-side cancellation propagation (is_canceled_by_buyer=1 OR mapped pull status_code='anulowane' → forces orders.status_code='anulowane')
  - Identical-payload no-op guard via normalizePayloadJson()

affects:
  - Future re-import scenarios (Allegro + shopPRO cron)
  - Project generation flow (stable order_items.id)
  - Any phase relying on local order_items state (project flags, future custom fields)

tech-stack:
  added: []
  patterns:
    - "Delta-only update: re-import touches only fields that legitimately change at source"
    - "Identical-payload guard: normalize JSON via decode→encode then compare strings; skip UPDATE on equality"

key-files:
  created: []
  modified:
    - src/Modules/Orders/OrderImportRepository.php
    - .paul/codebase/architecture.md
    - .paul/codebase/tech_changelog.md

key-decisions:
  - "Skip replaceAddresses/Items/Notes on re-import (created=false) — addresses/items/notes are immutable from source perspective"
  - "Cancellation override applies AFTER status preservation block, so is_canceled_by_buyer=1 always wins"
  - "Identical-payload guard short-circuits before updateOrderDelta when no transitions detected — fetched_at and updated_at remain unchanged"
  - "replacePayments at paymentTransition unchanged — risk of overwriting manual payments (Phase 56) deferred"

patterns-established:
  - "getCurrentOrderState() — extended SELECT returning status_code + payment_status + payload_json in one query (replaces getCurrentStatusAndPaymentStatus)"
  - "normalizePayloadJson() — reusable helper for comparing two JSON representations (array or stored string) under identical encoding flags"

duration: ~25min
started: 2026-05-07T00:00:00Z
completed: 2026-05-07T00:25:00Z
---

# Phase 112 Plan 01: Re-import Data Protection Summary

**Re-import istniejacego zamowienia jest teraz delta-only: skip dla pozycji/adresow/notatek, zawezony updateOrderDelta(), propagacja anulowania ze zrodla i identical-payload guard. Bug case #882 (znikajaca flaga "Projekt") przyczynowo naprawiony.**

## Performance

| Metric | Value |
|--------|-------|
| Duration | ~25 min |
| Started | 2026-05-07T00:00:00Z |
| Completed | 2026-05-07T00:25:00Z |
| Tasks | 3 / 3 completed |
| Files modified | 3 |

## Acceptance Criteria Results

| Criterion | Status | Notes |
|-----------|--------|-------|
| AC-1: Re-import nie kasuje pozycji | Pass (code-level) | `replaceItems` przeniesione pod `if ($created)`; manualny test na zywej DB pending (XAMPP offline w sesji) |
| AC-2: Re-import nie kasuje adresow ani notatek | Pass (code-level) | `replaceAddresses` i `replaceNotes` analogicznie; manualny test pending |
| AC-3: updateOrder zawezony do delta-only | Pass (code-level) | Stara `updateOrder()` usunieta, `updateOrderDelta()` aktualizuje tylko 7 kolumn + updated_at |
| AC-4: Pierwszy import bez regresji | Pass (code-level) | Galaz `if ($created)` zachowuje pelny przebieg insertOrder + 6 replace*; manualny test importu nowego zamowienia pending |
| AC-5: Re-import emituje payment.status_changed | Pass (code-level) | Logika `paymentTransition` (linie 50-51) + `replacePayments` przy `paymentTransition`/`statusOverwriteAllowed` zachowane; AllegroOrderImportService/ShopproOrdersSyncService nieruszane |
| AC-6: Re-import propaguje anulowanie | Pass (code-level) | Override `is_canceled_by_buyer || status_code='anulowane'` po preservacji; manualny test pending |
| AC-7: Identyczny payload jest no-op | Pass (code-level) | Guard via `normalizePayloadJson` zwraca przed updateOrderDelta gdy nie ma tranzycji; manualny test pending |

**Uwaga:** Wszystkie AC zweryfikowane na poziomie kodu (struktura, kontrola przeplywu, syntax check `php -l` OK). Manualne testy na zywej bazie odlozone — XAMPP/MySQL nieosiagalne w sesji. Operator powinien przeprowadzic AC-1, AC-3, AC-6, AC-7 po wdrozeniu.

## Accomplishments

- Bug case #882 przyczynowo naprawiony — `order_items.id` i flagi `project_generated`/`project_generated_at` (Phase 97) sa stabilne miedzy re-importami.
- Phase 111 (`payment.status_changed` event) dziala bez regresji — emit logiki nieruszany.
- Re-import nie nadpisuje juz pol zamowienia, ktore moga byc zmienione w orderPRO (zamowienie jest w docelowym narzedziu, nie w zrodle).
- Zerowe write'y do DB przy cyklicznym imporcie identycznych zamowien (identical-payload guard).
- Dokumentacja `.paul/codebase/architecture.md` i `.paul/codebase/tech_changelog.md` zaktualizowane.

## Task Commits

| Task | Commit | Type | Description |
|------|--------|------|-------------|
| Task 1: Rozdzielic create vs update w upsertOrderAggregate | (uncommitted) | feat | replace* tylko przy `created=true` |
| Task 2: updateOrderDelta + cancellation + payload guard | (uncommitted) | feat | Zawezony UPDATE, override anulowania, identical-payload no-op |
| Task 3: Architecture + tech_changelog | (uncommitted) | docs | Sekcja Re-import w architecture.md, wpis 2026-05-07 |

Plan metadata: bedzie commit'owany w ramach transition-phase (`feat(112): re-import data protection`).

## Files Created/Modified

| File | Change | Purpose |
|------|--------|---------|
| `src/Modules/Orders/OrderImportRepository.php` | Modified | Delta-only re-import, updateOrderDelta(), getCurrentOrderState(), normalizePayloadJson(), cancellation override |
| `.paul/codebase/architecture.md` | Modified | Sekcja Re-import (Phase 111 + 112) opisuje delta-only flow |
| `.paul/codebase/tech_changelog.md` | Modified | Wpis 2026-05-07 — Phase 112 Plan 01 |

## Decisions Made

| Decision | Rationale | Impact |
|----------|-----------|--------|
| Skip replaceAddresses/Items/Notes na re-imporcie | Pozycje/adresy/notatki sa de facto immutable ze zrodla; orderPRO jest narzedziem zarzadzania — edycja po stronie aplikacji, nie zrodla | Stabilne `order_items.id` i ochrona flag lokalnych (project_generated, przyszle pola) |
| updateOrderDelta z 7 kolumnami | Tylko payment_status/total_paid/is_canceled_by_buyer/source_updated_at/payload_json/fetched_at + status_code (warunkowo) realnie zmieniaja sie miedzy syncami | Eliminacja nadpisywania pol typu delivery_price, send_date_*, customer_login |
| Override anulowania niezalezny od statusOverwriteAllowed | Anulowanie ze zrodla musi przejsc nawet gdy status lokalny zostal recznie zmieniony | Spojnosc z business logic — anulowane zamowienie nie powinno wisiec w `w_realizacji` |
| Identical-payload guard via normalize→compare | Tani check eliminujacy niepotrzebne write'y; nie wymaga schema change | Zerowa praca dla cyklicznego importu identycznych zamowien |
| `replacePayments` przy `paymentTransition` zostaje | Decyzja "nie wiem" od uzytkownika; zachowanie z Phase 111 minimalizuje zmiany | Ryzyko nadpisania recznych platnosci (Phase 56) — odlozone jako deferred |

## Deviations from Plan

### Summary

| Type | Count | Impact |
|------|-------|--------|
| Auto-fixed | 0 | Brak |
| Scope additions | 0 | Plan wykonany 1:1 |
| Deferred | 2 | Manualne testy na DB, ryzyko replacePayments |

**Total impact:** Plan wykonany zgodnie z PLAN.md, bez scope creep. Manualne testy AC pending (XAMPP offline).

### Deferred Items

- **Manualne testy AC-1..AC-7 na zywej bazie** — wymagaja MySQL/XAMPP online; do przeprowadzenia przez operatora po wdrozeniu. Punkty wyjscia w PLAN.md `<verify>` blokach Task 1 i Task 2.
- **Backfill #882** — operator wykonuje recznie po wdrozeniu (decyzja uzytkownika z PLAN.md `<clarifications>`). Sugerowane query: `UPDATE order_items SET project_generated=1, project_generated_at=NOW() WHERE order_id=882 AND id IN (...);` po identyfikacji pozycji z fizycznie wygenerowanymi PSD.
- **`replacePayments` przy paymentTransition** — DELETE+INSERT moze nadpisac recznie dodane platnosci (Phase 56). Odlozone do oceny po wdrozeniu Phase 112; ewentualny fix w przyszlym planie (np. append po `(order_id, source_payment_id)` UNIQUE).

## Issues Encountered

| Issue | Resolution |
|-------|------------|
| MySQL/XAMPP offline w sesji — brak mozliwosci weryfikacji manualnej | AC zweryfikowane na poziomie kodu + `php -l`; manualne testy odlozone do operatora |

## Next Phase Readiness

**Ready:**
- Re-import delta-only dziala — Phase 111 chain (payment.status_changed → automatyzacja #7) zachowany.
- `order_items.id` jest stabilny — skrypt `tools/generowanie/_batch_run.sh` moze polegac na id miedzy re-importami.
- Schema DB nieruszane — bez migracji.

**Concerns:**
- `replacePayments` nadal DELETE+INSERT przy `paymentTransition` — moze wymagac patch w przyszlosci.
- Manualne AC pending — jezeli ktorys nie przejdzie na zywej bazie, wymagany hotfix.
- Identical-payload guard zaklada deterministyczny porzadek kluczy w JSON. Jezeli zrodlo (Allegro/shopPRO) reorderuje klucze miedzy syncami, guard nie bedzie blokowac — fail-open, bezpieczny.

**Blockers:**
- None (manualne testy nie blokuja transition; sa standardowym deferred follow-up).

---
*Phase: 112-reimport-data-protection, Plan: 01*
*Completed: 2026-05-07*
