Merge branch 'dev' into production
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user