Added support for discounts in the POS checkout process, allowing operators to apply either a fixed amount or a percentage off the gross total. Updated the state management to include discount details and modified the receipt and cart rendering to display pre- and post-discount values. Enhanced the checkout API to handle discount inputs and ensure proper calculations. Updated relevant tests to cover new discount logic.
15 KiB
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 incollection_poolorassignedto you. - To Order Operator → you mark
collectedon 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(notdelivered) 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_poolover 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.
- Open WFDASHDRIVER-APP.
- 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). - 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.
- Tap the supplier row in the app.
- Tap Navigate. The app opens your default maps app with the supplier's address.
- 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.
- Tap Confirm Collection on the supplier row.
- 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.
- 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. - Review the totals at the bottom. The qty received should match the qty ordered.
- Tap Submit. The order items flip to
collectedand 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.
- Follow §2.3 up to step 3.
- For each line, enter the actual qty received (not the full qty). The app shows the difference.
- In the Note field, write the reason ("supplier short by 2, ETA TBD" or "supplier out of stock on this SKU").
- Continue. Tap Submit.
- The items flip to
collectedwithqty_collectedset 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.
- Don't accept the goods (refuse at the supplier).
- In the app, on the line, tap Report Problem.
- Choose:
wrong_item,damaged,expired, orother. - Take photos — at least 2 (whole item + close-up of the
problem). The app attaches them with a
damage_orwrong_item_filename prefix. - Add a note explaining the situation.
- Get the supplier's rep to sign the delivery note with the discrepancy. Photograph the signed note.
- Tap Submit. The item stays in
collection_pool(not flipped tocollected). The Order Operator handles the dispute.
2.6 Deliver to a customer
When: you're at the customer's site with their order.
- Tap the order row in the app.
- Tap Confirm Delivery.
- 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).
- Take a photo of the delivered goods at the customer's site (best practice for disputes).
- Get the customer's signature. The app captures the signature on screen. Alternatively, photograph the signed waybill.
- 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).
- Follow §2.3, but for the over-delivered qty, tap Surplus.
- Enter the surplus qty (e.g. 2).
- The system splits the line: customer qty (10) stays on
the order, surplus qty (2) is booked to stock. A
stock_ledgerentry is written withreason: "purchase_surplus". - Continue with §2.3 for the rest.
2.8 Hand off a damaged delivery to stock
When: a customer refuses damaged goods on arrival.
- Take photos at the customer's site (see §2.5).
- Get the customer's signature on the refusal note.
- 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.