Friday, August 3, 2018

New Custom workflow in Dynamics 365 for finance and operation


Custom workflow creation in Dynamics 365 for finance and operations
Here example, Creating custom workflow for custom document
Steps:


Workflow creation for existing document in application without Overlaying on canSubmitToWorkflow method 

Suggested workaround solution:  (This can be achieve without overlay)
                  1. Create new table with unique field from custTable and WorkflowStatus Field alone.
                  2. Add newly created table in to form dataSource and link with primary custTable dataSource in FORM: CustTable using extension. 
 Note: Using outer join for join Type, Inner join type works only when there is no record in primary table.
                  3. Override canSubmitToWorkflow method in newly table created above.
                  4. Create new method for FormDataSourceEvent : OnWritten for Primary dataSource (custTable) and create new values for newly created table when record created in primary table.
                  5. UpdateWorfklowStatus method should update status both child Table (New table) and CustTable
                  6. Write method on Datasource On-initialized event method of New table datasource in existing form

Steps:
  •         Create new BASE ENUM (Named: Dev_WorkflowStatus)
  •         Create Extension class for Table (“CustTable”)
  •          Add newly created ENUM in to Table (“CustTable”)
  •         Add new method on CustTable
 Note: CanSubmitToWorkflow here added as overlay method.
It is not advisable from Microsoft. 
Currently canSubmitToWorkflow method through extension is not working as expected in PU12

Note:
-> PU22 (December 2018) - MS Enabled Chain of Command to target method overrides that have not been implemented to tables (now we can use it for canSubmitToWorkflow method).


o   CanSubmitToWorkflow
Code:
public boolean canSubmitToWorkflow(str _workflowType = '')
              {
                     boolean ret = false; 
                     
                     ret = this.DEV_WorkflowStatus == DEV_WorkflowStatus::Draft;
                         
                     return ret;
}
o   UpdateWorkflowStatus
Code:
public static void updateWorkflowStatus(RecId _documentRecId, DEV_WorkflowStatus _status)
{
       CustTable record;
ttsbegin;
update_recordset record setting DEV_WorkflowStatus = _status
where record.RecId == _documentRecId;          
ttscommit;
}
  • ·         Create new query for workflow with data source as CustTable (Named: Dev_CustTableWf_Query)


  • ·         Create new workflow category and mention name & label property


  • ·         Create new workflowType and mention property of DocumentMenuItem, Query, WorklfowCategory, then system create all default objects for workflow type


  • ·         Add pre-requisite method in Submit Manager class

Code:
/// <summary>
/// The Dev_Customer_WfTypeSubmitManager menu item action event handler.
/// </summary>
public class Dev_Customer_WfTypeSubmitManager
{
    public static void main(Args _args)
    {
        // Executes when a work item is submitted.
        RecId                   recId = _args.record().RecId;
        WorkflowSubmitDialog    workflowSubmitDialog;

        // Opens the submit to workflow dialog.
        workflowSubmitDialog = WorkflowSubmitDialog::construct(_args.caller().getActiveWorkflowConfiguration());
        workflowSubmitDialog.run();

        if (workflowSubmitDialog.parmIsClosedOK())
        {
            try
            {
                ttsbegin;
                // Activate the workflow.
                Workflow::activateFromWorkflowType(workFlowTypeStr(<WorkflowType>), recId, workflowSubmitDialog.parmWorkflowComment(), NoYes::No);

                //TODO write logic for ur Workflow document table status field update
                ttscommit;
            }
            catch(exception::Error)
            {
                info('Workflow activation failed');
            }

            // Refresh the caller form.
            FormDataSource callerDS = FormDataUtil::getFormDataSource(_args.record());
            callerDS.reread();
            callerDS.refresh();
            _args.caller().updateWorkflowControls();
        }
    }

}

  • Add pre-requisite method in ReSubmit Manager class

Code:
/// <summary>
/// The Dev_Customer_WfTypeReSubmitManager menu item action event handler.
/// </summary>
public class Dev_Customer_WfTypeReSubmitManager
{
public static void main(Args _args)
    {
        // Executes when a work item is resubmitted.
        RecID                   recID       = _args.record().RecId;
        WorkflowWorkItemTable   workItem    = _args.caller().getActiveWorkflowWorkItem();
       
        if (workItem)
        {
            try
            {
                // Opens the resubmit to workflow dialog.
                WorkflowWorkItemActionDialog dialog = WorkflowWorkItemActionDialog::construct(
                    workItem,
                    WorkflowWorkItemActionType::Resubmit,
                    new MenuFunction(_args.menuItemName(),_args.menuItemType()));
                dialog.run();
                if (dialog.parmIsClosedOK())
                {
                    ttsbegin;
                    workItem = _args.caller().getActiveWorkflowWorkItem();
                    WorkflowWorkItemActionManager::dispatchWorkItemAction(
                        workItem,
                        dialog.parmWorkflowComment(),
                        dialog.parmTargetUser(),
                        WorkflowWorkItemActionType::Resubmit,
                        _args.menuItemName());
                   
                   //TODO write logic for ur workflow document class status update logic
                    ttscommit;
                }
            }
            catch(exception::Error)
            {
                info('Workflow resubmission failed');
            }
        }
        // Refresh the caller form.
        FormDataSource callerDS = FormDataUtil::getFormDataSource(_args.record());
        callerDS.reread();
        callerDS.refresh();
        _args.caller().updateWorkflowControls();
    }
}

  • ·         Go to event handler created from workflow Type and follow below code

public class  Dev_Customer_WfTypeEventHandler implements WorkflowCanceledEventHandler,  WorkflowCompletedEventHandler, WorkflowStartedEventHandler
{
    public void started(WorkflowEventArgs _workflowEventArgs)
    {
        RecId documentRecId = _workflowEventArgs.parmWorkflowContext().parmRecId();
        CustTable::updateWorkflowStatus(documentRecId, DEV_WorkflowStatus::Submitted);
    }

    public void canceled(WorkflowEventArgs _workflowEventArgs)
    {
        RecId documentRecId = _workflowEventArgs.parmWorkflowContext().parmRecId();
        CustTable::updateWorkflowStatus(documentRecId, DEV_WorkflowStatus::Rejected);
    }

    public void completed(WorkflowEventArgs _workflowEventArgs)
    {
        RecId documentRecId = _workflowEventArgs.parmWorkflowContext().parmRecId();
        CustTable::updateWorkflowStatus(documentRecId, DEV_WorkflowStatus::Approved);
    }

}
  • ·         Create new workflow approval




  • ·         Add logic on workflow approval event handler class

For demo purpose, we have added code only for cancelled, completed, Change Request Methods
Code:
/// <summary>
/// The Dev_Customer_WfAppEventHandler workflow outcome event handler.
/// </summary>
public final class Dev_Customer_WfAppEventHandler implements WorkflowElementCanceledEventHandler,
            WorkflowElemChangeRequestedEventHandler,
            WorkflowElementCompletedEventHandler,
            WorkflowElementReturnedEventHandler,
            WorkflowElementStartedEventHandler,
            WorkflowElementDeniedEventHandler,
            WorkflowWorkItemsCreatedEventHandler
{
    public void canceled(WorkflowElementEventArgs _workflowElementEventArgs)
    {
        RecId documentRecId = _workflowElementEventArgs.parmWorkflowContext().parmRecId();
        CustTable::updateWorkflowStatus(documentRecId, DEV_WorkflowStatus::Rejected);
    }

    public void completed(WorkflowElementEventArgs _workflowElementEventArgs)
    {
        RecId documentRecId = _workflowElementEventArgs.parmWorkflowContext().parmRecId();
        CustTable::updateWorkflowStatus(documentRecId, DEV_WorkflowStatus::Approved);
    }

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

    public void changeRequested(WorkflowElementEventArgs _workflowElementEventArgs)
            {
                        RecId documentRecId = _workflowElementEventArgs.parmWorkflowContext().parmRecId();
        CustTable::updateWorkflowStatus(documentRecId, DEV_WorkflowStatus::RequestChange);
            }
}
  • ·         Add newly created workflow approvals to Workflow type created for this custom document workflow


Note: Like this we can add automated Task and mapping line item workflow as per your requirement.
·         Assign workflowType, workflowEnable and WorkflowDatasource on Document FORM
Note:
If properties enabled in FORM means, Assign there itself
Default Application Suite is Hard locked, no more overlay and currently system is not allowing to edit form design property in Form:CustTable through extensibility also.
Or else written code on Form Document datasource OnInitialized Method as follow
class Dev_Customer_Wf_EventHandler
{   
    /// <summary>
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    [FormDataSourceEventHandler(formDataSourceStr(CustTable, CustTable), FormDataSourceEventType::Initialized)]
    public static void CustTable_OnInitialized(FormDataSource sender, FormDataSourceEventArgs e)
    {         
        sender.formRun().design().workflowDatasource("CustTable");
        sender.formRun().design().workflowEnabled(true);
        sender.formRun().design().workflowType("Dev_Customer_WfType");
    }
}
  • ·         Navigate to document, Verify new workflow for customer document.
  • ·         Added new field in to document form
  • ·         Project Elements (New Workflow)


  • Navigate to workflow configuration on Module/Setup/Workflows and create new by selecting new workflow type

  • Create new customer to verify workflow
  • Rebuild solution

No comments:

Post a Comment

Convert Call stack to readable format in D365FO X++

//Input --container _xppCallStack = xSession::xppCallStack();  Public static str POL_formatXppCallStack(container _xppCallStack, int _skipFr...