Name

sn_hr_core.hr_ApprovalUtil

Description

Provides utility for HR approvals and subflows.

Script

var hr_ApprovalUtil = Class.create();
hr_ApprovalUtil.prototype = {
  type: 'hr_ApprovalUtil'
};

hr_ApprovalUtil.WORKFLOW_ID = '8de6490c53032200d901a7e6a11c0838';

hr_ApprovalUtil.checkApprovalParameters = function(caseGr, workflow) {
  var serviceActivity = workflow.variables.service_activity_sys_id;
  var leActivity = workflow.variables.le_activity_sys_id;
  var adhocApprovers = workflow.variables.adhoc_approvers;
  var waitFor = workflow.variables.wait_for;
  var onRejection = workflow.variables.on_rejection;

  if (serviceActivity)
  	hr_ApprovalUtil.getServiceActivityParameters(serviceActivity, workflow);
  else if (leActivity)
  	hr_ApprovalUtil.getLEActivityParameters(leActivity, workflow);
  else if (adhocApprovers) {
  	workflow.scratchpad.approvalUsers = adhocApprovers;
  	workflow.scratchpad.approval_accept_option = waitFor || 'first_response';
  	workflow.scratchpad.approval_reject_option = onRejection || 'close_case';
  	workflow.scratchpad.missing_some_approvers = 'skip';
  	workflow.scratchpad.missing_all_approvers = 'substitute';
  } else
  	hr_ApprovalUtil.getHRServiceParameters(caseGr, workflow);

  return !hr_ApprovalUtil.noApproversSet(workflow);
};

hr_ApprovalUtil.noApproversSet = function (workflow) {
  var noOptions = gs.nil(workflow.scratchpad.approvalOptions);
  var noGroups = gs.nil(workflow.scratchpad.approvalGroups);
  var noUsers = gs.nil(workflow.scratchpad.approvalUsers);

  return noOptions && noGroups && noUsers;
};

hr_ApprovalUtil.getServiceActivityParameters = function(activityId, workflow) {
  var grSA = new GlideRecord('sn_hr_core_service_activity');

  if (grSA.get(activityId)) {
  	workflow.scratchpad.approvalOptions = grSA.approval_options.toString();
  	workflow.scratchpad.approvalGroups = grSA.approver_groups.toString();
  	workflow.scratchpad.approvalUsers = grSA.approver_users.toString();
  	workflow.scratchpad.approval_accept_option = grSA.approval_accept_option || 'first_response';
  	workflow.scratchpad.approval_reject_option = grSA.approval_reject_option || 'close_case';
  	workflow.scratchpad.missing_some_approvers = grSA.missing_some_approvers || 'skip';
  	workflow.scratchpad.missing_all_approvers = grSA.missing_all_approvers || 'substitute';
  }
};

hr_ApprovalUtil.getLEActivityParameters = function(activityId, workflow) {
  var grLA = new GlideRecord('sn_hr_le_activity');

  if (grLA.get(activityId)) {
  	workflow.scratchpad.approvalOptions = grLA.approvers.toString();
  	workflow.scratchpad.approvalGroups = grLA.approver_groups.toString();
  	workflow.scratchpad.approvalUsers = grLA.approver_users.toString();
  	workflow.scratchpad.approval_accept_option = grLA.approval_accept_option || 'anyone';
  	workflow.scratchpad.approval_reject_option = grLA.approval_reject_option || 'reapprove';
  	workflow.scratchpad.missing_some_approvers = grLA.missing_some_approvers || 'skip';
  	workflow.scratchpad.missing_all_approvers = grLA.missing_all_approvers || 'substitute';
  }
};

hr_ApprovalUtil.getHRServiceParameters = function(caseGr, workflow) {
  // set fields to mimic the OOB functionality of Jakarta
  if (caseGr.hr_service.approval_options) {
  	workflow.scratchpad.approvalOptions = caseGr.hr_service.approval_options.toString();
  	workflow.scratchpad.approval_accept_option = 'first_response';
  	// approval subflow in Jakarta returns "rejected" and lets caller decide how to handle it
  	workflow.scratchpad.approval_reject_option = 'legacy';
  }
  workflow.scratchpad.insufficient_data_is_an_option = true;
};

hr_ApprovalUtil.approverCount = function(caseGr, workflow) {
  workflow.scratchpad.approvers = [];
  workflow.scratchpad.missingApprovers = [];

  if (workflow.scratchpad.approvalOptions)
  	hr_ApprovalUtil._getApproversFromOptions(caseGr, workflow);
  if (workflow.scratchpad.approvalGroups)
  	hr_ApprovalUtil._getApproversFromGroups(workflow);
  if (workflow.scratchpad.approvalUsers)
  	hr_ApprovalUtil._getApproversFromUsers(workflow);

  workflow.scratchpad.missingApprovers.sort();
  return workflow.scratchpad.approvers.length;
};

hr_ApprovalUtil.missingApprovers = function(caseGr) {
  var approvalContext = hr_ApprovalUtil.getApprovalSubflowsForCase(caseGr);
  return approvalContext.scratchpad.missingApprovers;
};

hr_ApprovalUtil._getApproversFromOptions = function(caseGr, workflow) {
  var gr = new GlideRecord('sn_hr_core_service_approval_option');
  gr.addActiveQuery();
  gr.addQuery('sys_id', 'IN', workflow.scratchpad.approvalOptions);
  gr.query();

  while (gr.next()) {
  	var glideEle = caseGr.getElement(gr.approval_assign_to);

  	if (glideEle) {
  		var refTo = glideEle.getReferenceTable();
  		var rootTable = new GlideTableHierarchy(refTo).getRoot();

  		switch (rootTable) {
  			case 'sys_user':
  				if (hr_ApprovalUtil.isValidUser(glideEle))
  					workflow.scratchpad.approvers.push(glideEle.toString());
  				else
  					workflow.scratchpad.missingApprovers.push(String(gr.name));
  				break;
  			case 'sys_user_group':
  				if (hr_ApprovalUtil.isValidGroup(glideEle))
  					workflow.scratchpad.approvers.push(glideEle.toString());
  				else
  					workflow.scratchpad.missingApprovers.push(String(gr.name));
  				break;
  		}
  	} else
  		workflow.scratchpad.missingApprovers.push(String(gr.name));
  }
};

hr_ApprovalUtil._getApproversFromGroups = function(workflow) {
  var approverGroups = String(workflow.scratchpad.approvalGroups).split(',');

  for (var i = 0; i < approverGroups.length; i++) {
  	if (hr_ApprovalUtil.isValidGroup(approverGroups[i]))
  		workflow.scratchpad.approvers.push(approverGroups[i]);
  	else {
  		var group = new GlideRecord('sys_user_group');
  		group.get(approverGroups[i]);
  		workflow.scratchpad.missingApprovers.push(String(group.name));
  	}
  }
};

hr_ApprovalUtil._getApproversFromUsers = function(workflow) {
  var approverUsers = String(workflow.scratchpad.approvalUsers).split(',');

  for (var j = 0; j < approverUsers.length; j++) {

  	if (hr_ApprovalUtil.isValidUser(approverUsers[j]))
  		workflow.scratchpad.approvers.push(approverUsers[j]);
  	else {
  		var grUser = new GlideRecord('sys_user');
  		grUser.get(approverUsers[j]);
  		workflow.scratchpad.missingApprovers.push(String(grUser.name));
  	}
  }
};

hr_ApprovalUtil.isValidGroup = function(groupId) {
  var grMember = new GlideRecord('sys_user_grmember');
  grMember.addQuery('user.active', 'true');
  grMember.addQuery('group', groupId);
  grMember.setLimit(1);
  grMember.query();

  return grMember.hasNext();
};

hr_ApprovalUtil.isValidUser = function(userId) {
  var grUser = new GlideRecord('sys_user');
  grUser.addQuery('sys_id', userId);
  grUser.addActiveQuery();
  grUser.query();

  return grUser.hasNext();
};

/*------------- Rejection option methods -------------*/

hr_ApprovalUtil._getStage = function(approvalContext) {
  var stage = new GlideRecord("wf_stage");
  if (stage.get(approvalContext.stage))
  	return stage.value;
  else
  	return null;
};

hr_ApprovalUtil.isReapprovalAuthorized = function(caseGr) {
  if (!new hr_Delegation().isAssignedOrDelegated(caseGr))
  	return false;

  var approvalContext = hr_ApprovalUtil.getApprovalSubflowsForCase(caseGr);
  return approvalContext != null && hr_ApprovalUtil._getStage(approvalContext) == 'awaiting_reapproval';
};

hr_ApprovalUtil.rerequestCaseApproval = function(caseGr) {
  var approvalContext = hr_ApprovalUtil.getApprovalSubflowsForCase(caseGr);
  new global.Workflow().broadcastEvent(approvalContext.sys_id, 'sn_hr_core.request_reapproval');
  gs.addInfoMessage(gs.getMessage('Approvals have been re-requested'));
};

hr_ApprovalUtil.isSubstituteApprovers = function(caseGr) {
  if (!new hr_Delegation().isAssignedOrDelegated(caseGr))
  	return false;

  var approvalContext = hr_ApprovalUtil.getApprovalSubflowsForCase(caseGr);
  var result = approvalContext != null && hr_ApprovalUtil._getStage(approvalContext) == 'substitute_approvers';
  return result;
};

hr_ApprovalUtil.getApprovalSubflowsForCase = function(grCase) {
  var wfContext = new global.Workflow().getRunningFlows(grCase);

  //Find running context for 'HR Case Approval Subflow'
  while (wfContext.next()) {
  	if (wfContext.workflow == hr_ApprovalUtil.WORKFLOW_ID)
  		return wfContext;
  }

  return null;
};

hr_ApprovalUtil.caseRejected = function(caseGr) {
  var approval = new GlideRecord("sysapproval_approver");
  approval.addQuery("sysapproval", current.sys_id);
  approval.addQuery("state", "rejected");
  approval.addQuery("wf_activity", "!=", "");
  approval.setLimit(1);
  approval.query();
  return approval.hasNext();
};

hr_ApprovalUtil.disassociateApprovalsFromWorkflow = function(grCase) {
  var approvalGr = new GlideRecord("sysapproval_approver");
  approvalGr.addQuery("sysapproval", grCase.getUniqueValue());
  approvalGr.addQuery('wf_activity.workflow_version.workflow', hr_ApprovalUtil.WORKFLOW_ID);
  approvalGr.addQuery("state", "!=", "cancelled");
  approvalGr.query();

  while (approvalGr.next()) {
  	approvalGr.setValue('wf_activity', "");
  	approvalGr.update();
  }
};

/*
* Returns list of missing approvers defined on approval HR service activity
*
* @param
*  caseID: HR case sys_id string value
* 
* @returns
*  array list of missing approvers defined on approval HR service activity  
*/
hr_ApprovalUtil.getMissingApproversOnCase = function(caseID) {

  var substituteApproverGR = new GlideRecord('sn_hr_core_substitute_approver');
  substituteApproverGR.addNotNullQuery('missing_approver');
  substituteApproverGR.addQuery('hr_case', caseID);
  substituteApproverGR.addQuery('approval_triggered', false);
  substituteApproverGR.orderBy('missing_approver');
  substituteApproverGR.query();

  var missingApprovers = [];
  while (substituteApproverGR.next()) {
  	var missingApprover = substituteApproverGR.getValue('missing_approver');
  	missingApprovers.push(missingApprover);
  }

  return missingApprovers;
};
  
/*
* Returns boolean true if atleast one approver is set for HR case
*
* @param
*  caseID: HR case sys_id string value
* 
* @returns
*  boolean true if atleast one approver is set false otherwise
*/
hr_ApprovalUtil.approverSetForCase = function(caseID) {
  
  var approverSet = false;
  var caseGR = new GlideRecord('sn_hr_core_case');

  if (caseGR.get(caseID)) {
  	var approvalWorkFlowContext = hr_ApprovalUtil.getApprovalSubflowsForCase(caseGR);
  	if (approvalWorkFlowContext.scratchpad.approvers.length > 0)
  		approverSet = true;
  }

  return approverSet;
}; 

/*
* Adds approvers to the case and updates case worflow
*
* @param 
*  approvers: comma separated string value containing sys_user ids
*  table: string value of table name
*  caseID: HR case sys_id string value
* 
* @returns error message as
*  object {
*    error: string error message if invalid user or missing caseID
*  }
*  OR null for success
*/
hr_ApprovalUtil.addApproversToCase = function(approvers, table, caseID) {
  var response = {};

  if (table && caseID) {
  	//add approvers to case
  	var failedToAddApprovers = [];
  	//when 'some' approvers are missing, list can be empty.
  	if (approvers) {
  		var approversList = approvers.split(',');
  		var hrSubstituteApproverGR = new GlideRecord('sn_hr_core_substitute_approver');
  		
  		for (var i = 0; i < approversList.length; i++) {
  			hrSubstituteApproverGR.initialize();
  			hrSubstituteApproverGR.approver = approversList[i];
  			hrSubstituteApproverGR.hr_case = caseID;
  			var inserted = hrSubstituteApproverGR.insert();
  			if (!inserted)
  				failedToAddApprovers.push(approversList[i]);
  		}
  		
  		hr_ApprovalUtil.setApprovalTriggerForMissingApprover(caseID);
  	}
  	
  	//update case workflow context
  	var caseGR = new GlideRecord(table);
  	if ( caseGR.get(caseID) ) {
  		var approvalContext = hr_ApprovalUtil.getApprovalSubflowsForCase(caseGR);
  		if (approvalContext)
  			new global.Workflow().broadcastEvent(approvalContext.sys_id, 'sn_hr_core.substitute_approvers');
  	}
  	
  	//error message for either failed insertion or no workflow found 
  	if ( (failedToAddApprovers.length > 0) || !approvalContext) {
  		response = {
  			error: failedToAddApprovers.length > 0 ? gs.getMessage('Insertion of following approval records failed {0}', failedToAddApprovers.toString()) : '' +
  				approvalContext ? '' : gs.getMessage('No workflow found for case {0}', caseID)
  		};
  	}
  } else {
  	response = {
  		error: table ?  gs.getMessage('Table name is not set.') :  '' +
  			caseID ?  gs.getMessage('HR case sys_id is not set.') : '' 
  	};
  }

  return JSON.stringify(response);
};

/*
* 
*
* @param
*  caseGr: HR case gliderecord
*  vars: variable to pass to workflow
*   vars = {};
  vars.adhoc_approvers: comma seperated sys_id(s) of approver users
  vars.wait_for : wait for option ['anyone','everyone','first_response']
  vars.on_rejection :reject option ['resubmit','close_case']
* 
* 
*/

hr_ApprovalUtil.startAdhocApprovalFlow = function(caseGr,vars) {
  var w = new global.Workflow();
  w.startFlow(hr_ApprovalUtil.WORKFLOW_ID, caseGr,"",vars);
};

/*
* Returns boolean true if adhoc approval action can be performed
*
* @param
*  caseGr: HR case gliderecord
* 
* @returns
*  boolean true if adhoc approval action can be performed false otherwise
*/

hr_ApprovalUtil.isAdhocApprovers = function(caseGr) {
  if(!hr_ApprovalUtil.checkApproverCaseOption(caseGr))
  	return false;
  if (!new hr_Delegation().isAssignedOrDelegated(caseGr))
  	return false;

  var approvalContext = hr_ApprovalUtil.getApprovalSubflowsForCase(caseGr);
  var result = (approvalContext == null) && (caseGr.state == '10'  || caseGr.state == '18') ;
  return result;
};

/*
* Returns boolean true if adhoc approval action can be performed
*
* @param
*  caseGr: HR case gliderecord
* 
* @returns
*  boolean true if AgentCanAddApprover case option is added 
*  in HR service false otherwise
*/

hr_ApprovalUtil.checkApproverCaseOption = function(caseGr) {
      var caseOptions = caseGr.hr_service.case_options;
      var optionsGr = new GlideRecord('sn_hr_core_service_option');
      optionsGr.get('value', 'AgentCanAddAnApproval');
      if (!gs.nil(caseOptions) && caseOptions.indexOf(String(optionsGr.sys_id)) > -1)
          return true;
      return false;
};

/*
* Set Approval triggered value for missing approver records
*
* @param
*  caseId: HR case sys Id
*  triggerValue: true/false
* 
*/
hr_ApprovalUtil.setApprovalTriggerForMissingApprover = function(caseId) {
  var substituteApproverGR = new GlideRecord('sn_hr_core_substitute_approver');
  substituteApproverGR.addNotNullQuery('missing_approver');
  substituteApproverGR.addQuery('hr_case', caseId);
  substituteApproverGR.addQuery('approval_triggered', false);
  substituteApproverGR.query();
  substituteApproverGR.setValue('approval_triggered', true);
  substituteApproverGR.updateMultiple();
};

Sys ID

3f09e559536232003585c3c606dc34d6

Offical Documentation

Official Docs: