feat(125): invoice_requested import fix + drop legacy is_invoice column
- shopPRO: ShopproOrderMapper jako jedyne zrodlo heurystyki detekcji faktury; mapOrderAggregate() zwraca top-level invoice_detected (transient). - ShopproOrdersSyncService: usunieta wlasna shouldRequestInvoice(); propagacja aggregate['invoice_detected'] do setInvoiceRequested() tylko przy created=true. - Allegro: nowa shouldRequestInvoice(payload) z 4 wzorcami (invoice.required, naturalPerson=false, address.taxId, companyName/address.company.name). Wczesniej tylko invoice.required -> analogiczna luka jak shopPRO. - Migracja 20260513_000113: idempotentny backfill (UPDATE invoice_requested=1 WHERE is_invoice=1 AND invoice_requested=0) + DROP COLUMN orders.is_invoice. Guard przez information_schema.COLUMNS + PREPARE/EXECUTE z ALTER TABLE COMMENT no-op fallbackiem (portable MySQL/MariaDB). - Cleanup is_invoice z OrderImportRepository (INSERT cols/values/params, docstring Phase 112) i OrdersRepository (paginate SELECT, transformOrderRow hydrate). AllegroOrderImportService mapping w mapCheckoutFormPayload tez usuniety (wymuszone konsekwencja DROP COLUMN). - Bugfix #1089: zamowienie shopPRO z firm_nip (bez wants_invoice/invoice.required) ustawia teraz invoice_requested=1 -> UI w zakladce Platnosci zaznacza checkbox, przycisk "Wystaw fakture" widoczny. Pending operator: php bin/migrate.php (XAMPP MySQL online) -> backfill 7 zamowien. Smoke test: re-import shopPRO + nowe Allegro z NIP.
This commit is contained in:
167
.paul/phases/125-invoice-requested-import-fix/125-01-SUMMARY.md
Normal file
167
.paul/phases/125-invoice-requested-import-fix/125-01-SUMMARY.md
Normal file
@@ -0,0 +1,167 @@
|
||||
---
|
||||
phase: 125-invoice-requested-import-fix
|
||||
plan: 01
|
||||
subsystem: orders-import
|
||||
tags: [shoppro, allegro, invoices, import, migration, fakturownia]
|
||||
|
||||
requires:
|
||||
- phase: 115-invoice-from-order
|
||||
provides: orders.invoice_requested column + setInvoiceRequested API + UI toggle
|
||||
- phase: 113-fakturownia-integration-foundation
|
||||
provides: invoice_requested schema baseline
|
||||
|
||||
provides:
|
||||
- Bugfix shopPRO/Allegro: invoice_requested auto-set ujednolicony z heurystyka mappera
|
||||
- DROP COLUMN orders.is_invoice (legacy z Phase 115) + backfill 7 zamowien
|
||||
- Wspolny kontrakt detekcji: ShopproOrderMapper.invoice_detected (aggregate top-level) + AllegroOrderImportService::shouldRequestInvoice()
|
||||
|
||||
affects: [future invoice automation, invoice.created event, fakturownia idempotency]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Mapper jako jedyne zrodlo heurystyki detekcji (sync service propaguje, nie duplikuje)"
|
||||
- "Migracje DROP COLUMN idempotentne przez information_schema.COLUMNS guard + prepared statement"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- database/migrations/20260513_000113_drop_orders_is_invoice_and_backfill_invoice_requested.sql
|
||||
modified:
|
||||
- src/Modules/Settings/ShopproOrderMapper.php
|
||||
- src/Modules/Settings/ShopproOrdersSyncService.php
|
||||
- src/Modules/Settings/AllegroOrderImportService.php
|
||||
- src/Modules/Orders/OrderImportRepository.php
|
||||
- src/Modules/Orders/OrdersRepository.php
|
||||
- .paul/codebase/db_schema.md
|
||||
- .paul/codebase/architecture.md
|
||||
- .paul/codebase/tech_changelog.md
|
||||
|
||||
key-decisions:
|
||||
- "Mapper jako jedyne zrodlo heurystyki shopPRO — sync service propaguje aggregate['invoice_detected'] zamiast wlasnej listy kluczy"
|
||||
- "Allegro shouldRequestInvoice rozszerzony o invoice.naturalPerson=false / invoice.address.taxId / invoice.companyName"
|
||||
- "DROP COLUMN orders.is_invoice w tej samej migracji co backfill (eliminacja dryftu architekturalnego, nie tylko fixu objawowego)"
|
||||
- "Migracja idempotentna przez information_schema guard zamiast DROP COLUMN IF EXISTS (portable MySQL/MariaDB)"
|
||||
|
||||
patterns-established:
|
||||
- "Top-level klucz 'invoice_detected' w mapOrderAggregate() — pattern dla transient flag detection (nie pisanej do DB ale konsumowanej przez sync service)"
|
||||
- "Idempotentny DROP COLUMN: SET @col_exists := (SELECT COUNT(*) FROM information_schema.COLUMNS...) + PREPARE/EXECUTE z fallbackiem ALTER TABLE COMMENT no-op"
|
||||
|
||||
duration: ~30min
|
||||
started: 2026-05-13T00:45:00Z
|
||||
completed: 2026-05-13T01:15:00Z
|
||||
---
|
||||
|
||||
# Phase 125 Plan 01: invoice_requested Import Fix Summary
|
||||
|
||||
**shopPRO order z `firm_nip` (bez explicit invoice.required flagi) ustawia teraz `invoice_requested=1` przy imporcie; Allegro rozszerzony o detekcje NIP/naturalPerson/companyName; legacy `orders.is_invoice` usunieta z DB i kodu (eliminacja dryftu architekturalnego z Phase 115).**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~30 min |
|
||||
| Started | 2026-05-13T00:45:00Z |
|
||||
| Completed | 2026-05-13T01:15:00Z |
|
||||
| Tasks | 4/4 completed |
|
||||
| Files modified | 8 (5 PHP + 1 SQL + 3 docs) |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Import shopPRO z firm_nip ustawia invoice_requested | Pass (kod) / Pending UAT | Kod: `ShopproOrdersSyncService::importOne` propaguje `aggregate['invoice_detected']`; mapper heurystyka obejmuje firm_name/firm_nip. UAT: smoke test po migracji. |
|
||||
| AC-2: Import Allegro z NIP ustawia invoice_requested | Pass (kod) / Pending UAT | `AllegroOrderImportService::shouldRequestInvoice` sprawdza required/naturalPerson=false/taxId/companyName. UAT: brak aktywnego zamowienia Allegro z samym NIP do potwierdzenia w tej sesji. |
|
||||
| AC-3: Re-import nie nadpisuje manualnego toggla | Pass (kod) | Guard `wasCreated=true` zachowany w obu importerach (kontrakt Phase 115/112). |
|
||||
| AC-4: Backfill istniejących zamówień | Pass (migracja gotowa) / Pending operator | Migracja idempotentna; 7 zamowien czeka na `php bin/migrate.php` (operator + XAMPP MySQL online). |
|
||||
| AC-5: Kolumna is_invoice nie istnieje w runtime | Pass (kod, czeka migracji) | Grep `is_invoice` w `src/`: tylko 1 trafienie (ShopproOrderMapper:388 — klucz payloadu, nie kolumna DB). Aplikacja nie referencuje juz kolumny `orders.is_invoice` — gotowa na DROP COLUMN. |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- **Bugfix #1089**: shopPRO order z `firm_nip` (klient firmowy bez `wants_invoice`/`invoice_required`/`invoice.required` flag) ustawia teraz `invoice_requested=1` przy pierwszym imporcie. UI w zakladce Platnosci pokazuje zaznaczony checkbox, przycisk "Wystaw fakture" jest dostepny.
|
||||
- **Fix architekturalny**: dwie kolumny dla tej samej semantyki (`is_invoice` + `invoice_requested`) i dwie sciezki detekcji (mapper + sync service) ujednolicone do jednego zrodla prawdy. Mapper jest jedyna heurystyka, sync service propaguje wynik.
|
||||
- **Allegro symmetric fix**: detekcja faktury rozszerzona o `invoice.naturalPerson=false`, `invoice.address.taxId`, `invoice.companyName`/`invoice.address.company.name` (wczesniej tylko `invoice.required`).
|
||||
- **Idempotentna migracja**: pattern `information_schema.COLUMNS` + `PREPARE/EXECUTE` z fallbackiem `ALTER TABLE COMMENT` no-op — portable na czysty MySQL (nie wymaga `DROP COLUMN IF EXISTS` MariaDB-only).
|
||||
|
||||
## Task Commits
|
||||
|
||||
Atomic commits zaplanowane na transition (delegation: off, inline execution; brak commitow w trakcie APPLY). Wszystkie zmiany w roboczym tree, gotowe do `feat(125): invoice_requested import fix` commit.
|
||||
|
||||
| Task | Status | Description |
|
||||
|------|--------|-------------|
|
||||
| Task 1: Mapper exposes detection, importers propagate | Done | ShopproOrderMapper.invoice_detected aggregate key + propagacja w sync service + Allegro shouldRequestInvoice |
|
||||
| Task 2: Migration backfill + DROP COLUMN | Done | 20260513_000113 idempotent SQL |
|
||||
| Task 3: Remove is_invoice from PHP | Done | OrderImportRepository INSERT + OrdersRepository SELECT/hydrate |
|
||||
| Task 4: Update docs | Done | db_schema.md note + architecture.md sekcja invoice_requested + tech_changelog.md wpis |
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `src/Modules/Settings/ShopproOrderMapper.php` | Modified | Usuniety klucz `is_invoice` z `order`; dodany top-level `invoice_detected` w `mapOrderAggregate()` |
|
||||
| `src/Modules/Settings/ShopproOrdersSyncService.php` | Modified | `shouldRequestInvoice()` usunieta; propagacja `!empty($aggregate['invoice_detected'])` w `importOne()` |
|
||||
| `src/Modules/Settings/AllegroOrderImportService.php` | Modified | `shouldRequestInvoice($payload)` (nowa) z 4 wzorcami heurystyki; mapping `is_invoice` w `mapCheckoutFormPayload()` usuniety |
|
||||
| `src/Modules/Orders/OrderImportRepository.php` | Modified | Usuniety `is_invoice` z INSERT (kolumny + values + orderParams); docstring `updateOrderDelta()` (Phase 112) zaktualizowany |
|
||||
| `src/Modules/Orders/OrdersRepository.php` | Modified | Usuniety `o.is_invoice` z SELECT (`paginate` query); usuniety `is_invoice` klucz z `transformOrderRow()` hydrate |
|
||||
| `database/migrations/20260513_000113_drop_orders_is_invoice_and_backfill_invoice_requested.sql` | Created | Idempotentny backfill + DROP COLUMN przez information_schema guard |
|
||||
| `.paul/codebase/db_schema.md` | Modified | Updated date + note pod tabela `orders` o usunieciu `is_invoice` |
|
||||
| `.paul/codebase/architecture.md` | Modified | Sekcja "Auto-import flagi invoice_requested" rozszerzona o Phase 125 zmiany (shopPRO mapper-driven + Allegro heurystyka NIP) |
|
||||
| `.paul/codebase/tech_changelog.md` | Modified | Prepend wpis Phase 125-01 z Co/Dlaczego/BREAKING |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| Mapper jako jedyne zrodlo heurystyki (top-level `invoice_detected` w aggregate) | Eliminuje dryft sync service vs. mapper. Sync service propaguje, nie duplikuje listy kluczy | Pattern dla przyszlych detection-flag (gdy mapper juz robi heurystyka, sync service czyta wynik) |
|
||||
| DROP COLUMN `is_invoice` razem z fixem detekcji | Korzen buga to dwie kolumny dla tej samej semantyki. Naprawa objawu (UI sync) bez usuwania struktury zostawia dryftowy potential dla kolejnych fix'ow | Cleaner architecture, +1 BREAKING (legacy column gone) |
|
||||
| Migracja idempotentna przez `information_schema.COLUMNS` guard | `DROP COLUMN IF EXISTS` to MariaDB only; portable MySQL pattern + idempotencja dla re-runow | Pattern dla przyszlych migracji DROP COLUMN |
|
||||
| Allegro shouldRequestInvoice z 4 wzorcami (required / naturalPerson / taxId / companyName) | Allegro miala analogiczna luke: klient firmowy z NIP ale bez `required=true` (np. checkout-form gdzie NIP zapisany w invoice.address ale invoice.required niewymaganie zaznaczony) | Symmetric fix shopPRO + Allegro w jednej fazie zamiast osobnego ticketu |
|
||||
| AllegroOrderImportService: usuniecie `is_invoice` z `mapCheckoutFormPayload()` poza scope Task 1 | Bez tego INSERT by sie wywalil po migracji (kolumna znika) — wymuszone przez DROP COLUMN, zalogowane w deviations | Scope addition wymuszony konsekwencja DROP COLUMN; bez ryzyka |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Auto-fixed | 1 | Niezbedny side-effect DROP COLUMN |
|
||||
| Scope additions | 0 | — |
|
||||
| Deferred | 0 | — |
|
||||
|
||||
**Total impact:** Niezbedna korekta w AllegroOrderImportService nieuwzgledniona explicite w PLAN.md Task 1; logiczny side-effect DROP COLUMN.
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. AllegroOrderImportService::mapCheckoutFormPayload: usuniecie `'is_invoice' => !empty($invoice['required'])` (linia 235)**
|
||||
- **Found during:** Task 1 (inspekcja AllegroOrderImportService przy dodawaniu `shouldRequestInvoice`)
|
||||
- **Issue:** PLAN Task 1 opisuje tylko zmiany w `importSingleOrder` (linie 99-103) dla Allegro. PLAN Task 3 dotyczy `OrderImportRepository`/`OrdersRepository`. Mapping `is_invoice` w Allegro pozostalby pisany do `$mapped['order']`, ktore potem `OrderImportRepository::insertOrder` wstawia do DB — po DROP COLUMN to powodowaloby SQL error.
|
||||
- **Fix:** Usuniety klucz `is_invoice` z mapowania w `mapCheckoutFormPayload()` (linia 235).
|
||||
- **Files:** `src/Modules/Settings/AllegroOrderImportService.php`
|
||||
- **Verification:** `grep is_invoice src/Modules/Settings/AllegroOrderImportService.php` → 0 trafien po edit; lint pass.
|
||||
- **Commit:** w zakresie wspolnego commitu phase-125.
|
||||
|
||||
### Deferred Items
|
||||
|
||||
Brak — plan wykonany pelnie.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
| Issue | Resolution |
|
||||
|-------|------------|
|
||||
| Allegro `is_invoice` mapping w mapCheckoutFormPayload poza explicite scope PLAN | Self-fix w Task 1 (logiczny side-effect DROP COLUMN); zalogowane w Deviations.Auto-fixed |
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Bugfix #1089 wdrozony w kodzie; gotowe do smoke testu po migracji.
|
||||
- Allegro symmetric fix — eliminacja analogicznej luki bez czekania na osobny ticket.
|
||||
- Migracja idempotentna — pattern do reuse w przyszlych DROP COLUMN.
|
||||
|
||||
**Concerns:**
|
||||
- AC-1/AC-2 wymagaja UAT na zywej bazie po `php bin/migrate.php` — operator musi potwierdzic na shopPRO 1089 i nowym Allegro zamowieniu z NIP.
|
||||
- Brak PHPUnit testow dla `shouldRequestInvoice` (Allegro) i `invoice_detected` (shopPRO mapper). Boundary explicit; mozliwe follow-up plan.
|
||||
|
||||
**Blockers:** None.
|
||||
|
||||
---
|
||||
*Phase: 125-invoice-requested-import-fix, Plan: 01*
|
||||
*Completed: 2026-05-13*
|
||||
Reference in New Issue
Block a user