THURSDAY, JANUARY 19, 2017

Prevent Redundant Approval Requests in ServiceNow

I

f you’re like many of the customers I’ve worked with, you may have dealt with the frustration of having excess or redundant approval requests come to you from ServiceNow. This happens very often simply because the same user may be responsible for various different tasks in the system. For example, on a change request, I may be asked to approve as the ‘Requested by’ person’s manager, then again because I own one of the affected CIs, and then again because I’m a member of the Change Advisory Board! While this behavior may be desirable in certain situations, most of the time it’s completely redundant and annoying to end users. If I’ve already indicated my approval on a change as a manager, why should I be asked to approve again later? I’ve come up with what I think is a pretty effective solution to this problem that I’ll share here in this article.

Redundant Approvals

The Solution…

At least in my mind, in order for this solution to be successful, it needs to meet a few criteria…

  1. First and foremost, the user should not be notified of an approval request if they’ve already approved a record.
  2. We need to maintain the history of the approval record for audit purposes and accurately reflect what happened to the redundant approval requests. This means that deleting or aborting record insertion is out of the question!
  3. We cannot negatively impact the approval workflow by interrupting the normal approval process.

The first two criteria need to be met at the same time. Preventing the approval request could be done in a couple of different ways. One way would be to somehow manipulate the workflow activity to check if the user has previously approved. The drawback to this approach is that it requires hacking the workflow approval activities…which would be a significant upgrade risk, or adding custom code to every single approval activity in every single workflow…which would be a maintenance nightmare.

That leaves us with intercepting the creation/update of the approval records in a business rule. Using a ‘before’ insert/update business rule we can evaluate every ‘requested’ approval record, run a script to determine if the approval is for a user that has already approved this item, and then adjust the approval record by setting the approval state to ‘No Longer Required’…all before we trigger any update or notification to the approving user. We can also add some approval comments so that we can accurately reflect why the approval isn’t required anymore. Adding the following business rule to the ‘Approval [sysapproval_approver]’ table accomplishes that for us.

‘Duplicate Approval Requests Not Required’ Business Rule
Name: Duplicate Approval Requests Not Required
Table: Approval [sysapproval_approver]
When: Before
Insert/Update: true
Condition: current.state.changesTo(‘requested’)
Script:

//Check to see if user has previously approved
approveDuplicateApproval();

function approveDuplicateApproval(){
    //Must have link to record being approved
    if(current.document_id || current.sysapproval){
        //Query for approval records for this user/record
        var app = new GlideRecord('sysapproval_approver');
        //Handle empty document_id and sysapproval fields
        if(!current.document_id.nil()){
            app.addQuery('document_id', current.document_id);
        }
        else if(!current.sysapproval.nil()){
            app.addQuery('sysapproval', current.sysapproval);
        }
        app.addQuery('approver', current.approver);
        app.addQuery('state', 'approved');
        //Optionally restrict to current workflow
        //app.addQuery('wf_activity.workflow_version', current.wf_activity.workflow_version);
        app.query();
        if(app.next()){
            //If previous approval is found set this approval to 'approved'
            current.state = 'not_required';
            current.comments = "Approval marked by system as 'Not Longer Required' due to a previous approval on the same record by the same user.";
        }
    }
}

While the above script works great in manipulating the approval records on its own, it fails in the third criteria mentioned above…it can negatively impact the workflow in certain situations. This is because the workflow processing that happens after an approval status is changed isn’t seeing the true value of the approval request to process the workflow approval activity correctly. In my testing this couldn’t be done in a ‘before’ or ‘after’ business rule due to the timing of the updates. What is needed is to run the workflow checks one more time, and ensure that it happens after all of the above manipulation happens. The best way I could find to do this is through a separate ‘async’ business rule. The only downside to this async business rule is that it may not process immediately depending on the load on your system. Generally it should process within a few seconds though so for all practical intents and purposes it’s a non-issue.

‘Run parent workflows (Not Required)’ Business Rule
Name: Run parent workflows (Not Required)
Table: Approval [sysapproval_approver]
When: async
Priority: 200 (This is important to be set to something greater than 100 to ensure that this runs ASAP!)
Insert/Update: true
Condition: current.state == ‘not_required’
Script:

// Run any workflows for our parent so they can check the approval states
runWorkflow_userApprove();

function runWorkflow_userApprove() {
   var id = current.sysapproval.nil() ? current.document_id : current.getValue('sysapproval');
   var table = current.source_table.nil() ? 'task' : current.source_table;
   if (id != null && table != null ) {
      var gr = new GlideRecord(table);
      if (gr.get(id)) {
           new Workflow().runFlows(gr, 'update');
        }
   }
}

With the above solution in place, you should have created a very effective way to prevent the issue of redundant approval requests for the same user against the same record in ServiceNow.

30 Comments

Steve 17-01-2014, 08:31

Thanks, this is great, the only issue that I seem to be having is that its changing it to no longer required but doesn’t continue on the workflow and just sits. I tried changing it to change it to approved instead and that didn’t continue either. Any thoughts?

Reply
Mark Stanger 17-01-2014, 09:23

Only thought I have is to check and make sure you’ve correctly added the second business rule in the article above. Without that, your workflow will hang until an update is made to the ticket.

Reply
Steve 21-01-2014, 09:35

Mark,
I verified that the second business rule was the same as in the post. I checked with ServiceNow and it appears that out of box when you change something to No Longer Required it will not auto continue the workflow. They said I would need to modify the ‘SNC – Run parent workflows (Approval)” I tried adding the condition of “current.state.changesTo(‘No Longer Required’)” and that didn’t seem to do anything. Any thoughts?

Reply
Mark Stanger 21-01-2014, 09:56

I don’t think ServiceNow support is telling you the right thing. Without access to your system it’s almost impossible to see what’s going on or make a suggestion. I know that this same solution has worked for several other people though so I don’t think it’s a conflict with out-of-box behavior. Only suggestion I can make is to try and set it up in a demo environment and see if it works there. If it does that will at least let you know a little better where you might look in your own environment.

Reply
J Ralph 17-04-2014, 15:22

Hi Mark, How would this work on an Order Guide ? I want a manager to only approve his Items once and I don’t want him to get multiple emails with an approval for each item. As always, any help would be greatly appreciated. Thanks

Reply
Mark Stanger 17-04-2014, 15:24

Order guides would be a completely different case. If you only want one approval for an order guide, then the approval should probably happen at the request level rather than the individual item level.

Reply
J Ralph 17-04-2014, 17:02

How would put all the items on hold until the Request of approved and then propogate them ? Sorry for all the questions, but I am new at creating order guides. Thanks again.

Reply
Mark Stanger 17-04-2014, 17:05

Unless something has been modified in your instance, the workflows for all request items should automatically wait for the parent request record to be approved. That’s the way it is by default. Once the request is approved, all of the item workflows can start. If it’s not behaving that way, you’ll need to take a closer look at the workflows associated to the request table.

Reply
J Ralph 17-04-2014, 17:14

I don’t know that. Thank you so much, I will give it a try.

Reply
Aditya 27-05-2014, 16:17

Can we do it via workflow script without using business rules because i want to it for some stages only instead of all.

Thanks

Reply
Mark Stanger 28-05-2014, 07:00

I don’t know of an easy way to do that since all of the code for that is handled within the workflow activity. You could adjust your business rule to look at the ‘wf_activity’ field on the approval table (it’s inactive by default) and selectively apply this logic based on the workflow activity field associated to the approval.

Reply
Aditya 28-05-2014, 10:00

Can you help me how to recognize change work flow stage via your script. For instance there are several stages in change workflow and i want to know at which stage the script is running. Here i want to block not to run for some stages.

Thanks

Reply
Luke 01-06-2015, 22:39

This breaks in eureka. There is something putting it back to required, looks to be an after busines rule introduced but I cannot find it. Any idea?

Reply
Kris K 16-09-2015, 07:29

Can I use these business rules to only affect one catalog item and not all approvals?

Reply
Mark Stanger 16-09-2015, 07:43

You could. You would need to adjust the query so that it only includes the catalog item or workflow activity you care about. The adjustment would need to be made to this area of the script…

//Optionally restrict to current workflow
//app.addQuery(‘wf_activity.workflow_version’, current.wf_activity.workflow_version);

Reply
Azjargal 17-10-2016, 02:41

Hi Mark,

Thank you for the script. Sorry I do not know any scripting and I just copied your business rule and it is working perfect. Now I would like to use your script only for two workflows named “HR generic_workflow” and “it generic_workflow”. How to apply this names into below script line?
//app.addQuery(‘wf_activity.workflow_version’, current.wf_activity.workflow_version);

thank you so much

Reply
Mark Stanger 17-10-2016, 06:16

If you want to restrict to a particular workflow you would probably be best off targeting that workflow by name, like this…

app.addQuery(‘wf_activity.workflow_version.name’, ‘HR generic_workflow’).addOrCondition(‘wf_activity.workflow_version.name’, ‘it generic_workflow’);

Reply
Azjargal 17-10-2016, 22:54

Dear Mark, Thank you so much. I really appropriate your help.

Reply
Azjargal 17-10-2016, 22:55

Dear Mark, Thank you so much. I really appreciate your help.

Azjargal 17-10-2016, 02:06

It is great. thank you Mark

Reply
fred 29-11-2016, 06:51

I’m troubleshooting the same issue. We r on Helsinki already. So not sure if this is still working.
What I see is that the async business rule seems not to have a condition …
ServiceNow: “Note: Asynchronous business rules do not have access to the previous version of a record. Therefore, these GlideElement methods do not work with async rules: changes(), changesTo(), changesFrom().”

… so I’m wondering how the 2nd Business Rule mentioned above will be fired without the condition set. Sorry I’m not the scripting guy 😉

Reply
Mark Stanger 29-11-2016, 07:50

I think you’re probably right about the ‘changesTo’ in the Async script above. You should be safe to change the condition to current.state == ‘not_required’ and it should work fine though. Normally I wouldn’t recommend running a business rule every time a state equals that value just because it’s going to be running way more than is necessary, but in this case an approval really wouldn’t be updated very often (if at all) once it reaches a ‘not required’ state. I’ve updated the article above reflecting this change.

Reply
Fred 29-11-2016, 08:18

Thanks Marks for the reply. Unfortunately conditions are somehow not possible to be used when set to async even there is a condition field on top of the script … but entering the condition – current.state == ‘not_required’ .. the error occurs: “JavaScript parse error at line (1) column (18) problem = illegal character (; line 1)” …
any idea?

Reply
Mark Stanger 29-11-2016, 08:26

Might be a copy/paste issue. You might try typing it in directly and see if that helps. You could also use the condition builder widget on the business rule to provide the condition.

Reply
Fred 29-11-2016, 08:37

When u change the “when” to “asncy” the filter conditions disappear .. . Same on my developer instance … there might be a reason for that ?! I could not find any UI Action that is triggering that (on the sys_script table) …

Reply
Mark Stanger 29-11-2016, 08:41

I’m not sure what else you can try then. I can enter that condition without issue into the standard ‘Condition’ field on the business rule in my Helsinki instance. You’re correct that the condition builder is removed, but the standard field should still work. You may try typing it in manually or testing with a completely different condition to see if anything works at all for you.

Reply
fred 29-11-2016, 08:50

Thanks! That was the problem (never have seen that before … ). Saving possible.

Unfortunately it is not working anyhow. Need to further investigate. Have 2 approvals sent to to different groups … meaning double mails in case one person is in both groups.

Reply
Fred 29-11-2016, 12:47

Hi again,
still struggling.
I tried in the developer instance – using the OoB Change Request Workflow and added a 2nd group to the CAB approval approvers and put same person in both groups.

With or without the Business Rules … same is happening.
Approval mail generated twice. After approving one of the approval tasks .. the 2nd approval is still open to be approved (for the same user).

No idea what is going wrong … any idea on that?

Reply
Mark Stanger 29-11-2016, 13:26

I think I see what’s going on now. All of your approvals are being included in the same approval activity. That’s not what this utility handles. This is for different approvals that are added later in an approval workflow. In your situation the workflow activity should handle this for you (or you need to code in logic in the workflow activity to make sure the same person isn’t added twice. I think the reason ServiceNow has left it this way is that you could argue (particularly for a group approval activity) that each user should need to approve on behalf of each group they are a member of.

Reply
Fred 29-11-2016, 14:02

OOOOOOK. This does make sense now.
I think I was close to find that out myself as I was wondering about the logic of the condition. Now this is clear.
Thanks a lot!

Reply

Leave a Reply


Latest Comments

  • David: It appears that I can hit sys_properties table with REST. This works, but I haven’t yet discovered the...
  • Mark Stanger: Hey David, It doesn’t surprise me that scoped apps have made this more difficult. I’m not...
  • David: Mark, do you have an example of how to do this in a scoped app? It seems there are many hoops to jump through...
  • Mark Stanger: The only possibility is to create a system property to override this in your application. Check out the...