Saturday, June 26, 2010

Sending emails from Dynamics AX

It’s a common requirement to have Dynamics AX to send emails as part of the business processes such as delivering the invoice/statement to customers by email. Out of box Dynamics AX already has the framework provided in X++ classes for developers to send email using X++ code. There are currently three ways to do so and each has its own features/limitation as described below:-

1) SysInetMail class
This is used in the standard MorphX report if E-mail recipient is selected as Print medium. It requires an email program to be installed at the AX client computer and the mail message will be sent using the email program. The advantage using this method is it provides the flexibility for users to edit the email before it get sent out, but also means you can’t use it automate the sending of email because it requires the user’s intervention to manually click the Send button.
One workaround for this is you can modify the reportSendMail() method in the Info class. This method is called when the report’s print medium is specified as E-mail recipient. So you can modify this method to email the report automatically using the two other following options instead of SysInetMail class.
SysINetMail m = new SysINetMail();
m.sendMailAttach(..);

2) SysEmailTable::sendMail(..) table method
This method uses the SysMailer class and the underlying framework which utilise the CDO COM object to send email. It is commonly used by standard Dynamics AX system when emailing function is required such as the sending of Alert email, Workflow, etc.
To use this method, you need to set up two things:-i) SMTP relay server information in the E-mail parameters (Administration > Setup > E-mail parameters), ii) Email template that specify the subject and the body of email, supported in plain text or HTML format (Basic > Setup > E-mail templates), and the use of placeholders.
There are a lot of advantages using this way of sending email:- The email body is user-definable using Email template and placeholders, the email could be sent without user intervention, the email sending process can be scheduled in batch job and retry sending when failed, and you also can attach file to the email (although it only support one file attachment).
SysEmailId sysEmailId = 'Alerts';
str  recipientEmail = 'john@contoso.com';
;
SysEmailTable::sendMail(
    sysEmailId,
    SysEmailTable::find(sysEmailId).DefaultLanguage,
    recipientEmail,
    null,
    '',
    '',
    true,
    curuserid(),
    true);

3) System.Net.Mail .NET class
The last option is to use the .NET classes to send email. It is very simple to use and yet it provides a lot of flexibilities. You can specify multiple recipients, cc and bcc, attach multiple files, turn on the email delivery notification and read receipt notification, and you can also archive the mail message in .eml file format.
System.Net.Mail.MailMessage mailMessage;
System.Net.Mail.SmtpClient smtpClient;
System.Net.Mail.MailAddress mailFrom;
System.Net.Mail.MailAddress mailTo;
str    smtpServer;
;

mailFrom = new System.Net.Mail.MailAddress('sender@contoso.com',"Sender name");
mailTo  = new System.Net.Mail.MailAddress('recipient@contoso.com',"Recipient name");

smtpServer = SysEmaiLParameters::find(false).SMTPRelayServerName;// using the SMTP server ip setup in Email Parameters
mailMessage = new System.Net.Mail.MailMessage(mailFrom,mailTo);
mailmessage.set_Subject('This is email subject');
mailmessage.set_Body('This is email body');

smtpClient = new System.Net.Mail.SmtpClient(smtpServer);
smtpClient.Send(mailmessage); 
mailMessage.Dispose();

If you want to use Email template instead of hard code the email body, consider using the following approach:-
System.Net.Mail.MailMessage mailMessage;
SysEmailMessageTable  message;
str    messageBody;
SysEmailId   sysEmailId = 'Alerts';
Map    mappings = new Map(Types::String, Types::String);
;
...
mappings.insert('message', 'This is the message');
message = SysEmailMessageTable::find(sysEmailId, SysEmailTable::find(sysEmailId).DefaultLanguage);
messageBody = SysEmailMessage::stringExpand(message.Mail, SysEmailTable::htmlEncodeParameters(mappings));
messageBody = WebLet::weblets2Html4Help(messageBody, '');
mailMessage.set_Body(messageBody);
mailMessage.set_IsBodyHtml(true);
...
To enable the Delivery Notification feature:-
System.Net.Mail.DeliveryNotificationOptions deliveryOptions;
;
...
deliveryOptions = ClrInterop::parseClrEnum('System.Net.Mail.DeliveryNotificationOptions', 'OnSuccess');
mailMessage.set_DeliveryNotificationOptions(deliveryOptions);
...
To save the mail message to .eml file instead of sending the email:-
System.Net.Mail.SmtpClient              smtpClient;
System.Net.Mail.SmtpDeliveryMethod      stmpDeliveryMethod;
;
...
stmpDeliveryMethod = ClrInterop::parseClrEnum('System.Net.Mail.SmtpDeliveryMethod', 'SpecifiedPickupDirectory');
smtpClient.set_DeliveryMethod(stmpDeliveryMethod);
smtpClient.set_PickupDirectoryLocation('C:\\Temp');
...
smtpClient.Send(mailMessage);  // Save to file instead of sending

Now you have created the codes and want to test the email sending job. You can use the Windows SMTP virtual server for the quick test if you do not have a SMTP server. After installing the SMTP server, you need to go to IIS Manager -> Default SMTP Virtual Server -> right-click Properties -> Access tab -> Relay button -> Select All except the list below, to enable to relay all e-mail messages through the current server.
Note: For Windows Server, you need to open IIS 6.0 Manger.

To specify the SMTP settings in AX, go to Administration -> Setup -> E-mail parameters, specify the 'localhost' in Outgoing mail server and 25 in SMTP port number.
If you are using the method 2 above (SysEmailTable), after calling the sendMail(..) method, you can check the email sending status in Administration -> Periodic -> E-mail processing -> E-mail sending status. To activate the actual email sending process, go to Administration -> Periodic -> E-mail processing -> Batch.

Saturday, June 12, 2010

Using .NET enum in X++

With the CLRInterop feature, you can easily write X++ codes that instantiate and call .NET classes. One of the limitation in IntelliSense is it does not support auto displaying the .NET enumeration value. Luckily there is a useful method called ClrInterop::parseClrEnum(_clrEnumTypeName, _enumValues), which you can convert a string of the .NET enum value to a CLRInterop object.

Example of the usage:-
System.Net.Mail.MailMessage             mailMessage;
System.Net.Mail.DeliveryNotificationOptions deliveryOptions; //.NET enum
;
deliveryOptions = ClrInterop::parseClrEnum('System.Net.Mail.DeliveryNotificationOptions', 'OnSuccess');
mailMessage.set_DeliveryNotificationOptions(deliveryOptions);

Keyboard shortcuts for date field

There are two keyboard shourtcut keys that you could use to enter today's date in Dynamics AX date field, namely the 'd' and 't' keys. The difference between these two keys is 't' is the machine date from Windows and 'd' is the session date which could be changed in Dynamics AX client Tools menu -> Session date. From the development perspective, 't' is the same as the today() method, and 'd' is same as the systemDateGet() method.