Programmatically Setting the Active Stage of Business Process Flows via Plugin

Isaac Stephens, 15 February 2016

Interacting with business process flows programmatically in Dynamics CRM can get a little tricky, due to some unexpected behaviour that still exists within CRM 2015. There are plenty of online resources which cover clientside programming for business process flows using javascript, which typically produces less unexpected behaviour, but at the time of writing this blog there weren't many which cover automation using C# plugins, which is what I'll focus on here.

When you first tick the 'Business Process Flows' checkbox in an entity's details and save the changes, two new fields are added to the entity – processid, and stageid – which record the business process flow and stage of the flow being used by records of that type, respectively.

 image

 image

It's perfectly possible to change the values of these fields programmatically in order to change the business process flow or stage being used by a record, however it may result in certain unexpected graphical errors. I'll cover the unexpected behaviour and a possible workaround at the end of the blog. For now, here's the plugin code I used to set the business process flow active stage of a record based on its status reason.

private void Process(Entity entity) { // Retrieves the processid field if we don't already have it // Get the status reason and processid for conditional logic OptionSetValue statusCode = entity.GetAttributeValue<OptionSetValue>("statuscode"); Guid processId = entity.GetAttributeValue<Guid>("processid"); if(statusCode.Value != 0 && processId != Guid.Empty) { Guid stageId = Guid.Empty; if (stageId != Guid.Empty) { entity["stageid"] = stageId; } } }

The Process method is called by the Execute method at the start of the plugin logic.

The code above does the following:

  • Fires on update of a record
  • Extracts the value of the status reason field (used as an example here, for some conditional logic)
  • Extracts the ids of the current business process flow and active stage for that record
  • Uses conditional logic based on the status reason to determine the name of the next active stage
  • Finds the id of that stage using the name and the id of the parent business process flow
  • Updates the record setting the stageid to the id of the new active stage

The unexpected behaviour I mentioned earlier occurs when your plugin changes the active stage of a business process flow upon saving the record. You may find that the active stage will correctly change, but you won't see it happen immediately – instead, a new flag is made visible on the stage which has been activated, but the old stage will still appear to be active also, as you can see below.

 image

One workaround for this is to use javascript to force the page to refresh after the form has been saved. Unfortunately it's very tricky to execute clientside code after a form saves – functions bound to the onSave event will always execute before the new data has been saved, and a save event no longer triggers a load event immediately afterwards as in previous CRM versions – so we have to rely on unsupported code to achieve a reliable page refresh.

refreshIfStatusReasonChanged = function () { var statusReasonIsDirty = Xrm.Page.getAttribute("statusreason").getIsDirty(); if (statusReasonIsDirty) { location.reload(); } }

This function is bound to the onSave event of the form.

In the code above I use the location.reload() method, which manipulates the DOM directly and is therefore unsupported. It's generally not a good idea to use unsupported code because, even if it's functional when you deploy it, it could break at some point in the future. However, even when the location.reload() call is made before the save event occurs, the record is still saved correctly and the page refreshes afterwards, which is currently the most reliable method I could find to work around the unexpected graphical behaviour.

Hopefully Microsoft eventually rectifies the graphical issue that's encountered in this scenario, but until then we can still work around the issue using the method in this blog.