In Dynamics 365 for Finance and Operations and Dynamics AX 2102 you can set up Email templates for sending email notifications for Retail orders, workflows, alerts, etc. Often these emails need to include some dynamic information, and this can be achieved using placeholders.
However, in order to use placeholders in the built-in editor you need to know exactly what their names are. Unlike in AX 2012, in Dynamics 365 you don’t really have an editor at your disposal – you can upload already prepared HTML file that represents a dynamic email body, i.e. a body with placeholders.
Docentric AX Free Edition enables you to:
- use an advanced HTML editor to format email bodies within D365FO,
- pick a placeholder from a list of available placeholders for the current email, if you need to create a dynamic email body.
Let’s see how this work.
Docentric email body editor
Navigate to Organization administration -> Setup -> Organization email templates (up to app ver 8.1 Organization administration -> Setup -> Email templates). Click the Docentric settings tab page and turn on the Use Docentric email editor checkbox.
When you click the Edit button located above the Email message content grid, the pop-up form is open but it contains Docentric email body editor instead of the built-in read-only HTML control.
Standard placeholders
In Docentric email body editor notice the Field dropdown list. It contains placeholders for the currently selected email, and this is defined by the Class for placeholders field.
When you turn on Use Docentric email editor option, a default handler class for placeholders is selected by default. This means that the Field dropdown list will contain only so called Standard placeholders such as Company info including Company logo, Email description, Sender name, Worker info, Current date and time, etc.
This is not preventing you from using any other placeholder, but it would be nice that you can pick it from the Field dropdown list, right? This kind of placeholders that are specific per email we called Custom placeholders and they can be defined by introducing a custom email handler class.
Custom placeholders
Custom placeholders will appear in the Field dropdown list in Docentric email body editor if we choose a custom email handler class from the Class for placeholders combobox.
For example, if we select the Order Confirmation email handler class, we will get the following list of custom placeholders.
Now, the question is, how to create such a class and which rules should be followed.
Custom ETH (Email Template Handler) class
When you need to define custom placeholders for an email, a new custom email handler class should be introduced. It should derive from the DocEmailTemplateHandlerBase class and implement at least two methods: description() and defineCustomPlaceholders():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
class DocOrderConfirmEmailHandler extends DocEmailTemplateHandlerBase { public const str PlaceholderCustomerName = 'customername'; public const str PlaceholderSalesId = 'salesid'; public const str PlaceholderDeliveryAddress = 'deliveryaddress'; public const str PlaceholderCustomerAddress = 'customeraddress'; public const str PlaceholderDeliveryDate = 'deliverydate'; public const str PlaceholderShipDate = 'shipdate'; public const str PlaceholderModeOfDelivery = 'modeofdelivery'; public const str PlaceholderCharges = 'charges'; public const str PlaceholderTax = 'tax'; public const str PlaceholderTotal = 'total'; public const str PlaceholderDiscount = 'discount'; public ClassDescription description() { return 'Order Confirmation email handler class'; } /**********************************************************************************/ /* Defining Custom Placeholders */ /**********************************************************************************/ protected void defineCustomPlaceholders(Map _customPlaceholderDefinitionMap) { _customPlaceholderDefinitionMap.insert(DocOrderConfirmEmailHandler::PlaceholderCustomerName, 'CO - Customer name'); _customPlaceholderDefinitionMap.insert(DocOrderConfirmEmailHandler::PlaceholderSalesId, 'CO - Sales ID'); _customPlaceholderDefinitionMap.insert(DocOrderConfirmEmailHandler::PlaceholderDeliveryAddress, 'CO - Delivery address'); _customPlaceholderDefinitionMap.insert(DocOrderConfirmEmailHandler::PlaceholderCustomerAddress, 'CO - Customer address'); _customPlaceholderDefinitionMap.insert(DocOrderConfirmEmailHandler::PlaceholderDeliveryDate, 'CO - Delivery date'); _customPlaceholderDefinitionMap.insert(DocOrderConfirmEmailHandler::PlaceholderShipDate, 'CO - Shipping date'); _customPlaceholderDefinitionMap.insert(DocOrderConfirmEmailHandler::PlaceholderModeOfDelivery, 'CO - Mode of delivery'); _customPlaceholderDefinitionMap.insert(DocOrderConfirmEmailHandler::PlaceholderCharges, 'CO - Charges'); _customPlaceholderDefinitionMap.insert(DocOrderConfirmEmailHandler::PlaceholderTax, 'CO - Tax'); _customPlaceholderDefinitionMap.insert(DocOrderConfirmEmailHandler::PlaceholderTotal, 'CO - Total'); _customPlaceholderDefinitionMap.insert(DocOrderConfirmEmailHandler::PlaceholderDiscount, 'CO - Discount'); } } |
You can also provide logic for supplying values for custom placeholders. This is not a mandatory step if you are using your own logic for sending emails, when you can fill the Placeholder name -> Placeholder value mapping outside this class before sending an email.
Let’s assume that we will supply the values for the custom placeholder within the class. We need to override and implement the fillMappingsWithCustomPlaceholderValues() method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
class DocOrderConfirmEmailHandler extends DocEmailTemplateHandlerBase { ... /**********************************************************************************/ /* Supplying values for Custom Placeholders */ /**********************************************************************************/ public void fillMappingsWithCustomPlaceholderValues(Map _mappings /* Map(PlaceholderName (str) -> PlaceholderValue (str)) */, container _contextInfo = conNull()) { str salesId = conPeek(_contextInfo, 1); SalesTable salesOrder = SalesTable::find(salesId); _mappings.insert(DocOrderConfirmEmailHandler::PlaceholderCustomerName, salesOrder.SalesName); _mappings.insert(DocOrderConfirmEmailHandler::PlaceholderSalesId, salesOrder.SalesId); _mappings.insert(DocOrderConfirmEmailHandler::PlaceholderDeliveryAddress, salesOrder.deliveryAddressing()); _mappings.insert(DocOrderConfirmEmailHandler::PlaceholderCustomerAddress, CustTable::find(salesOrder.CustAccount).address()); _mappings.insert(DocOrderConfirmEmailHandler::PlaceholderDeliveryDate, DocOrderConfirmEmailHandler::formatDatetimeData(salesOrder.deliveryDateDisplay(), languageId)); _mappings.insert(DocOrderConfirmEmailHandler::PlaceholderShipDate, DocOrderConfirmEmailHandler::formatDatetimeData(salesOrder.ShippingDateRequested, languageId)); _mappings.insert(DocOrderConfirmEmailHandler::PlaceholderModeOfDelivery, dlvMode::find(salesOrder.DlvMode).Txt); AmountCur totalAmount, totalTaxAmount, totalDiscount, totalCharge; try { SalesTotals salesTotals = salesTotals::construct(salesOrder, SalesUpdate::All); salesTotals.calc(); totalCharge = salesTotals.totalMarkup(); totalAmount = salesTotals.totalAmount() + salesOrder.amountInvoiced(); totalTaxAmount = salesTotals.totalTaxAmount(); totalDiscount = salesTotals.totalLineDisc(); } catch { totalCharge = 0; totalAmount = 0; totalTaxAmount = 0; totalDiscount = 0; } _mappings.insert(DocOrderConfirmEmailHandler::PlaceholderCharges, DocOrderConfirmEmailHandler::formatNumericData(totalCharge, languageId)); _mappings.insert(DocOrderConfirmEmailHandler::PlaceholderTax, DocOrderConfirmEmailHandler::formatNumericData(totalTaxAmount, languageId)); _mappings.insert(DocOrderConfirmEmailHandler::PlaceholderTotal, DocOrderConfirmEmailHandler::formatNumericData(totalAmount, languageId)); _mappings.insert(DocOrderConfirmEmailHandler::PlaceholderDiscount, DocOrderConfirmEmailHandler::formatNumericData(totalDiscount, languageId)); } } |
If you are using the built-in mechanism for sending emails, i.e. SysEmailTable::sendEmailTable(), or you are using Docentric APIs for sending emails, then you are done.
See how to use Docentric’s APIs to send emails >>
This method will fill the _mappings Placeholder name -> Placeholder value with the values for all standard placeholders, and in case that you have implemented the fillMappingsWithCustomPlaceholderValues() method in your email handler class, it will provide the values for all custom placeholders as well. Otherwise, you will need to fill the same _mappings with the all custom placeholders’ values manually.
Before sending an email, you will also have to replace all placeholders used in the email body and subject. Learn how >>
Use email templates for workflow notifications
If you need to send email notifications from a workflow, you don’t have to introduce a custom email handler class. It’s enough to turn on the Workflow emails checkbox.
With the Workflow emails option turned on, all workflow related placeholders will appear in the Field dropdown list automatically.
Learn more on improved workflow emails >>
Resources
Download custom email handler class >>
See how to create an email body with a dynamic table >>
Learn about sending emails using Docentric APIs >>
Improved Workflow email notifications >>
Improved Alert email notifications >>
Read the related how-to manual >>
Hello,
We are using Docentric at Siddons Martin and I have created an Email template for Project Invoice proposal workflow. I have used %EntityURL% as the placeholder. I need to have the Invoice proposal number listed in the body of the email , and I would like to have the URL linked to it, so that when the assigned user gets the email, that user will be able to see the invoice proposal number at the same time they should be able to click the hyperlink, which will take them to the form directly. Is that possible ?
Hello Hemant,
I suggest that you define a custom placeholder for the invoice proposal number. Custom placeholders can be defined by introducing a custom email handler class, as described in this article.
Then, in the body of your email template, use the a HTML tag element to refer to the project proposal, for example a href=”%ENTITYURL%” My custom project proposal is %ProjProposalId%
Please let me know if this helps.
Regards,
Hello Jovica,
Thanks for your reply. Yes, we will add that field to the extended class.
Hi,
We created a custom ETH class. We want to select this class on the form “SysEmailTable”. For any reasons we don’t see the class.
We are using the Free edition of Docentric. Did we forgot any step?
Model is referenced to all our Docentric Models and the right models. Full build, gated check-in, main build and release succeed all.
Do we need to do another step?
Hi Brooklyn,
The ETH classes should show up no matter if you’re using the Free edition of Docentric or not.
The minimum requirements that are needed for an ETH class to show up in the Organization email templates (SysEmailTable) form are the following:
1. The model that houses the class should have a reference to the DocentricAXEmails, ApplicationFoundation and ApplicationPlatform models.
2. The class should inherit the DocEmailTemplateHandlerBase class or one of the classes that already inherit DocEmailTemplateHandlerBase (ie. DocEmailTemplateDefaultHandler, DocEmailTemplateDefaultHandlerWithImages etc.).
3. The ETH class that you have created must be non-abstract.
In other words it must implement all of the abstract methods of the base class, like description() and defineCustomPlaceholders() and must not have the abstract keyword.
Another recommendation is to build your model and the DocentricAXEmails model together.
Hope this helps!