Inventory in Ax is quite important. Often developers need to find out what the stock levels are in code. It’s tempting to write a query directly on the InventSum table and be done with it. This is usually not a good idea. If your code needs to deal with different inventory dimensions or you need to aggregate results on some random dimension, things can get complicated.
The good news is that there in standard Ax there already is a class to help you with this: InventDimOnHand. Many people have heard about it but don’t really know how to use it. It’s actually not that hard. Recently for a modification I had to get hold of physically available stock of an item per location. Naturally I ended up using InventDimOnHand to avoid getting lost in complex queries.
As an example I have made a simplified version of it. Try running it in the CEU company of the Contoso demo database.
static void demoInventOnHand(Args _args) { InventDimOnHand onHand; InventDimOnHandIterator iter; InventDimOnHandMember member; ItemId itemId; InventDim inventDimCrit; InventDimParm inventDimParmCrit, inventDimParmOnHandLevel; InventDim inventDim; InventDimOnHandLevel level; ; itemId = '1509'; // Known dimensions inventDimCrit.InventLocationId = '21'; inventDimCrit = InventDim::findOrCreate(inventDimCrit); // Determine which of the known dimensions to use inventDimParmCrit.InventLocationIdFlag = true; level = InventDimOnHandLevel::DimParm; // Only matters for level DimParm. Determines the level of detail returned inventDimParmOnHandLevel.ItemIdFlag = true; inventDimParmOnHandLevel.InventLocationIdFlag = true; inventDimParmOnHandLevel.WMSLocationIdFlag = true; inventDimParmOnHandLevel.InventBatchIdFlag = true; onHand = InventDimOnHand::newAvailPhysical(itemId, inventDimCrit, InventDimParmCrit, level, inventDimParmOnHandLevel); iter = onHand.onHandIterator(); while (iter.more()) { member = iter.value(); inventDim = InventDim::find(member.parmInventDimId()); info(con2str([member.parmItemId(), inventDim.inventLocationId, inventDim.wMSLocationId, inventDim.inventBatchId, member.parmInventQty()])); iter.next(); } info('Done'); } |
This prints a list of the available stock per batch number of the item in warehouse 21. If you change the level to Item or comment out lines for the WMS location and batch flags, you will get the total stock for the item in the warehouse.
Basically the first two parameters of newAvailPhysical() contain the item and dimensions for which you want to find the inventory. The InventDimParmCrit parameter determines on which of the known dimensions should be filtered during the lookup. This means it is possible to ignore values in InventDimCrit or force checks on empty values.
The last two parameters determine the level of detail of the stock. In this case each batch of this item in warehouse 21 is reported. You can check this in the on-hand inventory screen.
The InventDimOnHand class then uses a couple of other classes like InventDimOnHandMember and InventDimOnHandIterator to make it possible to get the actual values.
Feel free to change the values of the parameters passed to availPhysical() to see what happens. It is also possible to get something other than the physically available stock. Check out the other functions on the class.
Nice article, though I usually find it faster to use static InventSum::findSum method to get inventory quantities for certain dimensions.
Good point. That works too. Although I find this clearer if I need a bit more detail than just a single inventory quantity.