Wednesday, 16 September 2020

Multiselect lookup on dialog in UI builder class (Sysoperation) in D365 F&O

 This way of setting Multiselectlookup(Lookup that allows Multiple value selections) can be used on any module (SSRS, Service, Batch or simple server/client based service class) utilizing SysOperation framework. 

Code design would be as follows:- 

1. Data contract class: (just showing relevant parameters, there can be other parameters too). Just to note, container type parameter can also be used in place of List.

[DataContract, SysOperationContractProcessing(ClassStr(TestExpImpUIBuilder))]
class TESTEXPDC extends SysOperationServiceBaseDataContract
{
    List                department;    
    
    [DataMemberAttribute, SysOperationLabel("@SYS850"), AifCollectionTypeAttribute('return', Types::String)]
    public List parmDepartment(List _department = department)
    {
        department = _department;

        return department;
    }
}
2. UI Builder Class: This is most important to build a multi select lookup.
class TestExpImpUIBuilder extends SysOperationAutomaticUIBuilder
{
    DialogField         dialogDepartment;
    SysLookupMultiSelectCtrl    ctrlDepartment;
    
    //Override this to add dialog field
    protected DialogField addDialogField(IdentifierName _methodName, Object _dataContract = this.dataContractObject())
    {
        DialogField dialogField;;

        switch (_methodName)
        {
            case methodStr(TESTEXPDC, parmDepartment):
                dialogDepartment = this.dialog().addField(
                    extendedTypeStr(Description),
                    "@SYS850");
                dialogField = dialogDepartment;
                break;
            default:
            dialogField = super(_methodName, _dataContract);
        }
        return dialogField;
    }

    //Override this
    public void postRun()
    {
        super();
        if (this.dataContractObject() is TESTEXPDC) //if is optional, in my case i was using single UI builder for multiple tasks so required
        {
            this.lookupDepartment();
        }
    }
    
    //to populate lookup
    protected void lookupDepartment()
    {
        TableId                 multiSelectTableNum = tableNum(EcoResCategory);
        Query                   query               = new Query();
        QueryBuildDataSource    qbds                = query.addDataSource(multiSelectTableNum);

        EcoResCategoryHierarchyRole categoryRole;

        select firstonly categoryRole
            where categoryRole.NamedCategoryHierarchyRole == EcoResCategoryNamedHierarchyRole::RetailChannelNavigation;

        qbds.addRange(fieldNum(EcoResCategory, Level)).value(queryValue(3));
        qbds.addRange(fieldNum(EcoResCategory, CategoryHierarchy)).value(queryValue(categoryRole.CategoryHierarchy));
        qbds.addSelectionField(fieldNum(EcoResCategory, Code)); //needed for field display in lookup
        qbds.addSelectionField(fieldNum(EcoResCategory, Name));

        container selectedFields = [multiSelectTableNum, fieldNum(EcoResCategory, Code)]; //irrelevant for lookup but value needed for further use in method calling

        ctrlDepartment = SysLookupMultiSelectCtrl::constructWithQuery(this.dialog().dialogForm().formRun(), dialogDepartment.control(), query, false, selectedFields);
    }


    public void getFromDialog()
    {
        super();
        //keep in mind, multiselect actually brings Recid and works like reference lookup
        if (this.dataContractObject() is TESTEXPDC)
        {
            List        listDepartment  = new List(Types::String);
            container   conDeptt = ctrlDepartment.get();

            Counter conCount = 1;
            Counter conDepLength = conLen(conDeptt);

            while (conCount //use sign for less and equal here// conDepLength)
            {
                listDepartment.addEnd(EcoResCategory::find(conPeek(conDeptt, conCount)).Code);
                conCount++;
            }
            this.dataContractObject().parmDepartment(listDepartment);
            
            //whole list code not needed in case you are using container type parameter
            //this.dataContractObject().parmDepartment(ctrlDepartment.get());
        }
    }
}

3. Service class : Its optional and purely based upon requirement. No lookup code is needed here, just needed for execution type and Contract mapping to further call Task based class.

4. Task class: actual iteration of selected multiselect values would be done here.

class TESTEXPTask
{
    List                listDepartment;
    
    public static TESTEXPTask construct()
    {
        return new TESTEXPTask();
    }
    
    public void run(TESTEXPDC _contract)
    {
        this.processDataforExport();
    }
    
    protected void processDataforExport()
    {
        try
        {
            ttsbegin;
            // any code execution
          
            //Line Creation
            this.createLinesPerDepartment();
            ttscommit;

        }
        catch (Exception::Error)
        {
            exceptionTextFallThrough();
        }
    }
    
    protected void createLinesPerDepartment()
    {
        if (listDepartment && !listDepartment.empty())
        {
            ListEnumerator listEnumerator;

            listEnumerator = listDepartment.getEnumerator();
            while (listEnumerator.moveNext())
            {
                this.methodName(listEnumerator.current());
            }
        }
        else
        {
            //call execution code
            this.methodeName();
        }
    }

and you are done. Just squeeze in your other code like additional parameters and business code and voila!!!

Keep in mind, UI builder class in anyways would be used in similar old manner (overridelookup) for conventional field lookups.

See you in next blog. Happy Daxing (Now D365ing)!!! Please drop a feedback in case of any issues or any other concerns.