Similar to the purchase order, while creating a transfer order from a warehouse to another warehouse or any other site/location, let us imagine that we make a transfer order with 1000 qty in different lines. The shipping warehouse shipped only 850 qty and the receiving warehouse received the 850 qty. Now, if you look at the status of the transfer order, it will show was an open order with status "created". The remaining 150 qty will remain in the inventory transactions as ordered. If the shipping warehouse is not going to issue the remaining 150 qty, this transfer order will remain open until someone manually changes the ordered qty of the receive remain items with the shipped qty.
What if the mother warehouse is doing an average shipment of 100 transfer orders a day and each transfer has an average of 100 line items. I am sure you will be wondering how tedious the efforts are going to be. I got this requirement from many of the operation teams and came up with a solution. A single button click will remove the receive remain qty from that transfer order.
The best part is that, operations will definitely look for the fulfillment rate of the warehouse. And if all the transfer orders show the ordered qty and shipped qty same (after we short close the transfer order) it means 100% fulfillment. To avoid this I am inserting all those lines into a custom table which can be used in the BI.
Sharing below the X++ code and images of the forms which does this job.
Button properties are shown below.
Override button click event as below.
void clicked()
{
InventTransferTable_ds.reread();
InventTransferTable_ds.refresh();
super();
InventTransferTable_ds.research(true);
InventTransferTable_ds.refresh();
}
As you can see in the button properties, this button is linked to a menuitem which is calling a class. Sharing below the methods inside the class.
ClassDeclaration
class VV_TOShortClose
{
Description description;
SysOperationProgress vvProgress;
#DEFINE.CurrentVersion(3)
#LOCALMACRO.CurrentList
description
}
Method pack
public container pack()
{
return [#CurrentVersion, #CurrentList];
}
Method shortCloseTo
public void shortCloseTO(InventTransferTable _transferTable)
{
InventTransferLine transferLine;
VV_TOShortCloseLines vvTransferLine;
int linenum=0;
if (Box::okCancel("Transfer order '" + _transferTable.TransferId + "' 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 TO '" + _transferTable.TransferId + "'");
vvprogress .setText("Short close TO process started..");
while select forUpdate transferLine
where transferLine.TransferId == _transferTable.TransferId
&& transferLine.QtyRemainShip !=0
//&& transferLine.RemainInventPhysical !=0
{
linenum++;
vvprogress.setText(strFmt("Processing line %1.", linenum));
transferLine.reread();
vvTransferLine.QtyTransfer = transferLine.QtyTransfer;
vvTransferLine.QtyRemainShip = transferLine.QtyRemainShip;
vvTransferLine.TransferId = transferLine.TransferId;
vvTransferLine.LineNum = transferLine.LineNum;
vvTransferLine.InventDimId = transferLine.InventDimId;
vvTransferLine.ItemId = transferLine.ItemId;
vvTransferLine.QtyShipped = transferLine.QtyShipped;
vvTransferLine.insert();
transferLine.QtyTransfer = transferLine.QtyShipped;
transferLine.QtyRemainShip = 0;
transferLine.QtyRemainReceive = (transferLine.QtyShipped - transferLine.QtyReceived);
transferLine.QtyReceiveNow = 0;
transferLine.NetAmount_IN = transferLine.UnitPrice_IN * transferLine.QtyShipped;
//transferLine.RemainInventPhysical = 0;
if(transferLine.QtyTransfer > 0) // Update the transfer qty with the shipped qty
{
transferLine.update();
}
else // if shipped qty is 0 , delete the line. since zero ordered qty is not allowed.
{
transferLine.delete();
}
}
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 unpack
public boolean unpack(container packedClass)
{
Integer version = RunBase::getVersion(packedClass);
switch (version)
{
case #CurrentVersion:
[version, #CurrentList] = packedClass;
break;
default:
return false;
}
return true;
}
Method validateTO
public void validateTO(InventTransferTable _transferTable)
{
InventTransferLine transferLine;
boolean ret = true;
select firstOnly transferLine
where transferLine.TransferId == _transferTable.TransferId
&& transferLine.QtyRemainShip != 0;
//&& transferLine.RemainPurchPhysical != 0;
if (!transferLine)
{
warning('No lines exist to short close TO.');
ret = false;
}
if (ret)
{
this.shortCloseTO(_transferTable);
}
}
Method main
public server static void main(Args _args)
{
VV_TOShortClose shortcloseTO;
InventTransferTable transferTable;
FormObjectSet transferTable_ds;
;
if (_args.dataset() == tableNum(InventTransferTable))
{
transferTable = _args.record();
if (transferTable)
{
shortcloseTO = new VV_TOShortClose();
shortcloseTO.validateTO(transferTable);
transferTable_ds = transferTable.dataSource();
transferTable_ds.reread();
transferTable_ds.refresh();
}
else
{
warning("Active record not found.");
}
}
else
{
warning("Wrong parameters.");
}
}
In the above set of methods, I have highlighted VV_TOShortCloseLines table in the shortCloseTo method. This is a custom table which stores the altered data. Below is the schema of that table.
As always, please test this in your UAT and pre-production environments prior to the deployment in live. I have masked the nomenclature in all the images due to some security reasons.
Happy Daxing!
Comments