History Of AX

Merhaba

Çok beğendiğim bende nostanji yaşatan iki makaleyi sizinle paylaşmak istiyorum.

History of AX

Technical History of Dynamics AX – From Axapta 3.0 to AX2012

Axapta’yı ilk olarak 2006 yılında Turkmedya firmasında tanıdım. Axapta 3.0 kullanılıyordu. Ben başladığımda proje canlıya geçmişti. Destek ve yeni geliştirmeler ile giriş yaptığım AX dünyasında 12 yılı devirdim. Hayatım üzerinden bu kadar etkisi olacağını tahmin edemezdim.

Selamlar.

XML’de metin değiştirmek

Merhaba

Ax ta E-Fatura süreçleriyle uğraşmayan yoktur. Biz SimpleUBL adında bir eklentiyle hem XML hemde UBL oluşturuyoruz. Şöyle bir ihtiyaç  oldu bazı durumlarda bir alanın verisinin değiştirilmesi gerekiyor. Örneğin matbu irsaliye numarası. Bunun  için gerekli tablolarda veriyi güncelleyip sonra E-Fatura UBL ini oluşturan kodu tekrar çağırınca direk güncelliyor. Ancak bu işlem biraz yavaş çok fazla kontrol ve  işlem yapıyor. Bense çok basit sadece bir alanı değiştirmek istiyorum ve çok hızlı  olması gerekiyor.  Bunu  için str fonksiyonlarından faydalanarak bir metot yazdım. Umarım  işinize yarar.

Örnek UBL in bir parçası  şöyle:


<?xml version="1.0" encoding="utf-8"?>
  <cbc:UBLVersionID>2.1</cbc:UBLVersionID>
  <cbc:CustomizationID>TR1.2</cbc:CustomizationID>
  <cbc:ProfileID>TEMELFATURA</cbc:ProfileID>
  <cbc:ID>YTD20182222225</cbc:ID>
  <cbc:CopyIndicator>false</cbc:CopyIndicator>
  <cbc:UUID>E01C17F9-B13B-4Df5E-BB5A-92F52E17ff3AB0</cbc:UUID>
  <cbc:IssueDate>2018-09-01</cbc:IssueDate>
  <cbc:IssueTime>06:43:11.0000000+03:00</cbc:IssueTime>
  <cbc:InvoiceTypeCode>ISTISNA</cbc:InvoiceTypeCode>
  <cbc:Note>Yazıyla :  TRY TRY</cbc:Note>
  <cbc:Note>TOPLAMADET: 1 / 22,00 </cbc:Note>
  <cbc:LineCountNumeric>1</cbc:LineCountNumeric>
  <cac:OrderReference>
    <cbc:ID>YKS022222222</cbc:ID>
    <cbc:IssueDate>2018-08-08</cbc:IssueDate>
    <cbc:IssueTime>07:25:31.0000000+03:00</cbc:IssueTime>
  </cac:OrderReference>
  <cac:DespatchDocumentReference>
  <cbc:ID>SVK000343433</cbc:ID>
    <cbc:IssueDate>2018-09-01</cbc:IssueDate>
  </cac:DespatchDocumentReference>
  <cac:Signature>
    <cbc:ID schemeID="VKN_TCKN">11111111</cbc:ID>
    <cac:SignatoryParty>

Ben ‘cbc:id SVK000343433 cbc:id’ buradaki SVK000343433 bu numarayı değiştirmek istiyorum.

Bir kaç zorluk var. Birincisi bu numaranın uzunluğu sabit değil.

İkincisi Her bir satır yeni satir karakteriyle ayrılmış durumda.


static void FD_UpdateXml(Args _args)
{
    EInvoiceTable  eInvoiceTable;
    str     str1;
    str     str2;
    str     str3;
    int     i ,j;
    num     packingSlipId = "FD000002";
    ;

    ttsBegin;

    select forUpdate eInvoiceTable
        where eInvoiceTable.RecId  == 5637292931;

    str3 = strFmt("<cbc:ID>%1</cbc:ID>" , DmrPackipackingSlipIdngSlipId ); // Değiştireceğim metin

    i = strScan( eInvoiceTable.InvoiceXml , // İrsaliye numrasının başladığı yeri tespit ediyorum.
        "<cac:DespatchDocumentReference>" , 0,
        strLen(eInvoiceTable.InvoiceXml));

    str1 = subStr( eInvoiceTable.InvoiceXml ,  i +31  , 50);
    // Karakteri sayip  ilerletiyorum ve 50 karakterlik bir parçayı alıyorum
    //str1  böyle bir string oluyor <cbc:ID>YKS000017102</cbc:ID>   <cbc:IssueDa

    j = strScan( str1 ,"</cbc:ID>" , 0, strLen(str1)); // Numaranın bitişini tespit ediyorum.
    str2 = subStr( eInvoiceTable.InvoiceXml ,  i +31  ,j+8);  // Değiştirmek istediğim metne ulaşıyorun
    // str2 <cbc:ID>YKS000017102</cbc:ID> bu oldu

    eInvoiceTable.InvoiceXml = strReplace(eInvoiceTable.InvoiceXml ,str2 ,str3);
    // str2 yi str3 ile değiştiriyorum.

    eInvoiceTable.update();

    ttsCommit;
}

Selamlar.

Dynamics Ax UserConnection kullanımı

Merhaba

Ax’ta  ilk versiyonlardan beri transaction (ttsBegin ttsCommit) blokları dediğimiz bir yapı vardır. Veri tutarlılığı için çok önemli olan roleback dediğimiz bir hata durumunda o blokta yapılan işlemlerin tümünü geri almayı sağlayan bir yapıdır. Ax ta çok sıklıkla kullanılır. Veri tabanında yapılacak işlemlerde mutlaka kullanmak gerekir. Benim bu yazıda bahsedeceğim konu bu bloklar arasında bir hata oluştuğunda roleback in dışında tutmak istediğim bir işlemi nasıl sağlayabileceğim. Genelde bu log atma bildirim gönderme gibi işlemler olur.  Bir örnekle açıklamaya çalışayım.  Eski sistemden satış verilerinin geldiği ve axta sipariş oluşturulup deftere nakledinlen bir yapı olsun.  Herhangi bir sebepten sipariş  oluşturulamadığında bir log tablosuna kayıt atmak istiyorum.   Örnek kodlar şöyle  olsun:

server static void FD_UserConnection1(Args _args)
{
    DmrSalesOrderHeader DmrSalesOrderHeader = DmrSalesOrderHeader::findByRecid(325355554);
    DmrSalesOrderLine   DmrSalesOrderLine;
    ;
    ttsBegin;
    try
    {
        // Sipariş başlığı oluşturma kodu
        while select DmrSalesOrderLine
            where DmrSalesOrderLine.OrderId == DmrSalesOrderHeader.OrderId
        {
            // Sipariş satırı oluşturma kodu
            throw error("hata");
        }
    }
    catch
    {
        DmrExceptionTable::findOrCreate("Satır oluşturulurken bir hata oluştu.", "DmrCreateSalesOrder",
                DmrSalesOrderHeader.recid,DmrSalesOrderHeader.tableId,DmrSalesOrderHeader.recversion);
    }

    ttsCommit;

}

Bu metod sipariş oluşturmaya çalışırken herhangi bir satırda hata veriyor. Bizde hata verdiğini yakalayıp hangi kayıttan sipariş oluştururken hata verdiğini bir tabloya yazmaya çalışıyoruz. Normalde hata verdiği ve tts blokları olduğu için bizim yazmaya çalıştığımız veriyi de geri alacaktı ancak UserConnection ve UnitOfWork kullanarak bunu engelliyoruz. Metot aşağıda.

static server void findOrCreate(    str1260          _exceptionDetail,
                                    str 100          _className,
                                    RefRecId         _RefRecId,
                                    RefTableId       _RefTableId,
                                    RefRecId         _RefRecVersionId)
{
    DmrExceptionTable   DmrExceptionTable;
    UserConnection      UserConnection;
    UnitOfWork          UnitOfWork;
    ;

    UserConnection = new UserConnection();
    UserConnection.ttsbegin();
    UnitOfWork = new UnitOfWork();

    DmrExceptionTable.ExceptionDetail       = _exceptionDetail;
    DmrExceptionTable.ClassName             = _className;
    DmrExceptionTable.RefRecId              = _RefRecId;
    DmrExceptionTable.RefTableId            = _RefTableId;
    DmrExceptionTable.RefRecVersionId       = _RefRecVersionId;

    UnitOfWork.insertonSaveChanges(DmrExceptionTable);
    UnitOfWork.saveChanges(UserConnection);

    UserConnection.ttscommit();

}

Selamlar.

Ax’ta yeni bir iş akışı ataması oluştuğunda aynı kullanıcının bu iş akışı için daha önce onayı var mı tespit etmek

Merhaba

İş akışında yeni bir atama oluştuğunda atanan kişinin aynı iş akışında daha önce onayı var mı diye kontrol etmek için aşağıdaki metodu kullanabilirsiniz. Bu metodu WorkflowTrackingTable’ a yazdım ama farklı bir yere de yazılabilir. İş akışı altyapısı biraz karışık standart yapıyı değiştirirken dikkatli olmakta fayda var.


Boolean dmrExistApprove()
{
    boolean ret = false;

    WorkflowTrackingStatusTable     workflowTrackingStatus;
    WorkflowTrackingStatusTable     workflowTrackingStatusExist;
    WorkflowTrackingTable           workflowTrackingTable;
    ;

    workflowTrackingStatusExist = WorkflowTrackingStatusTable::findRecId(this.WorkflowTrackingStatusTable);

    select firstOnly RecId, User from workflowTrackingTable
    exists join workflowTrackingStatus
    where  workflowTrackingTable.WorkflowTrackingStatusTable == workflowTrackingStatus.RecId
        && workflowTrackingTable.TrackingType       == WorkflowTrackingType::Approval
        && workflowTrackingTable.User               == this.User
        && workflowTrackingTable.RecId              != this.RecId
        && workflowTrackingStatus.ContextRecId      == workflowTrackingStatusExist.ContextRecId
        && workflowTrackingStatus.InstanceNumber    == workflowTrackingStatusExist.InstanceNumber
        && workflowTrackingStatus.ContextTableId    == workflowTrackingStatusExist.ContextTableId
        && workflowTrackingStatus.TrackingStatus    != WorkflowTrackingStatus::Cancelled ;

    if(workflowTrackingTable.recid)
    {
        ret = true;
    }

    return ret;
}

Selamlar.

İş akışı onayını koddan yapmak

Merhaba

Ax2012′de iş akışını onaylamak için aşağıdaki örnek kodu kullanabilirsiniz.


static void FD_WorkflowWorkItemTable(Args _args)
{

    WorkflowWorkItemTable WorkflowWorkItemTable;
    ;

    WorkflowWorkItemTable = WorkflowWorkItemTable::findRecId(123434343);

    if(WorkflowWorkItemTable)
    {
        ttsBegin;
        WorkflowWorkItemActionManager::dispatchWorkItemAction(
           WorkflowWorkItemTable, // Work item record for which the action is being taken
           strFmt("Auto Approve by %1 ", curUserId() ), // Comment associated with this action
           curUserId(), //The target user of the action
           WorkflowWorkItemActionType::Complete,//The work item action type to take
           "PurchTableApprovalApprove", // The name of the menu item from which the action originated
           false // Flag denoting if this request originated from web or rich client
           );

        ttsCommit;
    }
}

Selamlar.

Yazılım uzamanı iş ilanı

Merhaba ,

Dmr’de birlikte çalışmak için yazılım uzmanı arayışımız var. İlana buradan ulaşabilirsiniz. İlgelenenler benimle iletişime geçebilirler.

Selamlar.

Satınalma talep konsolidasyonu (PurchReqConsolidation) formu performans sorunu

Merhaba,

Bir müşterimizde Satınalma talep konsolidasyonu formu çok geç açılıyordu. Öncelikle sorunun ne olduğunu anlamadım. Formun veri kaynağında yavaşlığa sebep olacak bir tablo yoktu. Indexler düzgün bağlantılarda sorun yok. Formun açılışına yazılmış bir kod yok. Bir süre inceledikten sonra Partları gördüm. Partların kodunu inceleyince sorunu hemen tespit ettim.

public void linkActive()
{
    PurchReqConsolidationLine   purchReqConsolidationLine;
    PurchReqLine                purchReqLine;

    purchReqConsolidation = element.args().record();

    super();

    delete_from purchReqTmpConsolidationPart;

    while select purchReqConsolidationLine
    {
        purchReqLine = PurchReqLine::find(purchReqConsolidationLine.PurchReqLineID);

        purchReqTmpConsolidationPart.PurchReqConsolidationLine = purchReqConsolidationLine.RecId;
        purchReqTmpConsolidationPart.PurchReqConsolidationID   = purchReqConsolidationLine.PurchReqConsolidationId;
        purchReqTmpConsolidationPart.AmountBeforeConsolidation = purchReqConsolidationLine.calcAmountBeforeConsolidation();
        purchReqTmpConsolidationPart.AmountAfterConsolidation  = purchReqConsolidationLine.calcAmountAfterConsolidation();
        purchReqTmpConsolidationPart.VendAccount               = purchReqConsolidationLine.NewVendor;
        purchReqTmpConsolidationPart.setDataAreaForModifiedField(fieldNum(PurchReqTmpConsolidationPart, VendAccount), purchReqLine.buyingLegalEntity2DataArea());
        purchReqTmpConsolidationPart.CurrencyCode              = purchReqLine.CurrencyCode;
        purchReqTmpConsolidationPart.doInsert();
    }

    this.executeQuery();
    this.research();
}

Part formlarından biri olan PurchReqConsolidationPartByVendor formunun LinkActive metoduna yazılan koddaki temel hatayı hemen anlamışsınızdır. join yazılması gereken yerde find kullanılmış. Partları aktif olarak kullanmadığı için bu formdan kaldırıp formun çok daha hızlı açılmasını sağladım.

Daha vahim bir durum Satınalma talep konsolidasyonu formundan bir buton ile açılan PurchReqConsolidationAddLine formunda vardı.

PurchReqConsolidationAddLine Formunun PurchReqLine veri kaynağının init metodunda aşağıdaki kod mevcut.

public void init()
{
    PurchReqLine            tmpPurchReqLine;
    PurchReqTable           tmpPurchReqTable;
    QueryBuildRange         qbr;

    super();

    qbr = purchReqLine_ds.query().dataSourceTable(tableNum(PurchReqLine)).addRange(fieldNum(PurchReqLine,RequisitionPurpose));
    qbr.value(SysQuery::value(RequisitionPurpose::Consumption));
    qbr.status(RangeStatus::Locked);

    qbr = purchReqLine_ds.query().dataSourceTable(tableNum(PurchReqLine)).addRange(fieldNum(PurchReqLine,PurchLineCreated));
    qbr.value(SysQuery::value(NoYes::No));
    qbr.status(RangeStatus::Locked);

    qbr = purchReqLine_ds.query().dataSourceTable(tableNum(PurchReqLine)).addRange(fieldNum(PurchReqLine,RequisitionStatus));
    qbr.value(queryValue(PurchReqRequisitionStatus::Approved));
    qbr.status(RangeStatus::Locked);

    purchReqLine.setTmp();

    while select tmpPurchReqLine
            join RecId, SourceDocumentHeader   from tmpPurchReqTable
           where tmpPurchReqLine.PurchReqTable            == tmpPurchReqTable.RecId   &&
                 tmpPurchReqLine.IsPreEncumbranceRequired != UnknownNoYes::Unknown
    {
        if (PurchReqPurchaseOrderGenerationRule::canConsolidateStatic(tmpPurchReqLine) &&
            !PurchReqConsolidationLine::findByReqLineId(tmpPurchReqLine.RecId))
        {
            purchReqLine.data(tmpPurchReqLine.data());

            // Since tempory table will overwrite the RecId value, so assign RecId into an unused field
            purchReqLine.AddressRefRecId = tmpPurchReqLine.RecId;

            purchReqLine.doInsert();
        }
    }
}

PurchReqConsolidationLine join yerine if ile kontrol edildiği için bu formun açılması da dakikalar sürüyordu. Kodu aşağıdaki hale çevirince saniyeler içinde açılmaya başladı.

public void init()
{
    PurchReqLine            tmpPurchReqLine;
    PurchReqTable           tmpPurchReqTable;
    purchReqConsolidationLine purchReqConsolidationLine;
    QueryBuildRange         qbr;

    super();

    qbr = purchReqLine_ds.query().dataSourceTable(tableNum(PurchReqLine)).addRange(fieldNum(PurchReqLine,RequisitionPurpose));
    qbr.value(SysQuery::value(RequisitionPurpose::Consumption));
    qbr.status(RangeStatus::Locked);

    qbr = purchReqLine_ds.query().dataSourceTable(tableNum(PurchReqLine)).addRange(fieldNum(PurchReqLine,PurchLineCreated));
    qbr.value(SysQuery::value(NoYes::No));
    qbr.status(RangeStatus::Locked);

    qbr = purchReqLine_ds.query().dataSourceTable(tableNum(PurchReqLine)).addRange(fieldNum(PurchReqLine,RequisitionStatus));
    qbr.value(queryValue(PurchReqRequisitionStatus::Approved));
    qbr.status(RangeStatus::Locked);
    purchReqLine.setTmp();

    while select tmpPurchReqLine
            join RecId, SourceDocumentHeader   from tmpPurchReqTable
           where tmpPurchReqLine.PurchReqTable            == tmpPurchReqTable.RecId   &&
                 tmpPurchReqLine.IsPreEncumbranceRequired != UnknownNoYes::Unknown
        // Dmr FD -->
        notExists join purchReqConsolidationLine
            where tmpPurchReqLine.RecId ==  purchReqConsolidationLine.PurchReqLineID
        // Dmr FD <--

    {
        // Dmr FD -->
        //if (PurchReqPurchaseOrderGenerationRule::canConsolidateStatic(tmpPurchReqLine) &&
           // !PurchReqConsolidationLine::findByReqLineId(tmpPurchReqLine.RecId))
        if (PurchReqPurchaseOrderGenerationRule::canConsolidateStatic(tmpPurchReqLine) )
        // Dmr FD <--
        {
            purchReqLine.data(tmpPurchReqLine.data());

            // Since tempory table will overwrite the RecId value, so assign RecId into an unused field
            purchReqLine.AddressRefRecId = tmpPurchReqLine.RecId;

            purchReqLine.doInsert();
        }
    }
}

İki kod arasındaki farkı şöyle özetleyebilirim. İlk hali bütün PurchReqLine ları dolanıp PurchReqConsolidationLine da olmayanlar için temp kayıt oluşturur.
İkinci hali ise NotExists join ile bağlandığı için zaten sadece PurchReqConsolidationLine olmayan PurchReqLine ları getirecektir. Dolayısıyla çok daha hızlı olacaktır.

Performans ERP yazılımında belkide en çok dikkat edilmesi gereken konulardan biri. Yazdığımız her kodun birilerinin günlük hayatı olduğunu anlayıp mümkün olduğunda performanslı olmasına dikkat etmek gerekiyor. Gördüğünüz gibi çok basit dokunuşlarla çok büyük kazanımlar elde edebiliyorsunuz. Burada standart kodda böyle bir hatayla karşılaşmak beni şaşırttı. Genelde Standart kod çok kalitelidir ama bazen böyle gözden kaçan kodlar da olabiliyor.

Selamlar.

Muhasebe günlük satırlarından bir alanı tedarikçi açık hareketlerine taşımak

Merhaba

Muhasebe günlük satırlarından (LedgerJournalTrans) bir alanı Tedarikçi hareketlerine (VendTrans) ve Tedarikçi açık hareketlerine (VendTransOpen) taşımak için aşağıdaki adımları takip edebilirsiniz.

  1. LedgerJournalTrans.DmrWfId taşımak istediğimiz alan.
  2. VendTrans, VendTransOpen tablolarına ve  CustVendTrans mapine DmrWfId alanı açılır.
  3. LedgerJournalTransDaily formuna DmrWfId alanı eklenip veri girilmesi sağlanır.
  4. VendVoucher clasının post() metoduna  _vendTrans.DmrWfId   =  ledgerJournalTrans.DmrWfId; satırı eklenir.
  5. VendVoucherJournal clasının initCustVendTrans() metoduna vendTrans.DmrWfId = ledgerJournalTrans.DmrWfId; satırı eklenir.
  6. VendVoucher clasının createTransOpen() metoduna  vendTransOpen.DmrWfId  = _custVendTrans.DmrWfId; satırı eklenir.

Bu adımlarla DmrWfId alanını istediğimiz tablolara taşımış olduk.

Selamlar.

CIL Derleme sistematiği

Merhaba

Çok sık karşılaştığım bir sorun var. CIL derlenememesi ve buna bağlı olarak SSRS, Küp ve servislerin çalışmaması.  CIL sorunu çözmek için aşağıdaki adımları uygularsanız büyük oranda sorununuz çözülecektir.

  1. Bütün AOS’ları durdurun.
  2. c:\Program Files\Microsoft Dynamics AX\60\Server\InstanceName\bin\XppIL klasörünü silin. (Silmek uzun sürebiliyor klasörün adını değiştirip yedek almak daha mantıklı)
  3. Model veritabanındaki SysXppAssembly tablosunun bütün kayıtlarını silin.
  4. İşlem yapılacak AOS’u açın.
  5. Paralel derleme ile işlem yapılacak AOS’u derleyin. Paralel derleme için bakınız.
  6. C:\Program Files\Microsoft Dynamics AX\60\Server\DynamicsAX_Live\Log\AxCompalieAll.html dosyasından hataları kontrol edin. Tablo, Class ve güvenlik nesnelerinde hata olmamalı. Form ve job lardaki hatalar CIL için önemli değil.
  7. AOS’u yeniden başlatın.
  8. Ful CIL derleyin.
  9. Diğer AOS’larin XppIL klasörlerini silin.
  10. Diğer AOS ları başlatın.

CIL hatası hala devam ediyorsa Google veya tecrübesinde istifade edeceğiniz birine danışın :)

Selamlar.

Ax2012 Paralel derleme

Merhaba

Bazı durumlarda Ax komple derleme ihtiyacı oluyor. Bu durumda AOT’yi açıp tümünü derlemeye kalkarsanız çok uzun sürdüğünü görürsünüz. Bunu yerine Command Prompt’tan paralel derleme yapabilirsiniz. Sunucuya bağlı olarak 30 ile 50 dakika arasında derleme işlemi bitecektir.

Paralel derleme için öncelikle Command Prompt’u açıp aşağıdaki klasöre geçmelisiniz. Bu işlemi ASO’un kurulu olduğu sunucuda yapmalısınız. Kuruluma göre klasör değişebilir.

cd “C:\Program Files\Microsoft Dynamics AX\60\Server\DynamicsAX_Live\bin”

Daha sonra aşağıdaki kodu çalıştırmalısınız.

axbuild.exe xppcompileall /s=01 /altbin=”C:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin” /workers=12

S=01  AOS numarasıdır sizde farklı olabilir. Server Configuration Utility’den bakabilirsiniz.

Worker=12 kaç işlemin aynı anda çalışacağını gösterir. Kendi sunucunuzdaki  işlemcilere göre göre değiştirebilirsiniz.

Selamlar.

Page 1 of 23123451020...Last »