How to change process and stage programmatically

Scenario

UPDATE: With CRM2015 - the new client side process API should be used to set the current step. The following approach should be only used for setting the process to the first step.

If you have more than one Dynamics CRM 2013 Business Process Flow for a given entity users can easily change the current process by using the 'Switch Process' Command Bar button:

Once changed, you can identify which process is running by hovering over the 'I' on the bottom right of the process flow:

If your users would like to change the Business Process Flow stage dynamically based on a given set of attribute values then it is not immediately obvious how this can be achieved. This post provides one solution using a custom workflow activity in combination with the new Synchronous Workflow feature of Dynamics CRM 2013.

Solution

The currently selected process stage on a given entity record is stored in CRM using the following attributes:

  • processid – GUID of the selected Business Process (workflow)
  • stageid – GUID of the selected Process Stage (processstage)

Initially I thought that these attributes might be able to be updated using a workflow directly, but it turns out you can only update them via code.

You will need to know how to write Custom Workflow Activities and add JavaScript Web Resources to follow these steps:

1. Using the Visual Studio Developer Tookit found in the SDK, create a new Custom Workflow Activity that accepts the following input arguments:

[RequiredArgument]
[Input("Process Name")]
public InArgument Process { get; set; }

[RequiredArgument]
[Input("Process Stage Name")]
public InArgument ProcessStage { get; set; }

These parameters need to be strings since you cannot pass in EntityReferences to the Workflow and ProcessStage entities.

2. Add to the Execute code the following:

using (var ctx = new OrganizationServiceContext(service))
{   
    // Get the processid using the name provided
    var process = (from p in ctx.CreateQuery()
                   where
                   p.Name == Process.Get(executionContext)
                   &&
                   p.StateCode == WorkflowState.Activated
                   select new Workflow
                   {
                       WorkflowId = p.WorkflowId

                   }).FirstOrDefault();
    if (process==null)
        throw new InvalidPluginExecutionException(String.Format("Process '{0}' not found",Process.Get(executionContext)));

    // Get the stage id using the name provided
    var stage = (from s in ctx.CreateQuery()
                 where
                 s.StageName == ProcessStage.Get(executionContext)
                 &&
                 s.ProcessId.Id == process.WorkflowId
                 select new ProcessStage
                 {
                     ProcessStageId = s.ProcessStageId

                 }).FirstOrDefault();
    if (stage == null)
        throw new InvalidPluginExecutionException(String.Format("Stage '{0}' not found", Process.Get(executionContext)));

    // Change the stage
    Entity updatedStage = new Entity(context.PrimaryEntityName);
    updatedStage.Id = context.PrimaryEntityId;
    updatedStage["stageid"] = stage.ProcessStageId;
    updatedStage["processid"] = process.WorkflowId;
    service.Update(updatedStage);
}

3. Deploy your custom workflow activity.

4. Create a New Workflow that is defined with the following parameters:

  • Activate As: Process
  • Run this workflow in the background : NO
  • Scope: Organization (if you want it to run for all users)
  • Start when: Before – Record fields change. Select the attributes that the process stage is dependant on so that it does not run on every change.

4. Add a set of conditions to determine if the stage should be changed and call your custom workflow activity providing the Process Name and Process Stage to change to.

5. Since the Process and Stage will not automatically change on the form, you need to add some JavaScript to refresh the form by saving it when the dependant fields are changed. You can do this by adding an onChange event to the dependant fields that calls:

Xrm.Page.data.entity.save()

UPDATE: Rob Boyers pointed out that this doesn't reliably refresh the process, however there isn't currently a supported way of doing it.
You could use the following unsupported method call to reload the whole form:

Mscrm.ReadFormUtilities.openInSameFrame(window._etc, Xrm.Page.data.entity.getId())


Notes:

  1. This technique could also be written using a plugin - but the advantage of using a Synchronous workflow is that the logic that determines the stage can be easily changed without re-compiling code.
  2. You can use the same technique to read the process and stage name so that you can make a decision using the current position in the process. I've included a sample of this too in the download
  3. You can download the sample code from here - http://code.msdn.microsoft.com/Change-Dynamics-CRM-2013-a6beb85e

Pingbacks and trackbacks (19)+

Comments are closed