Blog Listem

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.