Create a custom feature in D365 for finance and operations – D365 FnO

Custom feature is a new addition in dynamics 365 for finance and operation. Microsoft is releasing continuously new features for the users and you can find those in the feature management workspace. Some features are enabled by default and some left for the users either they want to enable or not. If you are using the same functionality then you can enable to use the enhancement released in the feature else leave those as disable.

If you want to create a new custom feature use the below code,

Copy the code in new class (rename labels and links), build the code

Open d365 Feature management workspace and click on check for updates

using System.ComponentModel.Composition;
using Microsoft.Dynamics.ApplicationPlatform.FeatureExposure;

/// <summary>
/// Custom feature development
/// </summary>
[ExportAttribute(identifierStr(Microsoft.Dynamics.ApplicationPlatform.FeatureExposure.IFeatureMetadata))]
internal final class CustomFeature implements IFeatureMetadata
{
    private static CustomFeature instance = new CustomFeature();

    private void new()
    {
    }

    [Hookable(false)]
    public static CustomFeature instance()
    {
        return CustomFeature::instance;
    }

    [Hookable(false)]
    public FeatureLabelId label()
    {
        return literalStr('Custom Feature');//("@Label:CustomFeature");
    }

    [Hookable(false)]
    public int module()
    {
        return FeatureModuleV0::SubscriptionBilling;
    }

    [Hookable(false)]
    public FeatureLabelId summary()
    {
        return strFmt('Customer Feature summary');//("@Label:CustomFeatureSummary");
    }

    [Hookable(false)]
    public WebSiteURL learnMoreUrl()
    {
        return "https://www.usdynamics365.com";
    }

    [Hookable(false)]
    public boolean isEnabledByDefault()
    {
        return false;
    }

    [Hookable(false)]
    public boolean canDisable()
    {
        return true;
    }

}

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');
        }
    }
}

Package deployment timeout issues (retail retail related errors)- D365 for finance and operations

Scenario:

If you are receiving retail related errors on cloud hosted environments. here is the solution:

Note: Below solution is only applicable if you’re not using the retail functionality at all and you have RDP access of the environment.

Retail server:

If you’re receiving Retail server related error than do the below steps:

Run the DropAllRetailChannelDbObjects.sql and you can find the script in below path K:\DeployablePackages\\RetailServer\Scripts”

This will drop all retail server objects but will be recreated during the update. After running the script, resume the update again.

RetailCloudPos:

RetailStorefront:

If you’re receiving error related to above two and it is timeout error(check in the log file):

Find the step in the runbook and get the script location:

Copy the UpdateCloudPos to the desktop or any other folder as backup

Open the copy file in notepad and clear the script (empty file)

Copy the empty file back to the above folder and replace it.

Same steps you have to do for RetailStorefront.

Resume the package deployment.

D365 apply updates failed – package deployment failed – Dynamics 365 for finance and operations

Issue:

Few times we came across the issue of failing of D365 apply updates or package deployment failed at the step of DevTools.

Resolution:

To resolve the issue follow below steps:

  1. Download the package if not downloaded
  2. Unblock the downloaded package Zip file before extracting
  3. Extract the the package zip file
  4. Navigate to the DevToolsService/Scripts folder in the extracted package folder
  5. Find the the file Microsoft.Dynamics.Framework.Tools.Installer
  1. Double click on the file and install the extension for VS2019

  1. Once extension installation complete resume the package deployment.

Please feel free to comment if you’re facing any issue. We will try our best to help you solve the issues.

D365 Visual studio set default model for new projects

To set default model for new projects in visual studio update the below configuration with your model name

C:\Users\user account\Documents\Visual Studio Dynamics 365Screen Shot 2021-11-23 at 11.24.26 am.png

Edit the file in the notepad and search for default model for new projects node

Screen Shot 2021-11-23 at 11.27.49 am.png

Replace the model name in this case it “Fleet management” with your model name

General Electronic Reporting (GER) or Electronic Reporting (ER) in Dynamics 365 for Finance – Part 1

It is a configureable tool  for regulatory reporting, payments and electronic reporting.

The ER engine is targeted on the business users instead of developers. Because you configure formats instead of code.

ER supports :

  • TEXT
  • XML
  • PDF
  • Microsoft word document
  • Open XML Worksheets

 

The ER tool allows you to configure formats for electronic documents in accordance with the legal requirements of various countries or regions. ER lets you manage these formats during their life cycle.

 

ER engine capabilities:

  • single shared tool for electronic reporting in different domains, and replace more than 20 engines that do some electronic reporting for Finance.
  • Its reports formats is applicable to different versions of Finance means report dependancy not on the version of the finance.
  • Custom formats can be created based on original formats. Easily can change the format based on the requirement due its support for localisation.
  • It becomes the primary standard tool of electronic reporting for both Microsoft and Microsoft Partners.

 

 

 

How to apply platform update on local VM or development VM Dynamics 365 for Finance and Operations

Scenario: We were using dev environment of Dynamics Finance and Operations with Application release version 10.0 platform update 24. Microsoft released a new version 10.0.5 with platform update 26.

Now we have two options to use the new version.

  1.  Download new VM and moved the custom models to the newly created VM
  2. upgrade the current VM on the latest update

In first option we need to create and setup the testing data again to test the code.

2nd option everything will remain same and no extra effort needed other than the applying update.

Definitely we selected the 2nd option and below steps were performed to update the environment.

Steps:

  • Download the latest release package from the asset library to the local VM development environment
  • Unzip the zip file to the local VM in C drive by creating a custom folder (folder should not be in the users folder directory). I created  with below name FnO10.0.234.10001App
  • Stop the following services (Batch, Data import and IIS)
  • Close Visual studio instances
  • open command prompt as administrator
  • set the directory to your custom folder contains the unzip files
    • C:\FnO10.0.234.10001App>
  • Run the below command to check the list of components installed
    • C:\FnO10.0.234.10001App>AxUpdateInstaller.exe list
  • From the above command you will get all the components already installedCommponents versions before update.png
  • update the DefaultTopologyData.xml file located in the update folder in my case located in below location
    • FnO10.0.234.10001App -> DefaultTopologyData.xml

 

  • Best way to update this file copy on another location edit like below and replace in the above folderDefaultTopologyData file.png

 

  • Generate the run book thru command prompt using below command
    • Actual command: AXUpdateInstaller.exe generate -runbookid=[runbookID] -topologyfile=[topologyFile] -servicemodelfile=[serviceModelFile] -runbookfile=[runbookFile]
    • [runbookID]– A parameter that is specified by the developer who applies the deployable package.
    • [topologyFile]– The path of the DefaultTopologyData.xml file.
    • [serviceModelFile]– The path of the DefaultServiceModelData.xml file.
    • [runbookFile]– The name of the runbook file to generate (for example, AOSRunbook.xml).
    • Should be modify as below
    • AXUpdateInstaller.exe generate -runbookid=”Dev-runbook” -topologyfile=”DefaultTopologyData.xml -servicemodelfile=”DefaultServiceModelData.xml” -runbookfile=”Dev-runbook.xml”

 

  • Import the run thru command prompt using below command
    • AXUpdateInstaller.exe import -runbookfile=”Dev-runbook.xml”

 

  • Execute the runbook thru command prompt using below command
    • AXUpdateInstaller.exe execute -runbookid=”Dev-runbook”

runbook execute.png

This step is the longest step involving many sub-steps performed during this step.

Some steps even takes 2,3 or may more hours. please make sure system will not logout neither shutdown.

if any step fails during this step use below command to rerun

For me its fails on 19 step

runbook step rerun.png

 

Wait till last step completed

 

runbook execution complete.png

 

  • Verify the installation by running below command
    • AXUpdateInstaller.exe list

updated version list.png

You can check the version also by login to the dynamics browser about option.

 

Screen Shot 2019-11-26 at 11.37.19 AM.png

 

Please leave comment in the comments section if you are facing any issue while updating. I will try to assist.

 

Good luck!!!

 

How delete a specific model/package? Uninstall deployable package. Microsoft Dynamics 365 for Finance and Operations

Sometimes we need to delete the model and deployable package from the dev environments.

 

Model Deletion:

use Modelutil.exe to delete the model file from the packages local directory.

 

Package installation or deletion:

 

  1. Stop IIS (kill the IIS worker process from the task manager)
  2. Stop batch job DynamicsAXBatch job
  3. Delete the package folder located on C:\ or K:\AosService\PackagesLocalDirectory (make sure folder completely deleted)
  4. Open VS Dynamics -> Model Management -> Refresh models

 

 

 

Chain Of command – Forms – Dynamics 365 for Finance and Operations – X++

Chain of Command now supports the more scenarios for the extensions on forms:

Now you can wrap the methods on forms, Forms data sources, Datafields and on form control methods.

  1. Forms
  2. Form Data Sources
  3. Form Controls
  4. fields in data sources

Below is the complete example of Form Data source and just a code snippet for the rest. In below example we are updating the ratingmodel based on our custom logic that is global variable on the form.

Note: use element.FormToExtendVariable to access the form variables and datasources

Use element.FormToExtendMethod() to call the form methods

Screen Shot 2019-11-03 at 3.20.34 PM.png

 

  1. [ExtensionOf(FormStr(FormName))] // for form extension
  2. [ExtensionOf(FormControlStr(FormName,FormControl))] // for form control method extension
  3. [ExtensionOf(FormDataFieldStr(FormName,FormDataSource,DataField))] // for form datasource method extension
  4. [ExtensionOf(FormDataSource(FormName,FormDataSource) // for form data source extension

Get Form Control on Any form – Dynamics 365 for Finance and Operations – X++

To get any form control on any form use below method. Create this method as static and use anywhere in your code.

 

Just need to provide FormRun and control name as parameters

 

Public Static FormControl getFormControl(FormRun _formRun, str _controlName)

    {

        FormControl control = _formRun.control(_formRun.controlId(_controlName));

        Debug::assert(control != null);

        

        return control;

    }