web
You’re offline. This is a read only version of the page.
close
Skip to main content

Notifications

Announcements

No record found.

Community site session details

Community site session details

Session Id :
Microsoft Dynamics AX (Archived)

Using Print Management Settings to Send Emails with AX Templates and Dynamic Subject Lines (Not a question, but step-by-step guide)

(6) ShareShare
ReportReport
Posted on by

I didn't want to go through the hassle of setting up a blog just to post this tutorial, so I am posting it in the Forum instead...

Sending reports with Print Management is a nice feature; however, the report is sent as an attachment to a blank email.  I was asked by others in my company to find a way to send these reports using an AX email template and dynamic subject lines. I will outline the changes I needed to make to allow this to work. (Note: This may not be the best way, but it does work)

 

Step 1: Set up Print Destination to Handle the New Options

In \Classes\PrintDestinationSettings

Update the classDeclaration method to include the following (idNumber is not necessary if you do not need a dynamic subject line)

  

  boolean                     sendWithTemplate;

    str                         templateType;

    str                         idNumber;

 

    // Pack/unpack information

    #define.version     (1)

    #define.packVersion (1)

    #localmacro.currentList

        version,

        packedPrintMedium,

        printMediumType,

        printerName,

        printerPageSettings,

        landscape,

        printerStatus,

        printerType,

        printerWhere,

        printerComment,

        printAllPages,

        fromPage,

        toPage,

        numberOfCopies,

        emailTo,

        emailCc,

        emailSubject,

        emailAttachmentFileFormat,

        emailAttachmentImageFileFormat,

        sendWithTemplate,

        templateType,

        idNumber,

        fileName,

        fileFormat,

        imageFileFormat,

        overwriteFile,

        printToArchive,

        overridePageSettings,

        overwriteFileIsSet

    #endmacro

 


Because of the extra 3 (or 2) extra fields we are packing in the class declaration the unpack method needs to be updated  (Note:  if you are not using IdNumber omit the insert at line 22)

public boolean unpack(container _pack)

{

    boolean   ret;

    int       version;

    container packedPrintMedium;

 

    if (typeOf(conPeek(_pack, 1)) == Types::Integer)

    {

        version = conPeek(_pack, 1);

 

        if (version == #packVersion)

        {

            // This to to handle print destination settings that were created prior to the update

    if(conLen(_pack) < 28)

            {

                _pack = conIns(_pack,20,false);

                _pack = conIns(_pack,21,"");

                _pack = conIns(_pack,22,"");

            }           

 

            [#currentList] = _pack;

 

            // Need to use packedPrintMedium to create the print medium via the classFactory

            this.printMedium(new SRSPrintMediumScreen());

            ret = true;

        }

    }

 

    return ret;

}

 

If using dynamic subject line the new method will need to be updated as well

public void new(container _c = conNull(), str _idNumber = "")

{

    if (_c != conNull())

    {

        if(conLen(_c) == 29 && _idNumber != "")

        {

            _c = conPoke(_c, 22, _idNumber);

            this.idNumber(_idNumber);

        }

        this.unpack(_c);

 

    }

    else

    { …}

Now new methods need to be created to retrieve the new variables…

Send with Template

[DataMemberAttribute]

public boolean sendWithTemplate(boolean _value = sendWithTemplate)

{

    sendWithTemplate = _value;

    return sendWithTemplate;

}

Template Type

[DataMemberAttribute]

public str templateType(str _value = templateType)

{

    templateType = _value;

    return templateType;

}

IdNumber

public str idNumber(str _value = idNumber)

{

    idNumber = _value;

    return idNumber;

}

Step 2: Update the form to give the user the option to Send with Template

 

In \Forms\SRSPrintDestinationSettingsForm create a check box for the Send with Template option and an Editable String for Template Type. Both of these new fields need the Auto Declaration set to “YES”. To “DataSources” add “SysEmailTable” and in TemplateType properties set DataSource to ”SysEmailTable” and DataField to “Description”.

Override the lookup method for Template type, this will give the user a drop down menu of all available templates to select from (\Forms\SRSPrintDestinationSettingsForm\TemplateType:lookup)

public void lookup()

{

    Query query = new Query();

    QueryBuildDataSource queryBuildDataSource;

    QueryBuildRange queryBuildRange;

    SysTableLookup sysTableLookup = SysTableLookup::newParameters(tableNum(sysEmailTable), this);

    sysTableLookup.addLookupField(fieldNum(SysEmailTable, EmailId));

    sysTableLookup.addLookupField(fieldNum(SysEmailTable, Description));

 

    queryBuildDataSource = query.addDataSource(tableNum(SysEmailTable));

 

    queryBuildRange = queryBuildDataSource.addRange(fieldNum(SysEmailTable, EmailId));

    sysTableLookup.parmQuery(query);

    sysTableLookup.performFormLookup();

 

    mailSubject.text(SysEmailMessageTable::find(TemplateType.text(),'en-us').subject);

 

    //super();

}

formDesign.png

In \Forms\SRSPrintDestinationSettingsForm\init  add the following in the print to email settings (add at line 31 on my version) 

SendTemplate.value(printSettings.sendWithTemplate());

if(printSettings.sendWithTemplate())

{

  TemplateType.visible(true);

  TemplateType.text(printSettings.TemplateType());

 }

\Forms\SRSPrintDestinationSettingsForm\closeOK add the following to the print to email settings (add at line 46 in my version)

   

 printSettings.sendWithTemplate(SendTemplate.checked());

    printSettings.templateType(SendTemplate.checked() ? TemplateType.text():"");

 

Now your print destination settings form should look like this…  

formView.png

The subject line is pulled from the subject line in the SysEmailMessageTable during lookup of the email description.

Step 3: Sending the Email through X++

First I will show how to handle the sending the email without the dynamic subject line (a lot of changes are needed to make that work so I will cover that all together at the end), for sending with a template we need to make 3 simple changes.

\Classes\SRSPrintDestinationSettings\parmEMailContract

Prior to the return add the following if statement

if(emailContract.parmBody() == "" && this.templateType() != "")

{

   emailContract.parmBody(SysEmailMessage::stringExpand(SysEmailMessageTable::find(templateType,

   'en-us').Mail));

}

\Classes\SysINetMail\sendEMail

server static void sendEMail(

    SysEmailId      _emailId,

    LanguageId      _language,

    str             _emailAddr,

    Map             _mappings = null,

    str             _from = '',

    FilenameOpen    _attachmentFilename = '',

    boolean         _isHTML = false)

{

    #help

    SysINetMail             sysINetMail;

    SysEmailTable           table = SysEmailTable::find(_emailId);

    SysEmailMessageTable    message = SysEmailMessageTable::find(_emailId, _language);

    str                     messageBody;

    str                     htmlText;

    COMDispFunction         comWrite;

    COMVariant              text;

    COM                     ctrl;

    COM                     document;

    COM                     body;

    boolean                 isHTML = _isHTML;

    ;

 

    if (!message)

    {

        // Message not found for this language.

        message = SysEmailMessageTable::find(_emailId, table.DefaultLanguage);

    }

 

    if(isHTML==false)

    {

    isHTML = (message.LayoutType == SysEmailLayoutType::StaticLayout);

    messageBody = SysEmailMessage::stringExpand(message.Mail, _mappings);

    }

…

}

 

\Classes\SrsReportRunMailer\emailReport

Add the highlighted updates to the elseif(inetMailer != null) statement

else if (inetMailer != null)

    {

        result = inetMailer.sendMailAttach(_emailContract.parmTo(),

                                  _emailContract.parmCc(),

                                  _emailContract.parmSubject(),

                                  _emailContract.parmBody(),

                                  false, /* do not show dialog*/

                                  _attachmentPath,

                                  '',

                                  true);

    }

Now you can send the report attached to a template!!!

Step 4: Sending Email with Dynamic Subject Line

First thing be sure your template has the variable you want to use wrapped in ‘%’ so AX will recognize it as dynamic.

Here I am using the Purchase Order Id number as my dynamic item in the subject line. (see the Print Destination Settings Form Subject line %IdNumber%)

In \Classes\Print Mgmt\classDeclaration add

str idNumber;

 

In \Classes\Print Mgmt\buildEffectiveSettings add field idNumber to the following (line 27)

PrintMgmtNodeInstance::mergeIntoEffectiveSettings(_nodeInstance, childInstance, idNumber);

In \Classes\Print Mgmt\getNodeInstances add the highlighted parts (*NOTE: this is for a PO, if you are using it for something else you will need to change the table id to match what you are using*)

 

while (currentInstance != null && currentInstance.parmNodeDefinition().isValidDocumentType(documentType))

            {

                //Added to push PO Number to Template for Emails

                if(referencedTable.TableId == tableNum(PurchTable))

                {

                    purchTable = referencedTable;

                    idNumber = purchTable.PurchId;

                }

                else if(hierarchyContext.parmReferencedTableBuffer().TableId == tableNum(PurchTable))

                {

                    purchTable = hierarchyContext.parmReferencedTableBuffer();

                    idNumber = purchTable.PurchId;

                }

                else

                {

                    idNumber = "";

                }

 

                // We have the instance we need, so add to it

                setupDocument = PrintMgmtSetupDoc::construct(currentInstance, 
documentType, _languageId, null, idNumber);


An extra field is being added to that call so we must change the arguments for the call. Be sure to set the default to empty so it will not break calls made to it from elsewhere

\Classes\PrintMgmtSetupDoc\construct

public static PrintMgmtSetupDoc construct(PrintMgmtNodeInstance _nodeInstance, PrintMgmtDocumentType _docType, LanguageId _langId, PrintMgmtSetupFolder _parent = null, str _idNumber = "")

{

…

  while select RecId from setup

            order by PriorityId

            where

                setup.DocumentType == _docType

                && setup.NodeType == _nodeInstance.parmNodeDefinition().getNodeType()

                && setup.ReferencedRecId == recId

                && setup.ReferencedTableId == tableId

    {

        doc.addInstance(PrintMgmtSetupDocInstance::constructFromRec(doc, setup.RecId, _langId, _idNumber));

    }

}

Again we added a parameter, so go to \Classes\PrintMgmtSetupDocInstance\constructFromRec

public static PrintMgmtSetupDocInstance constructFromRec(PrintMgmtSetupDoc _parent, RefRecId _recId, LanguageId _langId, str _idNumber = "")

{

    …

    PrintMgmtSetupSettings::newSettingsFromRecs(docInstance, _langId, _idNumber);

 

    return docInstance;

}

\Classes\PrintMgmtSetupDocInstance\shallowCopy

public PrintMgmtSetupDocInstance shallowCopy(PrintMgmtSetupDoc _newParent, str _idNumber = "")

{

    return PrintMgmtSetupDocInstance::constructFromRec(_newParent, recId, langId, _idNumber);

}

\Classes\PrintMgmtSetupSettings\newSettingsFromRec

public static void newSettingsFromRecs(PrintMgmtSetupDocInstance _parent, LanguageId _langId, str _idNumber = "")

{

…

if(rec.QueryPacked == conNull()) // Default Setting?

        {

            if(defaultFound) // duplicate default settings are not allowed!

            {

                throw error(Error::wrongUseOfFunction(funcName()));

            }

            PrintMgmtSetupSettings::newDefaultFromRec(_parent, rec.RecId, _langId, _idNumber);

            defaultFound = true;

        }

…

}

\Classes\PrintMgmtSetupSettings\newDefaultFromRec 

 

public static PrintMgmtSetupSettingsDefault newDefaultFromRec(PrintMgmtSetupDocInstance _parent, RefRecId _recId, LanguageId _langId, str _idNumber = ""){

    PrintMgmtSettings rec;

    ;

    select firstonly rec where rec.RecId == _recId;

     if(conLen(rec.PrintJobSettings) == 29 && _idNumber != "")

    {

        ttsBegin;

        rec.PrintJobSettings = conPoke(rec.PrintJobSettings, 22, _idNumber);

        ttsCommit;

    }

    return PrintMgmtSetupSettingsDefault::constructFromRec(_parent, rec, _langId, _idNumber);

}

\Classes\PrintMgmtSetupSettingsDefault\constructFromRec

public static PrintMgmtSetupSettings constructFromRec(PrintMgmtSetupDocInstance _parent, PrintMgmtSettings _printMgmtSettings, LanguageId _langId, str _idNumber = "")

{

…

obj.init(_parent, _printMgmtSettings, _langId, _idNumber);

…

}

\Classes\PrintMgmtSetupSettingsDefault\init

 

protected void init(PrintMgmtSetupDocInstance _parent, PrintMgmtSettings _printMgmtSettings, LanguageId _langId, str _idNumber = "")

{

    ;

    if(conLen(_printMgmtSettings.PrintJobSettings) == 29 && conPeek(_printMgmtSettings.PrintJobSettings, 22) !=  _idNumber && _idNumber != "")

    {

        _printMgmtSettings.PrintJobSettings = conPoke(_printMgmtSettings.PrintJobSettings,22, _idNumber);

    }

…

}

\Classes\FormLetter\isClientOutput

public server static boolean isClientOutput(container _packedSettings, str _idNumber = "")

{

    return new SRSPrintDestinationSettings(_packedSettings, _idNumber).printMediumType() == SRSPrintMediumType::Screen;

}

\Classes\FormLetterSErviceController\checkClientOutputPrintManagement (again if you are using something other than the PurchTable you will need to change it here)

while select PrintJobSettings from printMgmtSettings

        where

            printMgmtSettings.Description == ''

        join PrintType from printMgmtDocInstance

        where printMgmtDocInstance.RecId             == printMgmtSettings.ParentId

           && printMgmtDocInstance.ReferencedRecId   == 0

           && printMgmtDocInstance.ReferencedTableId == 0

           && printMgmtDocInstance.DocumentType      == this.printMgmtDocumentType()

           && printMgmtDocInstance.NodeType          == this.printMgmtNodeType()

    {

 

if(strContains(callerFormName, "PurchTable"))

         {

            purchTable = this.getContract().parmCallerTable();

            if (FormLetter::isClientOutput(printMgmtSettings.PrintJobSettings, purchTable.PurchId ))

            {

                printerSetupErrorText += strFmt("@SYS118704", printMgmtDocInstance.PrintType) + '\n';

                onClient = true;

            }

        }

        else

        {

            if (FormLetter::isClientOutput(printMgmtSettings.PrintJobSettings))

            {

                printerSetupErrorText += strFmt("@SYS118704", printMgmtDocInstance.PrintType) + '\n';

                onClient = true;

            }

        }

}

\Classes\PurchPurchaseOrderController\initFormLetterReport (starting at line 23 in my version)

if (purchPurchOrderJournalPrint)

    {

        formLetterReport.parmDefaultCopyPrintJobSettings(new SRSPrintDestinationSettings(purchPurchOrderJournalPrint.parmPrinterSettingsFormLetterCopy()));

        formLetterReport.parmDefaultOriginalPrintJobSettings(new SRSPrintDestinationSettings(purchPurchOrderJournalPrint.parmPrinterSettingsFormLetter()));

        formLetterReport.parmUsePrintMgmtDestinations(purchPurchOrderJournalPrint.parmUsePrintManagement());

    }

Now we can get to the point of sending the email with the template and dynamic subject line!

Go back to \Classes\SRSPrintDestinationSettings\parmEMailContract that we edited in Step 3

public SrsReportEMailDataContract parmEMailContract(SrsReportEMailDataContract _emailContract = emailContract)

{

    //added

    Map mappings = new Map(Types::String, Types::String);

    mappings.insert('IdNumber', idNumber);

    //end added



if(emailContract.parmBody() == "" && this.templateType() != "")

    {

       if(strContains(this.emailSubject(), "%"))

       {

             emailContract.parmSubject(SysEmailMessage::stringExpand(SysEmailMessageTable::find(templateType,

 'en-us').Subject, mappings));

        }

        emailContract.parmBody(SysEmailMessage::stringExpand(SysEmailMessageTable::find(templateType,

'en-us').Mail));

    }


return emailContract;

}

Now emails will send with the template and a dynamic subject line!!! It is a lot of work (at least for the dynamic subject line), but it will make the end users very happy.

 

Hope this was helpful!

-Jessi

 

 

 

 

*This post is locked for comments

I have the same question (0)
  • Verified answer
    jmcpeek Profile Picture
    on at

    I tried to ensure that all changes are bold face within the code.  I hope you find this post helpful. I doubt this is the best approach, but it fits the needs.

  • Sohaib Cheema Profile Picture
    49,438 User Group Leader on at

    Thank you for sharing useful piece of information.

    But Blogs are primarily used for Informational Posts. Whereas community is for discussion with Questions and answers,

    kindly go through below link to have your own blog

    community.dynamics.com/.../136.request-a-new-blog-or-blog-to-syndicate

  • Community Member Profile Picture
    on at

    Hi Jessi,

    I have this same requirement from one of my client, they want Email body to be add on same.

    But I don't find \Classes\PrintDestinationSettings class.

    Is there a way to add text to the body of the (generated) email?

    System I am using is 2012 R3.

    Kernel version 6.3.6000.214

    Application version 6.3.6000.133

  • Community Member Profile Picture
    on at

    Hi Zeeshan,

    Those are things for SSRS reports so all classes connected to those functionalities has prefix "SRS" like SRSPrintDestinationSettings (which is mentioned in Ms McPeek post).

    If you are not familiar with such deep customization in system classes that you can't see such obvious spellings mistakes like lack of SRS I would not recommend you to play with it as it affects the whole "printing engine".

  • Community Member Profile Picture
    on at

    Hi Jessi -

    Thank you so much for this step-by-step guide to for modifying the email process.

    I am trying to replicate it in our AX environment, but am running into a few roadblocks.

    Could you confirm which version these modifications were developed for?

    We are running on AX 2012 R2 CU6.

    I do not have the following in our environment - "SendTemplate" - which was part of the modification to the "lookup" override method for the Templatetype object.

    Thanks..Tammy McNab

  • Community Member Profile Picture
    on at

    Hi Jessi -

    After more careful review of your solution, I have answered my own question. Thanks for putting this all together.

  • JII SAADUDDIN Profile Picture
    1,832 on at

    Ok. Thanks Jessi,

  • Vgubba Profile Picture
    120 on at

    Thank you for the knowledge. just a small question. What are the changes in 

    PrintMgmtNodeInstance::mergeIntoEffectiveSettings class? is that missed?
  • elhadidimohamed1 Profile Picture
    on at

    it did not work with me for sales agreement

  • Viral Suthar Profile Picture
    279 on at

    Good Solution, it works for me with little changes,

    You missed the class -> PrintMgmtNodeInstance and method -> megeIntoEffectiveSettings

    Add _idNumber parameter in the method and add the parameter in following line

    inheritedInstance = setupDocInstance.shallowCopy(childSetupDoc, _idNumber); (Line 64)

    Thanks,

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

Responsible AI policies

As AI tools become more common, we’re introducing a Responsible AI Use…

Neeraj Kumar – Community Spotlight

We are honored to recognize Neeraj Kumar as our Community Spotlight honoree for…

Leaderboard > 🔒一 Microsoft Dynamics AX (Archived)

#1
Martin Dráb Profile Picture

Martin Dráb 4 Most Valuable Professional

#1
Priya_K Profile Picture

Priya_K 4

#3
MyDynamicsNAV Profile Picture

MyDynamicsNAV 2

Last 30 days Overall leaderboard

Featured topics

Product updates

Dynamics 365 release plans