480 lines
19 KiB
Markdown
480 lines
19 KiB
Markdown
|
|
# Order Operator — SOP
|
||
|
|
|
||
|
|
**Owns:** the order lifecycle, from quote → order → delivered →
|
||
|
|
invoiced. Creates purchase orders, drives the `order_items` state
|
||
|
|
machine, hands off to invoicing.
|
||
|
|
|
||
|
|
**Tools:** `/orders`, `/orders/[id]`, `/orders/new`, `/collection-pool`,
|
||
|
|
`/collection-map`, `/stock`, `/stock-orders`, dashboard widgets.
|
||
|
|
|
||
|
|
**Handoffs:**
|
||
|
|
- **From Quote Operator** → customer accepted a quote.
|
||
|
|
- **To Collection Driver** → PO sent, item moves to
|
||
|
|
`collection_pool`.
|
||
|
|
- **To Invoicing** → item delivered, ready to invoice.
|
||
|
|
- **To Approver** → escalated pricing / margin / customer issues.
|
||
|
|
|
||
|
|
**Note:** the order lifecycle is **item-level**, not order-level.
|
||
|
|
`orders.status` does not exist as a field. The "Complete" badge on
|
||
|
|
the order header is derived from `every item is delivered`. This is
|
||
|
|
in `src/lib/server/order-completion.ts`. Get familiar with it.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1. Daily / Weekly Checklist
|
||
|
|
|
||
|
|
### Every working day (start of shift, 08:00 SAST)
|
||
|
|
|
||
|
|
- [ ] **Open `/orders` — New tab.** Every `pending` item should have
|
||
|
|
a clear next action: either waiting for pricing review, waiting
|
||
|
|
for a PO, or waiting for a supplier reply.
|
||
|
|
- [ ] **Open `/orders` — Processing tab.** Verify all `processed`
|
||
|
|
items have a PO generated. Items stuck in `processed` for >1
|
||
|
|
working day = process bug. Fix or escalate.
|
||
|
|
- [ ] **Open `/orders` — Collection Pool tab.** If you see
|
||
|
|
`ready_for_collection: true` items with no driver assigned, the
|
||
|
|
collection driver hasn't picked them up — phone them.
|
||
|
|
- [ ] **Open dashboard → "Follow-ups Due".** Action anything from
|
||
|
|
you (POs waiting on supplier reply, etc).
|
||
|
|
- [ ] **Check `/api/v1/purchase-orders/stale`** (or the dashboard
|
||
|
|
widget). POs with no supplier reply for >3 working days must
|
||
|
|
be phoned.
|
||
|
|
|
||
|
|
### Every working day (mid-day, 13:00 SAST)
|
||
|
|
|
||
|
|
- [ ] **Open `/orders` — Collected tab.** Verify `stock_verified`
|
||
|
|
and `picking` items are progressing. Anything stuck in
|
||
|
|
`stock_verified` for >2 hours = stock discrepancy. Resolve or
|
||
|
|
escalate to the Stock Controller.
|
||
|
|
- [ ] **Check `/stock?filter=low`** — if any of your orders used a
|
||
|
|
stock item that's now critical, the customer needs a heads-up.
|
||
|
|
|
||
|
|
### Every working day (end of shift, 17:00 SAST)
|
||
|
|
|
||
|
|
- [ ] **Review every order that changed today.** Make sure all
|
||
|
|
status transitions are correct. A `delivered` item that
|
||
|
|
shouldn't be `delivered` is a customer-facing disaster.
|
||
|
|
- [ ] **Triage `/orders` — Delivered tab.** Any `delivered` items
|
||
|
|
older than 1 working day that aren't yet `ready_invoice`? Push
|
||
|
|
them through.
|
||
|
|
|
||
|
|
### Every Monday morning
|
||
|
|
|
||
|
|
- [ ] **Review your KPIs** (see §4). Pay attention to the items
|
||
|
|
that drifted last week.
|
||
|
|
- [ ] **Triage stuck orders** — anything in `pending` or
|
||
|
|
`processed` for >5 working days is a zombie. Either ship,
|
||
|
|
cancel, or escalate.
|
||
|
|
|
||
|
|
### Every Friday afternoon
|
||
|
|
|
||
|
|
- [ ] **Close the week.** Make sure no orders have items in
|
||
|
|
limbo states over the weekend. `assigned` to a driver who
|
||
|
|
doesn't work weekends = uncollected Monday morning.
|
||
|
|
- [ ] **Invoicing handoff**: confirm the invoice clerk has
|
||
|
|
received everything they need for the week's `ready_invoice`
|
||
|
|
items. They'll print Monday morning.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. Step-by-Step Procedures
|
||
|
|
|
||
|
|
### 2.1 Create an order from a quote
|
||
|
|
|
||
|
|
**When:** a customer accepts a quote (you got the call, or the
|
||
|
|
customer replied accepting).
|
||
|
|
|
||
|
|
1. Open the quote detail.
|
||
|
|
2. Click **Create Order**. The system creates:
|
||
|
|
- One new `orders` record with number `ORD-YYYY-MM-NNN`.
|
||
|
|
- One `order_items` row per `quote_items` row, linked back
|
||
|
|
via `order_items.quote_item`.
|
||
|
|
3. The quote status does NOT change automatically. The quote
|
||
|
|
stays in `approved` (or `emailed` + your acceptance note).
|
||
|
|
4. The order starts in **New** (`pending`) tab.
|
||
|
|
|
||
|
|
**Don't:**
|
||
|
|
- Don't try to create an order from the same quote twice. The
|
||
|
|
system returns the existing order, not a duplicate.
|
||
|
|
- Don't edit the quote after creating the order — the items
|
||
|
|
are now in `order_items`, edits to `quote_items` don't
|
||
|
|
propagate.
|
||
|
|
|
||
|
|
### 2.2 Create a walk-in order (no quote)
|
||
|
|
|
||
|
|
**When:** a customer walks in / phones with an order that wasn't
|
||
|
|
quoted.
|
||
|
|
|
||
|
|
1. Open `/orders/new`.
|
||
|
|
2. Pick or create the customer (and company).
|
||
|
|
3. Add items manually. Each item needs at minimum `description`
|
||
|
|
+ `quantity` + `unit_price`. Cost is optional at creation
|
||
|
|
(defaults to 0 — fix later from the PO).
|
||
|
|
4. **Save.** Order lands in `pending`.
|
||
|
|
5. Pricing: see §2.3.
|
||
|
|
|
||
|
|
### 2.3 Price and process a `pending` item
|
||
|
|
|
||
|
|
**When:** an order item lands in `pending` and you have a quote
|
||
|
|
to source from.
|
||
|
|
|
||
|
|
1. Open the order detail.
|
||
|
|
2. Click the line item. Set `cost_price` (from the quote or
|
||
|
|
catalogue) and `selling_price`.
|
||
|
|
3. Click **Mark Processed** (status flips to `processed`).
|
||
|
|
4. **Decide next path:**
|
||
|
|
- **In stock?** → click **Book from Stock**. The item flips
|
||
|
|
to `stock_verified` and the stock qty decrements via the
|
||
|
|
ledger. (See §2.7 for stock flow.)
|
||
|
|
- **Not in stock, need to buy?** → generate a PO. See §2.4.
|
||
|
|
|
||
|
|
### 2.4 Generate a purchase order (PO)
|
||
|
|
|
||
|
|
**When:** an item needs to be ordered from a supplier.
|
||
|
|
|
||
|
|
1. On the order detail, click **Generate PO**. The system groups
|
||
|
|
items by supplier and creates one PO per supplier.
|
||
|
|
2. **Open the PO.** Verify:
|
||
|
|
- Items + quantities match the order.
|
||
|
|
- Cost prices are correct (from the quote or a fresh
|
||
|
|
supplier call).
|
||
|
|
- Delivery date is realistic.
|
||
|
|
3. Click **Send PO**. The email modal opens. Same shape as
|
||
|
|
sending a quote (§2.4 in the Quote Operator SOP).
|
||
|
|
4. n8n sends the email. The supplier reply monitor updates
|
||
|
|
`supplier_replied_at` when the supplier replies.
|
||
|
|
5. The order item flips to `po_generated`.
|
||
|
|
|
||
|
|
**Don't:**
|
||
|
|
- Don't generate a PO without verifying the cost. Once sent, the
|
||
|
|
PO is the contract — wrong cost = wrong margin.
|
||
|
|
|
||
|
|
### 2.5 Receive a supplier invoice and book the goods
|
||
|
|
|
||
|
|
**When:** a supplier invoice / delivery note arrives (PDF in
|
||
|
|
email, or paper).
|
||
|
|
|
||
|
|
1. Identify the PO. Look for the "Your Ref" / "Order No" on
|
||
|
|
the document. It'll be `PO-YYYY-MM-NNN` (customer PO) or
|
||
|
|
`PO{yy}{6d-seq}STK` (stock PO).
|
||
|
|
2. **Diff first** — call the system to compare the invoice
|
||
|
|
against the PO. Read the verdict:
|
||
|
|
- `ready_to_receive` → continue to step 3.
|
||
|
|
- Anything else (`cost_mismatch`, `qty_mismatch`,
|
||
|
|
`supplier_mismatch`, `wrong_status`, `po_not_found`,
|
||
|
|
`unmatched_*`) → **stop and report** to the supervisor.
|
||
|
|
Don't force it through.
|
||
|
|
3. **Commit the receive** — for stock POs this books into
|
||
|
|
`stock_items.current_qty` and writes a `stock_ledger` row.
|
||
|
|
For customer POs, the order items flip to `collected` and
|
||
|
|
the supplier invoice number is stamped.
|
||
|
|
4. The system attaches the invoice PDF to the PO.
|
||
|
|
|
||
|
|
**Mode:**
|
||
|
|
- `mode: "invoice"` — full invoice received (price + qty).
|
||
|
|
- `mode: "grn"` — only a delivery note (qty only, no price
|
||
|
|
check). Use this when the supplier sends the packing slip
|
||
|
|
before the invoice.
|
||
|
|
|
||
|
|
**Surplus / Short:**
|
||
|
|
- If the supplier delivered MORE than ordered (surplus), the
|
||
|
|
PO diff flags it. Confirm with the Stock Controller; they
|
||
|
|
may book the surplus to stock (§2.8).
|
||
|
|
- If LESS than ordered (short), the system records the
|
||
|
|
received qty. Create a continuation order for the missing
|
||
|
|
qty (§2.6).
|
||
|
|
|
||
|
|
### 2.6 Split an order into a continuation
|
||
|
|
|
||
|
|
**When:** some lines are stuck (waiting on a back-ordered part),
|
||
|
|
the rest can ship. Or: a walk-in order has lines added later
|
||
|
|
and the original is past `pending`.
|
||
|
|
|
||
|
|
1. Open the order detail.
|
||
|
|
2. Verify which lines are `pending`. **Only `pending` lines can
|
||
|
|
be moved** (others refuse silently to avoid orphaning a PO
|
||
|
|
or driver assignment).
|
||
|
|
3. Click **Split Order**. Pick the `pending` lines to move.
|
||
|
|
4. A new `ORD-YYYY-MM-NNN` order is created with the moved
|
||
|
|
lines. The parent keeps its number; the moved lines get a
|
||
|
|
breadcrumb in `notes`.
|
||
|
|
5. Process the new continuation order normally.
|
||
|
|
|
||
|
|
### 2.7 Book from stock
|
||
|
|
|
||
|
|
**When:** a `processed` line item has a `stock_item_id` set and
|
||
|
|
qty available.
|
||
|
|
|
||
|
|
1. On the order detail, click **Book from Stock**.
|
||
|
|
2. The system calls `/api/v1/stock/fulfill` — decrements
|
||
|
|
`stock_items.current_qty` and writes a `stock_ledger` row
|
||
|
|
with `reason: "customer_fulfillment"`.
|
||
|
|
3. The order item flips to `stock_verified` → `picking`
|
||
|
|
(or directly to `collected` if the picking step is
|
||
|
|
bypassed for direct-from-stock orders).
|
||
|
|
|
||
|
|
**If the stock qty is short** (someone else booked it first),
|
||
|
|
the system refuses. Either:
|
||
|
|
- Cancel the line (status → `cancelled`).
|
||
|
|
- Wait for restock and re-try later.
|
||
|
|
- Source from the supplier instead (revert to `processed` →
|
||
|
|
generate PO).
|
||
|
|
|
||
|
|
### 2.8 Book a surplus
|
||
|
|
|
||
|
|
**When:** the supplier delivered more than ordered (e.g. 12
|
||
|
|
instead of 10), and the customer only needs 10.
|
||
|
|
|
||
|
|
1. On the order item, click **Book Surplus**.
|
||
|
|
2. Enter the surplus qty (e.g. 2).
|
||
|
|
3. The system:
|
||
|
|
- Splits the order line: customer qty (10) stays on the
|
||
|
|
order, surplus qty (2) goes to stock.
|
||
|
|
- Writes a `stock_ledger` row with `reason: "purchase_surplus"`.
|
||
|
|
- Sets `order_items.surplus_qty` and `stock_booked_from` on
|
||
|
|
the line.
|
||
|
|
4. The stock item's `current_qty` increments.
|
||
|
|
|
||
|
|
### 2.9 Confirm collection (driver flow)
|
||
|
|
|
||
|
|
**When:** the driver has picked up the goods and the collection
|
||
|
|
is being confirmed back at the office (or via the mobile app).
|
||
|
|
|
||
|
|
1. On the order detail, find the item. It should be in
|
||
|
|
`collection_pool` or `assigned`.
|
||
|
|
2. Click **Confirm Collected**. Enter the qty received (the
|
||
|
|
`quantity_collected` field is the amount to ADD, not the
|
||
|
|
new total).
|
||
|
|
3. For partial deliveries: clamp to `received_qty - qty_collected`
|
||
|
|
(≥0). Add a `note` explaining the partial.
|
||
|
|
4. The item flips to `collected` (full) or stays in
|
||
|
|
`collection_pool` (partial) with the `qty_collected`
|
||
|
|
updated.
|
||
|
|
|
||
|
|
**Actor field is required.** The system stamps the actor on
|
||
|
|
every audit entry. If you don't pass it, the audit trail
|
||
|
|
loses you.
|
||
|
|
|
||
|
|
### 2.10 Pick, pack, deliver
|
||
|
|
|
||
|
|
**When:** items are `collected` and ready to be packed for
|
||
|
|
delivery.
|
||
|
|
|
||
|
|
1. `collected` → `stock_verified` (verify qty against the
|
||
|
|
shelf count).
|
||
|
|
2. `stock_verified` → `picking` (operator starts pulling).
|
||
|
|
3. `picking` → `ready_delivery` (packed, on the truck).
|
||
|
|
4. `ready_delivery` → `delivered` (signed for at the customer).
|
||
|
|
|
||
|
|
**Each step needs an operator action.** Don't bulk-flip —
|
||
|
|
the audit trail per step is the customer's dispute evidence.
|
||
|
|
|
||
|
|
### 2.11 Mark an order delivered (header-level)
|
||
|
|
|
||
|
|
**When:** every item on the order is `delivered` (or
|
||
|
|
short-circuits via the eligible set — see tool doc).
|
||
|
|
|
||
|
|
1. Open the order detail.
|
||
|
|
2. Click **Mark Delivered**. The system flips every eligible
|
||
|
|
item to `delivered` and stamps `delivered_at` +
|
||
|
|
`delivered_date`.
|
||
|
|
3. **Does NOT** flip `order.invoiced`. The Invoicing Clerk does
|
||
|
|
that.
|
||
|
|
|
||
|
|
If the tool reports `data.itemsFailed` (some items refused),
|
||
|
|
inspect them. The most common reason is an item in
|
||
|
|
`pending`/`processed`/`cancelled` that wasn't eligible.
|
||
|
|
|
||
|
|
### 2.12 Hand off to invoicing
|
||
|
|
|
||
|
|
**When:** items are `delivered` and the customer has been
|
||
|
|
notified.
|
||
|
|
|
||
|
|
1. On the order detail, click **Mark Ready to Invoice** (the
|
||
|
|
button shows when items are eligible). The items flip to
|
||
|
|
`ready_invoice`.
|
||
|
|
2. **Don't** call `mark_invoiced` here — that's the
|
||
|
|
Invoicing Clerk's job, after they generate the QB invoice
|
||
|
|
or delivery note.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. Decision Tree
|
||
|
|
|
||
|
|
### 3.1 The customer wants to cancel after the order is placed
|
||
|
|
|
||
|
|
```
|
||
|
|
Has the supplier been PO'd yet (item is in `processed` or later)?
|
||
|
|
├── No → cancel the line(s), no cost incurred. Easy.
|
||
|
|
└── Yes
|
||
|
|
├── Has the supplier confirmed the order (replied to PO)?
|
||
|
|
│ ├── No → phone the supplier NOW. Most will cancel
|
||
|
|
│ │ without charge if you catch it pre-confirm.
|
||
|
|
│ └── Yes
|
||
|
|
│ ├── Is the goods in transit / collected?
|
||
|
|
│ │ ├── No → call the supplier, negotiate a cancel.
|
||
|
|
│ │ │ Some will charge a handling fee.
|
||
|
|
│ │ └── Yes → goods will arrive. Three options:
|
||
|
|
│ │ a) Accept the goods, book to stock,
|
||
|
|
│ │ re-sell later.
|
||
|
|
│ │ b) Refuse delivery (record the refusal
|
||
|
|
│ │ on the order notes, supervisor
|
||
|
|
│ │ decides whether to charge the customer
|
||
|
|
│ │ a restocking fee).
|
||
|
|
│ │ c) Ask the supplier to return-to-vendor.
|
||
|
|
│ │ Their policy, not yours.
|
||
|
|
│ └── Surplus / different qty → see §3.3
|
||
|
|
└── Customer wants to change items, not cancel → see §3.2
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3.2 Customer wants to change items after the order is placed
|
||
|
|
|
||
|
|
```
|
||
|
|
Is the order still all-`pending`?
|
||
|
|
├── Yes → use the order editor to swap the items.
|
||
|
|
└── No (some lines already `processed` or later)
|
||
|
|
├── Only the `pending` lines need to change → split off the
|
||
|
|
│ pending lines, edit on the parent, leave the rest.
|
||
|
|
└── Some `processed` lines need to change
|
||
|
|
├── Has the supplier PO'd been sent?
|
||
|
|
│ ├── No → revert the line to `pending`, edit.
|
||
|
|
│ └── Yes → phone the supplier first, then decide:
|
||
|
|
│ either cancel the PO + re-issue, or ship
|
||
|
|
│ the original and create a separate order
|
||
|
|
│ for the replacement.
|
||
|
|
└── The change affects pricing → update the order's
|
||
|
|
selling price, document the customer agreement in
|
||
|
|
`order.notes`.
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3.3 Supplier delivered wrong / damaged / short
|
||
|
|
|
||
|
|
```
|
||
|
|
What's the discrepancy?
|
||
|
|
├── Wrong qty (short)
|
||
|
|
│ ├── Receive what arrived (use the `quantity_collected`
|
||
|
|
│ │ to-add semantics).
|
||
|
|
│ ├── Mark the missing qty as back-ordered. Create a
|
||
|
|
│ │ continuation order for it (§2.6) when you can re-supply.
|
||
|
|
│ └── Phone the supplier, log the short in `order.notes`.
|
||
|
|
├── Wrong qty (over — surplus)
|
||
|
|
│ └── See §2.8. Book the surplus to stock, keep the order qty
|
||
|
|
│ at the customer's need.
|
||
|
|
├── Wrong item
|
||
|
|
│ ├── Refuse delivery. Phone the supplier for a return-to-
|
||
|
|
│ │ vendor or a credit note.
|
||
|
|
│ └── If customer will accept the wrong item at a discount →
|
||
|
|
│ call the supervisor, don't unilaterally discount.
|
||
|
|
├── Damaged
|
||
|
|
│ ├── Refuse delivery. Take photos, attach to the PO via the
|
||
|
|
│ │ supplier_invoice_ prefix on `order_item_attachements`.
|
||
|
|
│ └── If customer accepts damaged at a discount → supervisor
|
||
|
|
│ call, photos on file, credit note from supplier.
|
||
|
|
└── All correct
|
||
|
|
└── Confirm the receive (§2.5).
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3.4 Stock item is short when I try to book
|
||
|
|
|
||
|
|
```
|
||
|
|
Is the short a rounding / measurement error?
|
||
|
|
├── Yes → recheck the stock count. The Stock Controller may
|
||
|
|
│ have missed an entry.
|
||
|
|
└── No
|
||
|
|
├── Is the item critical (low stock) → flagged in
|
||
|
|
│ `/stock?filter=critical`?
|
||
|
|
│ ├── Yes → the order is more important than the stock
|
||
|
|
│ │ refill. Book it. File a stock-order for
|
||
|
|
│ │ restock.
|
||
|
|
│ └── No → cancel the stock booking, source from the
|
||
|
|
│ supplier via PO.
|
||
|
|
└── Has the stock item been double-booked? → check
|
||
|
|
`stock_ledger` for recent bookings. The Stock
|
||
|
|
Controller can release the duplicate.
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3.5 Supplier hasn't replied to a PO in 3+ days
|
||
|
|
|
||
|
|
```
|
||
|
|
Did the PO send successfully (check `email_message_id`)?
|
||
|
|
├── No (mail_failed) → retry the send, verify recipient address
|
||
|
|
└── Yes
|
||
|
|
├── Phone the supplier. Most POs are lost in spam.
|
||
|
|
├── If no answer, email the sales rep directly (check
|
||
|
|
│ `/suppliers` for the contact).
|
||
|
|
└── After 7 working days with no reply → escalate to
|
||
|
|
supervisor. We may need to find a backup supplier.
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3.6 Customer wants a delivery date that's not realistic
|
||
|
|
|
||
|
|
```
|
||
|
|
Can the supplier meet it (have they confirmed)?
|
||
|
|
├── Yes → set the order's `delivery_date` accordingly.
|
||
|
|
└── No
|
||
|
|
├── Is the customer willing to wait?
|
||
|
|
│ ├── Yes → set a realistic date, log the agreement in
|
||
|
|
│ │ `order.notes`. Don't over-promise.
|
||
|
|
│ └── No → escalate to supervisor. We may need to split
|
||
|
|
│ sources, expedite (at a cost), or partial-
|
||
|
|
│ ship.
|
||
|
|
└── Is there stock for at least part of the order? → split
|
||
|
|
the order (§2.6). Ship what's in stock, back-order
|
||
|
|
the rest.
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3.7 An order has items in mixed states (some delivered, some pending)
|
||
|
|
|
||
|
|
```
|
||
|
|
Is the customer OK with partial delivery?
|
||
|
|
├── Yes → ship what's ready. The delivered items go to
|
||
|
|
│ invoicing. The pending items stay on the order
|
||
|
|
│ until they're ready.
|
||
|
|
└── No (customer needs everything together) → leave the order
|
||
|
|
open. Don't flip anything to delivered until everything
|
||
|
|
is on the truck. The customer is OK with the wait.
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. KPIs
|
||
|
|
|
||
|
|
All KPIs measured in working days.
|
||
|
|
|
||
|
|
| KPI | Target | How it's measured |
|
||
|
|
|---|---|---|
|
||
|
|
| **Order creation turnaround (quote accepted → order exists)** | < 30 min median | `orders.created - quotes.approved_at` |
|
||
|
|
| **`pending` → `processed` turnaround** | < 4 working hours | median `processed_at - created` on `order_items` |
|
||
|
|
| **PO send turnaround (processed → po_generated)** | < 1 working day | median `po_generated_at - processed_at` |
|
||
|
|
| **Supplier reply time** | < 2 working days median | `supplier_replied_at - email_message_id_sent` |
|
||
|
|
| **PO stale rate** | < 5% of open POs | POs without reply > 3 working days / open POs |
|
||
|
|
| **Collection turnaround (po_generated → collected)** | < 3 working days | median `collected_at - po_generated_at` |
|
||
|
|
| **`collected` → `delivered` turnaround** | < 1 working day | median `delivered_at - collected_at` |
|
||
|
|
| **Order completion rate (delivered within 5 working days of `pending`)** | > 80% | orders with all items `delivered` within 5 wd / orders created |
|
||
|
|
| **PO cost-mismatch rate** | < 3% of receives | count of `cost_mismatch` verdicts on diff / total receives |
|
||
|
|
| **Surplus booking rate** | 100% of surpluses booked same day | `stock_ledger` entries with `reason="purchase_surplus"` per day vs supplier delivery notes |
|
||
|
|
| **Split order rate** | < 10% of orders | `orders` with `notes` containing a split breadcrumb / total orders |
|
||
|
|
| **Cancellation rate** | < 5% of `pending` lines | `order_items.item_status="cancelled"` / total `order_items` created |
|
||
|
|
|
||
|
|
### How KPIs feed into your review
|
||
|
|
|
||
|
|
- **Weekly** (Monday standup): one-pager with the operator's
|
||
|
|
KPIs. Stale POs, cost mismatches, and stuck pending items are
|
||
|
|
the supervisor's first questions.
|
||
|
|
- **Monthly** (1st of month): 1-page review with the operator.
|
||
|
|
Surplus booking rate and PO cost-mismatch rate are weighted
|
||
|
|
double — getting them wrong is a money leak.
|
||
|
|
- **Quarterly**: top performers on completion rate + on-time
|
||
|
|
delivery are eligible for the "trusted buyer" tier — they can
|
||
|
|
raise POs up to a higher value without supervisor sign-off.
|
||
|
|
|
||
|
|
### What the system does NOT measure (yet)
|
||
|
|
|
||
|
|
- Customer satisfaction post-delivery (no feedback loop yet)
|
||
|
|
- Damage rate per supplier (only the PO-level diff verdict is
|
||
|
|
captured, not the cause)
|
||
|
|
- Driver utilisation (that's the collection driver's KPI)
|
||
|
|
|
||
|
|
If a KPI is unfair, raise it. The supervisor can adjust
|
||
|
|
targets, but the metric stays.
|