Mango.Nop.Plugins/Nop.Plugin.Misc.AIPlugin/docs/PREORDER/PREORDER_ISSUES.md

66 lines
7.7 KiB
Markdown

# PRE-ORDER — Known Issues
> Companion to [`README.md`](README.md). Topic `PREO`, prefix `MGFBANKPLUG` → entry IDs `MGFBANKPLUG-PREO-I-<RAND>` (issue) / `-B-` (confirmed bug).
> ID format, Status vocabulary, type codes and archival are **not restated here** — see `../../.github/TOPIC_CODES.md` (which points to the framework registry). Entry template: follow `AyCode.Core/.github/META_ISSUES.md`.
Scope: bugs / inconsistent or observable edge-case behaviour in the pre-order subsystem — placement, allocation/conversion, conversion window, expiry sweep, order create/append, product replacement, status derivation, pricing.
## Active entries
## MGFBANKPLUG-PREO-B-K7M2: A konverziós pool dupla-számolja a már teljesített foglalásokat — alul-allokáció
**Status:** Closed (2026-06-29) — javítva · **Priority:** P2 · **Type:** B (megerősített bug)
### Resolution (2026-06-29)
Kijavítva a `PreOrderConversionService`-ben, három összefüggő lépéssel (mind a `Services/PreorderConversionService.cs`-ben):
1. **Konvertálhatósági előszűrés** a `ConvertPreOrdersForProductsAsync`-ben: csak az a preorder kap teljesítést, ami tényleg rendelést tud létrehozni (van `OrderId` = append-út, vagy feloldható customer + számlázási cím = új-rendelés-út, az új `ResolveBillingAddressIdAsync` helperrel). Ezzel garantált az invariáns: **`Fulfilled ⇒ rendelés létrejön ⇒ készlet levonva`**. A nem konvertálható preorderek tételei `Pending`-ben maradnak.
2. **Delta-alapú order-item**: az `InsertOrderItemsAsync` mostantól a futásban szerzett mennyiséget (`gainedByItemId` delta) viszi a tételbe / vonja le a készletből, nem a kumulatív `FulfilledQuantity`-t. Ez javítja a **vele összefonódó, korábban elfedett második hibát**: részleges utántöltésnél (multi-dokumentum) az append a *teljes* mennyiséget vonta le → dupla-levonás + duplikált order-item.
3. A garantált invariáns mellett a `BuildIncomingQuantityPoolAsync` dupláló `alreadyAllocated` levonása **eltávolítva**: `pool = AvailableQuantity` (ami az `AdjustInventoryAsync` miatt már tartalmazza a korábbi foglalásokat).
**Eredmény:** a reprodukciós eset (`Stock=0, Incoming=10`, A 6-ot kap, B 6-ot kér) most helyesen B-nek 4-et ad; a részleges utántöltés is helyesen a deltát viszi.
**Maradék (elhanyagolható) él:** ha a customer/cím a futás közben (előszűrés után, rendeléslétrehozás előtt) törlődne — extrém versenyhelyzet —, az item `Fulfilled` maradhatna rendelés nélkül. A `CreateOrderAsync` korai return-jei safety netként megmaradtak; a teljes védelemhez a státusz-mentést a rendeléslétrehozás *után* kellene végezni (külön, nem szükséges most).
---
#### Eredeti leírás
**Type:** B (megerősített bug)
`PreOrderConversionService.BuildIncomingQuantityPoolAsync` ([`Services/PreorderConversionService.cs`](../../Services/PreorderConversionService.cs)) a rendelkezésre álló mennyiségből **kétszer** vonja le a korábbi futásokban már teljesített (`Fulfilled`/`PartiallyFulfilled`) preorder-tételeket.
- `AvailableQuantity = StockQuantity + IncomingQuantity` (SCHEMA.md), az élő `Product.StockQuantity`-ből.
- Teljesítéskor `InsertOrderItemsAsync``AdjustInventoryAsync(-FulfilledQuantity)` **már csökkenti** a `StockQuantity`-t, így a következő futás `AvailableQuantity`-je **már tartalmazza** a foglalást.
- A pool ezen felül **még levonja** az `alreadyAllocated`-et (a `Fulfilled`/`PartiallyFulfilled` itemek `FulfilledQuantity` összegét) → **futások közötti dupla-levonás**.
**Reprodukció:** `Stock=0, Incoming=10`. „A" preorder 6-ot teljesít (run 1) → `Stock=-6`, allocated=6. „B" preorder 6-ot kér (run 2): `available = -6+10 = 4`, `committed = 6``pool = max(0, 4-6) = 0`. **B nem kap semmit, pedig fizikailag 4 jár neki.** Az alul-allokáció a „még nincs beérkezve" eset — az előrendelés tipikus forgatókönyve.
**Miért nem puszta törlés a fix:** a `CreateOrderAsync` korai `return`-jei (nincs `Customer` / nincs számlázási cím, [sorok ~300-319](../../Services/PreorderConversionService.cs)) úgy lépnek ki, hogy az item a fő ciklusban (sor ~153) **már `Fulfilled`-re lett állítva és elmentve**, de a készletlevonás nem futott le. Ezeken a hibautakon az `alreadyAllocated` levonás *helyesen* kompenzál; a levonás puszta törlése **túl-allokációt (overselling)** okozna.
**Javítási irány (ajánlott):** az item csak akkor legyen `Fulfilled`, ha a rendelés + készletlevonás ténylegesen sikerült (korai return esetén maradjon `Pending`/visszaállítás), majd ezután eltávolítható a dupláló `alreadyAllocated` levonás. Így megszűnik az alul-allokáció túl-allokációs kockázat nélkül.
**Affected:**
- `Services/PreorderConversionService.cs``BuildIncomingQuantityPoolAsync` (a `alreadyAllocated` levonás), `ConvertPreOrdersForProductsAsync` fő ciklusa (státusz-mutáció sorrendje), `CreateOrderAsync` korai return ágai.
## MGFBANKPLUG-PREO-B-M4T9: Lejárt előrendeléseket kielégít a rendszer friss szállítmányból
**Status:** Closed (2026-07-08) — javítva · **Priority:** P1 · **Type:** B (megerősített bug, éles jelzés)
Admin-jelzés: hosszú ideje **lejárt** előrendeléseket a rendszer friss szállítmányból kielégít, holott a lejárt előrendelésnek lezártnak kellene lennie és soha többé nem konvertálódnia.
**Gyökérok — két hiba játszik össze:**
1. A `ConvertPreOrdersForProductsAsync` jogosultsági szűrőjének **csak felső korlátja volt** (`DateOfReceipt.Date <= ma + PreOrderConversionWindowDays`), **alsó korlátja nem** — így egy tetszőleges múltbeli dátumú előrendelés is átment (múltbeli dátum ≤ ma+4 mindig igaz).
2. A `SweepExpiredPreOrdersAsync` **csak a `Pending` tételeket ejti**; a `PartiallyFulfilled` tételeket érintetlenül hagyja (mert azok már valós rendelésen vannak). Ezek `OrderId`-vel rendelkeznek → az `AppendItemsToOrderAsync` a maradék mennyiséget **minden későbbi szállítmányból utántölti**, korlátlan ideig a lejárat után.
**Tipikus eset:** vevő 100-at rendel, 60 megjön (`PartiallyFulfilled`), a dátum lejár → a maradék 40-et a rendszer örökké tölti a friss szállítmányokból.
### Resolution (2026-07-08)
`Services/PreorderConversionService.cs`:
- **Alsó korlát** a jogosultsági szűrőben: `DateOfReceipt.Date >= ma` — lejárt (múltbeli) előrendelés **soha** nem kap allokációt (sem új rendelést, sem utántöltést). Ez az egyetlen konverziós kapu, minden trigger ezen megy át.
- A **sweep** lejárat-határa dátum-szintre igazítva (`DateOfReceipt < ma-éjfél`, kliens-oldali konstans SQL-biztos), hogy egy **aznapi** előrendelés a teljes szállítási napon aktív maradjon, és ne legyen félig lezárva" (Pending ejtve, de PartiallyFulfilled még tölthető). Így a sweep (dátum < ma) és a szűrő (dátum ma) pontos komplementerek, nincs rés.
**Maradék-véglegesítés (2026-07-08, riport-tisztaság):** a `SweepExpiredPreOrdersAsync` mostantól a lejárt előrendelések `PartiallyFulfilled` tételeit is lezárja: a tétel a teljesített mennyiségen záródik (`RequestedQuantity FulfilledQuantity`, státusz `Fulfilled`), így a maradék ejtve nem mutatkozik többé nyitott igényként (pl. DemandList). A shortfall (eredeti igény vs. teljesített) a kapcsolt **rendeléshez jegyzetként** rögzül, tehát nem vész el. A soha nem teljesített tételeket mennyiség-alapon (`FulfilledQuantity == 0`) ejti, robusztusan a bizonytalan enum-olvasás ellen.
**Affected:**
- `Services/PreorderConversionService.cs` `ConvertPreOrdersForProductsAsync` (jogosultsági szűrő alsó korlát), `SweepExpiredPreOrdersAsync` (dátum-szintű lejárat + `PartiallyFulfilled` maradék véglegesítése + rendelés-jegyzet).