D365 Mass de-reservation utility

Prehistoric art at the Niedersachsenhaus lodge, Austria

D365 Mass de-reservation utility

Cancel BOM reservation in D365FO

In the Production control module in Dynamics 365 for SCM there is an automatic BOM reservation routine. For example,  set the reservation on Estimation in the module parameters, run the materials Estimation for all open orders, done. If you did it by mistake, it is you who are done, because in the case of a just-in-time supply and a long production order backlog, the reservation is going to quickly deplete the stock, preventing any future urgent order from starting.

Since the reservation can only be cancelled production order by production order, BOM line by BOM line, then in a medium sized plant it can take a few days of work. In Dynamics 365 Business Central aka “Navision” the state of affairs is similar.

The below is a programmatic solution, a runnable class for mass de-reservation. You may place it on a D365 menu, or just run the following command in the browser:  https://xxxprod.operations.dynamics.com/?mi=SysClassRunner&cls=InventDeReserve

The user may select the type of the order to cancel the reservation for, and add additional criteria such as the warehouse, expected date of the order et cetera. Set the safeguard parameter Cancel reservation to Yes, then run with OK. Upon execution, the system is going to show a number of success messages like “Reference: Production line, Number: B000021 Item number: 205026, Lot ID: 002516 Reservation has been removed“.

You may copy or download the X++ source code here: InventDeReserve.axpp

				
					class InventDeReserve extends RunBaseBatch implements BatchRetryable
{
    QueryRun        queryRun;
    boolean         dereserveNow = false;
    DialogField     dlgDereserveNow;

    #define.CurrentVersion(1)
    #localmacro.CurrentList
        dereserveNow
    #endmacro

    protected void deReserveByQuery()
    {
        while (queryRun.next())
        {
            InventTrans inventTrans = queryRun.get(tableNum(InventTrans));
            if (inventTrans.StatusIssue != StatusIssue::ReservPhysical && inventTrans.StatusIssue != StatusIssue::ReservOrdered)
            {
                continue;
            }
            InventDim       inventDim = queryRun.get(tableNum(InventDim));
            InventDimParm   inventDimParm;
            inventDimParm.initFromInventDim(inventDim);

            ttsbegin;

            InventUpd_Reservation::newParameters(InventMovement::construct(inventTrans),
                // At these specific inventory dimensions only
                inventDim, inventDimParm, InventDimFixedClass::inventDimParm2InventDimFixed(inventDimParm),
                -inventTrans.Qty,
                false, // Do not allow auto reserve dim
                true /*Show info*/).updateNow();            

            ttscommit;
        }
    }
    public void run()
    {
        #OCCRetryCount

        if (! dereserveNow)
        {
            info("@SYS52714"); // Nothing to do
            return;
        }

        try
        {            
            this.deReserveByQuery();
        }
        catch (Exception::Deadlock)
        {
            retry;
        }
        catch (Exception::UpdateConflict)
        {
            if (appl.ttsLevel() == 0)
            {
                if (xSession::currentRetryCount() >= #RetryNum)
                {
                    throw Exception::UpdateConflictNotRecovered;
                }
                else
                {
                    retry;
                }
            }
            else
            {
                throw Exception::UpdateConflict;
            }
        }
    }
    public container pack()
    {
        return [#CurrentVersion, #CurrentList, queryRun.pack()];
    }
    public boolean unpack(container _packedClass)
    {
        Version     version = RunBase::getVersion(_packedClass);
        container   packedQueryRun;
        
        switch (version)
        {
            case #CurrentVersion:
                [version, #CurrentList, packedQueryRun] = _packedClass;
                queryRun = new QueryRun(packedQueryRun);
                break;
            default:
                return false;
        }

        return true;
    }
    public Object dialog()
    {
        DialogRunbase       dialog = super();
        
        dlgDereserveNow = dialog.addFieldValue(extendedTypeStr(NoYesId), dereserveNow, "@SYS50399" /* Cancel reservation */);

        return dialog;
    }
    public boolean getFromDialog()
    {
        dereserveNow = dlgDereserveNow.value();

        return super();
    }
    static void main(Args _args)
    {
        InventDeReserve    inventDeReserve = new InventDeReserve();
        if (inventDeReserve.prompt())
        {
            inventDeReserve.runOperation();
        }
    }
    public void initParmDefault()
    {
        super();
        queryRun = new QueryRun(new Query(queryStr(InventDeReserve)));
    }
    QueryRun queryRun()
    {
        return queryRun;
    }
    public boolean showQueryValues()
    {
        return true;
    }
    static ClassDescription description()
    {
        return "@SYS50399"; // Cancel reservation
    }
    public final boolean isRetryable()
    {
        return true;
    }
    public boolean canGoBatchJournal()
    {
        return true;
    }
    public boolean canRunInNewSession()
    {
        return false;
    }
}