docs(collection-ai): runbook tick table, audit scope; spec content_hash index is non-unique

F2: tick table (SKU green via the code match incl. AI fills, grey per tick,
pairing failure -> all grey, different product -> red), stock lines use the
line SKU, Add to POS needs a SKU, SKU fills audited for order lines only.
F3: content_hash is a non-unique index (identical uploads share a hash,
skipped PDFs have an empty hash) - matches the migration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Dave 2026-10-17 10:21:25 +02:00
parent 8efee9db42
commit cba2d84549
2 changed files with 16 additions and 7 deletions

View file

@ -19,12 +19,16 @@ per line (SKU, quantity, description, price), plus a POS column and an
by the tick.
- Lines with no AI check look exactly as before, with no ticks.
- **Add to POS** is a manual feature (per line, or "Confirm and add to
POS" on the card). It creates a `stock_items` row with zero stock.
POS" on the card). It creates a `stock_items` row with zero stock. A line
needs a SKU first (the button is disabled without one); the toast reports
lines added, failed and still needing a SKU.
What the AI writes: only `ai_check` on the order/stock line, the document
reads, and an **empty SKU**, and only when it is sure. It never overwrites
a SKU a person set, never confirms a collection, and never changes status.
The SKU fill is audited as an ordinary `sku` change.
On order lines the SKU fill is audited as an ordinary `sku` change; stock
purchase lines (`stock_purchase_items`) are not covered by the audit hook,
so a SKU fill there is not in the audit log.
## Going live (manual steps, in this order)
@ -81,10 +85,13 @@ Collection checks, and stored in
| Colour | Meaning |
|---|---|
| Green | The document agrees with our line (SKU matches after normalising, quantity equals what was collected and what was ordered, AI is sure it is the same item, unit price within 1 cent) |
| Amber | Close but worth a look: SKU differs from the document code; collected quantity matches the document but the ordered quantity differs; the AI thinks it is the same item but is only "likely" sure |
| Red | Mismatch or missing: no SKU on our line, quantity differs from the document, price differs or we have no cost, or the AI could not pair the line (all four red) |
| Grey | Not applicable: the document shows no code or quantity, or no price (delivery notes never have a price tick) |
| Green | The document agrees with our line: our SKU equals the document code after normalising (uppercase, letters and digits only; this is also how a SKU the AI filled goes green), quantity equals what was collected and what was ordered, the AI is sure it is the same item, unit price within 1 cent |
| Amber | Close but worth a look: our SKU differs from the document code; collected quantity matches the document but the ordered quantity differs; the AI thinks it is the same item but is only "likely" sure |
| Red | Mismatch or missing: no SKU on our line, quantity differs from the document, price differs or we have no cost, or the line is not on the document or the AI says it is a different product (all four red) |
| Grey | Can't check, per tick: **SKU** when the document line shows no code; **Qty** when it shows no quantity; **Price** on a delivery note or when the document line shows no price; **all four** when the AI couldn't pair the document (see the AI findings box for the reason; **Check with AI** retries) |
For stock purchase lines the SKU tick and robot use the line's own SKU
(the one a rep or the AI set on the purchase line), not the catalogue SKU.
Hover a tick for the values compared (ours vs the document). A robot icon
marks a SKU the AI filled. Click the document chip on a card to see the

View file

@ -292,7 +292,9 @@ A human edit always wins, and a re-run never overwrites it.
- **New collection `collection_doc_reads`**, created by a
`pb:migrate:collection-ai` script:
- `source_collection`, `source_id`, `filename`
- `content_hash` (unique index)
- `content_hash` (non-unique index: identical uploads each get their
own row sharing the hash, so a photo is read once but every copy is
recorded; skipped PDFs have an empty hash)
- `status` (text): `read` | `failed` | `skipped`
- `doc_type`, `document_no`, `doc_date`, `supplier_name`
- `po_refs` (json), `lines` (json), `notes` (json)