|
|
||
|---|---|---|
| .. | ||
| ORDERDRAFT_ISSUES.md | ||
| README.md | ||
README.md
Order-Draft Workflow
Part of
Nop.Plugin.Misc.FruitBankPlugin. See../README.mdfor the docs index,../../README.mdfor project overview. Closely coupled to the pre-order subsystem — see../PREORDER/README.md: an approved draft createsPreOrders and runs the same conversion. Known issues / planned work:ORDERDRAFT_ISSUES.md.
An order draft is an AI-assisted intake channel. External systems (Viber bot, email processor, dictation app) post a raw free-text message; the AI extracts delivery dates and product demands; a draft is created for admin review. On approval the draft becomes one or more PreOrders, which then flow through the existing PRE-ORDER conversion — so OrderDraft is a new, admin-gated placement channel into that pipeline, not a parallel order path.
Entity Hierarchy
OrderDraft ← one inbound message for one delivery date
└─ OrderDraftItem ← one parsed product-demand line [1:N]
ExternalUserPartnerMapping ← external username → nopCommerce CustomerId (learned)
ProductTextMapping ← raw text → admin-chosen ProductId (learned, for ranking)
All four tables live in FruitBank.Common/Entities/ and are registered on FruitBankDbContext. OrderDraftStatus: Pending → Approved / Rejected / Expired.
Workflow
1. Intake
OrderDraftApiController.Create (Controllers/): auth via X-FruitBank-ApiKey header (matched against FruitBankSettings.OrderDraftApiKey), body { externalUsername, messageText }.
2. Draft build
OrderDraftService.CreateDraftsFromMessageAsync:
- Partner resolution from
ExternalUserPartnerMapping(may benull→ admin assigns later). - AI parse (
OrderDraftParserService.ParseMessageAsync): one draft per delivery date;isPreorder = deliveryDate > today + PreorderThresholdDays (4). - Per item (
ProcessDraftItemAsync): historicProductTextMappingweighting (last 5 admin decisions) + AI canonical name/keywords +SearchProductsAsync+ AI ranking →SuggestedProductIdsJson, first suggestion pre-selected.
3. Admin review
OrderDraftAdminController (Areas/Admin/): DataTables list (urgent = Pending > 20h), Detail page with candidates, UpdateItem / AssignCustomer.
4. Approval (the entry point into PRE-ORDER)
OrderDraftService.ApproveAsync:
- Validates: partner assigned, every item has
SelectedProductId. - Saves
ProductTextMappinglearning records. - Splits items by
IsPreorder; both branches create aPreOrderviaInsertPreOrderAsync, then callPreOrderConversionService.ConvertPreOrdersForProductsAsync. The "immediate" branch relies on the 4-day conversion window to convert at once.
5. Expiry
OrderDraftService.ExpireOldDraftsAsync: Pending drafts older than 14 days → Expired (background job).
AI touchpoints
Three distinct AI calls, all in OrderDraftParserService via OpenAIApiService.GetSimpleResponseAsync (OpenAI Chat Completions). OrderDraft uses OpenAI exclusively — unlike QuickOrderController / OrderController parsing, which use CerebrasApiService.
| # | Call | Input → Output | Used in |
|---|---|---|---|
| 1 | ParseMessageAsync |
raw message → delivery-date groups of { rawProductText, requestedQuantity }; one draft per date |
CreateDraftsFromMessageAsync |
| 2 | ResolveProductNameAsync |
one raw product text → { canonicalName, searchKeywords[] } (keywords feed NopCommerce SearchProductsAsync) |
ProcessDraftItemAsync |
| 3 | RankCandidatesAsync |
raw text + canonical name + candidate (id, name)[] → ranked product-id list; only when >1 non-historic search hit |
ProcessDraftItemAsync |
Resolution is hybrid: AI proposes candidates, but ordering is front-loaded by historic admin decisions (ProductTextMapping, last 5 for the same raw text) — a DB heuristic, not AI. Product search itself (SearchProductsAsync) is NopCommerce full-text, not AI.
Model: GetModelName() from FruitBankSettings.OpenAIModelName (default gpt-3.5-turbo; code paths for gpt-4o-mini/4.1-mini/4.1-nano/5-nano at temperature=0.2, and a gpt-5 path at reasoning_effort=minimal).
Not yet AI-wired: partner identification (plain ExternalUserPartnerMapping username lookup), quantity/unit normalisation (relies on the parse prompt only), confidence/uncertainty scoring (admin approves everything manually), and the preorder-vs-immediate split (fixed 4-day PreorderThresholdDays, not AI).
Key Source
| Type | Location |
|---|---|
OrderDraftService / IOrderDraftService |
Services/ |
OrderDraftParserService (AI parse / resolve / rank) |
Services/ |
OrderDraftApiController |
Controllers/ |
OrderDraftAdminController (+ models) |
Areas/Admin/ |
OrderDraftDbTable / OrderDraftItemDbTable / ExternalUserPartnerMapDbTable / ProductTextMappingDbTable |
Domains/DataLayer/ |
OrderDraft / OrderDraftItem / ExternalUserPartnerMapping / ProductTextMapping entities |
FruitBank.Common/Entities/ |