Search for available putaway location using X++

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.

image

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 :-(
}