Name
global.OnCallCommonSNC
Description
No description available
Script
var OnCallCommonSNC = Class.create();
OnCallCommonSNC.COMMUNICATION_TYPES = {
EMAIL: 'email',
SMS: 'sms',
VOICE: 'voice',
SLACK: 'slack',
TASK: 'task',
MOBILE_NOTIFICATION: 'mobile_notification',
TEAMS: 'teams',
CONFERENCE_ESCALATION: 'conference_escalation'
};
OnCallCommonSNC.COMMUNICATION_TYPE_LABELS = {
EMAIL: gs.getMessage('E-mail'),
SMS: gs.getMessage('SMS'),
VOICE: gs.getMessage('Voice'),
SLACK: gs.getMessage('Slack'),
TASK: gs.getMessage('Task'),
TEAMS: gs.getMessage('Microsoft Teams'),
CONFERENCE_ESCALATION: gs.getMessage('Conference escalation')
};
OnCallCommonSNC.TEAMS_NOTIFICATION_ACTION_IDENTIFIER = 'com.snc.on_call_rotation';
OnCallCommonSNC.TEAMS_NOTIFICATION_PROCESSOR = 'sn_now_teams';
OnCallCommonSNC.MOBILE_PUSH_NOTIFICATION = 'Mobile push notification';
OnCallCommonSNC.prototype = {
initialize: function () {
this.escalation_rule_rota_overlap = {
START: 'start',
END: 'end',
ALL: 'all'
};
this.TABLES = {
CMN_ROTA: 'cmn_rota',
CMN_ROTA_MEMBER: 'cmn_rota_member',
CMN_ROTA_ROSTER: 'cmn_rota_roster',
SYS_USER_GRMEMBER: 'sys_user_grmember',
USER: 'sys_user',
SYS_CHOICE: 'sys_choice',
SYS_UI_ACTION: 'sys_ui_action'
};
this.SYS_USER_FIELDS = {
TIMEZONE: "time_zone"
};
this.CATCH_ALL_WAIT_TIME_PROPERTY = "com.snc.on_call_rotation.catch_all_wait_time";
},
/**
* Constrain a provided encoded query by the start and end dates that concern the query.
*
* @param limitedBy [String]
* @param startDate [String]
* @param endDate [String]
* @return [String] encoded query
**/
getDateLimitedEncQuery: function (limitedBy, startDate, endDate) {
var startDateTime = this.formatDateStr(this.getGlideDateTimeStr(false, startDate));
var endDateTime = this.formatDateStr(this.getGlideDateTimeStr(true, endDate));
var and = "^";
var or = "^NQ";
var startInEndIn = limitedBy + and + "start_date_time>=" + startDateTime + and + "end_date_time<=" + endDateTime;
var startOutEndIn = limitedBy + and + "start_date_time<=" + startDateTime + and + "end_date_time>=" + startDateTime + and + "end_date_time<=" + endDateTime;
var startInEndOut = limitedBy + and + "start_date_time>=" + startDateTime + and + "start_date_time<=" + endDateTime + and + "end_date_time>=" + endDateTime;
var startOutEndOut = limitedBy + and + "start_date_time<=" + startDateTime + and + "end_date_time>=" + endDateTime;
var repeatNotNull = limitedBy + and + "repeat_type!=null" + and + "repeat_until=00000000^ORrepeat_until=null^ORrepeat_until>=" + startDateTime.split("T")[0];
var encodedQueryStr = startInEndIn + or + startOutEndIn + or + startInEndOut + or + startOutEndOut + or + repeatNotNull;
return encodedQueryStr;
},
/**
* @param endOfDay [Boolean]: End of current day.
* @param date [String]: a string in the yyyy-MM-dd format. A default is applied if not provided which is the start of day, unless endOfDay is true
* return: yyyy-MM-dd hh:mm:ss [String]
*
*/
getGlideDateTimeStr: function (endOfDay, dateStr) {
var timePart = endOfDay ? " 23:59:59" : " 00:00:00";
var dateTime = "";
if (JSUtil.nil(dateStr)) {
var currentDateTime = new GlideDateTime();
dateTime = new GlideDateTime(currentDateTime.getDate() + timePart).getValue();
} else
dateTime = new GlideDateTime(dateStr + timePart).getValue();
return dateTime;
},
/*
* Input: yyyy-MM-dd hh:mm:ss
* Output: yyyyMMddThhmmssZ
* e.g.
* 2016-08-02 00:00:00
* 20160802T000000Z
*/
formatDateStr: function (strDate) {
var iCalDate = strDate + "";
iCalDate = iCalDate.replace(/\s+/g, "T");
iCalDate = iCalDate.replace(/\-/g, "");
iCalDate = iCalDate.replace(/\:/g, "");
iCalDate += "Z"; // Denotes UTC, hence expecting UTC input
return iCalDate;
},
/**
* Rhino engine parseInt does not behave like JS parseInt dumping floats
*/
parseInt: function (str) {
if (!str)
return 0;
var number = str.split(".");
return parseInt(number[0]);
},
/**
* ensure number is at least 2 digits long
*/
pad: function (number) {
number = parseInt(number || "");
if (isNaN(number))
return "00";
if (number >= 10)
return number;
return "0" + number;
},
/**
* git [GlideIntegerTime]
* return time hhmmss
*/
gitToTime: function (git) {
return (this.pad(git.getHour()) + "") + (this.pad(git.getMinute()) + "") + (this.pad(git.getSecond()) + "");
},
/**
* gd [GlideDate]
* return time yyyymmdd
*/
gdToDate: function (gd) {
return (gd.getYearUTC() + "") + (this.pad(gd.getMonthUTC()) + "") + (this.pad(gd.getDayOfMonthUTC()) + "");
},
/**
* gdt [GlideDateTime]
* return time yyyymmdd
*/
gdtToDate: function (gdt) {
return (gdt.getYearLocalTime() + "") + (this.pad(gdt.getMonthLocalTime()) + "") + (this.pad(gdt.getDayOfMonthLocalTime()) + "");
},
/*
* Check if overlapping shift is allowed for a group
* @param String groupId
*
* @return true if overlapping shift is allowed for a group
*/
isOverlapAllowed: function (groupId) {
var onCallGroupPreferenceGR = new GlideRecord("on_call_group_preference");
var hasGroupPreference = onCallGroupPreferenceGR.get("group", groupId);
if ((hasGroupPreference && (onCallGroupPreferenceGR.getValue("allow_rota_overlap") === 'yes' || (onCallGroupPreferenceGR.getValue("allow_rota_overlap") === 'default' && gs.getProperty('com.snc.on_call_rotation.allow_rota_overlap') == 'true'))) || (!hasGroupPreference && gs.getProperty('com.snc.on_call_rotation.allow_rota_overlap') == 'true'))
return true;
return false;
},
getEscalationSettings: function (groupId) {
var onCallGroupPreferenceGR = new GlideRecord("on_call_group_preference");
var hasGroupPreference = onCallGroupPreferenceGR.get("group", groupId);
var escalationSetting;
if (hasGroupPreference) {
escalationSetting = onCallGroupPreferenceGR.getValue("escalation_rule_rota_overlap");
if (escalationSetting == 'default') {
escalationSetting = gs.getProperty('com.snc.on_call_rotation.escalation_rule_rota_overlap');
}
} else {
escalationSetting = gs.getProperty('com.snc.on_call_rotation.escalation_rule_rota_overlap');
}
return escalationSetting;
},
toJS: function (gr, requiredFields, skipACL) {
if (!gr)
return;
if (!gr.canRead() && !skipACL)
return;
if (!requiredFields)
requiredFields = [];
var obj = {};
obj.sys_id = {};
obj.sys_id.value = gr.getUniqueValue();
var el = gr.getFields();
for (var i = 0; i < el.size(); i++) {
var elem = el.get(i);
var elName = elem.getName() + "";
var fieldType = elem.getED().getInternalType();
if (requiredFields.indexOf(elName) == -1 || fieldType == 'journal' || fieldType == 'journal_input' || fieldType == 'journal_list')
continue;
obj[elName] = {};
obj[elName].canRead = elem.canRead();
if (obj[elName].canRead || skipACL) {
/* This if check is only for a specific condition for on-call edit escalation details, if the sys_users glidelist contains any user with ',' in the userId */
if (fieldType == 'glide_list' && elem.getTableName() == 'cmn_rota_esc_step_def' && elName == 'sys_users' && elem.toString().split(',').length != elem.getDisplayValue().split(',').length) {
var userDisplayValues = [];
var userGr = new GlideRecord('sys_user');
userGr.addQuery('sys_id', 'IN', elem.toString());
userGr.query();
while (userGr.next()) {
userDisplayValues.push(userGr.getDisplayValue().replaceAll(',', ' '));
}
obj[elName].value = elem.toString();
obj[elName].display_value = userDisplayValues.join(',');
}
else {
obj[elName].display_value = elem.getDisplayValue();
obj[elName].value = elem.toString();
}
obj[elName].label = elem.getLabel();
if (gr[elName] != null && gr[elName].getGlideObject() && gr[elName]
.getGlideObject().getDisplayValueInternal()) {
obj[elName].display_value_internal = gr[elName].getGlideObject().getDisplayValueInternal();
}
}
}
return obj;
},
hasActiveRotas: function (groupId) {
var rotaGr = new GlideRecord(this.TABLES.CMN_ROTA);
rotaGr.addActiveQuery();
rotaGr.addQuery("group", groupId);
rotaGr.query();
return rotaGr.hasNext();
},
/**
* @param managerGroupGr [GlideRecord]: sys_user_group's GlideRecord.
* @param userSysId [String]
* @param isRotaAdmin [Boolean]
*/
addManagedGroupsQuery: function (managerGroupGr, userSysId, isRotaAdmin) {
if (!isRotaAdmin)
managerGroupGr.addQuery("manager", userSysId);
managerGroupGr.addActiveQuery();
managerGroupGr.addEncodedQuery("JOINsys_user_group.sys_id=cmn_rota.group!active=true");
},
/*
* Returns count of users how are active members of any roster from the given group
*/
getGroupMemberCount: function (groupId, includeDraft) {
var todayDate = new GlideDateTime().getDate().getValue(); // returns internal formatted date
var memberGr = new GlideRecord(this.TABLES.CMN_ROTA_MEMBER);
memberGr.addQuery("member.active", "=", true);
var qc = memberGr.addQuery("roster.rota.active", true);
if (includeDraft) {
var qcOR = qc.addOrCondition("roster.rota.active", false);
qcOR.addCondition("roster.rota.state", "draft");
}
memberGr.addEncodedQuery("from=NULL^ORfrom<=" + todayDate);
memberGr.addEncodedQuery("to=NULL^ORto>=" + todayDate);
memberGr.query();
var memberIds = [];
while (memberGr.next())
memberIds.push(memberGr.member + "");
if (!memberIds.length)
return 0;
var count = 0;
var userMemberGa = new GlideAggregate(this.TABLES.SYS_USER_GRMEMBER);
userMemberGa.addQuery("group", groupId);
userMemberGa.addQuery("user", "IN", memberIds.join(","));
userMemberGa.addQuery("group.active", "true");
userMemberGa.addEncodedQuery("JOINsys_user_grmember.group=cmn_rota.group!groupISNOTEMPTY");
userMemberGa.addAggregate('COUNT');
userMemberGa.query();
if (userMemberGa.next())
count = userMemberGa.getAggregate('COUNT');
return count;
},
escapeHTML: function (text) {
return text.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
},
getCurrentTimezone: function() {
return this.escapeHTML(gs.getSession().getTimeZoneName() + "") + "";
},
getChoiceList: function (table, field) {
var choiceListObj = GlideChoiceList.getChoiceList(table, field);
choiceListObj.removeNone();
var choiceListSize = choiceListObj.getSize();
var choices = [];
for (var i = 0; i < choiceListSize; i++){
var choice = choiceListObj.getChoice(i);
choices.push({
"name": this.escapeHTML(choice.getLabel() + ''),
"value": choice.getValue() + ''
});
}
return choices;
},
_sortTZ: function (timezones) {
timezones = timezones.sort(function (tz1, tz2) {
var tz1Name = tz1.name.toLowerCase();
var tz2Name = tz2.name.toLowerCase();
if (tz1Name < tz2Name)
return -1;
else if (tz1Name > tz2Name)
return 1;
else return 0;
});
return timezones;
},
getTimezoneList: function () {
var timezones = this.getChoiceList(this.TABLES.USER, this.SYS_USER_FIELDS.TIMEZONE);
var sessionTZ = this.escapeHTML(gs.getSession().getTimeZoneName() + "") + "";
timezones = timezones.filter(function (timezone) {
return timezone.value !== "NULL_OVERRIDE";
});
var isSessionTZAvailable = timezones.some(function (timezone) {
return timezone.value === sessionTZ;
});
if (!isSessionTZAvailable) {
timezones.push({
name: sessionTZ,
value: sessionTZ
});
}
return this._sortTZ(timezones);
},
_getEscalationOfRotateMembers: function (rosterGr, rotaGr) {
var numberOfReminders = rosterGr.attempts ? parseInt(rosterGr.attempts) : 0;
var reminderDuration = rosterGr.time_between_reminders ? rosterGr.time_between_reminders.getGlideObject().getNumericValue() : 0;
var memberGr = new GlideRecord(this.TABLES.CMN_ROTA_MEMBER);
memberGr.addQuery("roster", rosterGr.getUniqueValue());
memberGr.query();
var rostersCount = rosterGr.getRowCount();
rostersCount = !!rostersCount ? rostersCount : 0;
var timeToStart = 0;
var level = 1;
var escalationSteps = [];
while (memberGr.next()) {
var step = {};
step.name = {
value: gs.getMessage("On Call Member {0}", level + ""),
display_value: gs.getMessage("On Call Member {0}", level + "")
};
step.escalation_level = {
value: level,
display_value: level
};
step.delay_till_previous_step = {
value: timeToStart,
display_value: new GlideDuration(timeToStart).getDisplayValue()
};
step.roster_sys_id = {
value: rosterGr.getUniqueValue()
};
step.reminders = {
label: gs.getMessage("Number of Reminders"),
value: numberOfReminders
};
step.time_between_reminders = {
value: reminderDuration,
display_value: rosterGr.getDisplayValue('time_between_reminders')
};
step.detailed_reminders = [];
var reminderNumber = 1;
var perLevelOverallDuration = 0;
while (reminderNumber <= numberOfReminders) {
step.detailed_reminders.push({
value: reminderDuration,
display_value: gs.getMessage("Reminder {0} - {1}", [reminderNumber + "", new GlideDuration(reminderDuration).getDisplayValue()])
});
reminderNumber++;
perLevelOverallDuration += reminderDuration;
}
var timeTakenDisplayValue = new GlideDuration(perLevelOverallDuration + reminderDuration).getDisplayValue();
step.time_taken_at_step = {
value: perLevelOverallDuration + reminderDuration,
display_value: gs.getMessage("{0} delay", timeTakenDisplayValue)
};
step.time_to_next_step = {
value: reminderDuration,
display_value: new GlideDuration(reminderDuration).getDisplayValue()
};
timeToStart = timeToStart + perLevelOverallDuration + reminderDuration;
level++;
escalationSteps.push(step);
}
var totalTime;
if (rotaGr && rotaGr.catch_all) {
var catchAllStep = this._getCatchAllStep(rotaGr);
catchAllStep.delay_till_previous_step = {
value: timeToStart,
display_value: new GlideDuration(timeToStart).getDisplayValue()
};
escalationSteps.push(catchAllStep);
totalTime = {
value: timeToStart + catchAllStep.catch_all_wait_time,
display_value: new GlideDuration(timeToStart + catchAllStep.catch_all_wait_time).getDisplayValue()
};
} else {
totalTime = {
value: timeToStart,
display_value: new GlideDuration(timeToStart).getDisplayValue()
};
}
var escalation = {};
escalation.steps = escalationSteps;
escalation.total_time = totalTime;
escalation.rosters_count = rostersCount;
return escalation;
},
_getCatchAllWaitTime: function(rotaGr) {
var catchAllWaitTime = 0;
var catchAllDuration = rotaGr.catch_all_wait_time.getGlideObject();
catchAllWaitTime = catchAllDuration ? catchAllDuration.getNumericValue() : 0;
if (catchAllWaitTime == 0) {
catchAllWaitTime = parseInt(gs.getProperty(this.CATCH_ALL_WAIT_TIME_PROPERTY, 0)) * 1000;
}
return catchAllWaitTime;
},
_getCatchAllStep: function (rotaGr) {
var catchAllStep = {};
catchAllStep.name = {
value: 'catch_all',
display_value: gs.getMessage("Catch-all")
};
catchAllStep.catch_all_type = {
value: rotaGr.catch_all,
display_value: rotaGr.getDisplayValue('catch_all')
};
switch (rotaGr.catch_all + '') {
case 'all':
catchAllStep.value = rotaGr.catch_all_roster;
catchAllStep.display_value = rotaGr.getDisplayValue('catch_all_roster');
break;
case 'individual':
catchAllStep.value = rotaGr.catch_all_member;
catchAllStep.display_value = rotaGr.getDisplayValue('catch_all_member');
break;
case 'group_manager':
catchAllStep.value = rotaGr.group.manager;
catchAllStep.display_value = rotaGr.getDisplayValue('group.manager');
break;
default:
catchAllStep.value = '';
catchAllStep.display_value = '';
}
catchAllStep.catch_all_wait_time = this._getCatchAllWaitTime(rotaGr);
return catchAllStep;
},
_getEscalationOfRotateRoster: function (rosterGr, rotaGr) {
var timeToStart = 0;
var level = 1;
var escalationSteps = [];
var rostersCount = rosterGr.getRowCount();
rostersCount = !!rostersCount ? rostersCount : 0;
while (rosterGr.next()) {
var numberOfReminders = rosterGr.attempts ? parseInt(rosterGr.attempts) : 0;
var reminderDuration = rosterGr.time_between_reminders ? rosterGr.time_between_reminders.getGlideObject().getNumericValue() : 0;
var step = {};
step.name = {
value: rosterGr.name + "",
display_value: rosterGr.name + ""
};
step.escalation_level = {
value: level,
display_value: level
};
step.delay_till_previous_step = {
value: timeToStart,
display_value: new GlideDuration(timeToStart).getDisplayValue()
};
step.roster_sys_id = {
value: rosterGr.getUniqueValue()
};
step.reminders = {
label: gs.getMessage("Number of Reminders"),
value: numberOfReminders
};
step.time_between_reminders = {
value: reminderDuration,
display_value: rosterGr.getDisplayValue('time_between_reminders')
};
step.detailed_reminders = [];
var reminderNumber = 1;
var perLevelOverallDuration = 0;
while (reminderNumber <= numberOfReminders) {
step.detailed_reminders.push({
value: reminderDuration,
display_value: gs.getMessage("Reminder {0} - {1}", [reminderNumber + "", new GlideDuration(reminderDuration).getDisplayValue()])
});
reminderNumber++;
perLevelOverallDuration += reminderDuration;
}
var timeTakenDisplayValue = new GlideDuration(perLevelOverallDuration + reminderDuration).getDisplayValue();
step.time_taken_at_step = {
value: perLevelOverallDuration + reminderDuration,
display_value: gs.getMessage("{0} delay", timeTakenDisplayValue)
};
step.time_to_next_step = {
value: reminderDuration,
display_value: new GlideDuration(reminderDuration).getDisplayValue()
};
timeToStart = timeToStart + perLevelOverallDuration + reminderDuration;
level++;
escalationSteps.push(step);
}
var totalTime;
if (rotaGr && rotaGr.catch_all) {
var catchAllStep = this._getCatchAllStep(rotaGr);
catchAllStep.delay_till_previous_step = {
value: timeToStart,
display_value: new GlideDuration(timeToStart).getDisplayValue()
};
escalationSteps.push(catchAllStep);
totalTime = {
value: timeToStart + catchAllStep.catch_all_wait_time,
display_value: new GlideDuration(timeToStart + catchAllStep.catch_all_wait_time).getDisplayValue()
};
} else {
totalTime = {
value: timeToStart,
display_value: new GlideDuration(timeToStart).getDisplayValue()
};
}
var escalation = {};
escalation.steps = escalationSteps;
escalation.total_time = totalTime;
escalation.rosters_count = rostersCount;
return escalation;
},
getEscalationPathDefinition: function (rotaId, escalationSetId) {
/**
* @type {CmnRotaRosterGR}
*/
var rotaGr = new GlideRecord(this.TABLES.CMN_ROTA);
if (!rotaGr.get(rotaId)) {
return;
}
var isGroupEscalation = new OnCallRotation().isGroupEscalationApplied(rotaId);
var rosterGr = new GlideRecord(this.TABLES.CMN_ROTA_ROSTER);
rosterGr.addQuery("rota", rotaId);
rosterGr.orderBy("order");
rosterGr.addActiveQuery();
rosterGr.query();
if (isGroupEscalation || rotaGr.use_custom_escalation) {
if (escalationSetId)
return new OCEscalationDesigner().getEscalationSet(escalationSetId + "", rotaId);
else
return new OCEscalationDesigner().getDefaultEscalation(rotaId + "");
} else if (rosterGr.getRowCount() == 1) {
rosterGr.next();
return this._getEscalationOfRotateMembers(rosterGr, rotaGr);
} else {
return this._getEscalationOfRotateRoster(rosterGr, rotaGr);
}
},
/* Check whether to show the option to deactivate the schedule entry
* show for group and shift managers.
*/
showDeactivateScheduleSpan: function (current) {
if (current.group)
return new OnCallSecurityNG().rotaMgrAccess(current.group);
if (current.schedule.document == this.TABLES.CMN_ROTA) {
var rotaGr = new GlideRecord(this.TABLES.CMN_ROTA);
rotaGr.get(current.schedule.document_key);
if (rotaGr)
return new OnCallSecurityNG().rotaMgrAccess(rotaGr.group);
}
return false;
},
evaluateUiAction: function (table, current, actionName) {
if (!table || !current || !actionName)
return;
var uiActionGr = new GlideRecord(this.TABLES.SYS_UI_ACTION);
uiActionGr.addActiveQuery();
uiActionGr.addQuery('table', table);
uiActionGr.addQuery('action_name', actionName);
uiActionGr.query();
if (uiActionGr.next()) {
var evaluator = new GlideScopedEvaluator();
var result = evaluator.evaluateScript(uiActionGr, 'condition', {
current: current
});
if (result != null) {
if (result.toString() != "true")
return false;
}
return true;
}
return false;
},
/**
* To get Spans (Primarily for Mobile use case)
*/
getAllSpans: function (startDate, endDate, groupId, formatterType) {
var formatter = null;
var scriptInclude = OCFormatterMapping.formatters[formatterType];
if (scriptInclude === OCDHTMLXCalendarFormatter)
formatter = new scriptInclude(null, gs.getSession().getTimeZoneName() + "");
else if (scriptInclude)
formatter = new scriptInclude();
var ocrRotaV2 = new OCRotationV2(null, formatter);
return ocrRotaV2
.setStartDate(startDate)
.setEndDate(endDate, false)
.setGroupIds(groupId)
.getSpans();
},
/**
* To Request Time Off (Primarily for Mobile use case)
*/
requestTimeOff: function (startDate, endDate, allDayEvent, groupId, notes, proposedCover, memberSysId, formatterType) {
if (!memberSysId)
memberSysId = gs.getUserID();
var maxDateRangeDaysDuration = parseInt(gs.getProperty("com.snc.on_call_rotation.calendar_span.max_time_off_days", "365"));
var coverageDateDiff = gs.dateDiff(startDate, endDate, true);
if (coverageDateDiff <= 0) {
throw "Invalid time off spans";
}
var providedCoverageDurationInDays = parseInt(coverageDateDiff / (24 * 60 * 60));
if (providedCoverageDurationInDays > maxDateRangeDaysDuration) {
throw "Can not provide time off for more than " + maxDateRangeDaysDuration + " consecutive days.";
}
var formatter = null;
var scriptInclude = OCFormatterMapping.formatters[formatterType];
if (scriptInclude)
formatter = new scriptInclude();
return new OCAddItem(formatter)
.setMemberSysId(memberSysId)
.setGroupSysId(groupId)
.setStartDateTime(startDate)
.setEndDateTime(endDate)
.setAllDay(allDayEvent)
.setNotes(notes)
.setProposedCover(proposedCover)
.timeOff();
},
/**
* To Provide coverage for mobile and web
*/
addOverride: function (startSpanDate, endSpanDate, userId, rosterIds, groupId, format) {
var scriptInclude = OCFormatterMapping.formatters[format];
var spans = [];
var formatter = null;
if (scriptInclude)
formatter = new scriptInclude();
var rosterSysIds = [];
if (rosterIds.indexOf(",") > -1)
rosterSysIds = rosterIds.split(",");
else
rosterSysIds.push(rosterIds);
var isCoverageRotaTimesOnlyEnabled = (gs.getProperty("com.snc.on_call_rotation.coverage.rota_times_only", "true") == "true");
var maxDateRangeDaysDuration = parseInt(gs.getProperty("com.snc.on_call_rotation.calendar_span.max_date_range_days", "30"));
var coverageDateDiff = gs.dateDiff(startSpanDate, endSpanDate, true);
if (JSUtil.nil(coverageDateDiff)) {
throw "Invalid coverage spans";
}
var providedCoverageDuration = parseInt(coverageDateDiff / (24 * 60 * 60));
if (providedCoverageDuration > maxDateRangeDaysDuration) {
throw "Can not provide coverage for more than " + maxDateRangeDaysDuration + " consecutive days.";
}
if (isCoverageRotaTimesOnlyEnabled) {
var rotaRosterMap = {};
var rosterNameMap = {};
var rotaIds = [];
var rosterGr = new GlideRecord('cmn_rota_roster');
rosterGr.addQuery('sys_id', 'IN', rosterSysIds);
rosterGr.query();
while (rosterGr.next()) {
if (!rotaRosterMap[rosterGr.getValue('rota')]) {
rotaRosterMap[rosterGr.getValue('rota')] = {};
rotaRosterMap[rosterGr.getValue('rota')].rosters = [rosterGr.getUniqueValue()];
rotaIds.push(rosterGr.getValue('rota'));
} else {
rotaRosterMap[rosterGr.getValue('rota')].rosters.push(rosterGr.getUniqueValue());
}
rosterNameMap[rosterGr.getUniqueValue()] = rosterGr.getValue('name');
}
var ocRotationFormatter = new OCDHTMLXCalendarFormatter();
var ocrRotaV2 = new OCRotationV2(null, ocRotationFormatter);
var isSuccessAddedInSpan = false,
isWarningAddedInSpan = false;
var allSpansFromStartAndEndDate = ocrRotaV2
.setStartDate(startSpanDate, true)
.setEndDate(endSpanDate, null, true)
.setRosterIds(rosterIds)
.setGroupIds(groupId)
.setRotaIds(rotaIds)
.getSpans();
var filteredRotaSpans = [];
var filteredOverrideSpans = [];
for (var i = 0; i < allSpansFromStartAndEndDate.length; i++) {
if (allSpansFromStartAndEndDate[i].type == 'rota')
filteredRotaSpans.push(allSpansFromStartAndEndDate[i]);
if (allSpansFromStartAndEndDate[i].type == 'override')
filteredOverrideSpans.push(allSpansFromStartAndEndDate[i]);
}
var createOverride = function (rosterId, startDate, endDate) {
var successResult = new OCAddItem(formatter)
.setMemberSysId(userId)
.setRosterSysId(rosterId + "")
.setStartDateTime(startDate + "")
.setEndDateTime(endDate + "")
.createOverride();
spans.push(successResult);
};
var conflictingOverrideSkipped = false;
var errorMessagesData = {};
for (var i = 0; i < filteredRotaSpans.length; i++) {
var rotaSpan = filteredRotaSpans[i];
var rotaId = rotaSpan.rota_id + '';
var rosters = rotaRosterMap[rotaId].rosters.slice(0);
var rotaStartDate = new GlideDateTime();
rotaStartDate.setDisplayValueInternal(rotaSpan.start_date);
var rotaEndDate = new GlideDateTime();
rotaEndDate.setDisplayValueInternal(rotaSpan.end_date);
for (var k = 0; k < rosters.length; k++) {
var rosterId = rosters[k] + '';
var rosterCompletelyOverlaps = false;
var rosterPartiallyOverlaps = false;
var previousOverrideEndDate = null;
var previousOverrideEndDateGDT = null;
filteredOverrideSpans.sort(
function (start, end) {
var currentDate = new GlideDateTime();
currentDate.setDisplayValueInternal(start.start_date);
var nextDate = new GlideDateTime();
nextDate.setDisplayValueInternal(end.start_date);
var res = currentDate.compareTo(nextDate);
return res;
});
//search for an conflicting override for that roster
for (var j = 0; j < filteredOverrideSpans.length; j++) {
var overrideSpan = filteredOverrideSpans[j];
if (rosterId == (overrideSpan.roster_id + '')) {
var overrideSpanStartDate = new GlideDateTime();
overrideSpanStartDate.setDisplayValueInternal(overrideSpan.start_date);
var overrideSpanEndDate = new GlideDateTime();
overrideSpanEndDate.setDisplayValueInternal(overrideSpan.end_date);
// if rota span in completely overlapped by existing override
if (rotaStartDate.compareTo(overrideSpanStartDate) >= 0 && rotaEndDate.compareTo(overrideSpanEndDate) <= 0) {
rosterCompletelyOverlaps = true;
break;
}
// if rota span is partially overlapping with existing override
else if ((rotaStartDate.compareTo(overrideSpanStartDate) <= 0 && rotaEndDate.compareTo(overrideSpanEndDate) >= 0) || (rotaStartDate.compareTo(overrideSpanStartDate) <= 0 && rotaStartDate.compareTo(overrideSpanEndDate) >= 0) || (rotaEndDate.compareTo(overrideSpanStartDate) <= 0 && rotaEndDate.compareTo(overrideSpanEndDate) >= 0)) {
rosterPartiallyOverlaps = true;
if (!previousOverrideEndDate && rotaStartDate.compareTo(overrideSpanStartDate) < 0)
createOverride(rosterId, rotaSpan.start_date, overrideSpan.start_date);
else if (previousOverrideEndDate && previousOverrideEndDateGDT.compareTo(overrideSpanStartDate) < 0)
createOverride(rosterId, previousOverrideEndDate, overrideSpan.start_date);
previousOverrideEndDate = overrideSpan.end_date;
previousOverrideEndDateGDT = overrideSpanEndDate;
if (!errorMessagesData[rosterId])
errorMessagesData[rosterId] = [];
errorMessagesData[rosterId].push({
start: overrideSpanStartDate.getDisplayValue(),
end: overrideSpanEndDate.getDisplayValue()
});
}
}
}
if (previousOverrideEndDate && previousOverrideEndDateGDT.compareTo(rotaEndDate) < 0)
createOverride(rosterId, previousOverrideEndDate, rotaSpan.end_date);
if (rosterCompletelyOverlaps || rosterPartiallyOverlaps)
conflictingOverrideSkipped = true;
if (!rosterCompletelyOverlaps && !rosterPartiallyOverlaps && rotaStartDate.compareTo(rotaEndDate) < 0)
createOverride(rosterId, rotaSpan.start_date, rotaSpan.end_date);
if (rosterCompletelyOverlaps) {
if (!errorMessagesData[rosterId])
errorMessagesData[rosterId] = [];
errorMessagesData[rosterId].push({
start: rotaStartDate.getDisplayValue(),
end: rotaEndDate.getDisplayValue()
});
}
}
}
if (conflictingOverrideSkipped) {
var errorResult = {};
errorResult.message = gs.getMessage("Some Coverages spans were not created since they already have a Coverage defined");
errorResult.message_type = "warning";
errorResult.no_hide = true;
spans.push(errorResult);
Object.keys(errorMessagesData).forEach(function (rosterId) {
var dateRanges = "";
errorMessagesData[rosterId].forEach(function (obj) {
if (GlideI18NStyle.getDirection() == 'ltr')
dateRanges = dateRanges + "(" + obj.start + " - " + obj.end + ") ";
else
dateRanges = " (" + obj.end + " - " + obj.start + ")" + dateRanges;
});
errorResult = {};
errorResult.message = gs.getMessage("Coverage creation skipped for {0} on date(s) {1} as there was a conflicting Coverage present.", [rosterNameMap[rosterId], dateRanges]);
errorResult.message_type = "warning";
errorResult.no_hide = true;
spans.push(errorResult);
});
}
} else { // Legacy behaviour maintained for coverage creation
for (var i = 0; i < rosterSysIds.length; i++)
spans.push(new OCAddItem(formatter)
.setMemberSysId(userId)
.setRosterSysId(rosterSysIds[i])
.setStartDateTime(startSpanDate)
.setEndDateTime(endSpanDate)
.createOverride());
}
return spans;
},
/**
* To Delete coverage (for Mobile and web)
*/
deleteCoverage: function (tableName, sysId) {
if (JSUtil.nil(tableName) || JSUtil.nil(sysId)) {
return new sn_ws_err.BadRequestError("Invalid Table OR Sys_id provided");
}
var tableGr = new GlideRecord(tableName);
if (tableGr.get(sysId)) {
if (tableGr.canDelete()) {
if (!tableGr.deleteRecord()) {
return new sn_ws_err.BadRequestError("Could not delete the record");
}
} else {
var respError = new sn_ws_err.ServiceError();
respError.setStatus(403);
respError.setMessage("Security constraints prevent access to requested resource");
return respError;
}
}
},
/**
* To Replace coverage (for Mobile and web)
*/
replaceCoverage: function (startSpanDate, endSpanDate, userId, rosterId, groupId, tableName, sysId, format) {
var formatter = null;
var scriptInclude = OCFormatterMapping.formatters[format];
if (scriptInclude)
formatter = new scriptInclude();
var result = new OCAddItem(formatter)
.setMemberSysId(userId)
.setRosterSysId(rosterId)
.setStartDateTime(startSpanDate)
.setEndDateTime(endSpanDate)
.createOverride(true);
if (result.message_type !== "error") {
//delete coverage
if (JSUtil.nil(tableName) || JSUtil.nil(sysId)) {
return new sn_ws_err.BadRequestError("Invalid Table OR Sys_id provided");
}
var tableGr = new GlideRecord(tableName);
if (tableGr.get(sysId)) {
if (tableGr.canDelete()) {
if (!tableGr.deleteRecord()) {
return new sn_ws_err.BadRequestError("Could not delete the record");
}
} else {
var respError = new sn_ws_err.ServiceError();
respError.setStatus(403);
respError.setMessage("Security constraints prevent access to requested resource");
return respError;
}
}
//create coverage
result = new OCAddItem(formatter)
.setMemberSysId(userId)
.setRosterSysId(rosterId)
.setStartDateTime(startSpanDate)
.setEndDateTime(endSpanDate)
.createOverride();
}
return result;
},
/**
* to get the PTO Configuration for the given group
*/
getPTOConfiguration: function (groupId) {
return new OCRosterSpanApprovalUtil().isPTOApprovalRequired(groupId);
},
getManagedGroups: function () {
var groupId;
var myGroups = [];
var isRotaAdmin = false;
var ocsNG = new OnCallSecurityNG();
var userSysId = gs.getUserID();
if (gs.hasRole('rota_admin')) {
isRotaAdmin = true;
}
var managerGroupGr = new GlideRecord("sys_user_group");
this.addManagedGroupsQuery(managerGroupGr, userSysId, isRotaAdmin);
managerGroupGr.query();
while (managerGroupGr.next()) {
myGroups.push(managerGroupGr.getUniqueValue());
}
if (!isRotaAdmin) {
var gaHasRole = ocsNG.getDelegatedGroups(userSysId);
while (gaHasRole.next()) {
groupId = gaHasRole.getValue('granted_by');
myGroups.push(groupId);
}
var groupIds = ocsNG.getManagedGroupsByPreferences(userSysId);
groupIds.forEach(function (grId) {
myGroups.push(grId);
});
}
return myGroups;
},
getMyGroups: function () {
var myGroups = [];
var userSysId = gs.getUserID();
var memberGroupGr = new GlideRecord("sys_user_grmember");
memberGroupGr.addQuery('user', userSysId);
memberGroupGr.addEncodedQuery("JOINsys_user_grmember.group=cmn_rota.group!active=true");
memberGroupGr.query();
while (memberGroupGr.next()) {
myGroups.push(memberGroupGr.group + '');
}
return myGroups;
},
getUserGroups: function () {
if (this.getManagedGroups())
return (this.getManagedGroups()).concat(this.getMyGroups());
else
return this.getMyGroups();
},
getOnCallDashboardURL: function () {
if(GlidePluginManager.isActive("com.snc.pa.premium") && gs.hasRole("rota_prem_dashboard_user"))
g_response.sendRedirect("$pa_dashboard.do?sysparm_dashboard=292ad76653e700100c54ddeeff7b12a0");
else
g_response.sendRedirect("$pa_dashboard.do?sysparm_dashboard=af9f6e7d53af00100c54ddeeff7b127f");
},
getOnCallGroups: function() {
var uniqueValues = [];
var ga = new GlideAggregate('cmn_rota');
ga.addAggregate('COUNT');
ga.groupBy('group');
ga.addHaving('COUNT', '>', '0');
ga.query();
while (ga.next()) {
if (ga.getValue('group'))
uniqueValues.push(ga.getValue('group'));
}
return uniqueValues;
},
hasAccessToEscalationPolicy: function (escalationSetGr) {
var group = escalationSetGr.cmn_rota.group;
if (escalationSetGr.group_escalation + '')
group = escalationSetGr.group_escalation.group;
return new OnCallSecurityNG().rotaMgrAccess(group);
},
hasAccessToContactOverrides: function (contactPreferenceGr) {
var group = contactPreferenceGr.cmn_rota.group;
if (contactPreferenceGr.type == 'escalation_set' && contactPreferenceGr.escalation_set.group_escalation + '')
group = contactPreferenceGr.escalation_set.group_escalation.group;
return new OnCallSecurityNG().rotaMgrAccess(group);
},
getGroupEscalationMessage: function (type, rotaGr) {
if (this.isOnCallStoreAppInstalled())
return new sn_now_on_call.OnCallStoreAppUtil().getGroupEscalationMessage(type, rotaGr);
if (type == 'rota_form')
return gs.getMessage('Group escalation is applied.');
if (type == 'escalation_designer')
return gs.getMessage('Group escalation is applied. escalation designer is in read only mode.');
if (type == 'creation_wizard')
return gs.getMessage('Group escalation is applied.');
return '';
},
// use user preferred language if exist, otherwise use default language
getUserLang: function(userId) {
var defaultLang = 'en';
if (!userId)
return defaultLang;
var userGr = new GlideRecord(this.TABLES.USER);
if (!userGr.get(userId))
return defaultLang;
var lang = userGr.preferred_language;
if (lang)
return lang.toString();
return defaultLang;
},
isOnCallStoreAppInstalled: function () {
return GlidePluginManager.isActive('com.snc.on_call');
},
isNotifyPluginInstalled: function() {
return GlidePluginManager.isActive('com.snc.notify');
},
isSlackPluginInstalled: function() {
return GlidePluginManager.isActive('sn_slack_ah_v2');
},
isMobilePushInstalled: function() {
// OnCall Mobile Notification shipped in scoped app in if/<store app mobile app> so must check both active
return GlidePluginManager.isActive('com.sn_on_call_m_notif') && GlidePluginManager.isActive('com.sn_itsm_mobile_agent');
},
isTeamsPluginInstalled: function() {
return GlidePluginManager.isActive('sn_now_teams');
},
isUsingNotifyCallForVoice: function(useNotifyCallForVoiceWFScratchpad) {
var useNotifyCall = false;
if (typeof useNotifyCallForVoiceWFScratchpad === 'undefined')
useNotifyCall = gs.getProperty('com.snc.on_call_rotation.notification.voice.use_notify_call', false);
else
useNotifyCall = useNotifyCallForVoiceWFScratchpad;
if (typeof useNotifyCall === 'string')
useNotifyCall = (useNotifyCall === 'true');
return useNotifyCall;
},
// Use this in case of boolean property
getBooleanApplicationPropertyValue: function(prop) {
var propertyValue = GlideApplicationProperty.getValue(prop);
if (gs.nil(propertyValue)) {
return true;
}
return propertyValue == 'true' || false;
},
shouldUseSlackForDM: function() {
return this.isSlackPluginInstalled() && this.getBooleanApplicationPropertyValue("com.snc.on_call_rotation.use_slack_for_dm");
},
shouldUseTeamsForDM: function() {
return this.isTeamsPluginInstalled() && this.getBooleanApplicationPropertyValue("com.snc.on_call_rotation.use_msteams_for_dm");
},
getDirection: function() {
return GlideI18NStyle.getDirection();
},
isRecordAccessibleToUser: function(userSysId, recordGR) {
var currentUser = gs.getUserID();
if (!userSysId || !recordGR)
return false;
gs.getSession().impersonate(userSysId);
var canRead = recordGR.canRead() ? true : false;
gs.getSession().impersonate(currentUser);
return canRead;
},
type: 'OnCallCommonSNC'
};
Sys ID
9e1176a457811300532c3da73d94f971