diff --git a/Distribution/docs/refactor_docs/dispatch_pipeline.md b/Distribution/docs/refactor_docs/dispatch_pipeline.md new file mode 100644 index 0000000..c8c81df --- /dev/null +++ b/Distribution/docs/refactor_docs/dispatch_pipeline.md @@ -0,0 +1,195 @@ +# [Refactor] Dispatch Pipeline — Shop PO to Invoice Finalization + +**Status**: Proposed (design only — no code changes yet) +**Depends On**: [order_invoice_lifecycle.md](./order_invoice_lifecycle.md), [loading_event.md](./loading_event.md), [ride_lifecycle.md](./ride_lifecycle.md) + +--- + +## Goal + +Define the end-to-end order processing pipeline from a shop's initial PO through to invoice +completion. This replaces the separate Delivery ride and Sale ride flows +([05_delivery_ride.md](../05_delivery_ride.md), [06_sale_ride.md](../06_sale_ride.md)) with a +single unified pipeline. + +--- + +## Pipeline Overview + +``` +[Stage 1] Shop PO created (editable) + ↓ +[Stage 2] PO Confirmed + Invoice Created → Draft (Mode A: combined / Mode B: separate) + ↓ +[Stage 3] Invoice Printed → agent takes to field + ↓ +[Stage 4] Loading Sheet Population (dispatcher-triggered, separate flow) + ↓ +[Stage 5] Delivery (TBD) + ↓ +[Stage 6] Agent returns printed invoice → office worker enters edits → Invoice Finalized +``` + +--- + +## Stage 1 — Shop PO + +**Business term**: Shop PO +**System state**: `Order` (no Invoice yet) + +The entry point to the pipeline. A sales agent creates a PO for a shop capturing line items and +quantities. + +**Key rules:** +- Items editable — every edit soft-deletes current `OrderItem` rows (`IsActive = false`) and writes + a fresh set. `IsActive = true` = latest version; `IsActive = false` = history. +- Editing allowed only while Shop PO (before confirmation). +- No Invoice exists yet at this stage. + +**API:** +| Method | Route | Description | +|---|---|---| +| POST | `/api/orders` | Create Shop PO for a store. | +| PUT | `/api/orders/{id}/items` | Rewrite line items (versioned). | +| GET | `/api/orders/{id}` | Get PO with current active items. | + +--- + +## Stage 2 — Confirmed PO + Invoice Created + +**Business term**: Confirmed PO +**System state**: `Order` (items locked) + `Invoice` at `Draft` + +PO items are locked and an Invoice is created at `Draft`. How this happens depends on the active +mode — controlled by the system-level `InvoiceCreationMode` config flag. + +**Mode A — combined action (one button):** +- `POST /api/orders/{id}/confirm-and-create-invoice` +- Locks PO items and creates Invoice at `Draft` atomically. + +**Mode B — separate actions:** +- `POST /api/orders/{id}/confirm` — locks PO items only. +- Office user then browses Confirmed POs (filtered by `agentId`, `routeId`, `shopId`) and creates + the invoice: `POST /api/invoices { orderId }`. + +**Key rules:** +- Confirmation requires no signature or OTP. +- `OrderItem` lines are permanently locked after confirmation. +- `Order.Status` (fulfillment tracking) is untouched — separate axis. + +--- + +## Stage 3 — Invoice Printed + +**Trigger**: `POST /api/invoices/{id}/print` + +Office user prints the invoice. The agent takes the physical copy to the field. + +**Key rules:** +- Agent may make handwritten edits on the printed copy during the ride (quantity adjustments, + returns, damage, discounts). +- Invoice is locked from system edits between `Printed` and `Finalized`. + +--- + +## Stage 4 — Loading Sheet Population + +A dispatcher manually triggers population via `POST /api/loading-sheets/populate` with one or more +filters. The system finds all matching Confirmed POs and aggregates them into **one loading sheet**. + +**Filters (at least one required):** +- `routeId` — all Confirmed POs for shops on this route +- `agentId` — all Confirmed POs created by this agent +- `shopId` — all Confirmed POs for this specific shop + +**What happens:** +1. System finds all `Order` records matching the filter where items are locked (Confirmed PO) +2. One `LoadingSheet` created at `Planned` status +3. `LoadingSheetItem` rows populated from the active `OrderItem` lines of those POs +4. Each source PO recorded in `LoadingSheetOrder` linking table +5. Each item carries `FK_OrderItemId` pointing back to its source `OrderItem` + +**After population — editing:** +- Dispatcher can edit item quantities while the sheet is `Planned` or `Assigned` +- Every edit soft-deletes current `LoadingSheetItem` rows (`IsActive = false`) and writes a fresh + set — same versioning as Shop PO items + +**Confirmation and stock movement:** +- Ride is provided at populate time — no separate assign step +- Each item has its own `FK_WarehouseId` — one sheet can load from multiple warehouses +- Dispatcher confirms the sheet (`Planned → Confirmed`) — items frozen +- Stock movements: one debit per item's warehouse, one credit to the ride's vehicle +- Timing: open question (Path A at `Confirmed` vs Path B at `ReceiptConfirmed`) + +See [loading_event.md](./loading_event.md) for the full state machine. + +--- + +## Stage 5 — Delivery + +**Status**: TBD — to be designed in a subsequent pass. + +--- + +## Stage 6 — Invoice Finalized + +**Trigger**: `POST /api/invoices/{id}/complete` + +After the ride or day-end, the agent returns the printed invoice to the office. The office worker +enters any edits the agent made into the system and submits. + +**What happens:** +- Office worker enters adjustments (quantity changes, returns, damage, discounts) from the paper copy +- `Invoice.TotalAmount` is recomputed and locked +- `Invoice.Status` → `Finalized` +- `FinalizedAt` / `FinalizedBy` set + +--- + +## Stage 3 — Loading Sheet Population + +A dispatcher manually triggers population via `POST /api/loading-sheets/populate` with one or more +filters. The system finds all matching Confirmed POs and aggregates them into **one loading sheet**. + +**Filters (at least one required):** +- `routeId` — all Confirmed POs for shops on this route +- `agentId` — all Confirmed POs created by this agent +- `shopId` — all Confirmed POs for this specific shop + +**What happens:** +1. System finds all `Order` records matching the filter where `Invoice.Status = Draft` +2. One `LoadingSheet` created at `Planned` status +3. `LoadingSheetItem` rows populated from the active `OrderItem` lines of those POs +4. Each source PO recorded in `LoadingSheetOrder` linking table +5. Each item carries `FK_OrderItemId` pointing back to its source `OrderItem` + +**After population — editing:** +- Dispatcher can edit item quantities while the sheet is `Planned` or `Assigned` +- Every edit soft-deletes all current `LoadingSheetItem` rows (`IsActive = false`) and writes a + fresh set — same versioning mechanism as Shop PO items +- Active rows (`IsActive = true`) = latest version; soft-deleted = history + +**Confirmation and stock movement:** +- Dispatcher assigns a ride + source warehouse (`Planned → Assigned`) +- Dispatcher confirms the sheet (`Assigned → Confirmed`) — items frozen +- Stock movement timing is an open question — see [loading_event.md](./loading_event.md) Open Question 1 + +See [loading_event.md](./loading_event.md) for the full loading sheet state machine and dispatch/receipt flow. + +--- + +## Stage 4+ — Delivery and Invoice Finalization + +**Status**: TBD — to be designed in a subsequent pass. + +--- + +## Open Questions + +1. **Who can confirm a Shop PO?** — the creating sales agent, a supervisor, or either? +2. **Can a Confirmed PO be reverted to Shop PO?** — no un-confirm path designed here. +3. **Cancellation path for a Shop PO or Confirmed PO?** — not addressed here. +4. **Exact adjustment payload for `POST /complete`** — the fields the office worker can edit + (returns, damage, discounts) are assumed but not fully specified. +5. **Does Stage 4 (loading sheet) depend on Stage 3 (invoice printed), or can they happen in + parallel?** — not decided here.