Mass Reassign Workflows

Challenge/Problem: Many Dynamics AX Workflow work-items that need to be reassigned at once.

Description: At times we have had the request from clients to reassign many workitems from one person to another. My first reaction is why weren’t delegation parameters setup on the user so that one doesn’t need to manually reassign. However I have come to realise over time that there are a couple of good reasons to do so such as a person falling ill suddenly, a mistake in the workflow config, failure to set delegation parameters in time etc…

Solution: The following script/job designed for AX workflow will reassign workitems from one user to another. You can modify your query a bit to restrict the items to just the one that you want. The attached one simply reassigns all items currently assigned to user “123456” to user “654321”.

NOTE: This will not affect items in your workitem list as a result of queues

static void workflowMassReassign(Args _args)
    UserId fromUser = "123456"
    UserId toUser = "654321";
    //Comment for workflow history
    str comment = "Auto-Reassign 2014/11/26 08:00";
    WorkflowWorkItemTable workitems;
    int i;
    while select workitems where workitems.Status == WorkflowWorkItemStatus::Pending && workitems.UserId == fromUser 
        WorkflowWorkItem::delegateWorkItem(workitems.Id, toUser, comment);
    info(strFmt("Items re-assigned: %1",i));

Datasource Save, method execution order

Screen Shot 2014-11-17 at 4.10.15 PMWhen saving records in a form in Dynamics AX a lot of methods are fired off both on the form as well as on the table itself. I haven’t found another flow diagram describing what methods are fired and in what order, so I tested and found the results displayed in the diagram to the left:

  1. Form’s Validate() method on the datasource is called
  2. When super() is called with in the validate() method, the table’s validateField() method is called for each field
  3. Code placed after the super() in the form’s validate method is called
  4. Form’s validateWrite() method on the datasource is called
  5. When the super() is called in the validateWrite() method, the table’s validateWrite() method is called
  6. Code placed after the super() in the form’s validateWrite() method is called
  7. Form’s write() method is called.
  8. When the super() is called in the write() method, the table’s insert() method is called.
  9. Code placed after the super() in the form’s write() method is called.

Comment below if you have corrections or additions to this diagram.
Happy daxing.

Synchronize Dynamics AX DB in X++

Challenge/Problem: Synchronise Dynamics AX database from X++

Description: Previously I have written code to manipulate AX AOT objects via code (reflection) and found the need to kick off a database sync automatically from X++ code. I found the code snippets below to be useful for this operation.

The following two options allow you to perform the db syncronisation.

1. To synchronize a specific table: appl.dbSynchronize([tableid]);
2. To synchronize the whole app: appl.dbSynchronize(0);

Mass Resume Line Workflows

Challenge/Problem: Many line level workflows that need to be resumed.

Description: Sometimes due to data or setups one may have numerous line level workflows failing and entering a “Stopped” state. This may be a result of calendars that do not have enough dates created, users who have been disabled etc… If the workflows were header level, it is easy enough to select all in the Workflow History form and click resume, however on line level workflows one needs to view each line level workflow individually and resume them.

Solution: The following job can be used to perform mass resume on stopped workflows. You can adapt the SQL to limit to certain documents or document types of necessary.

static void resumeStoppedWorkflows(Args _args)
    WorkflowTrackinStatusTable tracking;
    int i, j;
    while select tracking where tracking.TrackingStatus == WorkflowTrackingStatus::Faulted
&& tracking.WorkflowType == WorkflowTrackingStatusWorkflowType::DependentSubworkflow
        try {
            Workflow::resumeWorkflow(tracking.CorrelationId, "Auto-resumed");
        catch (Exception::Error)
            //Some may not be able to be resumed but we dont want to stop the process
    info(strfmt("%1 workflows resumed, %2 workflows could not be resumed"));