Name

sn_hr_sp.hr_DashboardUtil

Description

Utility to help display cases on the HRJ Dashboard.

Script

var hr_DashboardUtil = Class.create();

//Default tables
hr_DashboardUtil.hrCaseTables = sn_hr_core.hr.TABLE_CASE_EXTENSIONS.toString();
hr_DashboardUtil.approvalTables = sn_hr_core.hr.TABLE_CASE_EXTENSIONS.slice();
hr_DashboardUtil.approvalTables.push('sc_request');

hr_DashboardUtil.LIST_TODO = 'TODO';
hr_DashboardUtil.LIST_OPEN = 'OPEN';
hr_DashboardUtil.LIST_CLOSED = 'CLOSED';

hr_DashboardUtil.QUERY_LIMIT = 10;

hr_DashboardUtil.prototype = {
  initialize: function() {
  	this._user = gs.getUserID();
  	this._portalUtil = new sn_hr_sp.hr_PortalUtil();
  },
  
  getMyTodos : function(limit) {
  	var todoQueryInfo = {queryList : [this._approvalTodoQuery(false, false), this._acceptanceTodoQuery(), this._taskTodoQuery(false, false, false), this._taskTodoQuery(true, false, false)],
  						 recordWatchers : [{table: 'sn_hr_core_case' , filter: 'active=true^opened_for=' + this._user}, 
  										   {table: 'sn_hr_core_task' , filter: 'active=true^assigned_to=' + this._user},
  										   {table: 'sysapproval_approver' , filter: 'state=requested^approverIN' + new sn_hr_core.hr_Utils().getApprovals(this._user)},
  										   {table: 'sc_request' , filter: 'active=true^requested_for=' + this._user}]};
  	
  	return this._constructOrderedList(todoQueryInfo, limit, hr_DashboardUtil.LIST_TODO);
  },
  
  getMyOpenItems : function(limit) {
  	var openItemQueryInfo = {queryList : [this._openedForCaseQuery(false), this._requestedForQuery(false), this._approvalTodoQuery(null, null), this._taskTodoQuery(null, null, null)],
  						 recordWatchers : [{table: 'sn_hr_core_case' , filter: 'active=true^opened_for=' + this._user}, 
  										   {table: 'sn_hr_core_task' , filter: 'active=true^assigned_to=' + this._user},
  										   {table: 'sysapproval_approver' , filter: 'state=requested^approverIN' + new sn_hr_core.hr_Utils().getApprovals(this._user)},
  										   {table: 'sc_request' , filter: 'active=true^requested_for=' + this._user}]};
  	
  	if (new GlidePluginManager().isActive('com.snc.service_management.core'))
  		openItemQueryInfo.queryList.push(this._openedForOrderQuery(false));
  	
  	return this._constructOrderedList(openItemQueryInfo, limit, hr_DashboardUtil.LIST_OPEN);
  },
  
  getMyCompletedItems : function(limit) {
  	var completedItemQueryInfo = {queryList : [this._openedForCaseQuery(true), this._requestedForQuery(true), this._approvalTodoQuery(true, true), this._taskTodoQuery(null, true, true)],
  								  recordWatchers : [{table: 'sn_hr_core_case' , filter: 'active=false^opened_for=' + this._user}, 
  													{table: 'sn_hr_core_task' , filter: 'active=false^assigned_to=' + this._user},
  													{table: 'sysapproval_approver' , filter: 'stateINapproved,rejected,not_required^approver=' + this._user},
  												    {table: 'sc_request' , filter: 'active=false^requested_for=' + this._user}]};
  	
  	if (new GlidePluginManager().isActive('com.snc.service_management.core'))
  		completedItemQueryInfo.queryList.push(this._openedForOrderQuery(true));
  	
  	return this._constructOrderedList(completedItemQueryInfo, limit, hr_DashboardUtil.LIST_CLOSED);
  },
  
  _constructOrderedList : function (queryInfo, limit, listType) {
  	var parentCaseIds;
  	var slotsToFill = limit + 1;
  	
  	if (listType == hr_DashboardUtil.LIST_TODO)
  		parentCaseIds = this._checkTodoQueries(queryInfo, slotsToFill);
  	else
  		parentCaseIds = this._checkUnprioritizedQueries(queryInfo, slotsToFill, listType);
  	
  	var caseInfo = {parentCaseList : {items : [], hasMoreItems : parentCaseIds.recordsToShow.length > limit}, watchers : queryInfo.recordWatchers};
  	
  	for (var j = 0; j < parentCaseIds.recordsToShow.length && j < limit; j++)
  		caseInfo.parentCaseList.items.push({recordInfo : parentCaseIds.recordsToShow[j]});
  			
  	return caseInfo;
  },
  
  /*------------------------* To-Do Items *------------------------*/
  
  _checkTodoQueries : function (queryInfo, slotsToFill) {
  	var parentCaseIds = {recordsToShow : [], excludedRecords : [], excludedParents : []};
  	
  	for (var i = 0; i < queryInfo.queryList.length && parentCaseIds.recordsToShow.length < slotsToFill; i++)
  		this._paginateTodoQueriesQuery(queryInfo.queryList[i], parentCaseIds, slotsToFill);
  	
  	return parentCaseIds;
  },
  
  _paginateTodoQueriesQuery : function(queryFunction, parentCaseIds, slotsToFill) {
  	var start = 0;
  	var parentAdded = false;
  	
  	do {
  		var grQuery = queryFunction(parentCaseIds.excludedRecords, parentCaseIds.excludedParents);
  		grQuery.chooseWindow(start, start + hr_DashboardUtil.QUERY_LIMIT);
  		grQuery.query();

  		if (!grQuery.hasNext()) //No more records
  			return;
  		
  		while (grQuery.next() && parentCaseIds.recordsToShow.length < slotsToFill) {
  			if (this._reconcileDisplayItem(grQuery, parentCaseIds, hr_DashboardUtil.LIST_TODO))
  				parentAdded = true;
  		}
  		
  		if (parentAdded) {
  			parentAdded = false;
  			start = 0;
  		}
  		else
  			start += hr_DashboardUtil.QUERY_LIMIT;
  		
  	} while (parentCaseIds.recordsToShow.length < slotsToFill);
  },
  
  
  /*------------------------* Open/Closed Items *------------------------*/
  
  _checkUnprioritizedQueries : function (queryInfo, slotsToFill, listType) {
  	var parentCaseIds = {recordsToShow : [], excludedRecords : [], excludedParents : []};
  	var queryStatus = this._initializeQueryStatus(queryInfo);
  	
  	while (queryStatus.length > 0 && parentCaseIds.recordsToShow.length < slotsToFill) {
  		var selectedIndex = this._chooseResultFromQueries(queryStatus, listType);
  		var gr = this._getRelevantRecord(queryStatus[selectedIndex].query);

  		if (gr && this._reconcileDisplayItem(gr, parentCaseIds, listType)) {
  			for (var i = 0; i < queryStatus.length; i++)
  				queryStatus[i].resetWindow = true;
  		}
  		
  		if (!queryStatus[selectedIndex].query.next())
  			this._moveQueryPointer(queryStatus, selectedIndex, parentCaseIds);
  	}
  	
  	return parentCaseIds;
  },
  
  //Initialize queries/windows; add those that have results to status object
  _initializeQueryStatus : function (queryInfo) {
  	var queryStatus = [];
  	
  	for (var i = 0; i < queryInfo.queryList.length; i++) {
  		var query = queryInfo.queryList[i];
  		var queryInstance = query();
  		
  		queryInstance.chooseWindow(0, hr_DashboardUtil.QUERY_LIMIT);
  		queryInstance.query();
  		
  		if (queryInstance.next())
  			queryStatus.push({query: queryInstance, queryFunction : query, window : 0, resetWindow : false});
  	}
  	return queryStatus;
  },
  
  //Pick a query result based on list priority
  _chooseResultFromQueries : function (queryStatus, listType) {
  	var chosenIndex = 0;
  	var comparisonField = listType == hr_DashboardUtil.LIST_CLOSED ? 'closed_at' : 'sys_created_on';
  	var currentQueryDateTime = new GlideDateTime();
  	var maxDateTime= new GlideDateTime(this._getDateForRelevantRecord(queryStatus[0].query, comparisonField));
  	
  	for (i = 1; i < queryStatus.length; i++) {
  		currentQueryDateTime.setValue(this._getDateForRelevantRecord(queryStatus[i].query, comparisonField));
  		
  		if (currentQueryDateTime.compareTo(maxDateTime) == 1) {
  			maxDateTime.setValue(currentQueryDateTime.getValue());
  			chosenIndex = i;
  		}
  	}
  	
  	return chosenIndex;
  },
  
  //Increment the query; grab the next window if this one is exhausted
  _moveQueryPointer : function (queryStatus, selectedIndex, parentCaseIds) {
  	var gr = queryStatus[selectedIndex].queryFunction(parentCaseIds.excludedRecords, parentCaseIds.excludedParents);
  	
  	if (queryStatus[selectedIndex].resetWindow) {
  		queryStatus[selectedIndex].window = 0;
  		queryStatus[selectedIndex].resetWindow = false;
  	}
  	else
  		queryStatus[selectedIndex].window += hr_DashboardUtil.QUERY_LIMIT;
  	
  	gr.chooseWindow(queryStatus[selectedIndex].window, queryStatus[selectedIndex].window + hr_DashboardUtil.QUERY_LIMIT);
  	gr.query();
  	
  	//Remove query if it has run out
  	if (!gr.next())
  		queryStatus.splice(selectedIndex, 1);
  	else 
  		queryStatus[selectedIndex].query = gr;
  },
  
  /*---------------------* Helper Functions *---------------------*/
  
  _getDateForRelevantRecord : function(gr, field) {
  	if (gr.getRecordClassName() == 'sysapproval_approver')
  		return gr.sysapproval[field];
  	else if (gr.getRecordClassName() == 'sn_hr_core_task')
  		return gr.parent[field];
  	else
  		return gr[field];
  },
  
  _getRelevantRecord : function (gr) {
  	var relevantRecord ;
  	
  	if (gr.getRecordClassName() == 'sysapproval_approver')
  		relevantRecord = gr.sysapproval.getRefRecord();
  	else if (gr.getRecordClassName() == 'sn_hr_core_task')
  		relevantRecord = gr.parent.getRefRecord();
  	else 
  		relevantRecord = gr;
  	
  	if (relevantRecord.isValidRecord() && (hr_DashboardUtil.hrCaseTables.indexOf(relevantRecord.getRecordClassName()) || relevantRecord.canRead()))
  		return relevantRecord;
  	
  	return null;
  },
  
  _getGroupByCase : function(gr, level, skipParentDisplay) {
  	level = level || 3;
  	var isCase = hr_DashboardUtil.hrCaseTables.indexOf(String(gr.sys_class_name)) >= 0;
  	var selfCheck = {record : gr, isOpenedForCase : isCase && gr.opened_for == gs.getUserID(), isAuthorizedSPCase : isCase && sn_hr_core.hr_Case.userHasSubjectPersonAccess(gr)};

  	if (gs.nil(gr.parent) || level == 1)
  		return selfCheck;

  	var parentCheck = this._getGroupByCase(gr.parent.getRefRecord(), level - 1);
  	var parentIsGroupBy = parentCheck.isOpenedForCase || parentCheck.isAuthorizedSPCase;

  	//Show by Opened For parent if that parent is not intermediate to another Opened For case
  	if (parentIsGroupBy && (skipParentDisplay || this._getGroupByCase(parentCheck.record, null, true).record.getUniqueValue() == parentCheck.record.getUniqueValue()))
  		return parentCheck;
  	else
  		return selfCheck;
  },
  
  getHeaderTodoCountForCase : function(recordId, isOpenedForCase) {
  	var todoCount = 0;
  	var todoQueries = [this._approvalTodoQuery(false, true, recordId, isOpenedForCase, false, true), 
  					   this._acceptanceTodoQuery(recordId, isOpenedForCase, false, true), 
  					   this._taskTodoQuery(null, false, true, recordId, isOpenedForCase, false, true)];
  	
  	for (var i = 0; i < todoQueries.length; i++) {
  		var todoQuery = todoQueries[i]();
  		todoQuery.query();
  		
  		if (todoQuery.next())
  			todoCount += Number(todoQuery.getAggregate('COUNT'));
  	}
  	
  	return todoCount;
  },
  
  getHeaderTodoCount : function() {
  	var todoCount = 0;
  	var todoQueries = [this._approvalTodoQuery(false, false, null, null, false, true), 
  					   this._acceptanceTodoQuery(null, null, false, true), 
  					   this._taskTodoQuery(null, false, false, null, null, false, true)];
  	
  	for (var i = 0; i < todoQueries.length; i++) {
  		var todoQuery = todoQueries[i]();
  		todoQuery.query();
  		
  		if (todoQuery.next())
  			todoCount += Number(todoQuery.getAggregate('COUNT'));
  	}
  	
  	return todoCount;
  },
  
  _hasAnyTodos : function(recordId, isOpenedForCase) {	
  	var todoQueries = [this._taskTodoQuery(null, null, null, recordId, isOpenedForCase, true, false)];
  	
  	if (this._user == gs.getUserID()) {
  		todoQueries.push(this._approvalTodoQuery(null, null, recordId, isOpenedForCase, true, false));
  		todoQueries.push(this._acceptanceTodoQuery(recordId, isOpenedForCase, true, false));
  	}
  	
  	for (var i = 0; i < todoQueries.length; i++) {
  		var todoQuery = todoQueries[i]();
  		todoQuery.query();
  		
  		if (todoQuery.hasNext())
  			return true;
  	}
  	
  	return false;
  },
  
  _hasActiveTodos : function(recordId, isOpenedForCase) {
  	var todoQueries = [this._approvalTodoQuery(false, true, recordId, isOpenedForCase, true, false), 
  					   this._acceptanceTodoQuery(recordId, isOpenedForCase, true, false), 
  					   this._taskTodoQuery(null, false, true, recordId, isOpenedForCase, true, false)];
  	
  	for (var i = 0; i < todoQueries.length; i++) {
  		var todoQuery = todoQueries[i]();
  		todoQuery.query();
  		
  		if (todoQuery.hasNext())
  			return true;
  	}
  	
  	return false;
  },
  
  _hasLifecycleEvents: function(gr) {
  	if (hr_DashboardUtil.hrCaseTables.indexOf(String(gr.sys_class_name)) == -1)
  		return false;
  	
  	this._portalUtil._gr = gr;
  	
  	return this._portalUtil.isOpenedForView() && this._portalUtil.hasLifecycleEvents();
  },
  
  _reconcileDisplayItem : function(gr, parentCaseIds, listType) {
  	var baseRecord = this._getRelevantRecord(gr);
  	
  	if (baseRecord) {
  		var sys_id = baseRecord.getUniqueValue();

  		if (parentCaseIds.excludedRecords.indexOf(sys_id) >= 0)
  			return false;
  		else
  			parentCaseIds.excludedRecords.push(sys_id);

  		var alreadyIncluded = false;
  		var relevantParent = this._getGroupByCase(baseRecord);
  		this._portalUtil._gr = relevantParent.record;
  		var parentId = relevantParent.record.getUniqueValue();
  		var isGroupByCase = relevantParent.isOpenedForCase || relevantParent.isAuthorizedSPCase;
  		
  		for (var i = 0; i < parentCaseIds.recordsToShow.length; i++) {
  			if (parentCaseIds.recordsToShow[i].sys_id == parentId) {
  				alreadyIncluded = true;
  				break;
  			}
  		}
  		
  		if (!alreadyIncluded) {
  			parentCaseIds.excludedRecords.push(parentId);

  			if (isGroupByCase)
  				parentCaseIds.excludedParents.push(parentId);
  			
  			//Show To-Do cases in Open Cases list
  			var satisfiesListCriteria = (listType == hr_DashboardUtil.LIST_CLOSED && !relevantParent.record.active) || 
  										(listType != hr_DashboardUtil.LIST_CLOSED && relevantParent.record.active);

  			//Do not show Subject Person cases that have no associated records beneath
  			var notSoloSPCase = listType == hr_DashboardUtil.LIST_TODO || !relevantParent.isAuthorizedSPCase || relevantParent.isOpenedForCase || new hr_PortalUtil(relevantParent.record).getChildTodoInfo().length > 0;

  			if (satisfiesListCriteria && notSoloSPCase) {
  				var recordInfo = this._portalUtil._getRelevantInfoForRecord(relevantParent.record);
  				recordInfo.hasLifecycleEvents = this._hasLifecycleEvents(relevantParent.record);
  				
  				parentCaseIds.recordsToShow.push(recordInfo);
  			}
  		}

  		return true;
  	}
  	
  	return false;
  },
  
  /*------------------------* Queries *------------------------*/
  
  _acceptanceTodoQuery : function(parentId, parentIsOpenedFor, limitToOne, isAggregate) {
  	var self = this;
  	
  	return function(excludes, parentExcludes) {
  		var acceptanceCases = new GlideRecord('sn_hr_core_case');
  		
  		if (isAggregate) {
  			acceptanceCases = new GlideAggregate('sn_hr_core_case');
  			acceptanceCases.addAggregate('COUNT');
  		}
  		
  		acceptanceCases.addQuery('opened_for', self._user);
  		acceptanceCases.addQuery('active', true);
  		acceptanceCases.addQuery('state', '20');
  		
  		if (parentId) {
  			var condition = acceptanceCases.addQuery('sys_id', parentId);
  			if (parentIsOpenedFor)
  				condition.addOrCondition('parent', parentId).addOrCondition('parent.parent', parentId);
  			
  			if (limitToOne)
  				acceptanceCases.setLimit(1);
  		}
  		else {
  			if (excludes && excludes.length > 0)
  				acceptanceCases.addQuery('sys_id', 'NOT IN', excludes);
  			if (parentExcludes && parentExcludes.length > 0)
  				acceptanceCases.addNullQuery('parent').addOrCondition('parent.sys_id', 'NOT IN', parentExcludes);
  		}
  		
  		if (!isAggregate)
  			acceptanceCases.orderBy('sys_created_on');

  		return acceptanceCases;
  	};
  },

  _approvalTodoQuery : function(checkCompleted, checkParentCompleted, parentId, parentIsOpenedFor, limitToOne, isAggregate) {
  	var self = this;
  	
  	return function(excludes, parentExcludes) {
  		var grApproval = new GlideRecord('sysapproval_approver');
  		
  		if (isAggregate) {
  			grApproval = new GlideAggregate('sysapproval_approver');
  			grApproval.addAggregate('COUNT');
  		}
  		
  		if (checkCompleted)
  			grApproval.addQuery('approver', self._user);
  		else
  			grApproval.addQuery('approver', 'IN' , new sn_hr_core.hr_Utils().getApprovals(self._user));
  		
  		if (checkCompleted) {
  			grApproval.addQuery('state','IN','approved,	rejected,not_required');
  			if (checkParentCompleted)
  				grApproval.orderByDesc('sysapproval.closed_at');
  			else
  				grApproval.orderByDesc('sysapproval.sys_created_on');
  		}
  		else if (!gs.nil(checkCompleted)) {
  			grApproval.addQuery('state', 'requested');
  			if (!isAggregate)
  				grApproval.orderBy('sys_created_on');
  		}
  		
  		if (parentId) {
  			var condition = grApproval.addQuery('sysapproval', parentId);
  			if (parentIsOpenedFor)
  				condition.addOrCondition('sysapproval.parent', parentId).addOrCondition('sysapproval.parent.parent', parentId);
  			
  			if (limitToOne)
  				grApproval.setLimit(1);
  		}
  		else {
  			grApproval.addQuery('sysapproval.sys_class_name', 'IN', hr_DashboardUtil.approvalTables);
  			
  			if (checkParentCompleted)
  				grApproval.addQuery('sysapproval.active', 'false');
  			else if (!gs.nil(checkParentCompleted))
  				grApproval.addQuery('sysapproval.active', 'true');
  			
  			if (excludes && excludes.length > 0)
  				grApproval.addQuery('sysapproval.sys_id', 'NOT IN', excludes);
  			if (parentExcludes && parentExcludes.length > 0)
  				grApproval.addNullQuery('sysapproval.parent').addOrCondition('sysapproval.parent.sys_id', 'NOT IN', parentExcludes);
  		}
  		
  		return grApproval;
  	};
  },
  
  _taskTodoQuery : function(filterDueDate, checkCompleted, checkParentCompleted, parentId, parentIsOpenedFor, limitToOne, isAggregate) {
  	var self = this;
  	
  	return function(excludes, parentExcludes) {
  		var grTask = new GlideRecord('sn_hr_core_task');
  		
  		if (isAggregate) {
  			grTask = new GlideAggregate('sn_hr_core_task');
  			grTask.addAggregate('COUNT');
  		}
  		
  		grTask.addQuery('assigned_to', self._user);
  		grTask.addQuery('state', '!=', '1');
  		
  		if (checkCompleted) {
  			grTask.addQuery('active', false);
  			grTask.addNotNullQuery('closed_at');
  			
  			if (checkParentCompleted)
  				grTask.orderByDesc('parent.closed_at');
  			else
  				grTask.orderByDesc('parent.sys_created_on');
  		}
  		else if (!gs.nil(checkCompleted)) {
  			grTask.addQuery('active', true);
  			
  			if (!gs.nil(filterDueDate)) {
  				if (filterDueDate)
  					grTask.addNullQuery('due_date');
  				else {
  					grTask.addNotNullQuery('due_date');
  					grTask.orderBy('due_date');
  				}
  			}
  		}
  		
  		if (parentId) {
  			var condition = grTask.addQuery('parent', parentId);
  			if (parentIsOpenedFor)
  				condition.addOrCondition('parent.parent', parentId).addOrCondition('parent.parent.parent', parentId);
  			
  			if (limitToOne)
  				grTask.setLimit(1);
  		} 
  		else {
  			grTask.addQuery('parent.sys_class_name', 'IN', hr_DashboardUtil.hrCaseTables);
  			grTask.addQuery('parent.state', '!=', '1');
  			
  			if (checkParentCompleted)
  				grTask.addQuery('parent.active', 'false');
  			else if (!gs.nil(checkParentCompleted))
  				grTask.addQuery('parent.active', 'true');
  			
  			if (excludes && excludes.length > 0)
  				grTask.addQuery('parent.sys_id', 'NOT IN', excludes);
  			if (parentExcludes && parentExcludes.length > 0)
  				grTask.addNullQuery('parent.parent').addOrCondition('parent.parent.sys_id', 'NOT IN', parentExcludes);
  		}

  		return grTask;
  	};
  },
  
  _openedForCaseQuery : function(checkCompleted) {
  	var self = this;
  	
  	return function(excludes, parentExcludes) {
  		var grCase = new GlideRecord('sn_hr_core_case');
  		grCase.addQuery('opened_for', self._user).addOrCondition('hr_service.subject_person_access', true).addCondition('subject_person', self._user);
  		grCase.addQuery('state', '!=', '1');
  		
  		if (checkCompleted) {
  			grCase.addQuery('active', false);
  			grCase.addNotNullQuery('closed_at');
  			grCase.orderByDesc('closed_at');
  		}
  		else {
  			grCase.addActiveQuery();
  			grCase.orderByDesc('sys_created_on');
  		}
  		
  		if (excludes && excludes.length > 0)
  			grCase.addQuery('sys_id', 'NOT IN', excludes);
  		if (parentExcludes && parentExcludes.length > 0)
  			grCase.addNullQuery('parent').addOrCondition('parent.sys_id', 'NOT IN', parentExcludes);
  		
  		return grCase;
  	};
  },
  
  _openedForOrderQuery : function(checkCompleted) {
  	var self = this;
  	
  	return function(excludes, parentExcludes) {
  		var grOrder = new GlideRecord('sm_order');
  		grOrder.addQuery('opened_for', self._user).addOrCondition('caller', self._user);									   
  		grOrder.addQuery('state', '!=', '1');
  		
  		if (checkCompleted) {
  			grOrder.addQuery('active', false);
  			grOrder.addNotNullQuery('closed_at');
  			grOrder.orderByDesc('closed_at');
  		}
  		else {
  			grOrder.addActiveQuery();
  			grOrder.orderByDesc('sys_created_on');
  		}
  		
  		if (excludes && excludes.length > 0)
  			grOrder.addQuery('sys_id', 'NOT IN', excludes);
  		if (parentExcludes && parentExcludes.length > 0)
  			grOrder.addNullQuery('parent').addOrCondition('parent.sys_id', 'NOT IN', parentExcludes);
  		
  		return grOrder;
  	};
  },
  
  _requestedForQuery : function(checkCompleted) {
  	var self = this;
  	
  	return function(excludes, parentExcludes) {
  		var grRequest = new GlideRecord('sc_request');
  		grRequest.addQuery('requested_for', self._user);
  		
  		if (checkCompleted) {
  			grRequest.addQuery('active', false);
  			grRequest.addNotNullQuery('closed_at');
  			grRequest.orderByDesc('closed_at');
  		}
  		else {
  			grRequest.addActiveQuery();
  			grRequest.orderByDesc('sys_created_on');
  		}
  		
  		if (excludes && excludes.length > 0)
  			grRequest.addQuery('sys_id', 'NOT IN', excludes);
  		if (parentExcludes && parentExcludes.length > 0)
  			grRequest.addNullQuery('parent').addOrCondition('parent.sys_id', 'NOT IN', parentExcludes);

  		return grRequest;
  	};
  },

  type: 'hr_DashboardUtil'
};

Sys ID

d6d8fe05533132003585c3c606dc34b6

Offical Documentation

Official Docs: