WFDASH-WEBAPP/docs/sops/collection-driver.md
Dave aed3f5c861 feat: implement discount functionality in POS system
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.
2026-09-03 12:19:21 +02:00

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 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.