Create a Simple Approval Workflow in Microsoft Dynamics D365


Step 1:
Basing on the requirement create a new base enum or use the existing base enum





Step 2:

          Create new fields to your main table – Ex:  SMJ_PurchaseOrder
•        Status – Enum Type -  SMJ_Workflow
•        SubmittedBy – Edt  –  UserId
•        SubmittedDateTime – Edt  –  DateSubmitted

Set the properties for the above 3 fields
 AllowEditOnCreate – No
AllowEdit – No 

Step 3:

Add table level methods
1.                    Override the Method in the Main table– canSubmitToWorkflow
2.                    Create a new method (Not overridden method) in the main table as below.

Below is the code:

Override the Method in the Main table– canSubmitToWorkflow

/// <returns></returns>
    public boolean canSubmitToWorkflow(str _workflowType = '')
    {
        boolean ret;
        SMJ_PurchaseOrder    SMJ_PurchaseOrder;
        select firstOnly SMJ_PurchaseOrder where SMJ_PurchaseOrder.PurchaseID ==          this.PurchaseID;
        ret = this.RecId != 0 && this.WorkflowStatus == SMJ_Workflow::NotSubmitted && SMJ_PurchaseOrder;
        //ret = super(_workflowType);
        return ret;
    }

Create a new method (Not overridden method) in the main table as below:

    public static void UpdateWorkflowState(RecId _recId, SMJ_Workflow _state)
    {
        SMJ_PurchaseOrder    SMJ_PurchaseOrder = SMJ_PurchaseOrder::findByRecID(_recId,       true,ConcurrencyModel::Auto);
        if(SMJ_PurchaseOrder)
        {
            ttsBegin;
            SMJ_PurchaseOrder.WorkflowStatus = _state;
            SMJ_PurchaseOrder.update();
            ttsCommit;
        }
     }

Step 4:

   Create query for the SMJ_PURCHASEORDER table, below are the screen shots


Step 5:

     Create workflow category











Set the following properties for the “Work flow category”
  • Label – Give the label.
  • Module – Select the module (in which module this workflow category belongs).
Step 6:

     Create new workflow Type.




In the wizard give the names like below
Name – (Name of the workflow type)
Category –  ( the workflow category name which created before).
Query – (Query name of this wf)
Document Menu item – (Menu item of the form where this wf is existing)

After we finish the wizard, it creates a project with objects.
Then following 3 class and 2 menu items automatically created.






Step 7:

        Create a new workflow approval



Name – (Name of the wf approval)
Workflow document –(Name of the wf type which created above and ends with Document).
Document preview field group – filed group of the main table.(optional)
Document Menu item – (Menu item of the forms where this wf is existing)

After we finish the wizard, it creates a project with objects.
Then following class and menu item automatically created.



Step 8:

       Go to workflow type  --->  Add new workflow supported element reference 




Step 9:

     In the form design of ListPage form and Details page form set the properties


example code 


SubmitManager Class:

/// <summary>
/// The JO_PurchWFTypeSubmitManager menu item action event handler.
/// </summary>
public class JO_PurchWFTypeSubmitManager
{


    SMJ_PurchaseOrder                       PurchaseOrder;//            IPMInvestmentMemorandumTable;
    boolean                                 submit;
    MenuItemName                            menuItemName;
    // EPWorkflowControlContext                workflowControlContext;
    WorkflowWorkItemTable                   workflowWorkItemTable;
    WorkflowComment                         workflowComment;
    WorkflowTypeName                        workflowTemplateName;
    WorkflowVersionTable                    workflowConfigurationTable;
    UserId                                  userId;
    Object                                  SMJ_PurchaseOrder_ds;
    #define.workflow('Purchase workflow')
    public boolean dialogOk()
    {
        WorkflowSubmitDialog         workflowSubmitDialog;
        WorkflowWorkItemActionDialog workflowWorkItemActionDialog;
   
        boolean ok;
        ;
        if (menuItemName == menuitemactionstr(JO_PurchWFTypeSubmitMenuItem))
        {
            workflowSubmitDialog = WorkflowSubmitDialog::construct(this.parmWorkflowConfigurationTable());
            workflowSubmitDialog.run();
            this.parmWorkflowComment(workflowSubmitDialog.parmWorkflowComment());
            ok = workflowSubmitDialog.parmIsClosedOK();
        }
        else if (menuItemName == menuitemactionstr(JO_PurchWFApprovalResubmitMenuItem))
        {
            workflowWorkItemActionDialog = WorkflowWorkItemActionDialog::construct( workflowWorkItemTable,
            WorkflowWorkItemActionType::Resubmit,
            new MenuFunction(menuitemactionstr(JO_PurchWFApprovalResubmitMenuItem), MenuItemType::Action));
            workflowWorkItemActionDialog.run();
            this.parmWorkflowComment(workflowWorkItemActionDialog.parmWorkflowComment());
            ok = workflowWorkItemActionDialog.parmIsClosedOK();
            userId = workflowWorkItemActionDialog.parmTargetUser();
        }
        return ok;
    }

    public void init( Common _documentRecord,
                        MenuItemName _menuItemName,
                        WorkflowVersionTable _workflowConfigurationTable,
                        WorkflowWorkItemTable _workflowWorkItemTable)
    {
        this.parmSMJ_PurchaseOrderTable(_documentRecord);
        this.parmSubmit(_menuItemName == menuitemactionstr (JO_PurchWFTypeSubmitMenuItem));
        this.parmMenuItemName(_menuItemName);
        this.parmWorkflowConfigurationTable(_workflowConfigurationTable);
        this.parmWorkflowWorkItemtable(_workflowWorkItemTable);
        this.parmWorkflowTemplateName (this.parmWorkflowConfigurationTable().WorkflowTable().TemplateName);
    }

    public SMJ_PurchaseOrder parmSMJ_PurchaseOrderTable(SMJ_PurchaseOrder _SMJ_PurchaseOrder = PurchaseOrder)
    {
        ;
        PurchaseOrder = _SMJ_PurchaseOrder;
        return PurchaseOrder;
    }

    public MenuItemName parmMenuItemName(MenuItemName _menuItemName = menuItemName)
    {
        ;
        menuItemName = _menuItemName;
        return menuItemName;
    }

    public boolean parmSubmit(boolean _submit = submit)
    {
        ;
        submit = _submit;
        return submit;
    }

    public WorkflowComment parmWorkflowComment(WorkflowComment _workflowComment = workflowComment)
    {
        ;
        workflowComment = _workflowComment;
        return workflowComment;
    }

    public WorkflowVersionTable parmWorkflowConfigurationTable(WorkflowVersionTable _workflowConfigurationTable = workflowConfigurationTable)
    {
        ;
        workflowConfigurationTable = _workflowConfigurationTable;
        return workflowConfigurationTable;
    }

    public WorkflowTypeName parmWorkflowTemplateName(WorkflowTypeName _workflowTemplateName = workflowTemplateName)
    {
        ;
        workflowTemplateName = _workflowTemplateName;
        return workflowTemplateName;
    }

    public WorkflowWorkItemTable parmWorkflowWorkItemtable(WorkflowWorkItemTable _workflowWorkItemTable = workflowWorkItemTable)
    {
        ;
        workflowWorkItemTable = _workflowWorkItemTable;
        return workflowWorkItemTable;
    }

    private void reSubmit()
    {
   
        NoYes                   reSubmittingFromWeb;
   
        // If we have a workflow control context, we are being resubmitted from EP
        //reSubmittingFromWeb = this.parmWorkflowControlContext() == null ? NoYes::No : NoYes::Yes;
        ttsbegin;
   
        WorkflowWorkItemActionManager::dispatchWorkItemAction( workflowWorkItemTable, workflowComment, userId, WorkflowWorkItemActionType::Resubmit, menuItemName);
        SMJ_PurchaseOrder_ds = PurchaseOrder.datasource();
        PurchaseOrder = SMJ_PurchaseOrder::findByRecID(PurchaseOrder.RecId, true, ConcurrencyModel::Auto);
   
        PurchaseOrder.WorkflowStatus = SMJ_Workflow::Submitted;
        PurchaseOrder.doUpdate();
        if (SMJ_PurchaseOrder_ds)
        {
            SMJ_PurchaseOrder_ds.write();
            SMJ_PurchaseOrder_ds.refresh();
        }
        ttscommit;
    }

    private void submit()
    {
   
        NoYes  activatingFromWeb;
        ;
        // If we have a workflow control context, we are being activated from EP
        //   activatingFromWeb = this.parmWorkflowControlContext() == null ? NoYes::No : NoYes::Yes;
   
        ttsBegin;
        Workflow::activateFromWorkflowType( this.parmWorkflowTemplateName(),PurchaseOrder.RecId, this.parmWorkflowComment(),activatingFromWeb, curuserid());
        SMJ_PurchaseOrder_ds = PurchaseOrder.dataSource();
        PurchaseOrder = SMJ_PurchaseOrder::findByRecID(PurchaseOrder.RecId, true);
        PurchaseOrder.SubmitedBy = curuserid();
        PurchaseOrder.SubmittedDateTime = utcDateTime2SystemDateTime(DateTimeUtil::utcNow());
        PurchaseOrder.WorkflowStatus = SMJ_Workflow::Submitted;
        PurchaseOrder.doUpdate();
        if (SMJ_PurchaseOrder_ds)
        {
            SMJ_PurchaseOrder_ds.write();
            SMJ_PurchaseOrder_ds.reread();
            SMJ_PurchaseOrder_ds.refresh();
        }
        ttsCommit;
    }

    public static JO_PurchWFTypeSubmitManager construct()
    {
        return new JO_PurchWFTypeSubmitManager();
    }

    public static void main(Args args)
    {
        JO_PurchWFTypeSubmitManager JO_PurchWFTypeSubmitManager;
        SMJ_PurchaseOrder                PurchaseOrder;
        // Variable declaration.
        Object                          callerDataSource;
        // Hardcoded workflow type name
        //workflowTypeName workflowTypeName = workflowtypestr(SMJ_Workflow);
   
        // Initial note is the information that users enter when they
        // submit the document for workflow.
        WorkflowComment initialNote = "Enter any comments";
        ;
        PurchaseOrder = args.record();
        JO_PurchWFTypeSubmitManager =  JO_PurchWFTypeSubmitManager::construct();
        if (args.menuItemName() == menuitemactionstr(JO_PurchWFTypeSubmitMenuItem) ||
            args.menuItemName() == menuitemactionstr(JO_PurchWFApprovalResubmitMenuItem))
        {
            JO_PurchWFTypeSubmitManager.init(args.record(), args.menuItemName(), args.caller().getActiveWorkflowConfiguration(), args.caller().getActiveWorkflowWorkItem());
        }
        else
        {
            JO_PurchWFTypeSubmitManager.init(args.record(), args.menuItemName(), null, null);
        }
   
        if (JO_PurchWFTypeSubmitManager.dialogOk())
        {
            if (JO_PurchWFTypeSubmitManager.parmSubmit())
            {
                JO_PurchWFTypeSubmitManager.submit();
            }
            else
            {
                JO_PurchWFTypeSubmitManager.reSubmit();
            }
   
            if (args.menuItemName() == menuitemactionstr(JO_PurchWFTypeSubmitMenuItem) ||
                    args.menuItemName() == menuitemactionstr(JO_PurchWFApprovalResubmitMenuItem))
                        args.caller().updateWorkflowControls();
        }
    }
 //   public static void main(Args args)
 //{
 //    //  TODO:  Write code to execute once a work item is submitted.
 //}

}

EventHandler for workflow type:
/// <summary>
/// The JO_PurchWFTypeEventHandler workflow event handler.
/// </summary>
public class  JO_PurchWFTypeEventHandler implements WorkflowCanceledEventHandler
       WorkflowCompletedEventHandler,
       WorkflowStartedEventHandler
{
    public void started(WorkflowEventArgs _workflowEventArgs)
       {
        SMJ_PurchaseOrder::UpdateWorkflowState(_workflowEventArgs.parmWorkflowContext().parmRecId(),   SMJ_Workflow::Submitted);
       }

    public void canceled(WorkflowEventArgs _workflowEventArgs)
       {
              // TODO:  Write code to execute once the workflow is canceled.
       }

    public void completed(WorkflowEventArgs _workflowEventArgs)
       {
        SMJ_PurchaseOrder::UpdateWorkflowState(_workflowEventArgs.parmWorkflowContext().parmRecId(),   SMJ_Workflow::Approved);
       }

}

EventHandler class for workflow approval:
/// <summary>
/// The JO_PurchWFApprovalEventHandler workflow outcome event handler.
/// </summary>
public final class JO_PurchWFApprovalEventHandler implements WorkflowElementCanceledEventHandler,
       WorkflowElemChangeRequestedEventHandler,
       WorkflowElementCompletedEventHandler,
       WorkflowElementReturnedEventHandler,
       WorkflowElementStartedEventHandler,
       WorkflowElementDeniedEventHandler,
       WorkflowWorkItemsCreatedEventHandler
{
    public void started(WorkflowElementEventArgs _workflowElementEventArgs)
       {
        SMJ_PurchaseOrder::UpdateWorkflowState(_workflowElementEventArgs.parmWorkflowContext().parmRecId(),SMJ_Workflow::Submitted);
       }

    public void canceled(WorkflowElementEventArgs _workflowElementEventArgs)
       {
              // TODO:  Write code to execute once the workflow is canceled.
       }

    public void completed(WorkflowElementEventArgs _workflowElementEventArgs)
       {
        SMJ_PurchaseOrder::UpdateWorkflowState(_workflowElementEventArgs.parmWorkflowContext().parmRecId(),SMJ_Workflow::Approved);
       }

    public void denied(WorkflowElementEventArgs _workflowElementEventArgs)
       {
              // TODO:  Write code to execute once the workflow is denied.
       }

    public void changeRequested(WorkflowElementEventArgs _workflowElementEventArgs)
       {
        SMJ_PurchaseOrder::UpdateWorkflowState(_workflowElementEventArgs.parmWorkflowContext().parmRecId(),SMJ_Workflow::ChangeRequest);
       }

    public void returned(WorkflowElementEventArgs _workflowElementEventArgs)
       {
              // TODO:  Write code to execute once the workflow is returned.
       }

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

}


Final output:
         
Before submit workflow 



 After submit workflow

 

Before Approve workflow


After Approve workflow








Comments

Popular posts from this blog

SSRS Report using Controller , Contract and RDP classes in D365

Exporting data to Excel through X++ code

COC for Form level method

How to pass the parameter from one form to another in Dynamic365

Multi Select Lookup in SSRS Report in D365