Skip to main content

Workflow in Ax 2012


In this blog we will discuss custom workflow in ax 2012. Follow below mentioned steps: 

1.Create a workflow status Enum to have the following elements:

customer-approval-workflow

2. Add field of type workflow status enum to the CustTable table.
3. Override canSubmitToWorkflow() method of CustTable table to define workflow submission criteria.

public boolean canSubmitToWorkflow(str _workflowType = '')
{
    boolean canSubmit = false;

    if (custTable.RecId &&
	custTable.MzkWorkflowApprovalStatus == MzkCustWFApprovalStatus::NotSubmitted)
    {
        canSubmit = true;
    }

    return canSubmit;
}

4. Add a static method on CustTable table to update workflow status. This method will be called from workflow event handlers and workflow approval event handlers.

static void mzkUpdateWorkflowStatus(RefRecId _recId, MzkCustWorkflowStatus _status)
{
    CustTable custTable;

    custTable = CustTable::findRecId(_recId, true);

    ttsBegin;
    custTable.MzkWorkflowApprovalStatus = _status;
    custTable.update();
    ttsCommit;
}

5. Create a Query for the CustTable table.

customer-approval-workflow

6. Create a Workflow Category.
7. Create a Workflow Type using wizard.

customer-approval-workflow

Where,

  • Category – Name of the workflow category.
  • Query – Name of the query.
  • Document menu item – Name of display menu item for the document form.

The following artifacts will be created:

  • Workflow type
  • Classes
    • Document class which extends WorkflowDocument.
    • EventHandler class which gives implementation to handle different workflow events.
    • SubmitManager class.
  • Action menu items:
    • SubmitMenuItem pointing to SubmitManager class.
    • CancelMenuItem pointing to WorkflowCancelManager class.

8. Enable Workflow on CustTable and CustTableListPage form by setting Design node properties as follows:

  • WorkflowEnabled – Yes.
  • WorkflowDatasource – Name of the form datasource, CustTable.
  • WorkflowType – Name of the custom workflow type created.

9. Give submit logic in SubmitManager class.

public static void main(Args _args)
{
    MzkCustWorkflowTypeSubmitManager submitManager;

    submitManager = new MzkCustWorkflowTypeSubmitManager();
    submitManager.submit(_args);
}
public void submit(Args _args)
{
    RecId                 _recId;
    WorkflowCorrelationId _workflowCorrelationId;
    workflowTypeName      _workflowTypeName;
    WorkflowComment       _initialNote;
    WorkflowSubmitDialog  workflowSubmitDialog;

    _recId = _args.record().RecId;
    _workflowTypeName = workFlowTypeStr("MzkCustWorkflowType");
    _initialNote = "";

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

    workflowSubmitDialog.run();

    if (workflowSubmitDialog.parmIsClosedOK())
    {
        _recId = _args.record().RecId;
        // Get comments from the submit to workflow dialog.
        _initialNote = workflowSubmitDialog.parmWorkflowComment();

        try
        {
            ttsbegin;

            _workflowCorrelationId = Workflow::activateFromWorkflowType(
		_workflowTypeName, _recId, _initialNote, NoYes::No);

            ttscommit;

            // Updates the workflow button to diplay Actions instead of Submit.
            _args.caller().updateWorkflowControls();

            info("Submitted to workflow.");
        }
        catch(exception::Error)
        {
            error("Error on workflow activation.");
        }
    }
}

10. Create a Workflow Approval element using the wizard.

customer-approval-workflow

11. Drag the newly created approval to the Supported elements node of the custom workflow type.
12. Define workflow type event handlers in workflow type event handler class.

class MzkCustWorkflowTypeEventHandler implements
    WorkflowCanceledEventHandler,
    WorkflowCompletedEventHandler,
    WorkflowStartedEventHandler
{
}
public void started(WorkflowEventArgs _workflowEventArgs)
{
    CustTable::mzkUpdateWorkflowStatus(
	_workflowEventArgs.parmWorkflowContext().parmRecId(),
	MzkCustWFApprovalStatus::Submitted);
}
public void completed(WorkflowEventArgs _workflowEventArgs)
{
    CustTable::mzkUpdateWorkflowStatus(
	_workflowEventArgs.parmWorkflowContext().parmRecId(),
	MzkCustWFApprovalStatus::Completed);
}
public void canceled(WorkflowEventArgs _workflowEventArgs)
{
    CustTable::mzkUpdateWorkflowStatus(
	_workflowEventArgs.parmWorkflowContext().parmRecId(),
	MzkCustWFApprovalStatus::Canceled);
}

13. Define approval element event handlers in workflow approval element event handler class.

class MzkCustWFApprovalEventHandler implements
    WorkflowElementCanceledEventHandler,
    WorkflowElemChangeRequestedEventHandler,
    WorkflowElementCompletedEventHandler,
    WorkflowElementReturnedEventHandler,
    WorkflowElementStartedEventHandler,
    WorkflowElementDeniedEventHandler,
    WorkflowWorkItemsCreatedEventHandler
{
}
public void started(WorkflowElementEventArgs _workflowElementEventArgs)
{
    CustTable::mzkUpdateWorkflowStatus(
	_workflowElementEventArgs.parmWorkflowContext().parmRecId(),
	MzkCustWFApprovalStatus::Submitted);
}
public void changeRequested(WorkflowElementEventArgs _workflowElementEventArgs)
{
    CustTable::mzkUpdateWorkflowStatus(
	_workflowElementEventArgs.parmWorkflowContext().parmRecId(),
	MzkCustWFApprovalStatus::ChangeRequested);
}
public void canceled(WorkflowElementEventArgs _workflowElementEventArgs)
{
    CustTable::mzkUpdateWorkflowStatus(
	_workflowElementEventArgs.parmWorkflowContext().parmRecId(),
	MzkCustWFApprovalStatus::PendingCancelation);
}
public void completed(WorkflowElementEventArgs _workflowElementEventArgs)
{
    CustTable::mzkUpdateWorkflowStatus(
	_workflowElementEventArgs.parmWorkflowContext().parmRecId(),
	MzkCustWFApprovalStatus::Completed);
}
public void returned(WorkflowElementEventArgs _workflowElementEventArgs)
{
    CustTable::mzkUpdateWorkflowStatus(
	_workflowElementEventArgs.parmWorkflowContext().parmRecId(),
	MzkCustWFApprovalStatus::Returned);
}
public void created(WorkflowWorkItemsEventArgs _workflowWorkItemsEventArgs)
{
    CustTable::mzkUpdateWorkflowStatus(
        _workflowWorkItemsEventArgs.parmWorkflowElementEventArgs().parmWorkflowContext().parmRecId(),
        MzkCustWFApprovalStatus::Created);
}

14. Define the resubmit action manager class.

public class MzkCustWFApprovalResubmitActionMgr
{
}
public static void main(Args _args)
{
    RecID                        recID;
    TableId                      tableId;
    CustTable                    custTable;
    WorkflowWorkItemTable        workItem;
    WorkflowWorkItemActionDialog workflowWorkItemActionDialog;
    
    recID = _args.record().RecId;
    tableId = _args.record().TableId;
    custTable = _args.record();
    workItem = _args.caller().getActiveWorkflowWorkItem();

    if (workItem.RecId > 0)
    {
        try
        {
            workflowWorkItemActionDialog = WorkflowWorkItemActionDialog::construct(
                workItem,
                WorkflowWorkItemActionType::Resubmit,
                new MenuFunction(_args.menuItemName(),_args.menuItemType()));

            workflowWorkItemActionDialog.run();

            if (workflowWorkItemActionDialog.parmIsClosedOK())
            {
                if (custTable.MzkWorkflowApprovalStatus ==
			MzkCustWFApprovalStatus::ChangeRequested)
                {
                    workItem = _args.caller().getActiveWorkflowWorkItem();
                    WorkflowWorkItemActionManager::dispatchWorkItemAction(workItem,
                        workflowWorkItemActionDialog.parmWorkflowComment(),
                        workflowWorkItemActionDialog.parmTargetUser(),
                        WorkflowWorkItemActionType::Resubmit,
                        _args.menuItemName(),
                        false);

                    custTable.MzkWorkflowApprovalStatus
			= MzkCustWFApprovalStatus::Submitted;

                    ttsbegin;
                    custTable.dataSource().write();
                    ttscommit;
                }
                else
                {
                    throw Exception::Error;
                }
            }
        }
        catch(Exception::Error)
        {
            throw error(strfmt("Cannot resubmit workflow."));
        }
    }

    _args.caller().updateWorkflowControls();
}

15. Design the Workflow.

  • Navigate to Accounts receivable > Setup > Accounts receivable workflows.
  • Create a new workflow instance of the workflow type you created.
  • Define the states from Start to End of the workflow.
    • Drag approval element from Toolbox on the left to the Designer pane on the right.
    • Connect the bottom of Start state with top of the Approval element.
    • Connect the bottom of Approval element to the top of End state.
  • Resolve any errors and warnings by setting workflow and approval element properties.
  • Activate it.

customer-approval-workflow

16. Test the workflow. You should be able to see the workflow bar.

customer-approval-workflow








HEY HEY HEY!!!! HACK OF THE DAY!!
Always remember to check the WORKFLOW CATEGORY module property should be same as the workflow form Enum Parameter value.

Comments

Popular posts from this blog

Edit Method on Form

Edit Method D365 for a form Data Source 1. To create an edit method first create a controller class. with following properties  public static edit MainAccountNum LedgerJournalTransLedger(LedgerJournalTrans _ledgerjournal, boolean _set, MainAccountNum _id) { MainAccountNum accountId = _id; MainAccount mainAccount = MainAccount::findByMainAccountId(_id); if(_set) { if(_ledgerjournal.AccountType== LedgerJournalACType::Ledger) { mainAccount = MainAccount::findByMainAccountId(accountId); if(_ledgerjournal.LedgerDimension) { DimensionDefault defaultDim = LedgerDimensionFacade::getDefaultDimensionFromLedgerDimension(_ledgerjournal.LedgerDimension); _ledgerjournal.LedgerDimension = LedgerDimensionDefaultingEngine::getLedgerDimensionFromAccountAndDim(mainAccount.RecId, DimensionHierarchy::getAccountStructure(mainAccount.RecId), defaultDim); } else { _ledgerjournal.LedgerDimension = LedgerDimensionDefaultingEngine::getLedgerDimensionFromAccountAndDim(mainAccount.RecId, DimensionHierarchy::getAcc

Security Objects In D365

   PRIVILEGES, DUTIES AND ROLES IN D365 FinOps To add customize security privilege, duty and role you should follow this flow because it is considered as the best practices  Role---> Duty---->Privilege Duty and Privilege would be created at the back end and where as role would be created at front end  1. create privilege from solution explorer in a project  and add new entry point for output, display or action menus to refer in privilege that for which entity we have to give privilege to the user 3. Now Create a duty from solution explorer same as privilege and add this new created privilege to the duty  Now you can refer this duty to the role created on the front end.   HEY HEY HEY !!!! HACK OF THE DAY !!           THE HIGHEST ACCESS LEVEL  FOR ACTION MENU ITEM IS   DELETE     

Deep Links

  DEEP LINKS FOR SALES ORDERS In this blog we will discuss about generating deep links for any form , record or datasource. Deep links are basically termed for generating URL's through code for any specific record in D365. Using Deep links other environments can access D365 records by using this URL generated from it. 1. In below blog I am creating Deep links for sales order header record. 2. To Access these links one should always be added the user in FinOps to access through URL. Step #1 Create and extension class of  URLUtility class  and also add following code snippet to access this class : using Microsoft.Dynamics.AX.Framework.Utilities; using Microsoft.Dynamics.@Client.ServerForm.Contexts; public static str generateRecordUrl(str _menuItemName, MenuItemType _menuItemType, DataSourceName _dataSourceName, Map _indexFieldValuesMap, DataAreaId _dataAreaId = curExt()) {   System.Uri host = SessionContext::Get_Current().Get_RequestUrl();   UrlHelper.UrlGenerator generator = new Url