SSRS Report Print management in D365

In order to add your customized report into Print management in D365, you needs to add an event handler to a delegate exposed by Microsoft.
Below is the example for Sales Invoice report

[SubscribesTo(classStr(PrintMgmtDocType), delegateStr(PrintMgmtDocType, getDefaultReportFormatDelegate))]
    public static void PrintMgmtDocType_getDefaultReportFormatDelegate(PrintMgmtDocumentType _docType, EventHandlerResult _result)
        PrintMgmtReportFormatName formatName;

        switch (_docType)
            case PrintMgmtDocumentType::SalesOrderInvoice:
                // <GEEEE>
                if (SysCountryRegionCode::isLegalEntityInCountryRegion([#isoEE]))
                    formatName =  ssrsReportStr(SalesInvoice, ReportEE);
                // </GEEEE>
                // <GEELT>
                if (SysCountryRegionCode::isLegalEntityInCountryRegion([#isoLT]))
                    formatName = ssrsReportStr(SalesInvoice, ReportLT);
                // </GEELT>
                // <GEEHU>
                if (SysCountryRegionCode::isLegalEntityInCountryRegion([#isoHU]))
                    formatName = ssrsReportStr(SalesInvoice, ReportHU);
                // </GEEHU>
                // <GEELV>
                if (SysCountryRegionCode::isLegalEntityInCountryRegion([#isoLV]))
                    formatName = ssrsReportStr(SalesInvoice, ReportLV);
                // </GEELV>
                // <GEECZ>
                if (SysCountryRegionCode::isLegalEntityInCountryRegion([#isoCZ]))
                    formatName = ssrsReportStr(SalesInvoice, ReportCZ);
                // </GEECZ>
                // <GEEPL>
                if (SysCountryRegionCode::isLegalEntityInCountryRegion([#isoPL]))
                    formatName = ssrsReportStr(SalesInvoice, ReportPL);
                // </GEEPL>
                // <GTH>
                if (SysCountryRegionCode::isLegalEntityInCountryRegion([#isoTH]))
                    formatName = ssrsReportStr(SalesInvoice, ReportTH);
                // </GTH>
                // <GMY>
                if (SysCountryRegionCode::isLegalEntityInCountryRegion([#isoMY]))
                    if (TaxParameters::find().GSTInvoiceFormat_MY == TaxGSTInvoiceFormat_MY::Full)
                        formatName = ssrsReportStr(SalesInvoice, ReportFull_MY);
                        formatName = ssrsReportStr(SalesInvoice, ReportSimplified_MY);
                // </GMY>

                if (!formatName)
                    formatName = ssrsReportStr(SalesInvoiceKAKA, Report);


How to add multiple report design under Print management in AX 2012

Below are the steps to be followed :

Step1. Create new Design for report under visual studio
Step2. Add code to method:
\Data Dictionary\Tables\PrintMgmtReportFormat\Methods\populate
Add code  before TTSCOMMIT:
addOther(PrintMgmtDocumentType::PurchaseOrderInvoice, ssrsReportStr(VendInvoiceDocument, MyReport), ssrsReportStr(VendInvoiceDocument, MyReport), #NoCountryRegionId);
Step 3. Choose you new format under:
AP -> setup -> form setup -> Print management -> Vendor invoice -> report format to VendInvoiceDocument.MyReport
Step 4.
New Report design can be executed from use Print management from Inquiry journal forms or during posting by selecting Print management destination.
Step 5. (Optional)
In case: the report still keep original design, add this code to class VendInvoiceDocumentController in method outputReport or Main:
//original menu item or your new menu item
if(args.menuItemName() == menuitemOutputStr(MyReportMenuItem))
formLetterReport.getCurrentPrintSetting().parmReportFormatName(ssrsReportStr( VendInvoiceDocument,MyReport));

Steps to export SSRS data into multiple Excel Sheets

1. Add Parent Group to the existing group
To add a parent group, select the “Details” row under Row Groups and then right click and select Add Group and then click on Parent Group. You can refer to the below image.



Once you click on Parent Group it will open a new window, there you have to provide a Group By field. In our case I want to divide data based on VendAcount, so I have to choose VendAcount data field and then click OK. You can refer to the below image.


As you can see from the below image a new dummy row group has been created. SSRS has automatically added a new column in the Tablix for this newly created row group.


2. Add Page Break

You need to create a page break between each instance of VendAcount, so right click on the recently created row group and click on Group Properties. You can refer to the below image.


Once you click on Group Properties, a new Group Properties window will open. Click on the Page Breaks tab and check “Between each instance of a group” check box and click OK. It will give a page break between each instance of VendAcount, so when you export the report into Excel each instance of VendAcount will be separated into different worksheets. You can refer to the below image.


3. Delete Dummy RowGroup Column

We don’t need the first column from the Tablix, this column was created automatically when we created the dummy row group, so delete the first column.

To delete the first column, right click on the first column then click on Delete Columns. We have to keep the group, so choose the second radio button Delete columns only and then click OK. You can refer to the below image.


Let’s preview and export the report into Excel. As you can see from the below image, data has been divided into four sheets because we have four different Product Categories. If you notice in Excel the sheet names are Sheet1, Sheet2, Sheet3 and Sheet4. This is because if neither an initial page name, nor page names related to page breaks are given then the worksheet tabs will have the default names Sheet1, Sheet2, and so forth, but we need meaningful sheet names.


4. Name the worksheets according to group value

You can dynamically assign sheet names. To assign sheets name select the Details Row under Row Groups and go to the properties by pressing the F4 key, it will open a Properties window. Look for “Group” in the properties window and expand it so you can see “PageName”. Assign the data field which will be used for sheet names. In our example since we used Vend Account data field in the grouping, I will use the same data field VendAccount. Now this will assign the VendAccount as the worksheet name.


5. Report Preview

We have made all necessary changes, let’s preview the report. As you can see from the below image when we exported the report into Excel each Vend Account was exported into different sheets and this time each sheet name is assigned dynamically.




Creating multi-select lookup dialog for SSRS report parameter in MS Dynamics AX 2012

Hello All,
I just recently found a way to create a multi select lookup
dialog for SSRS report parameter using SysLookupMultiSelectGrid
class. I thought it might be helpfull for other developers
as well since I couldn’t find any other Blog or Post answering this issue.

I am just assuming that you know how to create a simple dialog
for reporting parameter. However, if you don’t know this link might be helpfull to you.

I will just write those methods which will create a multi select
dialog. So, here we go.
Step 1:
In your data contract class declaration method, define your
parameter with ‘List’. For example I want to create a multi select dialog for
customers in which I need Customer account to be selected when a user selects
any customer. SO, I will write

List accountNum;

In your DataMemberAttribue method type
the following code

AifCollectionTypeAttribute('AccountNum', Types::String),
public List parmAccountNum(List _accountNum = accountNum)
  accountNum = _accountNum;
  return accountNum;

Your screen will look like this


Step 2:
Now that you have completed the contract class, let’s move on to
the UI Builder class. In your main lookup method write the following code.

public void lookup(FormStringControl _control)
  Query query = new Query(queryStr(CustTableSRS));

  container cnt;
  SysLookupMultiSelectGrid::lookup(query, _control, _control, cnt);

you may have to create 3 more methods to run your code without any
error. They are getFromDialog, initializeFields
and postRun. Here is the code for these methods. you have to change the contract class name with your
contract class
First create a new mothod for initializeFields and paste the following code

public void
  custMultiSelectContract contract = this.dataContractObject();

Then create another method for getFromDialog

public void getFromDialog()
  custMultiSelectContract contract = this.dataContractObject();

and then another method for postRun

public void postRun()
  custMultiSelectContract contract = this.dataContractObject();

That’s it. You are now done. Run your report and enjoy.

AX 2012 SSRS Report: Multiple report design under Print management

AX 2012 SSRS Report: Multiple design under Print management

If new formats for Sales order confirmation/Picking/Packing/Invoice Purchase order postings etc. are needs to be added. Following steps needs to be performed.

Step1. Create new Design for report under visual studio
Step2. Add code to method:
\Data Dictionary\Tables\PrintMgmtReportFormat\Methods\populate

Add Code in the before TTSCOMMIT:
addOther(PrintMgmtDocumentType::SalesOrderPackingSlip, ‘SalesPackingSlip.Report_XYZ,’SalesPackingSlip.Report_XYZ’ ,’XYZ’);

Step 3. Setup the Format under:
AR -> setup -> form setup -> Print management -> Sales order Packing slip
original -> report format to SalesPackingslip.Report_XYZ

Step 4.
New Report design can be executed from use Print management from Inquiry journal forms or during posting by selecting Print management destination.

More Links for Reference:

AX 2012 Print Management Integration Guide

Modifying the AX 2012 Customer or Vendor Aging Reports

In the class and method CustAgingReportDP\processReport on line 85 there is this line:

custVendBalanceList.calculateDetailsForMultipe(qr, contract.parmIncludeAmountCur());

The line above calls out to the CustBalanceList class which inherits from CustVendBalanceList.  In method calculateDetailsForMultiple in CustBalanceList on line 5 there is this line:

custVendAgingCalculateTmp = agingCalculation.process(_queryRun);

That lines calls out to the process method in the CustVendAgingCalculation class.  The process method is a driver method in which the data is collected that is later displayed in the report.  The process method is a good place to set your breakpoints.  On line 14 of the CustVendAgingCalculation\process method it collects all of the customers (or vendors) the report is going to look at.  This is the line of interest:


On line 20 of CustVendAgingCalculation\process it grabs the open and closed transactions that will be displayed in the report.  If you look at the CustVendAgingCalculation\selectTransactions method you will see it has two separate calls:



Both the selectClosedTransactions and selectOpenTransactions methods query the CustVendTrans and CustVendTransOpen tables for data.  The methods insert what they find into a CustVendAgingProcessingTmp table.  The details for the transactions are collected in the CustVendAgingProcessingDetailsTmp table and then both the CustVendAgingProcessingTmp and CustVendAgingProcessingDetailsTmp tables are combined into a single CustVendAgingCalculatedTmp temp table in the CustVendAgingCalculation\process method.  Once everything has been pulled together, the CustVendAgingCalculatedTmp table is returned to the CustBalanceList\calculateDetailsForMultiple method.

Next in the CustBalanceList\calculateDetailsForMultiple method the insertIntoTmpAccountSum method is called.  In the insertIntoTmpAccountSum method the contents of the CustVendAgingCalculatedTmp table are inserted into a CustTmpAccountSum temp table.  At the very end of the CustBalanceList\insertIntoTmpAccountSum method the TmpAccountSum is an instance of the AccountSumMap.  The map is set equal to the CustTmpAccountSum table.  The mappings on the AccountSumMap map translate the fields in the CustTmpAccountSum table to an instance of the TmpAccountSum temp table.

The TmpAccountSum table is then used by the CustAgingReportDP\processReport method on line 90 with this call:


The contents of the TmpAccountSum are inserted into a CustAgingReportTmp table and this temp table is what is returned to the SSRS report design.

Test SSRS Report by Job

static void TestSSRSReport(Args _args)
//initialize contract
ContractClassName cont = new ContractClassName();
DPClassName dp;

//fill the contract/fill the parm methods with data

dp = new DPClassName();

//pass the contract

//fill the table