WFDASH-WEBAPP/docs/sops/collection-driver.md

384 lines
15 KiB
Markdown
Raw Normal View History

# Collection Driver — SOP
**Owns:** picking up goods from suppliers, delivering to customers,
capturing proof of collection and delivery. Runs on the
**WFDASHDRIVER-APP** (mobile) and `/collection-confirm` (browser
fallback).
**Tools (mobile):** WFDASHDRIVER-APP, `/collection-confirm`.
**Tools (office view):** `/collection-pool`, `/collection-map`.
**Handoffs:**
- **From Order Operator** → PO is `po_generated`, item is in
`collection_pool` or `assigned` to you.
- **To Order Operator** → you mark `collected` on the way back
to the office, or on delivery.
- **To Stock Controller** → surplus or damaged goods need to be
booked.
**Note:** `drv_status` and `drv_id` are the driver-side fields on
`order_items`. The status used here is the item-level
`item_status` (see Decision Tree for the exact mapping).
---
## 1. Daily / Weekly Checklist
### Every working day (start of shift, 07:00 SAST)
- [ ] **Open WFDASHDRIVER-APP — Today's Run.** Review the
supplier list, the order of pickups, and any urgent adds from
the Order Operator.
- [ ] **Check the truck:** fuel, waybills, blank GRN books,
packing tape, camera (phone), and a power bank.
- [ ] **Confirm any special instructions** on items (oversize,
fragile, supplier with restricted hours).
- [ ] **Phone the first supplier** if you haven't worked with
them in a while. Confirm they're open and the order is ready.
### Every working day (mid-day, 12:00 SAST — or at lunch)
- [ ] **Triage anything blocking you.** Phone the Order Operator
if a supplier can't fulfil today, if a customer can't receive,
or if you have a route problem.
- [ ] **Update the app** with any deviations (supplier short,
customer closed, etc).
### Every working day (end of shift, 17:00 SAST)
- [ ] **Mark all today's items with their final status.** No
item should be left "in flight" overnight. If a customer can't
receive, mark `assigned` (not `delivered`) and add a note.
- [ ] **Park any surplus / damage** with the Stock Controller.
Don't leave it on the truck.
- [ ] **Sync the app** — verify all photos uploaded, all
confirmations saved. Mobile data can be patchy; the sync
indicator in the top-right is the truth.
### Every Monday morning
- [ ] **Review your KPIs** (see §4). Look at last week's
collection rate, on-time rate, and error rate.
- [ ] **Walk the truck.** Inventory the packing materials,
reusable crates, etc. Report any missing.
### Every Friday afternoon
- [ ] **Triage any open collections** — items still
`collection_pool` over the weekend are at risk of supplier
closure or customer cancellation. Flag to the Order Operator.
---
## 2. Step-by-Step Procedures
### 2.1 View your assigned run
**When:** you arrive for your shift.
1. Open WFDASHDRIVER-APP.
2. Tap **Today's Run**. You see a list grouped by supplier, in
the order the Order Operator suggested (usually optimised by
proximity — see `/collection-map`).
3. For each supplier, you see:
- Supplier name, address, contact.
- Items to collect, with qty + description.
- The PO number (so the supplier can match).
- Any special instructions.
**If you need to re-order the run:** tap the supplier row,
**Re-order**. The Order Operator gets a notification.
### 2.2 Navigate to a supplier
**When:** you're at the first pickup.
1. Tap the supplier row in the app.
2. Tap **Navigate**. The app opens your default maps app with
the supplier's address.
3. **If the address is wrong:** tap **Report Wrong Address** on
the supplier row. Don't try to find the supplier yourself —
the Order Operator needs to fix the master data.
### 2.3 Confirm collection (the happy path)
**When:** you arrive at the supplier and the order is ready.
1. Tap **Confirm Collection** on the supplier row.
2. The app shows each line item. For each:
- **Scan the barcode** if the supplier labels have them
(faster, more accurate). The app matches against the
expected SKU.
- **Or tap to mark received** if no barcode.
3. If the supplier gave you a delivery note / invoice (paper
or PDF), tap **Attach**. Photograph it. The app stores it
with the standard `supplier_invoice_` filename prefix.
4. **Review the totals** at the bottom. The qty received should
match the qty ordered.
5. Tap **Submit**. The order items flip to `collected` and
the supplier invoice is attached to the PO.
**Don't:**
- Don't submit without attaching the supplier's invoice. The
Order Operator needs it to do the diff/receive.
- Don't submit with the wrong qty. If the supplier shorted
you, see §3.1.
### 2.4 Confirm a partial collection
**When:** the supplier delivered less than ordered.
1. Follow §2.3 up to step 3.
2. For each line, enter the **actual qty received** (not the
full qty). The app shows the difference.
3. In the **Note** field, write the reason ("supplier short by
2, ETA TBD" or "supplier out of stock on this SKU").
4. Continue. Tap **Submit**.
5. The items flip to `collected` with `qty_collected` set to
the partial amount. The Order Operator creates a
continuation order for the missing qty (see Order Operator
§2.6).
### 2.5 Report a wrong / damaged item
**When:** the supplier gave you the wrong item, or it's damaged.
1. **Don't accept the goods** (refuse at the supplier).
2. In the app, on the line, tap **Report Problem**.
3. Choose: `wrong_item`, `damaged`, `expired`, or `other`.
4. **Take photos** — at least 2 (whole item + close-up of the
problem). The app attaches them with a `damage_` or
`wrong_item_` filename prefix.
5. Add a note explaining the situation.
6. **Get the supplier's rep to sign** the delivery note with
the discrepancy. Photograph the signed note.
7. Tap **Submit**. The item stays in `collection_pool` (not
flipped to `collected`). The Order Operator handles the
dispute.
### 2.6 Deliver to a customer
**When:** you're at the customer's site with their order.
1. Tap the order row in the app.
2. Tap **Confirm Delivery**.
3. **For each line:**
- **Mark delivered** (qty matches).
- Or **partial delivery** (customer accepts less — see §3.4).
- Or **refused** (customer won't accept — see §3.5).
4. **Take a photo of the delivered goods** at the customer's
site (best practice for disputes).
5. **Get the customer's signature.** The app captures the
signature on screen. Alternatively, photograph the signed
waybill.
6. Tap **Submit**. Items flip to `delivered`.
**Don't:**
- Don't deliver without a signature / photo. A "delivered"
item without proof is a dispute magnet.
### 2.7 Report a surplus
**When:** the supplier gave you MORE than ordered (e.g. 12
instead of 10).
1. Follow §2.3, but for the over-delivered qty, tap **Surplus**.
2. Enter the surplus qty (e.g. 2).
3. The system splits the line: customer qty (10) stays on
the order, surplus qty (2) is booked to stock. A
`stock_ledger` entry is written with
`reason: "purchase_surplus"`.
4. Continue with §2.3 for the rest.
### 2.8 Hand off a damaged delivery to stock
**When:** a customer refuses damaged goods on arrival.
1. Take photos at the customer's site (see §2.5).
2. Get the customer's signature on the refusal note.
3. Bring the goods back to the office. **Don't** book them to
stock yourself — hand to the Stock Controller. They'll
inspect, decide return-to-vendor vs write-off, and book the
ledger entry.
---
## 3. Decision Tree
### 3.1 Supplier short / partial
```
Did the supplier know they were short (pre-called you)?
├── Yes → log partial qty, attach their note, submit. Order
│ Operator creates the continuation.
└── No
├── Did they offer a back-order ETA?
│ ├── Yes (within 1 week) → take the partial, log the
│ │ ETA, Order Operator schedules the back-order.
│ └── No / vague → take what they have. Order Operator
│ phones the supplier for an ETA, may
│ source from a backup.
└── Is the partial critical (customer waiting, no
substitute)?
├── Yes → phone the Order Operator NOW. They may
│ source from a backup supplier while you're
│ still at the supplier.
└── No → take the partial, log it, continue the run.
```
### 3.2 Supplier closed / not ready
```
Can you reach the supplier by phone?
├── Yes
├── "Order not ready, will be in 2 hours" → wait if your
│ schedule allows. If not, re-order your run.
├── "Order not ready, will be tomorrow" → re-order your
│ run, do the other suppliers first. Phone the Order
│ Operator if it means the customer won't get it today.
└── "Closed for the day" → re-order, mark `assigned` not
`collected`, phone the Order
Operator.
└── No → re-order your run. Phone the Order Operator. Don't
wait — every minute parked is a minute not delivering.
```
### 3.3 Wrong address (supplier or customer)
```
Is the address in the app matching what you see?
├── No (typo in the app, building really exists)
│ └── Tap **Report Wrong Address**. Don't try to find it
│ yourself — the master data needs fixing. Wait for
│ the Order Operator.
└── The address is right but you can't find the place
├── Phone the contact on the row.
├── If no answer, ask locals / security.
└── If still can't find → mark `assigned`, note "couldn't
locate", return to the office. Order Operator
resolves.
```
### 3.4 Customer wants partial delivery (not what we brought)
```
Is the customer a strategic account?
├── Yes → call the Order Operator before doing anything. Don't
│ make a delivery decision on the customer's porch.
└── No
├── Will they accept what they need and leave the rest?
│ ├── Yes → deliver the partial, note the refused qty
│ │ and reason, return the rest to the office.
│ └── No (must be all or nothing) → mark `assigned`,
│ return the whole
│ delivery to the
│ office. Phone the
│ Order Operator.
└── Is the partial a "while you're here" ask (can they
take an extra qty)?
└── Phone the Order Operator. Don't unilaterally
add to the delivery.
```
### 3.5 Customer refuses (damaged, wrong, "changed my mind")
```
Why are they refusing?
├── Damaged → §2.5 / §2.8. Take photos, get signature, return
│ to office.
├── Wrong item → §2.5. Same process.
├── Customer changed mind (no defect)
│ ├── Customer pays restocking fee per policy?
│ │ ├── Yes → take the refusal, return to office, note
│ │ │ the fee on the order.
│ │ └── No / unclear → phone the Order Operator. Don't
│ │ bring the goods back without
│ │ confirming the restocking fee.
│ └── Customer wants to keep it but at a discount?
│ └── Phone the Order Operator. Don't unilaterally
discount.
└── Other
└── Phone the Order Operator. Get instruction.
```
### 3.6 Customer not on site when you arrive
```
Have you called ahead?
├── Yes (no answer) → wait 15 min. Try again. If still
│ nothing, leave a card / note, mark
│ `assigned` with "not on site", return
│ to office. Order Operator reschedules.
└── No → call now. If no answer, same as above.
```
### 3.7 Truck breakdown / accident
```
Is anyone hurt?
├── Yes → emergency services first. Then phone the Order
│ Operator. The goods are secondary.
└── No
├── Is the truck driveable?
│ ├── Yes → drive to a safe spot. Phone the Order
│ │ Operator. They'll re-assign the rest of
│ │ your run to another driver.
│ └── No → road service. Phone the Order Operator.
│ They'll re-assign.
└── Can the goods be salvaged? → assess, take photos,
continue if possible.
```
### 3.8 Bad weather
```
Is the road to the customer / supplier safe?
├── No → don't go. Phone the Order Operator. They'll
│ re-schedule. Customer/supplier gets a weather
│ delay note on the order.
└── Yes → continue. Take extra care.
```
---
## 4. KPIs
All KPIs measured per working day unless noted.
| KPI | Target | How it's measured |
|---|---|---|
| **On-time collection rate** | > 90% of `assigned` items `collected` same day | `count(items where item_status=collected AND collected_at date = assigned_at date) / count(assigned items)` |
| **On-time delivery rate** | > 95% of `ready_delivery` items `delivered` within the customer-promised window | per-order `delivery_date` vs `delivered_at` |
| **Error rate (wrong item / damaged)** | < 2% of line items | `count(items with damage/wrong_item photo attached) / total items handled` |
| **Photo proof rate** | 100% of collections + deliveries have at least one photo | `count(photos) / count(items handled)` — should be 1:1 minimum |
| **Signature capture rate** | 100% of deliveries have a signature | same as above, for the signature field |
| **Surplus booking rate** | 100% of surpluses booked same day | `stock_ledger` entries with `reason="purchase_surplus"` per day vs supplier delivery notes flagged with surplus |
| **Refusal rate** | < 3% of attempted deliveries | `count(customer refusals) / count(delivery attempts)` |
| **Re-run rate** | < 5% of items needing a second visit | `count(items with 2+ collection attempts)` |
| **App sync success** | 100% (all confirmations uploaded by EOD) | `count(items confirmed offline more than 4h) / total items confirmed` — should be zero |
| **Cost per collection** | tracked, target stable | total driver cost (incl truck, fuel) / number of collections per month |
### How KPIs feed into your review
- **Daily** (end of shift): the app's home screen shows your
day's KPI snapshot. If any are red, the supervisor gets an
alert.
- **Weekly** (Monday standup): one-pager with the week's KPIs.
On-time rate and error rate are the headline numbers.
- **Monthly** (1st of month): 1-page review with the
supervisor. **Photo proof rate and signature capture rate
must be 100%** — these are the dispute-defence evidence.
Anything below is a process failure.
- **Quarterly**: top performers on on-time + zero-error are
eligible for the senior-driver tier (they can take the
high-value / strategic-account runs).
### What the system does NOT measure (yet)
- Customer satisfaction (no NPS yet)
- Fuel efficiency per route
- Truck wear-and-tear
If a KPI is unfair, raise it. The supervisor can adjust
targets, but the metric stays.