Archive for Mart, 2021

Depoda toplanan ürünün yerine geri koyulmasını ve sevkiyatının iptal edilmesini kodla nasıl yaparız?

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

Ax2012 Clinet Crash Sorunu?

Bu yazıda bir müşterimde karşılaştığım Client kapanma sorununu nasıl çözdüğümü anlatacağım. Ax2012 R3 te olan bu ortamda birdenbire bazı clientlar kapanmaya başladı. Bazen AOS ta kapanıyordu. Dump alıp baktığımızda Heap corruption hatası vardı ama kodsal olarak bir yere işaret etmiyordu. Uzun incelemeler sonunda belli bir tablo tespit ettim. ProjProposalJour bu tabloya bazı yerlerden dokununca patlıyor bazı yerlerden dokununca patlamıyordu. Bu da sorunun tespitini çok zorlaştırdı çünkü ne hata veriyor ne de bir bilgi gösteriyordu.  Temel yapılması gerekenleri yaptım. Full derleme senkronizasyon CIL gibi ama buralarda da hatalar vardı. Özellikle SQL üzerinden oluşturulan indexler sebebiyle senkronizasyon baya sorunlu hala gelmişti.  Tek tek çözmeye çalıştım.  Bu problemleri çözünce müşterinin çalışmadığı bir saate ProjProposalJour patlatmayan yerden çoğaltım. Patlayan AOS’u restart edip çoğalttığım nesnede de patladığını gördüm. Bu durumda kesin nesnede bir sorun var diye düşündüm ve tek tek alt nesneleri silmeye başladım uzun uğraşlar sonunda patlatan şeyin relation’lar olduğunu tespit ettim ama bu çok mantıklı değildi relation neden patlatsın. Sonra tekrar full senkronizasyon başlattım. Sonunda aşağıdaki uyarıyı gördüm.

The SQL database has issued an error.
Info Synchronize database SQL error description: [Microsoft][SQL Server Native Client 11.0][SQL Server]The index ‘I_592INDEX1FGD’ on table ‘DBO.PROJINVOICEJOUR’ has 95 columns in the key list. The maximum limit for index key column list is 16.
Info Synchronize database SQL statement: CREATE   INDEX I_592INDEX1FGD’ ON “DBO”.PROJINVOICEJOUR (PARTITION,DATAAREAID,DELIVERYNAME,PROJGROUPID,ORDERACCOUNT,INVOICEACCOUNT,INVOICEDATE,DUEDATE,CASHDISC,CASHDISCDATE,COSTVALUE,SUMLINEDISC,SALESORDERBALANCE,ENDDISC,INVOICEAMOUNT,CURRENCYID,EXCHRATE,PROJINVOICEID,LEDGERVOUCHER,ONACCOUNTAMOUNT,TAXPRINTONINVOICE,ENTERPRISENUMBER,DLVTERM,DLVMODE,PAYMENT,CASHDISCCODE,INVOICEROUNDOFF,CASHDISCPERCENT,TAXGROUPID,TAXSPECIFYTOTAL,TAXSPECIFYBYLINE,PAYMENTSCHED,SUMTAX,PROPOSALID,POSTINGPROFILE,PROJINVOICEPROJID,PARMID,INTRASTATDISPATCHID,LISTCODEID,PROJINVOICETYPE,TRIANGULATION,EXCHRATESECONDARY,PORT,WEIGHT,VOLUME,QTY,VATNUM,LANGUAGEID,PAYMDAYID,EUSALESLIST,SUMMARKUP,NUMBERSEQUENCEGROUPID,POSTINGJOURNALID,EINVOICELINESPECIFIC,SMASPECINDEXCALC,GIROTYPE,EINVOICEACCOUNTCODE,INTERCOMPANYPOSTED,PAYMID,PRINTEDORIGINALS,DELIVERYPOSTALADDRESS,DEFAULTDIMENSION,VOUCHERNUMBERSEQUENCETABLE,SOURCEDOCUMENTHEADER,INVOICENUMBERINGCODE_LT,INVOICEREGISTER_LT,WHOISAUTHOR_LT,SALESDATE_CZ,DESCRIPTION,DOCUMENTDATE_W,INTRASTATADDVALUE_LV,PSAENDDATEMAXINVOICEID,DIRECTDEBITMANDATE,NARRATION_BR,PSAINVOICEFORMATS,REASONTABLEREF,TAXINFORMATION_IN,TRANSPORTATIONDOCUMENT,INVOICETYPE_MY,RECVERSION,MODIFIEDDATETIME,MODIFIEDBY,CREATEDDATETIME,CREATEDBY,
Error Synchronize database Cannot execute a data definition language command on  ().

ProjInvoiceJour ile ProjProposalJour ilişkili iki tablo asıl sorun ProjInvoiceJour’da. Sorunu tespit ettim. Bir şekilde tablonun bütün alanları key olan bir indexe eklenmeye çalışılmış. Biri mi yaptı yoksa metadata bozulmasından kaynaklı bir durum mu bilemiyorum. Ancak bu index ne axta ne de SQL tarafında yok. Dolayısıyla bunu metadata tanımlarından silmek gerekiyordu.

Bu indexi bulmak için Model db sinden birkaç sorgu yapmam gerekti. Öncelikle model tipini tespit ettim.

Resim-1

Sonrasında ismini bildiğim o tabloda bir index bulup ParentHandle kullanarak o tablodaki tüm indexleri listeledim ve silmem gereken indexi buldum.

Resim-2

Bu index tanımını sildikten sonra sistem düzeldi. Tabi bunu yapmadan yedeğimi almıştım. Tespiti beni en çok zorlayan durumlardan biri oldu. Bu arada Microsoft ta işin içine dahil oldu ama onlarda direk tespitte bulunamadılar.

Selamlar.

www.fatihdemirci.net

TAGs: Microsoft Life Cycle Services, LCS, Azure, Azure DevOps, Ax2012, Microsoft Dynamics 365, MsDyn365FO, MsDyn365CE, MsDyn365, Segmented Entry, Power Automate, Power Apss, Power Virtual Agents, Dynamics 365 nedir, Dynamics 365 ERP, Dynamics 365 CRM

Dynamics 365 Finance and Operations Boyut işlemleri?

Bu yazıda Dynamics 365 Finance and Operations için boyut işlemlerini nasıl yapabiliriz bir job ile anlatmaya çalışacağım. Ax2012 DimensionStore sınıfı birçok metodu barındırıyordu ancak MsDyn365FO ile helper sınıfları kullanılır oldu bu yüzden birçok metot bu sınıflara taşındı. Ax2012 için hazırladığım yazıya buradan ulaşabilirsiniz. Birkaç metot eksik olmakla birlikte genel manada 3 tane sınıf işimizi görüyor.

class FDDimJob1

{

public static void main(Args _args)

{

DimensionAttributeValueCombination  dimAttrValueCombDefaultAccount;

DimensionAttributeValueCombination  dimAttrValueComb;

DimensionAttributeValueCombination  dimAttrValueCombCompare;

DimensionAttributeValueCombination  dimAttrValueCombCust;

DimensionAttributeValueSet          dimAttrValueSet;

MainAccount                         mainAccount;

CustTable                           custTable;

LedgerJournalACType                 ledgerJournalACType;

DimensionHierarchy                  dimHierarchy;

;

// Örneklerde kullanmak için bazı kayıtları buluyorum

dimAttrValueCombDefaultAccount  = DimensionAttributeValueCombination::find(22565427296);

dimAttrValueComb                = DimensionAttributeValueCombination::find(22565428050);

dimAttrValueCombCompare         = DimensionAttributeValueCombination::find(22565428053);

mainAccount                     = MainAccount::find(22565425069);

custTable                       = CustTable::find(“US-006″);

// Bir hesap ve tip alarak size LedgerDimension’da kayıt varsa getirir yoksa oluşturur.

dimAttrValueCombCust = DimensionAttributeValueCombination::find(

LedgerDynamicAccountHelper::getDynamicAccountFromAccountNumber(custTable.AccountNum , LedgerJournalACType::Cust));

info(strfmt(“LedgerDimension RecId = %1″ , dimAttrValueCombCust.RecId));

// Bir MainAccount ait defaultAccount varsa getirir yoksa oluşturur.

info(strfmt(“DafaultAccount RecId = %1″ ,

LedgerDefaultAccountHelper::getDefaultAccountFromMainAccountRecId(mainAccount.RecId)));

// Bir MainAccount ait defaultAccount varsa getirir yoksa oluşturur.

info(strfmt(“DafaultAccount RecId = %1″ ,

LedgerDefaultAccountHelper::getDefaultAccountFromMainAccountId(mainAccount.MainAccountId)));

// Bir LedgerDimension’daki boyutları yanzı DimensionAttributeValueSet recId sini veriri.

info(strfmt(“DafaultDimension RecId = %1″ ,

LedgerDimensionFacade::getDefaultDimensionFromLedgerDimension(dimAttrValueComb.RecId)));

// LedgerDimension’a ait DefaultAccount’u verir.

info(strfmt(“DefaultAccount  RecId = %1″ ,

LedgerDefaultAccountHelper::getDefaultAccountFromLedgerDimension(dimAttrValueComb.RecId)));

// LedgerDimension’a ait MainAccount’u verir.

info(strfmt(“MainAccount  Name = %1″ ,

LedgerDimensionFacade::getMainAccountFromLedgerDimension(dimAttrValueComb.RecId).Name));

// LedgerDimension’a ait MainAccount’u verir.

info(strfmt(“MainAccount  RecId = %1″ ,

LedgerDimensionFacade::getMainAccountRecIdFromLedgerDimension(dimAttrValueComb.RecId)));

// LedgerDimension’a ait AccountNum’u verir.

info(strfmt(“CustTable AccountNum = %1″ ,

LedgerDynamicAccountHelper::getAccountNumberFromDynamicAccount( dimAttrValueCombDefaultAccount.RecId)));

}

}

Resim-1

İlerleyen yazılarda Ax2012 ile aradaki farkları açıklayan yazılar yazmaya devam edeceğim.

Selamlar.

www.fatihdemirci.net

TAGs: Microsoft Life Cycle Services, LCS, Azure, Azure DevOps, DefaultAccount, LedgerDimension, Microsoft Dynamics 365, MsDyn365FO, MsDyn365CE, MsDyn365, Dynamics 365 ERP, Dynamics 365 CRM

Dynamics 365 Finance and Operations SQL Insights?

Bu yazıda Dynamics 365 Finance and Operations SQL Insights hakkında kısa bir tanıtım yapmaya çalışacağım. MSDyn365FO bir bulut servisi olduğu için sunucularına direk ulaşımımız yok. Dolayısıyla SQL e direk bağlanamıyoruz. Ancak SQL üzerinden yapılması ve kontrol edilmesi gereken birçok işlem var. Bunları LCS üzerinden SQL Insights ile yapabilirsiniz. Nerden ulaşırız neler yapabiliriz hızlıca bakalım.

LCS’e giriş yaptıktan sonra Production ortamının detaylarını Full details ile açıyoruz.

Resim-1

Açılan detay sayfasının sonunda Environment Monitoring’e tıklıyoruz.

Resim-2

SQL Insights sekmesini açıyoruz. 5 faklı alt sekme var ben Queries ve Actions’tan bahsedeceğim.

Resim-3

Özellikle performans kontrolleri için çalıştırabileceğiniz birçok sorgu var. Bir tanesi çalıştırıp nasıl bir veri oluşturduğunu anlatacağım.

Resim-4

Action kısmında SQL den yapabileceğiniz birçok işlemi yapma imkân var.

Resim-5

İndex oluşturup silebilirsiniz. İstatistikleri güncelleyebilirsiniz. Listede bir çok işlem mevcut.

Resim-6

Örnek olsun diye en maliyetli sorguları veren işlemi çalıştırdım. Parametrelerden istediğiniz seçimleri yapabilirsiniz. Vergi ve fatura ile ilgili iki sorgunun en maliyetli sorgular olduğunu gördüm. Büyük ihtimalle bunlar için uygun index yok veya execution planlarında sorun var. Ayrıntılı inceleyip düzenleme yapmak lazım.

Resim-7

Direk SQL ulaşımımız olmadığı için SQL Insights’ın bu kadar gelişmiş imkânları sunması çok iyi olmuş bir çok işlemi kontrollü bir şekilde buradan yapabilirsiniz. Tabi ki direk SQL’e ulaşmak kadar esnek değil ama buda bir şey.

Selamlar.

www.fatihdemirci.net

TAGs: Microsoft Life Cycle Services, LCS, Azure, Azure DevOps, DefaultAccount, SQL Insights, Microsoft Dynamics 365, MsDyn365FO, MsDyn365CE, MsDyn365, Segmented Entry, Power Automate, Power Apss, Power Virtual Agents, Dynamics 365 nedir, Dynamics 365 ERP, Dynamics 365 CRM

Dynamics 365 Finance and Operations Dynalink nedir?

Bu yazıda Dynamics 365 Finance and Operations veri kaynakları arasında oluşan Dynalink nedir nasıl kullanılır anlatmaya çalışacağım. X++ tabloları ve EDT‘leri otomatik ilişkiler oluşturur ve bunları birçok yerde kullanır. Tablo veya EDT de olan ilişkiler üzerinden Datasource’lar arasında otomatik bir link oluşturur ve bu link sayesinden ilgili kayıtlar filtrelenmiş olur.  Bir örnekle açıklamaya çalışayım.

Aktivite projemden açıklamaya çalışacağım. Aktivite listesi üzerinde masrafları girebildiğim bir form var. Bu sadece MenuItem kullanarak açılan bir form içinde herhangi bir kod yok ama iki tablo arasında ilişki olduğu için ilgili aktiviteye ait masraflar filtrelenerek geliyor.  Burada oluşan linke Dynalink diyoruz.

Resim-1

Eğer tüm masraf girişlerini gösteren başka bir buton eklersem nasıl çalıştırabilirim bakalım. Bunun için ayrı bir form yapmıyorum yeni bir MenuItem oluşturup buton olarak ekliyorum.

Resim-2

Menu Item Button üzerinden bir değişiklik yapmıyorum.

Resim-3

init metodunu ezip aşağıdaki gibi bir kod yazıyorum. Burada önemli olan hangi MenuItem ile açtığımı anlamak için yazdığım kod ve veri kaynağı üzerinden clearDynalink() metodu. Bu sayede Aktiviteler ve Masraflar tabloları arasında oluşan linki kırıyoruz.

Resim-4

Bu yazıda veri kaynakları arasında tablo ve EDT ilişkilerinden kaynaklı oluşan Dynalink nedir nasıl oluşur ve nasıl kaldırılır anlatmaya çalıştım. Birçok durumda oluşan Dynalink sebebiyle formlarda istedikleri veriyi gösteremeyen arkadaşlara destek verdim. Bu konunun çok kolay gözden kaçtığını düşündüğüm için bir yazı hazırlamak istedim. Query altyapısına hâkim olmak iyi bir X++ yazılımcısı olmanın en temel adımlarından biri. Burayla ilgili çalışmak ve mantığını daha iyi anlamak için denemeler yapmak lazım.

Selamlar.

www.fatihdemirci.net

TAGs: Microsoft Life Cycle Services, LCS, Azure, Azure DevOps,  Dynalink, Microsoft Dynamics 365, MsDyn365FO, MsDyn365CE, MsDyn365, Segmented Entry, Power Automate, Power Apss, Power Virtual Agents, Dynamics 365 nedir

Microsoft MVP Oldum!!!

Merhaba,

Mart 2021 itibariyle ilk Microsoft MVP ödülünü aldığım. Çok uzun zamandır beklediğim bir haberdi. 3 yıl önce ilk defa aday gösterilmiştim. O zaman olmamıştı. Bu yıl tekrar aday gösterildim. Normalden uzun süren bir değerlendirme sonucunda ödüle layık görüldüm.

Şu anda Türkiye’de 20 tane aktif MVP var. İş uygulamaları kategorisinde tekim. Bu da bana çok fazla sorumluluk yüklüyor. Zaten halı hazırda bir ekosistem oluşturmak için çabalamaktayım am bu ödülle birlikte daha da motive oldum. Bu süreçte bir çok destek aldım. Herkese tek tek teşekkür etmek istiyorum. Bunlar olmasa bu ödül mümkün olmazdı. Son bir yılda 80’e yakın makale yazdım, 50’nin üzerinde video çektim ve 13 etkinlik düzenledim. Bunlar dışında konferanslar ve mentorluk faaliyetlerine de devam ettim. Çok yoğun bir yıl geçirmiş oldum. Önümüzdeki yılda da bu faaliyetlerime devam edeceğim.

www.fatihdemirci.net

TAGs: Microsoft Life Cycle Services, LCS, Azure, Azure DevOps, DefaultAccount, MVP, Microsoft Dynamics 365, MsDyn365FO, MsDyn365CE, MsDyn365, Segmented Entry, Power Automate, Power Apss, Power Virtual Agents, Dynamics 365 nedir, Dynamics 365 ERP, Dynamics 365 CRM

Dynamics 365 Finance and Operations LedgerDimension ve Segmented Entry nasıl eklenir?

Bu yazıda Dynamics 365 Finance and Operations içinde bir tabloya LedgerDimension nasıl eklenir ve bu alanı formda Segmented entry control ile nasıl kullanıcıya kullandırabiliriz anlatmaya çalışacağım.

LedgerDimension aslında DimensionAttributeValueCombination kaydıdır. LedgerDimension MainAccount ve DefaultDimension’ın bileşimidir.

Öncelikle Tabloya LedgerDimensionAccount EDT sini kullanarak bir alan ekleyelim. Eğer sürükle bırak ile eklerseniz otomatik ilişkiyi oluşturacak. Yoksa elle eklemeniz gerekir.

Resim-1

Sonrasında forma gelip eklediğimiz alanı tasarımda istediğimiz bir yere sürükleyip bırakıyoruz. Segmented Entry oluşuyor. Özelliklerinde resimde görünen tanımları yapmalıyız. Özellikle Controller Class çok önemli.

Resim-2

Derleyip çalıştırdığımızda formda alanımızı göreceğiz. DefaultAccount ile aradaki farkı görebilirsiniz. Sadece Main Account değil boyutlarda geliyor ve bir kombinasyon oluşturuluyor.

Resim-3

Oluşan verileri Sql üzerinden incelediğimizde aradaki fark çok net bir şekilde ortaya çıkıyor.

Resim-4

Bu yazıda LedgerDimension nedir ve nasıl eklenir anlatmaya çalıştım. Sonraki yazılarımda bunları kodda nasıl kullanıp birbirlerine çeviriyoruz anlatmaya devam edeceğim.

Selamlar.

www.fatihdemirci.net

TAGs: Microsoft Life Cycle Services, LCS, Azure, Azure DevOps, DefaultAccount, LedgerDimension, Microsoft Dynamics 365, MsDyn365FO, MsDyn365CE, MsDyn365, Segmented Entry, Power Automate, Power Apss, Power Virtual Agents, Dynamics 365 nedir, Dynamics 365 ERP, Dynamics 365 CRM