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.

MeasureWhat it tells youBest for
Days since last movementHow 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 supplyHow 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 ageHow 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.

TableGrainKey columns
LocationsOne row per storage locationLocation ID, zone, aisle, level, and X, Y, Z coordinates
InventoryOn hand by location and SKU, as of the latest snapshotLocation ID, SKU, quantity
PicksOne row per pick or pick lineLocation ID, SKU, pick date, units
ReceiptsOne row per putaway into a locationLocation 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.

  1. 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.
  2. Put Days Since Last Movement in Heat Value. Locations that have sat longest stand out at the hot end of the scale.
  3. Add On Hand Units, Last Movement Date, and Days of Supply to Tooltip measures, so hovering a location explains its color.
  4. 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.
  5. Turn on the exception finder and rank by Slow-Moving Score. Select a result and use Focus in 3D to frame it.
  6. 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

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

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.