Skip to main content

Notifications

Announcements

No record found.

Dynamics 365 Community / Forums / Finance forum / D365 Adding additional...
Finance forum

D365 Adding additional attachment during Email print.

(0) ShareShare
ReportReport
Posted on by Microsoft Employee

Hi All,

I have a query on the best possible approach to customize standard print to email function. I have a requirement where I have posted post project invoice proposals. This invoice has a document attached to it in docuref at customer level and legal entity level. Now when I print the project invoice via mail, standard would make a PDF or HTML of the report and send it via Email . My requirement needs me to add extra attachments to this Email. I.e. I am printing Invoice FT0001 that already has 2 documentd attached in docuref, when I print the invoice to mail, along with the invoice report that gets attached along with the mail, would like to have the document attachments  from docuref too included in the attachments as separate attachments in same mail.

I did a lot of research on Srsprintdestination class emaito method, where the logic of attaching of report files occurs, but the way the files get attached seems to be happening in kernel code which I cannot see. any thoughts on how best to proceed in this approach will be helpful using extension.

  • Community Member Profile Picture
    Community Member Microsoft Employee on at
    RE: D365 Adding additional attachment during Email print.

    Thanks you  Pedro, got my desired outcome by this //  result.result(false);

  • Suggested answer
    Pedro Tornich Profile Picture
    Pedro Tornich 955 on at
    RE: D365 Adding additional attachment during Email print.

    Hi,

    The delegate has a parameter named "result", you need to set it as false to skip the standard logic.

    Also, you should place your logic on a separate method and call it from the delegate to keep your code clean.

    Here is a sample code showing how to do this:

    [SubscribesTo(classStr(SRSPrintDestinationSettingsDelegates), delegateStr(SRSPrintDestinationSettingsDelegates, toEmail))]
    public static void SRSPrintDestinationSettingsDelegates_toEmail(SrsReportRunPrinter printer, SrsReportDataContract dataContract, Microsoft.Dynamics.AX.Framework.Reporting.Shared.ReportingService.ParameterValue[] paramArray, EventHandlerResult result)
    {
        // get projInvoiceParameters

        if(projInvoiceParameters.EnableAdditionalAttachments)
        {
            // Call a static method that will perform the logic you need
            MyMailerClass::sendEmail(...)
           
            // Set result
            result.result(false);

        }
    }

  • Community Member Profile Picture
    Community Member Microsoft Employee on at
    RE: D365 Adding additional attachment during Email print.

    Hi Perdo,,

    Still its calling the standard, with two separate mails,

    One from standard and another from customization.

    class DelegateEmailSubscriptions

    {    

       [SubscribesTo(classStr(SRSPrintDestinationSettingsDelegates), delegateStr(SRSPrintDestinationSettingsDelegates, toEmail))]

       public static void SRSPrintDestinationSettingsDelegates_toEmail(SrsReportRunPrinter printer, SrsReportDataContract dataContract, Microsoft.Dynamics.AX.Framework.Reporting.Shared.ReportingService.ParameterValue[] paramArray, EventHandlerResult result)

       {

           PSAProjInvoiceContract projInvoiceContract = dataContract.parmRdpContract();

           RecId recid = projInvoiceContract.parmProjInvoiceJourRecId();

           ProjInvoiceJour projInvoiceJour;

           select ProjInvoiceId, InvoiceAccount, DataAreaId from projInvoiceJour where projInvoiceJour.RecId == recid;

           ENWProjInvoiceParameters enwProjInvoiceParameters;

           select EnableAdditionalAttachments, EnableEmailTemplate, RecId from ProjInvoiceParameters

               where ProjInvoiceParameters.DataAreaId == projInvoiceJour.DataAreaId;

           str name = strFmt('%1', dataContract.parmReportName());

           if(dataContract.parmPrintSettings().printMediumType() == SRSPrintMediumType::Email  &&

              name == PSAProjInvoiceCopy.ENWReport'               &&

            ProjInvoiceParameters.EnableAdditionalAttachments == NoYes::Yes)

           {

               //SrsReportEMailDataContract emailContract = dataContract.parmPrintSettings().parmEMailContract();

               CustTable custTable;

               select RecId from custTable where custTable.AccountNum == projInvoiceJour.InvoiceAccount;

               DocuRef fileDocuRef = DocuRef::findTableIdRecId(projInvoiceJour.DataAreaId, custTable.TableId, custTable.RecId);

               System.DateTime dateTime = System.DateTime::get_UtcNow();

               str utcTimeAsStr = dateTime.ToString('yyyyMd');

               str                 milliSecondsAlive = int2str(winApi::getTickCount());

               str                 timeStr;

               timeStr = strFmt("%1 %2", time2str(timeNow(), 3, 1), substr(milliSecondsAlive, strlen(milliSecondsAlive)-2, 2));

             /*  Filename _filename = CompanyInfo::find().company() + '_' + reportName  + '_' + utcTimeAsStr + '_' + timeStr + '.PDF';

               System.Byte[] reportBytes = srsProxy.renderReportToByteArray(dataContract.parmReportPath(),

                                                       reportParamArray,

                                                       dataContract.parmPrintSettings().fileFormat(),

                                                       dataContract.parmPrintSettings().deviceInfo());

               System.IO.MemoryStream mstream = new System.IO.MemoryStream(reportBytes);*/

               //From Address

               str  fromAddress ;

               UserInfo userInfo;

               SysUserInfo sysUserInfo = SysUserInfo::find(curuserid());

               // try to get it from SysUserInfo

               str value = strrtrim(strltrim(sysUserInfo.emailDisplay()));

               if (strlen(value) == 0)

               {

                   select firstonly Id, NetworkAlias from userInfo where userInfo.Id == curuserid();

                   if (strlen(userInfo.NetworkAlias) > 0)

                   {

                       value = userInfo.networkAlias;

                       if (!strContains(userInfo.networkAlias, '@') && strLen(userInfo.networkDomain) > 0)

                       {

                           value += '@' + userInfo.networkDomain;

                       }

                   }

               }

               fromAddress = value;

               //Attachment message builder

               var messageBuilder = new SysMailerMessageBuilder();

             /*

               //Customer Billing invoice report attachment via email

               messageBuilder.setFrom(fromAddress)

                             .addTo(dataContract.parmPrintSettings().parmEMailContract().parmTo())

                             .addCc(dataContract.parmPrintSettings().parmEMailContract().parmCc())

                             .setSubject(dataContract.parmPrintSettings().parmEMailContract().parmSubject())

                             .setBody(dataContract.parmPrintSettings().parmEMailContract().parmBody())

                             .addAttachment(new System.IO.MemoryStream(reportBytes), _filename);*/

               //Customer specific attachments via email

               messageBuilder.setFrom(fromAddress)

                             .addTo(dataContract.parmPrintSettings().parmEMailContract().parmTo())

                             .addCc(dataContract.parmPrintSettings().parmEMailContract().parmCc())

                             .setSubject(dataContract.parmPrintSettings().parmEMailContract().parmSubject())

                             .setBody(dataContract.parmPrintSettings().parmEMailContract().parmBody())

                             .addAttachment(DocumentManagement::getAttachmentStream(fileDocuRef), strFmt('%1.%2', fileDocuRef.Name, fileDocuRef.fileExtension()));

               //Final Attachment to the mailer

               var mailer = SysMailerFactory::getNonInteractiveMailer();

               mailer.sendNonInteractive(messageBuilder.getMessage());

           }

       }

    }

    Kindly suggest.

    Regards

  • Suggested answer
    Pedro Tornich Profile Picture
    Pedro Tornich 955 on at
    RE: D365 Adding additional attachment during Email print.

    Hi,

    Unfortunately, you won't be able to bypass the standard code using CoC.

    Only methods decorated with [Replaceable(true) can ignore the next() call on CoC methods.

    That's why MS added the delegate I've mentioned.

    Looking at the following image I see that the delegate receives the report contract as parameter:

    Screen-Shot-2019_2D00_09_2D00_24-at-17.39.38.png

    In the subscriber method you can use the dataContract.parmRdpContract() method to access the report data contract class.

    Here is a sample code:

    [SubscribesTo(classStr(SRSPrintDestinationSettingsDelegates), delegateStr(SRSPrintDestinationSettingsDelegates, toEmail))]
    public static void SRSPrintDestinationSettingsDelegates_toEmail(SrsReportRunPrinter printer, SrsReportDataContract dataContract, Microsoft.Dynamics.AX.Framework.Reporting.Shared.ReportingService.ParameterValue[] paramArray, EventHandlerResult result)
    {
        MyReportContractClass myContract = dataContract.parmRdpContract();
    }

  • Community Member Profile Picture
    Community Member Microsoft Employee on at
    RE: D365 Adding additional attachment during Email print.

    Hi Perdo,

    Thanks for your valuable input.

    Please see my issues with finding the given

    1. class emailReport() while doing chain of command, I am unable to get the report data contact values depend on which I need to attach my additional documents from document ref.

    2.  SRSPrintDestinationSettingsDelegates class unable to acess the data contact class.

    While doing COC for

    public void printReport()

       {        

           next printReport();

           PSAProjInvoiceContract projInvoiceContract = this.reportContract.parmRdpContract();

           RecId recid = projInvoiceContract.parmProjInvoiceJourRecId();

           ProjInvoiceJour projInvoiceJour;

           select ProjInvoiceId, InvoiceAccount, DataAreaId from projInvoiceJour where projInvoiceJour.RecId == recid;

           ProjInvoiceParameters ProjInvoiceParameters;

           select customEnableAdditionalAttachments, customEnableEmailTemplate, RecId from ProjInvoiceParameters

               where enwProjInvoiceParameters.DataAreaId == projInvoiceJour.DataAreaId;

           str name = strFmt('%1', reportName);

           if(printSettings.printMediumType() == SRSPrintMediumType::Email  &&

              name == 'PSAProjInvoiceCopy.ENWReport'               &&

              ProjInvoiceParameters.CustomEnableAdditionalAttachments == NoYes::Yes)

           {

               SrsReportEMailDataContract emailContract = printSettings.parmEMailContract();

               CustTable custTable;

               select RecId from custTable where custTable.AccountNum == projInvoiceJour.InvoiceAccount;

               DocuRef fileDocuRef = DocuRef::findTableIdRecId(projInvoiceJour.DataAreaId, custTable.TableId, custTable.RecId);

               System.DateTime dateTime = System.DateTime::get_UtcNow();

               str utcTimeAsStr = dateTime.ToString('yyyyMd');

               str                 milliSecondsAlive = int2str(winApi::getTickCount());

               str                 timeStr;

               timeStr = strFmt("%1 %2", time2str(timeNow(), 3, 1), substr(milliSecondsAlive, strlen(milliSecondsAlive)-2, 2));

               Filename _filename = CompanyInfo::find().company() + '_' + reportName  + '_' + utcTimeAsStr + '_' + timeStr + '.PDF';

               System.Byte[] reportBytes = srsProxy.renderReportToByteArray(reportContract.parmReportPath(),

                                                       reportParamArray,

                                                       printSettings.fileFormat(),

                                                       printSettings.deviceInfo());

               System.IO.MemoryStream mstream = new System.IO.MemoryStream(reportBytes);

               //From Address

               str  fromAddress ;

               UserInfo userInfo;

               SysUserInfo sysUserInfo = SysUserInfo::find(curuserid());

               // try to get it from SysUserInfo

               str value = strrtrim(strltrim(sysUserInfo.emailDisplay()));

               if (strlen(value) == 0)

               {

                   select firstonly Id, NetworkAlias from userInfo where userInfo.Id == curuserid();

                   if (strlen(userInfo.NetworkAlias) > 0)

                   {

                       value = userInfo.networkAlias;

                       if (!strContains(userInfo.networkAlias, '@') && strLen(userInfo.networkDomain) > 0)

                       {

                           value += '@' + userInfo.networkDomain;

                       }

                   }

               }

               fromAddress = value;

               //Attachment message builder

               var messageBuilder = new SysMailerMessageBuilder();

               //Customer Billing invoice report attachment via email

              messageBuilder.setFrom(fromAddress)

                             .addTo(emailContract.parmTo())

                             .addCc(emailContract.parmCc())

                             .setSubject(emailContract.parmSubject())

                             .setBody(emailContract.parmBody())

                             .addAttachment(new System.IO.MemoryStream(reportBytes), _filename);

               //Customer specific attachments via email

               messageBuilder.setFrom(fromAddress)

                             .addTo(emailContract.parmTo())

                             .addCc(emailContract.parmCc())

                             .setSubject(emailContract.parmSubject())

                             .setBody(emailContract.parmBody())                          

                             .addAttachment(DocumentManagement::getAttachmentStream(fileDocuRef), strFmt('%1.%2', fileDocuRef.Name, fileDocuRef.fileExtension()));

               //Final Attachment to the mailer

               var mailer = SysMailerFactory::getNonInteractiveMailer();

               mailer.sendNonInteractive(messageBuilder.getMessage());

           }      

    This code send two mails

    one mail from standard sending report next printReport();

    another mail customization code from 3rd line to end  which i am passing sending two PDF attachments Report PDF and docuref PDF

    Kindly suggest is there any way to skip the next printReport() using Chain of command in if condition or is there any other way to achive this.

    Thanks in advance.

  • Suggested answer
    Pedro Tornich Profile Picture
    Pedro Tornich 955 on at
    RE: D365 Adding additional attachment during Email print.

    Hi Irtag,

    The method toEmail() from the SrsReportRunPrinter class sends the email, but inside this method right before sending the email it calls the  onToEmail() method from SRSPrintDestinationSettingsDelegates class, which in return will call the toEmail() delegate and if this delegate returns false it terminates the sending process without errors.

    Check the line 11 of the following method:

    private void toEmail()
    {
        guid printId = newGuid();
        SSRSReportRuntimeEventSource::EventWriteRenderReportToEmailDetailTaskStart(
            "Printing report to email started.",
             reportContract.parmReportExecutionInfo().parmReportRunId(),
             printId);
    
        try
        {
            if(!delegatesHelper.onToEmail(this, reportContract, reportParamArray))
            {
                SSRSReportRuntimeEventSource::EventWriteRenderReportToEmailDetailTaskStop(
                "Printing report to email ended.",
                 reportContract.parmReportExecutionInfo().parmReportRunId(),
                 printId);
                return;
            }
    
            if(!printSettings)
            {
                throw error(strfmt("@SYS318601", 'printSettings'));
            }
    
            SrsReportEMailDataContract emailContract = printSettings.parmEMailContract();
    
            if(emailContract)
            {
                SRSReportFileFormat fileFormat = emailContract.parmAttachmentFileFormat();
                SRSImageFileFormat imageFormat = emailContract.parmAttachmentImageFileFormat();
                str attachmentFileName;
                System.Byte[] reportBytes;
    
                attachmentFileName = this.getAttachmentName(fileFormat, imageFormat);
    
                reportBytes = this.renderReportToFile(attachmentFileName, fileFormat);
    
                if (!delegatesHelper.onToSendEmail(reportBytes, this, reportContract, reportParamArray))
                {
                    return;
                }
    
                if (reportBytes && reportBytes.Length > 0)
                {
                    if (this.parmReportRunMailer().emailReportWithRetry(emailContract, reportBytes, attachmentFileName))
                    {
                        // The report has been successfully sent as attachment to email.
                        str message = delegatesHelper.onToEmailSucceed(this, reportContract);
                        if(message == '')
                        {
                            message = "@SYS344685";
                        }
                        info(message);
                    }
                    else
                    {
                        str message  = delegatesHelper.onToEmailFailed(this, reportContract);
                        if(message)
                        {
                            info(message);
                        }
                    }
                }
            }
        }
        finally
        {
            SSRSReportRuntimeEventSource::EventWriteRenderReportToEmailDetailTaskStop(
            "Printing report to email ended.",
             reportContract.parmReportExecutionInfo().parmReportRunId(),
             printId);
        }
    }

    So, you can subscribe to this delegate and write your own logic to send emails.

    You might like to replicate the logic from the method emailReport() from SrsReportRunMailer class, as you can see in the following piece of code, this method attaches the report and sends the email:

    public boolean emailReport(SrsReportEMailDataContract emailContract, System.Byte[] reportBytes, str fileName)
    {
        SRSReportFileFormat fileFormat;
        boolean result = false;
    
        // Check args and validate contract
        if(!emailContract)
        {
            throw error(strfmt("@SYS318601", 'emailContract'));
        }
    
        if(reportBytes.Length == 0)
        {
            throw error(strfmt("@SYS318601", 'reportBytes'));
        }
    
        if(!fileName)
        {
            throw error(strfmt("@SYS318601", 'fileName'));
        }
    
        emailContract.validate();
    
        // Using mailer to send out report non interactively.
        if(mailer == null)
        {
            this.initMailer();
        }
    
        if (mailer)
        {
            // Construct mailer message builder and pass all the values from the contract.
            var messageBuilder = new SysMailerMessageBuilder();
            messageBuilder.setFrom(fromAddress)
                          .addTo(emailContract.parmTo())
                          .addCc(emailContract.parmCc())
                          .setSubject(emailContract.parmSubject())
                          .setBody(emailContract.parmBody())
                          .addAttachment(new System.IO.MemoryStream(reportBytes), fileName);
            result = mailer.sendNonInteractive(messageBuilder.getMessage());
        }
    
        return result;
    }

    Consider creating a class that extends the SrsReportRunMailer to add a new method that attaches all the files you need and send the email, then use it in your code.

    Best,
    Pedro

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

November Spotlight Star - Khushbu Rajvi

Congratulations to a top community star!

Forum Structure Changes Coming on 11/8!

In our never-ending quest to help the Dynamics 365 Community members get answers faster …

Dynamics 365 Community Platform update – Oct 28

Welcome to the next edition of the Community Platform Update. This is a status …

Leaderboard

#1
André Arnaud de Calavon Profile Picture

André Arnaud de Cal... 291,791 Super User 2024 Season 2

#2
Martin Dráb Profile Picture

Martin Dráb 230,488 Most Valuable Professional

#3
nmaenpaa Profile Picture

nmaenpaa 101,156

Leaderboard

Featured topics

Product updates

Dynamics 365 release plans