lunes, 17 de marzo de 2014

Activating (Sending/Submitting) Workflows

Gosh, workflows are hard.  There is a separate service that manages the framework which is a good thing for the users as it's more robust, however makes the developers life a little more complicated.  This can also mean that the changes we send to the system are not immediately visible on the AX forms until the process has been executed by the batch job called “Workflow Message processing”.

All that we would like to do is to submit to the workflow, as if approving a project quotation.

Running the code below works...  But the changes may not be immediately apparent:
static void ZZZ_WorkflowApprove(Args _args)
{
    // Variable declaration.
    recId                   _recId = SalesQuotationTable::find(@'P120139-2').RecId;
    WorkflowTypeName        _workflowTypeName = workflowtypestr("PSAProjQuotationTemplate");
    WorkflowComment         _initialNote = strFmt("Accepted");
    WorkflowCorrelationId   _workflowCorrelationId;
    
    // Activate the workflow from a template.
    _workflowCorrelationId = Workflow::activateFromWorkflowType(
                                _workflowTypeName, _recId, _initialNote, NoYes::No);
}
We can find the name of our workflow in the WorkflowTable entity, looking up the DocumentTableName field value, and obtaining the corresponding Name...  Excuse my ignorance with Workflows but I couldn't find an AX form to list them all.

To Approve/Reject a work item that is submitted to the workflow try the following snippet, for example approving a project budget:
    ProjId                  _projId = '120152';
    ProjBudget              _projBudget = ProjBudget::findOrCreateProjectBudget(_projId);
    
    while select workflowWorkItemTable where
        (workflowWorkItemTable.Type == WorkflowWorkItemType::WorkItem) &&
        (workflowWorkItemTable.Status == WorkflowWorkItemStatus::Pending) &&
        //workflowWorkItemTable.DueDateTime < DateTimeUtil::getSystemDateTime() &&
        workflowWorkItemTable.RefRecId == _projBudget.RecId &&
        workflowWorkItemTable.RefTableId == _projBudget.TableId
    {
        WorkflowWorkItemActionManager::dispatchWorkItemAction(
                            workflowWorkItemTable, 
                            "@SYS325206",//Approved budget 
                            curUserId(), 
                            WorkflowWorkItemActionType::Complete, 
                            "ProjBudgetOrigWorkflowApprove",
                            false);
    }


As indicated above, I'm ignorant as to workflows and had to spend a while investigating how to 'launch' the process behind them.  A quick look for AX 2009 and AX 2012 appears to be the same, yet for me still I still couldn't control properly the parallelism of handling the two separate processes.  Use the following at your own risk (a progress bar would be nice):
private void waitWorkFlow(Common _common)
{
    SysWorkflowMessageQueueManager  queueManager;
    WorkflowWorkItemDueDateJob      workItemDueDateJob;
    
    int                             seconds     = 0;
    int                             sleepTime   = (1 * 1000); //Milliseconds
    int                             maxWaitTime = 60;

    // "Microsoft internal use only."
    queueManager = SysWorkflowMessageQueueManager::construct();
    queueManager.run();

    workItemDueDateJob = new WorkflowWorkItemDueDateJob();
    workItemDueDateJob.run();
    
    // Check for 'Completed' status, otherwise wait...
    while (Workflow::findTrackingStatusForDocument(_common)
                .TrackingStatus != WorkflowTrackingStatus::Completed
            && seconds < maxWaitTime)
    {
        sleep(sleepTime);
        seconds += 1;
    }
}

Finally a few useful workflow related links: