When using an Approval - Group activity with the Wait For Condition based on script option, the Approval - Group activity may get stuck and not progress the workflow.

Steps to Reproduce


  1. Import the attached wf_workflow_version_9b2912815343030007d9435723dc348e.xml.

  2. Create a task record with approval rules set for it.

  3. Approve any of the generated user approvals.

    The group approval should be State = Approved, other approvals should No longer required, and the workflow should complete via the Approved path.



  1. Replace the Approval - Group activity definition with the code listed below. Please note the script to be replaced is the only section related to the function _determineOverallState and not the whole script code.
  2. Remove the related Customer Update [sys_update_xml] record, so that this file will receive future updates.

  // Determine the overall state of the approvals (approved, rejected or '')
   _determineOverallState: function() {
      var ids = activity.scratchpad.approval_ids;
      if (!ids)
         return 'skipped';
      var waitFor = activity.vars.wait_for;
      if (waitFor == 'script') {
         if (!activity.vars.approval_script)
            return 'approved';
         // Set up information to be passed to the script
         //    overall counts
         var ret = this.approvalUtils.getGroupIdListApprovalCounts(ids);
         ret.counts['total'] -= ret.counts['not_required'];
         ret.counts['total'] -= ret.counts['duplicate'];
         ret.counts['total'] -= ret.counts['cancelled'];
         workflow.prepareScriptVariable('counts', ret.counts);
         workflow.prepareScriptVariable('approvalIDs', ret.approvalIDs);
         //    group level counts and user ids
         var groups = {};
         var groupRet={};
         for (var i = 0; i < ids.length; i++) {
            var id = ids[i];
            // We now track the approvals in the scratchpad for better
            // detection of duplicate user approvals that were deleted
            if (GlideStringUtil.notNil(activity.scratchpad[id]) && typeof activity.scratchpad[id] !== "undefined")
            groupRet = this.approvalUtils.getGroupUserApprovalCounts(activity.scratchpad[id]);                                                                                                                                                                                                                                                                            
      groupRet = this.approvalUtils.getUserGroupApprovalCounts(id);
            groups[id] = {};
            groups[id].approvalIDs = groupRet.approvalIDs;
            for (var s in groupRet.counts)
               groups[id][s] = groupRet.counts[s];
         workflow.prepareScriptVariable('groups', groups);
         var newState = this.runScript(activity.vars.approval_script);
// If the newState is not requested then it has changed to Approved, Rejected, Cancelled
// or a custom state so mark any remaining Group approvals to No Longer Required
if (newState && GlideStringUtil.notNil(newState) && (newState != 'requested')) {
var groupApprovals = new GlideRecord('sysapproval_group');
groupApprovals.addQuery('sys_id', ids);
while ( {
groupApprovals.approval = newState;
this.approvalUtils.setPendingUserApprovalsByGroup(groupApprovals.sys_id, 'not_required');
if (!newState || GlideStringUtil.nil(newState))
newState = 'requested';
         return newState;
      // check for approval state change within each group
      var rejectHandling = activity.vars.reject_handling;
      var changed = false;
      var groupApprovals = new GlideRecord('sysapproval_group');
      groupApprovals.addQuery('sys_id', ids);
      while ( {
         if (this._determineGroupState(groupApprovals, waitFor, rejectHandling))
            changed = true;
      if (!changed && !this.forceState)
         return '';
      // And then the overall state
      var ret = this.approvalUtils.getGroupIdListApprovalCounts(ids);
      ret.counts['total'] -= ret.counts['not_required'];
      ret.counts['total'] -= ret.counts['duplicate'];
      ret.counts['total'] -= ret.counts['cancelled'];
      if (ret.counts['total'] == 0)
         return 'skipped';
      if ((ret.counts['rejected'] > 0) && (rejectHandling == 'reject'))
         return 'rejected';
      if ((waitFor == 'any' || waitFor == 'first_any') && (ret.counts['approved'] > 0))
         return 'approved';
      if ((waitFor == 'first_any') && (ret.counts['rejected'] > 0))
         return 'rejected';
      if ((waitFor == 'all' || waitFor == 'any_each' || waitFor == 'first_each') && (ret.counts['approved'] == ret.counts['total']))
         return 'approved';
      if ((ret.counts['rejected'] + ret.counts['approved']) == ret.counts['total']) {
         // we are complete - we have to make a decision
         return 'rejected';
      return 'requested';



Related Problem: PRB1244065

Seen In

There is no data to report.

Fixed In

Jakarta Patch 8
Kingston Patch 3

Associated Community Threads

There is no data to report.

Article Information

Last Updated:2019-06-27 09:01:29