Developer ToolsFree generatorPDF, Excel, Word exportInfor SyteLineAll Infor SyteLine (CloudSuite Industrial) tools

SyteLine Inventory Aging Query Generator

This generator builds an inventory aging query for SyteLine 9.x and CSI 10.x: quantity on hand by item, warehouse and location from itemloc_mst, aged by the last receipt date in matltran_mst and grouped into day buckets you choose. A summary block and a lot-based option are included.

Fill in

Example value only. Confirm receipt codes in your data with the decode query in the material transaction generator.

Generated code
Download:

1. Aged on-hand by item, warehouse and location

DECLARE @Site SiteType     = N'MAIN';
DECLARE @Whse NVARCHAR(10) = N'MAIN';
DECLARE @Item NVARCHAR(60) = N'%';
DECLARE @B1 INT = 30, @B2 INT = 60, @B3 INT = 90, @B4 INT = 180, @B5 INT = 365;
DECLARE @IncludeZeroCost BIT = 1;

IF OBJECT_ID('tempdb..#aged') IS NOT NULL DROP TABLE #aged;

WITH last_rcpt AS (
  SELECT m.item, m.whse, m.loc, MAX(m.trans_date) AS last_rcpt_date
  FROM matltran_mst AS m
  WHERE m.site_ref = @Site
    AND m.trans_type IN ('R')
  GROUP BY m.item, m.whse, m.loc
)
SELECT il.item, il.whse, il.loc, il.qty_on_hand,
       lr.last_rcpt_date,
       DATEDIFF(day, lr.last_rcpt_date, GETDATE()) AS age_days,
       ISNULL(i.unit_cost, 0) AS unit_cost,
       il.qty_on_hand * ISNULL(i.unit_cost, 0) AS ext_value,
       CASE WHEN lr.last_rcpt_date IS NULL THEN 7
            WHEN DATEDIFF(day, lr.last_rcpt_date, GETDATE()) <= @B1 THEN 1
            WHEN DATEDIFF(day, lr.last_rcpt_date, GETDATE()) <= @B2 THEN 2
            WHEN DATEDIFF(day, lr.last_rcpt_date, GETDATE()) <= @B3 THEN 3
            WHEN DATEDIFF(day, lr.last_rcpt_date, GETDATE()) <= @B4 THEN 4
            WHEN DATEDIFF(day, lr.last_rcpt_date, GETDATE()) <= @B5 THEN 5
            ELSE 6 END AS bucket_no
INTO #aged
FROM itemloc_mst AS il
JOIN item_mst AS i
  ON i.site_ref = il.site_ref AND i.item = il.item
LEFT JOIN last_rcpt AS lr
  ON lr.item = il.item AND lr.whse = il.whse AND lr.loc = il.loc
WHERE il.site_ref = @Site
  AND (@Whse = N'' OR il.whse = @Whse)
  AND il.item LIKE @Item
  AND il.qty_on_hand <> 0
  AND (@IncludeZeroCost = 1 OR ISNULL(i.unit_cost, 0) <> 0);

SELECT a.*,
       CASE a.bucket_no
         WHEN 1 THEN CONCAT('0-', @B1)
         WHEN 2 THEN CONCAT(@B1 + 1, '-', @B2)
         WHEN 3 THEN CONCAT(@B2 + 1, '-', @B3)
         WHEN 4 THEN CONCAT(@B3 + 1, '-', @B4)
         WHEN 5 THEN CONCAT(@B4 + 1, '-', @B5)
         WHEN 6 THEN CONCAT('Over ', @B5)
         ELSE 'No receipt found' END AS age_bucket
FROM #aged AS a
ORDER BY a.bucket_no DESC, a.ext_value DESC;

2. Summary by bucket (same session, after block 1)

SELECT a.bucket_no,
       COUNT(*)           AS location_rows,
       SUM(a.qty_on_hand) AS qty_on_hand,
       SUM(a.ext_value)   AS ext_value,
       CAST(100.0 * SUM(a.ext_value) / NULLIF((SELECT SUM(ext_value) FROM #aged), 0) AS DECIMAL(6,2)) AS pct_of_value
FROM #aged AS a
GROUP BY a.bucket_no
ORDER BY a.bucket_no;
-- bucket_no 7 = no receipt found in matltran for that item, warehouse and location.

3. Option: lot-tracked items aged by lot creation date

DECLARE @Site SiteType     = N'MAIN';
DECLARE @Whse NVARCHAR(10) = N'MAIN';
DECLARE @Item NVARCHAR(60) = N'%';

-- CreateDate is the record audit column, used here as a proxy for lot age.
-- If your lot_mst holds a receipt or manufacture date you trust, age by that column instead.
SELECT ll.item, ll.whse, ll.loc, ll.lot, ll.qty_on_hand,
       l.CreateDate AS lot_created,
       DATEDIFF(day, l.CreateDate, GETDATE()) AS age_days
FROM lot_loc_mst AS ll
JOIN lot_mst AS l
  ON l.site_ref = ll.site_ref AND l.item = ll.item AND l.lot = ll.lot
WHERE ll.site_ref = @Site
  AND (@Whse = N'' OR ll.whse = @Whse)
  AND ll.item LIKE @Item
  AND ll.qty_on_hand <> 0
ORDER BY age_days DESC;

Confirm on your SyteLine 9/10 database: itemloc_mst.qty_on_hand, the item cost column you value at (unit_cost on item_mst by default), the lot_loc_mst and lot_mst columns, and the receipt trans_type codes. Values use item unit cost, not costed layers, so this is an operational aging view, not an accounting valuation. Run on a reporting copy where possible.

Aging report reconciliation

Send your bucket summary and the standard valuation total; we will help explain the difference and tune the receipt codes.

Optional. The tool and every export work without this. No spam.

Was this tool useful?

How to use it

Set the site, warehouse and item filter, then the five bucket edges, for example 30, 60, 90, 180 and 365 days. Enter the receipt transaction codes you have confirmed in your data, and the item cost column to value at. Run block 1 in SSMS: it stores the aged rows in a temporary table and lists them oldest first. Run block 2 in the same window to get quantity, value and percent of value per bucket. Use block 3 for lot-tracked items when lot creation is a better age signal than the last receipt to a location.

  • Bucket edges must increase from 1 to 5.
  • Rows with no receipt found fall into bucket 7; investigate them separately.
  • Exclude zero-cost items when the report is for finance.

The method and its limits

Age here is days since the most recent receipt into that item, warehouse and location. That is a proxy, not true FIFO layer age: if 100 units were received a year ago and 5 last week, the whole 105 shows as one week old. Moves between locations post new transactions, so stock that moved recently can look young unless you age by item and warehouse only (drop loc from the CTE and join). Value is quantity on hand times the item unit cost, which matches neither actual nor FIFO cost exactly. For a defensible accounting number, use the costing reports.

Worked example

A site has 2,400 location rows with stock. Block 2 shows 61 percent of value received in the last 90 days, 14 percent between 91 and 180, 9 percent between 181 and 365, 11 percent over a year and 5 percent with no receipt found. The over-a-year bucket lists PN-30117 with 800 units at 12.50, worth 10,000. The no-receipt bucket usually holds stock loaded at go-live, items received under codes missing from the receipt list, or lot moves; fix the code list before trusting the report.

Gotchas

Missing receipt codes make stock look older than it is, so check the code list first. Multi-site databases need the site_ref filter on every join, which the query applies through the item and location joins. Negative on-hand rows appear because the filter is non-zero; review them, they usually point to backflush or timing issues. If the item cost column you value at is zero for purchased items, the cost rollup may not have run; the zero-cost switch lets you see them or exclude them.

Frequently Asked Questions

Does SyteLine have a standard inventory aging report?

SyteLine includes inventory and valuation reports, but sites often need aging by location with their own buckets and an Excel-friendly output. A query like this gives that control. Compare its totals with the standard valuation report for the same date before you circulate it.

Why age by last receipt instead of FIFO layers?

Last receipt is simple, fast and available for every costing method. FIFO layer age requires the cost layer tables and costing method in use, which differ by setup. Last receipt understates age when small receipts top up old stock, so treat the result as an operational signal.

How do I export the aging report to Excel?

Run block 1 or 2 in SSMS and copy with headers, or save the results as CSV. For a refreshable workbook, put the logic in a view or procedure on a reporting copy and connect Excel to it through Get Data from SQL Server with a read-only login.

Why do some rows show no receipt found?

The location has stock but no matching receipt transaction: opening balances loaded at go-live, receipts under codes not in your list, stock moved in from another location, or history purged. Add missing codes, or age those rows by item and warehouse or by lot instead.

If finance and planning want to ask SyteLine which stock is ageing in plain English, we can set up a private, read-only assistant over these tables on your own servers.