When receiving products from purchase, production or any other receipt, then most companies wants the system to present a location where the items should be placed.
Dynamics AX 2012 have the capability to find a available location in the warehouse, but this algorithm is based on that the receipt do have a pallet.
If you want to examine the standard X++ code for this, then take a look at Table\WMSPallet.findFreeLocation(..), and just move forward in the code.
But we who have been working with logistics for many years, know that it is just a fraction of our customers, that are using pallets. Most just have pure SKU’s that they want to store in their warehouse.
So in the PDA, I implemented a way to suggest put-away locations to the warehouse operator, that works without pallets.
The algorithm is simpler than the standard DAX2012 search for pallet location, but the response I get from my customers, it that it is more practical. So what the code basically do is :
1. If the item/warehouse have a default receipt location, then use this.
2. If the item have a default picking location, then suggest this.
3. Search for a existsing buffer location with onhand, and then suggest this.
4. Seach for a buffer location where the item has been before, but now is empty, then suggest this.
5. Search for any empty and available buffer location.
Here is the code available for the Dynamcis AX community. (it’s not perfect, but ment as an inspiration for all DAX developers that every day is creating value for our customers)
WMSLocationId suggestedWmsLocationId(ItemId _itemId, InventDimId _inventDimId)
{
InventItemLocation inventItemLocation;
WMSStoreZoneArea WMSStoreZoneArea;
InventDim parmInventdim = InventDim::find(_inventDimId);
InventDim inventDim;
InventDim inventDimCriteria;
InventDimParm inventDimParm;
InventSum inventSum;
InventSum inventSum2;
InventDim inventDim2;
WMSLocation wmsLocation;
if (parmInventdim.wmsLocation() )
return parmInventdim.wmsLocationId;
inventItemLocation = InventItemLocation::findFixedWarehouse(_itemId, _inventDimId);
if (inventItemLocation)
{
//1. use default receipt location on item
if (inventItemLocation.wmsLocationIdDefaultReceipt)
return inventItemLocation.wmsLocationIdDefaultReceipt;
//2. use picking location on item
if (inventItemLocation.wmsPickingLocation)
return inventItemLocation.wmsPickingLocation;
}
//3. Search for existing buffer locations with item onhand
inventDimCriteria = parmInventdim;
inventDimParm.initFromInventDim(inventDimCriteria);
inventDimParm.wmsLocationIdFlag = false;
inventDimParm.wmsPalletIdFlag = false;
inventDimParm.InventBatchIdFlag = false;
inventDimParm.InventSerialIdFlag = false;
while select firstonly sum(PhysicalInvent) from inventSum
group by InventDimId
where inventSum.ItemId == _itemId
&& inventSum.PhysicalInvent > 0
#InventDimExistsJoin(InventSum.inventdimid,inventdim,inventDimCriteria, InventDimParm)
exists join wmsLocation
where wmsLocation.InventLocationId == inventDim.InventLocationId
&& wmsLocation.wmsLocationId == inventDim.wmsLocationId
&& !wmsLocation.InputBlockingCauseId
&& (wmsLocation.LocationType == WMSLocationType::Buffer ||
wmsLocation.LocationType == WMSLocationType::Pick)
{
if (inventItemLocation.wMSStoreZoneId)
{
select firstonly WMSStoreZoneArea
where WMSStoreZoneArea.storeZoneId == inventItemLocation.wMSStoreZoneId
&& WMSStoreZoneArea.storeAreaId == inventSum.inventDim().wmsLocation().storeAreaId;
if (WMSStoreZoneArea)
return wmsLocation.wMSLocationId;
}
else
return inventSum.inventDim().wmsLocationId;
}
//4. Search for existing buffer locations where the item has been before, is zero, and no other items
while select firstonly sum(PhysicalInvent) from inventSum
group by InventDimId
where inventSum.ItemId == _itemId
&& inventSum.PhysicalInvent == 0
#InventDimExistsJoin(InventSum.inventdimid,inventdim,inventDimCriteria, InventDimParm)
exists join wmsLocation
where wmsLocation.InventLocationId == inventDim.InventLocationId
&& wmsLocation.wmsLocationId == inventDim.wmsLocationId
&& !wmsLocation.InputBlockingCauseId
&& (wmsLocation.LocationType == WMSLocationType::Buffer ||
wmsLocation.LocationType == WMSLocationType::Pick)
{
select firstonly sum(PhysicalInvent) from inventSum2
where inventSum2.ItemId != _itemId
&& inventSum2.PhysicalInvent > 0
exists join inventDim2
where inventDim2.InventLocationId == inventSum.inventDim().InventLocationId
&& inventDim2.wmsLocationId == inventSum.inventDim().wmsLocationId;
if (inventSum2.PhysicalInvent == 0)
{
if (inventItemLocation.wMSStoreZoneId)
{
select firstonly WMSStoreZoneArea
where WMSStoreZoneArea.storeZoneId == inventItemLocation.wMSStoreZoneId
&& WMSStoreZoneArea.storeAreaId == inventSum.inventDim().wmsLocation().storeAreaId;
if (WMSStoreZoneArea)
return wmsLocation.wMSLocationId;
}
else
return inventSum.inventDim().wMSLocationId;
}
}
//5. Search for empty and available location in buffer
while select wmsLocation order by SortCode, wmsLocationId
where wmsLocation.InventLocationId == parmInventdim.InventLocationId
&& !wmsLocation.InputBlockingCauseId
&& (wmsLocation.LocationType == WMSLocationType::Buffer ||
wmsLocation.LocationType == WMSLocationType::Pick)
exists join inventDim
where inventDim.InventLocationId == wmsLocation.InventLocationId
&& inventDim.wmsLocationId == wmsLocation.wmsLocationId
exists join inventSum
where inventSum.InventDimId == inventDim.InventDimId
&& inventSum.PhysicalInvent == 0
&& inventSum.Ordered == 0
{
select firstonly sum(PhysicalInvent) from inventSum2
where inventSum2.ItemId != _itemId
&& inventSum2.PhysicalInvent > 0
exists join inventDim2
where inventDim2.InventLocationId == wmsLocation.InventLocationId
&& inventDim2.wmsLocationId == wmsLocation.wmsLocationId;
if (inventSum2.PhysicalInvent == 0)
{
if (inventItemLocation.wMSStoreZoneId)
{
select firstonly WMSStoreZoneArea
where WMSStoreZoneArea.storeZoneId == inventItemLocation.wMSStoreZoneId
&& WMSStoreZoneArea.storeAreaId == wmsLocation.storeAreaId;
if (WMSStoreZoneArea)
return wmsLocation.wMSLocationId;
}
else
return wmsLocation.wmsLocationId;
}
}
return ''; //No locations was found :-(
}