Files
orderPRO/.paul/phases/119-reimport-total-paid-protection/119-01-SUMMARY.md
Jacek Pyziak 3a2c419c25 feat(119): protect total_paid from re-import overwrite
OrderImportRepository::updateOrderDelta() przechodzi na dynamic SET builder.
total_paid jest dolaczane do UPDATE tylko gdy payment_status realnie sie
zmienia; is_canceled_by_buyer analogicznie, ale z override przez
cancelledBySource (cancel ze zrodla nadal propaguje sie do bazy).

Chroni reczne korekty operatora (zwroty czesciowe) przed cichym
nadpisaniem z payloadu zrodla przy kolejnym sync. Incydent #976:
operator zwrocil klientowi 28,00 PLN obnizajac total_paid 119->91,
co bez tej zmiany byloby cofniete przez kolejny re-import shoppro.

Boundaries: identical-payload guard, paymentTransition, statusOverwriteAllowed,
cancel propagation (status_code='anulowane') - bez zmian.

Tests: tests/Unit/OrderImportRepositoryTest.php - 3 scenariusze
(preserve / transition / cancel propagation) via Reflection + sqlite
in-memory. PHPUnit run odroczony (vendor/ gitignored).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 14:57:04 +02:00

7.0 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, duration, started, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established duration started completed
119-reimport-total-paid-protection 01 orders
reimport
idempotency
payment
refund
delta-update
phase provides
112-reimport-data-protection delta-only updateOrderDelta + identical-payload guard
phase provides
111-payment-transition-event paymentTransition detection (0/1 -> 2)
total_paid protection when payment_status unchanged
is_canceled_by_buyer cancel-propagation override at field level
dynamic SQL SET builder in updateOrderDelta
orders re-import
manual payment corrections
refund flow
accounting
added patterns
Dynamic SET fragment builder for partial column updates (PHP array + implode)
Boolean-flag-driven conditional column inclusion in UPDATE
created modified
tests/Unit/OrderImportRepositoryTest.php
src/Modules/Orders/OrderImportRepository.php
.paul/codebase/architecture.md
.paul/codebase/tech_changelog.md
total_paid chronione gdy oldPaymentStatus === newPaymentStatus (any value, not only 2->2)
is_canceled_by_buyer chronione analogicznie, ale cancelledBySource override wymusza wpis
Brak backfillu - fix forward (order #976 poprawiony recznie)
Test flat path tests/Unit/ zamiast nested - match konwencji
Dynamic SET builder: $setFragments[] + implode(', ', ...) zamiast statycznego SQL z NULL-bindem (NULL nadpisuje, NIE pomija)
Conditional column inclusion poprzez flagi boolean wyliczane w upsertOrderAggregate i przekazywane do delta method
~20min 2026-05-12T14:00:00Z 2026-05-12T14:20:00Z

Phase 119 Plan 01: Re-import total_paid Protection Summary

updateOrderDelta() chroni total_paid (i pomocniczo is_canceled_by_buyer) przed nadpisaniem z payloadu zrodla gdy payment_status nie ulega zmianie - reczne korekty operatora (zwroty czesciowe) przezywaja re-import.

Performance

Metric Value
Duration ~20 min
Started 2026-05-12T14:00:00Z
Completed 2026-05-12T14:20:00Z
Tasks 3 completed
Files modified 4 (1 source, 1 test, 2 docs)

Acceptance Criteria Results

Criterion Status Notes
AC-1: total_paid preserved when payment_status unchanged Pass (code + test) SQL builder pomija total_paid gdy $paymentStatusUnchanged=true. Test napisany.
AC-2: total_paid updated on payment transition Pass (code + test) Gdy payment_status rozni sie (np. 0->2), fragment SET dolaczany. Test napisany.
AC-3: is_canceled_by_buyer propagated on source cancel Pass (code + test) Drugi warunek (`!$paymentStatusUnchanged
AC-4: Test coverage Partial 3 testy napisane, syntax-checked (php -l). PHPUnit run odroczony - vendor/ nieobecny, composer poza PATH.

Accomplishments

  • Order #976 (i przyszle przypadki recznych korekt total_paid) chronione przed nadpisaniem przy kolejnym sync shoppro.
  • updateOrderDelta() przeszedl ze static SQL na czytelny dynamic builder - latwo dodawac kolejne warunkowe kolumny w przyszlosci.
  • Cancel propagation ze zrodla pozostaje silnym kontraktem - flaga is_canceled_by_buyer zawsze trafia do bazy gdy zrodlo anuluje, niezaleznie od ruchu payment_status.

Task Commits

Atomic commits TBD (commit po UNIFY zgodnie z transition workflow).

Task Type Description
Task 1: Dynamic SET builder feat updateOrderDelta() warunkowe total_paid + is_canceled_by_buyer
Task 2: Test PHPUnit test 3 testy via Reflection + sqlite in-memory
Task 3: Docs update docs architecture.md + tech_changelog.md Phase 119-01

Files Created/Modified

File Change Purpose
src/Modules/Orders/OrderImportRepository.php Modified upsertOrderAggregate wylicza $paymentStatusUnchanged; updateOrderDelta() dynamic SET builder z warunkowym total_paid i is_canceled_by_buyer
tests/Unit/OrderImportRepositoryTest.php Created 3 testy PHPUnit pokrywajace AC-1/2/3 (sqlite + ReflectionMethod)
.paul/codebase/architecture.md Modified Sekcja Re-import rozszerzona o akapit Phase 119-01
.paul/codebase/tech_changelog.md Modified Nowy wpis 2026-05-12 Phase 119 Plan 01

Decisions Made

Decision Rationale Impact
paymentStatusUnchanged definiowany jako oldPaymentStatus === newPaymentStatus (any value) Konserwatywne ograniczenie do 2 == 2 byloby surprising; "any value" jest spojne z intencja "nie ruszaj gdy nic sie nie zmienilo" Operator moze recznie ustawic total_paid przy dowolnym payment_status (np. czesciowa platnosc 1) i recznie ja korygowac
is_canceled_by_buyer chronione ale override przez cancelledBySource Bez override blokowalibysmy propagacje anulowania ze zrodla - to bylby regres wzgledem Phase 112-01 Cancel ze zrodla nadal flaguje zamowienie i ustawia status_code='anulowane'
Brak CLI backfillu Order #976 poprawiony recznie; inne przypadki ad-hoc; plan maly i skupiony Mozliwe stare zamowienia z "uszkodzonym" total_paid - dopuszczalne ryzyko
Test flat path (tests/Unit/) zamiast nested (tests/Unit/Modules/Orders/) Match istniejacej konwencji projektu (wszystkie testy plasko) Spojnosc struktury testow

Deviations from Plan

Summary

Type Count Impact
Auto-fixed 0 -
Scope additions 0 -
Deferred 1 PHPUnit run odroczony do srodowiska z composer install

Total impact: Brak scope creep. Jedna deviation srodowiskowa (vendor/ gitignored).

Deferred Items

  • PHPUnit run - vendor/ nieobecne, composer poza PATH. Test napisany i syntax-checked (php -l). Manual command po composer install: vendor/bin/phpunit tests/Unit/OrderImportRepositoryTest.php. Analogiczna sytuacja do gapow w Phase 116/117 (sonar-scanner).

Issues Encountered

Issue Resolution
PHPUnit nieuruchamialny (brak vendor/) Code + test napisany, syntax check przez php -l. Run odroczony. Pattern z Phase 117 (manual verification deferred).
Plan zakladal nested test path Wybrano flat path - match konwencji projektu (wszystkie istniejace testy w tests/Unit/ plasko, namespace Tests\Unit)

Next Phase Readiness

Ready:

  • OrderImportRepository::updateOrderDelta() chroni reczne korekty total_paid przy kolejnych syncach.
  • Pattern dynamic SET buildera dostepny dla przyszlych warunkowych UPDATE-ow.

Concerns:

  • Brak realnego potwierdzenia testem zielonym (vendor unavailable). Operator powinien uruchomic composer install + phpunit przed produkcyjnym push.
  • Brak manualnego smoke testu na zywym shoppro - re-sync order #976 powinien potwierdzic, ze total_paid=91.00 przetrwa.

Blockers: None.


Phase: 119-reimport-total-paid-protection, Plan: 01 Completed: 2026-05-12