Documentation
A product record, end to end
What the record id in the address really selects, what each headline metric prints when its input is missing, and why a drawn price history is not proof of a comparable price.
Last reviewed against the product on .
A product page is not a page about one invoice line. It is the page for one comparison group, and the purchase record id in the address is only the way in.
What the address actually names
The address ends in a purchase record id, but the page is assembled from every imported row that shares that row's supplier, item name, unit and currency. Any record id inside that group opens the identical page. The purchases table links by the row you clicked; the dashboard, the price changes table and the weekly report all link by the latest row in the group. Those are the same destination, not two views of it.
The grouping key is not this page's invention, and it is described in full in how a price change is detected. What matters here is the consequence: the header prints the supplier, the currency and the unit as an exact boundary, because everything below the header is true only inside that boundary.
A record id belonging to another organization does not resolve. Purchase rows are filtered to your organization before any grouping happens, so an id you are not entitled to read is not found rather than partially rendered.
The four headline metrics
| Metric | What it shows | Printed when the input is missing |
|---|---|---|
| Current unit price | The unit price on the most recent purchase date in the group | Unavailable |
| Previous unit price | The unit price on the purchase date before that one | No baseline |
| Price movement | Current minus previous, with a percentage badge beside it | No baseline |
| Weekly impact | The movement multiplied by your observed weekly quantity | Needs quantity |
Measured from the metric row in app/app/products/[recordId]/page.tsx and buildProduct in src/lib/purchasing/intelligence.ts.
None of those four is ever estimated. A missing input produces the words above and nothing else, which is the same rule the price changes table follows.
The percentage badge and the money figure can disagree
The badge beside the price movement is a percentage, and a percentage needs a previous price above zero to divide by. Where a previous price of exactly zero was imported, the movement still prints as money and the badge next to it reads "No baseline". The two are not contradicting each other; one of them simply cannot be formed.
Your purchasing, and the window behind it
- Usage window
- 0, or 7 to 56 days
- 0 when nothing falls in the 56-day lookback, otherwise your observed span, floored at 7
- Weekly quantity
- Window quantity / weeks
- only rows carrying a quantity above zero
- Weekly spend
- Window total / weeks
- every row in the window, quantity or not
- Annualized run rate
- Weekly spend x 52
- the window projected forward, not a forecast
- Purchase rows
- The whole group
- not only the rows inside the window
Measured from buildProduct and observedWindowDays in src/lib/purchasing/intelligence.ts.
Two of those are counted differently on purpose, and the difference shows up on this panel more often than anywhere else on the site. Weekly spend uses every row in the window. Weekly quantity uses only the rows that carried a readable quantity above zero. A file that imports totals cleanly but quantities poorly therefore produces a confident spend figure beside an unavailable quantity, and the weekly impact above goes with the quantity.
The row count is the size of the whole group rather than the window, so a product bought heavily two years ago and twice last month shows a large row count beside a small weekly figure. That is the correct reading of both, not a disagreement between them.
The window itself is anchored to your most recent purchase date anywhere in the organization, not to today. A product you have stopped buying eventually falls outside it entirely, at which point the window reports no length, the weekly quantity is unavailable and the weekly spend is nothing. The panel is then describing history rather than current usage, and the price metrics above it are unaffected.
The price history line
One price is kept per purchase date, and the most recent of those dates are plotted oldest to newest. With fewer than two dates there is no line at all, and the panel says so rather than drawing a single point and letting it imply a trend.
- Dates plotted
- 52 at most
- one unit price per distinct purchase date
- Dates needed
- 2
- below that the panel prints a message instead of a line
- Dates labelled
- 2
- the first and the last, at the ends of the axis
Measured from buildProduct in src/lib/purchasing/intelligence.ts and the PriceHistory component in app/app/products/[recordId]/page.tsx.
A drawn line is not proof of a comparable price
A group whose unit or currency is missing is not comparison eligible. Its current and previous unit prices are withheld, its movement and weekly impact are withheld, and the header prints the boundary as unspecified. The chart is still drawn, because the history is built from any row with a resolvable unit price, and money elsewhere on the page prints with a currency missing note rather than a symbol.
So a healthy looking line above a column of withheld metrics is the expected appearance of an import problem, not a display fault. The import contract is where that is fixed, and it is fixed at the file rather than on this page.
The comparable supplier panel
Where a cheaper supplier exists for the same item, unit and currency, a panel appears naming that supplier and a potential weekly figure, with a link through to the evidence. It appears only when this product is the dearer side of the pair. Open the cheaper product and there is no panel, because nothing is being proposed about it.
The panel is also subject to review. Once an administrator rejects a proposed match, it stops appearing here as well as on the savings surface, and the link that used to reach its evidence stops resolving. A savings record covers what that panel leads to and who decides it.
What this page does not claim
The imported history is your purchasing evidence, not a supplier contract feed, and the weekly figures are observed run rates ending at your most recent purchase date rather than at today. Pack sizes are never converted, so a case that quietly became smaller while keeping its unit text reads here as a price rise. Checking the pack is still the first thing to do with any row that surprises you.
Where to look next
How a price change is detected owns the grouping key and the weekly impact arithmetic that this page displays for a single product. What a saving claims sets out the comparison behind the supplier panel. For the same evidence assembled across every product at once, see reading the dashboard.