Skip to main content

Process automation in Microsoft dynamics 365 Finance and Operations

Process automation in Microsoft Dynamics 365 Finance and Operations (D365FO) is a powerful feature that allows you to schedule processes to be executed by the batch server automatically. It simplifies the creation of batch jobs, thanks to the built-in wizards within the Process Automation setup. The updated calendar view of the scheduled work allows end users to view and take action on scheduled and completed work. 


The central administration page for all process automations is found in the System Administration module under the Setup menu. This page will list all automated processes (series) that are set up in the system. It will also allow you to add new process automations directly from this page. After a series is set up, you can manage each series from this list. You can choose to edit the entire series, delete it, view all occurrences in a list view, or disable the series if you would like to pause the scheduled work for a while.

For more information about process automation, see Process automation and Process automation framework development.

In this blog, we will create a dummy process to demonstrate the necessary steps for implementing scheduled tasks using Process Automation.

Before we implement any of the required components, ensure that your model references the ProcessAutomation Model.


When implementing a process automation solution, you’ll encounter a mix of required and optional tasks. However, it’s essential to note that most UI customizations are not supported for background processes. The Series list page and logging of results and messages are fully supported. Read more here.

To implement a process by using the process automation framework, you must first understand the concept of a type in the framework. A type is a unique process that is integrated with the batch framework and that uses the SysOperations framework, specifically the SysOperationServiceController class. The types are stored in the ProcessScheduleType table.

If an existing process uses RunBaseBatch, consider wrapping it with SysOperationServiceController. The process automation framework doesn't support RunBaseBatch

Below is our code for BloggerController extending the SysOperationServiceController class:

using System.ComponentModel.Composition;

/// <summary>
/// Implementation of <c>SysOperationServiceController</c> which
/// orchestrates the work of the required operation task.
/// </summary>
internal final class BloggerController extends SysOperationServiceController
{
    private ProcessExecutionSourceLink sourceLink;

    /// <summary>
    /// Implement logic here to enable/disbale the automation of the task
    /// </summary>
    /// <returns>True if the process automation task is enabled;
    /// false otherwise.</returns>
    internal static boolean isEnabled()
    {
        return true;    
    }

    /// <summary>
    /// Initializes a new instance of the <c>BloggerController</c> class.
    /// </summary>
    /// <returns>A new instance of the <c>BloggerController</c> class.</returns>
    internal static BloggerController construct()
    {
        return new BloggerController(
            classStr(BloggerController),
            methodStr(BloggerController, processTask),
            SysOperationExecutionMode::Synchronous);
    }

    /// <summary>
    /// Process the tasks in the active batch.
    /// </summary>
    /// <param name = "_processScheduleWorkItem">The active process
    /// automation schedule work item.</param>
    internal void processTask(ProcessScheduleWorkItem _processScheduleWorkItem)
    {                
        sourceLink = this.createProcessExecutionSourceLink(_processScheduleWorkItem);
        BloggerController::doWork();                      
    }

    /// <summary>
    /// Obtains a <c>ProcessExecutionSourceLink</c> record identifier.
    /// </summary>
    /// <param name = "_worker">A worker record.</param>
    /// <param name = "_processScheduleWorkItem">The active process automation
    /// schedule work item.</param>
    /// <returns>A record id within the ProcessExecutionSourceLinktable.</returns>
    private ProcessExecutionSourceLink createProcessExecutionSourceLink(
        ProcessScheduleWorkItem _processScheduleWorkItem)
    {
        ProcessExecutionSourceLinkItem sourceLinkItem =
            ProcessExecutionSourceLinkItem::construct();
        sourceLinkItem.parmExecutionId(
            _processScheduleWorkItem.parmExecutionId());
        sourceLinkItem.parmProcessScheduleOccurrenceRecId(
            _processScheduleWorkItem.parmProcessScheduleOccurrenceRecId());
        sourceLinkItem.parmProcessScheduleSeriesPatternRecId(
            _processScheduleWorkItem.parmProcessScheduleSeriesPatternRecId());
        sourceLinkItem.parmMessage(literalStr("Blogger automation process"));
        sourceLinkItem.parmExecutionSourceStatus(
            ProcessExecutionSourceStatus::Success);

        return ProcessExecutionSourceLink::insertSourceLink(sourceLinkItem);
    }

    internal static void doWork()
    {
        // do nothing;
        // Add your code here
    }

}


Process Automation Implementation
To run the process automation framework, a process must implement the ProcessAutomationTask interface. The framework uses this interface to provide an instance of ProcessScheduleWorkItem. That instance contains information about the series, the occurrence that is being run (if applicable), and the execution ID. You must create the batch task that has to be run, and you must provide the task to the framework. The framework then creates the batch header and the tasks that are provided.

Polled processes don't have occurrences, because they might be run frequently, and those frequent runs will create many more occurrences than you want to track. Instead, you use a unique execution ID to track every run of a polled process. An execution ID is also assigned to scheduled processes.

The following code snippet shows the implementation of the ProcessAutomationTask interface for our BloggerAutomationTask:

using System.ComponentModel.Composition;

[ExportMetadataAttribute(classStr(ProcessAutomationTask),
classStr(BloggerAutomationTask))]
[ExportAttribute(
identifierStr('Microsoft.Dynamics.AX.Application.ProcessAutomationTask'))]
internal final class BloggerAutomationTask  extends ProcessAutomationTask
{
    /// <summary>
    /// Gets a value indicating whether this process automation
    /// task is enabled in the system.
    /// </summary>
    /// <returns>True if the process automation task is enabled;
    /// false otherwise.</returns>
    protected final boolean isProcessAutomationEnabledForThisTask()
    {
        return BloggerController::isEnabled();
    }

    /// <summary>
    /// Obtains <c>SysOperationController</c> instances which orchestrate
    /// work to be performed.
    /// </summary>
    /// <returns>A list of <c>SysOperationController</c> objects.</returns>
    protected List getListOfWorkToBePerformed()
    {
        List tasks = new List(Types::Class);

        BloggerController bloggerController = BloggerController::construct();
        ProcessScheduleWorkItem processScheduleWorkItemContract =
            bloggerController.getDataContractObject() as ProcessScheduleWorkItem;
        processScheduleWorkItemContract.initializeFromScheduleWorkItem(
            this.parmProcessScheduleWorkItem());
       
        tasks.addEnd(bloggerController);

        return tasks;
    }

    /// <summary>
    /// Gets and sets the batch job caption that will be used
    /// by the batch job scheduled to execute this process.
    /// </summary>
    /// <returns>Batch caption that will be used for the batch job that
    /// will perform the task.</returns>
    protected BatchCaption batchJobCaption()
    {
        return "Blogger automation process";
    }

}


The getListOfWorkToBePerformed method determines whether there is work that must be done. If there is, the method returns a list of batch-enabled classes that inherit from SysOperationServiceController. This method must be efficient and fast, because it's run in the context of the polling process. Therefore, you should not do any work in this method except check whether work must be done and create batch tasks for any work that must be done. It's OK if the method returns an empty list, because an empty list indicates that no work must be done. In this case, the process automation framework doesn't create any batch processes. Read more on running processes in the process automation framework here.

Type registration
To register your type with the process automation framework, you must implement the ProcessScheduleITypeRegistration interface. This interface has a single method that returns an instance of ProcessScheduleTypeRegistrationItem.

If your process uses a feature flag, you must disable and enable the type as the feature is disabled and enabled, respectively.
  • If you disable the feature flag for a type, the type doesn't appear in the user interface (UI). The scheduler won't schedule any occurrences or background processes of that type to run, and the runtime side of the process automation framework won't create any batch jobs for that type.
  • If you enable the feature flag for a type, any occurrences or background processes that are scheduled to run in the past will be run immediately. Usually, this behavior is what you want. However, if it isn't what you want, consider disabling any series that is related to the type before you disable the feature flag.
Feature management has events that you can subscribe to. The method that you use to enable and disable types is ProcessScheduleTypeRegistration.enableOrDisableType.

We will  implement the following BloggerAutomationTypeRegistration: 
using System.ComponentModel.Composition;

[Export(identifierStr(Dynamics.AX.Application.ProcessScheduleITypeRegistration))]
internal final class BloggerAutomationTypeRegistration
    implements ProcessScheduleITypeRegistration
{
    internal const ProcessScheduleTypeName TypeName = literalStr("BloggerAutomation");

    /// <summary>
    /// Obtains information about the type being registered.
    /// </summary>
    /// <returns>
    /// A new instance of <c>ProcessScheduleTypeRegistrationItem</c>
    /// which describes the type being registered
    /// with process automation.
    /// </returns>
    public ProcessScheduleTypeRegistrationItem getScheduleTypeRegistrationItem()
    {
        ProcessScheduleTypeRegistrationItem item =
            ProcessScheduleTypeRegistrationItem::construct();

        item.parmName(TypeName);
        item.parmCompanyScope(ProcessScheduleTypeCompanyScope::Global);
        item.parmLabelId(literalStr("Blogger automation process"));
        item.parmScheduleType(ProcessScheduleProcessType::Polled);
        item.parmProcessAutomationTaskClassName(classStr(BloggerAutomationTask));
        item.parmIsEnabled(BloggerController::isEnabled());

        return item;
    }

}

Here are a few examples of different types that are registered with the process automation framework:
  • Vendor Payment Proposal (VendPaymProposalAutomationTypeRegistrationProvider class)
  • Vendor Invoice Posting (VendInvoicePostProcessScheduleTypeRegistration class)
  • Subledger transfer to general ledger (SubledgerJournalVoucherTransferServiceRegistration class)
Read more on the ProcessScheduleTypeRegistrationItem class here.

Series registration
Every process must have a series. The concept of a series in process automation resembles the concept of a meeting series in Microsoft Outlook. However, a series in process automation is a series of scheduled runs of a process. For most scheduled process types, users create the series in the user interface (UI), and series registration never has to be implemented. However, if the process that is being implemented is a schedule series, you can skip this task.

Background processes typically create a series via code, by using series registration, because background processes tend to be "under the hood" processes that don't allow for user interaction. To create a series via code, you implement the ProcessScheduleISeriesRegistration interface. This interface contains a single method that returns an instance of ProcessScheduleSeriesRegistrationItem.

Here is the code snippet for our BloggerAutomationSeriesRegistration that implements the ProcessScheduleISeriesRegistration:
using System.ComponentModel.Composition;

[Export(identifierStr(Dynamics.AX.Application.ProcessScheduleISeriesRegistration))]
[ExportMetadata(classStr(ProcessScheduleISeriesRegistration),
classStr(BloggerAutomationSeriesRegistration))]
internal final class BloggerAutomationSeriesRegistration
implements ProcessScheduleISeriesRegistration
{
    public ProcessScheduleSeriesRegistrationItem getProcessScheduleSeriesRegistrationItem()
    {
        ProcessScheduleSeriesRegistrationItem processScheduleSeriesRegistrationItem =
            ProcessScheduleSeriesRegistrationItem::construct();

        processScheduleSeriesRegistrationItem.parmDescription(
            "Blogger automation process");
        processScheduleSeriesRegistrationItem.parmOwnerId(curUserId());
        processScheduleSeriesRegistrationItem.parmTypeName(
            BloggerAutomationTypeRegistration::TypeName);
        processScheduleSeriesRegistrationItem.parmSeriesName(
            "Blogger automation process");

        processScheduleSeriesRegistrationItem.parmProcessScheduleSeriesPatternList(
            this.getSeriesPatternList());

        return processScheduleSeriesRegistrationItem;
    }

    private List getSeriesPatternList()
    {
        List list = new List(Types::Class);

        ProcessScheduleSeriesPatternItem patternItem =
            ProcessScheduleSeriesPatternItem::construct();
        patternItem.parmUnit(ProcessScheduleUnit::Minute);
        patternItem.parmPollingInterval(5);

        list.addEnd(patternItem);

        return list;
    }
}


When the pattern is configured, applicable fields are determined based on the unit. Not all methods that are defined in the following table work for all units. The methods that apply to units are defined in the tables later in this section. Other combinations will be ignored. For polled processes, only the unit and the polling interval are used. 
Other fields are ignored. Read more on the ProcessScheduleSeriesRegistrationItem class here.

Build your project & Synchronise the DB.

Finally, navigate to System administration > Setup > Process automations. Click on Initialize process automations in the Action pane to initialize the automated process tasks.

Click on Background processes and ensure that your automated task is added to the process automation tasks schedule.

Users can also update the schedule of the task once the automated process series has been  initialized by clicking on the Edit button.



If the batch job isn't running, or it's in an error state, select Initialize process automation to reset the batch job. This reset ensures that any new automations released in a more recent version of the application are initialized.

The process automation framework allows developers to extend the process automation framework. The Process automation framework documentation provides information about how you can create custom processes that you require to be run by the batch server scheduled with the process automation wizard and appear in the calendar view automatically.

I hope this guide can be helpful for any D365FO enthusiast trying to streamline things with Process automation. :)

Comments

Popular posts from this blog

Conditionally Hiding Menu Items in D365 FinOps Menus Without Using Feature Keys

In Microsoft Dynamics 365 Finance and Operations (D365 F&O), menu items are the links that can be clicked on to access forms, reports, classes, jobs, and queries. Feature keys are typically used to control the visibility of menu items based on user licenses. However, there are scenarios where you might want to hide menu items conditionally without relying on feature keys.  This can be accomplished by extending the 'SysMenuNavigationObjectFactory' class and modifying the checkAddSubMenu(...) method.  Suppose we want to hide the  Vendor payment journal menu item under Accounts payable > Payments > Vendor payment journal Steps 1. Create an extension of the SysMenuNavigationObjectFactory class [ ExtensionOf ( classStr (SysMenuNavigationObjectFactory))] internal final class SysMenuNavigationObjectFactory_PS_Extension { } 2. Create a Chain of Command (CoC) for the checkAddSubMenu method [ ExtensionOf ( classStr (SysMenuNavigationObjectFactory))] internal final...

Electronic reporting (ER) - Create custom destinations

Electronic reporting (ER) is a configurable tool in Microsoft Dynamics 365 that helps create and maintain regulatory electronic reporting and payments. Configuration of reports can be done by business users with the use of Visual Editors without the need for developers. Refer to Electronic reporting (ER) overview - Finance & Operations | Dynamics 365 | Microsoft Docs for more information and detailed overview on ER. Destinations can be configured for each ER format configuration and its output component. Destinations can only be set up for ER configurations that have been imported into the current Finance instance, and for the formats that are available on the Electronic reporting configurations page. The functionality for ER destination management is available at Organization administration > Electronic reporting > Electronic reporting destination.  Microsoft Dynamics 365 for Operations version 1611 (November 2016) or later allows the use of  the following destinat...

Dynamics 365 eCommerce - Setting up the Development Environment

Microsoft Dynamics 365 Commerce is an evolution of Dynamics 365 Retail, which launched with new and improved capabilities for e-commerce, content management. To begin development and extension on Dynamics 365 eCommerce we'll need to install the following tools on the development machine: VSCODE - https://code.visualstudio.com Node.js - https://nodejs.org   (Currently version 10.x is the main supported version and the MSI installer can be found here : https://nodejs.org/dist/latest-v10.x/   ) Yarn - https://legacy.yarnpkg.com Git - https://git-scm.com/downloads (Note that development on eCommerce is only supported on Windows (as at 18/06/20).) Create a folder in your local drive to hold the e-Commerce site code - 'C:\repos' Open CMD in administrator mode and go to the folder directory you just created and clone the  Microsoft eCommerce repository with the following command:  git clone https://github.com/microsoft/Msdyn365.Commerce.Online.git This may take a while....