Bu yazıda Dynamics 365 Finance and Operations içinde depo yönetimi modülünde toplanan bir ürünün kodla yerine koyulması ve sevkiyatın iptal edilmesinin nasıl yapılacağını anlatacağım.
Aslında bu işlemi ekran üzerinden tek tek yapabiliyoruz.  Load Line formunu açıp Reduce picked quantity’ye tıklıyoruz.
  

Resim-1
Açılan formda miktarı ayarlayıp Ok dediğimizde aslında bu işlemi yapmış oluyoruz.
  

Resim-2
Benim burada yapacağım bu işlemi toplu yapmak için bir kod yamak olacak. Bir sebepten otomatik toplanan bazı siparişlerin iptali gerektiğinde elle tek tek yapmak çok uzun süreceğinden böyle bir koda ihtiyaç oldu. Öncelikle Ax2012’de bu işlem için Güven arkadaşım şu makaleyi yazmış. Benim işimi tam çözmüyor. Ben bir dosyadan beri okuyup D365 te bu işlemi yapmak istiyorum.
Öncelikle WhsUnShip sınıfından bir Extension sınıfı oluşturmam gerekti.  BuildTmpTable aslında Ax2012 olan bir metottu ama D365 te kullanamıyoruz.
[ExtensionOf(classStr(WhsUnShip))]
final class WhsUnShip_Extension
{
public WHSTmpLoadLineInventory dmrBuildTmpTable(WHSLoadLine _loadLine = loadLine)
{
WHSTmpLoadLineInventory     tmpLoadLineInv;
WHSWorkLine                 workLine;
WHSWorkLine                 putWorkLine;
WHSWorkTable                workTable;
WHSDimTracking              dimTracking;
InventDim                   inventDim;
Qty                         closedContainerQty;
InventDim                   inventDimId;
SalesLine                   salesLine;
InventTransferLine          transferLine;
InventDim                   loadLineInventDim;
boolean                     putIsPackingStation;
WHSWorkId                   lastWHSWorkId;
WMSLocation                 wmsLocation;
boolean                     hasDimTracking;
// Set LoadLine if specified.
loadLine = _loadLine;
loadLineInventDim = loadLine.inventDim();
switch (loadLine.InventTransType)
{
case InventTransType::Sales:
salesLine = loadLine.getOrderCommonFromLoadLine() as SalesLine;
break;
case InventTransType::TransferOrderShip:
transferLine = loadLine.getOrderCommonFromLoadLine() as InventTransferLine;
break;
}
while select InventQtyWork, ItemId, LineNum, WorkId, ContainerId from workLine
order by workLine.WorkId
where workLine.LoadLineRefRecId  == loadLine.RecId &&
workLine.WorkStatus        == WHSWorkStatus::Closed
join WorkId, TargetLicensePlateId, InventLocationId from workTable
where workTable.WorkId       == workLine.WorkId &&
workTable.WorkStatus   == WHSWorkStatus::Closed
{
if (lastWHSWorkId != workLine.WorkId)
{
select firstonly wmsLocationId from putWorkLine
order by putWorkLine.LineNum desc
where putWorkLine.WorkId     == workTable.WorkId        &&
putWorkLine.WorkStatus == WHSWorkStatus::Closed   &&
putWorkLine.WorkType   == WHSWorkType::Put
join LocProfileId from wmsLocation
where wmsLocation.wMSLocationId == putWorkLine.wmsLocationId &&
wmsLocation.InventLocationId == workTable.InventLocationId;
putIsPackingStation = wmsLocation.LocProfileId == parameters.PackingLocType;
lastWHSWorkId = workLine.WorkId;
}
hasDimTracking = false;
// If the workLine uses dim tracking we must use those values.
while select InventDimId, Qty from dimTracking
where dimTracking.WorkId    == workLine.WorkId
&&    dimTracking.LineNum   == workLine.LineNum
{
hasDimTracking = true;
inventDim = InventDim::find(dimTracking.InventDimId);
inventDim.wmsLocationId = putWorkLine.wmsLocationId;
if (WMSLocation::find(inventDim.wmsLocationId, inventDim.InventLocationId).whsLocationIsLPControlled())
{
inventDim.LicensePlateId = workTable.TargetLicensePlateId;
}
inventDim = InventDim::findOrCreate(inventDim);
// If put location is a packing station, then we need to look for closed containers that may have moved inventory.
if (putIsPackingStation)
{
InventDimParm       inventDimParm;
InventDim           joinInventDim;
InventDim           inventDimMethod;
WHSContainerLine    containerLine;
WHSContainerTable   containerTable;
inventDimParm.initFromInventDim(inventDim);
// Loop over container lines for closed containers
while select containerLine
where containerLine.LoadLine    == loadLine.RecId
join InventDimId, ContainerId from containerTable
where containerTable.ContainerId        == containerLine.ContainerId
&&    containerTable.ContainerStatus    == WHSContainerStatus::Closed
#InventDimExistsJoin(containerLine.inventDimId, joinInventDim, inventDim, inventDimParm)
{
tmpLoadLineInv.InventQty = InventTableModule::unitConvert(containerLine.ItemId, ModuleInventPurchSales::Invent, containerLine.UnitId, containerLine.Qty);
inventDimMethod = containerLine.mergedClosedInventDim();
tmpLoadLineInv.InventDimId = inventDimMethod.InventDimId;
tmpLoadLineInv.RefRecId = loadLine.RecId;
tmpLoadLineInv.ContainerId = containerTable.ContainerId;
tmpLoadLineInv.insert();
closedContainerQty += tmpLoadLineInv.InventQty;
}
}
if (dimTracking.Qty > closedContainerQty)
{
tmpLoadLineInv.clear();
tmpLoadLineInv.InventQty = dimTracking.Qty – closedContainerQty;
tmpLoadLineInv.InventDimId = inventDim.InventDimId;
tmpLoadLineInv.RefRecId = loadLine.RecId;
tmpLoadLineInv.ContainerId = workLine.ContainerId;
tmpLoadLineInv.insert();
}
}
if (!hasDimTracking)
{
inventDim.clear();
inventDim.initFromInventDim(loadLineInventDim);
inventDim.wmsLocationId = putWorkLine.wmsLocationId;
if (WMSLocation::find(inventDim.wmsLocationId, inventDim.InventLocationId).whsLocationIsLPControlled())
{
inventDim.LicensePlateId = workTable.TargetLicensePlateId;
}
inventDim = InventDim::findOrCreate(inventDim);
// If put location is a packing station, then we need to look for closed containers that may have moved inventory.
if (putIsPackingStation)
{
InventDimParm       inventDimParm;
InventDim           joinInventDim;
InventDim           inventDimMethod;
WHSContainerLine    containerLine;
WHSContainerTable   containerTable;
inventDimParm.initFromInventDim(inventDim);
// Loop over container lines for closed containers
while select containerLine
where containerLine.LoadLine    == loadLine.RecId
join InventDimId, ContainerId from containerTable
where containerTable.ContainerId        == containerLine.ContainerId
&&    containerTable.ContainerStatus    == WHSContainerStatus::Closed
#InventDimExistsJoin(containerLine.inventDimId, joinInventDim, inventDim, inventDimParm)
{
tmpLoadLineInv.InventQty = InventTableModule::unitConvert(containerLine.ItemId, ModuleInventPurchSales::Invent, containerLine.UnitId, containerLine.Qty);
inventDimMethod = containerLine.mergedClosedInventDim();
tmpLoadLineInv.InventDimId = inventDimMethod.InventDimId;
tmpLoadLineInv.RefRecId = loadLine.RecId;
tmpLoadLineInv.ContainerId = containerTable.ContainerId;
tmpLoadLineInv.insert();
closedContainerQty += tmpLoadLineInv.InventQty;
}
}
if (workLine.InventQtyWork > closedContainerQty)
{
tmpLoadLineInv.clear();
tmpLoadLineInv.InventQty = workLine.InventQtyWork – closedContainerQty;
tmpLoadLineInv.InventDimId = inventDim.InventDimId;
tmpLoadLineInv.RefRecId = loadLine.RecId;
tmpLoadLineInv.ContainerId = workLine.ContainerId;
tmpLoadLineInv.insert();
}
}
}
return tmpLoadLineInv;
}
public WHSLoadLine parmLoadLine(WHSLoadLine _loadLine = loadLine)
{
loadLine = _loadLine;
return loadLine;
}
}
Sonrasında asıl işi yapan sınıfı yazdım. Burada bir csv dosyasından veri okuyup ilgili kaydı bulup iptal işlemini yapan kod var.
class DmrUnsipLoadCsv
{
public static void main(Args _args)
{
DmrUnsipLoadCsv DmrUnsipLoadCsv;
DmrUnsipLoadCsv = new DmrUnsipLoadCsv();
DmrUnsipLoadCsv.run();
}
void run()
{
whsworkid       whsworkid;
container   rec;
Array                               fileLines;
Counter                             counter = 0;
AsciiStreamIo                       file;
InventTable                         inventTable;
FileUploadTemporaryStorageResult    fileUpload;
#OCCRetryCount
//conPeek(rec, 3);
try
{
fileUpload  = File::GetFileFromUser() as FileUploadTemporaryStorageResult;
file        = AsciiStreamIo::constructForRead(fileUpload.openResult());
if (file)
{
if (file.status())
{
throw error(“@SYS52680″);
}
file.inFieldDelimiter(“;”);
file.inRecordDelimiter(“\r\n”);
}
while (!file.status())
{
counter++;
rec = file.read();
if (conLen(rec))
{
whsworkid = conPeek(rec, 1);
ttsbegin;
this.unship(whsworkid);
ttscommit;
//info(strFmt(“%1″,conPeek(rec, 1)));
}            }
// info(“Aktarım tamamlandı.”);
}
catch (Exception::Deadlock)
{
retry;
}
catch (Exception::UpdateConflict)
{
if (appl.ttsLevel() == 0)
{
if (xSession::currentRetryCount() >= #RetryNum)
{
throw Exception::UpdateConflictNotRecovered;
}
else
{
retry;
}            }
else
{
throw Exception::UpdateConflict;
}
}
}
void unship(whsworkid _whsworkid )
{
WHSWorkLine             WHSWorkLine;
WHSWorkLine             WHSWorkLineLocation;
WHSUnShip               WHSUnShip;
WHSTmpLoadLineInventory tmpLoadLineInv;
WHSLoadLine             WHSLoadLine;
;
while select WHSWorkLine
group by LoadLineRefRecId
where WHSWorkLine.WorkId == _whsworkid
&&    WHSWorkLine.WorkType == WHSWorkType::Pick
&&    WHSWorkLine.WorkStatus == WHSWorkStatus::Closed
{
WHSLoadLine = WHSLoadLine::findbyRecId(WHSWorkLine.LoadLineRefRecId);
WHSUnShip = null;
WHSUnShip = new WHSUnShip();
tmpLoadLineInv = null;
tmpLoadLineInv = WHSUnShip.dmrbuildTmpTable(WHSLoadLine);
while select tmpLoadLineInv
{
select WHSWorkLineLocation
where WHSWorkLineLocation.LoadLineRefRecId == WHSLoadLine.RecId;
WHSUnShip.parmMoveToLocation(WMSLocation::find(WHSWorkLineLocation.WMSLocationId,InventDim::find(WHSWorkLineLocation.inventDimId).inventLocationId));
WHSUnShip.parmDecrementLoadLine(true);
WHSUnShip.unShip(
InventDim::find(tmpLoadLineInv.InventDimId),
tmpLoadLineInv.InventQty, // qty to reduce
WHSLoadLine,
tmpLoadLineInv.ContainerId,
tmpLoadLineInv.InventQty);
}
//info(_whsworkid);
}
info(_whsworkid);
}
}
Bu kodları kullanacaksanız mutlaka canlıdan önce bir ortamda test etmelisiniz. Her kurulum farklı olabilir verilerinizde sorun oluşturma ihtimali olabilir dikkatli olmak lazım. Biz baya test ettik sonunda işimizi gördüğünü düşündük ve canlı ortamda çalıştırdık. Umarım sizin de işinize yarar.
Selamlar.
www.fatihdemirci.net
TAGs: Microsoft Life Cycle Services, LCS, Azure, Azure DevOps, WmsUnShip, Microsoft Dynamics 365, MsDyn365FO, MsDyn365CE, MsDyn365, Segmented Entry, Power Automate, Power Apss, Power Virtual Agents, Dynamics 365 nedir, Dynamics 365 ERP, Dynamics 365 CRM