Send Email from D365 FO X++

There are multiple ways in Dynamics to send emails to the users.  for  sending  an email from dynamics we need to configure a few things in the system adminstartion.

Configuration : 

Go to System Administrator> Setup> Email> Email parameters










Fill the details in the Configuration Tab as below











Provide the user name and password in the SMTP settings. Fill in the server Name and Port (you can find these details from your EMail > EMail Settings > SMTP settings)












Now Send the test Email to confirm the settings. Make sure "send as" is set to your admin user which you have setup in the step below.

setup the To Mail in below settings and click on "Send test Email"







Sometimes you may get  the issue as below.







To resolve the issue, ensure that the Admin Email ID you set up in the User Options > Accounts > Sender Email ID matches the Send As email ID.


Send email from D365 using Email templates and email history tables :

When we are sending mails using Email templates the following terms will be used : 

  • Email template - Templates stored underOrganization administration>Setup>Organization email templates
  • The underlying tables are SysEmailTable and SysEmailMessageTable.
  • Placeholder - a string enclosed within percent (%) symbols. E.g. %placeholder%.
  • Email processing table - two tables SysOutgoingEmailTable and SysOutgoingEmailData that store emails to be processed by a batch job. Emails can be found under System administration>Periodic tasks>Email processing>Email sending status
Below is the sample code for sending emails using templates. This code allows us to monitor the email sending history and resend in case of failures.
    internal final class DAXSendMailToResponsiblePerson extends RunBaseBatch
{
    /// <summary>
    /// Class entry point. The system will call this method when a designated menu
    /// is selected or when execution starts and this class is set as the startup class.
    /// </summary>
    /// <param name = "_args">The specified arguments.</param>
    public static void main(Args _args)
    {
        DAXSendMailToResponsiblePerson SendMailToResponsiblePerson = DAXSendMailToResponsiblePerson::construct();
        if (SendMailToResponsiblePerson.prompt())
        {
            SendMailToResponsiblePerson.run();
        }
    }

    public void run()
    {
        LedgerPeriodCloseTemplateTask                   ledgerPeriodCloseTemplateTask;
        LedgerPeriodCloseProjectTaskTemplateRelation    ledgerPeriodCloseProjectTaskTemplateRelation;
        LedgerPeriodCloseProjectTask                    ledgerPeriodCloseProjectTask;
        utcdatetime                                     duedate;
        LedgerPeriodCloseWorker                         periodworker;
        DirPersonUser                                   dirpersonuser;


        while select LedgerPeriodCloseProjectTask
            where  LedgerPeriodCloseProjectTask.DAXMailRemainder == NoYes::Yes
            && ledgerPeriodCloseProjectTask.DAXMailSentStatus == NoYes::No
        {
            duedate = DateTimeUtil::addMinutes(LedgerPeriodCloseProjectTask.DueDateTime, -15);

            if(duedate <= DateTimeUtil::getSystemDateTime())
            {
                try
                {
                 ttsbegin;
                    SysEmailSystemTable        sysEmailTable        = SysEmailSystemTable::find(LedgerPeriodCloseProjectTask.DAXHMailTemplate);
                    SysEmailMessageSystemTable sysEmailMessageTable = SysEmailMessageSystemTable::find(sysEmailTable.EmailId, sysEmailTable.DefaultLanguage);

                str messageBody = sysEmailMessageTable.Mail;
                str subject = sysEmailMessageTable.Subject;

                Map placeholderMap = new Map(Types::String, Types::String);

                HcmWorker  hcmWorker  = HcmWorker::find(LedgerPeriodCloseProjectTask.ResponsibleWorker);
                if (hcmWorker)
                {
                    dirpersonuser =  HcmWorker::findPersonUser(hcmWorker.RecId);

                }

                placeholderMap.insert("Employee",hcmWorker.name());
                placeholderMap.insert("Task", LedgerPeriodCloseProjectTask.Name);
               
                messageBody = SysEmailMessage::stringExpand(messageBody, placeholderMap);
                subject     = SysEmailMessage::stringExpand(subject, placeholderMap);
                SysOutgoingEmailTable      outgoingEmailTable;
                SysOutgoingEmailData       outgoingEmailData;
                outgoingEmailTable.EmailItemId                  = EventInbox::nextEventId();
                outgoingEmailTable.TemplateId                   = sysEmailTable.EmailId;
                outgoingEmailTable.Sender                       = sysEmailTable.SenderAddr;
                outgoingEmailTable.SenderName                   = sysEmailTable.SenderName;
                outgoingEmailTable.Recipient                    = SysUserInfo::getUserEmail(dirpersonuser.User);
                outgoingEmailTable.Subject                      = subject;
                outgoingEmailTable.Message                      = messageBody;
                outgoingEmailTable.Priority                     = sysEmailTable.Priority;
                outgoingEmailTable.WithRetries                  = true;
                outgoingEmailTable.RetryNum                     = 0;
                outgoingEmailTable.UserId                       = curuserid();
                outgoingEmailTable.Status                       = SysEmailStatus::Unsent;
                outgoingEmailTable.SessionLoginDateTime         = DateTimeUtil::addMinutes(DateTimeUtil::getSystemDateTime(), 2);
                outgoingEmailTable.LatestStatusChangeDateTime   = DateTimeUtil::getSystemDateTime();
                outgoingEmailTable.insert();
                if (outgoingEmailTable)
                {
                    SysEmailDistributor::construct().run();
                }
                    ledgerPeriodCloseProjectTask.reread();
                    ledgerPeriodCloseProjectTask.selectForUpdate(true);
                    ledgerPeriodCloseProjectTask.DAXMailSentStatus =  NoYes::Yes;
                    ledgerPeriodCloseProjectTask.doUpdate();
                    ttscommit;

            }
                catch (Exception::Deadlock)
                {
                    continue;
                }
                catch (Exception::Error)
                {
                    continue;
                }
                catch (Exception::Warning)
                {
                    CLRInterop::getLastException();
                    continue;
                }
                catch (Exception::CLRError)
                {
                    CLRInterop::getLastException();
                    continue;
                }
            }
        }

    }

    public server static DAXSendMailToResponsiblePerson construct()
    {
        return new DAXSendMailToResponsiblePerson();
    }

    public client server static ClassDescription description()
    {
        return "Send Mail remainders";
    }

    protected boolean canGoBatchJournal()
    {
        return true;
    }

    public boolean canGoBatch()
    {
        return true;
    }

    public boolean runsImpersonated()
    {
        return true;
    }

    protected boolean canRunInNewSession()
    {
        return true;
    }

}

Send Email with an attachment using SysMailer classes

Let's send an email with a report attachment to the corresponding email address. In my case, I'm sending the email to the registered vendor's email address when a vendor registers with the vendor portal.

Below is the Method responsible for sending mail with the  report stream.
    public void sendEmail(System.IO.MemoryStream   _mstream, Filename    _fileName, str _requestID,Email _mailFrom,Email _mailTo)
    {
        Map                                         templateTokens;
        str                                         emailSubject,emailBody;
        Filename                                    fileName;
        SysEmailTable                               SysEmailTable;
       
        var messageBuilder      = new SysMailerMessageBuilder();
        templateTokens          = new Map(Types::String, Types::String);

        
        emailSubject = "Vendor Request Report";
        templateTokens.insert("@SYS74341", emailSubject);
        templateTokens.insert("@SYS4009003", _fileName);
        emailBody =  strFmt("A vendor request has been registerd with the vendor request id %1.Please find the attached document for more information.",_requestID);
        emailBody = SysEmailMessage::stringExpand(emailBody, SysEmailTable::htmlEncodeParameters(templateTokens));
        emailBody = strReplace(emailBody, '\n', '<br>');
        emailBody +='<br><br>Thanks,<br>' +'ERPTeam';
        messageBuilder.addTo(_mailTo)
                                    .setSubject(emailSubject)
                                    .setBody(emailBody)
                                    .addCC("");

        messageBuilder.setFrom(_mailFrom, "ERPTeam") ;
        messageBuilder.addAttachment(_mstream, _fileName);

        SysMailerFactory::sendNonInteractive(messageBuilder.getMessage());

        info(strFmt("Email sent successfully for vendor request %1",_requestID));
    }
Additionally, below is the report bytes generate method used to generate and send the stream to above method.
    public static void generateReportAsBytes(VendVendorRequestNewVendor   _vendVendorRequestNewVendor,VendProspectiveVendorRegistrationRequest _vendorRegistrationRequest)
    {
        System.IO.MemoryStream                      mstream;
        Filename                                    fileName = strFmt("VendorRequest - %1.pdf", _vendVendorRequestNewVendor.RequestId);
        Args                                        args = new Args();
        PROSendEmailForVendRequest                  sendEmailForVendRequest = new PROSendEmailForVendRequest();

        VendProspectiveVendorRegistrationRequest    vendProspectiveVendorRegistrationRequest;
        VendProspectiveVendorRegistration           vendProspectiveVendorRegistration;
        SysEmailParameters                          sysEmailParameters = SysEmailParameters::find();

        try
        {
            args.record(_vendVendorRequestNewVendor);
            // get the report Contract tp intilize the request ID
            PRO_vendRequestContractClass vendRequestContractClass = new PRO_vendRequestContractClass();
            vendRequestContractClass.parmVendRequestNumber(_vendVendorRequestNewVendor.RequestId);

            // Runs the report 
            SrsReportRunController  srsReportRunController = new SrsReportRunController();
            srsReportRunController.parmReportName(ssrsReportStr(PRO_VendRequest, Report));
            srsReportRunController.parmExecutionMode(SysOperationExecutionMode::Asynchronous);
            srsReportRunController.parmShowDialog(false);
            srsReportRunController.parmReportContract().parmRdpContract(vendRequestContractClass);
            srsReportRunController.parmReportContract().parmReportExecutionInfo(new SRSReportExecutionInfo());
            srsReportRunController.parmReportContract().parmReportServerConfig(SRSConfiguration::getDefaultServerConfiguration());

            //Set the Printing settings for the report generation
            SRSPrintDestinationSettings printerSettings = srsReportRunController.parmReportContract().parmPrintSettings();
            printerSettings.printMediumType(SRSPrintMediumType::File);
            printerSettings.fileFormat(SRSReportFileFormat::PDF);
            printerSettings.parmFileName(fileName);
            printerSettings.overwriteFile(true);

            // Run the report service class to get the report stream 
            SRSReportRunService srsReportRunService = new SrsReportRunService();
            srsReportRunService.getReportDataContract(srsReportRunController.parmReportContract().parmReportName());
            srsReportRunService.preRunReport(srsReportRunController.parmReportContract());
            Map reportParametersMap = srsReportRunService.createParamMapFromContract(srsReportRunController.parmReportContract());
            Microsoft.Dynamics.AX.Framework.Reporting.Shared.ReportingService.ParameterValue[]  parameterValueArray = SrsReportRunUtil::getParameterValueArray(reportParametersMap);

            SRSProxy srsProxy = SRSProxy::constructWithConfiguration(srsReportRunController.parmReportContract().parmReportServerConfig());

            System.Byte[] reportBytes = srsproxy.renderReportToByteArray(srsReportRunController.parmreportcontract().parmreportpath(),
                                                parameterValueArray,
                                                printerSettings.fileFormat(),
                                                printerSettings.deviceinfo());
            mstream         = new System.IO.MemoryStream(reportBytes);

            // send the stream to send Mail method
            sendEmailForVendRequest.sendEmail(mstream, fileName,_vendVendorRequestNewVendor.RequestId, sysEmailParameters.SMTPUserName,_vendorRegistrationRequest.Email);
        }
        catch
        {
            throw error(sendEmailForVendRequest.getErrorStr());
        }
    }
Trigger the above methods from Main method. 
    public static void main(Args _args)
    {
        DaxvendRequestContractClass                contractclass =  new DaxvendRequestContractClass();
        VendProspectiveVendorRegistrationRequest    vendProspectiveVendorRegistrationRequest;
        VendProspectiveVendorRegistration           vendProspectiveVendorRegistration;
        VendVendorRequestNewVendor                  vendVendorRequestNewVendor;
        SysEmailParameters                          sysEmailParameters;
        PurchParameters                             purchParameters = PurchParameters::find();

        // Get the data fro Vendor request table for the current vendor
        VendVendorRequest       vendVendorRequest = _args.record();

        if (purchParameters.DaxSendEmailForVendorRequest)
        {
            select vendVendorRequestNewVendor
                where vendVendorRequestNewVendor.RecId == vendVendorRequest.RecId;

            select vendProspectiveVendorRegistration
                where vendProspectiveVendorRegistration.RecId == vendVendorRequestNewVendor.ProspectiveVendorRegistration;

            select vendProspectiveVendorRegistrationRequest
                where vendProspectiveVendorRegistrationRequest.RecId == vendProspectiveVendorRegistration.RegistrationRequest;

            DaxSendEmailForVendRequest::generateReportAsBytes(vendVendorRequestNewVendor,vendProspectiveVendorRegistrationRequest);
        }
        else
        {
            checkFailed(strFmt("field %1 in table %2 must be enabled. Please validate the setup in procurment and sourcing parameters", fieldPName(PurchParameters, DaxSendEmailForVendorRequest), tablePName(PurchParameters)));
        }
    }


We can also Send Email using standard email alerts, Business events and Power automate flow, we can see that in my upcoming posts.

Thanks !!



No comments:

Post a Comment