Productivity is easier to increase when Microsoft Dynamics 365 Finance & Operations (D365 F&O) comes with the ability to send reports via email and schedule in recurring batches. Print Destination settings give you the flexibility to configure the dynamic value for remail reports, including Subject, To, CC, and the attachment file format. Still, there are limitations such as the lack of functionality to include email body and subject text placeholders.
However, there is a workaround that resolves this issue. Follow these steps to add email body and subject text placeholders.
1. Open the print destination form for email.
a. Create an extension of SRSPrintDestinationSettingsForm. Add form string control for body.
b. Create an extension class of SRSPrintDestinationSettingsForm to populate mail body on init.
[ExtensionOf(formStr(SRSPrintDestinationSettingsForm))]
final class SRSPrintDestinationSettingsForm_Extension
{
public void init()
{
next init();
SRSPrintDestinationSettings printSettingsLocal = this._printDestinationSettings;
FormStringControl mailBodyControl = this.design().controlName(formControlStr(SRSPrintDestinationSettingsForm,MailBody));
mailBodyControl.text(printSettingsLocal.EmailBody());
}
}
c. Create an SRSPrintDestinationSettings FormEventHandler class to subscribe formCloseOk delegate.
class SRSPrintDestinationSettingsFormEventHandler
{
[SubscribesTo(classStr(SRSPrintDestinationSettingsDelegates), delegateStr(SRSPrintDestinationSettingsDelegates, formCloseOk))]
public static void SRSPrintDestinationSettingsDelegates_formCloseOk(FormRun settingsForm, SRSPrintDestinationSettings settings)
{
FormDesign design = settingsForm.design();
FormStringControl mailBodyControl = design.controlName(formControlStr(SRSPrintDestinationSettingsForm,MailBody));
str bodyemail = mailBodyControl.text();
settings.emailBody(bodyemail);
}
}
d. Create an extension class of SRSReportRunPrinter_extension to populate mail body with placeholders.
Note: In this class, we are using Report DP Class and Report Name from Print Management Settings as examples.
I have added two reports here – one is Coupon which is a custom report and the other is Payment Advice which is a standard report. Add multiple if you have cases for multiple reports.
[ExtensionOf(classStr(SrsReportRunPrinter))]
public final class SRSReportRunPrinter_extension
{
public void printReport()
{
if (reportContract.parmRdpName() == “CouponProformaDP”) //Find DP Class of Report and add name Here
{
CouponProformaContract coupnProformaContract = reportContract.parmRdpContract() as CouponProformaContract;
printSettings.reportTitle(“Coupon Proforma Report”); //As Seen in Screenshot
printSettings.voucherRecId(coupnProformaContract.parmCouponProformaRecId());
}
if (reportContract.parmRdpName() == “BankPaymAdviceVendDP”) //Find DP Class of Report and add name Here
{
BankPaymAdviceContract bankPaymAdviceContract = reportContract.parmRdpContract() as BankPaymAdviceContract;
printSettings.reportTitle(“Payment Advice”); //As Seen in Screenshot
printSettings.voucherRecId(bankPaymAdviceContract.parmLedgerJournalTransRecId());
}
next printReport();
}
}
e. Create an extension of the SRSPrintDestinationSettings class to augment additional functions.
i. Create a new Data member attribute for Email Body, Report Title and RecId (To get placeholder values)
ii. Using Chain of command, extend the functionality of the parmEMailContract function to set the Body and Subject with placeholders in email contract class.
/// </summary>
[ExtensionOf(classstr(SRSPrintDestinationSettings))]
public final class SRSPrintDestinationSettings_Extension
{
internal str reportTitle;
public str emailBody;
internal RecId voucherRecId;
#define.CurrentVersion(1)
#localmacro.CurrentList
emailBody
#endmacro
//Type of report
[DataMemberAttribute]
public str reportTitle(str _reportTitle = reportTitle) //To get Report Title from Print destinations form
{
reportTitle = _reportTitle;
return reportTitle;
}
[DataMemberAttribute]
public RecId voucherRecId(RecId _voucherRecId = voucherRecId)
{
voucherRecId = _voucherRecId;
return voucherRecId;
}
[DataMemberAttribute]
public str emailBody(str _value = emailBody)
{
emailBody = _value;
return emailBody;
}
/// </summary>
public SrsReportEMailDataContract parmEMailContract(SrsReportEMailDataContract _emailContract)
{
SrsReportEmailDataContract contract;
CustQuotationJour custQuotationJour;
contract = next parmEMailContract(_emailContract);
if(reportTitle == “Coupon Proforma Report”)
{
emailSubject = contract.parmSubject();
select firstonly custQuotationJour where custQuotationJour.RecId == voucherRecId; //RecId is used to get Related Report placeholder values
emailSubject = strReplace(emailSubject,”%CustomerName%”,CustTable::find(custquotationjour.InvoiceAccount).name());
contract.parmSubject(emailSubject); // Update values
emailBOdy = strReplace(emailBOdy,”%CustomerName%”,CustTable::find(custquotationjour.InvoiceAccount).name());
emailBOdy = strReplace(emailBOdy,”%LegalEntityName%”,CompanyInfo::findRecId(CompanyInfo::current()).Name);
contract.parmBody(emailBody); // Update values
}
if (reportTitle == “Payment Advice”)
{
emailSubject = contract.parmSubject();
select firstonly ledgerJournalTrans where ledgerJournalTrans.RecId == voucherRecId; //RecId is used to get Related Report placeholder values
emailSubject = strReplace(emailSubject,”%VendorAccount%”,ledgerJournalTrans.accountDisplay());
contract.parmSubject(emailSubject);
emailBody = contract.parmBody(this.emailBody());
emailBody = strReplace(emailBody,”%VendorName%”,ledgerJournalTrans.LedgerDimensionName);
emailBody = strReplace(emailBody,”%LegalEntityName%”,CompanyInfo::findRecId(CompanyInfo::current()).Name);
contract.parmBody(emailBody);
}
return contract;
}
f. In above created SRSPrintDestination Settings, extend the pack and unpack function.
Extend the Augmented class macro to include mail body and define logic for pack and unpack using the below implementation. Use the pack-unpack pattern to save and/or store the state of an object, and then later re-instantiate the same email parameters on the next run.
public container pack()
{
container packedClass = next pack();
return SysPackExtensions::appendExtension(packedClass, classStr(SRSPrintDestinationSettings_Extension), this.demoPack());
}
private container demoPack()
{
return [#CurrentVersion, #CurrentList];
}
public boolean unpack(container _packedClass)
{
boolean result = next unpack(_packedClass);
if (result)
{
container packedExtension = SysPackExtensions::findExtension(_packedClass, classStr(SRSPrintDestinationSettings_Extension));
result = this.demoUnpack(packedExtension);
}
return result;
}
private boolean demoUnpack(container _packedClass)
{
boolean success = false;
Integer version = RunBase::getVersion(_packedClass);
switch (version)
{
case #CurrentVersion:
[version, #currentList] = _packedClass;
success = true;
break;
default:
success = false;
break;
}
return success;
}
2. Send report email including the body and subject with placeholders.
Now we can include the email body and subject in print destination settings.
Subject = For %CustomerName%
Body = Good Afternoon, <b>%CustomerName%</b> <Br> Please find Attached Document <Br> Thanks and Regards, <Br> <b>%LegalEntityName%</b><Br>
(For Formatting Use Appropriate HTML Tags, I have used Line break and bold here, Use Exact placeholder as defined in ParmEmailContract Method for Subject And Body)
The report emailed now includes the body and subject with placeholdervalues as well and can be further configured for dynamics messages using print management settings based on conditions.
Coupon Proforma example:
Payment Advice example:
With these placeholders, you’ll be able to save time when emailing reports and spend more time focusing on your business needs.
About the expert
Sachin Mittal, Technical Associate, OnActuate
Sachin is a Microsoft Dynamics certified Technical Associate. He has worked extensively on customization and development of new functionalities and SSRS Reports in Dynamics 365 F&O projects. He also has knowledge of LCS, and DevOps projects and Power Platform including Power Canvas Apps, and integration with D365F&O. Sachin holds multiple certifications including an MB-500: Microsoft Dynamics 365: Finance and Operations Apps Developer and AZ-900: Microsoft Azure Fundamentals certification. |
The “OneByte” blog series are technical how-to articles by OnActuate’s expert consultants covering Microsoft Business Applications (Microsoft Dynamics, Power Platform) and related technologies. Have a topic you’d like to learn more about? Email us at info@onactuate.com!