Monday, February 16, 2026

Custom Workflow in D365fo

In this blog we see all about Workflows development in D365fo 

Output as shown in below


We will see in this blog first way

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

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

Configure workflow in front and assign required approvers.

Thankyou!!

No comments:

Post a Comment

Custom Workflow in D365fo

In this blog we see all about Workflows development in D365fo  Output as shown in below We will see in this blog first way Step1:-Create a b...