Sunday, 1 November 2015

Adding FilePath and FileName EDT with Directory Browser on AX Forms

One of the essential requirement usually requested by user is to one time setup of File path and File name for Exporting/Importing various types of data. Similarly I did this for one of my user who wants single time setup of exporting data to particular location.
In following sections I will show how to add FilePath EDT on table with Directory browser on the form.
1) ADD/DROP the FilePath and FileName EDT's on your setup table  , I have used EcoResProductParameter in my case :
Drag and Drop FilePath EDT on table fields as shown below :
Drag and Drop FileName EDT on table fields as shown below :
2) After adding FilePath and FileName fields on table , find your setup form and drop the newly added fields from Form datasource to Form design. See the snapshot below : 
Drag and drop FilePath and FileName Field from Form datasource to design  :
After Adding the fields on form, make the AutoDeclaration property of FilePath field only to yes.
You can open the form from AX and see the directory open icon along with field :
The problem here is that it would not open directory browser by clicking the file browser icon so only adding FilePath EDT would not work, you will need to add the following methods to your form methods node : 
str fileNameLookupFilename()
{
     Filename filepath;
     Filename filename;
     Filename fileType;
     [filepath, filename, fileType] = fileNameSplit(KwdUPCNullFilePath.text()); //KwdUPCNullFilePath is the                                                                                                                                   FilePath field control name
     return filename + fileType;
}
container fileNameLookupFilter()
{
      #File
      Filename filepath;
      Filename filename;
      Filename fileExtention;
[filepath, filename, fileExtention] = Global::fileNameSplit(KwdUPCNullFilePath.text()); //KwdUPCNullFilePath is the                                                                                                                              File Path field control name
     if (!fileExtention)
     {
         fileExtention = #txt;
     }
      return [WinAPI::fileType(fileExtention),#AllFilesName+fileExtention, #AllFilesExt, #AllFilesType];
}

str filenameLookupInitialPath()
{
      return "";
}
str filePathLookupTitle()
{
     return "Select Directory";
}
str fileNameLookupTitle()
{
     return "Select a folder for export";
}
Now Try opening form again and click on directory browser icon it will open the new window to browse directory. 

AX 2012 R3: Multi select lookups with the 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.