23 Mayıs 2022 Pazartesi

365 F&O - Date değişken kullanarak conditional breakpoint

F&O öncesinde şartlı breakpoint yapabilmek için aşağıdaki gibi bir kod yazmamız gerekiyordu:


if (cnt>4)

    breakpoint;

F&O ile birlikte geliştirme tamamen VS içinden olduğu için conditional breakpointe kavuşmuş olduk:



 

 


 

Eğer şartımız bir date alan ise mkDate kullanmak işe yaramıyor malesef. Bir forumda Martin Drab'dan öğrendiğim üzere burada C# fonksiyonları kullanmamız gerekiyor:

 



 

 

 

27 Kasım 2021 Cumartesi

Dynamics 365 F&O - Debugger saçmalarsa bu bir içiçe çağrı yüzünden olabilir

 AX 2009 ile zamanında bir kodumda bilmeden içiçe sonsuz çağrı yazmıştım. Kodu çalıştırdığımda kilitlenince sonsuz döngüden şüphelendim ve debug etmeye kalktım. Debuggerın kafa karıştırıcı bir şekilde saçma sapan yerlere atladığını gördüm. İnternette kısa bir araştırmadan sonra buna sonsuz bir içiçe döngünün sebep olabileceğini okudum. 

Aynı durumu iki gün önce 365 F&O ile de yaşayınca doğrusu çok şaşırdım. Tamamen yenilenen altyapısıyla derlenmiş kodun Visual Studio debugger ile aynı şekilde saçmalamasını beklemiyordum doğrusu. Bu sefer biraz daha farklı saçmalıyordu ama yine aynı davranışa benzerdi (2009'da tamamen rastgele satırlara gidip duruyordu, bunda yine aniden alakasız bir koda gitti, ama orada döndü durdu). Tabii bu sefer yıllar önce yaşadığım tecrübeyi hatırlayarak daha çabuk gittim sonuca.

Yani demem o ki, debugger tuhaf tuhaf yerlere atlıyorsa içiçe sonsuz çağrı hatasını yapmış olabilirsiniz (Tabii bir danışman arkadaşınız DEV makinesinde test yapıyor da olabilir).

16 Eylül 2021 Perşembe

Dynamics 365 F&O - Extensionslara bir bakış

 Daha önce birkaç AX upgrade projesinde bulundum. Katmanlı mimari malesef upgrade projelerinde çok baş ağrıtıyor. Extensions yaklaşımı ile bu çözülmüş görünüyor. 

Size ait olmayan tablo/Data entitylere field/indeks/relation... veya formlara kontrol eklemek istediğinizde direk bir extensions oluşturuyorsunuz:

 

 

Enumlar için de aynı şekilde extensions oluşturulurken Enumlarda numara yerine isimden gidilmesi de çok iyi bir çözüm olmuş. Daha önce malesef sistem enumlarında sayısal boşluk bırakmadan eklenen alanlar upgrade esnasında sorun oluyordu. Bu nasıl problem oluyordu:

Mesela bir sistem enum 1-2-3-4-5 diye gidiyor. Bir developer yeni element eklerken 20-30 gibi değer seçmemiş, 6 seçmiş. Microsoft da yeni versiyonda 6'yı ekleyeyim artık demiş. :)

Kod kısmında da yine kolaylıklar var. İlgili nesneyi açıp  metodun üzerine gelip sağ mouse ile extensions için gereken şablonu kopyalayabilirsiniz:


Bir class metodundan extension:

 [PostHandlerFor(classStr(LedgerJournalCheckPost), methodStr(LedgerJournalCheckPost, run))]
    public static void LedgerJournalCheckPost_Post_run(XppPrePostArgs args)
    {
        LedgerJournalCheckPost      post = args.getThis() as                         LedgerJournalCheckPost;

        LedgerJournalTable          journalTable;
 

        journalTable = post.parmLedgerJournalTable();

        ...

    }

Bu metodu uygun gördüğünüz bir classa yazıyorsunuz ve artık run metodundan sonra bu metod çalışıyor. Pre-event seçseydik önce çalışacaktı.

Bu örnekte de bir tablonun ondeleted eventını extend ediyoruz:


 

 [DataEventHandler(tableStr(LedgerJournalTable), DataEventType::Deleted)]
    public static void LedgerJournalTable_onDeleted(Common sender,             DataEventArgs e)
    {

    LedgerJournalTable       ledgerJournalTable = sender as                     LedgerJournalTable;

    ...


Bir tabloya yeni bir metod eklemek istediğimizde durum biraz değişiyor. Tablonun adı ve _Extension eki içeren bir aşağıdaki gibi class yazıyoruz:

[ExtensionOf(tableStr(ABCRCreditTable))]
final class AGC_ABCRCreditTable_Extension
{
    public void updateDefaultDimension()
    {
            this.DefaultDimension = 11111;

    }

}

Veya tablo metodlarından birine ekleme yapmak için yukarıdaki class'a aşağıdaki gibi bir kod yazabilirsiniz. next diğer bağlı metodları çağırmış olursunuz:

  void initfromCreditPosting()
    {
 

..... //your codes here

        next initfromCreditPosting();
    }

Tablo modifiedfield eventına erişim:

[DataEventHandler(tableStr(ABCRCreditTable), DataEventType::ModifiedField)]
    public static void ABCRCreditTable_onModifiedField(Common sender, DataEventArgs e)
    {
        ABCRCreditTable         creditTable = sender as                             ABCRCreditTable;
        ModifyFieldEventArgs    eventArgs = e;
        int                     id = eventArgs.parmFieldId();
        switch(id)
        {
            case fieldNum(ABCRCreditTable,CreditAccount):
                if (ABCRParameters::find().CRCreditAccountType ==                         ABCRAccountType::Vend)
                    creditTable.updateDefaultDimension();
                break;
            case fieldNum(ABCRCreditTable,StrategyNumber):

...

 

LedgerJournalCheckPost  validate methoda bir ekleme yapıyoruz:

 [PostHandlerFor(classStr(LedgerJournalCheckPost), methodStr(LedgerJournalCheckPost, validate))]
    public static void LedgerJournalCheckPost_Post_validate(XppPrePostArgs args)
    {
       LedgerJournalCheckPost   thisClass = args.getThis();
        LedgerJournalTable       ledgerJournalTable =     thisClass.ledgerJournalTable;

....

....

    if (abc)

    {

         args.setReturnValue(false);

         checkfailed("wrong value");

    }


}

 

Form lookup metodu override etmek:

  [FormDataSourceEventHandler(formDataSourceStr(ABCRCreditTable, ABCRCreditTable), FormDataSourceEventType::Initialized)]
    public static void ABCRCreditTable_OnInitialized(FormDataSource sender, FormDataSourceEventArgs e)
    {
        var overrides = new AGC_CreditTableExt();
        sender.object(fieldNum(ABCRCreditTable,                 TradeNumber)).registerOverrideMethod(methodStr(FormDataObject, lookup),
            methodStr(AGC_CreditTableExt, lookupTrade), overrides);
    
    } 

 

Lookup metod içinden form datasource çağırabilmek için aşağıdaki kırmızı işaretli kod gerekiyor:

   public void lookupStrategyInterCompany(FormControl _formControl)//, str _filterStr)
    {
        SysTableLookUp          sysTableLookUp = SysTableLookup::newParameters(tablenum(AGC_LoanStrategy),_formControl);
        Query                   query = new Query();
        QueryBuildDataSource    qbds = query.addDataSource(tablenum(AGC_LoanStrategy));
        FormObjectSet           dataSource =   _formControl.formRun().dataSource("ABCRCreditTable");
        ABCRCreditTable         creditTable = dataSource.cursor();


        query.allowCrossCompany(true);
        query.addCompanyRange(creditTable.CreditICCompany);
        qbds.orderMode(OrderMode::GroupBy);
        sysTableLookup.addLookupfield(fieldnum(AGC_LoanStrategy,StrategyNumber));
        sysTableLookup.parmQuery(query);
        sysTableLookup.performFormLookup();
    }

 Örnekte formun data source OnInitialized eventında yukarıdaki kodun çalışmasını sağlıyoruz. Bu kod ile datasource içindeki tradenumber fieldının lookup metodunu AGC_CreditTableExt classının lookupTrade metodu ile override ediyoruz. Yukarıdaki metodu istediğiniz bir sınıfa yazabilirsiniz. Ben AGC_CreditTableExt içine yazmayı tercih ettim.

Geçenlerde Peter Ramer'in blogundan CancelSuperCall() metodu ile başka bir lookup extensions yöntemi daha olduğunu öğrendim.


ValidateField örneği:

Tablodaki validateField metodu:

   public boolean validateField(FieldId _fieldIdToCheck)
    {
        CustTable            CustTable;
        VendTable            vendTable;
        boolean ret;
    
        ret = super(_fieldIdToCheck);
    
        switch(_fieldIdToCheck)
        {

....

....

....

        }

}


Classa koyduğumuz validateField extension:


[PostHandlerFor(tableStr(ABCRCreditTable), tableMethodStr(ABCRCreditTable, validateField))]
    public static void ABCRCreditTable_Post_validateField(XppPrePostArgs args)
    {
        ABCRCreditTable     creditTable = args.getThis();
        FieldId             fieldId = args.getArg("_fieldIdToCheck");
        boolean             ret = args.getReturnValue();
        AGC_LoanStrategy    strategy;
        AGC_LoanTradeComb   trade;
 
        switch(fieldId)
        {
            case fieldNum(ABCRCreditTable, StrategyNumberInterCompany):
                if (creditTable.CreditICCompany != "")
                {
                    changecompany(creditTable.CreditICCompany)
                    {
                        select firstonly strategy
                            where strategy.StrategyNumber == creditTable.StrategyNumberInterCompany;
                        if (strategy.RecId == 0)
                        {
                            ret = checkFailed(strFmt("@SCM:TableFieldValidation",
                                    creditTable.StrategyNumberInterCompany,fieldPName(ABCRCreditTable,StrategyNumberInterCompany),
                                    tablePName(ABCRCreditTable)));
                        }
                    }
                }
                break;
           }
 
        args.setReturnValue(ret);
    }

Form metodu post event içinden datasource'a ulaşmak:

 [PostHandlerFor(formStr(ABCRCreditTable), formMethodStr(ABCRCreditTable, setInterCompanyFields))]
    public static void ABCRCreditTable_Post_setInterCompanyFields(XppPrePostArgs args)
    {
        formrun o = args.getThis();
                   
        FormObjectSet   ds = o.dataSource("ABCRCreditTable");
        ABCRCreditTable creditTable = ds.cursor();
        ds.object(fieldNum(ABCRCreditTable,TradeNumberInterCompany)).allowEdit(creditTable.SourceOfLoan == AGC_SourceOfLoan::Intercompany);
        ds.object(fieldNum(ABCRCreditTable,StrategyNumberInterCompany)).allowEdit(creditTable.SourceOfLoan == AGC_SourceOfLoan::Intercompany);
       
    }


Bir forma display metod eklemek:

[ExtensionOf(formStr(SalesAgreement))]
final class AGC_SalesAgreement_Extension
{
    display Amount dispRemainAmount()
    {     
       FormRealControl n = this.design().controlName("NetAmount");
       FormRealControl a = this.design().controlName("agreementRemainingRelease");
       
        return n.realValue() * a.realValue();
    }

}

Daha sonra bir form extension oluşturulur. Türü uyumlu bir kontrol eklendiğinde (bu örnekte real kontrol) data method combobox içinde yukarıdaki metodun referansı çıkacaktır.