Render report to memory stream D365 (aka AX7)

Share on linkedin
LinkedIn
Share on twitter
Twitter
Share on facebook
Facebook

Recently I was tasked with an upgrade of a functionality we have on AX2012 to Dynamics365, which includes running the report from the code and attaching it to the caller record. As you are probably aware of, this was very easy to accomplish in the earlier Microsoft Dynamics AX versions, where you could simply run the report to a file, save it locally and attach it to the record using the DocuActionArchive class.

Things are a bit more complicated when it comes to D365 in cloud. You are no longer able to save the file locally (for example using System.IO.Path::GetTempPath() + fileName) as storage is now moved to Azure and files are stored as a Blob. You may have also noticed that most of the classes that work with files now use stream objects with their content type instead.

In order to attach the report to a record I needed to provide a MemoryStream object which would represent my report. As I found no existing code that could provide me with the memory stream output of the report I created my own method to do this. Below is given a code (runnable class – job) to perform rendering of a report to a memory stream.

[crayon lang=”x++”]class RunReportToStream
{
public static void main(Args _args)
{
DocuRef addedRecord;
ProdTable prodTable = ProdTable::find(‘P000173’);
Filename fileName = “AbcTest.pdf”;
YourReportController controller = new YourReportController();
YourReportContract contract = new YourReportContract();
SRSPrintDestinationSettings settings;
Array arrayFiles;
System.Byte[] reportBytes = new System.Byte[0]();
SRSProxy srsProxy;
SRSReportRunService srsReportRunService = new SrsReportRunService();
Microsoft.Dynamics.AX.Framework.Reporting.Shared.ReportingService.ParameterValue[] parameterValueArray;
Map reportParametersMap;
SRSReportExecutionInfo executionInfo = new SRSReportExecutionInfo();
;

_args = new Args();
_args.record(prodTable);
// Provide all the parameters to a contract
contract.parmProdId(‘P000173’);
contract.parmNumberOfLabels(1);
// Provide details to controller and add contract
controller.parmArgs(_args);
controller.parmReportName(ssrsReportStr(YourReportName, DesignName));
controller.parmShowDialog(false);
controller.parmLoadFromSysLastValue(false);
controller.parmReportContract().parmRdpContract(contract);
// Provide printer settings
settings = controller.parmReportContract().parmPrintSettings();
settings.printMediumType(SRSPrintMediumType::File);
settings.fileName(fileName);
settings.fileFormat(SRSReportFileFormat::PDF);

// Below is a part of code responsible for rendering the report
controller.parmReportContract().parmReportServerConfig(SRSConfiguration::getDefaultServerConfiguration());
controller.parmReportContract().parmReportExecutionInfo(executionInfo);

srsReportRunService.getReportDataContract(controller.parmreportcontract().parmReportName());
srsReportRunService.preRunReport(controller.parmreportcontract());
reportParametersMap = srsReportRunService.createParamMapFromContract(controller.parmReportContract());
parameterValueArray = SrsReportRunUtil::getParameterValueArray(reportParametersMap);

srsProxy = SRSProxy::constructWithConfiguration(controller.parmReportContract().parmReportServerConfig());
// Actual rendering to byte array
reportBytes = srsproxy.renderReportToByteArray(controller.parmreportcontract().parmreportpath(),
parameterValueArray,
settings.fileFormat(),
settings.deviceinfo());

if (reportBytes)
{
// Converting byte array to memory stream
System.IO.MemoryStream stream = new System.IO.MemoryStream(reportBytes);

// Upload file to temp storage and direct the browser to the file URL
File::SendFileToUser(stream, settings.parmFileName());

stream.Position = 0;
str fileContentType = System.Web.MimeMapping::GetMimeMapping(fileName);
// Attach the file to a record using stream object
addedRecord = DocumentManagement::attachFile(prodTable.TableId,prodTable.RecId,prodTable.DataAreaId, ‘File’, stream, fileName, fileContentType,”PDF file attached”);
}

// You can also convert the report Bytes into an xpp BinData object if needed
container binData;
Binary binaryData;
System.IO.MemoryStream mstream = new System.IO.MemoryStream(reportBytes);
binaryData = Binary::constructFromMemoryStream(mstream);
if(binaryData)
{
binData = binaryData.getContainer();
}

}

}

[/crayon]

With the output found in the code above like reportBytes, stream and binData you can do many things, like sending the report to a certain SharePoint location from code, sending the report as an email attachment from code, attaching the report to a record as a document attachment from code and I am sure that you will find many other usages.

I hope that you will find this text helpful.

You Might Also Like:

This Post Has 9 Comments

  1. Very nice share. Thank you.

  2. very useful! thank you

  3. My issue is with the Report Contract class and Controller class. How do i get through the 2 classes because using this code i have some errors until i extend the SrsReportRun on the Controller class and i still get another error saying "Controller class does not implement the SRSDataProvider interface". I will be glad if someone can throw some light into this.

  4. Hi – first of all this is great. It has helped me lots of time trying to figure out – thanks for sharing!

    There is one tiny issue with this – for the ‘PreProcess’ reports (like Sales Invoice) it does not perform the cleanup after execution. Normally this happens when you close the Report Viewer form.
    I have recently discovered that SalesInvoiceHeaderFooterTmp had thousands of rows.
    In order to fix this, you need to add following line of code at the end of execution:

    srsReportRunService.cleanUpRdpPreProcessTables(classStr(SalesInvoiceDP), executionInfo);

  5. Hello,
    Thank you for sharing, very usefull.
    I have an issue with “renderReportToByteArray” that returns to me “value cannot be null. Parameter name: the value supplied for parameter ‘xml’ cannot be null or empty ”
    All the values passed are OK, except for deviceinfo (= “”). could not go further in debugging.
    Can you tell me what am I missing ? thank you very much
    Regards,
    Mourad.

  6. My fault, working perfectly.

  7. Thanks a lot for sharing. Great learning for D 365

Leave a Reply