AX 2012 R3 SSRS reports to multi select in parameter and functionality of multivalued filtration for SSRS report dialog

In this walk-through , I will give an overview of multi select lookups with filtration of lookup values based on another multi select lookup.  There was requirement of five parameters for a report including: from dateTo DateProject groupsProject names and customer of selected projects as shown below :
In following sections I will show you the implementation of the multi select lookups step by step:   
1.       Creating UI Builder class
Create a class and extend it with SysOperationAutomaticUIBuilder
class SL_InvoiceDetailsUIBuilder extends SysOperationAutomaticUIBuilder
{
    //Dialog fields declaration
    DialogField                             dfprojGroup, dfprojName, dfcustName, dfFromDate, dfToDate;
    container                               ProjGroupsContainer, ProjIdContainer;
    //Multiselect grid declaration
    SysLookupMultiSelectGrid     msCtrlProjGroups, msCtrlProjNames, msCtrlCustNames;
    SL_InvoiceDetailsContract      contract;
}
Override the build method to add dialog fields as follows:
public void build()
{
 contract               = this.dataContractObject();
dfFromDate          = this.addDialogField(methodStr(SL_InvoiceDetailsContract, parmFromDate), contract);
dfToDate              = this.addDialogField(methodStr(SL_InvoiceDetailsContract, parmToDate), contract);
dfprojGroup         = this.addDialogField(methodStr(SL_InvoiceDetailsContract, parmProjGroups), contract);
dfprojName         = this.addDialogField(methodStr(SL_InvoiceDetailsContract, parmProjNames), contract);
dfcustName         = this.addDialogField(methodStr(SL_InvoiceDetailsContract, parmCustNames), contract);
}
Override the PostBuild method to register the multi select lookups with lookup events as follows:
public void postBuild()
{
    super();
    //fromDate
dfFromDate =     this.bindInfo().getDialogField(this.dataContractObject(), methodStr(SL_InvoiceDetailsContract, parmfromdate));
    // to date
dfToDate  = this.bindInfo().getDialogField(this.dataContractObject(), methodStr(SL_InvoiceDetailsContract, parmtodate));
    //Project Group
 dfprojGroup   = this.bindInfo().getDialogField(contract, methodStr(SL_InvoiceDetailsContract,      parmProjGroups));
 dfprojGroup.registerOverrideMethod(methodStr(FormStringControl, lookup), methodStr(SL_InvoiceDetailsUIBuilder, ProjGroupLookup), this);
       if (dfprojGroup){
        dfprojGroup.lookupButton(2);
       }
    //Project Names
dfprojName   = this.bindInfo().getDialogField(contract, methodStr(SL_InvoiceDetailsContract, parmProjNames));
dfprojName.registerOverrideMethod(methodStr(FormStringControl, lookup), methodStr(SL_InvoiceDetailsUIBuilder, ProjNamesLookup), this);
      if (dfprojName){
        dfprojName.lookupButton(2);
      }
    //Customers Name
dfcustName   = this.bindInfo().getDialogField(contract, methodStr(SL_InvoiceDetailsContract, parmCustNames));
dfcustName.registerOverrideMethod(methodStr(FormStringControl, lookup), methodStr(SL_InvoiceDetailsUIBuilder, CustNamesLookup), this);
      if (dfcustName){
        dfcustName.lookupButton(2);
     }
}
There are three multi select lookups Project Groups, Project Names and Customer names, the implementation of Project Group lookup is as follows:
private void ProjGroupLookup(FormStringControl _control)
{
    msCtrlProjGroups = SysLookupMultiSelectGrid::construct(_control, _control);
    msCtrlProjGroups.parmQuery(this.ProjGroupQuery());
    msCtrlProjGroups.run();
}
The following method returns the query for ProjGroupLookup to fill in the data.
private Query ProjGroupQuery()
{
    Query       query;
    query = new query();
    query.addDataSource(tableNum(ProjGroup));
    query.dataSourceTable(tableNum(ProjGroup)).addSelectionField(fieldNum(ProjGroup, ProjGroupId));
    query.dataSourceTable(tableNum(ProjGroup)).addSelectionField(fieldNum(ProjGroup, Name));
    return query;
}
Implementation of Project names lookup:
private void ProjNamesLookup(FormStringControl _control)
{
    msCtrlProjNames = SysLookupMultiSelectGrid::construct(_control, _control);
    msCtrlProjNames.parmQuery(this.ProjNameQuery());
    msCtrlProjNames.run();
}
The following method returns the query for ProjNamesLookup to fill in the data; the project group range is also applied to the query that is selected on the parameter screen.
private Query ProjNameQuery()
{
    Query            query;
    int              counter;
    query = new query();
    query.addDataSource(tableNum(ProjTable));
    if(msCtrlProjGroups){
        //container is filled with selected project groups on the parameter screen
        ProjGroupsContainer = msCtrlProjGroups.getSelected();
    }
    //Loop over the container values and apply range to query
    for(counter = 1; counter <= conLen(ProjGroupsContainer); counter++)
    {
query.dataSourceTable(tableNum(ProjTable)).addRange(fieldNum(ProjTable,     ProjGroupId)).value(queryValue(ProjGroup::sl_findRecId(conPeek(ProjGroupsContainer , counter)).ProjGroupId));
    }
    query.dataSourceTable(tableNum(ProjTable)).addSelectionField(fieldNum(ProjTable, ProjId));
    query.dataSourceTable(tableNum(ProjTable)).addSelectionField(fieldNum(ProjTable, Name));
    return query;
}
Implementation of Customer names lookup:
private void CustNamesLookup(FormStringControl _control)
{
    msCtrlCustNames = SysLookupMultiSelectGrid::construct(_control, _control);
    msCtrlCustNames.parmQuery(this.CustNameQuery());
    msCtrlCustNames.run();
}
The following method returns the query for CustNamesLookup to fill in the data; only those customers are filled whose projects are selected in project names parameter.
private Query CustNameQuery()
{
    Query  query;
    int    counter;
    query = new query();
    query.addDataSource(tableNum(CustTable));
    query.dataSourceTable(tablenum(CustTable)).addDataSource(tableNum(DirPartyTable));
    if(msCtrlProjNames)
    {
        //container is filled with selected projects on the parameter screen
        ProjIdContainer = msCtrlProjNames.getSelected();
    }
    //Loop over the container values and apply range to query
    for(counter = 1; counter <= conLen(ProjIdContainer); counter++)
    {
        query.dataSourceTable(tableNum(CustTable)).addRange(fieldNum(CustTable, AccountNum)).
        value(queryValue(ProjTable::findRecId(conPeek(ProjIdContainer, counter)).CustAccount));
    }
    query.dataSourceTable(tableNum(CustTable)).addSelectionField(fieldNum(CustTable, AccountNum));
    //Below relation is applied to get customer name along with customer account
    query.dataSourceTable(tablenum(DirPartyTable)).joinMode(JoinMode::InnerJoin);
    query.dataSourceTable(tablenum(DirPartyTable)).relations(true);
    query.dataSourceTable(tablenum(DirPartyTable)).addSelectionField(fieldNum(DirPartyTable,  Name));
    return query;
}
This completes my implementation of multiselect lookups for SSRS report, remember to comment the super in postRun() method after overriding it, to avoid the exception of RegisterOverrideMethod was called twice for the same object for method ‘lookup’
2.       Contract class
The implementation of contract class for sl_InvoiceDetailsUIBuilder is given below:
Class Declaration:
[
    DataContractAttribute,
    SysOperationContractProcessingAttribute(classstr(SL_InvoiceDetailsUIBuilder)),
    SysOperationGroupAttribute('Date range', literalStr("@SYS329083"),  '1',FormArrangeMethod::HorizontalFlushRight),
    SysOperationGroupAttribute('Criteria', literalStr("Criteria"),'2')
]
class SL_InvoiceDetailsContract
{
    FromDate    fromDate;
    ToDate      toDate;
    List        projGroup, projName, Customers;
}
From Date data contract methods:
[
    DataMemberAttribute('FromDate'),
    SysOperationLabelAttribute(literalStr("@SYS329084")),
    SysOperationGroupMemberAttribute('Date range'),
    SysOperationDisplayOrderAttribute('1')
]
public FromDate parmFromDate(FromDate _fromDate = fromDate)
{
    fromDate = _fromDate;
    return fromDate;
}
To Date data contract methods:
[
    DataMemberAttribute('ToDate'),
    SysOperationLabelAttribute(literalStr("@SYS329086")),
    SysOperationGroupMemberAttribute('Date range'),
    SysOperationDisplayOrderAttribute('2')
]
public ToDate parmToDate(ToDate _toDate = toDate)
{
    toDate = _toDate;
    return toDate;
}
Project Group data contract methods:
[
    DataMemberAttribute("ProjGroupList"),
    AifCollectionTypeAttribute("ProjGroupList", Types::String),
    SysOperationLabelAttribute(literalStr("Project Groups")),
    SysOperationGroupMemberAttribute('Criteria'),
    SysOperationDisplayOrderAttribute('1')
]
public List parmProjGroups(List _projGroups = projGroup)
{
    projGroup = _projGroups;
    return projGroup;
}
Project Names data contract methods:
[
    DataMemberAttribute("ProjNameList"),
    AifCollectionTypeAttribute("ProjNameList", Types::String),
    SysOperationLabelAttribute(literalStr("Project Names")),
    SysOperationGroupMemberAttribute('Criteria'),
    SysOperationDisplayOrderAttribute('2')
]
public List parmProjNames(List _projNames = projName)
{
    projName = _projNames;
    return projName;
}
Customer Names data contract methods:
[
    DataMemberAttribute("CustNameList"),
    AifCollectionTypeAttribute("CustNameList", Types::String),
    SysOperationLabelAttribute(literalStr("Customers")),
    SysOperationGroupMemberAttribute('Criteria'),
    SysOperationDisplayOrderAttribute('3')
]
public List parmCustNames(List _custNames = Customers)
{
    Customers = _custNames;
    return Customers;
}
This completes my data contract class implementation, Below are the few snapshots for demonstration.  
3.       Demonstration
Figure 1 : Multi select Project group parameter
Figure 2: Multi select Project names parameter filtered according to project groups selected
Figure 3: Multi select customer parameter filtered according to project names
Note : This article assumes that you have prior basic knowledge of Data contract class, User Interface (UI) Builder Class and Report Data Provider Class.

No comments:

Post a Comment