Tag Archives: workflow

CRM 2013 New Features: Using Real-time Workflow for Validation Rules

Real-time workflows are a new feature in CRM 2013.  When I train on these the learning curve tends to be a gentle slope, the simple message is “you can now configure your workflows to run synchronously”.   Those 9 words comprise 90% of what you need to know.  That’s not to say this isn’t an awesome feature, its just easy to understand.   Here are a few other things you should know and cool new usage scenario that you should consider…

 

Execution Context

Workflows (both asynchronous and real-time types) can now execute either in the context of the User or in the context of the Owner of the Workflow:

image

If you define the workflow to execute as the owner of the workflow than the workflow can potentially perform actions the user does not have permission to – e.g. create certain records, or delete a record.   The workflow will have the permissions of the Owner of the workflow rule which you can set higher than the permissions of the end users.

If you define the workflow to execute in the context of the user then any records created or updated will reflect the user as the CreatedBy / ModifiedBy.

 

Validation Rules

I’ve had scenarios in the past where I have needed to perform a validation when a user attempts to update or delete a record.  If the record in question has certain properties I would need to reject the user action.  A common example is where you have external data that is synchronized with CRM and you need to prevent updates to those synchronized records, but not other records that are user-created – and these records reside in the same entity.

In CRM 2011 I either did this via JavaScript or a pre-update/pre-delete Plug-in.   A plug-in is more robust solution as it would fire regardless of how the update/delete was initiated.    No, we can do this via a real-time workflow!   Here’s how…

In my system I have Account records that are either user-created or created externally from CRM and synchronized with CRM via an integration.  I have an “Account Source” field which distinguishes between these two:

  image

My users should be able to delete Accounts that are flagged as “User-entered” but not those flagged as “Integration”.   I can’t achieve this via the CRM Security Model as it does not support data-based access rules.   I need to grant my users delete permissions and then overwrite their delete attempts when I need to block that action.  I can do this with a Workflow process.

Here’s the properties of my Workflow rule:

image

Note I have configured this as a real time workflow:

image

And I am triggering the Workflow to run Before the record is deleted:

image

Here’s the conditions and actions:

image

A simple condition that checks the value of the Account Source field.  If the value is “Integration” then the proceeding Action fires.   The Action is to Stop the Workflow with a status of Canceled.   Stopping the Workflow has always been an available Action:

image

But with workflows not able to run synchronously we can use this feature to block the user action that triggered the Workflow in the first place.

When you add this Action to a Workflow there are 2 options to pick from.  You either stop the Workflow as Succeeded or as Canceled: 

image

We need to stop the Workflow as Canceled in this scenario as that will block the delete from completing.

Next to this dropdown box is a Set Properties button.  If you click that you can add an Exception message that will pop to the user to explain why you have blocked their action:

image

You can embed fields from the record or from related records just like you can when you are using workflow to create a record.   In my case I just include some static text. 

Here’s the workflow in action.   I select an Account with an Account Source value of “Integration” and click Delete:

image 

I click through a couple of standard CRM warnings:

image

And then the workflow kicks in and throws the exception that I configured:

image

And my record remains:

image

Let’s look now and how this type of validation approach works in an Update scenario.   I tweak my workflow to run on Update instead:

image

And I adjust the error message:

image

Let’s test this out…

I set the workflow to fire when the Account name changes.   Let’s change that and a couple of other fields too:

image

And then wait for the auto-save to fire.   What you will see is a Business Process Error notification down in the Save section of the form:

image

Clicking on that will reveal the error:

image

Trying to force the save or navigating away from the record also results in the error popping.

The nice thing here is you don’t lose your changes when you hit this error.  You can’t save until you address the issue and none of the updated fields get updated.   But if I remove the change from the Account Name I can then save successfully and the other fields that I was also editing get updated successfully.

Hope this helps someone.

Advertisements

Analysing Workflows that Failed to Complete

There are a number of reasons why CRM workflow instances fail to complete.  It is important you regularly check for failed workflows to ensure the integrity of  your system. 

A failed workflow instance might be indicative of:

  • A workflow rule operating under a user context which has insufficient permissions assigned
  • A sloppily written workflow rule that in some scenarios attempts record updates to records that no longer exist
  • Environmental issues / timeouts / system errors
  • Missing email addresses

Each time a workflow rule is triggered a System Job is created.  These jobs carry a status and when a failure is experienced they are stamped with an error code, an error message and are set to a ‘Waiting’ status.

Here’s a useful bit of SQL I have been using to analyse a system’s failed workflow instances.  It provides a meaningful description for the error codes I have encountered.  I put this behind an Excel report so that I can pivot the results and monitor volumes.

select asyncoperation0.asyncoperationid as ‘asyncoperationid’, asyncoperation0.name as ‘name’,

asyncoperation0.regardingobjectidname as ‘regardingobjectidname’, asyncoperation0.operationtypename as ‘operationtypename’,

asyncoperation0.statuscodename as ‘statuscodename’, asyncoperation0.owneridname as ‘owneridname’,

convert(date,asyncoperation0.startedon,123) as ‘startedon’,

asyncoperation0.statecodename as ‘statecodename’, convert(date,

asyncoperation0.createdon,123) as ‘createdon’,

asyncoperation0.errorcode,

[message],

waitingreason = case                                         when asyncoperation0.errorcode = -2147204784 then ‘SQL error’

                                                                        when asyncoperation0.errorcode = -2147220946 then ‘Cannot updated closed record’

                                                                        when asyncoperation0.errorcode = -2147187962 then ‘CRM permissions issue’

                                                                        when asyncoperation0.errorcode = -2147218688 then ‘Object address not found or party maked as non-emailable’            

                                                                        when asyncoperation0.errorcode = -2147220969 then ‘Record to be changed no longer exists’           

                                                                        when asyncoperation0.errorcode = -2147204303 then ‘Maximum field length exceeded’

                                                                        when asyncoperation0.errorcode = -2147220970 then ‘Cannot access file – file in use’

                                                                        when asyncoperation0.errorcode = -2147201001 then ‘Request timed out’           

                                                                        when asyncoperation0.errorcode = -2147204718 then ‘Invalid email address for send email’                         

                                                                        else ‘<Unknown>’

                                                end

from FilteredAsyncOperation as asyncoperation0

where (asyncoperation0.recurrencestarttimeutc is null and asyncoperation0.statuscode = 10)

and asyncoperation0.errorcode is not null

/*and case                                            when asyncoperation0.errorcode = -2147204784 then ‘SQL error’

                                                                        when asyncoperation0.errorcode = -2147220946 then ‘Cannot updated closed record’

                                                                        when asyncoperation0.errorcode = -2147187962 then ‘CRM permissions issue’

                                                                        when asyncoperation0.errorcode = -2147218688 then ‘Object address not found or party maked as non-emailable’            

                                                                        when asyncoperation0.errorcode = -2147220969 then ‘Record to be changed no longer exists’           

                                                                        when asyncoperation0.errorcode = -2147204303 then ‘Maximum field length exceeded’

                                                                        when asyncoperation0.errorcode = -2147220970 then ‘Cannot access file – file in use’

                                                                        when asyncoperation0.errorcode = -2147201001 then ‘Request timed out’           

                                                                        when asyncoperation0.errorcode = -2147204718 then ‘Invalid email address for send email’                         

                                                                        else ‘<Unknown>’

                                                end = ‘<Unknown>’*/

order by asyncoperation0.startedon desc, asyncoperation0.asyncoperationid asc