Shopify inventory aging report: 30/60/90/180 buckets
Build a Shopify inventory aging report in four steps: bucket definitions, where receipt age and days-since-sale disagree, and a table you act on.
InventoryX is in development and not available today. ClearStock is the live Shopify app.
A Shopify inventory aging report groups every SKU into 30/60/90/180-day buckets by how long its current on-hand units have been sitting, valued at cost, so you can see which stock is quietly turning into a write-off. Shopify doesn't produce this report natively — it stores quantities, not vintage — so the report is a build: a product export, a receipt log, and a FIFO allocation in a spreadsheet. Done properly it takes about two hours the first time and fifteen minutes a month after that.
The part most guides skip is the definition. "Age" in an aging report means how long the units you still hold have been held, and that is not the same as "days since this SKU last sold." Confusing the two produces a report that flatters fast-restocked items and buries the real problem. This article covers the distinction first, then the build, then the buckets.
Receipt age vs days since last sale
Two numbers get called "age" in inventory reporting. They answer different questions and they disagree on exactly the SKUs you care about.
| Metric | What it measures | What it's good for | Where it misleads |
|---|---|---|---|
| Receipt age (FIFO) | How long the oldest units still on hand have been held | True capital exposure; the number your accountant means by "aged stock" | Needs a receipt log; manual to maintain |
| Days since last sale | Time since the last unit of that SKU shipped | Spotting dead demand fast; the metric most Shopify apps actually show | Resets to zero on any single sale, so a SKU selling one unit a month looks fresh forever |
| Weeks/days of cover | On hand ÷ recent velocity | How long until it's gone at the current pace | Says nothing about how old the existing stock is |
A concrete collision: a candle restocked monthly, 40 units selling 30/month. Days since last sale: 1. Receipt age: 45 days. Harmless. The same store's throw blanket: one sale 12 days ago, 180 units on hand, last received nine months ago. Days since last sale: 12 — it looks healthier than the candle. Receipt age: 270 days. That is the SKU that belongs on the report.
Use receipt age as the primary sort, days-since-last-sale as a staleness override (anything with no sale in 60 days goes in the oldest bucket it qualifies for, regardless of vintage), and velocity as the third column that decides the action. All three, or the report misleads.
The bucket table
Bucket boundaries are a judgment call, not a standard. The four below work for most general-merchandise stores; seasonal and perishable categories compress them, high-ticket furniture stretches them.
| Bucket | What it usually means | Share of inventory value you'd like to see | Action |
|---|---|---|---|
| 0–30 days | Bought, received, mostly already selling | 30–45% | None. Watch for fast movers stuck here with zero sales — a launch that died |
| 31–60 days | Normal mid-life for anything with a multi-month sell-through | 25–35% | Confirm velocity. If a SKU has zero sales here, it is already slow-moving |
| 61–90 days | Capital starting to drag | 15–25% | Non-price intervention: bundle, feature, rephotograph, reposition in collections |
| 91–180 days | Carrying cost now visible in the monthly numbers | 10–20% | Markdown ladder or exit decision. This is where recovery value is still meaningful |
| 180+ days | Vintage. Demand has likely changed under it | Under 10% | Liquidation, donation, or write-off. Stop trying to sell it at retail price |
Two calibration notes. The "share of value" column is a sanity band, not a target — a store that buys two seasons ahead will legitimately run heavy in 91–180. And if more than 10% of your value sits in 180+, the problem isn't the report; it's the buying rhythm, and the fix lives in stopping the reorders that create it.
Building it in four steps
Step 1 — the SKU file. Export products with handle, title, variant, SKU, on-hand quantity, and cost per item. Shopify's product CSV includes all of these; cost per item is the field people forget, and an empty cost column makes every dollar figure zero. Fix costs before you build.
Step 2 — the receipt log. The date a unit arrived lives in your purchase orders, transfers, and any stock received before you started using Shopify. Extract one row per receipt: SKU, receipt date, quantity received. Start the log now even with a handful of rows and accept that the first run under-reports age.
Step 3 — allocate the on-hand units FIFO. For each SKU, sort its receipt layers oldest first and walk down, assigning the current on-hand quantity to layers:
units remaining in layer = MAX(0, MIN(layer quantity, on hand − quantity received in all older layers))
Stop when you've assigned the full on hand. What's left of the oldest layers is your aged stock; the newest layers are effectively new stock. This is the step that separates a real aging report from a last-receipt-date approximation, and it takes one helper table plus a running total column.
Step 4 — bucket and value it. For each SKU's oldest remaining layer: days = TODAY() − receipt date, bucket = an IFS formula over 30/60/90/180, value = remaining units × cost. Sum by bucket for the store-level view; sort by value within bucket for the worklist.
The output is a five-row summary you can trend month over month, plus a ranked SKU list whose top ten rows are this month's decisions.
Worked example
A six-SKU slice from a home-goods store, both metrics side by side, run on November 10, 2026:
| SKU | On hand | Cost | Oldest layer | Receipt age | Days since last sale | Bucket | Value at cost |
|---|---|---|---|---|---|---|---|
| MUG-CEL-16 | 48 | $4.20 | Sep 2 | 69 | 3 | 61–90 | $201.60 |
| BLANKET-WOOL | 180 | $19.00 | Feb 18 | 265 | 12 | 180+ | $3,420.00 |
| TRAY-ACACIA | 64 | $11.50 | Jun 30 | 133 | 41 | 91–180 | $736.00 |
| VASE-RIB-SM | 22 | $7.80 | Oct 26 | 15 | 2 | 0–30 | $171.60 |
| COASTER-SET6 | 310 | $1.15 | Apr 4 | 220 | 0 (sold 2 today) | 180+ | $356.50 |
| RUNNER-TBL | 9 | $14.20 | Aug 12 | 90 | 9 | 61–90 | $127.80 |
Read the two SKUs that look healthy on one metric and not the other.
BLANKET-WOOL sold 12 days ago, which a days-since-last-sale report would call active. Receipt age says 265 days on 180 units at $19 cost — $3,420 of capital, by far the largest line in the slice, and 180 units at one sale per 12 days is a 2,160-day sell-through. This is the SKU that should consume the meeting.
COASTER-SET6 sold today and still belongs in the worst bucket: 310 units received in April, selling in dribs. One sale resets a recency metric and changes nothing about the vintage — the exact failure mode a recency-based app produces.
TRAY-ACACIA, 133 days old with 41 days since its last sale, is the real markdown candidate: old enough that carrying cost is visible, recent enough that demand hasn't died. MUG-CEL-16 and RUNNER-TBL are mid-life and fine; VASE-RIB-SM is new stock, included to show the report isn't only a problem list.
Keeping it honest
Three things degrade an aging report over time:
- Unlogged receipts. Every receipt missing from the log drags its SKU's apparent age younger. Make the log a step in receiving, not a monthly archaeology project.
- Adjusted quantities. Inventory adjustments for damage, shrinkage, and manual corrections carry no date, so they remove units from the middle of the stack invisibly. Track the adjustment total per SKU in a spare column.
- Returns re-entering stock. A returned unit re-enters at today's date on paper while being a year old physically. For high-return categories, add returns to the oldest layer, not the newest.
Run it monthly on the same date, and keep each month's summary row. The trend is more useful than any single snapshot: a store where the 180+ bucket shrinks from 14% to 8% of value over two quarters is executing; a store where it grows is accumulating.
When you're ready to act on the top of the list, the carrying-cost math that prices each month of delay is in dead stock on Shopify, and the report-by-report view of which native Shopify exports feed which column is in Shopify inventory reports.
Where a tool actually helps
The spreadsheet is the right starting point because it forces you to define the buckets and see the allocation. What it doesn't do is re-run itself the day after a receipt lands, which is why aging reports decay into last quarter's snapshot. ClearStock, the app we build, reads velocity, runway, and days since last sale from Shopify and turns the slow and dead portion of that catalog into a live worklist — it can apply rule-based discounts with a cap you set, logged and reversible. It doesn't invent receipt dates or replace the FIFO logic above; if your data can't support the spreadsheet, it can't support the app either.
Common questions
Does Shopify have an inventory aging report? No. Shopify stores on-hand quantities and dates its purchase orders and transfers, but core admin has no report that ages stock by receipt date. The standard method is a spreadsheet built from the product export, a receipt log, and a FIFO allocation — roughly two hours to build, fifteen minutes a month to maintain.
What is the difference between inventory age and days since last sale? Inventory age is how long the units you still hold have been held. Days since last sale resets to zero on a single unit, so a SKU selling one unit every two weeks looks active while holding nine months of vintage stock — which is why aging reports sort on receipt age and treat recency as an override.
What are good aging buckets? 30, 60, 90, and 180 days cover most general-merchandise stores: 0–30 fresh, 31–60 mid-life, 61–90 watch, 91–180 act, 180+ vintage. Compress the boundaries for seasonal, trend, or perishable goods and stretch them for high-ticket items with planned long sell-through — the structure matters more than the exact day counts.
How much of my inventory value should be over 180 days? Under 10% is a workable ceiling for most stores; above that the aged tail is large enough to distort your cash cycle. Treat it as a calibration band rather than a benchmark — a store that deliberately buys two seasons ahead will run heavier in the 91–180 bucket by design.
How often should I run the report? Monthly, on a fixed date, keeping each month's summary row so the trend is visible. Aging is a slow variable — weekly runs produce noise, quarterly runs let a whole bucket fill unnoticed.
Next step
Build the receipt log first, even if it's five rows, then run the FIFO allocation on your twenty highest-value SKUs. Those twenty usually hold the majority of your inventory value, so the first pass gives you most of the answer without touching your long tail — and anything landing in 91+ goes straight onto a markdown or exit list with a date attached.