Skip to main content

Sending Email with attached Excel file in D365 FO and Ax 2012 using SysMailerMessageBuilder Framework X++

Moving on from Ax dynamics 2012 to D365, there are multiple frameworks that has been added on by Microsoft to use its basic functionality.In the case of email sending functionality in Dynamics Ax we probably prefer SMTP in 2012 that is to add on the code and bypassing its required parameter.While in D365 we could use SysMailerMessageBuilder framework to perform our work that is sending email to multiple recipient too.

The piece of code firstly has been attached  is using SysMailerMessageBuilder framework

Requirements: 

*Our objective it to Generate an excel File and attach it to an email and sending it to multiple
recipients
*The file data will be from financial dimension view and it will be distributed into
three worksheets that is D1,D2 and D4_5 according to conditions
*Data of Data Entity of D4 & D5 will be inserted into same worksheet that is D4_5



public class IRC_FinancialDimExportService extends SysOperationServiceBase
{
    public const str T1 = 'T1-D1';
    public const str T3 = 'T3-D3';
    public const str T5 = 'T5-D4';
    public const str D4 = 'D04Location';
    public const str D5 = 'D05Entity_Code';
  // used for validating email
    public const Str MatchEmailPattern =
       @"^(([\w-]+\.)+[\w-]+|([a-zA-Z]{1}|[\w-]{2,}))@"
     + @"((([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?
       [0-9]{1,2}|25[0-5]|2[0-4][0-9])\."
     + @"([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?
       [0-9]{1,2}|25[0-5]|2[0-4][0-9])){1}|"
     + @"([\w-]+\.)+[a-zA-Z]{2,4})$";
 
// setEmailParm is used when we want to send
// email to multiple recipients 
public SysMailerMessageBuilder setEmailParm(IRC_FinancialDimExportContract _contract)
    {
        container                                con       = str2con(_contract.parmEmails());
        SysMailerMessageBuilder    mailer   = new SysMailerMessageBuilder();
        UserInfo                               user        = xUserInfo::find();
        SysUserInfo                         sysUse    = SysUserInfo::find(user.id);
        str                                        sender      = sysUser.getSendAsEmail(user);
        IRC_RecipientEmail           recipient;

        for(int i=1;i<=conLen(con);i++)
        {
            if(this.validateEmail(conPeek(con,i)) == true)
            {
                recipient   = conPeek(con,i);
                mailer.addTo(recipient);
            }
            else
            {
                Global::warning(strFmt("%1 , This email is incorrect",conPeek(con,i)));
            }
        }
     
        mailer.setSubject("Financial Details");
        mailer.setFrom(sender);
        mailer.setBody("PFA");
        return mailer;
    }
   //emailSender has been used to attach Excel file
  //through stream
    public  void emailSender(IRC_FinancialDimExportContract _contract)
    {
        try
        {
            SysMailerMessageBuilder objMailer      = new SysMailerMessageBuilder();
            SysMailerSMTP                smtp               = new SysMailerSMTP();
            container                           fileTitle           = ['T1-D1','D01Project_Code','T3-                  D3','D03Program_Area','T5-D4','D04Location'];
            str                     fileName;

            objMailer = this.setEmailParm(_contract);
            System.IO.MemoryStream memoryStream;
     
            for(int iterator=1 ;iterator <=conLen(fileTitle) ;iterator++)
            {
             
                memoryStream = this.csvFileExporter(conPeek(fileTitle,iterator),conPeek(fileTitle,iterator+1));
                memoryStream.Seek(0, System.IO.SeekOrigin::Begin);
                fileName = conPeek(fileTitle,iterator)+'.csv';
                //The below line used to attach excel file to email.
         
                objMailer.addAttachment(memoryStream, fileName, "application/csv");
//For PDF we use objMailer.addAttachment(memoryStream, fileName, "application/pdf");
                memoryStream.Flush();
                iterator++;
            }
            try
            {
                smtp.sendNonInteractive(objMailer.getMessage());
                Global::info("Email has been sent sucessfully");
            }
            catch(Exception::CLRError)
            {
                error(CLRInterop::getLastException().toString());
            }
     
        }
     
        catch(Exception::CLRError)
        {
            error(CLRInterop::getLastException().toString());
        }
    }
   //Used for validating email's structure
    private boolean validateEmail(Email _email)
    {
        boolean   ret = true;
        Boolean   VerifEmails;
     
        System.Text.RegularExpressions.Match emailMatch;
 

        if(_email != '')
        {
            new InteropPermission(InteropKind::ClrInterop).assert();
            emailMatch = System.Text.RegularExpressions.Regex::Match(_email,MatchEmailPattern);
            VerifEmails = emailMatch.get_Success();
            ret = VerifEmails;
            CodeAccessPermission::revertAssert();
        }
       
        return ret;
    }
  // The pupose of this function is to write into
//excel file and create it
    public System.IO.MemoryStream csvFileExporter(Filename fName , Name _dimAttribute)
    {
        System.IO.MemoryStream              memoryStreamer = new System.IO.MemoryStream();
        commaStreamIo           iO;
        boolean                         isFirstRow = true;
        Name                            tempDimAttribute;
        str                                 financialDesc,tempStr,fPrefix,code;
        container                      conDim,conDimTemp;
     
        iO = commaStreamIo::constructForWrite();
        container header = ["Type","Code","Description"];

        iO.writeExp(header);
        if(_dimAttribute == D4)
        {
            tempDimAttribute = D5;
        }

        conDim = this.financialDimDetails(_dimAttribute);
        for(int iterator=1 ;iterator <=conLen(conDim) ;iterator++)
        {
            tempStr =  isFirstRow ? fName : ''; 
            fPrefix = fName==T1?'': (fName == T3 && fName != T1) ? 'T3' : 'T5';
            conDimTemp = conPeek(conDim,iterator);
            code = con2Str(conPeek(conDimTemp,1));
            if(strScan(code,'_',0,strLen(code)) == 0)
            {
                financialDesc = subStr(code +'-'+ fPrefix +' '+ conPeek(conDimTemp,2),1,29);
                financialDesc = strLTrim(financialDesc);
                financialDesc = System.Text.RegularExpressions.Regex::replace(financialDesc,@"[^A-Za-z0-9.\s]", "-");
                financialDesc = strLTrim(financialDesc);
                container line =  [tempStr,code,financialDesc];
                iO.writeExp(line);
                isFirstRow = false;
            }
        }
     
        memoryStreamer = iO.getStream();
        memoryStreamer.Position = 0;
     
        return memoryStreamer;
    }

    //The purpose of this function is to return
    //a container that holds Financial dimensions values according to
    //financial dimension received through parameter
    public container  financialDimDetails(Name _FinDimAttribute)
    {
        FINANCIALDIMENSIONVALUEENTITYVIEW financialDimension,financialDimensionAttr;
        DimensionAttrValueLedgerOverride dimensionAttrValueLedgerOverride;
        container                   conDim;
        while select Description,DimensionValue,LegalEntityId,RecId from financialDimension
                     where financialDimension.DimensionAttribute == _FinDimAttribute && financialDimension.LegalEntityId == curExt()
        {
            conDim += [[financialDimension.DimensionValue,financialDimension.Description ]];
        }
     
     
        while select DimensionAttributeValue,DimensionValue,Description from financialDimensionAttr
                    where financialDimensionAttr.LegalEntityId == '' &&  financialDimensionAttr.DimensionAttribute == _FinDimAttribute
              outer join IsSuspended,DimensionAttributeValue from dimensionAttrValueLedgerOverride
                    where    dimensionAttrValueLedgerOverride.DimensionAttributeValue == financialDimensionAttr.DimensionAttributeValue
        {
            if( dimensionAttrValueLedgerOverride.IsSuspended == NoYes::No
                    && financialDimensionAttr.IsSuspended == NoYes::No
                    && dimensionAttrValueLedgerOverride.company()==curExt())
            {
                if(financialDimensionAttr.DimensionValue!='' && financialDimensionAttr.Description!='')
                {
                    conDim += [[financialDimensionAttr.DimensionValue,financialDimensionAttr.Description ]];
                }
            }
        }
        return conDim;
    }

}Preview :






Below Piece of code could be used in Ax 2012 /D365 In many cased users faces problem in attaching excel file but for sending simple emails without attachment it works perfect.



class TestEmail

{





    /// <summary>

    /// Runs the class with the specified arguments.

    /// </summary>

    /// <param name = "_args">The specified arguments.</param>

public static void Main(Args args)

{

    // Set these variables.

    str                                   sender = 'IRCIntegra@rescue.org';

    str                                   recipient = 'shayan.ar007@gmail.com';

    str                                   cc = 'shayan.ar007@gmail.com';

    str                                   subject = 'TheAxapta_Subject';

    str                                   body = 'TheAxapta_Msg';

    str                                   fileName = @'C:\Users\Admin1986cca646\Desktop\\D Codes FCM.xlsx';

    List                                  toList;

    List                                  ccList;

    ListEnumerator                        le;

    Set                                   permissionSet;

    System.Exception                      e;

    str                                   mailServer;
    int                                   mailServerPort;
    System.Net.Mail.SmtpClient            mailClient;
    System.Net.Mail.MailMessage           mailMessage;
    System.Net.Mail.MailAddress           mailFrom;
    System.Net.Mail.MailAddress           mailTo;
    System.Net.Mail.MailAddressCollection mailToCollection;
    System.Net.Mail.MailAddressCollection mailCCCollection;
    System.Net.Mail.AttachmentCollection  mailAttachementCollection;
    System.Net.Mail.Attachment            mailAttachment;
    ;
    try
    {
        toList = strSplit(recipient, ';');
        ccList = strSplit(cc, ';');
        permissionSet = new Set(Types::Class);
        permissionSet.add(new InteropPermission(InteropKind::ClrInterop));
        permissionSet.add(new FileIOPermission(filename, 'rw'));
        CodeAccessPermission::assertMultiple(permissionSet);
        mailServer = SysEmaiLParameters::find(false).SMTPRelayServerName;
        mailServerPort = SysEmaiLParameters::find(false).SMTPPortNumber;
        mailClient = new System.Net.Mail.SmtpClient(mailServer, mailServerPort);
        le = toList.getEnumerator();
        le.moveNext();
        mailFrom = new System.Net.Mail.MailAddress(sender);
        mailTo  = new System.Net.Mail.MailAddress(strLTrim(strRTrim(le.current())));
        mailMessage = new System.Net.Mail.MailMessage(mailFrom, mailTo);
        mailToCollection = mailMessage.get_To();
        while (le.moveNext())
        {
            mailToCollection.Add(strLTrim(strRTrim(le.current())));
        }
        le = ccList.getEnumerator();
        mailCCCollection = mailMessage.get_CC();
        while (le.moveNext())
        {
            mailCCCollection.Add(strLTrim(strRTrim(le.current())));
        }
        mailMessage.set_Priority(System.Net.Mail.MailPriority::High);
        mailMessage.set_Subject(subject);
        mailMessage.set_Body(body);
        mailAttachementCollection = mailMessage.get_Attachments();
        mailAttachment = new System.Net.Mail.Attachment(fileName);
        mailAttachementCollection.Add(mailAttachment);
        mailClient.Send(mailMessage);
        mailMessage.Dispose();
        CodeAccessPermission::revertAssert();
        info("Email sent.");
    }
    catch (Exception::CLRError)
    {
        e = ClrInterop::getLastException();
        while (e)
        {
            info(e.get_Message());
            e = e.get_InnerException();
        }
        CodeAccessPermission::revertAssert();
    }
}

}


Comments

Popular posts from this blog

Update record set using update_recordset in d365 F&O | AX 2012 using X++

Hello Devs! One the most common condition that we being a developer faces is to update existing records set that is mostly done on report level when we need to add new fields in the Temporary table and we don't wants to make extensions on class level. Here is a small piece of code that will assist you in updating existing record sets.  update_recordset custAgingReportTmp                 setting                 CMTCustInternalCollector = hcmWorker.PersonnelNumber,                 PaymDayId                         = custTable.PaymDayId,                 PaymTermId                       = custTable.PaymTermId,                 CashDisc      ...

Import DB backup from .BacPac file in D365 F&O using CMD

Hello Devs! This is one of the most quickest way to import DB from .Bacpac file. Bacpac files are those which are generated from Tier 2 or higher environments as a DB backup.   Alternate Approach ----> Using wizard Prerequisites: 1) Access to LCS folder Asset library --> Database Backup Incase you don't see the folder ask your project owner to give you the access. Step 1:  Download the file and save it in local non-user directory.  Step 2: - Services to be stopped World Wide Web Publishing Service Management Reporter 2012 Process Service Microsoft Dynamics 365 Unified Operations: Batch Management Service Microsoft Dynamics 365 Unified Operations: Data Import Export Framework Service - Download SqlPackage.Exe.  Download link :  https://learn.microsoft.com/en-us/sql/tools/sqlpackage/sqlpackage-download?view=sql-server-ver15 Extract it and Open CMD as admin.  Step 3 :  Navigate to directory where you have extracted the SqlPackage.Exe folder us...

Adding D365 F&O Extension in Visual Studio 2022 | Dynamics

  Hello Devs! This is a very common scenario when it comes to enabling D365 extension within VS 2022, so for that below are list of steps needs to be done to enable extension for development. Please note for Older VS versions please refer to my blog below : https://www.linkedin.com/feed/update/urn:li:ugcPost:7033677029451440128/ Prerequisites: Visual Studio 2022 should be installed before performing below steps. Step # 1: Login to LCS (Microsoft Dynamics lifecycle services). In the main page after login there is a option of Shared asset library (without entering into project folder). Step # 2: In the list of sub menus , Select Software deployment package and search for latest Service update. For my case I am downloading 10.0.41 Step # 3 : Once you have downloaded it. Unzip the folder and search for FinanceAndOperations_10.0.2015.54_Application\DevToolsService\Scripts Before installation you will not be able to see Dynamics 365 option within VS 2022. Now please clos...