16 Eylül 2021 Perşembe

Dynamics 365 F&O - Extensions overview

 I worked a few AX upgrade projects. Unfortunately multi-layer architect is so painful with upgrade projects. Seem this problem is solved with extensions approach.

When you wanted to add fields/indexes/relations... in tables/data entities or controls in forms you create extensions directly:

 

 

Enums doesn't use numbers (at least in development environment) anymore. This's very good for upgrade projects. Unfortunately I lived problems about added elements without any gap to system enums. How it was a problem:

Sample, a system enum goes 1-2-3-4-5 and a developer added a new element. He/she actually should add new element as at least from 20 maybe 30 but added from 6. And in new version Microsoft added 6. :)

In the case of writing code there are some shortcuts. You can open related object and click right  mouse for copy template:


A sample class method 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();

        ...

    }

You can put this method in any class and this method will run after run method. If we selected Pre-event, it would be run before run.

In this case we extend ondeleted of a table:

 

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

    LedgerJournalTable       ledgerJournalTable = sender as                     LedgerJournalTable;

    ...


If we want to add a new method of a table, things change a bit. We need a new class named  Table name and  _Extension word like this:

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

    }

}

 

Or you can write additional code for a table method below in class up. You call chain methods with next keyword:

  void initfromCreditPosting()
    {
 

..... //your codes here

        next initfromCreditPosting();
    }

 

Access to modifiedfield event of a table:

[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):

...

We add extra control on LedgerJournalCheckPost  validate method:

 [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");

    }


}

 

Override form lookup method:

  [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);
    
    } 

 

For call forms datasource from inside Lookup method  we need this red highlighted code:

   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();
    }

 

With this sample we make run code at up in forms data source OnInitialized event. We override datasource tradenumber fields lookUp method with  AGC_CreditTableExt class lookupTrade method.  You can write this code in any class. I choosed AGC_CreditTableExt .

Recently I learned another extension method for override lookups by Peter Ramer's blog by CancelSuperCall() .



ValidateField sample:

ValidateField method in table:

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

....

....

....

        }

}


ValidateField extension which we put in class:


[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);
    }


Access datasource from form method post event:

 [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);
       
    }

 



Adding a display method to a form:

[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();
    }

}

Than create a form extension. When added a control compatible with display method (for this sample a real control) datamethod combobox will show this display method.