The situation. An operations team ran receiving, picking and purchasing on a SQL Server–backed ERP. Operational questions — is receiving keeping up, are we paying what the purchase order said, who picked what yesterday — were answered by whoever had been there longest. The data existed. Nothing assembled it.
What was built. A reporting console with two audiences and two very different trust levels. Supervisors get the full set in a browser. The floor gets a subset on wall-mounted displays, driven by scoped display tokens.
That split turned out to be the design constraint that mattered. Several reports carry cost and margin. A screen on an operations floor is visible to everyone who walks past it, including visitors and contractors. So report definitions carry a flag: anything with dollars in it is backend-only, the server refuses to attach it to a display token, and it is never listed in the publicly enumerable set. The restriction lives in the report definition rather than in the page that renders it, because the page is the thing most likely to be rewritten later by someone who does not know the rule.
The receiving metrics. Volume was the easy part. The ones that changed conversations:
| Metric | What it measures, and the modelling decision behind it |
|---|---|
| Purchase price variance | Receipt cost minus purchase-order line cost. Positive means the business paid above what the PO said. This is the number that gets attention, because it is the gap between what purchasing agreed and what accounts payable will actually be billed. |
| On-time percentage | Receipt date against the due date, preferring promise date, then required, then original. Lines with no due date are excluded from the ratio rather than silently counted as on time — an undated line is missing data, not a success, and rolling it into the numerator flatters the number in exactly the cases nobody is watching. |
| In-full percentage | Only open receipts carry an in-full flag; the posted-history table has no equivalent. History rows are therefore excluded from the ratio via an explicit marker instead of scoring zero, so a full month does not read worse than a single day purely because most of it has been posted. |
| Lines per PO | Receiving effort per order. A cheap proxy for whether purchasing is consolidating or drip-feeding the dock. |
Two of those four rows are decisions about what not to count. That ratio is normal. Most of the work in an operational metric is deciding which rows are evidence and which are absence of evidence, and a dashboard that gets it wrong is worse than no dashboard, because it is confidently wrong in the direction nobody checks.
Open versus posted. The same period can span unposted receipts and posted history, which live in separate tables with different columns. Today reads from open receipts only. Week and month union both. Getting this wrong is the classic ERP reporting bug: numbers that look right on a daily view and quietly drift on anything longer.
Picker productivity. A parallel set covers lines picked, quantity, active hours and first and last pick per picker. The awkward part was not the aggregation, it was identity: pickers appear as initials on the transaction, and the mapping to a human runs through a chain of fallbacks — a description field, a Windows login, the local part of an email address, and finally an explicit unmapped bucket. Unmapped is displayed rather than hidden. An unattributed pick is a data-quality finding, and hiding it turns a fixable mapping gap into a permanently wrong leaderboard.
Worth measuring if you run this
- PPV dollars surfaced in the first full month, against what was being caught manually before.
- Time from “how did receiving do last week” to an answer, before and after.
- Share of picks landing in the unmapped bucket, tracked down over time as the mapping is cleaned.
Related
The report that worked on Today and timed out on Month · One label service instead of a label program per label