MSDyn365FO. How-to print a report to a byte array.

There are blogs available on this topic, like this one, but they are using lots of internal use only objects, like SRSProxy. It’s a well-known problem and hopefully it will be fixed one day, meanwhile here is another way how to achieve the same without any compile warnings.

First, lets extend SrsReportDataContract and add 2 variables: do not save file flag and byte array.

[ExtensionOf(classStr(SrsReportDataContract))]
final public class SrsReportDataContract_IM_Extension
{
    private System.Byte[] reportBytes_IM;
    private boolean doNotSaveFile_IM;

    public System.Byte[] parmReportBytes_IM(System.Byte[] _reportBytes_IM = reportBytes_IM)
    {
        reportBytes_IM = _reportBytes_IM;
        return reportBytes_IM;
    }

    public boolean parmDoNotSaveFile_IM(boolean _doNotSaveFile_IM = doNotSaveFile_IM)
    {
        doNotSaveFile_IM = _doNotSaveFile_IM;
        return doNotSaveFile_IM;
    }
}

Now, let’s create helper class that will do all the magic required. Solution here is simple, subscribe to toSendFile delegate and if a contact has new flag specified then save report bytes back to the contact and set result to false, so standard code won’t save the file. The class has another helper method that accepts report contract and returns byte array.

public static class SrsReportRunHelper_IM
{
    [SubscribesTo(classStr(SRSPrintDestinationSettingsDelegates), delegateStr(SRSPrintDestinationSettingsDelegates, toSendFile))]
    public static void SRSPrintDestinationSettingsDelegates_toSendFile(System.Byte[] _reportBytes, SrsReportRunPrinter _printer, SrsReportDataContract _dataContract, Microsoft.Dynamics.AX.Framework.Reporting.Shared.ReportingService.ParameterValue[] _paramArray, EventHandlerResult _result)
    {
        if (_dataContract.parmDoNotSaveFile_IM())
        {
            _dataContract.parmReportBytes_IM(_reportBytes);
            _result.result(false);
        }
    }

    public static System.Byte[] printReportToByteArray(SrsReportRunController _controller)
    {            
        SrsReportDataContract reportContract = _controller.parmReportContract();
        SRSPrintDestinationSettings printerSettings = reportContract.parmPrintSettings();
        printerSettings.printMediumType(SRSPrintMediumType::File);
        printerSettings.fileFormat(SRSReportFileFormat::PDF);

        reportContract.parmDoNotSaveFile_IM(true);

        _controller.parmShowDialog(false);
        _controller.startOperation();
                
        return reportContract.parmReportBytes_IM();
    }
}

Usage example

SalesInvoiceController controller = SalesInvoiceController::construct();
                controller.parmReportName(PrintMgmtDocType::construct(PrintMgmtDocumentType::SalesOrderInvoice).getDefaultReportFormat());

SrsReportDataContract reportContract = controller.parmReportContract();
SRSPrintDestinationSettings printerSettings = reportContract.parmPrintSettings();
                printerSettings.printMediumType(SRSPrintMediumType::File);
printerSettings.fileFormat(SRSReportFileFormat::PDF);

SalesInvoiceJournalPrint salesInvoiceJournalPrint = SalesInvoiceJournalPrint::construct();
salesInvoiceJournalPrint.parmPrintFormletter(NoYes::Yes);
salesInvoiceJournalPrint.parmUsePrintManagement(false);
salesInvoiceJournalPrint.parmUseUserDefinedDestinations(true);
salesInvoiceJournalPrint.parmPrinterSettingsFormLetter(
controller.parmReportContract().parmPrintSettings().pack());

Args args = new Args();
args.caller(salesInvoiceJournalPrint);
args.parmEnumType(enumNum(PrintCopyOriginal));
args.parmEnum(PrintCopyOriginal::OriginalPrint);
args.record(custInvoiceJour);

controller.parmArgs(args);

System.Byte[] reportBytes = SrsReportRunHelper_IM::printReportToByteArray(controller);

And then you can convert it to base64 string

if (reportBytes)
{
    using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(reportBytes))
        {
            ret = System.Convert::ToBase64String(memoryStream.ToArray());
        }
}