How to find slow-moving inventory by location in Power BI
Most inventory aging reports tell you which items are slow. Few tell you where those items sit. This guide explains how to measure slow-moving and dead stock in Power BI, build the DAX behind it, and map the result onto your warehouse so you can see which aisles, racks, and levels it is occupying.
Slow-moving inventory is a space problem
A typical slow-moving inventory report is a list of SKUs sorted by days since their last sale, with the oldest at the top. It is useful to a buyer deciding what to discount or return to a vendor. It is much less useful to the person running the building, because it says nothing about where that stock is.
For warehouse teams, location is the point. A pallet of slow stock in a high-bay reserve location costs very little. The same pallet in a waist-height pick face near the dock blocks a location your fastest movers should be using, and every picker who walks past it pays for that. Ten slow SKUs spread across the building are a housekeeping task; ten slow SKUs filling one aisle are a slotting decision.
Power BI already holds the data needed to answer both questions. What it needs is the right measures and a way to show them on the floor.
Slow-moving, aging, and dead stock
These terms get used interchangeably, but they measure different things, and the choice changes what you see on the map.
| Measure | What it tells you | Best for |
|---|---|---|
| Days since last movement | How long the stock in a location has sat without a pick or a receipt. | Finding dead stock and locations that have gone quiet. |
| Days of supply | How long the stock on hand will last at the recent pick rate. | Spotting overstock: stock that moves, but too slowly for the quantity held. |
| Inventory age | How long ago the stock in the location was received. | Shelf life, lot control, and FIFO compliance. |
Slow-moving inventory usually means stock whose days of supply is far beyond what you plan to hold. Dead stock is stock with no movement at all for a long period, often six months or a year. Aging inventory is about the age of the stock itself. Days since last movement is the most practical starting point for a location view, because it catches both dead stock and the quiet locations around it, and needs only pick and receipt history.
The data you need
Four tables cover it. Most warehouse management systems (WMS) can export each of them, and many teams already load them into Power BI for other reports.
| Table | Grain | Key columns |
|---|---|---|
| Locations | One row per storage location | Location ID, zone, aisle, level, and X, Y, Z coordinates |
| Inventory | On hand by location and SKU, as of the latest snapshot | Location ID, SKU, quantity |
| Picks | One row per pick or pick line | Location ID, SKU, pick date, units |
| Receipts | One row per putaway into a location | Location ID, SKU, receipt date, units |
Relate Inventory, Picks, and Receipts to Locations on Location ID, and Picks and Receipts to a Date table. Keeping Locations as its own table matters: it is what lets empty locations appear on the map instead of disappearing from it. If you don't have coordinates for your locations yet, the warehouse heat map guide explains how to get them from a CAD drawing.
One caution: a location can hold more than one SKU. The measures below work per location, so a location counts as slow only when nothing in it has moved. If you need the SKU-level view as well, put SKU in the tooltip or a table beside the map.
DAX measures for slow-moving stock
Start with the stock on hand and the last time anything moved in or out of the location. Removing the date filter lets the history reach back past whatever period the report is showing.
On Hand Units = SUM ( Inventory[Quantity] )
Last Movement Date =
VAR LastPick =
CALCULATE ( MAX ( Picks[Pick Date] ), REMOVEFILTERS ( 'Date' ) )
VAR LastReceipt =
CALCULATE ( MAX ( Receipts[Receipt Date] ), REMOVEFILTERS ( 'Date' ) )
RETURN
MAX ( LastPick, LastReceipt )
Days Since Last Movement =
VAR LastMove = [Last Movement Date]
RETURN
IF (
[On Hand Units] > 0 && NOT ISBLANK ( LastMove ),
DATEDIFF ( LastMove, TODAY (), DAY )
)
Empty locations return blank rather than zero, which keeps them out of the color scale. For days of supply, divide what is on hand by the recent pick rate. Ninety days smooths out a busy week without hiding a seasonal change.
Avg Daily Units Picked (90 days) =
DIVIDE (
CALCULATE (
SUM ( Picks[Units] ),
DATESINPERIOD ( 'Date'[Date], TODAY (), -90, DAY )
),
90
)
Days of Supply =
DIVIDE ( [On Hand Units], [Avg Daily Units Picked (90 days)] )
A location with stock but no picks in 90 days returns blank days of supply, because there is no rate to divide by. That is dead stock, and days since last movement already catches it. Finally, a score that turns the aging into bands is useful for ranking:
Slow-Moving Score =
VAR Days = [Days Since Last Movement]
RETURN
SWITCH (
TRUE (),
Days >= 365, 3,
Days >= 180, 2,
Days >= 90, 1
)
The 90, 180, and 365 day bands are common starting points, not rules. A spare parts warehouse may consider a year normal; a food distributor may treat 30 days as old. Agree on the bands with the people who will act on the report, and if they differ by zone or product family, calculate the thresholds in DAX instead of hard-coding them.
Put slow-moving stock on the warehouse map
Power BI has no built-in visual that draws a warehouse, so this step uses Unclocked 3D Heatmap, a certified custom visual that places every location at its coordinates and colors it by a measure. The free plan covers up to 2,500 locations.
- Add Location ID and the X and Y coordinates from the Locations table, and Z if your racks have levels. No coordinates yet? The visual's layout builder lets you draw racks over a floor plan instead.
- Put Days Since Last Movement in Heat Value. Locations that have sat longest stand out at the hot end of the scale.
- Add On Hand Units, Last Movement Date, and Days of Supply to Tooltip measures, so hovering a location explains its color.
- In Heat scale, set the thresholds to your bands. To keep them in one place, set Threshold source to Measures and bind DAX measures to the Low, Medium, High, and Critical thresholds, for example 30, 90, 180, and 365 days.
- Turn on the exception finder and rank by Slow-Moving Score. Select a result and use Focus in 3D to frame it.
- Add zone or product family as a category filter, and use the cutaway to look at one rack level at a time. Ground-level pick faces are usually where slow stock costs the most.
With a Professional license, add the snapshot date to the Time field and play the map back month by month. Watching a zone cool down over a quarter often explains it faster than any table: a promotion ended, a customer left, or a product line was replaced and nobody cleared its locations.
Reading the patterns
- Hot spots near the dock or in prime pick faces. The most expensive kind of slow stock. These are the first candidates for moving to reserve storage, which frees space for fast movers.
- A whole aisle or bay running hot. Usually one cause: a discontinued product line, a lost customer, or a seasonal range out of season. Treat it as one decision, not dozens of separate SKUs.
- Single hot locations scattered everywhere. Remnants and partial pallets. Consolidating them is often the quickest way to recover locations.
- Upper levels running hot. Often by design: reserve stock is supposed to sit. Compare against days of supply before acting on it.
- Hot locations with very little on hand. A few units that never move still occupy a whole location. Check the tooltip; these are cheap to clear.
Turning the map into action
The map is most useful as part of a routine. A weekly or monthly review works well: open the report on the slow-moving view, work through the exception list from the highest score down, and decide for each cluster whether to consolidate, re-slot, return to vendor, discount, or write off. Then review the reorder points that created the overstock, so the same locations don't fill up again.
Re-slotting deserves its own pass. Once slow stock leaves the prime locations, switch the heat value to picks over the last four weeks and look for fast movers sitting in poor locations. Those are the candidates to move into the space you just freed.
Before you share it
- The inventory snapshot and the pick history cover the same locations and use the same Location IDs.
- Pick history goes back further than your longest band, or old stock will look newer than it is.
- Empty locations show as empty, not as zero days.
- Bands are agreed with the team that will act on them, and noted on the report page.
- The report opens on one zone or level, rather than every location at once.
See where your slow stock sits
Unclocked 3D Heatmap is free on Microsoft Marketplace for up to 2,500 locations. Download the sample report to try it with stock-age data before connecting your own. If your layout only exists in AutoCAD, we can map it for you.