Revamp dispatch pipeline and PO cancellation docs
Updated documentation to clarify and redesign dispatch pipeline, loading event, and order/invoice lifecycle. Added PO cancellation flow for both Shop and Confirmed POs, introduced new API endpoint for cancellation, and simplified loading sheet model and endpoints. Added new permissions for loading operations, improved auditability, and detailed migration steps. Highlighted open questions on cancellation cascades, permissions, and vehicle capacity checks for future resolution.
This commit is contained in:
@@ -45,6 +45,8 @@ quantities.
|
||||
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.
|
||||
- A Shop PO can be cancelled (`Order.Status → Cancelled`). No Invoice exists at this point so no
|
||||
cascade required.
|
||||
|
||||
**API:**
|
||||
| Method | Route | Description |
|
||||
@@ -52,6 +54,7 @@ quantities.
|
||||
| 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. |
|
||||
| POST | `/api/orders/{id}/cancel` | Cancel a Shop PO. |
|
||||
|
||||
---
|
||||
|
||||
@@ -76,6 +79,9 @@ mode — controlled by the system-level `InvoiceCreationMode` config flag.
|
||||
- Confirmation requires no signature or OTP.
|
||||
- `OrderItem` lines are permanently locked after confirmation.
|
||||
- `Order.Status` (fulfillment tracking) is untouched — separate axis.
|
||||
- A Confirmed PO can be cancelled even after confirmation (`Order.Status → Cancelled`). If an
|
||||
Invoice was already created, cascade behaviour (void Invoice, impact on loading sheet items) is
|
||||
unresolved — see Open Questions.
|
||||
|
||||
---
|
||||
|
||||
@@ -150,8 +156,13 @@ enters any edits the agent made into the system and submits.
|
||||
|
||||
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.
|
||||
3. **Cancellation cascade** — both Shop PO and Confirmed PO can be cancelled. If cancelling a
|
||||
Confirmed PO that already has a linked Invoice: should the Invoice be voided automatically?
|
||||
If the PO's items were already pulled into a loading sheet: what happens to those loading sheet
|
||||
items? Cascade rules needed before implementation.
|
||||
4. **Exact adjustment payload for `POST /api/invoices/{id}/finalize`** — the fields the office
|
||||
worker can edit (returns, damage, discounts) are assumed but not fully specified. "Free issues"
|
||||
handling (items given to the shop at no charge) is a known case needing its own payload field(s).
|
||||
5. **Stage 3 and Stage 4 ordering** — Invoice printing (Stage 3) and loading sheet population
|
||||
(Stage 4) are independent flows. They are not strictly sequential — both depend on a Confirmed
|
||||
PO existing but neither requires the other to complete first.
|
||||
|
||||
@@ -13,7 +13,7 @@ Replace the previous loading event design with a dispatcher-driven, multi-PO mod
|
||||
1. **No more auto-creation.** Loading sheets are no longer generated automatically when a PO is
|
||||
confirmed. A dispatcher manually triggers population.
|
||||
2. **No more `Open → Active`.** `Ride.Status` collapses to `Open`/`Cancelled`/`Closed` — no
|
||||
`Active` stage. Loading sheet confirmation no longer changes `Ride.Status` at all.
|
||||
`Active` stage. Loading sheet confirmation no longer changes `Ride.Status`.
|
||||
3. **One sheet aggregates multiple POs.** A single loading sheet is created from all Confirmed POs
|
||||
matching a dispatcher's filter (route/agent/shop). No longer one sheet per PO.
|
||||
4. **Ride set at populate time.** The ride is provided when the dispatcher triggers population —
|
||||
@@ -22,15 +22,17 @@ Replace the previous loading event design with a dispatcher-driven, multi-PO mod
|
||||
loaded from different warehouses. Stock movements are generated per item's warehouse.
|
||||
6. **Item versioning.** Loading sheet items are versioned the same way as Shop PO items — every
|
||||
edit soft-deletes current rows (`IsActive = false`) and writes a fresh set.
|
||||
7. **Stock moves at confirmation.** When the dispatcher confirms the sheet, stock moves from each
|
||||
item's warehouse to the vehicle. No driver receipt confirmation step.
|
||||
|
||||
---
|
||||
|
||||
## Background / Context
|
||||
|
||||
Same physical mechanics as `04_loading_event.md`: a vehicle is a location with
|
||||
`LocationType.Vehicle` in the stock ledger. Confirming receipt (or resolving a discrepancy)
|
||||
creates stock movements — debit source warehouse(s), credit vehicle. What changes is *when and how
|
||||
a `LoadingSheet` comes into existence*, *which POs it covers*, and *how warehouse selection works*.
|
||||
`LocationType.Vehicle` in the stock ledger. Confirming the loading sheet creates stock movements —
|
||||
debit source warehouse(s), credit vehicle. What changes is *when and how a `LoadingSheet` comes
|
||||
into existence*, *which POs it covers*, and *how warehouse selection works*.
|
||||
|
||||
---
|
||||
|
||||
@@ -43,18 +45,12 @@ a `LoadingSheet` comes into existence*, *which POs it covers*, and *how warehous
|
||||
| Id | Guid | PK |
|
||||
| ~~`FK_OrderId`~~ | — | **Removed.** One sheet now aggregates multiple POs — replaced by `LoadingSheetOrder` linking table. |
|
||||
| **`FK_RideId`** | Guid | **Required. Set at population time** — dispatcher provides the ride when triggering populate. |
|
||||
| ~~`FK_SourceWarehouseId`~~ | — | **Removed.** Warehouse is tracked per item, not per sheet — a single sheet can be fulfilled from multiple warehouses. |
|
||||
| Status | LoadingSheetStatus | See state machine below. |
|
||||
| ~~`FK_SourceWarehouseId`~~ | — | **Removed.** Warehouse is tracked per item, not per sheet. |
|
||||
| Status | LoadingSheetStatus | `Planned` or `Confirmed`. |
|
||||
| **`PopulatedBy`** | Guid | **New.** UserId of dispatcher who triggered population. |
|
||||
| **`PopulatedAt`** | DateTime | **New.** When population was triggered. |
|
||||
| ~~`AssignedAt`~~ | — | **Removed.** No separate assign step. |
|
||||
| ~~`AssignedBy`~~ | — | **Removed.** Same reason. |
|
||||
| ConfirmedAt | DateTime? | Set when loading sheet is confirmed (items locked). |
|
||||
| ConfirmedBy | Guid? | UserId of whoever confirmed the sheet. |
|
||||
| DispatchConfirmedAt | DateTime? | Path B only — see Open Questions. |
|
||||
| DispatchConfirmedBy | Guid? | Path B only. |
|
||||
| ReceiptConfirmedAt | DateTime? | Path B only. |
|
||||
| ReceiptConfirmedBy | Guid? | Path B only. |
|
||||
| ConfirmedAt | DateTime? | Set when loading sheet is confirmed (items locked, stock moves). |
|
||||
| ConfirmedBy | Guid? | UserId of whoever confirmed the sheet. TBD — see Open Questions. |
|
||||
| CreatedAt | DateTime | From Template. |
|
||||
|
||||
### New Entity — `LoadingSheetOrder` (linking table)
|
||||
@@ -76,8 +72,6 @@ One loading sheet aggregates multiple Confirmed POs. This table records which PO
|
||||
| **`FK_OrderItemId`** | Guid? | **New.** Traces back to the source `OrderItem` from the Confirmed PO. Null for manually added lines. |
|
||||
| FK_ItemId | Guid | FK → Item (product) |
|
||||
| QuantityDispatched | decimal | Quantity to load from `FK_WarehouseId` |
|
||||
| QuantityReceived | decimal? | Driver's received qty (Path B only) |
|
||||
| QuantityResolved | decimal? | Supervisor's resolved qty after discrepancy (Path B only) |
|
||||
| **`IsActive`** | bool | **Versioning.** Every edit soft-deletes current items (`IsActive = false`) and writes a fresh set. Active items = latest version; soft-deleted = history. |
|
||||
| CreatedAt | DateTime | From Template |
|
||||
|
||||
@@ -86,36 +80,30 @@ One loading sheet aggregates multiple Confirmed POs. This table records which PO
|
||||
```csharp
|
||||
public enum LoadingSheetStatus
|
||||
{
|
||||
Planned, // populated by dispatcher (ride set, items editable/versioned)
|
||||
Confirmed, // items locked — stock movement path TBD (see Open Questions)
|
||||
DispatchConfirmed, // Path B only: warehouse confirms dispatch
|
||||
ReceiptConfirmed, // Path B only: driver confirms receipt, stock moves
|
||||
DiscrepancyFlagged, // Path B only: received qty mismatch
|
||||
Resolved // Path B only: supervisor resolves discrepancy, stock moves
|
||||
Planned, // populated by dispatcher — ride set, items editable and versioned
|
||||
Confirmed // items locked, stock movements created
|
||||
}
|
||||
```
|
||||
|
||||
`Pending` from `04_loading_event.md` is replaced by `Planned` and `Confirmed`. `Assigned` is
|
||||
removed — ride is set at populate time, no separate assign step.
|
||||
`Pending`, `Assigned`, `DispatchConfirmed`, `ReceiptConfirmed`, `DiscrepancyFlagged`, and
|
||||
`Resolved` from `04_loading_event.md` are all removed. Stock moves at `Confirmed` — no driver
|
||||
receipt confirmation step in this flow.
|
||||
|
||||
---
|
||||
|
||||
## State Machine
|
||||
|
||||
```text
|
||||
Planned -> Confirmed -> [Path A: stock moves here]
|
||||
\-> [Path B: DispatchConfirmed -> ReceiptConfirmed]
|
||||
\-> DiscrepancyFlagged -> Resolved
|
||||
Planned → Confirmed (stock moves)
|
||||
```
|
||||
|
||||
| Transition | Trigger | What it does |
|
||||
|---|---|---|
|
||||
| (create) → Planned | `POST /api/loading-sheets/populate` | Dispatcher provides `rideId` + filters. System finds all matching Confirmed POs, creates one `LoadingSheet` with `FK_RideId` set, populates `LoadingSheetItem` rows from active `OrderItem` lines, records PO links in `LoadingSheetOrder`. `PopulatedBy`/`PopulatedAt` set. Items editable (versioned via `IsActive`). Dispatcher sets `FK_WarehouseId` per item. |
|
||||
| Planned → Confirmed | `POST /{id}/confirm` | Locks the sheet — `LoadingSheetItem` rows frozen. Sets `ConfirmedAt`/`ConfirmedBy`. Stock movement path TBD (see Open Questions). |
|
||||
| Confirmed → DispatchConfirmed | `POST /{id}/confirm-dispatch` | **Path B only.** Warehouse manager confirms dispatch quantities. |
|
||||
| DispatchConfirmed → ReceiptConfirmed | `POST /{id}/confirm-receipt` | **Path B only.** Driver's received quantities all match. Stock movements created — one debit per `LoadingSheetItem.FK_WarehouseId`, one credit to the ride's vehicle. No ride status transition. |
|
||||
| DispatchConfirmed → DiscrepancyFlagged | `POST /{id}/confirm-receipt` | **Path B only.** Any line mismatch. No stock movements yet. |
|
||||
| DiscrepancyFlagged → Resolved | `POST /{id}/resolve` | **Path B only.** Supervisor enters `QuantityResolved` per line. Stock movements created using resolved quantities. |
|
||||
| Planned → Confirmed | `POST /{id}/confirm` | Locks the sheet — `LoadingSheetItem` rows frozen. Stock movements created: one debit per `LoadingSheetItem.FK_WarehouseId`, one credit to the ride's vehicle. Sets `ConfirmedAt`/`ConfirmedBy`. |
|
||||
|
||||
A `Planned` sheet stays as `Planned` indefinitely until the dispatcher confirms it — there is no
|
||||
timeout or auto-cancellation.
|
||||
|
||||
---
|
||||
|
||||
@@ -145,18 +133,14 @@ and writes a fresh set. Latest version = `IsActive = true` rows.
|
||||
|
||||
## Stock Movements
|
||||
|
||||
One stock movement pair per `LoadingSheetItem` (active rows only). Created exactly once.
|
||||
Created at `Confirmed`. One pair per active `LoadingSheetItem`:
|
||||
|
||||
- **Debit**: `LoadingSheetItem.FK_WarehouseId` (the warehouse that item loads from)
|
||||
- **Credit**: the vehicle on the assigned ride (`LoadingSheet.FK_RideId → DeliveryRun.FK_VehicleId`)
|
||||
- **Credit**: vehicle on the assigned ride (`LoadingSheet.FK_RideId → DeliveryRun.FK_VehicleId`)
|
||||
- **Type**: `Load`
|
||||
|
||||
Since different items can have different `FK_WarehouseId` values, one loading sheet can produce
|
||||
stock movements from multiple warehouses into one vehicle.
|
||||
|
||||
**When** movements are created depends on the path chosen (see Open Questions):
|
||||
- **Path A** — stock moves at `Confirmed`
|
||||
- **Path B** — stock moves at `ReceiptConfirmed` or `Resolved`
|
||||
stock movements from multiple warehouses into one vehicle in a single confirmation action.
|
||||
|
||||
---
|
||||
|
||||
@@ -166,15 +150,11 @@ Base route: `/api/loading-sheets`
|
||||
|
||||
| Method | Route | Who | Description |
|
||||
|---|---|---|---|
|
||||
| POST | `/populate` | Dispatcher | Trigger population: `{ rideId, routeId?, agentId?, shopId? }` — `rideId` required, at least one filter required. Creates one `LoadingSheet` at `Planned` with `FK_RideId` set. |
|
||||
| POST | `/populate` | Dispatcher | `{ rideId, routeId?, agentId?, shopId? }` — `rideId` required, at least one filter required. Creates one `LoadingSheet` at `Planned` with `FK_RideId` set. |
|
||||
| GET | `/` | Any | List sheets. Filter by `routeId`, `agentId`, `shopId`, `rideId`, `status`. Paginated. `routeId` resolves via `Store.FK_RouteId → Order.FK_StoreId → LoadingSheetOrder.FK_OrderId` server-side. |
|
||||
| GET | `/{id}` | Any | Get sheet detail. Active items only by default; `?history=true` includes soft-deleted versions. |
|
||||
| PUT | `/{id}/items` | Dispatcher | Rewrite items while `Planned`. Each item must include `FK_WarehouseId`. Soft-deletes current rows (`IsActive = false`) and writes a fresh set. |
|
||||
| POST | `/{id}/confirm` | Dispatcher | Lock the sheet. `Planned → Confirmed`. Items frozen. |
|
||||
| POST | `/{id}/confirm-dispatch` | Warehouse mgr | **Path B only.** `Confirmed → DispatchConfirmed`. |
|
||||
| PUT | `/{id}/receipt-items` | Driver | **Path B only.** Set `QuantityReceived` per line. |
|
||||
| POST | `/{id}/confirm-receipt` | Driver | **Path B only.** `→ ReceiptConfirmed` or `DiscrepancyFlagged`. |
|
||||
| POST | `/{id}/resolve` | Supervisor | **Path B only.** Enter `QuantityResolved` per line `→ Resolved`. |
|
||||
| POST | `/{id}/confirm` | TBD | Lock the sheet. `Planned → Confirmed`. Items frozen. Stock movements created. |
|
||||
|
||||
---
|
||||
|
||||
@@ -192,11 +172,12 @@ Base route: `/api/loading-sheets`
|
||||
8. Items are editable (versioned via `IsActive`) while `Planned` — frozen at `Confirmed`.
|
||||
9. Every item edit soft-deletes all current `LoadingSheetItem` rows (`IsActive = false`) and writes
|
||||
a fresh set. Active rows = latest version; soft-deleted = history.
|
||||
10. Stock movements are created exactly once — one debit per item warehouse, one credit to the
|
||||
vehicle. Timing depends on path chosen (see Open Questions).
|
||||
11. No `Ride.Status` transition is triggered at any point in this flow.
|
||||
12. All list endpoints return paginated responses.
|
||||
13. Vehicle capacity check is not enforced in this step.
|
||||
10. Stock movements are created at `Confirmed` — one debit per item's warehouse, one credit to the
|
||||
vehicle — all in one transaction.
|
||||
11. A `Planned` sheet stays `Planned` until confirmed — no timeout or auto-cancellation.
|
||||
12. No `Ride.Status` transition is triggered at any point in this flow.
|
||||
13. All list endpoints return paginated responses.
|
||||
14. Vehicle capacity check is not enforced in this step.
|
||||
|
||||
---
|
||||
|
||||
@@ -205,50 +186,49 @@ Base route: `/api/loading-sheets`
|
||||
| Code | Who |
|
||||
|---|---|
|
||||
| `loading.view` | All roles |
|
||||
| **`loading.populate`** | **New.** Dispatcher — trigger population, edit items, confirm sheet |
|
||||
| `loading.dispatch` | Warehouse manager — Path B dispatch confirmation |
|
||||
| `loading.receive` | Driver — Path B receipt confirmation |
|
||||
| `loading.resolve` | Supervisor — Path B discrepancy resolution |
|
||||
| **`loading.populate`** | **New.** Dispatcher — trigger population, edit items |
|
||||
| **`loading.confirm`** | **New.** TBD — who can confirm the sheet (see Open Questions) |
|
||||
|
||||
---
|
||||
|
||||
## Fraud / Control Considerations
|
||||
|
||||
- Discrepancies are immediately visible and block further progress.
|
||||
- `QuantityResolved` is permanently stored per line for audit purposes.
|
||||
- `PopulatedBy`/`PopulatedAt` gives an audit trail for who triggered population and when.
|
||||
- `ConfirmedBy`/`ConfirmedAt` records who committed stock movements.
|
||||
- `FK_OrderItemId` on each item traces the exact source PO line for full audit.
|
||||
- `IsActive = false` rows preserve the full edit history of the sheet before confirmation.
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **Path A vs Path B — when do stock movements happen?** Stock can move at `Confirmed` (Path A)
|
||||
or at `ReceiptConfirmed`/`Resolved` (Path B). Both paths retained until decided.
|
||||
2. **Who confirms the loading sheet?** — the dispatcher who populated it, the warehouse manager,
|
||||
or either?
|
||||
3. **What happens to a `Planned` sheet if it is never confirmed?** — no cancellation or timeout
|
||||
path is designed here.
|
||||
4. Vehicle capacity check — still deferred.
|
||||
1. **Who confirms the loading sheet?** — dispatcher, warehouse manager, or either? TBD. Controls
|
||||
the `loading.confirm` permission assignment.
|
||||
2. **What happens to a `Planned` sheet if its source PO is cancelled?** — if a Confirmed PO is
|
||||
cancelled after a loading sheet has been populated from it, the sheet's items sourced from that
|
||||
PO become invalid. No cascade behaviour designed here.
|
||||
3. Vehicle capacity check — still deferred.
|
||||
|
||||
---
|
||||
|
||||
## Migration Notes (for when this is implemented)
|
||||
|
||||
- Remove `FK_OrderId` and `FK_SourceWarehouseId` from `Models/LoadingSheet.cs`. Make `FK_RideId`
|
||||
required (non-nullable). Remove `AssignedAt`/`AssignedBy`. Add `PopulatedBy`, `PopulatedAt`,
|
||||
- Remove `FK_OrderId`, `FK_SourceWarehouseId`, `AssignedAt`, `AssignedBy`,
|
||||
`DispatchConfirmedAt`, `DispatchConfirmedBy`, `ReceiptConfirmedAt`, `ReceiptConfirmedBy` from
|
||||
`Models/LoadingSheet.cs`. Make `FK_RideId` required. Add `PopulatedBy`, `PopulatedAt`,
|
||||
`ConfirmedAt`, `ConfirmedBy`.
|
||||
- Add `Models/LoadingSheetOrder.cs` (`FK_LoadingSheetId`, `FK_OrderId`) with composite PK.
|
||||
- Add `FK_WarehouseId` (required), `FK_OrderItemId` (nullable), `IsActive` to
|
||||
`Models/LoadingSheetItem.cs`.
|
||||
- Replace `LoadingSheetStatus.Pending` and `Assigned` with `Planned` and `Confirmed`; update all
|
||||
status checks in `LoadingSheetService`/`LoadingSheetRepository`.
|
||||
- Remove `POST /api/loading-sheets` (old manual create) and `POST /{id}/assign-ride`; add
|
||||
`POST /api/loading-sheets/populate` and `POST /{id}/confirm`.
|
||||
`Models/LoadingSheetItem.cs`. Remove `QuantityReceived`, `QuantityResolved`.
|
||||
- Replace `LoadingSheetStatus` entirely with `Planned` and `Confirmed`; remove all other values
|
||||
and update all status checks in `LoadingSheetService`/`LoadingSheetRepository`.
|
||||
- Remove `POST /api/loading-sheets` (old manual create), `POST /{id}/assign-ride`,
|
||||
`POST /{id}/confirm-dispatch`, `PUT /{id}/receipt-items`, `POST /{id}/confirm-receipt`,
|
||||
`POST /{id}/resolve`; add `POST /api/loading-sheets/populate` and `POST /{id}/confirm`.
|
||||
- Wire stock movements into `POST /{id}/confirm` — one debit per `LoadingSheetItem.FK_WarehouseId`,
|
||||
one credit to the ride's vehicle, in a single transaction.
|
||||
- Remove ride-route-based item population (`GetAggregatedOrderItemsByRouteIdAsync`); replace with
|
||||
filter-based multi-PO aggregation in `LoadingSheetService`.
|
||||
- Remove all calls to ride-activation logic from `confirm-receipt`/`resolve` — no `Ride.Status`
|
||||
transition exists anymore.
|
||||
- Seed the new `loading.populate` permission.
|
||||
- Resolve Open Question 1 (stock movement timing) before implementing `POST /{id}/confirm` and
|
||||
`POST /{id}/confirm-receipt` stock movement logic.
|
||||
filter-based multi-PO aggregation.
|
||||
- Remove all calls to ride-activation logic — no `Ride.Status` transition exists anymore.
|
||||
- Seed `loading.populate` and `loading.confirm` permissions.
|
||||
- Resolve Open Question 1 (who confirms) before assigning `loading.confirm` permission.
|
||||
|
||||
@@ -168,6 +168,11 @@ Draft → Printed → Finalized
|
||||
10. `Order.FK_SaleId` is optional — a PO can exist and be confirmed before any `Sale` is recorded.
|
||||
11. Loading sheet population is a separate dispatcher-triggered flow — not triggered by PO
|
||||
confirmation or invoice creation. See [dispatch_pipeline.md](./dispatch_pipeline.md).
|
||||
12. **A Shop PO (pre-confirmation) can be cancelled** — `Order.Status → Cancelled`. No Invoice
|
||||
exists at this stage so no cascade required.
|
||||
13. **A Confirmed PO can also be cancelled** even after confirmation — `Order.Status → Cancelled`.
|
||||
If an Invoice was already created for this PO, cascade behaviour (void/cancel the Invoice) and
|
||||
behaviour for any loading sheet that included this PO are open questions — see Open Questions.
|
||||
|
||||
---
|
||||
|
||||
@@ -191,9 +196,14 @@ Draft → Printed → Finalized
|
||||
2. ~~**One Invoice per PO or multiple?**~~ — **Resolved:** 1:1 for v1.
|
||||
3. **Should `Order.Status` and `Invoice.Status` eventually merge?** — kept separate; not decided.
|
||||
4. **How does `Payment` attach to `Invoice`?** — see Impact item 2. Not designed here.
|
||||
5. **Cancellation/void path for a Shop PO, Confirmed PO, or Invoice?** — not addressed here.
|
||||
5. **Cancellation cascade — Invoice and LoadingSheet** — a Shop PO can be cancelled (no Invoice
|
||||
exists yet, no cascade). A Confirmed PO can be cancelled even after confirmation. If the PO
|
||||
already has a linked Invoice, what happens to that Invoice (voided? stays Draft?) is unresolved.
|
||||
If the PO's items were pulled into a loading sheet, what happens to those loading sheet items is
|
||||
also unresolved. Both cases need explicit cascade rules before implementation.
|
||||
6. **Exact adjustment payload for `POST /finalize`** — quantity adjustments, returns, damage, and
|
||||
discounts are assumed but the payload shape is not fully specified here.
|
||||
discounts are assumed but the payload shape is not fully specified. "Free issues" handling (items
|
||||
given to the shop at no charge) is a known case that needs its own field(s) in the payload.
|
||||
|
||||
---
|
||||
|
||||
@@ -219,3 +229,5 @@ Draft → Printed → Finalized
|
||||
`FinalizedAt`/`FinalizedBy`. Resolve Open Question 6 before specifying the exact payload shape.
|
||||
- `LoadingSheet` population is a separate flow — see [dispatch_pipeline.md](./dispatch_pipeline.md).
|
||||
- A new PO-creation path independent of `SaleService.CreateAsync` is needed — not specified here.
|
||||
- Add `POST /api/orders/{id}/cancel` — cancels a Shop PO or Confirmed PO. Resolve Open Question 5
|
||||
(cascade to Invoice and LoadingSheet) before implementing for the Confirmed PO case.
|
||||
|
||||
Reference in New Issue
Block a user