Name
sn_hr_core.hr_CaseAjax
Description
Handles HR Case Ajax requests.
Script
/**
* hr_CaseAjax
*
* Wraps all ajax request methods used by HR Core.
*/
var hr_CaseAjax = Class.create();
hr_CaseAjax.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {
initialize: function(request, responseXML, gc) {
global.AbstractAjaxProcessor.prototype.initialize.call(this, request, responseXML, gc);
},
/**
* getMLPredictorResults - Get the predictions for the useCase of case
* @private - called from getCasePredictions
* Security requirements (implied): user has case writer role
* @params:
* caseId - HR Case sys_id
* useCase - HR AI configuration Use Case name
* domain
* predicted output value from the ml_predictor_results table
*/
getMLPredictorResults: function(caseID, useCase, domain) {
// fetch solution from HR Ai condfig
var aiConfig = new GlideRecord('sn_hr_core_ai_configuration');
aiConfig.addQuery('use_case', useCase);
aiConfig.addQuery('sys_domain', domain);
aiConfig.query();
var solutionDef;
if (aiConfig.next())
solutionDef = aiConfig.solution_capability_definition;
else {
aiConfig = new GlideRecord('sn_hr_core_ai_configuration');
aiConfig.addQuery('use_case', useCase);
aiConfig.addQuery('sys_domain', 'global');
aiConfig.query();
aiConfig.next();
solutionDef = aiConfig.solution_capability_definition;
}
if (gs.nil(solutionDef))
return;
// Return if solution definition is not active
if (!aiConfig.solution_capability_definition.active)
return;
// fetch the solution and pass solution id
var sol = new GlideRecord('ml_solution');
sol.addQuery('active', true);
sol.addQuery('solution_name', aiConfig.solution_capability_definition.solution_name);
sol.query();
if (!sol.next())
return;
var predictions = {};
// fetch predicted results
var predictorResult = new GlideRecord('ml_predictor_results');
predictorResult.addQuery('source_sys_id', caseID);
predictorResult.addQuery('solution', sol.sys_id);
predictorResult.orderByDesc('sys_created_on');
predictorResult.setLimit(1);
predictorResult.query();
if (predictorResult.next()) {
predictions[useCase] = predictorResult.predicted_output_value;
}
return predictions;
},
/**
* Get the Predicted HR Service for the given HR Case
* Security requirements (from getHRServicePredictions): user can write to case and read the predicted service
*/
getPredictionsForCaseTransfer: function() {
var domain = this.getParameter('sysparm_domain');
var subject_person = this.getParameter('sysparm_subject_person');
var sys_id = this.getParameter('sysparm_sys_id');
return this.getHRServicePredictions(sys_id, subject_person, domain);
},
/**
* @private - called from getPredictionsForCaseTransfer
* Security requirements: user can write to case and read the predicted service
*/
getHRServicePredictions: function(caseId, subjectPerson, domain) {
var hrCase = new GlideRecord('sn_hr_core_case');
var predictions = {};
if (hrCase.get(caseId) && hrCase.canWrite()) {
if (!gs.nil(hrCase.predicted_hr_service)) {
var gr = new GlideRecord('sn_hr_core_service');
if (gr.get(hrCase.predicted_hr_service) && gr.canRead()) {
if (gr.getValue('active') == 1) {
var coe_display = gr.getValue('service_table');
var service = gr.getValue('name');
var service_id = gr.getUniqueValue();
predictions = {
coe_display: coe_display,
service_display: service,
service_id: service_id
};
if (predictions && new sn_hr_core.hr_CaseCreation().hasAccessForService(subjectPerson, true, predictions.service_id))
return JSON.stringify(predictions);
}
}
} else {
predictions = this.getHRCasePredictions(hrCase, domain, hr.CASE_CATEGORIZATION);
if (predictions && new sn_hr_core.hr_CaseCreation().hasAccessForService(subjectPerson, true, predictions.service_id)) {
hrCase.setValue('predicted_hr_service', predictions.service_id);
hrCase.update();
return JSON.stringify(predictions);
}
}
}
},
/**
* Security requirements: none
*/
getPredictions: function(hrCaseGr) {
if (!(new GlidePluginManager().isActive('com.glide.platform_ml')))
return;
var predictions = new sn_hr_core.HRMLUtils().getHRPredictions(hrCaseGr);
return predictions;
},
/**
* @private (unused OOB)
* Security requirements: user has case writer role
*/
predict: function(short_description, description, domain) {
var hr_case = new GlideRecord('sn_hr_core_case');
hr_case.short_description = short_description;
hr_case.description = description;
if (!(new GlidePluginManager().isActive('com.glide.platform_ml')))
return hr_case;
if (!gs.hasRole('sn_hr_core.case_writer'))
return hr_case;
var predictor = new global.MLPredictor();
var gr = new GlideRecord('sn_hr_core_ai_configuration'); //configuration table
gr.addQuery('use_case', 'email_categorization');
gr.addQuery('sys_domain', domain);
gr.query();
// No need for canRead here. This is just a configuration record that determines which solution def to use.
if (!gr.next()) {
gr = new GlideRecord('sn_hr_core_ai_configuration');
gr.addQuery('use_case', 'email_categorization');
gr.addQuery('sys_domain', 'global');
gr.query();
if (!gr.next())
return hr_case;
}
var name = gr.solution_capability_definition.solution_name.toString(); //Get solution name from configuration
var solution = predictor.findActiveSolution(name);
if (solution)
predictor.applyPredictionForSolution(hr_case, solution);
return hr_case;
},
/**
* Security requirements: none
*/
getHRCasePredictions: function(hrCase, domain, usecase) {
if (!(new GlidePluginManager().isActive('com.glide.platform_ml')))
return false;
var hrServiceId = this.hrCasePredict(hrCase, domain, usecase);
if (gs.nil(hrServiceId))
return false;
var gr = new GlideRecord('sn_hr_core_service');
if (gr.get(hrServiceId) && gr.canRead()) {
if (gr.getValue('active') == 1) {
var coe_display = gr.getValue('service_table');
var service = gr.getValue('name');
var service_id = hrServiceId;
var predictions = {
coe_display: coe_display,
service_display: service,
service_id: service_id
};
return predictions;
}
} else
return false;
},
/**
* @private - called from getHRCasePredictions
* Security requirements (implied) - user can read case and predicted service
*/
hrCasePredict: function(hrCase, domain, usecase) {
if (!(new GlidePluginManager().isActive('com.glide.platform_ml')))
return;
// Look for configuration in @param domain, otherwise check 'global'
var hrAIConfiguration = new GlideRecord('sn_hr_core_ai_configuration');
hrAIConfiguration.addQuery('use_case', usecase);
hrAIConfiguration.addQuery('sys_domain', domain);
hrAIConfiguration.addActiveQuery();
hrAIConfiguration.query();
if (!hrAIConfiguration.next()) {
hrAIConfiguration = new GlideRecord('sn_hr_core_ai_configuration');
hrAIConfiguration.addQuery('use_case', usecase);
hrAIConfiguration.addQuery('sys_domain', 'global');
hrAIConfiguration.addActiveQuery();
hrAIConfiguration.query();
if (!hrAIConfiguration.next())
return;
}
var solutionDefinition = new GlideRecord('ml_capability_definition_classification');
if (!solutionDefinition.get(hrAIConfiguration.solution_capability_definition))
return;
try {
var mlSolution = sn_ml.ClassificationSolutionStore.get(hrAIConfiguration.solution_capability_definition.solution_name.toString());
if (gs.nil(mlSolution)) {
return;
}
var options = {};
options.mluc = "MLUC-HR-00003";
options.top_n = 1;
options.apply_threshold = true;
var result = mlSolution.getActiveVersion().predict(hrCase, options);
if (result === null) {
//instead of returning null returning empty array
return [];
}
var resultObj = JSON.parse(result);
var keys = Object.keys(resultObj);
if (gs.nil(keys)||resultObj[keys[0]].length===0) {
return;
}
var hrServiceSysId = resultObj[keys[0]][0]["predictedSysId"];
return hrServiceSysId;
} catch (err) {
var logError = function(err) {
gs.error(err);
};
gs.getMessage('Error while predicting: {0}', err.toString(), logError);
}
},
/**
* Returns null if the capability field is empty, otherwise returns the capability of the solution
*/
_validSolutionCapability: function(solution) {
var capability = solution.getCapability();
if ((capability == null) || (typeof capability == 'undefined') || ('' == '' + capability))
return null;
return capability;
},
/**
* Security requirements: none
*/
getDefaultVIPPriority: function() {
var vipPriority = hr.DEFAULT_HIGH_PRIORITY;
var vipPropertyName = 'sn_hr_core.hr_vip_default_priority';
var priority = gs.getProperty(vipPropertyName);
vipPriority = priority ? priority : vipPriority;
return vipPriority;
},
_getDefaultPriorityFromService: function(service) {
var grService = new GlideRecord('sn_hr_core_service');
if (grService.get(service) && grService.canRead()) {
var priorityFromTemplate = new sn_hr_core.hr_TemplateUtils()._getTemplateProperty(grService.template, 'priority');
if (priorityFromTemplate != null)
return priorityFromTemplate;
}
},
/**
* Security requirements: can read the user records of `sysparm_users` or
* can read HR service (sysparm_service) default priority or
* can create a new case
*/
getPriority: function() {
var service = this.getParameter('sysparm_service');
var usersParm = this.getParameter('sysparm_users');
var users = usersParm ? new global.JSON().decode(usersParm) : [];
if (this._containsVipUser(users))
return this.getDefaultVIPPriority();
else if (service) {
var priority = this._getDefaultPriorityFromService(service);
if (!priority) {
var glideRecord = new GlideRecord("sn_hr_core_case");
if (glideRecord.canCreate()) {
glideRecord.newRecord();
priority = glideRecord.getValue('priority');
}
}
return priority;
}
},
/**
* Security requirements: can read HR Profile represented by userSysId
*/
getNoticePeriod: function(userSysId) {
if (!userSysId)
return '';
var hrProfile = new GlideRecord(hr.TABLE_PROFILE);
hrProfile.addQuery('user', userSysId);
hrProfile.query();
if (hrProfile.next() && hrProfile.canRead())
return hrProfile.notice_period + '';
else
return '';
},
/**
* Security requirements: can read HR position represented by positionSysId
*/
getDepartment: function(positionSysId) {
if (!positionSysId)
return {};
var department = {};
var position = new GlideRecord(hr.TABLE_POSITION);
position.addQuery('sys_id', positionSysId);
position.query();
if (position.next() && position.canRead()) {
department.name = 'department';
department.value = position.department + '';
department.displayValue = position.getDisplayValue('department') + '';
}
return department;
},
/**
* Security requirements: can read department represented by departmentSysId
*/
getManager: function(departmentSysId) {
if (!departmentSysId)
return {};
var manager = {};
var department = new GlideRecord(hr.TABLE_DEPARTMENT);
department.addQuery('sys_id', departmentSysId);
department.query();
if (department.next() && department.canRead()) {
manager.name = 'manager';
manager.value = department.dept_head + '';
manager.displayValue = department.getDisplayValue('dept_head') + '';
}
return manager;
},
/**
* Security requirements: none
*/
isPhoneNumberValid: function(number) {
if (gs.nil(number)) {
number = this.getParameter('sysparm_phoneNumber');
}
if (gs.nil(number)) {
return new global.JSON().encode({
valid: false,
number: ''
});
}
var invalidResultsArray = [];
if ((number + "").indexOf(',') > 0) {
var numbers = (number + "").split(',');
for (var num in numbers) {
if (gs.nil(numbers[num])) {
invalidResultsArray.push(false);
}
if (numbers[num]) {
var data = JSON.parse(this._validateNumber(numbers[num]));
if (!data.valid)
return new global.JSON().encode({
valid: false
});
}
}
if (invalidResultsArray.length > 0) {
return new global.JSON().encode({
valid: false
});
}
return new global.JSON().encode({
valid: true
});
} else {
return this._validateNumber(number);
}
},
_validateNumber: function(number) {
var record = new GlideRecord('sn_hr_core_profile');
if (!record.canRead())
return;
record.initialize();
// home_phone is used only for validating any phone number
var gePN = record.home_phone;
var data = {};
data.valid = gePN.setPhoneNumber(number + "", true);
if (!data.valid) {
var numberWithUsersCode = this.getUsersCountryCode() + number;
data.valid = gePN.setPhoneNumber(numberWithUsersCode, true);
}
if (data.valid) {
data.number = gePN.getGlobalDisplayValue();
} else {
data.number = '';
}
return new global.JSON().encode(data);
},
/**
* Get the country code for the current logged-in user
* Security requirements: none (assumes user can read their own user record)
*/
getUsersCountryCode: function() {
var defaultCode = '+1';
var user = new GlideRecord('sys_user');
user.get(gs.getUserID());
var country = user.getDisplayValue('country');
if (country) { //Users having country code 'US' have country value null.
var prefix = '+';
var code = new GlideRecord('sys_phone_territory');
code.get('name', country);
return prefix + code.ccc;
}
return defaultCode;
},
_inactivePreviousSignature: function(tableName, documentId, keepLastSignActive) {
var record = new GlideRecordSecure(tableName);
if (record.get(documentId) && !record.active) {
return; //don't delete signature for completed tasks
}
var gr = new GlideRecord('signature_image');
gr.addQuery('user', gs.getUserID());
gr.addQuery('table', tableName);
gr.addQuery('document', documentId);
gr.addActiveQuery();
gr.orderByDesc('signed_on');
gr.query();
if (keepLastSignActive)
gr.next();
while (gr.next()) {
gr.setValue('active', false);
gr.update();
}
},
/**
* Security requirements: can read and write to the record represented by tableName and tableId
*/
documentBody: function(tableName, tableId, targetTable, targetId, canEdit, keepLastSignActive) {
// Ensure you can read and write to the target record, unless the target record does not exist
var task = new GlideRecord(targetTable);
var tableGr = new GlideRecord(tableName);
if (task.isValid() && task.get(targetId)) {
if (!(task.canRead() && task.canWrite()))
return {};
} else if (tableGr.isValid() && tableGr.get(tableId)) {
if (!(tableGr.canRead() && tableGr.canWrite()))
return {};
} else
return {};
if (canEdit == 'true')
this._inactivePreviousSignature(tableName, tableId, keepLastSignActive);
var hrform = new GeneralHRForm(tableName, tableId, targetTable, targetId);
return canEdit == 'true' ? hrform : {
body: hrform.remove_all_variables(hrform.body)
};
},
/**
* Security requirements: can read and write to the record represented by tableName and tableId
*/
setDocumentBody: function(documentBody, tableName, tableId, targetTable, targetId, canEdit, keepLastSignActive) {
var response = {};
// Ensure you can read and write to the target record, unless the target record does not exist
var task = new GlideRecord(targetTable);
var tableGr = new GlideRecord(tableName);
if (task.isValid() && task.get(targetId)) {
if (!(task.canRead() && task.canWrite()))
return {
error: gs.getMessage('Invalid user. User does not have read and write access to {0}', targetTable)
};
} else if (tableGr.isValid() && tableGr.get(tableId)) {
if (!(tableGr.canRead() && tableGr.canWrite()))
return {
error: gs.getMessage('Invalid user. User does not have read and write access to {0}', tableName)
};
} else {
return {
error: gs.getMessage('Invalid target record.')
};
}
if (documentBody.trim() == '')
gs.debug("[setDocument] The document is empty, returning");
else {
if (canEdit == 'false') {
var templateBody = this.documentBody(tableName, tableId, targetTable, targetId, 'true', keepLastSignActive);
documentBody = templateBody.body;
}
var draftId;
var gr = new GlideRecord("draft_document");
gr.addQuery('table', tableName);
gr.addQuery('document', tableId);
gr.addActiveQuery();
gr.query();
if (gr.next() && gr.canWrite()) {
draftId = gr.getUniqueValue();
gr.setValue('body', documentBody);
gr.body.setDisplayValue(documentBody);
gr.target_table = targetTable;
gr.target_id = targetId;
gr.update();
response = {
success: gs.getMessage('Document draft updated.')
};
} else {
gr.initialize();
gr.table = tableName;
gr.document = tableId;
gr.body = documentBody;
gr.target_table = targetTable;
gr.target_id = targetId;
gr.user = gs.getUserID();
draftId = gr.insert();
//Update the draft_document reference in HR CASE if a row is getting inserted in draft_document table for case.
var caseId = '';
if (new hr_CoreUtils().isCase(tableName)) {
var caseGr = new GlideRecord(tableName);
caseGr.get(tableId);
if (!gs.nil(draftId)) {
caseGr.setValue('draft_document', draftId);
caseId = caseGr.update();
}
}
response = {
success: caseId ? gs.getMessage('Document draft created and HR case document reference updated.') : gs.getMessage('Document draft created.')
};
}
return response;
}
},
/**
* Tests for an sn_hr_core_profile/sys_user record that contains the specified UserID
* Security requirements: can read HR Profile represented by sysparm_user or can
* create an HR profile
*
* @param user user field of record being searched
* @return sys_id of sn_hr_core_profile if record exists for this field combination,
* empty string otherwise
*/
ajaxFunction_createOrGetProfileFromUser: function() {
var userId = this.getParameter('sysparm_user');
if (!userId) {
gs.debug("[getProfileFromUser] Didn't receive any parameters, returning");
return;
}
var hrProfile = new GlideRecord(hr.TABLE_PROFILE);
if (hrProfile.get('user', userId))
return hrProfile.canRead() ? hrProfile.sys_id : '';
else if (hrProfile.canCreate())
return this._createProfileFromUser(userId);
},
/**
* Creates profile when called from HR case form
*/
_createProfileFromUser: function(userId) {
var gr = new GlideRecord('sn_hr_core_profile');
if (!gr.canCreate()) {
gs.debug("[createProfileFromUser] No create access granted to sn_hr_core_profile table");
return;
} else if (!userId) {
gs.debug("[createProfileFromUser] Didn't receive any parameters, returning");
return;
}
var user = this._getUserNameFromSysId(userId);
gs.addInfoMessage(gs.getMessage('HR profile is created for {0}', user));
var answer = new hr_Profile().createProfileFromUser(userId);
return answer;
},
/**
* Security requirements: sn_hr_core.profile_reader role
*/
ajaxFunction_canAccessProfile: function() {
if (new sn_hr_core.hr_CoreUtils().impersonateCheck())
return false;
var roles = new hr_Utils();
if (roles.checkUserHasRole(hr.ROLE_HR_PROFILE_READER))
return true;
var profileId = this.getParameter('sysparm_profile');
var caseId = this.getParameter('sysparm_caseId');
if (gs.nil(profileId)) {
if (gs.getUserID() == this._getOpenedForFromCaseId(caseId))
return true;
profileId = this._getProfileIdFromCaseId(caseId);
if (gs.nil(profileId))
return false;
}
// Check if the profile is related to the current user
var profile = new GlideRecord('sn_hr_core_profile');
if (profile.get(profileId)) {
var profileUser = profile.getValue("user");
if (!gs.nil(profileUser) && (gs.getUserID() == profileUser || new global.HRSecurityUtilsAjax().userReportsTo(profileUser, gs.getUserID(), '')))
return true;
}
return false;
},
_getProfileIdFromCaseId: function(caseId) {
var gr = new GlideRecordSecure('sn_hr_core_case');
if (gs.nil(caseId) || !gr.get(caseId))
return '';
return gr.hr_profile;
},
_getOpenedForFromCaseId: function(caseId) {
var gr = new GlideRecordSecure('sn_hr_core_case');
if (gs.nil(caseId) || !gr.get(caseId))
return '';
return gr.opened_for;
},
/**
* Security requirements: can read variable names from hr_benefit_questions variable set
* @deprecated (unused OOB with HR Scoped: Core)
*/
ajaxFunction_getCatalogVariableNames: function() {
//this._log
// .debug("[getCatalogVariableNames] Retrieving all the variables for the hr_case_management variable set");
var answer = {
variables: []
};
var gr = new GlideRecord("item_option_new");
gr.addQuery("variable_set.name", "hr_benefit_questions");
gr.query();
while (gr.next()) {
if (gr.canRead())
answer.variables.push(gr.name + "");
}
if (gs.isDebugging())
gs.debug("[getCatalogVariableNames] variables -- " + answer.variables.join(", "));
return new global.JSON().encode(answer);
},
/**
* Security requirements: sn_hr_core.case_writer role
*/
getTemplateFields: function() {
if (!new hr_Utils().checkUserHasRole('sn_hr_core.case_writer'))
return;
var templateSysId = this.getParameter('sysparm_template_sys_id');
var usersParm = this.getParameter('sysparm_users');
var users = usersParm ? new global.JSON().decode(usersParm) : [];
var templateData = new sn_hr_core.hr_TemplateUtils()._getTemplateData(templateSysId);
// g_form does not fully support setValue(field, "javascript:...")
for (var key in templateData)
if (String(templateData[key].value).indexOf("javascript:") > -1)
delete templateData[key];
if (templateData.hasOwnProperty("priority") && this._containsVipUser(users))
templateData["priority"] = this.getDefaultVIPPriority();
return new global.JSON().encode(templateData);
},
_containsVipUser: function(userArr) {
var userGr = new GlideRecord("sys_user");
for (var i = 0; i < userArr.length; i++)
if (userArr[i] && userGr.get(userArr[i]) && userGr.canRead() && userGr.vip)
return true;
return false;
},
/**
* Security requirements: user can read topic detail represented by sysparm_detail_sys_id
*/
getCoeFromDetail: function() {
var detailId = this.getParameter('sysparm_detail_sys_id');
var detailGr = new GlideRecord("sn_hr_core_topic_detail");
if (detailGr.get(detailId) && detailGr.canRead() && !gs.nil(detailGr.topic_category.coe))
return detailGr.topic_category.coe.toString();
return "";
},
_getData: function(gr) {
var data = {};
var elements = gr.getElements();
for (var i = 0; i < elements.length; i++) {
if (!elements[i].canRead())
continue;
var ed = elements[i].getED();
var name = ed.getName();
data[name] = gr.getValue(name);
}
data['ZZ_YY_display_value'] = gr.getDisplayValue();
return data;
},
/**
* Security requirements: user can read table and records represented by query
*/
getGlideRecordSecureData: function(table, query) {
return this._getGlideRecordSecureData(table, query);
},
/**
* Security requirements: user can read table and records represented by query
*/
getGlideRecordSecureSetData: function(table, query) {
return this._getGlideRecordSecureSetData(table, query);
},
/**
* Security requirements: user can read table and records represented by query
*/
getGlideRecordSecureDataWS: function() {
var table = this.getParameter('sysparm_tableName');
var query = this.getParameter('sysparm_query');
return this._getGlideRecordSecureData(table, query);
},
/**
* Security requirements: user can read table and records represented by query
*/
getGlideRecordSecureSetDataWS: function() {
var table = this.getParameter('sysparm_tableName');
var query = this.getParameter('sysparm_query');
return this._getGlideRecordSecureSetData(table, query);
},
_getGlideRecordSecureData: function(table, query) {
var gr = new GlideRecordSecure(table);
if (gr.get(query))
return new global.JSON().encode(this._getData(gr));
else
return new global.JSON().encode(false);
},
_getGlideRecordSecureSetData: function(table, query) {
var records = [];
var gr = new GlideRecordSecure(table);
gr.addEncodedQuery(query);
gr.query();
while (gr.next())
records.push(this._getData(gr));
return new global.JSON().encode(records);
},
/**
* Sends an event request to reactivate the specified user
* Security requirements: sn_hr_core.profile_writer
*
* @param sysparm_user - id of the user to reactivate
* @return true if the user request has been added to the queue
*/
ajaxFunction_reactivateUser: function() {
var userId = this.getParameter('sysparm_user');
var grProfile = new GlideRecord(hr.TABLE_PROFILE);
if (!userId) {
gs.warn("[reactivateUser] Didn't receive id of user to reactivate, returning");
return false;
} else if (!grProfile.get('user', userId)) {
gs.warn("[reactivateUser] Unable to retrieve profile for user to reactivate, returning");
return false;
} else if (!gs.hasRole(hr.ROLE_HR_PROFILE_WRITER)) {
gs.warn("[reactivateUser] User does not have permission to reactivate the user, returning");
return false;
}
gs.eventQueue("sn_hr_core_profile.reactivate_user", grProfile, '', '');
return true;
},
/**
* Checks whether the user with the sepcified sysId given can be found
* Security requirements: can read user record represented by sysparm_user
*
* @param sysparm_user - id field of record being searched for
* @return true if the record was found, false otherwise
*/
ajaxFunction_checkUserActive: function() {
var userId = this.getParameter('sysparm_user');
if (!userId) {
gs.warn("[checkUserActive] Didn't receive any parameters, returning");
return;
}
var grUser = new GlideRecord('sys_user');
if (!grUser.get(userId) || !grUser.canRead())
return;
if (grUser.getValue('active'))
return true;
return;
},
/**
* Security requirements: can write to sn_hr_core_direct_deposit table records
* represented by sysparm_query; (records where canWrite fails will not be updated)
*/
ajaxFunction_inactivateRecords: function() {
var tableName = this.getParameter('sysparm_tableName');
if (tableName !== 'sn_hr_core_direct_deposit')
return;
var query = this.getParameter('sysparm_query');
var filter = this.getParameter('sysparm_filter');
var re = /^employee=([a-z0-9]{32})$/;
// Only allow queries and filters of the form employee={sys_id}
if (!query.match(re) || !filter.match(re))
return;
var numRecordsDeactivated = 0;
var gr = new GlideRecord(tableName);
gr.addEncodedQuery(query);
gr.addActiveQuery();
gr.query();
while(gr.next()) {
if (!gr.canWrite() || !gr.isValidField('active') || !gr.active.canWrite())
continue;
gr.setValue('active', 'false');
gr.update();
numRecordsDeactivated++;
}
var msg;
if (numRecordsDeactivated == 1)
msg = gs.getMessage('1 record inactivated.');
else
msg = gs.getMessage('{0} records inactivated.', numRecordsDeactivated.toString());
msg += ' ';
var clickHere = gs.getMessage('Click here.');
msg += '<a href="' + tableName + '_list.do?sysparm_query=' + filter + '">' + clickHere + '</a>';
gs.addInfoMessage(msg);
return;
},
/**
* Security requirements: can read task record represented by sys_id and
* its parent task record (if the task is an sn_hr_core_task)
* @deprecated (unused OOB)
*/
getOfficeSpaceParams: function(sys_id) {
var params = {
'user_to_be_moved': '',
'short_description': ''
};
var grTask = new GlideRecord('task');
if (!grTask.get(sys_id + ''))
return params;
var className = grTask.sys_class_name;
if (className == 'sn_hr_core_task') {
grTask = new GlideRecord('sn_hr_core_task');
if (grTask.get(sys_id + '') && grTask.canRead()) {
params["short_description"] = grTask.short_description + '';
var parentCaseGR = new GlideRecord('sn_hr_core_case');
if (parentCaseGR.get(grTask.getValue('parent')) && parentCaseGR.canRead())
params['user_to_be_moved'] = parentCaseGR.subject_person + '';
}
} else {
var hierarchyUtils = new GlideTableHierarchy(grTask.sys_class_name);
var baseTable = hierarchyUtils.getBase();
if (baseTable == 'sn_hr_core_case') {
grTask = new GlideRecord(className + '');
if (grTask.get(sys_id + '') && grTask.canRead()) {
params['user_to_be_moved'] = grTask.subject_person + '';
var tasks = new GlideRecord('sn_hr_core_task');
tasks.addQuery('parent', sys_id);
tasks.query();
while (tasks.next() && tasks.canRead()) {
if (tasks.sc_cat_item) {
// Set params for Select Office Space SC Catalog item
if (tasks.sc_cat_item.sys_id == '65f6ad093b143200705d86a734efc43b')
params['short_description'] = tasks.short_description + '';
}
}
}
}
}
return params;
},
/**
* Security requirements: user can read direct deposits for employee represented in param
*/
directDepositValidation: function(param) {
var record = JSON.parse(param);
var amounts = [];
var percentages = [];
var balances = [];
populateArrays(record);
var deposits = new GlideRecordSecure('sn_hr_core_direct_deposit');
deposits.addActiveQuery();
deposits.addQuery('employee', record.employee);
deposits.addQuery('sys_id', '!=', record.sys_id);
deposits.query();
while (deposits.next()) {
populateArrays(deposits);
}
if (record.deposit_type == 'amount')
return validateAmount(record);
else if (record.deposit_type == 'percentage')
return validatePercentage(record);
else if (record.deposit_type == 'balance')
return validateBalance(record);
function populateArrays(gr) {
if (gr.deposit_type == 'amount')
amounts[gr.sys_id] = gr.deposit_amount;
else if (gr.deposit_type == 'percentage')
percentages[gr.sys_id] = gr.deposit_percentage.toString();
else if (gr.deposit_type == 'balance')
balances.push(gr.sys_id);
else {
return new global.JSON().encode(gs.getMessage("Deposit type is required"));
}
}
function validateAmount(record) {
if (record.deposit_amount == 0 || !record.deposit_amount)
return new global.JSON().encode(gs.getMessage("Deposit amount must be greater than zero"));
if (Object.keys(percentages).length > 0)
return new global.JSON().encode('conflict');
return true;
}
function validatePercentage(record) {
if (!record.deposit_percentage || record.deposit_percentage <= 0 || record.deposit_percentage > 100)
return new global.JSON().encode(gs.getMessage("Deposit percentage must be greater than zero and less than or equal to 100"));
var sum = 0;
for (var key in percentages)
sum += Number(percentages[key]);
if (sum > 100)
return new global.JSON().encode(gs.getMessage("Total direct deposit instructions for {0} exceed 100%", record.employee_name));
else if (sum == 100 && Object.keys(balances).length > 0)
return new global.JSON().encode(gs.getMessage("Total direct deposit instructions for {0} are already 100%", record.employee_name));
if (Object.keys(amounts).length > 0)
return new global.JSON().encode('conflict');
return true;
}
function validateBalance(record) {
if (Object.keys(percentages).length > 0) {
var sum = 0;
for (var key in percentages)
sum += Number(percentages[key]);
if (sum >= 100)
return new global.JSON().encode(gs.getMessage("A balance deposit type cannot be added because {0} has direct deposit instructions totalling 100%", record.employee_name));
}
if (Object.keys(balances).length > 1)
return new global.JSON().encode('conflict');
return true;
}
},
/*
* Returns an array of objects. Each object contains:
* -Field : whose value is update
* -Table : To which table that field belongs to(user or profile)
* -newValue : The value requested by user.
* Security requirements: user can read case represented by sysparm_caseId
*
* @param - Unique value of case record.
* This function uses payload object for finding
* all the modified fields.
*/
getModifiedFields: function() {
var caseId = this.getParameter('sysparm_caseId') + '';
var grCase = new GlideRecord(sn_hr_core.hr.TABLE_CASE);
if (grCase.get(caseId) && grCase.canRead()) {
var modifiedFields = this.getCaseModifiedFields(grCase);
return new global.JSON().encode(modifiedFields);
}
},
/*
* Public method to determine all modified fields from case record
* Security requirements: user can read grCase
*/
getCaseModifiedFields: function(grCase) {
if (!grCase || !grCase.canRead())
return;
//Cat item variables implementation may differ from form fields
var mismatchFields = ["country", 'country_of_birth', 'home_phone', 'work_mobile', 'mobile_phone'];
var fields = [];
if (!grCase.payload)
return fields;
var payload = JSON.parse(grCase.payload);
var grProfile = grCase.hr_profile.getRefRecord();
var grUser = grCase.subject_person.getRefRecord();
//Loop through the fields from RP
for (var prop in payload) {
var isMisMatchProperty = mismatchFields.indexOf(prop) > -1;
//Filter out mismatched fields
if (!isMisMatchProperty) {
//Check if field belongs to HR Profile
if (grProfile.hasOwnProperty(prop)) {
fields.push(this._getModifiedFieldInfo(grProfile, 'profile', prop, payload[prop]));
continue;
//Check if field belongs to Sys User record
} else if (grUser.hasOwnProperty(prop)) {
fields.push(this._getModifiedFieldInfo(grUser, 'user', prop, payload[prop]));
continue;
}
//Handle mismatch fields
} else {
if ((prop == 'country' || prop == 'country_of_birth') && grProfile.getValue(prop) != payload[prop])
fields.push(this._getModifiedFieldInfo(grProfile, 'profile', prop, this.getCountryName(payload[prop])));
else if (prop == 'home_phone' || prop == 'work_mobile' || prop == 'mobile_phone') {
var phoneNumber = this.sanitizePhoneNumber(payload[prop]);
if (phoneNumber != grProfile.getValue(prop))
fields.push(this._getModifiedFieldInfo(grProfile, 'profile', prop, payload[prop]));
}
}
}
fields = fields.filter(function(field) {
if (field)
return field;
});
return fields;
},
//Returns the object that contains updated field info
_getModifiedFieldInfo: function(grProfile, table, prop, newValue) {
var currentValue = this.sanitize(grProfile.getValue(prop), '');
//Checking if value is modified
if (currentValue !== newValue) {
return ({
table: table,
field: prop,
newValue: this.sanitize(newValue, 'Empty')
});
}
},
/**
* @private
*/
sanitizePhoneNumber: function(number) {
return number.replace(/[\s-()]/g, "");
},
/**
* @private
*/
getCountryName: function(countryId) {
var c = new GlideRecord('core_country');
return (c.get(countryId) && c.canRead()) ? c.getDisplayValue() : gs.getMessage('Empty');
},
/**
* @private
*/
sanitize: function(value, defaultVal) {
return (value) ? value : defaultVal;
},
/*
* Public method to add info message when opened_for is a user without HR profile
* Security requirements: user can read hr profile represented by user id in sysparm_sys_id
*/
addMsgForOpenedForHRProfile: function() {
var gr = new GlideRecord('sn_hr_core_profile');
gr.addQuery('user', this.getParameter('sysparm_sys_id'));
gr.query();
if (!gr.canRead())
return;
else if (!gr.hasNext()) {
var user = this._getUserNameFromSysId(this.getParameter('sysparm_sys_id'));
if (user)
gs.addInfoMessage(gs.getMessage('HR profile will be created for {0}', user));
else
gs.addInfoMessage(gs.getMessage('HR profile will be created for opened_for user'));
}
},
_getUserNameFromSysId: function(sysId) {
var grUser = new GlideRecord('sys_user');
grUser.addQuery('sys_id', sysId);
grUser.query();
if (grUser.next() && grUser.canRead())
return grUser.name;
},
/*
This method takes document type and a user and returns a comma separated
string of PDF template sys_ids filtered by the provided document type and
provided user's HR criteria
Security requirements (from getPDFTemplateBasedOnDocumentType): user can read
templates with document type represented by sysparm_document_type. (Templates
that cannot be read will not be returned)
@param sysparm_document_type: Document Type to filter PDF Templates
@param sysparm_subject_person: Subject person whose HR criteria will be
checked against the HR Criteria of PDF Templates
@return Comma separated string of PDF Template sys_ids
*/
ajaxFunction_getPDFTemplate: function() {
var documentType = this.getParameter('sysparm_document_type');
var subjectPerson = this.getParameter('sysparm_subject_person');
var answer = new sn_hr_core.hr_Utils().getPDFTemplateBasedOnDocumentType(documentType, subjectPerson);
return answer;
},
/*
This method returns an array of objects containing sys_id and
display value of PDF Templates matching document type and
user's HR criteria
Security requirements (from getPDFTemplateBasedOnDocumentType): user can read
templates with document type represented by sysparm_document_type. (Templates
that cannot be read will not be returned)
@param sysparm_document_type: Document Type to filter PDF Templates
@param sysparm_subject_person: Subject person whose HR criteria will be
checked against the HR Criteria of PDF Templates
@return JSON encoded object containing sys_id and display value of matching PDF Template
*/
ajaxFunction_getMatchingPDFTemplate: function() {
var documentType = this.getParameter('sysparm_document_type');
var subjectPerson = this.getParameter('sysparm_subject_person');
var answer = new sn_hr_core.hr_Utils().getPDFTemplateObjectsForDocumentType(documentType, subjectPerson);
return answer && (answer.length == 1) ? new global.JSON().encode(answer[0]) : '';
},
/*
* This method is called by the Approve and Reject UI actions on sysapproval_approver record
* Security requirements: none (it is used to do security checks)
*/
canBeApprovedByHRAdmin: function(current) {
return current.state == 'requested' &&
current.sysapproval && hr.TABLE_CASE_EXTENSIONS.toString().indexOf(current.sysapproval.sys_class_name) >= 0 &&
new hr_Utils().checkUserHasRole('sn_hr_core.admin');
},
/*
* This method is called by the read ACL on sysapproval_approver
* Security requirements: none (it is used to do security checks)
*/
hasHRAccess: function(current) {
var hasCorrectHRRole = new hr_Utils().checkUserHasRole(sn_hr_core.hr.ROLE_HR_CASE_READER);
var validImpersonation = new sn_hr_core.hr_CoreUtils().impersonateCheck() == false;
var hasAccessForOpenForUser = false;
var hrCase = new GlideRecord(current.sysapproval.sys_class_name.toString());
if (hrCase.get(current.document_id)) {
var openForUser = hrCase.opened_for;
hasAccessForOpenForUser = ((openForUser != null) && (gs.getUserID() == openForUser.sys_id));
}
return validImpersonation && (hasAccessForOpenForUser || hasCorrectHRRole || new global.ApprovalDelegationUtil().isMyApproval(current));
},
/*
* Utility method that returns list of all the table that extend (including) sn_hr_core_case.
* Security requirements: can read the HR Case table
*
* @returns
* List of all the table that extend (including) sn_hr_core_case table.
*
*/
getAllHRCaseTables: function() {
var HR_CASE_TABLE = "sn_hr_core_case";
var grCase = new GlideRecord(HR_CASE_TABLE);
if (!grCase.canRead())
return;
return new GlideTableHierarchy(HR_CASE_TABLE).getAllExtensions();
},
/*
* This method is called by the UI Action Close Complete, related to HR Case.
* Updates the work notes and state to 'closed incomplete'
* Security requirements: can write to the record represented by sysparm_obj_id and
* sysparm_table_name
*/
closeIncompleteAction: function() {
//gets the parameters passed in by the UI Action
var objSysId = this.getParameter('sysparm_obj_id');
var tblName = this.getParameter('sysparm_table_name');
var newWorkNote = this.getParameter('sysparm_work_note');
//opens the record
var task = new GlideRecord(tblName);
if (task.get(objSysId) && task.canWrite()) {
//updates the worknotes, time work ended (if null), and the state
this._updateNotes(task, newWorkNote, false);
this._workEnd(task);
task.state = hr.STATE_CLOSE_INCOMPLETE;
task.update();
//checks the request table for a parent of our record
//changed the state to closed_cancelled for our record if found
var gr = new GlideRecord('sc_request');
gr.addQuery('parent', objSysId);
gr.addActiveQuery();
if (!gr.canRead())
return;
gr.setValue('request_state', 'closed_cancelled');
gr.updateMultiple();
}
return gr;
},
/**
* Security reqiurements: can read the case represented by the case_id parameter
*/
getSuspendReasons: function() {
var reasons = [];
var grCase = new GlideRecord("sn_hr_core_case");
var caseId = this.getParameter('case_id');
var tableName = this.getParameter('sysparm_table_name');
var parent = this.getParameter('sysparm_parent_case_table_name');
if (tableName === 'sn_hr_core_task')
tableName = parent;
if (!gs.nil(caseId))
grCase.get(caseId);
if (!grCase.canRead() || !grCase.getElement("sla_suspended_reason").canRead())
return new global.JSON().encode(reasons);
var choiceList = GlideChoiceList.getChoiceList(tableName, 'sla_suspended_reason');
choiceList.removeNone();
for (var i = 0; i < choiceList.getSize(); i++) {
var choice = choiceList.getChoice(i);
reasons.push({
displayValue: choice.label.toString(),
value: choice.value.toString()
});
}
return new global.JSON().encode(reasons);
},
/*
* This method is called by the UI Action Suspend/Activate, related to HR Case.
* Updates the work notes and state to 'Suspended'
* Security requirements: can write to the case represented by sysparm_obj_id and
* sysparm_table_name
*/
suspendCaseAction: function() {
var objSysId = this.getParameter('sysparm_obj_id');
var tblName = this.getParameter('sysparm_table_name');
var newWorkNote = this.getParameter('sysparm_work_note');
var newReason = this.getParameter('sysparm_suspend_reason');
var hrCase = new GlideRecord(tblName);
hrCase.get(objSysId);
if (!hrCase.isValid() || !hrCase.canWrite())
return null;
this._setSuspensionReason(hrCase, newReason);
this._updateNotes(hrCase, newWorkNote, false);
hrCase.state = hr.STATE_SUSPENDED;
hrCase.update();
return hrCase;
},
/**
* This function takes a table name and sys_id and checks if the
* passed in corresponding record has any children that are open.
* Security requirements: can read parent case
*
* @returns {boolean} - true if an open child case exists, false otherwise.
*
*/
hasOpenChildCases: function() {
var tblName = this.getParameter('sysparm_table_name');
if (tblName !== 'sn_hr_core_case')
return null;
var parentId = this.getParameter('sysparm_parent_id');
var parentCase = new GlideRecord('sn_hr_core_case');
if (!parentCase.get(parentId) || !parentCase.canRead())
return null;
var notInQuery = [hr_Constants.CASE_DRAFT, hr_Constants.CASE_CLOSED_COMPLETE, hr_Constants.CASE_CLOSED_INCOMPLETE, hr_Constants.CASE_CANCELLED];
var openChildQuery = "active=true^stateNOT IN".concat(notInQuery, "^parent=", parentId);
var gr = new GlideRecord(tblName);
gr.addQuery(openChildQuery);
gr.setLimit(1);
gr.query();
return gr.hasNext(); //true if the record has open children
},
/**
* Security requirements: user can write to record represented by sysparm_table_name
* and sysparm_sys_id
*/
settingTaskToDraft: function() {
var sysId = this.getParameter('sysparm_sys_id');
var className = this.getParameter('sysparm_table_name');
var workNote = this.getParameter('sysparm_work_note');
var task = new GlideRecord(className);
if (task.get(sysId) && task.work_notes.canWrite()) {
task.work_notes = workNote;
if (task.work_end.nil()) {
task.work_end = new GlideDateTime().getDisplayValue();
}
task.state = hr.STATE_DRAFT;
task.update();
}
},
/*
* This method to be used for E-signature flows of HR Templates
* Security requirements: user can read record represented by table_name and sys_id
*/
generateDocumentAndCloseTask: function(table_name, sys_id) {
var taskGR = new GlideRecord(table_name);
taskGR.get(sys_id);
if (taskGR.canRead() && new hr_Delegation().isAssignedOrDelegated(taskGR)) {
var request = {
"user_id": gs.getUserID(),
"sys_id": sys_id
};
var hrPdfUtils = new sn_hr_core.hr_PdfUtils();
var generalHrForm = new sn_hr_core.GeneralHRForm();
var esignTaskUtil = new sn_esign.esign_taskUtils();
if (hrPdfUtils.isValidPdfTemplate(table_name, sys_id)) {
var response = hrPdfUtils.createPdfForDocument(table_name, sys_id, false);
if (response.indexOf('Error') > 0)
gs.addInfoMessage(gs.getMessage("isValidPdfTemplate error response {0}", response));
else
esignTaskUtil.setTaskFinished(request);
} else if (generalHrForm.hasDraftDocument(table_name, sys_id)) {
var draftDocument = generalHrForm.getDraftDocument(table_name, sys_id);
new sn_hr_core.GeneralHRForm(draftDocument.table, draftDocument.document, draftDocument.target_table, draftDocument.target_id).generate();
esignTaskUtil.setTaskFinished(request);
}
}
},
/*
* A private function that updates the comments or worknotes field
* depending on the passed in parameter of 'isComment'
*/
_updateNotes: function(gr, newNote, isComment) {
if (gr.canWrite() && newNote) {
if (isComment)
gr.comments = newNote;
else
gr.work_notes = newNote;
}
},
/*
* This is a private function that sets the time work was ended on
* a case or task;
*/
_workEnd: function(gr) {
if (gr.canWrite() && gr.work_end.nil()) {
gr.work_end = new GlideDateTime().getDisplayValue();
}
},
/*
* A private function that checks if the parent has been assigned, if not
* give the parent the same assignments as the child task.
*
* Used for Start Work UI action for HR tasks.
*/
_checkParentAssignment: function(gr) {
if (gr.parent.assigned_to.nil() && gr.parent.assignment_group.nil()) {
var parentCase = new GlideRecord("sn_hr_core_case");
if (parentCase.get(gr.parent) && parentCase.canWrite()) {
parentCase.assigned_to = gr.assigned_to;
parentCase.assignment_group = gr.assignment_group;
parentCase.update();
}
}
},
/*
* This private function writes the reason provided for suspension and
* sets the approiate fields for a suspended case or task
*/
_setSuspensionReason: function(gr, reason) {
if (gr.canWrite() && reason) {
var isHrCase = new sn_hr_core.hr_CoreUtils().isCase(gr.sys_class_name);
if (isHrCase) {
gr.sla_suspended_reason = reason;
gr.sla_suspended = true;
gr.sla_suspended_on = new GlideDateTime().getDisplayValue();
} else {
gr.suspend_request = true;
gr.request_suspension_reason = reason;
}
}
},
/*
* This private function suspends the parent of the task and sets all
* approiate fields for a suspended case.
*/
_suspendParent: function(gr, reason) {
var parentCase = new GlideRecord("sn_hr_core_case");
var caseOpen = parentCase.state != (hr.STATE_CLOSE_COMPLETE || hr.STATE_CLOSE_INCOMPLETE || hr.STATE_CANCEL);
if (parentCase.get(gr.parent) && caseOpen && parentCase.sla_suspended == false && parentCase.canWrite()) {
this._setSuspensionReason(parentCase, reason);
parentCase.state = hr.STATE_SUSPENDED;
parentCase.update();
}
},
/*
* The function querys the task table and sets all suspend requests for
* siblings tasks of the current task to false.
*/
_updateSiblings: function(gr) {
if (gr.canWrite()) {
var siblings = new GlideRecord("sn_hr_core_task");
siblings.addQuery('parent', gr.parent.sys_id);
siblings.addQuery('suspend_request', true);
siblings.setValue('suspend_request', false);
siblings.updateMultiple();
}
},
/*
* This function is called by the UI Action Suspend and the UI Action
* Request Additional User Information, related to HR Task.
*
* Suspends the current parent case for the task and updates the work notes.
* Security requirements: user can write to record represented by sysparm_table_name
* and sysparm_sys_id
*/
suspendTask: function() {
var tblName = this.getParameter("sysparm_table_name");
var sysId = this.getParameter("sysparm_sys_id");
var reason = this.getParameter("sysparm_reason");
var newNote = this.getParameter("sysparm_new_note");
var gr = new GlideRecord(tblName);
if (gr.get(sysId) && gr.canWrite()) {
this._updateSiblings(gr);
this._updateNotes(gr, newNote, false);
this._setSuspensionReason(gr, reason);
this._suspendParent(gr, reason);
gr.update();
}
},
/*
* This function is called by the Start Work UI Action related to HR Task.
*
* Assigns the parent case the same assignees and sets the state to work in progress
* Security requirements: user can write to record represented by sysparm_table_name
* and sysparm_sys_id
*/
startTask: function() {
var tblName = this.getParameter("sysparm_table_name");
var sysId = this.getParameter("sysparm_sys_id");
var gr = new GlideRecord(tblName);
if (gr.get(sysId) && gr.canWrite()) {
this._checkParentAssignment(gr);
gr.state = hr.STATE_WORK_IN_PROGRESS;
gr.update();
}
},
/*
* This function is called by Close Incomplete UI action related to HR Task.
*
* Closes the task as Close Incomplete and updates the work notes.
* It will clone the task if the user requests to create a follow.
* Security requirements: user can write to record represented by sysparm_table_name
* and sysparm_sys_id
*/
closeIncompleteTask: function() {
var tblName = this.getParameter("sysparm_table_name");
var sysId = this.getParameter("sysparm_sys_id");
var newNote = this.getParameter("sysparm_new_note");
var createFollow = this.getParameter("sysparm_create_follow");
var sysIdToReturn;
var gr = new GlideRecord(tblName);
if (gr.get(sysId) && gr.canWrite()) {
sysIdToReturn = gr.sys_id;
if (createFollow == "Yes") {
var clonedTask = new hr_Task().cloneTask(gr, true);
sysIdToReturn = clonedTask.sys_id;
}
this._updateNotes(gr, newNote, false);
this._workEnd(gr);
gr.state = hr.STATE_CLOSE_INCOMPLETE;
gr.update();
}
return sysIdToReturn;
},
/*
* This function is called by Close Complete Ui Action related to HR task.
*
* Closes the task to Closed Complete and updates the work notes.
* Security requirements (from closeHRTaskWithComment): user can write to record
* represented by sysparm_table_name and sysparm_sys_id
*/
closeTask: function() {
var tblName = this.getParameter("sysparm_table_name");
var sysId = this.getParameter("sysparm_sys_id");
var newNote = this.getParameter("sysparm_new_note");
this.closeHRTaskWithComment(tblName, sysId, newNote);
},
/**
* Add work note and close complete HR task
* Security requirements: user can write to record represented by table and uniqueID
* @params:
* table: String value of HR task table name
* uniqueID: String value of HR task sys_id
* comment: Work note
*
* @returns Object {
* success: boolean true/ false
* error: string message
* }
*/
closeHRTaskWithComment: function(table, uniqueID, comment) {
var response = {};
var hrTaskGR = new GlideRecord(table);
if (hrTaskGR.get(uniqueID)) {
if (hrTaskGR.canWrite()) {
this._updateNotes(hrTaskGR, comment, false);
this._workEnd(hrTaskGR);
hrTaskGR.state = hr.STATE_CLOSE_COMPLETE;
response.success = hrTaskGR.update() ? true : false;
} else
response.error = 'No write access to record.';
} else
response.error = 'Record not found.';
return response;
},
/*
* This function is called by Cancel Task Ui Action related to HR Task.
*
* Cancels the task by setting the state to cancelled and updates worknotes.
* Security requirements: user can write to record represented by sysparm_table_name
* and sysparm_obj_id
*/
cancelAction: function() {
//gets the parameters passed in by the UI Action
var objSysId = this.getParameter('sysparm_obj_id');
var tblName = this.getParameter('sysparm_table_name');
var newWorkNote = this.getParameter('sysparm_work_note');
//opens the record
var grCase = new GlideRecord(tblName);
if (grCase.get(objSysId) && grCase.canWrite()) {
//updates the worknotes and state fields
this._updateNotes(grCase, newWorkNote, false);
grCase.state = hr.STATE_CANCEL;
grCase.update();
}
},
/**
* Security requirements: user has sn_hr_core.basic role and
* can read the case represented by sysparm_sys_id and sysparm_table_name
*/
escalateCase: function() {
if (!gs.hasRole('sn_hr_core.basic'))
return;
//Get the passed in parameters
var caseSysId = this.getParameter('sysparm_sys_id');
var tblName = this.getParameter('sysparm_table_name');
var newWorkNote = this.getParameter('sysparm_work_note');
var caseExtensions = new GlideTableHierarchy("sn_hr_core_case").getAllExtensions();
if (caseExtensions.indexOf(tblName) < 0)
return;
//Get the record
var caseToEscalate = new GlideRecord(tblName);
if (!caseToEscalate.get(caseSysId) || !caseToEscalate.canRead())
return;
this._updateNotes(caseToEscalate, newWorkNote, false);
//Query the next escalation tier
var gr = new GlideRecord('sn_hr_core_tier_definition');
gr.addQuery('escalate_from', caseToEscalate.assignment_group);
gr.addNotNullQuery('escalate_to');
gr.query();
if (gr.next()) {
caseToEscalate.assignment_group = gr.escalate_to;
caseToEscalate.assigned_to = '';
// auto assignment BR will try to find an agent
}
caseToEscalate.update();
},
/**
* Returns boolean true if user is assigned_to on case (or delegated) and has sn_hr_core.case_writer role
* Security requirements: userID must be the same as the logged in user's ID (cannot call this API for
* other users)
* @param {string} userID - sys_id of the user
* @param {string} caseID - the sys_id of the case
* @returns {boolean} - true if user is assigned_to on case (or delegated) and has sn_hr_core.case_writer role otherwise false
*/
isAssignedToCaseWriter: function(userID, caseID) {
if (gs.getUserID() !== userID)
return false;
//verify user is assigned to on case
var hrCaseGR = new GlideRecord('sn_hr_core_case');
if (!hrCaseGR.get(caseID))
return false;
var isAssignee = new hr_Delegation().isAssignedOrDelegated(hrCaseGR, userID);
return (isAssignee && new hr_Utils().checkUserHasRole('sn_hr_core.case_writer'));
},
/**
* getCasePredictions - Get all the predictions for the given case
* Security requirements: user has sn_hr_core.case_writer role and can read case
* represented by sysparm_case_id
* @param:
* sysparm_case_id - HR Case sys_id
*/
getCasePredictions: function() {
if (!gs.hasRole('sn_hr_core.case_writer'))
return;
if (!(new GlidePluginManager().isActive('com.glide.platform_ml')))
return;
var caseId = this.getParameter('sysparm_case_id');
var toPredict = this.getParameter('sysparm_predict');
var predictions = {};
var caseGR = new GlideRecord('sn_hr_core_case');
if (!caseGR.get(caseId) || !caseGR.canRead())
return;
var subjectPerson = caseGR.subject_person;
var domain = caseGR.domain;
if (toPredict == 'HR_SERVICE' && gs.getProperty("sn_hr_core.case_auto_categorization") == 'true') {
var service = this.getHRServicePredictions(caseId, subjectPerson, domain);
if (!gs.nil(service)) {
service = JSON.parse(service);
if (!gs.nil(service.service_id) && service.service_id != caseGR.hr_service) {
service.message = gs.getMessage('Predicted HR Service is {0}. To transfer this case initiate Transfer Case.', service.service_display);
predictions.service = service;
}
}
}
if (toPredict == 'ASSIGNMENT_GROUP' && gs.getProperty("sn_hr_core.case_auto_assignment") == 'true') {
var reassignCount = caseGR.reassignment_count;
if (gs.nil(reassignCount) || reassignCount == 0) {
var result = this.getMLPredictorResults(caseId, hr.CASE_ASSIGNMENT, domain);
if (!gs.nil(result)) {
var aGroup = '';
if (!gs.nil(result[hr.CASE_ASSIGNMENT]))
aGroup = result[hr.CASE_ASSIGNMENT];
aGroup = aGroup.split(',')[0];
if (!gs.nil(aGroup) && aGroup != caseGR.assignment_group.name)
predictions.assignmentGroup = {
assignmentGroup: aGroup,
message: gs.getMessage('Predicted Assignment group is {0}.', aGroup)
};
}
}
}
return JSON.stringify(predictions);
},
/**
* Security requirements: none
*/
isEndDateAfterStartDate: function() {
var startDate = new GlideDateTime(this.getParameter('sysparm_start_date'));
var endDate = new GlideDateTime(this.getParameter('sysparm_end_date'));
return endDate.compareTo(startDate) >= 0;
},
/**
* Description: Returns solution definition and solution for given use case and domain
* Security requirements: none
* @param {String} sys_domain
* @param {String} Use Case of HRAI Configuration
* @return {Object} Glide Record of valid solution definition and active solution
*/
getValidSolution: function(domain, useCase) {
var hrAIConfiguration = new GlideRecord('sn_hr_core_ai_configuration'); //configuration table
hrAIConfiguration.addQuery('use_case', useCase);
hrAIConfiguration.addQuery('sys_domain', domain);
hrAIConfiguration.query();
if (!hrAIConfiguration.next()) {
hrAIConfiguration = new GlideRecord('sn_hr_core_ai_configuration');
hrAIConfiguration.addQuery('use_case', useCase);
hrAIConfiguration.addQuery('sys_domain', 'global');
hrAIConfiguration.query();
if (!hrAIConfiguration.next())
return null;
}
var solutionDefinition = new GlideRecord('ml_capability_definition_base');
if (!solutionDefinition.get(hrAIConfiguration.solution_capability_definition))
return null;
var predictor = new global.MLPredictor();
var name = solutionDefinition.solution_name.toString(); //Get solution name from configuration
var solution = predictor.findActiveSolution(name);
if (solution) {
var solutionInfo = {
solutionDefinition: solutionDefinition,
solution: solution
};
return solutionInfo;
}
},
/**
* Description: Predicts ETTR for HR cases
* Security requirements: none
* @param {GlideRecord} hrCase
* @param {String} solution_name
* @return {Object} Contains lowerbound, upperbound, point estimate of predicted ETTR
*/
getERTPrediction: function(hrCase, solution_name) {
var options = {};
options.mluc = "MLUC-HR-00012";
var sysId = hrCase.sys_id;
options.confidence_level = GlideApplicationProperty.getValue('sn_hr_core.estimated_resolution_time_confidence_level');
var mlSolution = sn_ml.RegressionSolutionStore.get(solution_name);
var result = mlSolution.getActiveVersion().predict(hrCase, options);
var ans = (JSON.parse(result)[sysId]);
return ans;
},
/**
* Description: Returns whether to display ETTR for Agent
* Security requirements (from getERTDisplayAgent): user can read case represented by sysparm_sysId
* @param {sys_id} sys_id of HR Case
* @return {boolean} True if ETTR display is enabled for Agent
*/
getERTDisplayAgentAJAX: function() {
var sysId = this.getParameter('sysparm_sysId');
var result = this.getERTDisplayAgent(sysId);
return result;
},
/**
* Description: Returns whether to display ETTR for Agent
* Security requirements: user can read case represented by sysparm_sysId
* @private
* @param {sys_id} sys_id of HR Case
* @return {boolean} True if ETTR display is enabled for Agent
*/
getERTDisplayAgent: function(sys_id) {
var gr = new GlideRecord("sn_hr_core_case");
if (!gr.get(sys_id) || !gr.canRead())
return false;
var isSolutionDefActive = this.isSolutionDefinitionActive(gr.sys_domain, hr.ESTIMATED_RESOLUTION_TIME);
if (!isSolutionDefActive)
return false;
var tableName = gr.sys_class_name.toString();
var prop = this.getERTEnabledCOEsForAgent();
if (prop.indexOf(tableName) > -1)
return true;
return false;
},
/**
* Description: Returns list of COEs for which ETTR display is active for Employee
* @private
* @return {array} List of COEs
*/
getERTEnabledCOEsForEmp: function() {
var prop = GlideApplicationProperty.getValue('sn_hr_core.COE_ETTR_display_employee').split(',');
prop.forEach(function(entry) {
entry.trim();
});
return prop;
},
/**
* Description: Returns list of COEs for which ETTR display is active for Agent
* @private
* @return {array} List of COEs
*/
getERTEnabledCOEsForAgent: function() {
var prop = GlideApplicationProperty.getValue('sn_hr_core.COE_ETTR_display_agent').split(',');
prop.forEach(function(entry) {
entry.trim();
});
return prop;
},
_checkERTDisplayPropertiesEmployee: function(gr) {
var isSolutionDefActive = this.isSolutionDefinitionActive(gr.sys_domain, hr.ESTIMATED_RESOLUTION_TIME);
if (!isSolutionDefActive)
return false;
var prop = this.getERTEnabledCOEsForEmp();
var tableName = gr.sys_class_name.toString();
if (prop.indexOf(tableName) > -1)
return true;
return false;
},
/**
* Description: Returns ETTR value if the feature is active and display is enabled for employees
* Security requirements: user can read hr case represented by sys_id
* @return {String} ETTR
*/
getERTValueEmployee: function(sys_id) {
var gr = new GlideRecord("sn_hr_core_case");
if (!gr.get(sys_id) || !gr.canRead())
return '-1';
if (!gs.nil(gr) && !gs.nil(gr.max_ettr) && this._checkERTDisplayPropertiesEmployee(gr))
return gr.max_ettr.toString();
},
/**
* Description: Returns whether a solution definition is active corresponding to a given use case and domain
* @private
* @param {String} sys_domain
* @param {String} Use Case of HRAI Configuration
* @return {Boolean} True if solution definition is active.
*/
isSolutionDefinitionActive: function(domain, useCase) {
var configuration;
var hrAIConfiguration = new GlideRecord('sn_hr_core_ai_configuration'); //configuration table
hrAIConfiguration.addQuery('use_case', useCase);
hrAIConfiguration.addActiveQuery();
var orCondition = hrAIConfiguration.addQuery('sys_domain', domain);
orCondition.addOrCondition('sys_domain', 'global');
hrAIConfiguration.query();
while (hrAIConfiguration.next()) {
if (hrAIConfiguration.sys_domain == domain) {
configuration = hrAIConfiguration;
break;
} else if (hrAIConfiguration.sys_domain == 'global')
configuration = hrAIConfiguration;
}
var solutionDefinition = new GlideRecord('ml_capability_definition_base');
if (gs.nil(configuration) || !solutionDefinition.get(configuration.solution_capability_definition))
return false;
return solutionDefinition.active;
},
/**
* Description: Returns list of domains, other than global, for which solution definition corresponding to a ETTR use case is active
* Security requirements: none (used by HR Agent workspace, HR Agent workspace V2, and app-hr-mobile [employee HR Cases])
* @return {Array} Array of sys_domain
*/
getERTActiveSolutionDomains: function() {
var domains = [];
var useCase = hr.ESTIMATED_RESOLUTION_TIME;
var hrAIConfiguration = new GlideRecord('sn_hr_core_ai_configuration'); //configuration table
hrAIConfiguration.addQuery('use_case', useCase);
hrAIConfiguration.addQuery('sys_domain', '!=', 'global');
hrAIConfiguration.addActiveQuery();
hrAIConfiguration.query();
while (hrAIConfiguration.next()) {
if (hrAIConfiguration.solution_capability_definition.active)
domains.push(hrAIConfiguration.sys_domain);
}
return domains;
},
/**
* Description: If solution definition corresponding to global domain Use Case is active, returns list of COEs for which ETTR display is enabled for given persona,
* Security requirements: none (used by HR Agent workspace, HR Agent workspace V2, and app-hr-mobile [employee HR Cases])
* @return {Array} Array of COEs
*/
getCOEsWhenGlobalSolutionActive: function(persona) {
var prop = [];
var useCase = hr.ESTIMATED_RESOLUTION_TIME;
var hrAIConfiguration = new GlideRecord('sn_hr_core_ai_configuration'); //configuration table
hrAIConfiguration.addQuery('use_case', useCase);
hrAIConfiguration.addQuery('sys_domain', 'global');
hrAIConfiguration.addActiveQuery();
hrAIConfiguration.setLimit(1);
hrAIConfiguration.query();
if (hrAIConfiguration.next() && hrAIConfiguration.solution_capability_definition.active) {
if (persona == "employee")
prop = this.getERTEnabledCOEsForEmp();
else if (persona == "agent")
prop = this.getERTEnabledCOEsForAgent();
}
return prop;
},
type: "hr_CaseAjax"
});
Sys ID
b25370019f22120047a2d126c42e7000