Age of Inventory
Age of Inventory lists every in-stock product with a "days old" figure and buckets the whole catalog into 0-30, 31-90, 91-180 and 180+ day age bands, so you can spot dead stock at a glance before deciding what to send to Markdown Rules or clearance.
Overview
The page loads up to 1,000 products from the generic product catalog and keeps only active products (isActive !== false) that currently have stock greater than zero — items with no stock left have nothing to mark down, so they are excluded entirely.
Age ("days old") is computed client-side from each product's createdAt timestamp, using the exact same definition as the Markdown Rules preview calculation on the backend — so the "days old" you see here should always agree with what a matching Markdown Rule preview would report for the same product.
Four clickable bucket tiles (0–30 days, 31–90 days, 91–180 days, 180+ days) show a live count for each age band; clicking one filters the table down to just that band, click again to clear it.
A running "Estimated dead-stock cost value" figure (cost price × stock quantity, summed) is shown above the table and recalculates against whatever the current search/season/bucket filters have narrowed the list down to.
Before You Start
- Requires the "inventory" plan feature to be enabled.
- Only products with stock greater than zero are shown — a product that has sold out entirely will not appear here even if its record is very old.
- The Season filter dropdown only appears at all if at least one in-stock product currently has a Season value set.
Step-by-Step Guide
1 Spot dead stock via the age buckets
- Look at the four bucket tiles at the top — 0–30 days (green), 31–90 days (amber), 91–180 days (orange) and 180+ days (red) — each shows a live count of in-stock products in that age range.
- Click a tile to filter the table to just that band; click the same tile again to clear the filter.
- Treat the 180+ days tile as your priority clearance list — those items have been sitting the longest.
2 Search, filter and sort the list
- Use the search box to filter by product name or SKU.
- Use the Season dropdown (if shown) to narrow to a single season, e.g. "SS25".
- Use the sort dropdown — Oldest first, Newest first, Most stock, or Name A-Z.
- Click Clear filters to reset search, season and bucket selections at once.
3 Use the age figures to decide on a markdown
- Note the SKUs sitting in the 91–180 or 180+ day buckets with meaningful stock quantity and cost value.
- Go to Markdown Rules and create or run a rule scoped to the same Season/Category to discount them, using Preview first to confirm the same products show up.
Every Field & Button, Explained
| Field / Button | What it does |
|---|---|
Age bucket tiles | 0-30 days, 31-90 days, 91-180 days, 180+ days — each clickable to filter the table to that band; counts are computed across every in-stock product regardless of other active filters. |
Search box | Filters by product name or SKU (case-insensitive substring match). |
Season dropdown | Filters to a single Season value; only rendered when at least one in-stock product has a Season set. |
Sort dropdown | Oldest first, Newest first, Most stock, or Name A-Z. |
Clear filters | Resets search, season and bucket selection together; only shown once at least one filter is active. |
Estimated dead-stock cost value | Sum of cost price × stock quantity across the currently filtered/sorted list of products (not the whole catalog) — changes as you apply search/season/bucket filters. |
Table — Product / SKU / Brand / Season | Basic identifying columns; Season is shown as a badge, "—" if not set. |
Table — Stock | Current stock quantity on hand. |
Table — Cost/unit, Value | Cost price per unit, and Cost/unit × Stock as that row's contribution to the dead-stock value figure. |
Table — Age | Days since the product record was created, colour-coded to match its age bucket. |
Tips & Best Practices
- Check this page before configuring a new Markdown Rule — it tells you exactly which products and how much stock value you would actually be discounting.
- Sort by "Most stock" within the 91–180/180+ buckets to prioritise clearing the SKUs tying up the most capital first.
- Remember the age here is the product record's age, not a restock date — if you frequently restock the same SKU rather than creating fresh product records each season, this figure will not reflect the physical stock's real age.
Troubleshooting & FAQ
A product I know is old shows a low "days old" number.
createdAt) in the system, not from when it was last restocked or put on the floor — if the SKU's record was edited/recreated recently, or if only the stock quantity was topped up, the age will look newer than the physical garment actually is.A product I know is sitting unsold is missing from the list entirely.
There is no Season dropdown on my screen.
The dead-stock value total changed after I searched or filtered.
🧑💻 Developer Notes
Source component(s):
frontend-app/pos-app-garment/src/pages/garment/AgeOfInventoryPage.jsx
Backend endpoints used:
GET /products (limit=1000) — the generic product-listing endpoint; there is no dedicated garment route for this page
Related tables (db-core repositories):
Product
Redux slices:
None — local component state only
Feature flag key: inventory
No dedicated garment.routes.js endpoint backs this page — it simply calls the generic GET /products endpoint with a high limit and computes buckets, sorting, filtering and the dead-stock value figure entirely client-side (see daysOld() and getBucket() at the top of AgeOfInventoryPage.jsx). The daysOld() calculation is textually identical in spirit to the one in packages/pos-core/src/garment.js's previewMarkdownRule, so the two pages should always agree on a given product's age. This client-only implementation also means large catalogs (beyond the 1000-row limit param) will silently truncate rather than paginate.