top of page

Purchase order short close

Updated: Dec 8, 2020

There are cases where a purchase order is created with 100 quantity and the product receipt is done only for 90 qty. The invoicing is also done and while checking the open order list, this purchase order will be listed. There were many requests from the operations and clients to hide these purchase orders from the open order list.

Even if we tweak our BI to hide these orders, this will show up in the inventory transactions of dynamics AX/ D365 as open order. There is an option in AX to delete the remaining qty. The real pain starts where the purchase order has hundreds of line items (You will have to hire someone to remove these items). I have made a utility to tackle this situation. Sharing with you all the code and the screenshots.


This will consider the purchase orders that are linked to projects and will release the budget allocated.


There is a button created in the purchase order tab inside the purchase order form.

Button properties are shown below.


Write the below code in the click even of that button

void clicked()

{

PurchTable_ds.reread();

PurchTable_ds.refresh();

super();

PurchLine_ds.research(true);

PurchLine_ds.refresh();

}

The MenuItem is pointing to a class. So you need to create a class VV_PurchShortClose and write the below set of codes. Why I created a class because of the re-usability of the code.

Method 1 inside the Class

class VV_PurchShortClose

{

Description description;

SysOperationProgress vvProgress;

PurchTable purchTable;

#DEFINE.CurrentVersion(3)

#LOCALMACRO.CurrentList

description

}

Method 2 inside the Class

public container pack()

{

return [#CurrentVersion, #CurrentList];

}

Method 3 inside the Class

public void shortClosePO(PurchTable _purchTable)

{

PurchLine purchLine;


if (Box::okCancel("Purchase order '" + _purchTable.PurchId + "' lines remain physical will be set to 0. Do you wish to continue?", DialogButton::Cancel) == DialogButton::Ok)

{

try

{

info(strFmt("Process started %1 %2.", systemDateGet(), time2str(timeNow(), TimeSeparator::Colon, TimeFormat::AMPM)));

ttsBegin;

vvProgress= new SysOperationProgress();

vvProgress.setAnimation(#AviUpdate);

vvProgress.setCaption("Short close PO '" + _purchTable.PurchId + "'");

vvProgress.setText("Short close PO process started..");

while select forUpdate purchLine

where purchLine.PurchId == _purchTable.PurchId

&& purchLine.RemainPurchPhysical !=0

&& purchLine.RemainInventPhysical !=0

{

vvProgress.setText(strFmt("Processing line %1.", PurchLine.LineNumber));

purchLine.reread();

this.updateVendorPackingSlipTrans(purchLine);

purchLine.RemainPurchPhysical = 0;

purchLine.RemainInventPhysical = 0;

purchLine.update();

}

ttsCommit;

info(strFmt("Process ended %1 %2.", systemDateGet(), time2str(timeNow(), TimeSeparator::Colon, TimeFormat::AMPM)));

}

catch (Exception::UpdateConflict)

{

retry;

}

catch (Exception::UpdateConflictNotRecovered)

{

retry;

}

catch

{

ttsAbort;

}

}

}

Method 3 inside the Class

This method is called if the purchase order is linked to a project and if any budget is allocated for project.

public void shortCloseProjectPO(PurchTable _purchTable)

{

PurchLine purchLine;


if (Box::okCancel("Purchase order '" + _purchTable.PurchId + "' lines remain physical will be set to 0 and Project budget would be updated. Do you wish to continue?", DialogButton::Cancel) == DialogButton::Ok)

{

try

{

info(strFmt("Process started %1 %2.", systemDateGet(), time2str(timeNow(), TimeSeparator::Colon, TimeFormat::AMPM)));

ttsBegin;

vvProgress= new SysOperationProgress();

vvProgress.setAnimation(#AviUpdate);

vvProgress.setCaption("Short close PO '" + _purchTable.PurchId + "'");

vvProgress.setText("Short close PO process started..");

while select forUpdate purchLine

where purchLine.PurchId == _purchTable.PurchId

&& (purchLine.RemainPurchPhysical !=0

|| purchLine.RemainInventPhysical !=0)

{

vvProgress.setText(strFmt("Processing line %1.", PurchLine.LineNumber));

PurchLine.reread();

this.updateCommittedCost(PurchLine);

this.updateVendorPackingSlipTrans(purchLine);

PurchLine.RemainPurchPhysical = 0;

PurchLine.RemainInventPhysical = 0;

PurchLine.update();

}

ttsCommit;

info(strFmt("Process ended %1 %2.", systemDateGet(), time2str(timeNow(), TimeSeparator::Colon, TimeFormat::AMPM)));

}

catch (Exception::UpdateConflict)

{

retry;

}

catch (Exception::UpdateConflictNotRecovered)

{

retry;

}

catch

{

ttsAbort;

}

}

}

Method 4 inside the Class

public boolean unpack(container packedClass)

{

Integer version = RunBase::getVersion(packedClass);


switch (version)

{

[version, #CurrentList] = packedClass;

break;

default:

return false;

}

return true;

}


Method 5 inside the Class

This is to release the committed cost

public void updateCommittedCost(PurchLine _purchLine)

{

CostControlTransCommittedCost committedCost,committedCostInsert;


while select forUpdate committedCost

where committedCost.EmplItemId == _purchLine.ItemId

&& committedCost.SourceDocumentLine == _purchLine.SourceDocumentLine

&& committedCost.Qty == _purchLine.RemainPurchPhysical


if(committedCost)

{

this.updateForecastSales(committedCost);


committedCostInsert.ProjId = committedCost.ProjId;

committedCostInsert.CategoryId = committedCost.CategoryId;

committedCostInsert.CommittedDate = committedCost.CommittedDate;

committedCostInsert.AmountMst = committedCost.AmountMst*-1;

committedCostInsert.EmplItemId = committedCostInsert.EmplItemId;

committedCostInsert.CurrencyCode = committedCost.CurrencyCode;

committedCostInsert.ProjType = committedCost.ProjType;

committedCostInsert.ProjTransType = committedCost.ProjTransType;

committedCostInsert.RefId = committedCost.RefId;

committedCostInsert.CommittedCostOrig = committedCost.CommittedCostOrig;

committedCostInsert.Qty = committedCost.Qty *-1 ;

committedCostInsert.ActivityNumber = committedCost.ActivityNumber;

committedCostInsert.Amount = committedCost.Amount*-1;

committedCostInsert.ProjTransId = committedCost.ProjTransId;

committedCostInsert.Open = NoYes::No;

committedCostInsert.Reverse = NoYes::No;

committedCostInsert.VendAccount = committedCost.VendAccount;

committedCostInsert.InventTransId = committedCost.InventTransId;

committedCostInsert.LedgerDimension = committedCost.LedgerDimension;

committedCostInsert.DefaultDimension = committedCost.DefaultDimension;

committedCostInsert.SourceDocumentLine = committedCost.SourceDocumentLine;

committedCostInsert.Worker = committedCost.Worker;

committedCostInsert.insert();


committedCost.Open = 0;

committedCost.update();

}

}

Method 6 inside the Class

public void updateForecastSales(CostControlTransCommittedCost transCommit)

{

ForecastSales forecastSales;


select forUpdate forecastSales

where forecastSales.ProjId == transCommit.ProjId

&& forecastSales.ModelId == "REMBGT"

&& forecastSales.ProjCategoryId == transCommit.CategoryId;


if(forecastSales)

{

forecastSales.CostPrice += transCommit.Amount;

forecastSales.update();

}

}

Method 7 inside the Class

public void updateVendorPackingSlipTrans(PurchLine _purchLine)

{

VendPackingSlipTrans vendPackingslipTrans;


select forUpdate vendPackingslipTrans

where vendPackingslipTrans.PurchaseLineLineNumber == _purchLine.LineNumber

&& vendPackingslipTrans.ItemId == _purchLine.ItemId

&& vendPackingslipTrans.InventDimId == _purchLine.InventDimId

&& vendPackingslipTrans.Remain != 0;


if(vendPackingslipTrans)

{

vendPackingslipTrans.Remain = 0;

vendPackingslipTrans.update();

}

}

Method 8 inside the Class

public void validatePO(PurchTable _purchTable)

{

PurchLine purchLine;

boolean ret = true;


select firstOnly purchLine

where purchLine.PurchId == _purchTable.PurchId

&& purchLine.RemainInventPhysical != 0

&& purchLine.RemainPurchPhysical != 0;

if(!purchLine)

{

ret = false;

}

if (ret)

{

this.shortClosePO(_purchTable);

}

ret = true;


select * from purchTable

where purchTable.PurchId == _purchTable.PurchId

&& purchTable.ProjId != '';

if(!purchTable)

{

ret = false;

}

if(ret)

{

this.shortCloseProjectPO(_purchTable);

}

if (!purchLine && !purchTable)

{

warning('No lines exist to short close PO.');

ret = false;

}

}

Method 9 inside the Class

public server static void main(Args _args)

{

VV_PurchShortClose shortclosePO;

PurchTable purchTable;

FormObjectSet purchTable_ds;

;


if (_args.dataset() == tableNum(PurchTable))

{

purchTable = _args.record();

if (purchTable)

{

shortclosePO = new VV_PurchShortClose();

shortclosePO.validatePO(purchTable);

purchTable_ds = purchTable.dataSource();

purchTable_ds.reread();

purchTable_ds.refresh();

}

else

{

warning("Active record not found.");

}

}

else

{

warning("Wrong parameters.");

}

}


You can skip the methods that are used to release the committed cost and budget, if you are not using the concept of project purchase order.

Once you select the purchase order and click on that button, you will be prompted with the below message.


Once you click OK, the above class will be triggered and the purchase order will be marked as either invoiced/closed based on the status.


Recent Posts

See All
Post: Blog2_Post
bottom of page