How To Extend A D365 Email Template

Share this:

In Microsoft Dynamics 365 for Finance and Supply Chain, the system sends out emails when certain transactional events occur. For example, when a sales order is placed, shipped, or read for pick up. Businesses will often have custom fields that require them to extend a D365 email template. In this article, learn how to extend a D365 email template to use custom data and placeholders in emails sent to customers.

Background

Before you extend a D365 email template, there are a couple of things you need to understand first.

First, set up an email provider to send emails.

Second, set up an email template.

After you set up the email template, you may discover that you want to show additional pieces of data in the email. The next step is to modify the code to relate the placeholder value in the email template to that data.

Sales Order Example

The easiest way to understand how to extend a D365 email template is with an example. There are different steps for extending an email based off an order, compared to other types of emails. We will start with a sales order email.

Setup Email Template

To start, create an email template that includes the new placeholders you want to replace with data from the order. To allow you to follow along, create and run this runnable class (job). This will create an email template named ‘SalesOrder‘.

class tutorial_CreateEmailTemplateSalesOrder
{
    public static void main(Args _args)
    {
        SysEmailTable sysEmailTable;
        SysEmailId emailID = "SalesOrder"; //Usually this would come from a parameter.
        LanguageId languageID = "en-us"; //Usually this would come from a parameter.

        sysEmailTable = SysEmailTable::find(emailID);

        if (!sysEmailTable)
        {
            sysEmailTable.EmailId = emailID;
            sysEmailTable.Description = "Sales order sample email template";
            sysEmailTable.DefaultLanguage = "en-us";
            sysEmailTable.Priority = eMailPriority::Normal;
            sysEmailTable.SenderAddr = "april@contosoax7.onmicrosoft.com";
            sysEmailTable.SenderName = "Dynamics 365 Musings Orders";
            sysEmailTable.BatchGroupId = "";
            sysEmailTable.insert();
            Info(strfmt("Added sysEmailTable record with EmailID %1",emailID));
        }

        SysEmailMessageTable sysEmailMessageTable = SysEmailMessageTable::find(emailID, languageID);

        if (!sysEmailMessageTable)
        {
            sysEmailMessageTable.EmailId = emailID;
            sysEmailMessageTable.LanguageId = languageID;
            sysEmailMessageTable.LayoutType = SysEmailLayoutType::StaticLayout;
            sysEmailMessageTable.Subject = "Sales order %salesId%";
            str mailBody = @'
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "https://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <style type="text/css" nonce="">
            body,
            td,
            div,
            p,
            a,
            input {
                font-family: arial, sans-serif;
            }
        </style>
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <title>Your Fabrikam order details</title>
        <style type="text/css" nonce="">
            body,
            td {
                font-size: 10px;
                font-family: "Segoe UI", "Segoe", "Tahoma", "Verdana", "Arial", "sans-serif";
                margin: 0px;
                border: 0px;
            }
            a:link,
            a:active {
                color: #1155cc;
                text-decoration: none;
            }
            a:hover {
                text-decoration: underline;
                cursor: pointer;
            }
            a:visited {
                color: #6611cc;
            }
            img {
                border: 0px;
            }
            .logo {
                left: -7px;
                position: relative;
            }
            .label {
                font-weight: bolder;
            }
            .totalslabel {
                text-align: right;
                padding-right: 20px;
            }
            .headerprops {
                margin-bottom: 4px;
            }
            .linevalue {
                text-align: right;
            }
        </style>
    </head>
    <body style="background-color: #eeeeee; color: #1a1a1a">
        <table
            style="margin-top: 20px; padding: 0px 20px 20px 20px; width: 640px; background-color: #fff"
            align="center"
        >
            <tbody>
                <tr>
                    <td>
                        <table style="width: 100%; margin-bottom: 10px">
                            <tr>
                                <td style="text-align: right">Order: %salesid%</td>
                            </tr>
                            <tr>
                                <td style="text-align: center; padding-top: 10px">
                                    <!-- Logo -->
                                    <a href="https://sales1.commerce.dynamics.com/"
                                        ><img
                                            width="180"
                                            style="border-style: none; outline: none"
                                            src="https://images-us-prod.cms.commerce.dynamics.com/cms/api/dbsbfgcfvq/imageFileData/MAaq0?pubver=1"
                                            alt="Fabrikam Online logo"
                                    /></a>
                                </td>
                            </tr>
                            <tr>
                                <td style="text-align: center">
                                    <h1 style="font-family: &quot;Segoe UI light&quot;; margin-bottom: 10px">
                                        <!-- Subject -->
                                        Thank you for your order!
                                    </h1>
                                </td>
                            </tr>
                            <tr>
                                <td style="text-align: center; padding: 0px 100px 10px 100px">
                                    <!-- Instructions -->
                                    Hello %customername%. We received your order and we are working on it now. We will
                                    email you an update as soon as your order is processed.
                                </td>
                            </tr>
                            <tr>
                                <td style="text-align: center" align="center">
                                    <table style="width: auto" align="center">
                                        <tbody>
                                            <tr>
                                                <td
                                                    style="
                                                        padding: 0 25px;
        vertical-align: middle;
        background-color: #4c833a;
        color: #fff;
        height: 30px;
        text-align: center;
        "
                                                >
                                                    <!-- CTA -->
                                                    <a
                                                        style="
                                                            color: #fff;
        text-decoration: none;
        background-color: #4c833a;
        box-sizing: border-box;
        display: inline-block;
        font-size: 16px;
        height: 30px;
        line-height: 30px;
        vertical-align: middle;
        "
                                                        href="https://sales1.commerce.dynamics.com/orderdetails?confirmationId=%orderconfirmationid%&propertyName=email&propertyValue=%customeremailaddress%"
                                                        target="_blank"
                                                        >View my order status</a
                                                    >
                                                </td>
                                            </tr>
                                        </tbody>
                                    </table>
                                </td>
                            </tr>
                        </table>
                        <table style="width: 100%; margin-bottom: 10px">
                            <tr>
                                <td
                                    colspan="2"
                                    style="
                                        padding: 2px 5px 2px 5px;
        margin-bottom: 10px;
        background-color: lightblue;
        font-size: larger;
        "
                                >
                                    Your order
                                </td>
                            </tr>
                            <!-- Order parameters -->
                            <tr>
                                <td width="50%" style="vertical-align: top; padding: 5px 0 0 5px">
                                    <div class="headerprops">
                                        <span class="label">Order number</span>:
                                        <a
                                            style="color: #1570a6; text-decoration: none"
                                            href="https://sales1.commerce.dynamics.com/orderdetails?salesid=%salesID%"
                                            target="_blank"
                                            >%orderconfirmationid%</a
                                        >
                                    </div>
                                    <div class="headerprops">
                                        <span class="label">Order date</span>: %shipdate%<br />
                                    </div>
                                    <div class="headerprops">
                                        <span class="label">Mode of delivery</span>: %modeofdelivery%<br />
                                    </div>
                                    <div class="headerprops">
                                        <span class="label">Tutorial Custom Field</span>: %TutorialField%<br/>
                                    </div>
                                </td>
                                <td width="50%" style="vertical-align: top; padding: 5px 0 0 5px">
                                    <div class="headerprops">
                                        <span class="label">Delivery address</span>:<br />
                                        %customername%<br />
                                        %deliveryaddress%
                                    </div>
                                </td>
                            </tr>
                        </table>
                        <table style="width: 100%; margin-bottom: 10px">
                            <!-- Product list -->
                            <tr>
                                <td
                                    colspan="5"
                                    style="
                                        padding: 2px 5px 2px 5px;
        margin-bottom: 10px;
        background-color: lightblue;
        font-size: larger;
        "
                                >
                                    Items
                                </td>
                            </tr>
                            <!--%tablebegin.salesline% -->
                            <tr>
                                <td style="text-align: left; padding-left: 5px">
                                    <a href="https://sales1.commerce.dynamics.com/%productid%.p"
                                        ><img
                                            style="float: left"
                                            src="https://cms-ppe-imageresizer-mr.trafficmanager.net/cms/api/gfhwnkhdlh/imageFileData/search?fileName=/Products/%lineitemid%_000_001.png&w=125&h=125&q=80&m=6&f=jpg&cropfocalregion=true"
                                            alt="%lineproductname%"
                                    /></a>
                                </td>
                                <td>
                                    <div style="margin-bottom: 3px">
                                        <a href="https://sales1.commerce.dynamics.com/%productid%.p"
                                            ><span style="font-size: larger; color: #1570a6">%lineproductname%</span></a
                                        >
                                    </div>
                                    <div style="margin-bottom: 12px">
                                        <span style="font-weight: bold">Item number:</span> %lineitemid%
                                    </div>
                                    <table style="padding: 0 15px 0 0; text-align: left">
                                        <tr style="font-weight: bold">
                                            <td>Item price:</td>
                                            <td>Qty:</td>
                                            <td>Discount:</td>
                                            <td>Total:</td>
                                            <td>Custom Field:</td>
                                        </tr>
                                        <tr>
                                            <td>%lineprice%</td>
                                            <td>%linequantity_withoutunit%</td>
                                            <td>%linediscount%</td>
                                            <td>%linenetamount%</td>
                                            <td>%TutorialLineField%</td>
                                        </tr>
                                    </table>
                                </td>
                            </tr>
                            <!--%tableend.salesline%-->
                        </table>
                        <!-- Order summary -->
                        <table width="30%" align="right" style="margin-bottom: 30px">
                            <tbody>
                                <tr>
                                    <td class="totalslabel">Subtotal</td>
                                    <td style="text-align: right" width="60">%ordernetamount%</td>
                                </tr>
                                <tr>
                                    <td class="totalslabel">Discount</td>
                                    <td style="text-align: right">%discount%</td>
                                </tr>
                                <tr>
                                    <td class="totalslabel">Sales Tax</td>
                                    <td style="text-align: right">%tax%</td>
                                </tr>
                                <tr>
                                    <td class="totalslabel"><b>Total</b></td>
                                    <td style="text-align: right"><b>%total%</b></td>
                                </tr>
                            </tbody>
                        </table>
                    </td>
                </tr>
                <!-- Footer -->
                <tr>
                    <td>
                        <table style="background-color: #eee; padding: 0px 5px">
                            <tr>
                                <td>
                                    This email was sent to %customeremailaddress%. Fabrikam respects your privacy. See
                                    our online
                                    <a style="color: #1570a6" href="https://aka.ms/commerceemailsetup" target="_blank"
                                        >Privacy Statement</a
                                    >.<br /><br />
                                    Fabrikam Corporation, One Fabrikam Way, Redmond, WA, 98052, USA
                                </td>
                            </tr>
                            <tr>
                                <td style="font-size: smaller">
                                    Notification type: <b>Order created</b><br />
                                    Mode of delivery: <b>default</b><br />
                                    Order confirmation ID: <b>%orderconfirmationid%</b>
                                </td>
                            </tr>
                        </table>
                    </td>
                </tr>
            </tbody>
        </table>
    </body>
            </html>

';
            sysEmailMessageTable.Mail = mailBody;
            sysEmailMessageTable.insert();
            Info(strfmt("Added sysEmailMessageTable record with EmailID %1",emailID));
        }
    }

}

Note, this is essentially a copy of the ‘New_dflt‘ email template in the Microsoft demo data. However, I made two changes to it.

First, I added the placeholder ‘%TutorialField%‘ in the first column of the top table. See the bold text. Here, we intend to have the system replace the placeholder for data from the sales order header.

Second, I added a placeholder ‘%TutorialLineField%’ in the lines section of the email template. Here, we intend to have the system replace this placeholder with data from each sales line.

Configure Email Notification Profile

After creating an email template with the new placeholders, we need to tell the system to use the email template with an event.

First, go to Retail and Commerce>Headquarters setup>Parameters>Commerce parameters. Review and set if needed, the value in the ‘Email notification profile’ field.

Second, navigate to Retail and commerce>Headquarters setup>Commerce email notification profile. Next, select the profile from the previous step.

Second, select the profile that is specified in the Commerce Parameters form,

Third, locate the line in the grid with the ‘Email notification type‘ set to ‘Order created‘. Create it if it does not exist.

Finally, set the ‘Email ID’ value in this row to the new email template. For this example, set this to ‘SalesOrder‘.

To confirm the email profile was created correctly, click on the ‘SalesOrder‘ email ID value. This will drill into the ‘Organizational email templates‘ form.

Click on the ‘Email message’ button to view a preview of the order template. Confirm you see the new placeholders added.

Extend The Email Template Code

Finally, the next step to extend a D365 email template is to create a Chain of Command class that extends RetailOENInfo. This code will replace and populate the newly added placeholders.

First, in Visual Studio, create a new class named ‘tutorialRetailOENInfo_Class_Extension’.

Second, add the attribute ‘[ExtensionOf(classStr(RetailOENInfo))]’ above the class declaration.

Third, add the keyword word ‘final‘ in front of the word ‘class’. This is a requirement of Chain of Command.

Fourth, add Macros for each of the new custom placeholders you have in the email template. See this example, along with several other placeholders.

[ExtensionOf(classStr(RetailOENInfo))]
final class tutorialRetailOENInfo_Class_Extension
{
    #define.TutorialField('TutorialField')

    #define.TutorialLineField('TutorialLineField')
}

Fourth, add the method initParameterMap. Then, for each placeholder, insert a name/value into the map named ‘parameterMap‘. This is a map defined in the parent class. Set the name to the placeholder name defined in the macro. Then, specify the second parameter as the value you wish to dynamically use in place of that placeholder in the email template.

protected void initParameterMap()
{
    next initParameterMap();
    //Add your placeholder code for the sales order header here.
    parameterMap.insert(#TutorialField, salesOrder.TutorialField);
}

Fifth, add the method createSalesLineMap. Similarly, for each placeholder that read from sales line data, insert name/value pairs into the salesLineMap

protected Map createSalesLineMap(SalesLine salesLine, RetailGiftCardTransactions retailGiftCardTransactions,MCRSalesLine mcrSalesLine)
{
    Map saleslineMap = next createSalesLineMap(salesLine, retailGiftCardTransactions, mcrSalesLine);
    //Add your placeholder code for the sales order lines here
    saleslineMap.insert(#TutorialLineField, salesLine.TutorialLineField);

    return saleslineMap;
}

As shown above, use this process to extend a d365 email template and display new custom fields.

Display Existing Fields And Methods

It may be obvious, but this same process can be used to add placeholders that will be replaced by data from any field or method related to the sales order.

In the example below, notice the multiple added placeholders. See how they are set using a mixture of existing fields and new methods to retrieve their values.

[ExtensionOf(classStr(RetailOENInfo))]
final class tutorialRetailOENInfo_Class_Extension
{
    #define.CustomerPhone('CustomerPhone')
    #define.TrackingNumber('trackingnumber')
    #define.TrackingInfo('trackinginfo')
    #define.TrackingURL('trackingurl')
    #define.CreatedDate('createddate')
    #define.TutorialField('TutorialField')


    #define.DeliveryNameLine('deliverynameline')
    #define.TutorialLineField('TutorialLineField')
    protected void initParameterMap()
    {
        next initParameterMap();

        CustTable                custTable        = CustTable::find(salesOrder.CustAccount);
        parameterMap.insert(#CustomerPhone, custTable.phone());
        parameterMap.insert(#TrackingNumber, this.getTrackingNumber(salesOrder));
        parameterMap.insert(#TrackingInfo, this.getTrackingInfo(salesOrder));
        parameterMap.insert(#TrackingURL, this.getTrackingUrl(salesOrder));
        parameterMap.insert(#CreatedDate, RetailENInfo::formatDatetimeData(DateTimeUtil::date(salesOrder.CreatedDateTime), languageId));

        parameterMap.insert(#TutorialField, salesOrder.TutorialField);
    }

    protected Map createSalesLineMap(SalesLine salesLine, RetailGiftCardTransactions retailGiftCardTransactions,MCRSalesLine mcrSalesLine)
    {
        Map saleslineMap = next createSalesLineMap(salesLine, retailGiftCardTransactions, mcrSalesLine);
        
        saleslineMap.insert(#DeliveryNameLine, salesLine.DeliveryName);

        saleslineMap.insert(#TutorialLineField, salesLine.TutorialLineField);

        return saleslineMap;
    }

    public str getTrackingNumber(SalesTable _salesTable)
    {
        SalesPackingSlipTrackingInformation trackingInformation;
        CustPackingSlipJour                 custPackingSlipJour;
        str                                 trackingNumber;

        while select RecId, PackingSlipId, DeliveryDate
            from custPackingSlipJour
                Where custPackingSlipJour.SalesId       == _salesTable.SalesId
            join TrackingNumber
                from trackingInformation
                    where trackingInformation.SalesId       == _salesTable.SalesId
                       && trackingInformation.PackingSlipId == custPackingSlipJour.PackingSlipId
                       && trackingInformation.DeliveryDate  == custPackingSlipJour.DeliveryDate
        {
            if (!trackingNumber)
            {
                trackingNumber = trackingInformation.TrackingNumber;
            }
            else
            {
                trackingNumber += ', ' + trackingInformation.TrackingNumber;
            }
        }

        return trackingNumber;
    }

    public str getTrackingUrl(SalesTable _salesTable)
    {
        SalesPackingSlipTrackingInformation trackingInformation;
        CustPackingSlipJour                 custPackingSlipJour;
        str                                 trackingUrl;

        while select RecId, PackingSlipId, DeliveryDate
            from custPackingSlipJour
                Where custPackingSlipJour.SalesId       == _salesTable.SalesId
            join TrackingURL
                from trackingInformation
                    where trackingInformation.SalesId       == _salesTable.SalesId
                       && trackingInformation.PackingSlipId == custPackingSlipJour.PackingSlipId
                       && trackingInformation.DeliveryDate  == custPackingSlipJour.DeliveryDate
        {
            if (!trackingUrl)
            {
                trackingUrl = trackingInformation.TrackingURL;
            }
            else
            {
                trackingUrl += ', ' + trackingInformation.TrackingURL;
            }
        }

        return trackingUrl;
    }

    public str getTrackingInfo(SalesTable _salesTable)
    {
        SalesPackingSlipTrackingInformation trackingInformation;
        CustPackingSlipJour                 custPackingSlipJour;
        str                                 trackingInfo = '<table style="margin: 0 20px 15px;">';

        while select RecId, PackingSlipId, DeliveryDate
            from custPackingSlipJour
                Where custPackingSlipJour.SalesId       == _salesTable.SalesId
            join TrackingNumber, TrackingURL
                from trackingInformation
                    where trackingInformation.SalesId       == _salesTable.SalesId
                       && trackingInformation.PackingSlipId == custPackingSlipJour.PackingSlipId
                       && trackingInformation.DeliveryDate  == custPackingSlipJour.DeliveryDate
        {
            trackingInfo = trackingInfo 
                + '<tr> '
                + '<td style="padding-right: 50px;">' + trackingInformation.TrackingNumber + '</td> '
                + '<td>' + trackingInformation.TrackingURL + '</td> '
                + '</tr>';
        }

        trackingInfo = trackingInfo + '</table>';

        return trackingInfo;
    }

}

Testing

To test and see if your changes to extend a D365 email template worked, perform the event associated with your email template. In the example above, we associated the email template with creating a sales order.

Importantly, when creating the sales order, ensure the sales order is a call center sales order. See my article on how to Create A Call Center In D365.

Also, on the sales order, on the header tab, set the ‘Email‘ to the email address of an address you have access to.

After creating the sales order, run the batch job Retail and Commerce>Retail and Commerce IT>Email and notifications>Send email notification, or wait for it to run on recurrence.

Finally, check your email. Notice that the new placeholders are filled in.

Microsoft Documentation

While I did not find specific documentation on adding additional placeholders, this documentation includes some information about the existing placeholders.

Conclusion

While the most common fields already have placeholders for emails, businesses will likely want to add more. For example, tracking number information is not included in the base functionality. Therefore, it is very helpful to understand how to extend a D365 email template to include custom placeholders. These placeholders can be linked to database data. This allows for email messages that are accurate, timely, and informative.

Peter Ramer
Peter Ramer is a part of the Managed Application Services team at RSM working on Microsoft Dynamics 365. He focuses on the Retail and Commerce industries. When he is not solving problems and finding ways to accelerate his clients' business, he enjoys time with his three kids and amazing wife.

Share this:

One thought on “How To Extend A D365 Email Template

Add yours

  1. Great post, very informative! I learned something new today. Looking forward to reading more from you. (ref:f7c76ea6dc0b)

Leave a Reply

Your email address will not be published. Required fields are marked *

Proudly powered by WordPress | Theme: Baskerville 2 by Anders Noren.

Up ↑