Name
global.OnCallEscalationUtilSNC
Description
No description available
Script
var OnCallEscalationUtilSNC = Class.create();
OnCallEscalationUtilSNC.prototype = {
initialize: function () {
this._log = new GSLog('com.snc.on_call_rotation.log.level', this.type);
this.oncallCommon = new OnCallCommon();
this.contactPrefUtil = new OnCallContactPreferenceUtil();
this.oncallRotation = new OnCallRotation();
},
TABLES: {
ON_CALL_ESCALATION: 'on_call_escalation',
ON_CALL_ESCALATION_LEVEL: 'on_call_escalation_level',
ON_CALL_ESCALATION_CON_ATTEMPT: 'on_call_escalation_con_attempt',
ON_CALL_ESCALATION_COMM: 'on_call_escalation_comm',
CMN_NOTIF_DEVICE: 'cmn_notif_device'
},
ON_CALL_ESCALATION_FIELDS: {
STATUS: {
LIVE: 'live',
COMPLETED: 'completed',
CANCELLED: 'cancelled'
}
},
ESCALATION_TYPE: {
ROTATE_THROUGH_MEMBER: 'rotate_through_member',
ROTATE_THROUGH_ROSTER: 'rotate_through_roster',
CUSTOM: 'custom'
},
ESCALATEE_TYPE: {
USER: 'user',
DEVICE: 'device'
},
ESCALATION_STATUS: {
COMPLETE: 'complete',
ACTIVE: 'active',
PENDING: 'pending'
},
CATEGORY_FILTER: ['conferencing'],
COMMUNICATION_TYPE: OnCallCommonSNC.COMMUNICATION_TYPES,
COMMUNICATION_STATUS: {
SENT: 'sent',
FAILED: 'failed',
},
COMMUNICATION_RESPONSE: {
ACCEPTED: 'accepted',
REJECTED: 'rejected',
ACCEPTED_FROM_OTHER_DEVICE: 'accepted_from_other_device',
REJECTED_FROM_OTHER_DEVICE: 'rejected_from_other_device',
AUTO_ASSIGNED: 'auto_assigned',
INVALID_RESPONSE: 'invalid_response'
},
COMMUNICATION_RESPONSE_TYPE: {
ACCEPTED: 'accepted',
REJECTED: 'rejected',
INVALID: 'invalid',
PARTIAL_REJECT: 'partial_reject',
NO_ANSWER: 'no_answer'
},
CONFERENCE_SERVICE_PROVIDER: [
{
value: 'Microsoft Teams',
translation: gs.getMessage('Microsoft Teams')
},
{
value: 'Telephony',
translation: gs.getMessage('Telephony')
},
{
value: 'Cisco Webex',
translation: gs.getMessage('Cisco Webex')
},
{
value: 'Zoom',
translation: gs.getMessage('Zoom')
}
],
isEscalationLogEnabled: function () {
return gs.getProperty('com.snc.on_call_rotation.log_escalations', 'true') === 'true';
},
setCommunicationResponseByUser: function(userSysId, response, table, source, escalationSysId /*optional*/) {
if (!userSysId)
return;
var communicationGr = new GlideRecord(this.TABLES.ON_CALL_ESCALATION_COMM);
communicationGr.addQuery('contact_attempt.escalation_level.escalation.table', table);
communicationGr.addQuery('contact_attempt.escalation_level.escalation.source', source);
if (escalationSysId)
communicationGr.addQuery('contact_attempt.escalation_level.escalation', escalationSysId);
communicationGr.addQuery('escalatee_type', this.ESCALATEE_TYPE.USER);
communicationGr.addQuery('user', userSysId);
communicationGr.orderByDesc('contact_attempt.escalation_level.level');
communicationGr.orderByDesc('contact_attempt.contact_attempt');
communicationGr.setLimit(1);
communicationGr.query();
if (communicationGr.next() && !communicationGr.response) {
communicationGr.setValue('response', response);
communicationGr.setValue('responded_at', new GlideDateTime());
communicationGr.update();
this._updateOtherCommunicationsOfEscalatee(communicationGr);
this._propagateAcknowledgedAt(communicationGr);
}
},
setCommunicationResponse: function (table, source, commType, commValue, response, escalationId /*optional*/) {
if (!this.isEscalationLogEnabled())
return this._getResultPayload();
/**
* There can be multiple escalations running for a task with different groups
* STEP-1: Get all rota ids across escalations for a given task
* STEP-2: find communication record ordered by level and with in level ordered by attempt
* STEP-3: update response details of that communication record
* STEP-4: find other communications sent out to that escalatee in the same attempt and update the response to 'accepted/rejected from other device'
*/
var rotas = this._getRotasOfEscalation(table, source, escalationId);
for (var i = 0; i < rotas.length; i++) {
var communicationGr = this._getCommunicationGr(table, source, rotas[i], commType, commValue, escalationId);
if (communicationGr) {
var respondedAt = new GlideDateTime();
communicationGr.setValue('response', response);
communicationGr.setValue('responded_at', respondedAt);
communicationGr.update();
this._updateOtherCommunicationsOfEscalatee(communicationGr);
this._propagateAcknowledgedAt(communicationGr);
}
}
},
_propagateAcknowledgedAt: function (communicationGr) {
var response = communicationGr.response;
var attemptedAtGdt = communicationGr.attempted_at.getGlideObject();
var respondedAtGdt = communicationGr.responded_at.getGlideObject();
if (this._log.atLevel(GSLog.DEBUG))
this._log.logDebug('[_propagateAcknowledgedAt] response: ' + response);
if (response != this.COMMUNICATION_RESPONSE.ACCEPTED && response != this.COMMUNICATION_RESPONSE.AUTO_ASSIGNED)
return;
communicationGr.setValue('acknowledged', true);
communicationGr.update();
var contactAttemptGr = communicationGr.contact_attempt.getRefRecord();
contactAttemptGr.setValue('acknowledged', true);
contactAttemptGr.setValue('acknowledged_at', respondedAtGdt);
contactAttemptGr.setValue('time_to_acknowledge', this._getDuration(attemptedAtGdt, respondedAtGdt));
contactAttemptGr.update();
var escalationLevelGr = contactAttemptGr.escalation_level.getRefRecord();
escalationLevelGr.setValue('acknowledged', true);
escalationLevelGr.setValue('acknowledged_at', respondedAtGdt);
escalationLevelGr.setValue('time_to_acknowledge', this._getDuration(escalationLevelGr.sys_created_on.getGlideObject(), respondedAtGdt));
escalationLevelGr.update();
var escalationGr = escalationLevelGr.escalation.getRefRecord();
escalationGr.setValue('acknowledged_at', respondedAtGdt);
escalationGr.setValue('time_to_acknowledge', this._getDuration(escalationGr.start_time.getGlideObject(), respondedAtGdt));
escalationGr.setValue('acknowledged_comm', communicationGr.getUniqueValue());
escalationGr.setValue('acknowledged', true);
escalationGr.update();
},
_getDuration: function (startGdt, endGdt) {
var startMillis = startGdt.getNumericValue();
var endMillis = endGdt.getNumericValue();
return new GlideDuration(endMillis - startMillis);
},
_getRotasOfEscalation: function (table, source, escalationId) {
var rotas = [];
var escalationLevelGr = new GlideAggregate(this.TABLES.ON_CALL_ESCALATION_LEVEL);
escalationLevelGr.addQuery('escalation.table', table);
escalationLevelGr.addQuery('escalation.source', source);
escalationLevelGr.addQuery('escalation.active', true);
if (escalationId)
escalationLevelGr.addQuery('escalation', escalationId);
escalationLevelGr.groupBy('rota');
escalationLevelGr.query();
while (escalationLevelGr.next())
rotas.push(escalationLevelGr.rota + '');
return rotas;
},
_updateOtherCommunicationsOfEscalatee: function (respondedCommunicationGr) {
var escalateeResponse = respondedCommunicationGr.response + '';
if (escalateeResponse != this.COMMUNICATION_RESPONSE.ACCEPTED && escalateeResponse != this.COMMUNICATION_RESPONSE.REJECTED)
return;
var communicationGr = new GlideRecord(this.TABLES.ON_CALL_ESCALATION_COMM);
communicationGr.addQuery('sys_id', '!=', respondedCommunicationGr.getUniqueValue());
communicationGr.addQuery('contact_attempt', respondedCommunicationGr.contact_attempt + '');
var escalateeType = respondedCommunicationGr.escalatee_type + '';
if (escalateeType == this.ESCALATEE_TYPE.USER) {
communicationGr.addQuery('escalatee_type', this.ESCALATEE_TYPE.USER);
communicationGr.addQuery('user', respondedCommunicationGr.user + '');
} else if (escalateeType == this.ESCALATEE_TYPE.DEVICE)
communicationGr.addQuery('device', respondedCommunicationGr.device + '');
communicationGr.query();
while (communicationGr.next()) {
if (escalateeResponse == this.COMMUNICATION_RESPONSE.ACCEPTED)
communicationGr.setValue('response', this.COMMUNICATION_RESPONSE.ACCEPTED_FROM_OTHER_DEVICE);
else if (escalateeResponse == this.COMMUNICATION_RESPONSE.REJECTED)
communicationGr.setValue('response', this.COMMUNICATION_RESPONSE.REJECTED_FROM_OTHER_DEVICE);
communicationGr.update();
}
},
_getCommunicationGr: function (table, source, rotaId, commType, commValue, escalationId) {
var communicationGr = new GlideRecord(this.TABLES.ON_CALL_ESCALATION_COMM);
communicationGr.addQuery('communication_type', commType);
if (this._log.atLevel(GSLog.DEBUG))
this._log.logDebug('[_getCommunicationGr] commType: ' + commType);
if (commType == this.COMMUNICATION_TYPE.EMAIL || commType == this.COMMUNICATION_TYPE.SLACK)
communicationGr.addQuery(this.COMMUNICATION_TYPE.EMAIL, commValue);
else if (commType == this.COMMUNICATION_TYPE.SMS || commType == this.COMMUNICATION_TYPE.VOICE)
communicationGr.addQuery('phone_number', commValue);
else if (commType == this.COMMUNICATION_TYPE.TEAMS || commType == this.COMMUNICATION_TYPE.MOBILE_NOTIFICATION || commType == this.COMMUNICATION_TYPE.CONFERENCE_ESCALATION) {
communicationGr.addQuery("user", commValue);
}
communicationGr.addQuery('contact_attempt.escalation_level.escalation.table', table);
communicationGr.addQuery('contact_attempt.escalation_level.escalation.source', source);
communicationGr.addQuery('contact_attempt.escalation_level.escalation.active', true);
if (escalationId)
communicationGr.addQuery('contact_attempt.escalation_level.escalation', escalationId);
communicationGr.addQuery('contact_attempt.escalation_level.rota', rotaId);
communicationGr.orderByDesc('contact_attempt.escalation_level.level');
communicationGr.orderByDesc('contact_attempt.contact_attempt');
communicationGr.setLimit(1);
communicationGr.query();
if (this._log.atLevel(GSLog.DEBUG))
this._log.logDebug('[_getCommunicationGr] communicationGr: ' + communicationGr.getTableName() + ' encodedQuery: ' + communicationGr.getEncodedQuery());
if (communicationGr.next())
return communicationGr;
},
logEscalationStart: function (group, table, source, workflowDefinition, workflowContext, parentEscalationLevelId, category, channels, ignoreDefReminders) {
if (!this.isEscalationLogEnabled())
return this._getResultPayload();
if (!group)
return this._getResultPayload(false, 'invalid group');
if (!table)
return this._getResultPayload(false, 'invalid table');
if (!source)
return this._getResultPayload(false, 'invalid source');
var escalationGr = this._createEscalation(group, table, source, workflowDefinition, workflowContext, parentEscalationLevelId, category, channels, ignoreDefReminders);
this._populateAdditionalEscalation(escalationGr, parentEscalationLevelId);
return this._getResultPayload(true, '', escalationGr);
},
_populateAdditionalEscalation: function (escalationGr, parentEscalationLevelId) {
if (!escalationGr || !parentEscalationLevelId)
return;
var parentEscalationLevelGr = new GlideRecord(this.TABLES.ON_CALL_ESCALATION_LEVEL);
if (parentEscalationLevelGr.get(parentEscalationLevelId)) {
var additionalEscalations = parentEscalationLevelGr.additional_escalations + '';
if (additionalEscalations)
additionalEscalations += ',';
additionalEscalations += escalationGr.getUniqueValue();
parentEscalationLevelGr.setValue('additional_escalations', additionalEscalations);
parentEscalationLevelGr.update();
}
},
logEscalationEnd: function (escalationId, escalationStatus) {
if (!this.isEscalationLogEnabled())
return this._getResultPayload();
if (!escalationId)
return this._getResultPayload(false, 'invalid escalationId');
var gr = new GlideRecord(this.TABLES.ON_CALL_ESCALATION);
if (gr.get(escalationId)) {
if (gr.active == true) {
gr.setValue('active', false);
gr.setValue('end_time', new GlideDateTime());
if (escalationStatus) gr.setValue('status', escalationStatus);
else gr.setValue('status', this.ON_CALL_ESCALATION_FIELDS.STATUS.COMPLETED);
gr.update();
}
return this._getResultPayload(true, '', gr);
}
return this._getResultPayload(false, 'escalation not found');
},
logEscalationLevel: function (escalationId, rotaId, level, escalatee, catchAll) {
if (!this.isEscalationLogEnabled())
return this._getResultPayload();
if (!escalationId)
return this._getResultPayload(false, 'invalid escalationId');
if (!rotaId)
return this._getResultPayload(false, 'invalid rotaId');
if (!level)
return this._getResultPayload(false, 'invalid level');
if (!escalatee)
return this._getResultPayload(false, 'invalid escalatee object');
var escalationGr = this._getEscalationGr(escalationId);
if (!escalationGr)
return this._getResultPayload(false, 'unable to find escalation');
var escalationType = escalatee.escalationType;
var rosterOrCustEscStep = this._getRosterOrStepRefFromEscalatee(escalatee);
var escalationLevelGr = this._getEscalationLevelGr(escalationId, rotaId, level);
if (!escalationLevelGr)
escalationLevelGr = this._createEscalationLevel(escalationId, rotaId, level, escalationType, rosterOrCustEscStep, catchAll);
if (escalationLevelGr)
return this._getResultPayload(true, '', escalationLevelGr);
return this._getResultPayload(false, 'unable to create escalation level');
},
logEscalationAttempt: function (escalationId, rotaId, level, contactAttempt) {
if (!this.isEscalationLogEnabled())
return this._getResultPayload();
if (!escalationId)
return this._getResultPayload(false, 'invalid escalationId');
if (!rotaId)
return this._getResultPayload(false, 'invalid rotaId');
if (!level)
return this._getResultPayload(false, 'invalid level');
if (!contactAttempt)
return this._getResultPayload(false, 'invalid contactAttempt');
var escalationGr = this._getEscalationGr(escalationId);
if (!escalationGr)
return this._getResultPayload(false, 'unable to find escalation');
var escalationLevelGr = this._getEscalationLevelGr(escalationId, rotaId, level);
if (!escalationLevelGr)
return this._getResultPayload(false, 'unable to find escalation level');
var contactAttemptGr = this._getContactAttemptGr(escalationLevelGr.getUniqueValue(), contactAttempt);
if (!contactAttemptGr)
contactAttemptGr = this._createContactAttempt(escalationLevelGr.getUniqueValue(), contactAttempt);
if (contactAttemptGr)
return this._getResultPayload(true, '', contactAttemptGr);
return this._getResultPayload(false, 'unable to create escalation attempt');
},
_getRosterOrStepRefFromEscalatee: function (escalatee) {
var escalationType = escalatee.escalationType;
if (escalationType === this.ESCALATION_TYPE.ROTATE_THROUGH_ROSTER || escalationType === this.ESCALATION_TYPE.ROTATE_THROUGH_MEMBER) {
return escalatee.rosterId;
}
else if (escalationType === this.ESCALATION_TYPE.CUSTOM) {
return escalatee.cmnRotaEscStepDefId;
}
},
logEscalationCommunication: function (escalationId, rotaId, level, contactAttempt, escalateeType, escalateeId, commType, commValue, status, escalatee, catchAll) {
if (!this.isEscalationLogEnabled())
return this._getResultPayload();
if (!escalationId)
return this._getResultPayload(false, 'invalid escalationId');
if (!rotaId)
return this._getResultPayload(false, 'invalid rotaId');
if (!level)
return this._getResultPayload(false, 'invalid level');
if (!contactAttempt)
return this._getResultPayload(false, 'invalid contactAttempt');
if (!escalateeType)
return this._getResultPayload(false, 'invalid escalateeType');
if (!escalateeId)
return this._getResultPayload(false, 'invalid escalateeId');
if (!commType)
return this._getResultPayload(false, 'invalid commType');
if (!escalatee)
return this._getResultPayload(false, 'invalid escalatee object');
var escalationGr = this._getEscalationGr(escalationId);
if (!escalationGr)
return this._getResultPayload(false, 'unable to find escalation');
var escalationLevelGr = this._getEscalationLevelGr(escalationId, rotaId, level);
if (!escalationLevelGr) {
var escalationType = escalatee.escalationType;
var rosterOrCustEscStep = this._getRosterOrStepRefFromEscalatee(escalatee);
escalationLevelGr = this._createEscalationLevel(escalationId, rotaId, level, escalationType, rosterOrCustEscStep, catchAll);
}
var contactAttemptGr = this._getContactAttemptGr(escalationLevelGr.getUniqueValue(), contactAttempt);
if (!contactAttemptGr) {
contactAttemptGr = this._createContactAttempt(escalationLevelGr.getUniqueValue(), contactAttempt);
}
var communicationGr = this._createCommunicationDetails(escalationId, contactAttemptGr.getUniqueValue(), escalateeType, commType, escalateeId, commValue, status);
if (communicationGr)
return this._getResultPayload(true, '', communicationGr);
return this._getResultPayload(false, 'unable to create communication');
},
_getResultPayload: function (success, errMsg, logGr) {
var escalationLogEnabled = this.isEscalationLogEnabled();
var result = {
escalationLogEnabled: escalationLogEnabled
};
if (!escalationLogEnabled) {
result.msg = 'Escalation log is not enabled';
return result;
}
result.success = success;
if (success)
result.logGr = logGr;
else
result.error = {
msg: errMsg
};
return result;
},
_getEscalationGr: function (escalationId) {
if (!escalationId)
return;
var gr = new GlideRecord(this.TABLES.ON_CALL_ESCALATION);
if (gr.get(escalationId))
return gr;
},
_getEscalationLevelGr: function (escalationId, rotaId, level) {
if (!escalationId || !rotaId || !level)
return;
var gr = new GlideRecord(this.TABLES.ON_CALL_ESCALATION_LEVEL);
gr.addQuery('escalation', escalationId);
gr.addQuery('rota', rotaId);
gr.addQuery('level', level);
gr.query();
if (gr.next())
return gr;
},
_getContactAttemptGr: function (escalationLevelId, contactAttempt) {
if (!escalationLevelId || !contactAttempt)
return;
var gr = new GlideRecord(this.TABLES.ON_CALL_ESCALATION_CON_ATTEMPT);
gr.addQuery('escalation_level', escalationLevelId);
gr.addQuery('contact_attempt', contactAttempt);
gr.query();
if (gr.next())
return gr;
},
_createEscalation: function (groupId, tableName, sourceId, wfDefinition, wfContext, parentEscalationLevelId, category, channels, ignoreDefReminders) {
var startTime = new GlideDateTime();
var domain = '';
try {
if (tableName && sourceId) {
var sourceGr = new GlideRecord(tableName);
if (sourceGr.get(sourceId) && sourceGr.isValidField('sys_domain')) {
domain = sourceGr.getValue('sys_domain');
}
}
}
catch(e) {
gs.error('Failed to find source record for tableName: ' + tableName + ', sourceId: ' + sourceId + ', exception: ' + e);
}
var gr = new GlideRecord(this.TABLES.ON_CALL_ESCALATION);
gr.initialize();
gr.setValue('table', tableName);
gr.setValue('source', sourceId);
gr.setValue('group', groupId);
gr.setValue('workflow_definition', wfDefinition);
gr.setValue('workflow_context', wfContext);
gr.setValue('start_time', startTime);
gr.setValue('parent_escalation_level', parentEscalationLevelId);
gr.setValue('category', category);
gr.setValue('channels', channels);
gr.setValue('status', this.ON_CALL_ESCALATION_FIELDS.STATUS.LIVE);
if (domain)
gr.setValue('sys_domain', domain);
if (ignoreDefReminders)
gr.setValue('ignore_def_reminders', true);
if (gr.insert())
return gr;
},
_createEscalationLevel: function (escId, shiftId, escLevel, escType, rosterOrCustEscStep, catchAll) {
var gr = new GlideRecord(this.TABLES.ON_CALL_ESCALATION_LEVEL);
gr.initialize();
gr.setValue('rota', shiftId);
gr.setValue('level', escLevel);
gr.setValue('escalation', escId);
gr.setValue('escalation_type', escType);
if (catchAll)
gr.setValue('catch_all', true);
else {
if (escType == this.ESCALATION_TYPE.ROTATE_THROUGH_ROSTER || escType == this.ESCALATION_TYPE.ROTATE_THROUGH_MEMBER)
gr.setValue('roster', rosterOrCustEscStep);
else if (escType == this.ESCALATION_TYPE.CUSTOM)
gr.setValue('custom_escalation_step', rosterOrCustEscStep);
}
if (gr.insert())
return gr;
},
_createContactAttempt: function (escLevelSysId, attempt) {
var gr = new GlideRecord(this.TABLES.ON_CALL_ESCALATION_CON_ATTEMPT);
gr.initialize();
gr.setValue('escalation_level', escLevelSysId);
gr.setValue('contact_attempt', attempt);
if (gr.insert())
return gr;
},
_getUserByDevice: function (deviceId) {
if (!deviceId)
return;
var deviceGr = new GlideRecord(this.TABLES.CMN_NOTIF_DEVICE);
if (deviceGr.get(deviceId))
return deviceGr.user + '';
},
_getConfServiceProviderByEscId: function (escalationId) {
var escalationGr = this._getEscalationGr(escalationId);
if (escalationGr && escalationGr.isValidRecord() && escalationGr.getValue('category') == 'conferencing') {
var wfContextGr = escalationGr.workflow_context.getRefRecord();
if (wfContextGr && wfContextGr.isValidRecord()) {
var confCallGr = wfContextGr.id.getRefRecord();
if (confCallGr && confCallGr.isValidRecord() && confCallGr.canRead())
return confCallGr.getValue('service_provider');
}
}
return '';
},
_createCommunicationDetails: function (escId, attemptId, escltType, commType, escalateeId, commValue, status, attemptedAt) {
attemptedAt = attemptedAt || new GlideDateTime();
status = status || this.COMMUNICATION_STATUS.SENT;
commValue = commValue || '';
var gr = new GlideRecord(this.TABLES.ON_CALL_ESCALATION_COMM);
gr.initialize();
gr.setValue('escalatee_type', escltType);
gr.setValue('contact_attempt', attemptId);
var serviceProvider = this._getConfServiceProviderByEscId(escId);
if (escltType == this.ESCALATEE_TYPE.USER)
gr.setValue('user', escalateeId);
else if (escltType == this.ESCALATEE_TYPE.DEVICE) {
gr.setValue('device', escalateeId);
var user = this._getUserByDevice(escalateeId);
if (escalateeId)
gr.setValue('user', user);
}
gr.setValue('communication_type', commType);
if (this._log.atLevel(GSLog.DEBUG))
this._log.logDebug('[_createCommunicationDetails] commType: ' + commType + ' commValue: ' + commValue);
if (commType == this.COMMUNICATION_TYPE.SMS || commType == this.COMMUNICATION_TYPE.VOICE || serviceProvider == "Telephony")
gr.setValue('phone_number', commValue);
else if (commType == this.COMMUNICATION_TYPE.EMAIL || commType == this.COMMUNICATION_TYPE.SLACK)
gr.setValue(this.COMMUNICATION_TYPE.EMAIL, commValue);
else if (commType == this.COMMUNICATION_TYPE.MOBILE_NOTIFICATION)
gr.setValue(this.COMMUNICATION_TYPE.MOBILE_NOTIFICATION, commValue);
gr.setValue('conference_service_provider', serviceProvider);
gr.setValue('status', status);
gr.setValue('attempted_at', attemptedAt);
if (gr.insert())
return gr;
},
/* override */
getCommResponseChoices: function() {
return this.oncallCommon.getChoiceList(this.TABLES.ON_CALL_ESCALATION_COMM, 'response');
},
/* override */
getAcceptedResponseChoices: function() {
var self = this;
return this.getCommResponseChoices().filter(function(choice) {
return (
choice.value + "" === self.COMMUNICATION_RESPONSE.ACCEPTED ||
choice.value + "" === self.COMMUNICATION_RESPONSE.ACCEPTED_FROM_OTHER_DEVICE ||
choice.value + "" === self.COMMUNICATION_RESPONSE.AUTO_ASSIGNED
);
});
},
/* override */
getRejectedResponseChoices: function() {
var self = this;
return this.getCommResponseChoices().filter(function(choice) {
return (
choice.value + "" === self.COMMUNICATION_RESPONSE.REJECTED ||
choice.value + "" === self.COMMUNICATION_RESPONSE.REJECTED_FROM_OTHER_DEVICE
);
});
},
_getResponseType: function(responseValue) {
if (this.getAcceptedResponseChoices().some(function(choice) { return choice.value + "" === responseValue; }))
return 'accepted';
if (this.getRejectedResponseChoices().some(function(choice) { return choice.value + "" === responseValue;}))
return 'rejected';
return 'invalid';
},
hasEscalations: function(sourceGr) {
if (!sourceGr || !sourceGr.isValidRecord())
return false;
var escalationGr = new GlideRecord(this.TABLES.ON_CALL_ESCALATION);
escalationGr.addQuery('source', sourceGr.getUniqueValue());
escalationGr.addQuery('table', sourceGr.getRecordClassName());
escalationGr.query();
while(escalationGr.next())
if (escalationGr.canRead())
return true;
return false;
},
getEscalations: function (source, table, groupSysId, filterQuery) {
if (!source || !table)
return [];
var escalations = [];
var escalationGr = new GlideRecord(this.TABLES.ON_CALL_ESCALATION);
escalationGr.addQuery('table', table);
escalationGr.addQuery('source', source);
escalationGr.orderByDesc('start_time');
if (filterQuery)
escalationGr.addEncodedQuery(filterQuery);
if (groupSysId)
escalationGr.addQuery('group', groupSysId);
escalationGr.query();
while (escalationGr.next()) {
var escalation = this._getEscalationDetails(escalationGr);
if (escalation)
escalations.push(escalation);
}
return escalations;
},
getEscalationById: function (escalationSysId) {
return this._getEscalationDetails(this._getEscalationGr(escalationSysId));
},
_getEscalationDetails: function (escalationGr) {
if (!escalationGr || !escalationGr.isValidRecord())
return {};
var escalation = this.oncallCommon.toJS(escalationGr, ['start_time', 'end_time', 'category', 'status']);
if (escalationGr.active)
escalation.active = true;
escalation.channels = escalationGr.channels.split(',');
escalation.group = this._getGroupDetails(escalationGr);
escalation.shifts = this._getShiftDetails(escalationGr);
return escalation;
},
_getGroupDetails: function (escalationGr) {
var groupGr = escalationGr.group.getRefRecord();
if (!groupGr.isValidRecord())
return {};
var result = {};
result = this.oncallCommon.toJS(groupGr, ['name', 'description']);
result.initials = this._getNameInitials(result.name.value);
var aggregateMembers = new GlideAggregate('sys_user_grmember');
aggregateMembers.addAggregate('COUNT');
aggregateMembers.addQuery('group', groupGr.getUniqueValue());
aggregateMembers.addQuery('user.active', true);
aggregateMembers.query();
var members = 0;
if (aggregateMembers.next())
members = aggregateMembers.getAggregate('COUNT');
result.groupMembersCount = members;
var managerGr = groupGr.manager.getRefRecord();
if (managerGr.isValidRecord()) {
var manager = GlideUser.getUserByID(managerGr.getUniqueValue());
result.manager = {
avatar: manager.getAvatar() || '',
initials: manager.getInitials() || '',
name: manager.getFullName() || '',
title: manager.getTitle() || '',
value: managerGr.getUniqueValue()
};
}
return result;
},
_getShiftDetails: function (escalationGr) {
var shifts = [];
var escalationLevelGa = new GlideAggregate(this.TABLES.ON_CALL_ESCALATION_LEVEL);
escalationLevelGa.addQuery('escalation', escalationGr.getUniqueValue());
escalationLevelGa.addAggregate('count', 'rota');
escalationLevelGa.groupBy('rota');
escalationLevelGa.query();
while (escalationLevelGa.next()) {
var shiftGr = escalationLevelGa.rota.getRefRecord();
var shift = this.oncallCommon.toJS(shiftGr, ['name', 'active'], true);
var result = this._getEscalationLevels(escalationGr, shiftGr);
if (result.escalation_set) {
shift.escalation_set = result.escalation_set;
shift.is_custom_escalation = true;
}
shift.escalation_levels = result.levels;
shifts.push(shift);
}
return shifts;
},
_getEscalationLevels: function (escalationGr, shiftGr) {
var result = { levels: [] };
var escalationLevelGr = new GlideRecord(this.TABLES.ON_CALL_ESCALATION_LEVEL);
escalationLevelGr.addQuery('escalation', escalationGr.getUniqueValue());
escalationLevelGr.addQuery('rota', shiftGr.getUniqueValue());
escalationLevelGr.orderBy('level');
escalationLevelGr.query();
while (escalationLevelGr.next()) {
var fields = ['escalation_type', 'sys_created_on', 'level_end_time'];
if (escalationLevelGr.roster)
fields.push('roster');
if (escalationLevelGr.custom_escalation_step) {
fields.push('custom_escalation_step');
var escalationStepGr = escalationLevelGr.custom_escalation_step.getRefRecord();
var escalationSetGr = escalationStepGr.escalation_set.getRefRecord();
result.escalation_set = this.oncallCommon.toJS(escalationSetGr, ['name', 'condition', 'description', 'default', 'active', 'table'], true);
}
var escalationLevel = this.oncallCommon.toJS(escalationLevelGr, fields);
escalationLevel.escalation_level = parseInt(escalationLevelGr.level);
if (escalationLevelGr.catch_all)
escalationLevel.catch_all = true;
escalationLevel.status = this.ESCALATION_STATUS.COMPLETE;
var attemptDetails = this._getContactAttempts(escalationLevelGr, escalationLevel);
escalationLevel.contact_attempts = attemptDetails.attempts;
// Set escalation level response type
escalationLevel.response_type = this._getEscalationLevelResponseType(escalationLevel);
if (attemptDetails.acknowledged) {
escalationLevel.acknowledged = attemptDetails.acknowledged;
}
escalationLevel.roster_details = this.getRosterDetails(escalationLevelGr, escalationLevel, this.ESCALATION_STATUS.COMPLETE);
if (escalationLevelGr.additional_escalations)
escalationLevel.additional_escalations = this._getAdditonalEscalations(escalationLevelGr.additional_escalations + '');
result.levels.push(escalationLevel);
}
// Set total completion time of all levels
this.setEscalationLevelsDuration(result.levels, escalationGr);
// If live escalation, process pending levels from escalation plan
if (escalationGr.active) {
var activeLevel = result.levels[result.levels.length - 1];
activeLevel.status = this.ESCALATION_STATUS.ACTIVE;
this.setContactAttemptStatus(activeLevel, this.ESCALATION_STATUS.ACTIVE);
result.levels = result.levels.concat(this.getPendingLevels(activeLevel, escalationGr, shiftGr, result.escalation_set ? result.escalation_set.sys_id.value : ''));
// Handle for overlapping shifts scenario
if (activeLevel.shift_completed) {
activeLevel.status = this.ESCALATION_STATUS.COMPLETE;
this.setContactAttemptStatus(activeLevel, this.ESCALATION_STATUS.COMPLETE);
}
}
return result;
},
_ignoreETA: function(escalationGr) {
return escalationGr.ignore_def_reminders;
},
/*
* return value in seconds
*/
getCatchAllWaitTime: function(shiftGr) {
if (shiftGr.catch_all_wait_time) {
return shiftGr.catch_all_wait_time.getGlideObject().getNumericValue() / 1000;
}
return parseInt(gs.getProperty('com.snc.on_call_rotation.catch_all_wait_time') || 0);
},
_getAdditonalEscalations: function(escalationSysIds) {
var escalations = escalationSysIds.split(',');
var escalationGr = new GlideRecord(this.TABLES.ON_CALL_ESCALATION);
var result = [];
escalations.forEach(function(sysId) {
escalationGr.initialize();
escalationGr.get(sysId);
result.push({ escalation_id: sysId, group: this._getGroupDetails(escalationGr) });
}, this);
return result;
},
setContactAttemptStatus: function(escalationLevel, status) {
if (escalationLevel && escalationLevel.contact_attempts && escalationLevel.contact_attempts.length > 0) {
escalationLevel.contact_attempts.sort(function(attempt1, attempt2) {
return parseInt(attempt1.contact_attempt) - parseInt(attempt2.contact_attempt);
});
escalationLevel.contact_attempts[escalationLevel.contact_attempts.length - 1].status = status;
}
},
getPendingLevels: function(activeLevel, escalationGr, shiftGr, escalationSetSysId) {
var pendingLevels = [];
var escalationStartGdt = new GlideDateTime(escalationGr.start_time);
var rotaPath = new global.OCEscalationPathUtil().getOnCallEscalationShift(escalationGr.group + '', shiftGr.getUniqueValue(), escalationStartGdt, escalationSetSysId, escalationGr.table + '', escalationGr.source + '');
var ignoreETA = this._ignoreETA(escalationGr);
var escalationPlan;
if (escalationSetSysId) {
escalationPlan = this.oncallRotation.getEscalationPlanByEscalationSet(escalationSetSysId, escalationGr.group + '', shiftGr.getUniqueValue(), escalationStartGdt);
} else {
escalationPlan = this.oncallRotation.getEscalationPlan(escalationGr.group + '', escalationStartGdt, shiftGr.getUniqueValue(), escalationGr.source.getRefRecord());
}
if (rotaPath && rotaPath.escalationDetails && rotaPath.escalationDetails.data) {
rotaPath.escalationDetails.data.sort(function(step1, step2) {
return step1.level - step2.level;
});
var prevLevel = {};
rotaPath.escalationDetails.data.forEach(function(step) {
var pendingLevel = {};
if (parseInt(activeLevel.escalation_level) == parseInt(step.level)) {
this._setPendingNotifications(activeLevel, step.rosterDetails, '', ignoreETA, shiftGr);
activeLevel.pending_preferences = this.getPendingLevelPreferences(activeLevel, escalationGr, shiftGr, escalationSetSysId, escalationPlan, ignoreETA);
activeLevel.escAudiences = step.escAudiences;
prevLevel = activeLevel;
}
if (parseInt(activeLevel.escalation_level) < parseInt(step.level)) {
pendingLevel.escalation_level = step.level;
pendingLevel.escAudiences = step.escAudiences;
pendingLevel.status = this.ESCALATION_STATUS.PENDING;
this._setPendingNotifications(pendingLevel, step.rosterDetails, prevLevel.last_contact_at, ignoreETA, shiftGr);
pendingLevel.pending_preferences = this.getPendingLevelPreferences(pendingLevel, escalationGr, shiftGr, escalationSetSysId, escalationPlan, ignoreETA);
prevLevel = pendingLevel;
pendingLevels.push(pendingLevel);
}
}, this);
}
return pendingLevels;
},
_setPendingNotifications: function(escalationLevel, rosterDetails, lastContactedAt, ignoreETA, shiftGr) {
if (!escalationLevel || !rosterDetails)
return;
var repeats = [];
var lastReminderIndex = 0, lastReminder = {};
var tempGdt = new GlideDateTime();
if (escalationLevel.status == this.ESCALATION_STATUS.ACTIVE)
tempGdt.setDisplayValueInternal(escalationLevel.sys_created_on.display_value_internal);
if (escalationLevel.status == this.ESCALATION_STATUS.PENDING && lastContactedAt)
tempGdt.setDisplayValueInternal(lastContactedAt.display_value_internal);
escalationLevel.roster_details = JSON.parse(JSON.stringify(rosterDetails)); //deep copy
// Handle for catch-all
if (rosterDetails.catch_all) {
if (escalationLevel.status === this.ESCALATION_STATUS.PENDING) {
escalationLevel.catch_all = true;
escalationLevel.roster_details.notifications = { repeats: []};
var catchAllNotif = {};
if (this._log.atLevel(GSLog.DEBUG))
this._log.logDebug('[_setPendingNotifications] ignoreETA: ' + ignoreETA);
if (ignoreETA)
catchAllNotif.message = gs.getMessage('Escalation - Pending');
else {
catchAllNotif.message = gs.getMessage('Escalation ETA - {0}', tempGdt.getDisplayValue());
catchAllNotif.contact_eta = this.getDateTimeJS(tempGdt);
}
catchAllNotif.status = this.ESCALATION_STATUS.PENDING;
catchAllNotif.is_attempt = true;
escalationLevel.roster_details.notifications.repeats.push(catchAllNotif);
return;
} else {
escalationLevel.roster_details.notifications = {};
rosterDetails.isLastStep = true;
}
}
if (rosterDetails.notifications.escalated_at)
escalationLevel.roster_details.notifications.escalated_at = escalationLevel.sys_created_on;
if (rosterDetails.notifications.overallTimeSec)
escalationLevel.step_time_sec = rosterDetails.notifications.overallTimeSec;
// Add overall time info for pending step
if(escalationLevel.status == this.ESCALATION_STATUS.PENDING) {
if (rosterDetails.notifications.overallTime) {
var escalationNotif = {};
escalationNotif.status = this.ESCALATION_STATUS.PENDING;
escalationNotif.is_attempt = true;
if (this._log.atLevel(GSLog.DEBUG))
this._log.logDebug('[_setPendingNotifications] ignoreETA: ' + ignoreETA);
if (ignoreETA)
escalationNotif.message = gs.getMessage('Escalation - Pending');
else {
escalationNotif.message = gs.getMessage('Escalation ETA - {0}', tempGdt.getDisplayValue());
escalationNotif.contact_eta = this.getDateTimeJS(tempGdt);
}
repeats.push(escalationNotif);
}
}
// Add completed reminders for active step
if (escalationLevel.status == this.ESCALATION_STATUS.ACTIVE) {
repeats = this._getCompletedReminders(escalationLevel);
if (repeats.length > 0) {
lastReminderIndex = repeats.length - 1;
lastReminder = repeats[lastReminderIndex];
tempGdt.setDisplayValueInternal(lastReminder.contacted_at.display_value_internal);
escalationLevel.last_contact_at = lastReminder.contacted_at;
lastReminder.status = this.ESCALATION_STATUS.ACTIVE;
}
}
// Add Pending reminders for active/pending steps
if (rosterDetails.notifications.repeats) {
rosterDetails.notifications.repeats.forEach(function(reminder, index) {
var notification = {
status: this.ESCALATION_STATUS.PENDING
};
if (escalationLevel.status == this.ESCALATION_STATUS.PENDING || (escalationLevel.status == this.ESCALATION_STATUS.ACTIVE && index >= lastReminderIndex)) {
tempGdt.addSeconds(parseInt(reminder.seconds));
if (this._log.atLevel(GSLog.DEBUG))
this._log.logDebug('[_setPendingNotifications] ignoreETA: ' + ignoreETA);
if (ignoreETA)
notification.message = gs.getMessage('Reminder {0}', (index + 1) + '');
else
notification.message = gs.getMessage('Reminder {0} - {1}', [(index + 1) + '', tempGdt.getDisplayValue()]);
notification.seconds = reminder.seconds;
notification.is_attempt = true;
notification.contact_eta = this.getDateTimeJS(tempGdt);
escalationLevel.last_contact_at = this.getDateTimeJS(tempGdt);
repeats.push(notification);
}
}, this);
}
// Next escalation time
if (rosterDetails.gapBetweenSteps && rosterDetails.gapBetweenStepsSec && !ignoreETA) {
var nextEscalationInfo = {
status: this.ESCALATION_STATUS.PENDING,
message: rosterDetails.gapBetweenSteps,
is_attempt: false
};
if (tempGdt && tempGdt.isValid()) {
tempGdt.addSeconds(parseInt(rosterDetails.gapBetweenStepsSec));
if (this._log.atLevel(GSLog.DEBUG))
this._log.logDebug('[_setPendingNotifications] tempGdt: ' + tempGdt);
nextEscalationInfo.message = gs.getMessage('Next escalation - {0}', tempGdt.getDisplayValue());
nextEscalationInfo.contact_eta = this.getDateTimeJS(tempGdt);
escalationLevel.last_contact_at = this.getDateTimeJS(tempGdt);
if (rosterDetails.isLastStep) {
nextEscalationInfo.message = gs.getMessage('Escalation ends at {0}', tempGdt.getDisplayValue());
nextEscalationInfo.end = true;
escalationLevel.timer_message = gs.getMessage('Time until end of escalation');
}
}
repeats.push(nextEscalationInfo);
}
// Update timer
if (!ignoreETA) {
if (rosterDetails.catch_all)
tempGdt.add(this.getCatchAllWaitTime(shiftGr) * 1000);
escalationLevel.timer = this.getDateTimeJS(tempGdt);
if (!escalationLevel.timer_message) {
if (this._log.atLevel(GSLog.DEBUG))
this._log.logDebug('[_setPendingNotifications] lastStep: ' + rosterDetails.isLastStep);
if (!rosterDetails.isLastStep)
escalationLevel.timer_message = gs.getMessage('Time until next escalation');
else
escalationLevel.timer_message = gs.getMessage('Time until end of escalation');
}
}
// Check if shift is completed - overlapping shift scenario
if (escalationLevel.status === this.ESCALATION_STATUS.ACTIVE && rosterDetails.isLastStep && !ignoreETA) {
var lastNotification = repeats[repeats.length - 1];
var timeGdt;
if (lastNotification.contact_eta) // when waiting for next step
timeGdt = new GlideDateTime(lastNotification.contact_eta.value);
else if (lastNotification.contacted_on) // when last reminder sent and no time_to_next_step
timeGdt = new GlideDateTime(lastNotification.contacted_at.value);
if (timeGdt && new GlideDateTime().compareTo(timeGdt) == 1) { // timeGdt has elapsed
escalationLevel.shift_completed = true;
lastNotification.status = this.ESCALATION_STATUS.COMPLETE;
}
}
escalationLevel.roster_details.notifications.repeats = repeats;
},
getPendingLevelPreferences: function(escalationLevel, escalationGr, shiftGr, escalationSetSysId, escalationPlan, ignoreETA) {
if (!escalationLevel || !shiftGr)
return [];
var contactPreferences = [];
var supportedChannels = escalationGr.channels.split(',');
// Handle catch-all scenario
if (escalationLevel.catch_all) {
var catchAllStartGdt = new GlideDateTime();
if (escalationLevel.contact_eta)
catchAllStartGdt.setDisplayValueInternal(escalationLevel.contact_eta.display_value_internal);
contactPreferences = this.contactPrefUtil.getCatchAllContacts(shiftGr.getUniqueValue(), catchAllStartGdt);
contactPreferences.forEach(function(pref) {
if (!ignoreETA)
pref.fetch_time = catchAllStartGdt.getDisplayValue();
pref.contact_preferences = pref.contact_preferences.filter(function(cp) {
return supportedChannels.indexOf(cp.type) > -1;
});
pref.user_preferences = pref.user_preferences.filter(function(cp) {
return supportedChannels.indexOf(cp.type) > -1;
});
});
return contactPreferences;
}
var escalatee;
if (escalationPlan)
escalatee = escalationPlan[escalationLevel.escalation_level - 1];
var contactAttempt = 1;
if (escalationLevel.roster_details && escalationLevel.roster_details.notifications && escalationLevel.roster_details.notifications.repeats) {
escalationLevel.roster_details.notifications.repeats.forEach(function(attempt, index) {
if (attempt.status == this.ESCALATION_STATUS.PENDING && attempt.is_attempt) {
var startGdt;
if (attempt.contact_eta)
startGdt = new GlideDateTime(attempt.contact_eta.value);
var preferences = this.contactPrefUtil.getPreferencesByContactAttempt(escalationGr.group + '', escalationLevel.escalation_level, startGdt, shiftGr.getUniqueValue(), escalationSetSysId, escalationGr.table + '', escalationGr.source + '', index+1, escalatee);
if (preferences) {
preferences.forEach(function(pref) {
// Filter contact preferences based on supported channels
pref.contact_preferences = pref.contact_preferences.filter(function(cp) {
return supportedChannels.indexOf(cp.type) > -1;
});
// Fetch user preferences
if (pref.type === this.ESCALATEE_TYPE.USER) {
var userPrefs = this.contactPrefUtil.getUserPreferences(pref.sys_id, index + 1, startGdt);
if (userPrefs.length > 0)
pref.user_preferences = pref.user_preferences.concat(userPrefs);
}
// Filter user preferences based on supported channels
pref.user_preferences = pref.user_preferences.filter(function(up) {
return supportedChannels.indexOf(up.type) > -1;
});
// Fetch default preferences if no preference found
if (pref.contact_preferences.length == 0 && pref.user_preferences.length === 0) {
pref.user_preferences = pref.user_preferences.concat(this.contactPrefUtil._getDefaultEscalateePreferences(pref.sys_id, supportedChannels));
}
if (!ignoreETA)
pref.fetch_time = attempt.contact_eta.display_value;
}, this);
}
contactPreferences = contactPreferences.concat(preferences);
}
}, this);
}
return contactPreferences;
},
setEscalationLevelsDuration: function(levels, escalationGr) {
if (!levels || levels.length === 0)
return;
for (var i = 0; i < levels.length - 1; i++)
if (levels[i].sys_created_on && levels[i + 1].sys_created_on)
levels[i].total_time = this.getDuration(levels[i].sys_created_on.value, levels[i + 1].sys_created_on.value);
// Calculate duration for the last escalation level if past escalation
var lastLevel = levels[levels.length - 1];
if (!escalationGr.active) {
if (lastLevel.response_type != "no_answer" && lastLevel.acknowledged && lastLevel.acknowledged.at)
lastLevel.total_time = this.getDuration(lastLevel.sys_created_on.value, lastLevel.acknowledged.at.value);
else if (lastLevel.level_end_time && lastLevel.level_end_time.value)
lastLevel.total_time = this.getDuration(lastLevel.sys_created_on.value, lastLevel.level_end_time.value);
else if (escalationGr.end_time)
lastLevel.total_time = this.getDuration(lastLevel.sys_created_on.value, escalationGr.end_time.getGlideObject().getValue());
}
},
getDuration: function(start, end) {
var gdtStart = new GlideDateTime(start);
var gdtEnd = new GlideDateTime(end);
var duration = GlideDateTime.subtract(gdtStart, gdtEnd);
return duration.getDisplayValue();
},
getDateTimeJS: function(gdt) {
var dateTime = {};
if (gdt && gdt.isValid()) {
dateTime.value = gdt.getValue();
dateTime.display_value = gdt.getDisplayValue();
dateTime.display_value_internal = gdt.getDisplayValueInternal();
}
return dateTime;
},
getRosterDetails: function(escalationLevelGr, escalationLevelJS, escalationStatus) {
if (!escalationLevelGr.isValidRecord())
return {};
var result = {
name: '',
notifications: {
repeats: []
}
};
// Add roster name
if (!escalationLevelJS.roster_details && escalationLevelGr && escalationLevelGr.isValidRecord())
if (escalationLevelGr.escalation_type == this.ESCALATION_TYPE.CUSTOM && escalationLevelGr.custom_escalation_step) {
result.name = escalationLevelGr.custom_escalation_step.getRefRecord().name;
} else {
if (this._log.atLevel(GSLog.DEBUG))
this._log.logDebug('[getRosterDetails] escalationLevelGr.catch_all: ' + escalationLevelGr.catch_all);
if (escalationLevelGr.catch_all)
result.name = gs.getMessage('Catch All');
else
if (escalationLevelGr.roster)
result.name = escalationLevelGr.roster.getRefRecord().getDisplayValue();
}
// Add notification details
if (escalationStatus == this.ESCALATION_STATUS.COMPLETE && escalationLevelJS) {
result.notifications.escalated_at = escalationLevelJS.sys_created_on;
if (escalationLevelJS.acknowledged)
result.notifications.acknowledged = escalationLevelJS.acknowledged;
// Add reminder details
result.notifications.repeats = this._getCompletedReminders(escalationLevelJS);
}
return result;
},
_getCompletedReminders: function(escalationLevel) {
var reminders = [];
// Add reminder details
if (escalationLevel && escalationLevel.contact_attempts && escalationLevel.contact_attempts.length > 0) {
escalationLevel.contact_attempts.forEach(function(attempt, index) {
if (this._log.atLevel(GSLog.DEBUG))
this._log.logDebug('[_getCompletedReminders] attempt: ' + JSON.stringify(attempt));
var message = '';
if (attempt.first_contacted_at && !attempt.acknowledged) {
message = (index === 0) ? gs.getMessage('Escalated at {0}', attempt.first_contacted_at.display_value) :
gs.getMessage('Reminder {0} - {1}', [index, attempt.first_contacted_at.display_value]);
reminders.push({
status: this.ESCALATION_STATUS.COMPLETE,
message: message,
contacted_at: attempt.first_contacted_at
});
}
if (attempt.acknowledged && attempt.acknowledged.at) {
message = (index == 0) ? gs.getMessage('Escalated at {0}', attempt.acknowledged.at.display_value)
: gs.getMessage('Reminder {0} - {1}', [index, attempt.acknowledged.at.display_value]);
var ack = {};
ack.status = this.ESCALATION_STATUS.COMPLETE;
ack.contacted_at = attempt.acknowledged.at;
ack.response_type = attempt.response_type;
switch (attempt.response_type) {
case this.COMMUNICATION_RESPONSE_TYPE.ACCEPTED:
message = gs.getMessage('Accepted at {0}', attempt.acknowledged.at.display_value);
break;
case this.COMMUNICATION_RESPONSE_TYPE.REJECTED:
message = gs.getMessage('Rejected at {0}', attempt.acknowledged.at.display_value);
break;
case this.COMMUNICATION_RESPONSE_TYPE.PARTIAL_REJECT:
break;
default:
message = gs.getMessage('Invalid response at {0}', attempt.acknowledged.at.display_value);
ack.response_type = this.COMMUNICATION_RESPONSE_TYPE.INVALID;
}
ack.message = message;
reminders.push(ack);
}
}, this);
}
return reminders;
},
/*
* no_answer, accepted, rejected
*/
_getEscalationLevelResponseType: function(escalationlevel) {
if (!escalationlevel || !escalationlevel.contact_attempts)
return '';
var responseType = this.COMMUNICATION_RESPONSE_TYPE.NO_ANSWER;
escalationlevel.contact_attempts.forEach(function(attempt) {
if (attempt.response_type) {
if (attempt.response_type === this.COMMUNICATION_RESPONSE_TYPE.REJECTED)
responseType = this.COMMUNICATION_RESPONSE_TYPE.REJECTED;
else
if (attempt.response_type === this.COMMUNICATION_RESPONSE_TYPE.ACCEPTED)
responseType = this.COMMUNICATION_RESPONSE_TYPE.ACCEPTED;
}
}, this);
return responseType;
},
/*
* no_answer, accepted, rejected, partial_reject
*/
_getAttemptResponseType: function(attempt) {
var responseType = this.COMMUNICATION_RESPONSE_TYPE.NO_ANSWER;
if (!attempt || !attempt.acknowledged || !attempt.communications.length === 0)
return responseType;
var escalateeCount = attempt.escalatee_count || 0;
if (attempt.rejected_count && attempt.rejected_count > 0) {
if (attempt.rejected_count === escalateeCount)
responseType = this.COMMUNICATION_RESPONSE_TYPE.REJECTED;
else
responseType = this.COMMUNICATION_RESPONSE_TYPE.PARTIAL_REJECT;
}
// Override if any one is accepted
if (attempt.accepted_count && attempt.accepted_count > 0)
responseType = this.COMMUNICATION_RESPONSE_TYPE.ACCEPTED;
return responseType;
},
_getContactAttempts: function (escalationLevelGr) {
var result = {
attempts: []
};
var contactAttemptGr = new GlideRecord(this.TABLES.ON_CALL_ESCALATION_CON_ATTEMPT);
contactAttemptGr.addQuery('escalation_level', escalationLevelGr.getUniqueValue());
contactAttemptGr.orderBy('contact_attempt');
contactAttemptGr.query();
this.baseCommMap = undefined;
while (contactAttemptGr.next()) {
var contactAttempt = {};
contactAttempt.contact_attempt = contactAttemptGr.getValue('contact_attempt');
var commDetails = this._getCommunications(contactAttemptGr);
contactAttempt.communications = commDetails.communications;
contactAttempt.status = this.ESCALATION_STATUS.COMPLETE;
if (commDetails.acknowledged) {
// There can be only one ack from any communication record in one attempt
contactAttempt.acknowledged = commDetails.acknowledged;
result.acknowledged = commDetails.acknowledged;
}
contactAttempt.escalatee_count = commDetails.escalatee_count || 0;
contactAttempt.accepted_count = commDetails.accepted_count || 0;
contactAttempt.rejected_count = commDetails.rejected_count || 0;
// Set contact attempt response type
contactAttempt.response_type = this._getAttemptResponseType(contactAttempt);
if (commDetails.first_contacted_at)
contactAttempt.first_contacted_at = commDetails.first_contacted_at;
result.attempts.push(contactAttempt);
}
this.baseCommMap = undefined;
return result;
},
_getCommunications: function (contactAttemptGr) {
var result = {
communications: []
};
var communicationGr = new GlideRecord(this.TABLES.ON_CALL_ESCALATION_COMM);
communicationGr.addQuery('contact_attempt', contactAttemptGr.getUniqueValue());
communicationGr.orderBy('attempted_at');
communicationGr.query();
var rejectedCount = 0, escalateeCount = 0, acceptedCount = 0, escalateeSysId;
var escalateeAcceptedMap = {}, escalateeMap = {}, escalateeRejectedMap = {};
var lastRejectResponse = null;
while (communicationGr.next()) {
var communication = {};
var fields = ['communication_type', 'escalatee_type', 'attempted_at', 'status'];
if (communicationGr.response)
fields.push('response');
if (communicationGr.responded_at)
fields.push('responded_at');
if (communicationGr.email)
fields.push('email');
if (communicationGr.phone_number)
fields.push('phone_number');
if (communicationGr.conference_service_provider)
fields.push('conference_service_provider');
communication = this.oncallCommon.toJS(communicationGr, fields, true);
if (communication && communication.conference_service_provider && communication.conference_service_provider.display_value) {
var serviceProvider = communication.conference_service_provider.display_value;
var serviceProviders = this.CONFERENCE_SERVICE_PROVIDER;
for (var i = 0; i < serviceProviders.length; i++) {
if (serviceProvider == serviceProviders[i].value) {
communication.conference_service_provider.display_value = serviceProviders[i].translation;
break;
}
}
}
var liveFeedApi = new SNC.LiveFeedApi();
if (communication.escalatee_type.value == this.ESCALATEE_TYPE.USER && communicationGr.user) {
var userGr = this._getUser(communicationGr.user + "");
var userContactNumber = '';
if (!this.checkAccess) {
userContactNumber = (userGr.mobile_phone + '' != '') ? userGr.mobile_phone + '' : userGr.phone + '';
} else if ((userGr.mobile_phone + '' != '') && userGr.mobile_phone.canRead()){
userContactNumber = userGr.mobile_phone + '';
} else if (userGr.phone.canRead()) {
userContactNumber = userGr.phone + '';
}
var avatarPath = GlideAvatarFinder.getAvatarPath(userGr.getUniqueValue());
if (avatarPath)
avatarPath = '/' + avatarPath;
communication.user = {
sys_id: userGr.getUniqueValue(),
avatar: avatarPath || '',
initials: liveFeedApi.getInitials(userGr.name + '') || '',
name: userGr.getValue('name'),
title: userGr.getValue('title'),
email: (!this.checkAccess || userGr.email.canRead()) ? userGr.email + '' : '',
contact_number: userContactNumber || ""
};
escalateeSysId = communication.user.sys_id;
if (!escalateeMap[escalateeSysId]) {
escalateeMap[escalateeSysId] = 1;
escalateeCount++;
}
}
if (communicationGr.device) {
var deviceGr = communicationGr.device.getRefRecord();
communication.device = this.oncallCommon.toJS(deviceGr, ['name', 'type', 'phone_number', 'email_address'], true);
communication.device.initials = this._getNameInitials(deviceGr.name);
escalateeSysId = communication.device.sys_id.value;
if (!escalateeMap[escalateeSysId]) {
escalateeMap[escalateeSysId] = 1;
escalateeCount++;
}
}
if (communication.response) {
// Available choices = accepted, rejected, invalid
communication.response_type = this._getResponseType(communication.response.value);
// Populate result.acknowledged only when an attempt is either accepted or is rejected by all.
if (communication.responded_at && !result.acknowledged){
if (communication.response_type === this.COMMUNICATION_RESPONSE_TYPE.ACCEPTED) {
result.acknowledged = {
at: communication.responded_at,
response: communication.response,
response_type: communication.response_type
};
} else if (communication.response_type === this.COMMUNICATION_RESPONSE_TYPE.REJECTED) {
if (!lastRejectResponse || lastRejectResponse.at.before(communication.responded_at)) {
lastRejectResponse = {
at: communication.responded_at,
response: communication.response,
response_type: communication.response_type
};
}
}
}
if (communication.response_type === this.COMMUNICATION_RESPONSE_TYPE.ACCEPTED) {
escalateeMap[escalateeSysId] = {response: this.COMMUNICATION_RESPONSE_TYPE.ACCEPTED};
if (!escalateeAcceptedMap[escalateeSysId]) {
escalateeAcceptedMap[escalateeSysId] = 1;
acceptedCount++;
}
} else if (communication.response_type === this.COMMUNICATION_RESPONSE_TYPE.REJECTED) {
escalateeMap[escalateeSysId] = { response: this.COMMUNICATION_RESPONSE_TYPE.REJECTED, communication: JSON.parse(JSON.stringify(communication))};
// Rejected and invalid response considered rejected
if (!escalateeRejectedMap[escalateeSysId]) {
escalateeRejectedMap[escalateeSysId] = 1;
rejectedCount++;
}
}
} else
if (escalateeMap[escalateeSysId] && escalateeMap[escalateeSysId].response !== this.COMMUNICATION_RESPONSE_TYPE.ACCEPTED && this.COMMUNICATION_RESPONSE_TYPE.REJECTED !== escalateeMap[escalateeSysId].response)
escalateeMap[escalateeSysId] = { response: this.COMMUNICATION_RESPONSE_TYPE.NO_ANSWER };
if (!result.first_contacted_at)
result.first_contacted_at = communication.attempted_at;
result.communications.push(communication);
}
result.accepted_count = acceptedCount;
result.rejected_count = rejectedCount;
result.escalatee_count = escalateeCount;
if (rejectedCount === escalateeCount && lastRejectResponse) {
result.acknowledged = lastRejectResponse;
}
if (!this.baseCommMap)
this.baseCommMap = escalateeMap;
else
result.communications = result.communications.concat(this._getCommunicationGaps(this.baseCommMap, escalateeMap));
return result;
},
_getUser: function(userSysId) {
if (!this._userCache) {
this._userCache = {};
}
if (!this._userCache[userSysId]) {
var userGr = new GlideRecord('sys_user');
userGr.get(userSysId);
this._userCache[userSysId] = userGr;
}
return this._userCache[userSysId];
},
_getCommunicationGaps: function(baseCommMap, currentCommMap) {
if (!baseCommMap || !currentCommMap)
return;
var dummyComms = [];
for(var escalateeId in baseCommMap) {
if (baseCommMap.hasOwnProperty(escalateeId) && baseCommMap[escalateeId].response === this.COMMUNICATION_RESPONSE_TYPE.REJECTED) {
// Now next reminder may or may not have been sent depending on workflow implemention
if (!currentCommMap[escalateeId]) {
// No reminder sent i.e. further communication is discontinued
var lastComm = baseCommMap[escalateeId].communication;
var dummy = {};
dummy.escalatee_type = lastComm.escalatee_type;
if (lastComm.escalatee_type.value === this.ESCALATEE_TYPE.USER)
dummy.user = lastComm.user;
if (lastComm.escalatee_type.value === this.ESCALATEE_TYPE.DEVICE)
dummy.device = lastComm.device;
if (this._log.atLevel(GSLog.DEBUG))
this._log.logDebug('[_getCommunicationGaps] set dummy');
dummy.isDummy = true;
dummy.status = { value: 'discontinued', display_value: gs.getMessage('Discontinued') };
dummy.communication_type = { value: 'dummy', display_value: gs.getMessage('Dummy') };
dummy.display_message = gs.getMessage('No communication sent as user has rejected earlier');
dummyComms.push(dummy);
}
}
if (currentCommMap[escalateeId]) {
// Update base map
baseCommMap[escalateeId] = currentCommMap[escalateeId];
}
}
return dummyComms;
},
_getNameInitials: function (name) {
var splitName = name.split(' ');
var initials = '';
for (var index = 0; index < splitName.length && index < 2; index++)
initials += splitName[index].charAt(0).toUpperCase();
return initials;
},
updateCanceledEscalation: function (contextSysId) {
var gr = new GlideRecord(this.TABLES.ON_CALL_ESCALATION);
gr.addActiveQuery();
gr.addQuery('workflow_context', contextSysId);
gr.query();
if (gr.next()) {
this.logEscalationEnd(gr.getUniqueValue(), this.ON_CALL_ESCALATION_FIELDS.STATUS.CANCELLED);
}
},
getCatchAllWaitTimeAnnotation: function() {
if (this._log.atLevel(GSLog.DEBUG))
this._log.logDebug('[getCatchAllWaitTimeAnnotation]');
var message = '';
if (gs.getProperty('glide.ui.escape_text') + '' === 'true')
message = gs.getMessage('Time to wait, for Catch-All response, is set to {0} by administrator. To override, users can specify the <b>"Catch-all wait time"</b> below.', new GlideDuration(parseInt(gs.getProperty('com.snc.on_call_rotation.catch_all_wait_time') || 0) * 1000).getDisplayValue());
else
message = GlideStringUtil.unEscapeHTML(gs.getMessage('Time to wait, for Catch-All response, is set to {0} by administrator. To override, users can specify the <b>"Catch-all wait time"</b> below.', new GlideDuration(parseInt(gs.getProperty('com.snc.on_call_rotation.catch_all_wait_time') || 0) * 1000).getDisplayValue() ));
return message;
},
setCheckAccess: function(checkAccess) {
this.checkAccess = checkAccess;
return this;
},
type: 'OnCallEscalationUtilSNC'
};
Sys ID
e3e4ae19c70200102e65e122c7c26057