Enum values SQL – D365 finance and operation

Enum values in D365 finance and operations stored in a table ‘ENUMIDTABLE’ and ‘ENUMVALUETABLE’. Below is the query to get or verify the enum values

Select eit.NAME,evt.ENUMID,evt.ENUMVALUE, evt.NAME as EnumValueName 
from ENUMIDTABLE eit
inner join ENUMVALUETABLE evt 
on eit.ID= evt.ENUMID
where eit.NAME='TaxDirection'

QR code for the page:

Security privilege access to the user – D365 finance and operations

To check that user has access to specific privilege in d365 finance and operation use below code

    // <summary>
    /// check user has privilige access
    /// </summary>
    /// <param name = "_privilege">privilege identifier as string</param>
    /// <param name = "_userID">UserId</param>
    /// <returns>boolean</returns>
    public static boolean checkUserHasPrivilgeAccess(str _privilege, UserId _userID = curUserId())
    {
        UserInfo userInfo;
        SecurityUserRole securityUserRole;
        SecurityPrivilege securityPrivilege;
        SecurityRolePrivilegeExplodedGraph securityRolePrivilegeExplodedGraph;

        select firstonly RecId, Id, ObjectId, networkDomain from userInfo
            exists join securityUserRole where securityUserRole.User == _userID
            exists join securityRolePrivilegeExplodedGraph 
                where securityRolePrivilegeExplodedGraph.SecurityRole == securityUserRole.SecurityRole
            exists join securityPrivilege 
                where securityPrivilege.RecId == securityRolePrivilegeExplodedGraph.SecurityPrivilege 
                && securityPrivilege.Identifier == _privilege;

        return userInfo.RecId;
    }

You can get privilege identifier from SecurityPrivilege table using privilege name or description

Leave a comment if you have any question.

Copy multiple attachments between different forms with data – Dynamics 365 for Finance and Operations

Scenario:

Sometime a requirement come that we need to copy multiple attachments from one form to another with the flow of data.
For example when purchase order creates from sales order then the attachments also need to be flow from sales order to purchase order

Solution:

First thing first -> Identify the relation between new and existing form(tables).

Then use the below code to achieve this.

I used the onInserted event ,you can use the same or whatever suitable for your scenario. Important thing is record should be created in new table then you can copy the attachment.

    /// <summary>
    ///
    /// </summary>
    /// <param name=”sender”></param>
    /// <param name=”e”></param>
    [DataEventHandler(tableStr(PurchTable), DataEventType::Inserted)]
    public static void PurchTable_onInserted(Common sender, DataEventArgs e)

    {
        DocuRef docuRef;
        PurchTable purchTable = sender;
        SalesTable salesTable = SalesTable::find(purchTable.InterCompanyOriginalSalesId);

        while select docuRef
              where docuRef.RefCompanyId== salesTable.DataAreaId
              && docuRef.RefTableId == salesTable.TableId
              && docuRef.RefRecID == salesTable.RecId
        {

            docuRef.RefTableId = purchTable.TableId;
            docuRef.RefRecId = purchTable.RecId;
            docuRef.insert();
        }
    }

 

//Leave your comments below if you have any query. I will try to help you to solve your problem

Get email subject and body from email template – D365 finance and operations

        SysEmailTable                   sysEmailTable;
        SysEmailMessageTable            sysEmailMessageTable;
        SysEmailContents                sysEmailContents;
        str                             subject, body;
        SysEmailId                      emailTemplateId = 'DlvNote'; //email template name
        LanguageId                      language = 'en-US';
               

        select sysEmailTable
               join sysEmailMessageTable
                where sysEmailMessageTable.EmailId==sysEmailTable.EmailId
                    && sysEmailMessageTable.EmailId== emailTemplateId
                    && sysEmailMessageTable.LanguageId==language;

        subject = SysEmailMessage::stringExpand(sysEmailMessageTable.Subject, mappings); //mappings = placeholders
        body    =  SysEmailMessage::stringExpand(sysEmailMessageTable.Mail, mappings);

Create and send SSRS report as attachment through email (using SysOutgoingEmailTable and Interactive and non-Interactive methodology) – D365 finance and operation

Generate report – In this example we are using delivery note report

Copy the below complete code - CustPacking slip email class contains all the code , Report generation, Email template, email place holders mapping, send email using sysoutgoingemailtable and interactive way email 

/// <summary>
/// Class to send packing slip email
/// </summary>
public class CustPackingSlipEmail
{
    CustPackingSlipJour     custPackingSlipJour;
    Map                     mappings = new Map(Types::String,Types::String);   

    /// <summary>
    /// CustPackingSlipEmail
    /// </summary>
    /// <param name = "_custPackingSlipJour">CustPackingSlipJour</param>
    /// <returns>CustPackingSlipEmail</returns>
    public static CustPackingSlipEmail construct(CustPackingSlipJour _custPackingSlipJour)
    {
        CustPackingSlipEmail CustPackingSlipEmail = new CustPackingSlipEmail();
        custPackingSlipEmail.parmPackingSlip(_custPackingSlipJour);

        return custPackingSlipEmail;
    }

    /// <summary>
    /// CustPackingSlipJour
    /// </summary>
    /// <param name = "_custPackingSlipJour">CustPackingSlipJour</param>
    /// <returns>CustPackingSlipJour</returns>
    public CustPackingSlipJour parmPackingSlip(CustPackingSlipJour _custPackingSlipJour = custPackingSlipJour)
    {
        custPackingSlipJour = _custPackingSlipJour;
        
        return custPackingSlipJour;
    }

    /// <summary>
    /// generateAndSendPackingSlipReport as binary container
    /// </summary>
    /// <param name = "_args">Args</param>
    public void generateAndSendPackingSlipReport(Args _args)
    {
        //Set  variables
        Array                           arrayFiles;
        SRSProxy                        srsProxy;
        Map                             reportParametersMap;
        SRSPrintDestinationSettings     settings;
        Filename                        fileName =     custPackingSlipJour.InternalPackingSlipId + '.pdf';
        SrsReportRunController          formLetterController = SalesPackingSlipController::construct();
        SalesPackingSlipController      controller = formLetterController;
        SalesPackingSlipContract        contract = new SalesPackingSlipContract();
        System.Byte[]                   reportBytes = new System.Byte[0]();
        SRSReportRunService             srsReportRunService = new SrsReportRunService();
        SRSReportExecutionInfo          executionInfo = new SRSReportExecutionInfo();

        Microsoft.Dynamics.AX.Framework.Reporting.Shared.ReportingService.ParameterValue[] parameterValueArray;
        
        //set the report contract parameters
        contract.parmRecordId(custPackingSlipJour.RecId);
        contract.parmTableId(tableNum(CustPackingSlipJour));

        //2nd paramter contract.parm2ndtParameter('2ndParamter value');
        //3rd parameter
        //4th parameter

        //set the report controller parameters
 
        //set report name and design name
        controller.parmArgs(_args);
        controller.parmReportName(PrintMgmtDocType::construct(
                                    PrintMgmtDocumentType::SalesOrderPackingSlip).getDefaultReportFormat());
        controller.parmShowDialog(false);
        controller.parmLoadFromSysLastValue(false);
        controller.parmReportContract().parmRdpContract(contract);

        // Provide printer settings

        settings = controller.parmReportContract().parmPrintSettings();
        settings.printMediumType(SRSPrintMediumType::File);
        settings.fileName(fileName);
        settings.fileFormat(SRSReportFileFormat::PDF);

        // Below is a part of code responsible for rendering the report

        controller.parmReportContract().parmReportServerConfig(SRSConfiguration::getDefaultServerConfiguration());
        controller.parmReportContract().parmReportExecutionInfo(executionInfo);
        srsReportRunService.getReportDataContract(controller.parmreportcontract().parmReportName());
        srsReportRunService.preRunReport(controller.parmreportcontract());
        reportParametersMap = srsReportRunService.createParamMapFromContract(controller.parmReportContract());
        parameterValueArray = SrsReportRunUtil::getParameterValueArray(reportParametersMap);
        srsProxy = SRSProxy::constructWithConfiguration(controller.parmReportContract().parmReportServerConfig());

        // Actual rendering to byte array
        reportBytes = srsproxy.renderReportToByteArray(controller.parmreportcontract().parmreportpath(),
                                                        parameterValueArray,
                                                        settings.fileFormat(),
                                                        settings.deviceinfo());

        // You can also convert the report Bytes into an xpp BinData object if needed
        container binData;
        Binary binaryData;
        System.IO.MemoryStream mstream = new System.IO.MemoryStream(reportBytes);
        binaryData = Binary::constructFromMemoryStream(mstream);

        if (binaryData)
        {
            binData = binaryData.getContainer();
        }

        SysEmailRecipients recipientEmailAddr = SysUserInfo::find(curUserId()).Email;

        //Send email using sysoutgoing email
        this.sendEmailWithAttachment(binData, fileName, recipientEmailAddr);

        //Send email using interactive methodology
        this.sendEmailWithAttachmentInteractive(binData, fileName, recipientEmailAddr);
    }


    /// <summary>
    /// sendEmailWithAttachment using sysoutgoing email table
    /// </summary>
    /// <param name = "_binData">container</param>
    /// <param name = "_fileName">str</param>
    /// <param name = "_recipientEmailAddr">SysEmailRecipients</param>
    public void sendEmailWithAttachment(container _binData, str _fileName, SysEmailRecipients _recipientEmailAddr)
    {
        SysEmailItemId                  nextEmailItemId;
        SysEmailTable                   sysEmailTable;
        SysEmailContents                sysEmailContents;
        SysOutgoingEmailTable           outgoingEmailTable;
        SysOutgoingEmailData            outgoingEmailData;
        str                             subject;
        Filename                        fileExtension = ".pdf";
        
        [sysEmailTable, subject, sysEmailContents] = this.getEmailTemplateDetails();
       
        if (sysEmailTable.RecId > 0)
        {
            nextEmailItemId  = EventInbox::nextEventId();
      
     
            outgoingEmailTable.clear();
            outgoingEmailTable.Origin                       = sysEmailTable.Description;
            outgoingEmailTable.EmailItemId                  = nextEmailItemId;
            outgoingEmailTable.IsSystemEmail                = NoYes::Yes;
            outgoingEmailTable.Sender                       = sysEmailTable.SenderAddr;
            outgoingEmailTable.SenderName                   = sysEmailTable.SenderName;
            outgoingEmailTable.Recipient                    = _recipientEmailAddr;
            outgoingEmailTable.Subject                      = subject;
            outgoingEmailTable.Priority                     = eMailPriority::High;
            outgoingEmailTable.WithRetries                  = NoYes::No;
            outgoingEmailTable.RetryNum                     = 0;
            outgoingEmailTable.UserId                       = curUserId();
            outgoingEmailTable.Status                       = SysEmailStatus::Unsent;
            outgoingEmailTable.Message                      =  sysEmailContents;
            outgoingEmailTable.LatestStatusChangeDateTime   = DateTimeUtil::getSystemDateTime();
            outgoingEmailTable.TemplateId                   = sysEmailTable.EmailId;
            outgoingEmailTable.insert();

            if (conLen(_binData) > 0)
            {
                outgoingEmailData.clear();
                outgoingEmailData.EmailItemId               = nextEmailItemId;
                outgoingEmailData.DataId                    = 1;
                outgoingEmailData.EmailDataType             = SysEmailDataType::Attachment;
                outgoingEmailData.Data                      = _binData;
                outgoingEmailData.FileName                  = _filename;
                outgoingEmailData.FileExtension             = fileExtension;
                outgoingEmailData.insert();
            }
        }
    }

    /// <summary>
    /// send email as interactive or non interactive methodology
    /// </summary>
    /// <param name = "_binData">container</param>
    /// <param name = "_fileName">str</param>
    /// <param name = "_recipientEmailAddr">SysEmailRecipients</param>
    public void sendEmailWithAttachmentInteractive(container _binData, str _fileName, SysEmailRecipients      
                                                     _recipientEmailAddr)
    {  
        System.Byte[]               binData1;
        System.IO.Stream            stream1;
        SysEmailTable               sysEmailTable; 
        SysEmailContents            sysEmailContents;
        str                         subject;
        SysIMailerInteractive       mail;
        SysMailerMessageBuilder     messageBuilder;
        
        Email                       sendFrom = SysUserInfo::find(curUserId()).Email;
        
        [sysEmailTable, subject, sysEmailContents] = this.getEmailTemplateDetails();
                
        // Turn the Bytes into a stream
        for (int i = 0; i < conLen(_binData); i++)
        {
            binData1 = conPeek(_binData,i+1);
            stream1  = new System.IO.MemoryStream(binData1);
        }

        //email sending settings
        mail            = SysMailerFactory::getInteractiveMailer();
        messageBuilder  = new SysMailerMessageBuilder();
        
        messageBuilder.reset()
            .setFrom(sendFrom) // From email address
            .addTo(_recipientEmailAddr) // To Email address
            .setSubject(subject) // Email Subject
            .setBody(sysEmailContents);        //Email Body

        if (stream1 != null)
        {
            //add attachment to the email
            messageBuilder.addAttachment(stream1, _filename);
        }
        
        //send email
        mail.sendInteractive(messageBuilder.getMessage());
    }

    /// <summary>
    /// getEmailTemplateDetails
    /// </summary>
    /// <returns>container</returns>
    private container getEmailTemplateDetails()
    {
        SysEmailTable                   sysEmailTable;
        SysEmailMessageTable            sysEmailMessageTable;
        SysEmailContents                sysEmailContents;
        str                             subject;
        SysEmailId                      emailTemplateId = this.getCustPackingSlipTemplateEmailId();
        LanguageId                      language = 'en-Au';
               
        this.populateEmailMessageMap();

        select sysEmailTable
               join sysEmailMessageTable
                where sysEmailMessageTable.EmailId==sysEmailTable.EmailId
                    && sysEmailMessageTable.EmailId== emailTemplateId
                    && sysEmailMessageTable.LanguageId==language;

        subject = SysEmailMessage::stringExpand(sysEmailMessageTable.Subject, mappings);
        sysEmailContents =  SysEmailMessage::stringExpand(sysEmailMessageTable.Mail, mappings);

        return [sysEmailTable, subject, sysEmailContents];
    }

    /// <summary>
    /// Get Email template
    /// </summary>
    /// <returns>SysEmailId</returns>
    private SysEmailId getCustPackingSlipTemplateEmailId()
    {
        SysEmailId emailId = CustParameters::find().DeliveryNoteEmailId; // new parameter field for email template

        if (!emailId)
        {
            throw error("@Label:DeliveryNoteEmailTemplateError");
        }

        return emailId;
    }

    /// <summary>
    /// populate map for email place holders
    /// </summary>
    private void populateEmailMessageMap()
    {
        SalesTable salesTable = SalesTable::find(custPackingSlipJour.SalesId);

        mappings.insert('DeliveryNoteNumber', custPackingSlipJour.PackingSlipId);
        mappings.insert('Version', custPackingSlipJour.InternalPackingSlipId);
        mappings.insert('SalesOrder', custPackingSlipJour.SalesId ); //strFmt("%1", missingHourTmp.Hour));
        mappings.insert('ProjectID', salesTable.ProjId);
        mappings.insert('CustAccount', custPackingSlipJour.OrderAccount);
        mappings.insert('DeliveryName', custPackingSlipJour.DeliveryName);
        mappings.insert('DeliveryAddress', custPackingSlipJour.deliveryAddress());
        mappings.insert('InvoiceName', custPackingSlipJour.InvoicingName);        
        mappings.insert('InvoiceAddress', custPackingSlipJour.invoicingAddress());
    }
}

Call the above class from the Sales packing slip controller class extension 

[ExtensionOf(classStr(SalesPackingSlipController))]
public final class custSalesPackingSlipController_Extension
{
    /// <summary>
    /// Send delivery note email
    /// </summary>
    /// <param name = "_args">Args</param>
    protected static void doMainJob(Args _args)
    {
        boolean sendMail = false;
        if (Box::yesNo('@Label:SendDeliveryNoteEmail', DialogButton::Yes, 
                         '@Label:SendDLvNoteEmail') == DialogButton::Yes)
        {
            sendMail = true;
        }

        next doMainJob(_args);
       
        if (sendMail)
        {
            CustPackingSlipJour custPackingSlipJour = _args.record();
            CustPackingSlipEmail custPackingSlipEmail = custPackingSlipEmail::construct(custPackingSlipJour);
            custPackingSlipEmail.generateAndSendPackingSlipReport(_args);

            Info('@Label:DlvNoteEmailSent');
        }
    }
}