Monday, February 16, 2026

How to develop Custom Workflow in D365fo

In this blog we see all about Workflows development in D365fo 

Output as shown in below




Step1:-Create a base enum with elements as shown in below and name it as Workflowstatus
Step2:-Add it in the table as well as form as shown in below



Step3:-Write code in canSubmitToWorkFlowmethod in Table level , This will make you to submit the workflow in its is Draft allow.

Code:-
public boolean canSubmitToWorkFlow(str _workflowType = "")  
  {        
      boolean ret;           
      ret = super(_workflowType);       
    if (this.WorkflowStatus == WorkflowStatus::Draft)    
    {        
       ret = true;       
    }         
    return ret;   
}

And also write new method for updating workflow status

Code:-
public static void  updateWorkFlowStatus(RefRecId _recId , WorkflowStatus _status)   
   {   
     EmployeeTable   employeeTable;    
      ttsbegin;           
     select firstonly forupdate employeeTable        
      where employeeTable.RecId == _recId;      
    
employeeTable.EmployeeWFStatus = _status;    
    employeeTable.update();       
    ttscommit;    
}

Step4:-Add new Query and drag table into datasource and make Dynamicfields property to YES 
as shown in below
Step 5:-Create a Workflow category and give Module name in property and this will make workflow type available in frontend to configure the workflow steps.Here i am giving Vendor to visible 
in Accounts payable workflows
Step 6:-Add Workflow Type 
And Provide all details created earlier as shown in below:-

Click on next then system itself creates a event handlers, classes as shown in below
Step 7:-Write code in Workflowtype eventhandler class

Code:-
          /// <summary>

/// The CustomWorkflowTypeEventHandler workflow event handler.
/// </summary>
public class  CustomWorkflowTypeEventHandler implements WorkflowCanceledEventHandler,  
  WorkflowCompletedEventHandler,
  WorkflowStartedEventHandler
{
    public void started(WorkflowEventArgs _workflowEventArgs)
  {
    // TODO:  Write code to execute once the workflow is started.
        EmployeeTable::updateWorkFlowStatus(_workflowEventArgs.parmWorkflowContext().parmRecId(), WorkflowStatus::Pending);
  }

 

    public void canceled(WorkflowEventArgs _workflowEventArgs)
  {
    // TODO:  Write code to execute once the workflow is canceled.
        EmployeeTable::updateWorkFlowStatus(_workflowEventArgs.parmWorkflowContext().parmRecId(), WorkflowStatus::Draft);
  }

 

    public void completed(WorkflowEventArgs _workflowEventArgs)
  {
    // TODO:  Write code to execute once the workflow is completed.
        EmployeeTable::updateWorkFlowStatus(_workflowEventArgs.parmWorkflowContext().parmRecId(), WorkflowStatus::Completed);
  }

 

}

Step8:- Add code in Submitmanagerclass

Code:- 

          /// <summary>

/// The CustomWorkflowTypeSubmitManager menu item action event handler.
/// </summary>
public class CustomWorkflowTypeSubmitManager 
{
    public static void main(Args _args)
  {
    //  TODO:  Write code to execute once a work item is submitted.

 

        EmployeeTable                 employeetable;
        CustomWorkflowTypeSubmitManager    submitManger = new CustomWorkflowTypeSubmitManager();
        recId                              _recId =_args.record().RecId;
        WorkflowCorrelationId         _workflowCorrelationId;
        workflowTypeName           _workflowTypeName = workFlowTypeStr("CustomWorkflowType");
        WorkflowComment              note = "";
        WorkflowSubmitDialog        workflowSubmitDialog;

 

        workflowSubmitDialog = WorkflowSubmitDialog::construct(_args.caller().getActiveWorkflowConfiguration());
        workflowSubmitDialog.run();

        if (workflowSubmitDialog.parmIsClosedOK())
        {
            employeetable = _args.record();
            note = workflowSubmitDialog.parmWorkflowComment();
            try
            {
                ttsbegin;
                _workflowCorrelationId = Workflow::activateFromWorkflowType(_workflowTypeName, employeetable.RecId, note, NoYes::No);
                employeetable.WorkflowStatus = WorkflowStatus::Submitted;
                employeetable.update();
                ttscommit;

                info("Submitted to workflow.");
            }
            catch (Exception::Error)
            {

                error("Error on workflow activation.");
            }
        }
        _args.caller().updateWorkFlowControls();
  }

 

}

Step 9:-Add Workflow Approval 

Provide details created in above steps


After clicking next system will create some classes and action menuitems

Step10:-Add code in Approval event handler

Code:-

       /// <summary>

/// The CustomWorkflowApprovalEventHandler workflow outcome event handler.
/// </summary>
public final class CustomWorkflowApprovalEventHandler implements WorkflowElementCanceledEventHandler,
  WorkflowElemChangeRequestedEventHandler,
  WorkflowElementCompletedEventHandler,
  WorkflowElementReturnedEventHandler,
  WorkflowElementStartedEventHandler,
  WorkflowElementDeniedEventHandler,
  WorkflowWorkItemsCreatedEventHandler
{
    public void started(WorkflowElementEventArgs _workflowElementEventArgs)
  {
    // TODO:  Write code to execute once the workflow is started.
        EmployeeTable::updateWorkFlowStatus(_workflowElementEventArgs.parmWorkflowContext().parmRecId(), WorkflowStatus::InReview);
  }

 

    public void canceled(WorkflowElementEventArgs _workflowElementEventArgs)
  {
    // TODO:  Write code to execute once the workflow is canceled.
        EmployeeTable::updateWorkFlowStatus(_workflowElementEventArgs.parmWorkflowContext().parmRecId(), WorkflowStatus::Draft);
  }

 

    public void completed(WorkflowElementEventArgs _workflowElementEventArgs)
  {
    // TODO:  Write code to execute once the workflow is completed.
        EmployeeTable::updateWorkFlowStatus(_workflowElementEventArgs.parmWorkflowContext().parmRecId(), WorkflowStatus::Completed);
  }

 

    public void denied(WorkflowElementEventArgs _workflowElementEventArgs)
  {
    // TODO:  Write code to execute once the workflow is denied.
        EmployeeTable::updateWorkFlowStatus(_workflowElementEventArgs.parmWorkflowContext().parmRecId(), WorkflowStatus::Rejected);
  }

 

    public void changeRequested(WorkflowElementEventArgs _workflowElementEventArgs)
  {
    // TODO:  Write code to execute once change is requested for the workflow.
        EmployeeTable::updateWorkFlowStatus(_workflowElementEventArgs.parmWorkflowContext().parmRecId(), WorkflowStatus::ChangeRequest);
  }

 

    public void returned(WorkflowElementEventArgs _workflowElementEventArgs)
  {
    // TODO:  Write code to execute once the workflow is returned.
        EmployeeTable::updateWorkFlowStatus(_workflowElementEventArgs.parmWorkflowContext().parmRecId(), WorkflowStatus::Rejected);
  }

 

    public void created(WorkflowWorkItemsEventArgs _workflowWorkItemsEventArgs)
  {
    // TODO:  Write code to execute once work items are created.
  }

 

}

Step11:-Write code in Resubmit class

/// <summary>
/// The CustomWorkflowApprovalResubmitActionMgr menu item action event handler.
/// </summary>
public class CustomWorkflowApprovalResubmitActionMgr 
{
    public static void main(Args _args)
  {
    //  TODO:  Write code to execute once work items are resubmitted.

 

        if (!_args.record() || !_args.caller())
        {
            throw error(Error::wrongUseOfFunction(funcName()));
        }

        WorkflowWorkItemTable workItem = _args.caller().getActiveWorkflowWorkItem();
        if (workItem)
        {
            try
            {
                WorkflowWorkItemActionDialog dialog = WorkflowWorkItemActionDialog::construct(
                    workItem,
                    WorkflowWorkItemActionType::Resubmit,
                    new MenuFunction(_args.menuItemName(),_args.menuItemType()));
                dialog.run();

                if (dialog.parmIsClosedOK())
                {
                    workItem = _args.caller().getActiveWorkflowWorkItem();
                    WorkflowWorkItemActionManager::dispatchWorkItemAction(
                        workItem,
                        dialog.parmWorkflowComment(),
                        dialog.parmTargetUser(),
                        WorkflowWorkItemActionType::Resubmit,
                        _args.menuItemName());

                    RecID recID = _args.record().RecId;
                    ttsbegin;
                    EmployeeTable::updateWorkFlowStatus(recID, WorkflowStatus::Submitted);
                    info("@AccountsPayable:VendBankAccountChangeProposalResubmit_Success");
                    ttscommit;
                }
            }
            catch(exception::Error)
            {
                info("@AccountsPayable:VednBankAccountChangeProposalResubmit_Error");
            }
        }

        // make sure changes in status are reflected on the caller
        if (FormDataUtil::isFormDataSource(_args.record()))
        {
            FormDataSource callerDS = FormDataUtil::getFormDatasource(_args.record());



            callerDS.reread();
            callerDS.refresh();
        }
        _args.caller().updateWorkflowControls();
  }

 

}


Step12:-Change labels for action menu items that have been created by systems

Note:- for cancel put Recall

Step13:-In the Workflowtype add in supported elements and in the properties give workflowapproval and give Approval in type as shown in below

Step 14:-In form design properties changes properties of Workflow


Configure workflow in front and assign required approvers.

Step15:- Create security privilege maintain and view for all action menuitems that have created in workflow 

Thankyou!!

No comments:

Post a Comment

Generate entity diagram in d365fo

 Today in this blog we learn about how to see relations between tables in pictorial representation. Step1:- Navigate to Github  https://gith...