docs: driver invoice photo handover - widen the filter, use confirm_collected

Tested the handover against live PB on 2026-10-27. The read path works as
written; four defects fixed:

- The filter only caught item_status = 'assigned'. The collection_pool
  tab group is [collection_pool, assigned, ready_delivery] and
  order_items.confirm_collected accepts collection_pool or assigned, so
  a line stuck in either was invisible to the sweep.
- Stock POs were not covered at all. Stock lines live in
  stock_purchase_items and their photos on the parent
  stock_purchases.additional_attachments.
- The write path hand-rolled order_items.update + update_status, which
  skips the eligibility check, the over-collect guard, drv_collected_at
  and the actor-stamped audit note. Now uses
  order_items.confirm_collected / stock_purchase_items.mark_collected.
- No de-duplication: the NSS M24 line is still 'assigned' with a photo,
  so every run would re-report it. Added a 'Waiting on a person' list.

Also noted that no cron runs this yet, that order_items_attachements is
multi-file (read every filename), and added a supplier check.
This commit is contained in:
Dave 2026-10-27 07:07:40 +02:00
parent b212d0fcd8
commit 16743f87ed

View file

@ -0,0 +1,130 @@
# Handover: driver invoice photos that were never collected
Run every 30 minutes during the working day (06:00–17:00 SAST).
> **Not scheduled yet.** No cron task runs this — the 5 live crons are the
> MCODe basket, three PocketBase backups and the n8n watchdog. Until one is
> created, someone has to run this by hand.
## The problem
Drivers photograph the supplier invoice on a PO in the driver app, but
sometimes never tap **Collect**. The line then stays waiting, so it
never reaches **Collection Confirm** and never gets invoiced. A new app
version (auto invoice match + "Collect these items?" prompt) should stop
this, but until it is proven, check by hand.
Sometimes the photo is also on the **wrong PO** (e.g. the Metabo invoice
was uploaded on the Mega Lube PO), or is not an invoice at all.
Two sides to check — they are different collections with different
attachment fields:
| Side | Line lives in | Waiting statuses | Photo lives in |
|---|---|---|---|
| Customer PO | `order_items` | `collection_pool`, `assigned`, `ready_delivery` | `order_items.order_item_attachements` |
| Stock PO | `stock_purchase_items` | `collection_pool`, `assigned_to_driver` | `stock_purchases.additional_attachments` (**on the parent PO**) |
All three customer waiting statuses count. The "collection pool" tab group
is `["collection_pool", "assigned", "ready_delivery"]` and
`order_items_confirm_collected` accepts `collection_pool` **or**
`assigned` — a line stuck in any of them never reaches Collection Confirm.
## The check
### 1. Find the lines
Customer lines — `order_items_list` with filter:
```
(item_status = "collection_pool" || item_status = "assigned" || item_status = "ready_delivery") && order_item_attachements:length > 0
```
expand `purchase_order,supplier` — the PO number is
`expand.purchase_order.po_number`, it is not a field on the line itself.
Stock lines — `stock_purchase_items_list` with filter:
```
status = "collection_pool" || status = "assigned_to_driver"
```
expand `purchase` — the PO number is `expand.purchase.purchase_number`
(looks like `PO26100606STK`, not `PO...MAS`), and the photos are in
`expand.purchase.additional_attachments`.
### 2. Read every photo
`order_item_attachements` is a **multi-file** field (up to 10), and stock
POs can have several pages (`..._p1_`, `..._p2_`). Open **each** filename:
- customer line → `files_fetch`, collection `order_items`, the line id, the filename
- stock line → `files_fetch`, collection `stock_purchases`, the **parent PO** id, the filename
If a photo cannot be read as an invoice, treat the whole line as
unreadable (below).
### 3. Compare the invoice with the line
- the PO number printed on the invoice (reference / customer order no.)
= the line's PO number
- the supplier on the invoice = the line's supplier
- the item matches the line's description
- the quantity on the invoice = the line's `quantity`
- the invoice is dated today or recently
## What to do
- **Everything matches** → mark it collected. Do **not** hand-roll this
with `order_items_update` + `order_items_update_status`; use the tool
that exists for it, because it validates eligibility, refuses to
over-collect, stamps the audit note and refuses rows that are already
past the pool:
- customer line → `order_items_confirm_collected` with
`{ item_ids: [...], actor: { id, name }, quantity_collected: { <line id>: <invoice qty> } }`
- stock line → `stock_purchase_items_mark_collected` with
`{ id: <line id>, quantity_collected: <invoice qty> }`
- `quantity_collected` is the qty to **add**, not the new total. Omit it
to collect the full remaining quantity.
- `actor.id` is a free-form string — use `mavis` / `Mavis` so the
`notes` feed shows who did it.
- Do every line on that PO that is on the invoice.
- **Invoice shows less than ordered** → do NOT mark it. Report it
(the driver must collect the short quantity on the item card).
- **Photo is for a different PO** → do NOT mark it. Report both PO
numbers so a person can move the photo to the right PO.
- **Photo is not an invoice** → do NOT mark it. Report it.
- **Can't read the photo / not sure** → do NOT mark it. Report it.
Never cancel, delete, confirm, or change prices. Only ever mark a line
collected, and nothing else.
## Report
A short list: what you marked collected (supplier, PO, item, qty,
invoice number) and what needs a person (and why). If nothing was
found, say "nothing new".
**Do not re-report what is already on the "Waiting on a person" list
below** — those lines stay in the waiting status until somebody moves
them, so every run would otherwise find them again and repeat the same
report 22 times a day. Add anything new to that list.
## Waiting on a person
- **NSS M24 × 160 hex bolt ×60, PO202610018MAS** (line
`890919xtp0i8zfp`, driver Joachim): the photo on it is NSS invoice
5031768 for **PO202610080MAS** (M12 cap screws, already collected and
confirmed on 27 Oct). Wrong photo, so nothing proves the M24 bolts
were collected. Ask Joachim.
- **Mega Lube oil aerosol ×3, PO202610073MAS** (line `ganf24h10lyrzdl`,
driver Joachim): no Mega Lube invoice anywhere — the line has no
attachment at all, so the filter will not find it. Moved to 27 Oct.
Ask Joachim.
- **UPL UNBRAKO 1 1/2 × 7 allen caps ×34, PO202609005CEM** (line
`47it7cxyu27dmd5`): photo is on the line, but an operator bounced it
back to "NOT READY" on 26 Oct, so it is not a forgotten Collect.
Do not mark it.
- **NSS M6 × 30 CSK ×100, PO26100606STK** (stock line
`o9jtrr0wasxxq2g`): the photo on the PO is a picture of a laptop
keyboard, not an invoice. Needs a person to re-shoot it.