Hiding parameter or Dynamics filters on SSRS report dialog at runtime

Description:-

Recently I had a requirement to hide report parameters from report dialog based on the caller menu item. So here is how to do it. I will show you the example of Item transaction summary report. Item transaction summary report in AX 2012 feature pack version has the following report parameters:



There are parameter on the report dialog above which are as follows:
·         From Date
·         To Date
·         Item Id
·         Item Group
·         Dimension

And below is the classDeclaration of the contract class, as shown below:

[DataContractAttribute,
    SysOperationContractProcessingAttribute(classstr(ItemTransactionSummaryUIB))]
class ItemTransactionSummaryContract implements SysOperationValidatable
{
    List  ItemGroupIdList,ListItemId;
    BaseDate FromDate,ToDate;
    NoYesId   IsDiamension;
}

The View group fields comes from ItemTransactionSummaryContract class.

Suppose, we want to hide all the parameter from report dialog and just want to display the dynamics filters on the report. You need to modify the UIBuilder class of your report. For Item transaction summary report, the UI Builder class is ItemTransactionSummaryUIB. The build method needs to be modified and the code in Bold below can be used to hide the whole parameter group from SSRS report dialog:

public void build()
{
    FormBuildGroupControl grp;
    grp = this.dialog().curFormGroup();
    grp.frameType();
    grp.columns(2);
    if (this.controller().parmArgs().menuitemName() == "ItemTransactionSummaryController")
    {
        grp.visible(false);
    }
    super ();
 }

Below is the output, after hiding the whole parameter group:



To hide the dynamic filters from SSRS report dialog based on caller menu item, you need to override the following method in your controller class and return false from this method based on your condition:

ShowQueryValues

Override this method in your controller class and write the following code:

public boolean showQueryValues(str parameterName)
{
    //return false;
    If (this.parmArgs().menuItemName() == menuItemOutputStr("ItemTransactionSummaryController"))
    {
        return false;
    }
    else
    {
        return true;
    }
}

You will see the following output when the report is run:



Only the Printer and Print destination options will be shown which is displayed by default on the reports.
SSRS Report Data Provider class in Ax 2012

SSRS Report Data Provider class in Ax 2012

Description:-

There are multiple methods to develop SSRS reports in Microsoft Dynamics AX 2012. This tutorial will guide you in developing Report Data Provider (RDP) based SSRS reports.

RDP based SSRS Reports are used when complex business logic cannot be achieved using AOT query.

Pre-requisites
·         Microsoft Dynamics AX 2012
·         Visual studio 2012
·         SQL Server Reporting Services (SSRS) must be configured
·         Reporting services extensions must be installed in Dynamics AX

Report Data Provider (RDP) Class

Report Data Provider Class is an X++ class that is used to access and process data for a SSRS report. The RDP class processes the business logic based on a specified parameter and/or query and returns a dataset to the reporting services. In order to create a RDP class in AX, you have to extend that class with SRSReportDataProviderBase. This tells AX that this class will be used by reporting services to process the data.

Two important attributes are used in RDP classes:

1) SRSReportQueryAttribute: specifies which AOT query will be used in this report. If the RDP class uses an AOT query to process data, define this attribute at the beginning of the class.
2) SRSReportParameterAttribute: defines the data contract class that will be used by this report to prompt for parameter values. If the RDP class contains any parameters this define this attribute at the beginning of the class.

Both the attributes are optional. If the report does not use any query or does not want any parameter to filter report data, these attributes do not need to be used.

Data Contract Class

A data contract class is an X++ class which contains parm methods with the DataMemberAttribute defined at the beginning of the method. This class is used to define one or more parameters that will be used in a SSRS report.

Table

An AX table is used as the dataset to store data for the report. The RDP class processes the data and stores it in the table which is then used by a SSRS report to render data.

A table can be a temporary table (InMemory or TempDB) or a regular table, but it is Microsoft best practice to use a temporary table.

The type of temporary table is based upon the performance considerations. InMemory temporary table is used when the data set is small, while TempDB is normally used for larger datasets to improve performance.

Report Data Provider (RDP) Class declaration

If you are passing parameter through contract class.

[SRSReportParameterAttribute(classStr(ItemTransactionSummaryContract))]
class ItemTransactionSummaryDp extends SRSReportDataProviderBase
{
    ItemTransactionSummaryContract  contract;
    ItemTransactionSummary          ItemTransactionSummary;
    BaseDate                        _FromDate,_ToDate;
    NoYesId                         _IsDeamensionEnable;
}

Add a new method and name it getItemTransactionSummary. This method is mandatory because reporting services uses this method to get the table buffer containing the processed data.

The SRSReportDataSetAttribute attribute is used to indicate the temporary table name and also tells the reporting services to use this method to retrieve the processed data.

Write the following code in the method:

[SRSReportDataSetAttribute(tableStr("ItemTransactionSummary"))]
public ItemTransactionSummary getItemTransactionSummary()
{
    select * from ItemTransactionSummary;
    return ItemTransactionSummary;
}

Add a new method and name it processReport. This method contains the business logic and is called by reporting services to generate data.

This method will query item transaction details and fill the temporary table buffer. Write the following code in the method:

/// Processes the SQL Server Reporting Services report business logic
/// This method provides the ability to write the report business logic. This method will be called by
/// SSRS at runtime. The method should compute data and populate the data tables that will be returned
/// to SSRS.
[SysEntryPointAttribute]
public void processReport()
{
    InventTrans                         InventTrans;
    InventTransOrigin                   InventTransOrigin;
    
    //Itemid List Object Start
    List                    list1 = new List(Types::String);
    Query                   queryItemId;
    QueryRun                queryRunItemId;
    InventItemGroupItem     InventItemGroupItem;
    QueryBuildDataSource    qbdsItemId;
    ListIterator            ItemIdListIterator;
    //Itemid List Object end    

    contract            = this.parmDataContract() as ItemTransactionSummaryContract;
    _FromDate           = contract.ParmFromDate();
    _ToDate             = contract.ParmToDate();
    _IsDeamensionEnable = contract.parmDiamension();
    //list              = contract.parmItemGroup();
    list1               = contract.parmItemId();   
    
    //Itemid List start
    if(list1 != null)
    {
        ItemIdListIterator  = new ListIterator(list1);
        queryItemId         = new Query(queryStr(dev_ItemTransSumItemIdQry));
        qbdsItemId          = queryItemId.dataSourceTable(tableNum(InventItemGroupItem));

        while(ItemIdListIterator.more())
        {
            qbdsItemId.addRange(fieldNum(InventItemGroupItem, ItemId)).value(ItemIdListIterator.value());
            qbdsItemId.addRange(fieldNum(InventItemGroupItem, ItemDataAreaId)).value(curExt());
            qbdsItemId.addRange(fieldNum(InventItemGroupItem, ItemGroupDataAreaId)).value(curExt());
            ItemIdListIterator.next();
        }
        queryRunItemId = new QueryRun(queryItemId);
        while(queryRunItemId.next())
        {
            InventItemGroupItem = queryRunItemId.get(tableNum(InventItemGroupItem));

            //Get start
            InventTrans.clear();
            InventTransOrigin.clear();

            while select sum(Qty) from InventTrans
            order by InventTrans.DatePhysical
            group by InventTrans.DatePhysical,InventTrans.StatusReceipt,
                InventTrans.ItemId,InventTransOrigin.RecId,InventTransOrigin.ReferenceCategory,
                InventTransOrigin.ReferenceId,InventTransOrigin.ItemId,
                InventTransOrigin.InventTransId,InventTrans.inventDimId
     join InventTransOrigin
            where InventTrans.InventTransOrigin == InventTransOrigin.RecId
                && (InventTrans.StatusReceipt == StatusReceipt::Purchased ||
                    InventTrans.StatusReceipt == StatusReceipt::Received ||
                    InventTrans.StatusReceipt == StatusReceipt::Arrived ||
                    InventTrans.StatusReceipt == StatusReceipt::QuotationReceipt)
                && (InventTrans.StatusIssue != StatusIssue::Sold ||
                    InventTrans.StatusIssue != StatusIssue::Deducted ||
                    InventTrans.StatusIssue != StatusIssue::Picked ||
                    InventTrans.StatusIssue != StatusIssue::QuotationIssue)
                && InventTrans.DatePhysical != dateNull()
                && InventTransOrigin.ReferenceCategory != InventTransType::InventTransaction
            && InventTrans.DatePhysical >= _FromDate
            && InventTrans.DatePhysical <= _ToDate
            && InventTransOrigin.ItemId == InventItemGroupItem.ItemId
            {
   ItemTransactionSummary.clear();
   ItemTransactionSummary.ItemId         = InventTrans.ItemId;
   ItemTransactionSummary.Name           = EcoResProductTranslation::findByProductLanguage(EcoResProduct::findByDisplayProductNumber(InventTrans.ItemId).RecId,'en-in').Name;
   ItemTransactionSummary.ItemGroup      = inventItemGroupItem::findByItemIdLegalEntity(InventTrans.ItemId).ItemGroupId;
   ItemTransactionSummary.Configuration  = InventDim::find(InventTrans.InventDimId).configId;
   ItemTransactionSummary.InventColorId  = InventDim::find(InventTrans.InventDimId).InventColorId;
   ItemTransactionSummary.InventSizeId   = InventDim::find(InventTrans.InventDimId).InventSizeId;
   ItemTransactionSummary.InventStyleId  = InventDim::find(InventTrans.InventDimId).InventStyleId;
   ItemTransactionSummary.FromDate       = Fromdate;
   ItemTransactionSummary.ToDate         = Todate;
   ItemTransactionSummary.UnitId         = InventTableModule::find(InventTransOrigin.ItemId,ModuleInventPurchSales::Invent).UnitId;
   ItemTransactionSummary.IsDimensionEnable = _IsDeaEnable;
   if(ItemTransactionSummary.validateWrite())
   {
               ItemTransactionSummary.insert();
   }
     }
            //Get End
        }
    }
    //Itemid List end
}

If you are using query based data provider class

Processes business logic based on parameters and a query, and then returns the tables as a dataset for the report. Let me explain all the steps mentioned above with an example.

Step 1: Create a new query and add the datasource as Table.

[SRSReportQueryAttribute (querystr(AOTQueryName)), 
SRSReportParameterAttribute(classstr(ItemTransactionSummaryContract))]
class ItemTransactionSummaryDp extends SRSReportDataProviderBase 
{

}

Add a new method and name it processReport. This method contains the business logic and is called by reporting services to generate data.

[SysEntryPointAttribute(false)] 
public void processReport() 
{ 
    QueryRun       queryRun; 
    Query        query; 
    CustTable       custTable; 
    ItemTransactionSummaryContract  Contract; 
    AccountNum       accountNum; 
    QueryBuildDataSource    queryBuildDataSource; 
    QueryBuildRange     queryBuildRange;

    query = this.parmQuery();

    Contract = this.parmDataContract() as ItemTransactionSummaryContract; 
    accountNum = Contract.parmAccountNum();

    // Add parameters to the query. 
    queryBuildDataSource = query.dataSourceTable(tablenum(CustTable));

    if(accountNum) 
    { 
        queryBuildRange = queryBuildDataSource.findRange(fieldnum(CustTable, AccountNum)); 
 if (!queryBuildRange) 
 { 
     queryBuildRange = queryBuildDataSource.addRange(fieldnum(CustTable, AccountNum)); 
 } 
 // If an account number has not been set, then use the parameter value to set it. 
 if(!queryBuildRange.value()) 
 queryBuildRange.value(accountNum); 
    }
    queryRun = new QueryRun(query);
    while(queryRun.next()) 
    { 
        custTable = queryRun.get(tableNum(CustTable)); 
  
 ItemTransactionSummary.AccountNum  = custTable.AccountNum; 
 ItemTransactionSummary.Blocked   = custTable.Blocked; 
 ItemTransactionSummary.PriceGroup  = custTable.PriceGroup; 
 ItemTransactionSummary.Currency  = custTable.Currency; 
 ItemTransactionSummary.insert();
    } 
}

Example for Query and contract class parameter based Data Provider class in Ax 2012
Use a Report Data Provider Class in a Report

SSRS Report UI Builder class in Ax 2012

Description:-

User Interface (UI) Builder Class is used to define the layout of the parameter dialog box that opens before a report is run in Microsoft Dynamics AX. It is used to add the customizations as well as additional fields in the dialog.

Following are the scenarios where UI Builder Class can be used:

1.       Grouping dialog fields
2.       Overriding dialog field events
3.       Adding a customized lookup to a dialog field
4.       Binding dialog fields with Report contract parameters
5.       Changing the layout of the dialog
6.       Adding custom controls to the dialog

To create a UI builder class, extend it with SrsReportDataContractUIBuilder.

Sample UI Builder Class

·         Create a new class. Open AOT à Classes.
·         Right Click on Classes and select New Class. Name it as ItemTransactionSummaryUIB.
·         Open the Class declaration by right clicking on it and selecting View code.
·         Write the following code.

Class ItemTransactionSummaryUIB extends SysOperationAutomaticUIBuilder
{

}

Now open the contract class and add the following line to the header of the class. It will tell the contract class to build the parameter dialog. In other words, it will link the UI Builder Class with the contract class.

[DataContractAttribute,
    SysOperationContractProcessingAttribute(classstr(ItemTransactionSummaryUIB))]
class ItemTransactionSummaryContract implements SysOperationValidatable
{

}

Examples of UI Builder Class Usage

Based on different scenarios, different methods are overridden as shown in the following examples:

Grouping the dialog fields/Changing the layout of the dialog/Adding custom controls to the dialog

To customize the layout and add custom fields, override the build as shown below:

public void build()
{
    DialogGroup dlgGrp;    

    //get the current dialog
    Dialog      dlg = this.dialog();       

    //make required modifications to the dialog
    dlgGrp = dlg.addGroup('Dates');  
    dlgGrp.columns(2);   
    dlg.addField(identifierStr(FromDate));
    dlg.addField(identifierStr(ToDate));    
        
    dlgGrp = dlg.addGroup('Customer');  
    dlg.addField(identifierStr(CustAccount));    
}

This build method is called by the report framework to generate the layout of the dialog.

Binding dialog fields with Report contract parameters. 
Write the following code in the build method:

Public void build()
{
    ItemTransactionSummaryContract   contract;

    contract = this.dataContractObject() as ItemTransactionSummaryContract;

    dialogFromDate    = this.addDialogField(methodStr(ItemTransactionSummaryContract, ParmFromDate),contract);
    dialogToDate      = this.addDialogField(methodStr(ItemTransactionSummaryContract, ParmToDate),contract);
    dialogItemId      = this.addDialogField(methodStr(ItemTransactionSummaryContract, parmItemId),contract);
    dialogItemGroupId = this.addDialogField(methodStr(ItemTransactionSummaryContract, parmItemGroup),contract);
    dialogDiamension  = this.addDialogField(methodStr(ItemTransactionSummaryContract, parmDiamension),contract);
}

Overriding dialog field events/Adding a customized lookup to a dialog field

To add a customized lookup or to override a control method, create a new method containing the business logic. The new method must have the same signature as the method you want to override.
Then, override the postBuild method and register the method to override with the new method created.

In the following example, the lookup method of a field is to be overridden. To do this, create a new method lookupCustGroup and add the following code:

public void lookupCustGroup(FormStringControl _formStringControl)
{    
    Query query = new Query();
    QueryBuildDataSource DS;    
    SysTableLookup sysTablelookup;

    //create a table lookup    
    sysTablelookup = SysTableLookup::newParameters(tableNum(CustGroup),_formStringControl);
    sysTablelookup.addLookupfield(fieldNum(CustGroup,CustGroup));
    sysTablelookup.addLookupfield(fieldNum(CustGroup,Name));

    //create a query
    DS = query.addDataSource(tableNum(CustGroup));
    DS.addRange(fieldNum(CustGroup,PaymTermId)).value('N030');

    //assign the query and call lookup
    sysTablelookup.parmQuery(query);
    sysTablelookup.performFormLookup();
}

Now, override the postBuild method and write the following code:

public void postBuild()
{
    DialogField dlgCustGroup;
    
    super();
    
    //get the field to override by providing the data contract object and the associated attribute/method
    dlgCustGroup = this.bindInfo().getDialogField(this.dataContractObject(),
                methodStr(ItemTransactionSummaryContract,parmCustGroupId));

    //register the method we want to override
    dlgCustGroup.registerOverrideMethod(
          methodStr(FormStringControl, lookup),
          methodStr(ItemTransactionSummaryUIB,lookupCustGroup),
          this);    
}

bindInfo returns an object of type SysOperationUIBindInfo. It contains information about the dialog controls bounded to a report contract. postBuild method is called when dialog is created.

Also you can create lookup using the AOT query and use in UI Builder class like below.

I have used below AOT query to get ItemGroupId for creating lookup in SSRS report.

 

UI Builder class declaration

class ItemTransactionSummaryUIB extends SysOperationAutomaticUIBuilder
{
    DialogField                    dialogItemGroupId;
    ItemTransactionSummaryContract ItemTransactionSummaryContract;
}

Build method

public void build()
{
    ItemTransactionSummaryContract   contract;
    contract = this.dataContractObject() as ItemTransactionSummaryContract;
 dialogItemGroupId = this.addDialogField(methodStr(ItemTransactionSummaryContract, parmItemGroup),contract);
}

PostBuild method

public void postBuild()
{
    ItemTransactionSummaryContract   contract;
    super();
    contract = this.dataContractObject() as ItemTransactionSummaryContract;
    dialogItemGroupId = this.bindInfo().getDialogField(contract,methodStr(ItemTransactionSummaryContract, parmItemGroup));
    dialogItemGroupId.registerOverrideMethod(methodStr(FormStringControl, lookup),
        methodStr(ItemTransactionSummaryUIB, ItemGroupLookup),this);
    if (dialogItemGroupId)
    {
        dialogItemGroupId.lookupButton(2);
    }
}

Lookup method

private void ItemGroupLookup(FormStringControl _control1)
{
    Query       query;
    container   conItemGroup;

    query = new Query(queryStr(dev_ItemTransSumItemGroupQry));
    SysLookupMultiSelectGrid::lookup(query,_control1,_control1,conItemGroup);
}

If you have check box and you want to enable/disable other control based on the checkbox tick you also can do using the UI Builder class.

class ItemTransactionSummaryUIB extends SysOperationAutomaticUIBuilder
{
    DialogField                    dialogDiamension;
    ItemTransactionSummaryContract ItemTransactionSummaryContract;
}

public void build()
{
    ItemTransactionSummaryContract   contract;
    contract = this.dataContractObject() as ItemTransactionSummaryContract;
    
    dialogDiamension = this.addDialogField(methodStr(ItemTransactionSummaryContract, parmDiamension),contract);
}

public void postBuild()
{
    ItemTransactionSummaryContract   contract;
    super();
    contract = this.dataContractObject() as ItemTransactionSummaryContract;
 
    dialogDiamension  = this.bindInfo().getDialogField(contract, methodstr(ItemTransactionSummaryContract, parmDiamension));
    dialogDiamension.registerOverrideMethod(methodstr(FormCheckBoxControl, modified),
        methodstr(ItemTransactionSummaryUIB, dialoggetDiamension), this);
    //dialogToDate.enabled(any2enum(dialogDiamension.value()));
    if (dialogDiamension)
    {
        dialogDiamension.value();
    }
}

public boolean dialoggetDiamension(FormCheckBoxControl _checkBoxControl)
{
    ;
    if(any2enum(dialogDiamension.value()) == true)
    {
      //return true;
      //dialogToDate.enabled(any2enum(dialogDiamension.value()));
    }
    else
    {
      //return false;
    }
    //set enabled or disabled based on checkbox
    dialogFieldTransDate.enabled(any2enum(dialogFieldAllowModifyDate.value()));

    //or alternatively
    dialogFieldTransDate.enabled(_checkBoxControl.checked());

    return true;
}

SSRS Report Controller Class in Ax 2012

Description:-

Controller class is used to control the report execution as well as pre-processing of the report data. The SSRS reporting framework uses this class to modify the report dialogs, calling the SQL Server reporting services, as well pre-processing parameters for the report.

Following are the scenarios where Controller class can be used:

1.       Modifying a report query based on the input data
2.       Modifying report contract data based on the input data
3.       Control a report parameters dialog
4.       Open different reports/designs from the same menu item based on the input data
5.       Reports that are opened from a form

To create a controller class, extend it with SrsReportRunController.

Simple Controller class

·         Create a new class. Open AOT → Classes.
·         Right Click on Classes and select New Class. Name it as SSRSDemoController.
·         Open the Class declaration by right clicking on it and selecting View code.
·         Now write the following code:

class ItemTransactionSummaryController extends SrsReportRunController
{
    //#define.ReportName('ItemTransactionSummary.PrecisionDesign')
}

     ·         Create a new method and write the following code:

public static void main(Args _args)
{
    //define the new object for controller class
    ItemTransactionSummaryController controller;

    controller = new ItemTransactionSummaryController();
    //set the report name and report design to run
    controller.parmReportName(ssrsReportStr(ItemTransactionSummary, PrecisionDesign));
    //controller.parmReportName(#ReportName);
    //pass the caller args to the controller
    controller.parmArgs(_args);
    //execute the report
    controller.startOperation();
}

Examples of Controller Class Usage

Based on different scenarios, different methods are overridden as shown in the following examples:
1)    Modifying report query based on the input data
Used in those scenarios where a report query needs to be modified based on the caller args parameters or recorded before the report parameter dialog is rendered.

Override prePromptModifyContract method to modify the report query as shown below:

public void prePromptModifyContract()
{
    //add a range in the report query  
    SrsReportHelper::addParameterValueRangeToQuery(this.getFirstQuery(),tableNum(SSRSReportDemo),
       fieldNum(SSRSReportDemo, RecId),SysQuery::value(this.parmArgs().record().RecId));
}

Note:- prePromptModifyContract is called by report controller before the parameter dialog is shown to the User.

2)    Modifying report contract data based on the input data

Used in those scenarios where report contract parameters need to be modified based on the caller args prior to the execution of the report.

Override preRunModifyContract method to modify the report contract as shown below:

protected void preRunModifyContract()
{    
    //define object for report contract
    ItemTransactionSummaryContract contract;

    //get the reference of the current contract object
    contract = this.parmReportContract().parmRdpContract() as ItemTransactionSummaryContract;

    //modify the parameter value of the contract
    contract.ParmFromDate(FiscalCalendarYear::FindCYStartDate(today()));
    contract.ParmToDate(today());
    contract.ParmItemId();
    contract.parmItemGroup();
    contract.parmDiamension();
    contract.parmDiamension(this.parmArgs().parm());
}

Note:- preRunModifyContract is called by report controller before the report is run.

3)    Control report parameters dialog

In some scenarios, a report parameter dialog should not be visible to the end user. Controller class is also used to control the visibility of the report parameter UI.

Add the following code in the main method of the controller class before startOperation method call to hide/show the report parameter UI:

public static void main(Args _args)
{
    ItemTransactionSummaryController controller;

    controller = new ItemTransactionSummaryController();
    controller.parmReportName(ssrsReportStr(ItemTransactionSummary, PrecisionDesign));
    controller.parmArgs(_args);
    controller.startOperation();

    //hide the report parameter dialog
    controller.parmShowDialog(false);
}

4)    Open different reports from the same menu item based on the input data

It is used in those scenarios where different reports or different designs of a same report need to be opened from a same menu item depending upon the caller args.

Write the following code in main method to achieve this scenario:

public static client void main(Args args)
{    
    //define the new object for controller class
    ItemTransactionSummaryController controller;   
    controller = new ItemTransactionSummaryController();
    
    //pass the caller args to the controller
    controller.parmArgs(args);
        
    //if report is run from edit mode then run the PrecisionDesign_1 of the report otherwise run the PrecisionDesign of the report
    if(args.parmEnum() == FormOpenMode::ForEdit)
    {
        //set the report name and report design to run
        controller.parmReportName(ssrsReportStr(ItemTransactionSummary,PrecisionDesign_1));    
    }
    else
    {
        //set the report name and report design to run
        controller.parmReportName(ssrsReportStr(ItemTransactionSummary,PrecisionDesign));    
    }   
    
    //execute the report
    controller.startOperation();   
}

5)    Reports that are opened from a form

Controller class is also used when reports are opened from a form and are needed to show selected record details.

Use either prePromptModifyContract method or preRunModifyContract method to achieve this scenario like below.

In my case I have to run this report from the report as finished journal form or from the Job card form from the production journal then I will use like below. Declare table name in class declaration.

Protected void prePromptModifyContract()
{
    super();
    if(this.parmArgs().dataset() == tableNum(ProdJournalProd))
    {
        if (this.parmArgs() && this.parmArgs().record())
        {
            contract = this.parmReportContract().parmRdpContract() as ProductionBarcodeContract;
            ObjProdJournalProd = this.parmArgs().record();

            contract.parmProdId(ObjProdJournalProd.ProdId);
            contract.parmJournalId(ObjProdJournalProd.JournalId);
            contract.parmRecId(ObjProdJournalProd.RecId);
            contract.parmLineNum(ObjProdJournalProd.LineNum);
        }
    }

    if(this.parmArgs().dataset() == tableNum(ProdJournalRoute))
    {
        if (this.parmArgs() && this.parmArgs().record())
        {
            contract = this.parmReportContract().parmRdpContract() as ProductionBarcodeContract;
            ObjProdJournalRoute = this.parmArgs().record();

            contract.parmProdId(ObjProdJournalRoute.ProdId);
            contract.parmJournalId(ObjProdJournalRoute.JournalId);
            contract.parmRecId(ObjProdJournalRoute.RecId);
            contract.parmLineNum(ObjProdJournalRoute.LineNum);
        }
    }
}

Protected void preRunModifyContract()
{
    super();
}

ShowQueryValues Method 

Determines whether the query values will be added to the dialog box. If you want to modify the query value then you have to set this method in you controller class and it will return false. By default it will always return true. Used by the query Ui builder.

public boolean showQueryValues(str parameterName)
{
    return false;
}

public boolean showQueryValues(str parameterName)
{
    If (this.parmArgs().menuItemName() == menuItemOutputStr(#YourMenuItemName)
    {
        return false;
    }
    else
    {
        return true;
    }
}

ShowPrintSettings method

Indicates whether print settings should be added to the dialog. True if the print settings are to be shown; otherwise, false.

SSRS Report contract class in Ax 2012

SSRS Report contract class in Ax 2012

Description:-

Report contracts in AX 2012 are used for defining parameters to the SSRS report. We can define any number of parameters using X++ statements of any data type, which can be passed on to the RDP class. And then, we can use the same contracts to query data from the database engine which will decrease an overhead on execution of a query in SQL.

If you have a validation method in your contract class then you have to implements SysOperationValidatable in your contract class like below syntax.

To define a Report contract class we use the following syntax:

[DataContractAttribute]
class ItemTransactionSummaryContract implements SysOperationValidatable
{
    List  ItemGroupIdList,ListItemId;
    BaseDate FromDate,ToDate;
    NoYesId   IsDiamension;
}

If you are using UI Builder class then you have to mansion UI Builder class in contract class like below.

[DataContractAttribute,
    SysOperationContractProcessingAttribute(classstr(ItemTransactionSummaryUIB))]
class ItemTransactionSummaryContract implements SysOperationValidatable
{
    List      ItemGroupIdList,ListItemId;
    BaseDate  FromDate,ToDate;
    NoYesId   IsDiamension;
}

Below are different methods which I have used in my contract class.

[DataMemberAttribute("From Date"),
SysOperationLabelAttribute("From Date"),
SysOperationDisplayOrderAttribute('1')]
public BaseDate ParmFromDate(FromDate _FromDate = FromDate)
{
    FromDate = _FromDate;
    return     FromDate;
}

[DataMemberAttribute("To Date"),
SysOperationLabelAttribute("To Date"),
SysOperationDisplayOrderAttribute('2')]
public BaseDate ParmToDate(ToDate _ToDate = ToDate)
{
    ToDate = _ToDate;
    return   ToDate;
}

[DataMemberAttribute("Item Group"),
    AifCollectionTypeAttribute("Item Group", Types::String),
    SysOperationLabelAttribute(literalStr("Item Group"))]
public List parmItemGroup(List _ItemGroupIdList = ItemGroupIdList)
{
    ItemGroupIdList = _ItemGroupIdList;
    return            ItemGroupIdList;
}

[DataMemberAttribute("Item Id"),
    AifCollectionTypeAttribute("Item Id", Types::String),
    SysOperationLabelAttribute(literalStr("Item Id"))]
public List parmItemId(List _ListItemId = ListItemId)
{
    ListItemId = _ListItemId;
    return       ListItemId;
}

[DataMemberAttribute,
SysOperationLabelAttribute("Diamension")]
public NoYesId parmDiamension(NoYesId _IsDiamension = IsDiamension)
{
    IsDiamension = _IsDiamension;
    return         IsDiamension;
}

You can create validate method for validate the report parameter like below. In my case I have ItemGroupId, ItemId, FromDate and ToDate. I have below validation in my validation method.

1)      FromDate should not be empty.
2)      ToDate should not be empty.
3)      FromDate should not be greater than ToDate.
4)      ItemGroupId and ItemId should not be empty.
ItemGroupId and ItemId both should not be selected.

public boolean validate()
{
    boolean         isValid = true;
    List            ItemGroupList = new List(Types::String);
    List            ItemIdList = new List(Types::String);
    
    ItemGroupList   = this.parmItemGroup();
    ItemIdList      = this.parmItemId();

    if(!ItemGroupList.elements() && !ItemIdList.elements())
    {
        isValid = checkFailed("ItemGroupId and ItemId both should not be empty");
    }
    if(ItemGroupList.elements() && ItemIdList.elements())
    {
        isValid = checkFailed("ItemGroupId and ItemId both should not be selected");
    }
    if (!FromDate)
    {
        isValid = checkFailed("From Date should be entered");
    }
    if (!ToDate)
    {
        isValid = checkFailed("To Date should be entered");
    }
    if (isValid && (FromDate > ToDate))
    {
        isValid = checkFailed(strfmt("From Date should be less than or equal to To Date", 
                  date2StrUsr(fromDate, DateFlags::FormatAll), date2StrUsr(toDate, DateFlags::FormatAll)));
    }
    return isValid;
}

Dialog fields and runtime lookups on SSRS Report parameters

Description:-

In this post, we will learn how to add dialog fields and get run time lookups on the dialog field in AX 2012 SSRS reports. In Dynamics AX 5.0, we used to get this by overriding dialog, dialogpostrun, controlMethodOverload, Field_runTimeFieldId_lookup/modified methods  etc.

In AX 2012 there is a class by name “SrsReportDataContractUIBuilder” to serve the same purpose.

Example: In the below screen shot, I have added dialog field “Filter by Cust” and in the lookup it will only display the customers who belongs to Customer group “20”.


Interesting right, now lets see how to accomplish this:
Create a new Query by name “SRCustTable” and add data source as “CustTable” as shown below.


Then create a new temporary Table by name TmpSRSalesTable and add 2 fields CustAccount and SalesId fields as shown below.


Now, we need to create contract classes, DP class and Builder class as shown below.
Follow the below classes and methods.

SRCustomLookupUIBuilder Class:

Create a new class by name SRCustomLookupsUIBuilder that should extend SrsReportDataContractUIBuilder as shown below

class SRCustomLookupsUIBuilder extends SrsReportDataContractUIBuilder
{
      DialogField   dialogAccountNum;
      DialogGroup   dialogGroup;
      boolean       enable; 
}


//The below accountNumLookUp method will help to get the runtime lookup based on the query defined in the code [customer group – 20]
private void accountNumLookup(FormStringControl accountNumLookup)
{
    Query                   query = new Query();
    QueryBuildDataSource    qbds_CustTable;
    SysTableLookup          sysTableLookup;
    QueryBuildRange         qbr;
 
    if (accountNumLookup != null)
    {
        // Create an instance of SysTableLookup with
        // the current calling form control.
 
        sysTableLookup = SysTableLookup::newParameters(tablenum(CustTable), accountNumLookup);
        //sysTableLookup.addLookupMethod(
        // Add fields to be shown in the lookup form.
        qbds_CustTable = query.addDataSource(tableNum(CustTable));
        sysTableLookup.addLookupfield(fieldnum(CustTable, AccountNum), true);
        sysTableLookup.addLookupfield(fieldnum(CustTable, CustGroup),false);
 
        qbr = qbds_CustTable.addRange(fieldNum(CustTable,CustGroup));
        qbr.value(20);
        sysTableLookup.parmUseLookupValue(false);
 
        sysTableLookup.parmQuery(query);
 
        // Perform the lookup.
        sysTableLookup.performFormLookup(); 
    }
}


/// <summary>
///    Builds the dialog.
/// </summary>
/// <remarks>
///    The dialog appears with the parameters.
/// </remarks>
public void build()
{
    SRCustomLookUpContract rdpContract =  this.dataContractObject();
 
    dialogAccountNum = this.addDialogField(methodstr(SRCustomLookUpContract,parmAccountNum),rdpContract);
    dialogAccountNum.lookupButton(2);
}


public void postRun()
{
    Dialog dialogLocal = this.dialog();
    DialogField dialogField;
 
    super();
 
    // This method should be called in order to handle events on dialogs.
    dialogLocal.dialogForm().formRun().controlMethodOverload(false);
 
    // Override the methods of department  field.
    dialogField = this.bindInfo().getDialogField(this.dataContractObject(), methodstr(SRCustomLookUpContract, parmAccountNum));
    dialogField.registerOverrideMethod(methodstr(FormStringControl, lookup), methodstr(SRCustomLookupsUIBuilder, accountNumLookup), this);
 
}

Note : we can even use intializeFields and getfromdialog overridden methods to initialize values and get the values from dialog.

Contract class

[DataContractAttribute,
SysOperationContractProcessingAttribute(classstr(SRCustomLookupsUIBuilder))
]
class SRCustomLookUpContract
{
    AccountNum  accountNum; 
}

//Add parmAccountNum method as shown below
[DataMemberAttribute(‘AccountNum’)
    ]
public AccountNum parmAccountNum(AccountNum _accountNum = accountNum)
{
    accountNum = _accountNum;
    return accountNum;
}

Data Provider class

[
    SRSReportQueryAttribute(queryStr(SRCustTable)),
    SRSReportParameterAttribute(classStr(SRCustomLookUpContract))
 
]
class SRCustomLookupDP extends SRSReportDataProviderBase
{
 
    SRCustomLookUpContract contract;
    TmpSRSalesTable        tmpSRSalesTable;
}

/// <summary>
/// executes the logic based on the parameter entries
/// </summary>
/// <remarks>
/// fills up the temp table
/// </remarks>
[SysEntryPointAttribute]
public void processReport()
{
    Query query;
    QueryRun qRun;
    QueryBuildRange qbr;
    CustTable       custTable;
 
    contract = this.parmDataContract() as SRCustomLookUpContract;
    query =  this.parmQuery(); 
 
    qbr = query.dataSourceNo(1).addRange(fieldNum(CustTable, AccountNum));
    qbr.value(contract.parmAccountNum()); 
    qRun = new QueryRun(query); 
    while(qRun.next())
    {
        custTable = qRun.get(tableNum(custTable));
        this.insertInToTempTable(custTable.AccountNum);
    }
}

public  void insertInToTempTable(AccountNum _accountNum)
{
    SalesTable salesTable;
    ;
    while select salesTable where salesTable.CustAccount == _accountNum
    {
       tmpSRSalesTable.CustAccount = _accountNum;
       tmpSRSalesTable.SalesId     = salesTable.SalesId;
 
       tmpSRSalesTable.insert();
    } 
}

[SRSReportDataSetAttribute('TmpSRSalesTable')]
public TmpSRSalesTable getTmpSRSalesTableDetails()
{
    select * from tmpSRSalesTable; 
    return tmpSRSalesTable;
}

We are done with all the classes and I have already posted in my earlier blogs how to add this data provider classes as a dataset data source. Follow the same process and create a new SSRS report in visual studio 2010 and save the report to AOT and deploy as well.

Once you invoke the report from within AX, you will get the parameters from as shown below and runtime lookup clearly shows all the customers who belong to customer group 20. Click on Ok button.