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:
@@ -258,9 +258,11 @@ tests/
|
||||
- `OrdersController::toggleInvoiceRequested` — POST `/orders/{id}/invoice-requested/toggle`. CSRF, JSON response `{success, invoice_requested}`. Loguje `order_activity_log` z `event_type='invoice_requested_changed'`.
|
||||
- `public/assets/js/modules/invoice-requested-toggle.js` — vanilla JS, idempotent guard `dataset.bound='1'`. AJAX POST przy `change`, optimistic show/hide `[data-invoice-button-wrap]`. Rollback checkbox przy HTTP/network blad.
|
||||
|
||||
### Auto-import flagi invoice_requested
|
||||
- `AllegroOrderImportService::importSingleOrder` — przy `wasCreated=true` jezeli `payload.invoice.required` truthy -> `setInvoiceRequested(true)`. Tylko pierwszy import (delta-only re-import nie nadpisuje manualnej zmiany).
|
||||
- `ShopproOrdersSyncService::shouldRequestInvoice($rawOrder)` — flexible parser sprawdzajacy `wants_invoice`, `invoice_required`, `invoice.required`, `buyer.wants_invoice`, `buyer.invoice` (akceptuje true/1/'1'/'true'/'yes'/'tak'). Wywolany tylko przy `wasCreated=true`.
|
||||
### Auto-import flagi invoice_requested (zaktualizowane Phase 125-01)
|
||||
- **shopPRO:** `ShopproOrderMapper::resolveInvoiceRequested($payload)` jest jedynym zrodlem heurystyki — sprawdza top-level klucze payloadu: `is_invoice`, `invoice.required`, `invoice` (bool), oraz obecnosc danych firmowych (`firm_name`/`firm_nip`/`invoice.company_name`/`invoice.tax_id`/`invoice.nip`/`company_name`/`tax_id`/`nip` etc.). Wynik eksponowany w `mapOrderAggregate()` jako top-level klucz `invoice_detected` (transient, nie pisany do DB). `ShopproOrdersSyncService::importOne` propaguje `!empty($aggregate['invoice_detected'])` do `setInvoiceRequested(true)` tylko przy `wasCreated=true`. Stara metoda `shouldRequestInvoice` w sync service usunieta (zastapiona heurystyka mappera).
|
||||
- **Allegro:** `AllegroOrderImportService::shouldRequestInvoice($payload)` sprawdza w kolejnosci: `invoice.required` (truthy), `invoice.naturalPerson === false` (klient firmowy), `invoice.address.taxId` (NIP w adresie faktury), `invoice.companyName`/`invoice.address.company.name`. Wywolane tylko przy `wasCreated=true` w `importSingleOrder`. Rozszerzenie heurystyki naprawia luke gdy klient Allegro podaje NIP bez ustawiania `invoice.required=true`.
|
||||
- **Kontrakt Phase 115/112 zachowany:** auto-set TYLKO przy `wasCreated=true`. Delta-only re-import nie nadpisuje manualnej flagi operatora (manualny toggle przez `/orders/{id}/invoice-requested/toggle` przezywa kolejne synchronizacje).
|
||||
- **Legacy:** kolumna `orders.is_invoice` (Phase 115) usunieta migracja `20260513_000113_drop_orders_is_invoice_and_backfill_invoice_requested.sql`. Backfill: 7 zamowien gdzie `is_invoice=1 AND invoice_requested=0` dostalo `invoice_requested=1` przed DROP COLUMN.
|
||||
|
||||
### View hierarchy
|
||||
- `accounting/invoice_form.php` — formularz wystawiania.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Database Schema
|
||||
|
||||
**Updated:** 2026-05-12 | **Total tables:** 61 | **Engine:** InnoDB | **Charset:** utf8mb4_unicode_ci
|
||||
**Updated:** 2026-05-13 | **Total tables:** 61 | **Engine:** InnoDB | **Charset:** utf8mb4_unicode_ci
|
||||
|
||||
---
|
||||
|
||||
@@ -275,6 +275,8 @@ UNIQUE: `(integration_id, external_product_id, external_variant_id)`
|
||||
UNIQUE: `(integration_id, external_order_id)`
|
||||
|
||||
> Note: Order notes are stored in the separate `order_notes` table (no `notes` column on `orders`).
|
||||
>
|
||||
> Note (Phase 125-01, 2026-05-13): `invoice_requested` (Phase 113-01) jest jedynym znacznikiem zadania faktury. Legacy kolumna `is_invoice` zostala usunieta w Phase 125-01 (migracja `20260513_000113_drop_orders_is_invoice_and_backfill_invoice_requested.sql`), bo dryfowala wzgledem `invoice_requested` (mapper pisal do `is_invoice`, UI czytalo `invoice_requested` — bug #1089).
|
||||
|
||||
**order_items** — Line items within orders
|
||||
| Column | Type | Nullable | Notes |
|
||||
|
||||
@@ -1,5 +1,28 @@
|
||||
# Technical Changelog
|
||||
|
||||
## 2026-05-13 - Phase 125 Plan 01: invoice_requested Import Fix
|
||||
|
||||
**Co zrobiono:**
|
||||
- Bugfix #1089: shopPRO order z `firm_nip` (bez kluczy z 5-elementowej listy `shouldRequestInvoice`) nie ustawial `invoice_requested=1` przy imporcie. Mapper wykrywal poprawnie (`is_invoice` przez heurystyke NIP), ale `ShopproOrdersSyncService::shouldRequestInvoice` mial wezsza liste kluczy -> UI w zakladce Platnosci wyswietlal odznaczony checkbox.
|
||||
- `ShopproOrderMapper::mapOrderAggregate()` zwraca teraz top-level klucz `invoice_detected` (wynik `resolveInvoiceRequested($payload)`). Klucz `is_invoice` usuniety z tablicy `order` (nie odpowiada juz zadnej kolumnie DB).
|
||||
- `ShopproOrdersSyncService::importOne()` propaguje `!empty($aggregate['invoice_detected'])` do `setInvoiceRequested(true)` zamiast wlasnej heurystyki. Stara metoda `shouldRequestInvoice` usunieta (zastapiona heurystyka mappera — zero duplikacji).
|
||||
- `AllegroOrderImportService::shouldRequestInvoice($payload)` (nowa prywatna metoda) — rozszerza detekcje o `invoice.naturalPerson === false`, `invoice.address.taxId`, `invoice.companyName`/`invoice.address.company.name`. Wczesniej tylko `invoice.required` -> analogiczna luka jak shopPRO dla klientow Allegro z NIP bez `required=true`.
|
||||
- Migracja `20260513_000113_drop_orders_is_invoice_and_backfill_invoice_requested.sql`:
|
||||
- Idempotentny guard przez `information_schema.COLUMNS` + prepared statements (DDL no-op gdy kolumna juz nie istnieje, pattern z Key Decision 2026-05-10).
|
||||
- Backfill: `UPDATE orders SET invoice_requested=1 WHERE is_invoice=1 AND invoice_requested=0` (7 zamowien na produkcji, w tym #1089).
|
||||
- DROP COLUMN `orders.is_invoice` (legacy z Phase 115, dryft wzgledem `invoice_requested`).
|
||||
- `OrderImportRepository::insertOrder()` SQL — usuniety `is_invoice` z kolumn INSERT i `:is_invoice` z VALUES. `orderParams()` — usunieta linia mapowania. Docstring `updateOrderDelta()` (Phase 112) — usunieta wzmianka.
|
||||
- `OrdersRepository` — usuniety `o.is_invoice` z SELECT (`paginate` query) i `transformOrderRow()` hydrate (klucz `is_invoice` nie wystepuje juz w zwracanych row'ach).
|
||||
|
||||
**Dlaczego:**
|
||||
- Bug #1089: zamowienie shopPRO z fakturowymi danymi firmowymi mialo `is_invoice=1` (mapper) ale `invoice_requested=0` (sync service). UI pokazywal odznaczony checkbox -> przycisk "Wystaw fakture" niedostepny -> operator musial recznie klikac toggle.
|
||||
- Dryft: Phase 115 zostawila dwie kolumny dla tej samej semantyki (`is_invoice` legacy + `invoice_requested` nowy). Dwie sciezki detekcji (mapper vs. sync service) mialy rozna szerokosc heurystyki -> systematyczny rozjazd dla shopPRO orders z `firm_name`/`firm_nip`.
|
||||
- Fix architekturalny: jedno zrodlo prawdy (`invoice_requested`), jedna heurystyka per zrodlo (mapper dla shopPRO, prywatna metoda dla Allegro).
|
||||
|
||||
**BREAKING:**
|
||||
- Kolumna `orders.is_invoice` przestaje istniec po migracji `20260513_000113_*`. Wewnetrzny kontrakt — nie wystepuje w API ani odpowiedziach JSON. Jezeli ktokolwiek (skrypt operatora, raport custom) czytal `is_invoice` z DB -> przelaczyc na `invoice_requested`.
|
||||
- `ShopproOrderMapper::mapOrderAggregate()` zwraca teraz dodatkowy top-level klucz `invoice_detected` (boolean). Klucz `is_invoice` znika z podtablicy `order` (nie odpowiada juz kolumnie DB).
|
||||
|
||||
## 2026-05-12 - Phase 124 Plan 01: SMS Templates
|
||||
|
||||
**Co zrobiono:**
|
||||
|
||||
Reference in New Issue
Block a user