docs(dn): Inbox 3 matching screen design
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
ca7d6c94e3
commit
841947a161
1 changed files with 190 additions and 0 deletions
190
docs/superpowers/specs/2026-10-26-inbox-3-matching-design.md
Normal file
190
docs/superpowers/specs/2026-10-26-inbox-3-matching-design.md
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
# Inbox 3 — delivery-note matching screen
|
||||
|
||||
Date: 2026-10-26 · Status: draft for approval
|
||||
|
||||
## Why
|
||||
|
||||
We need to know, for every purchase order, exactly what was delivered and how much. Today's
|
||||
matching screen gets in the way:
|
||||
|
||||
- Two sources of suggestions disagree. The rule matcher (`match-po-item.ts`, the
|
||||
`product_matched` check) and the AI allocation preview (`reconciliation_preview`) are both
|
||||
shown, and they often contradict each other. On slip 0007075669 the AI matched all 7 lines
|
||||
while the rule check said 6 matched "no PO line".
|
||||
- The line picker (`ProductDecisions` in `document-detail-view.tsx`) lists every PO line
|
||||
with no quantities, and lets you pick a PO line but not say how many arrived, in what unit,
|
||||
or at what price.
|
||||
- Units that differ between slip and PO (roll vs m, roll vs kg, 5 kg bag) cannot be
|
||||
resolved on screen. The AI turned "2 rolls" into 60 m and flagged a huge excess.
|
||||
- A slip stays on the screen as one block until every line is sorted, even when most lines
|
||||
are plainly right.
|
||||
|
||||
## What it is
|
||||
|
||||
A new screen, **Inbox 3**, for the office to test on real delivery notes. The site keeps
|
||||
using the current inbox until we are happy. Then we switch site over.
|
||||
|
||||
The capture flow does not change: someone takes a photo of the slip in the app and it comes
|
||||
in. Inbox 3 changes what happens after that.
|
||||
|
||||
## How it behaves
|
||||
|
||||
1. **Auto-match first.** When a slip arrives, the system matches every line it is sure of.
|
||||
Each line that passes all checks goes straight to **Ready to confirm**. Only lines that
|
||||
still need help stay in Inbox 3.
|
||||
2. **Slip-first table, one numbered row per slip line** (1, 2, 3…). Columns:
|
||||
- **On the slip:** the description, plus the slip's own qty × price = amount.
|
||||
- **Goes to PO line:** a searchable dropdown containing only this PO's lines (type "bail"
|
||||
to narrow), plus "Not on this PO". It is pre-filled with the single best suggestion.
|
||||
- **Received:** the quantity, editable.
|
||||
- **Unit:** a text box with suggestions as you type (each, roll, m, m², m³, kg, 5 kg
|
||||
bag, ton, length, box, pack). It is never limited to a list. When the unit differs from
|
||||
the PO line's unit, a box appears: "1 roll = __ m on the PO". When it is filled in,
|
||||
quantity, value and "left on PO" recalculate.
|
||||
- **Price:** editable. It starts at the PO rate, with the slip's price shown beside it.
|
||||
When they differ, the line is flagged and the user picks one or types their own.
|
||||
- **Value:** received × price, ex VAT.
|
||||
- **Left on PO:** ordered − (earlier receipts + this line), in PO units.
|
||||
3. **Money totals** at the bottom: the value of the remaining lines, plus the whole slip ex
|
||||
VAT against what the PO would charge for the same items.
|
||||
4. **Slip and PO pictures on the right**: slip above, PO below, with the current line
|
||||
highlighted where possible. On narrow screens, a switch between Both / Slip / PO.
|
||||
5. **The assistant** is a text box beside the pictures. You talk to it by line number ("fix
|
||||
line 5", "lines 3 and 4 are one item, 60 m total, per metre"). It can change anything on a
|
||||
line: quantity, unit, unit conversion, price, which PO line, and combining several slip
|
||||
lines into one PO line. It can only choose PO lines that are on this PO. It never posts.
|
||||
The furthest it goes is Ready to confirm.
|
||||
6. **Finished lines leave the screen.** When a line is complete (saved by the user, or fixed
|
||||
by the assistant and passing checks), it moves to Ready to confirm. A banner says how many
|
||||
lines have gone ("5 lines sent to Ready to confirm · show").
|
||||
7. **Ready to confirm** groups the ready lines by slip. **Both site and office can confirm**
|
||||
(David, 2026-10-26):
|
||||
- In Inbox 3, admin and office users press Confirm.
|
||||
- The same ready lines also appear in site's normal Ready to post, where the site manager
|
||||
presses Confirm delivery as today.
|
||||
|
||||
Either one posts the ready lines of that slip, never the unresolved ones. Whoever confirms
|
||||
first wins; the other sees the lines as posted. A slip is complete when all its lines are
|
||||
posted.
|
||||
|
||||
This means the existing inbox's Ready to post and Confirm delivery must handle a slip where
|
||||
only some lines are ready. They list and post just the ready lines, instead of requiring
|
||||
the whole document to be ready (`deliveryReady` / `prepareDelivery` today). This is the
|
||||
second deliberate change to the existing inbox.
|
||||
|
||||
## Goals
|
||||
|
||||
- One suggestion per line, from one source, with quantity, unit, price and value visible.
|
||||
- Any slip line can be fixed in place, by hand or by asking the assistant.
|
||||
- Lines move to Ready to confirm one at a time, as soon as they are right.
|
||||
- Slip-vs-PO unit and price differences are resolved explicitly and remembered.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Redesigning the site's current inbox screen or the photo capture flow. Only two safety and
|
||||
readiness changes reach it: the supplier guard, and posting only the ready lines.
|
||||
- Agents (the assistant, Foreman, MCP tools) posting. Only a person presses Confirm: site
|
||||
manager, admin or office user.
|
||||
- New OCR or page-grouping work.
|
||||
|
||||
## Anti-goals (a failure even if it "works")
|
||||
|
||||
- The assistant links a line to anything that is not a line on this PO, or invents an item.
|
||||
- A price or unit conversion is applied silently. Every change from the PO rate or PO unit
|
||||
carries a reason and shows on the line.
|
||||
- A line disappears from Inbox 3 without arriving in Ready to confirm.
|
||||
- A slip is linked to a PO from a different supplier without being stopped. This happened
|
||||
live: Chamberlain slips sat on Avidan and Raumix POs.
|
||||
|
||||
## Decided by David
|
||||
|
||||
- Slip-first layout (mockup A, then v2).
|
||||
- Numbered lines; quantity, unit and price all editable; a money value on every line.
|
||||
- Slip and PO pictures on the same screen.
|
||||
- A searchable PO-line dropdown limited to this PO.
|
||||
- An assistant with full edit rights on the lines, stopping at Ready to confirm.
|
||||
- Completed lines move to Ready to confirm individually; only unresolved lines stay.
|
||||
- Built as a separate Inbox 3, tested by us first, then rolled out to site.
|
||||
- During the test, both site (from the normal Ready to post) and office (from Inbox 3) can
|
||||
confirm ready lines.
|
||||
- The price on each line is editable.
|
||||
|
||||
## Claude's calls (check these)
|
||||
|
||||
- **Who sees Inbox 3 during testing:** admin and office_user only, not site_user. This
|
||||
needs a new role check, because financial pages currently let site managers in
|
||||
(`access.ts`, `path-access.ts`).
|
||||
- **Price default:** the PO rate. The slip price is shown alongside, and any difference
|
||||
above R0.50 per unit or 1% flags the line.
|
||||
- **The unit box** is free text plus suggestions. A "1 X = n PO-units" factor is required
|
||||
when the unit differs from the PO's. The factor is remembered per supplier + product
|
||||
(extends `remembered-match.ts`, which today refuses unit overrides).
|
||||
- **Suggestions come from one source:** the AI allocation. The rule matcher stays as a
|
||||
guard. If it finds a dimension conflict (e.g. 600 vs 1050 pipe), the AI suggestion is
|
||||
shown as "check", not ready.
|
||||
- **Line numbers** in the UI and assistant are 1-based and map to `lineIndex` (0-based).
|
||||
- **Supplier guard:** a slip whose supplier does not match the PO's supplier is blocked with
|
||||
a clear message, in Inbox 3 and the existing inbox. This is the one deliberate change to the
|
||||
existing inbox: it is a safety check, not a screen change. Supplier aliases and merged
|
||||
suppliers count as a match. The user can override from the line with a reason. The guard
|
||||
applies whenever a note is re-checked, so existing wrong links show up too.
|
||||
- **Auto-match ("sure of")** puts a line straight into Ready to confirm only when all of
|
||||
these hold:
|
||||
- the AI allocation and the rule guard agree on the PO line;
|
||||
- `prepareLineBooking` passes;
|
||||
- the unit equals the PO unit, or is converted by a remembered factor (the factor is shown
|
||||
on the line);
|
||||
- the price is within tolerance.
|
||||
|
||||
It runs where the AI preview runs today, on arrival. Queued notes are re-run once when
|
||||
Inbox 3 ships.
|
||||
- **Partial slips:** these use the existing per-line posting. The first posted line creates
|
||||
the delivery note, the slip completes when all its lines are posted, and unposted lines
|
||||
remain visible in both inboxes. Editing or reversing a posted line stays as today (correct
|
||||
receipt). No new document state is added.
|
||||
- **Memory:** only unit factors are remembered. Price differences are not; each one is
|
||||
decided on its line.
|
||||
- **Combined lines** are one row labelled with all their numbers ("3+4"). They are ready and
|
||||
posted together, as one `kit`/`converted` allocation. Quantity and price are entered once,
|
||||
in PO units.
|
||||
- **Price tolerance:** a line is flagged if it differs from the PO rate by more than R0.50
|
||||
per PO unit OR more than 1%, compared after unit conversion. Every change away from the PO
|
||||
rate or PO unit needs a free-text reason. The assistant writes its own reason.
|
||||
- **Assistant safety:** its edits go through the same validation as manual edits
|
||||
(`prepareLineBooking`). Chat and changes are logged per note in `dn_review_events`, along
|
||||
with who asked.
|
||||
|
||||
## Built on what exists
|
||||
|
||||
- The list model and tabs from `src/lib/inbox-two/model.ts` and `inbox-two-view.tsx`.
|
||||
- The page viewer (`PageViewer`) and the two-column workspace from
|
||||
`inbox-document-workspace.tsx`.
|
||||
- The assistant: route `api/dn-batches/[id]/notes/[noteId]/assistant`, `review.ts`,
|
||||
`review-schema.ts` and the MiniMax model. The schema already supports qty/unit `edits`,
|
||||
`purchaseOrderItemId` and `kit` combining. It needs to add **price** (`netAmount` / unit
|
||||
price, today forced to `usePoRate`) and a **unit factor** on converted lines. The server
|
||||
must reject any `purchaseOrderItemId` that is not on the note's PO.
|
||||
- Per-line validation and posting: `prepareLineBooking` (`line-booking.ts`) and
|
||||
`postLineWithinLease` (`post-line.ts`). These already support per-line posting,
|
||||
`unitOverride`, `netAmount` and `usePoRate`.
|
||||
- Readiness: `deliveryReady` is whole-document today. Inbox 3 needs a **per-line ready
|
||||
state**: a line that passes `prepareLineBooking` is "ready" even while other lines on the
|
||||
same slip are not.
|
||||
|
||||
## Left to the builder
|
||||
|
||||
- How per-line ready state is stored (on the allocation or in a separate field), and how
|
||||
the Ready to confirm view reads it.
|
||||
- Highlighting the current line on the slip and PO images (best effort; there are no page
|
||||
coordinates today).
|
||||
- Whether Inbox 3 is a new route (`/dashboard/financial/delivery-notes/inbox-3`) or a mode
|
||||
of the inbox-2 document page. A new route is preferred so site never sees it by accident.
|
||||
- Exact styling, using the existing shadcn components.
|
||||
|
||||
## Success
|
||||
|
||||
- The 14 notes in today's queue can each be cleared in Inbox 3 without the old screen or
|
||||
agent tools.
|
||||
- No line can reach Ready to confirm with a PO line from another PO, an unexplained unit
|
||||
change, or an unexplained price.
|
||||
- After a week of office use, David decides whether to switch site over.
|
||||
Loading…
Reference in a new issue