Documentation
How a price change is detected
The four-part key a purchase price must match before a change is calculated, the window behind weekly impact, and when a figure is withheld instead of estimated.
Last reviewed against the product on .
The price changes surface at /app/price-changes reads your own imported invoices, not the market. This page sets out the one comparison it makes, where the numbers in it come from, and the situations in which it refuses to produce a figure at all.
What is compared with what
A price change is the difference between the two most recent unit prices in a group, and a group is narrow on purpose.
| Part of the key | How it is derived | If it is missing |
|---|---|---|
| Supplier | The supplier record the row was imported against | The row cannot be stored at all |
| Item | Item name lowercased, with punctuation collapsed to single spaces | The row is rejected at import |
| Unit | Unit text trimmed and lowercased | Recorded as unspecified and never compared |
| Currency | Three letters, uppercased | Recorded as unspecified and never compared |
Measured from comparisonKey, normalizedUnit and normalizedCurrency in src/lib/purchasing/intelligence.ts.
Two prices from different suppliers are never compared here, however similar the item. That is a different question with different evidence behind it, and it belongs to what a saving is.
Where a unit price comes from
The importer prefers a unit price column when the file carries one and the value is not negative. Where it does not, a unit price is derived by dividing the line total by the quantity, and only when the quantity is greater than zero. Where neither route works, the product carries no unit price and the table prints "Unavailable" rather than a number.
One price is kept per purchase date, and the most recent 52 dates are retained. The change you see is between the last two of them, so a week with three deliveries at three prices contributes one price to the comparison, not three.
Weekly impact, and the window behind it
The default sort is absolute weekly financial impact, not percentage movement. A small rise on something you buy every day outranks a large rise on something you buy twice a year, which is the ordering a kitchen can act on.
- Usage window
- 56 days
- ending at your latest purchase date, never shorter than 7
- Price dates kept
- 52
- one unit price per distinct purchase date
- Prices compared
- 2
- the latest and the one before it
- Purchase rows read
- 10,000
- the most recent rows for your organization
Measured from buildPurchasingIntelligence in src/lib/purchasing/intelligence.ts and QUERY_LIMIT in src/lib/purchasing/queries.ts.
The formula is the price difference multiplied by the observed weekly quantity, where weekly quantity is the total quantity bought inside that window divided by the number of weeks in it. The seven-day floor matters more than it looks: if every purchase of an item sits on one day, the window is still treated as a week, so a single large delivery reads as a week of usage.
The anchor is your latest invoice, not today
Every window on the page is measured backwards from the most recent purchase date in your organization, and the header prints that date. Stopping imports therefore does not make the page calm, it freezes it. A quiet page with an old anchor is a reporting gap, not a stable month.
Where a figure is withheld
The weekly increase total in the summary row is the sum of positive weekly impacts only, kept separately per currency. Two currencies are never added together, here or anywhere else on this site.
What a price change does not tell you
It does not tell you why. A supplier who shrinks the case while keeping the same unit text on the invoice produces a rise on this page that is really a pack change, and nothing in the imported data can tell the two apart. Checking the pack is the first thing to do with any row that surprises you.
It also does not tell you what you consumed. Weekly quantity is what you bought in the window, so a stock-up before a closure inflates it and a week of running down the walk-in deflates it.
Finally, two prices are a step and not a trend. The page retains a long history per product but compares only the last two dates in it, so a return to a normal price after a brief spike shows as a fall.
Where to look next
The import contract is what decides whether a row arrives with a usable unit and currency in the first place, and it is where most unavailable cells are actually resolved. For market movements rather than your own, see reading analytics.