how to create report ?

Community Alums
Not applicable

hi all,

 

 

Looking for functionality  that will allow me to measure assignment group lifespan per incident ticket or amount of time taken before the incident ticket is assigned to another work group

Report will be needed to determine amount of time spent by a work group on each ticket before it is escalated/transferred to another assignment group.

 

Thanks

 

10 REPLIES 10

Dr Atul G- LNG
Tera Patron
Tera Patron

Hi @Community Alums 

 

I agree with @AndersBGS  & @Jaspal Singh 

 

Best way is SLA and use OLA to get the records or Metrics is another best option. Here are few OOTB Metrics 

 

LearnNGrowAtul_0-1705398336446.png

 

 

*************************************************************************************************************
If my response proves useful, please indicate its helpfulness by selecting " Accept as Solution" and " Helpful." This action benefits both the community and me.

Regards
Dr. Atul G. - Learn N Grow Together
ServiceNow Techno - Functional Trainer
LinkedIn: https://www.linkedin.com/in/dratulgrover
YouTube: https://www.youtube.com/@LearnNGrowTogetherwithAtulG
Topmate: https://topmate.io/atul_grover_lng [ Connect for 1-1 Session]

****************************************************************************************************************

Rajdeep Ganguly
Mega Guru


Sure, you can achieve this by creating a custom report in ServiceNow. Here are the steps:

1. Create a new table to store the assignment group history. This table should have fields for the incident number, assignment group, assigned time, and transferred time.

2. Create a business rule that triggers when an incident is updated. This rule should check if the assignment group has changed.

3. If the assignment group has changed, the business rule should create a new record in the assignment group history table. It should set the incident number to the current incident, the assignment group to the old assignment group, the assigned time to the time when the incident was last assigned to this group, and the transferred time to the current time.

4. Create a scripted field on the assignment group history table that calculates the lifespan of the assignment group for each incident. This can be done by subtracting the assigned time from the transferred time.

5. Create a report that groups by incident number and assignment group, and calculates the average lifespan for each group.

Here is a sample code for the business rule:

javascript
(function executeRule(current, previous /*null when async*/) {

// Check if the assignment group has changed
if (current.assignment_group != previous.assignment_group) {
// Create a new record in the assignment group history table
var gr = new GlideRecord('u_assignment_group_history');
gr.initialize();
gr.u_incident = current.sys_id;
gr.u_assignment_group = previous.assignment_group;
gr.u_assigned_time = previous.sys_updated_on;
gr.u_transferred_time = current.sys_updated_on;
gr.insert();
}

})(current, previous);


And here is a sample code for the scripted field:

javascript
(function calculateLifespan() {

// Calculate the lifespan by subtracting the assigned time from the transferred time
var lifespan = gs.dateDiff(new GlideDateTime(current.u_assigned_time), new GlideDateTime(current.u_transferred_time), true);
return lifespan;

})();


Please note that you need to replace 'u_assignment_group_history', 'u_incident', 'u_assignment_group', 'u_assigned_time', and 'u_transferred_time' with the actual names of your table and fields.


nowKB.com

cloudops
Tera Expert

Sure, you can achieve this by creating a custom field to track the time spent by each assignment group on an incident before it is transferred to another group. Here's a step-by-step guide:

1. Create a custom field in the Incident table to store the time spent by each assignment group. This field can be of type 'Duration'.

2. Create a Business Rule that triggers on the update of the 'Assignment group' field in the Incident table. This Business Rule should calculate the time difference between the current time and the time when the assignment group was last changed, and store this value in the custom field created in step 1.

3. The Business Rule script could look something like this:

javascript
(function executeRule(current, previous /*null when async*/) {
// Check if the assignment group has changed
if (current.assignment_group != previous.assignment_group) {
// Calculate the time difference
var timeDiff = gs.dateDiff(previous.sys_updated_on, current.sys_updated_on, true);
// Store the time difference in the custom field
current.u_time_spent = timeDiff;
}
})(current, previous);


4. Create a report to display the time spent by each assignment group on each incident. You can use the 'List' report type and add the 'Incident', 'Assignment group', and 'Time spent' fields to the report.

5. To calculate the total time spent by each assignment group, you can create an 'Aggregate' report and group by the 'Assignment group' field, then sum the 'Time spent' field.

sumanta pal
Kilo Guru

- Create a custom field in the Incident table to store the time spent by each assignment group. This field can be of type 'Duration'.
- Create a Business Rule that triggers on the update of the 'Assignment group' field in the Incident table. This Business Rule should calculate the time difference between the current time and the time when the assignment group was last changed, and store this value in the custom field created in step 1.
- The Business Rule script could look something like this:

javascript
(function executeRule(current, previous /*null when async*/) {
// Check if the assignment group has changed
if (current.assignment_group != previous.assignment_group) {
// Calculate the time difference
var timeDiff = gs.dateDiff(previous.sys_updated_on, current.sys_updated_on, true);
// Store the time difference in the custom field
current.u_time_spent = timeDiff;
}
})(current, previous);


- Create a report to display the time spent by each assignment group on each incident. You can use the 'List' report type and add the 'Incident', 'Assignment group', and 'Time spent' fields to the report.
- To calculate the total time spent by each assignment group, you can create an 'Aggregate' report and group by the 'Assignment group' field, then sum the 'Time spent' field.


nowKB.com

Ahana 01
Tera Expert

var FormattedScheduleReport = Class.create();
FormattedScheduleReport.UTC_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
FormattedScheduleReport.UTC_TIME_FORMAT = "HH:mm:ss";
FormattedScheduleReport.prototype = {

initialize: function (timeZone) {
this.log = new GSLog("com.snc.on_call_rotation.log.level", this.type);

this.arrayUtil = new ArrayUtil();
this.userInfo = {};
this.daysToReport = [];

this.schedule = {
groups: {}
};

this.shiftTimeslots = {
rotas: {}
};

this.rosterPerRota = {
rotas: {}
};

this.timeZone = gs.getSession().getTimeZoneName();
if (timeZone)
this.timeZone = timeZone;
},

isPublic: function () {
return false;
},

/**
* If the high security settings plugin is turned on this will returns groups that
* the current user has the rights to view
*/
getPermittedGroups: function (commaSeparatedGroups) {
this.log.debug("[getPermittedGroups] commaSeparatedGroups: " + commaSeparatedGroups);

var permittedGroups;

// high security is on so we can allow access to all groups
if (!pm.isRegistered("com.glide.high_security"))
permittedGroups = commaSeparatedGroups;
else {
var ocsng = new OnCallSecurityNG();
var permittedGroupIds = [];
var groupIds = commaSeparatedGroups.split(',');
for (var i = 0; i < groupIds.length; i++)
if (ocsng.rotaAccess(groupIds[i]))
permittedGroupIds.push(groupIds[i]);
permittedGroups = permittedGroupIds.join();
}
this.log.debug("[getPermittedGroups] permittedGroups: " + permittedGroups);
return permittedGroups;
},

/**
* start [GlideDateTime / String]
* return GlideScheduleDateTime
*/
getStartDate: function(start) {
var startGSDT = new GlideScheduleDateTime(start + '');
startGSDT.setTimeZone(this.timeZone);
startGSDT.setBeginningOfDay();
return startGSDT;
},

/**
* end [GlideDateTime / String]
* return GlideScheduleDateTime
*/
getEndDate: function(end) {
var endGSDT = new GlideScheduleDateTime(end + '');
endGSDT.setTimeZone(this.timeZone);
endGSDT.setEndOfDay();
return endGSDT;
},

/*
* Generates the data structure containing the schedule of the groups that user is authorized to see
* in the period of start and end date
*/
buildSchedule: function (groups, startAsString, endAsString, rotaID) {
this.log.debug("[buildSchedule] groups: " + groups + " startAsString: " + startAsString + " endAsString: " + endAsString + " rotaID: " + rotaID);

//Add time part if it is missing
var start = this.getStartDate(startAsString);
var end = this.getEndDate(endAsString);

var rotaGR = new GlideRecord("cmn_rota");
rotaGR.addQuery('group', 'IN', this.getPermittedGroups(groups));
rotaGR.query();

if (typeof rotaID === 'undefined') {
while (rotaGR.next())
this.buildData(rotaGR, start, end);
} else if (rotaGR.get(rotaID))
this.buildData(rotaGR, start, end);

},

buildData: function (rotaGR, start, end) {
this.log.debug("[buildData] start: " + start + " end: " + end);

var tempStart = this.getStartDate(start);
var tempEnd = this.getEndDate(start);
tempEnd.addSeconds(-1);

var rotaId = rotaGR.getUniqueValue();
var rotaName = rotaGR.getValue('name');
var groupId = rotaGR.group + "";

this.log.debug("[buildData] rotaId: " + rotaId + " rotaName: " + rotaName + " groupId: " + groupId);

//Go forward day by day and get the schedule for the day
while (gs.dateDiff(tempEnd.getGlideDateTime(), end.getGlideDateTime(), true) >= 0) {
var page = new GlideAJAXSchedulePage(tempStart, tempEnd, this.timeZone);
var rotationCalc = new OnCallRotationCalculator();
rotationCalc.setPage(page);
rotationCalc.limitRotaId(rotaId);
rotationCalc.run(groupId);
var items = rotationCalc.page.getItems();

this.log.debug("[buildData] items.size: " + items.size() + " items: " + items);

// Extract data from each time slot and add them to the schedule object
for (var i = 0; i < items.size(); i++) {
var ajaxScheduleItem = items.get(i);
var type = ajaxScheduleItem.getDataByKey("type");
var roster = ajaxScheduleItem.getDataByKey("roster");
var spans = ajaxScheduleItem.getTimeSpans();

this.log.debug("[buildData] ajaxScheduleItem: " + ajaxScheduleItem + " type: " + type + " roster: " + roster + " spans: " + spans);

// Ignore the roster type record as it is a summary of rotations
if (type != "roster" && spans.size() > 0) {
var userId = ajaxScheduleItem.getDataByKey("user");

for (var k = 0; k < spans.size(); k++) {
var beginShift = this._getTimeDisplayFromScheduleDateTime(spans.get(k).getStart());
var endShift = this._getTimeDisplayFromScheduleDateTime(spans.get(k).getEnd());
this.addScheduleEntry(groupId, rotaId, tempStart, beginShift, endShift, roster, userId, rotaName);
}
}
}

// Updating loop variables
tempStart = new GlideScheduleDateTime(tempEnd);
tempStart.setTimeZone(this.timeZone);
tempStart.addSeconds(1);
tempEnd.addDays(1);
}
},

/*
* Generates the data structure containing the schedule of the groups that user is authorized to see
* in the period of start and end date
*/
buildScheduleEmail: function (groups, startGDT, endGDT, rotaID) {
this.log.debug("[buildScheduleEmail] groups: " + groups + " startGDT: " + startGDT + " endGDT: " + endGDT + " rotaID: " + rotaID);

//Add time part if it is missing
var start = this.getStartDate(startGDT);
var end = this.getEndDate(endGDT);

var rotaGR = new GlideRecord("cmn_rota");
rotaGR.addQuery('group', 'IN', this.getPermittedGroups(groups));
rotaGR.query();

if (typeof rotaID === 'undefined') {
while (rotaGR.next())
this.buildDataEmail(rotaGR, start, end);
} else if (rotaGR.get(rotaID))
this.buildDataEmail(rotaGR, start, end);
},

/*
* Generates the data structure containing the schedule of the rota for resending email reminders
* @Param GlideRecord rotaGR (rota GlideRecord)
* @Param String start (asDisplayed)
* @Param String end (asDisplayed)
*/
buildDataEmail: function (rotaGR, start, end) {
var endSDT = new GlideScheduleDateTime(end);
endSDT.setTimeZone(this.timeZone);
endSDT.setEndOfDay();
endSDT.addSeconds(-1);

if (rotaGR) {
var tempStart = new GlideScheduleDateTime(start);
tempStart.setTimeZone(this.timeZone);
tempStart.setBeginningOfDay();

var tempEnd = new GlideScheduleDateTime(start);
tempEnd.setTimeZone(this.timeZone);
tempEnd.setEndOfDay();
tempEnd.addSeconds(-1);

var rotaId = rotaGR.getUniqueValue();
var rotaName = rotaGR.getValue('name');
var groupId = rotaGR.group + "";

while (gs.dateDiff(tempEnd.toString(), endSDT.toString(), true) >= 0) {
var page = new GlideAJAXSchedulePage(tempStart, tempEnd, this.timeZone);
var rotationCalc = new OnCallRotationCalculator();
rotationCalc.setPage(page);
rotationCalc.limitRotaId(rotaId);
rotationCalc.run(groupId);
var items = rotationCalc.page.getItems();

for (var i = 0; i < items.size(); i++) {
var ajaxScheduleItem = items.get(i);
var type = ajaxScheduleItem.getDataByKey("type");
var roster = ajaxScheduleItem.getDataByKey("roster");
var spans = ajaxScheduleItem.getTimeSpans();

if (type != "roster" && spans.size() > 0) {
var userId = ajaxScheduleItem.getDataByKey("user");

for (var k = 0; k < spans.size(); k++) {
var beginShift = this._getTimeValueFromScheduleDateTime(spans.get(k).getStart());
var endShift = this._getTimeValueFromScheduleDateTime(spans.get(k).getEnd());
this.addScheduleEntry(groupId, rotaId, tempStart.toString(), beginShift, endShift, roster, userId, rotaName);
}
}
}

// Update tempStart and tempEnd for the next day
// Constructs new GlideScheduleDateTime using GlideScheduleDateTime(GlideScheduleDateTime sdt) constructor.
tempStart = new GlideScheduleDateTime(tempEnd);
tempStart.setTimeZone(this.timeZone);
tempStart.addSeconds(1);
tempEnd.addDays(1);
}
}

},

_dateObjToString: function(date) {
if (typeof date == 'undefined' || !date)
return "";

if (typeof date == 'object') {
if (typeof date.getDisplayValue == 'function')
date = date.getDisplayValue();
else if (typeof date.toString == 'function') {
var dateObj = new GlideDateTime();
//setDisplay and getDisplay must always be used together for 0 conversion.
dateObj.setDisplayValue(date);
//date must be returned as display value in this function to support user format.
date = dateObj.getDisplayValue();
}
}
return date;
},

/*
* Populates the schedule object with the passed info
*/
addScheduleEntry: function (groupId, rotaId, day, startShift, endShift, rosterId, userId, rotaName) {
this.log.debug("[addScheduleEntry] groupId: " + groupId + " rotaId: " + rotaId + " day: " + day + " startShift: " + startShift + " endShift: " + endShift + " rosterId: " + rosterId + " userId: " + userId + " rotaName: " + rotaName);

//check if the group entry exists
if (this.schedule.groups.length == 0 || typeof this.schedule.groups[groupId] == "undefined")
this.schedule.groups[groupId] = {
rotas: {}
};

if (this.schedule.groups[groupId].rotas.length == 0 || typeof this.schedule.groups[groupId].rotas[rotaId] == "undefined") {
this.schedule.groups[groupId].rotas[rotaId] = {
days: {}
};
// Also update timespan def, the default value of used is false and we only set it to true if there is a user working in that timeslot for the given rota, this way we avoid rep[orting empty timeslots
this.shiftTimeslots.rotas[rotaId] = {
used: false,
name: rotaName,
timespans: []
};
this.rosterPerRota.rotas[rotaId] = {
rosters: {}
};
}

day = this._dateObjToString(day);

if (this.schedule.groups[groupId].rotas[rotaId].days.length == 0 ||
typeof this.schedule.groups[groupId].rotas[rotaId].days[day] == "undefined") {

this.schedule.groups[groupId].rotas[rotaId].days[day] = {
timeslots: {}
};

if (!this.arrayUtil.contains(this.daysToReport, day)) {
this.daysToReport.push(day);
this.daysToReport.sort(this._dateComparator);
}
}

var period = startShift + "," + endShift;

if (this.schedule.groups[groupId].rotas[rotaId].days[day].timeslots.length == 0 ||
typeof this.schedule.groups[groupId].rotas[rotaId].days[day].timeslots[period] == "undefined") {
this.schedule.groups[groupId].rotas[rotaId].days[day].timeslots[period] = {
rosters: {}
};

// Also update timespan definition
if (!this.arrayUtil.contains(this.shiftTimeslots.rotas[rotaId].timespans, startShift)) {
this.shiftTimeslots.rotas[rotaId].timespans.push(startShift);
this.shiftTimeslots.rotas[rotaId].used = true;
}

if (!this.arrayUtil.contains(this.shiftTimeslots.rotas[rotaId].timespans, endShift))
this.shiftTimeslots.rotas[rotaId].timespans.push(endShift);

this.shiftTimeslots.rotas[rotaId].timespans.sort();
}

if (!this.schedule.groups[groupId].rotas[rotaId].days[day].timeslots[period].rosters[rosterId])
this.schedule.groups[groupId].rotas[rotaId].days[day].timeslots[period].rosters[rosterId] = userId;
//Update roster info
if (userId && (!this.rosterPerRota.rotas[rotaId].rosters[rosterId] || this.rosterPerRota.rotas[rotaId].rosters[rosterId] == "undefined")) {
var currentRoster = new GlideRecord('cmn_rota_roster');
currentRoster.get(rosterId);
this.rosterPerRota.rotas[rotaId].rosters[rosterId] = {
name: currentRoster.getValue('name'),
order: currentRoster.getValue("order"),
id: rosterId
};
}
//Update User info
if (!this.userInfo[userId] || this.userInfo[userId] == "undefined") {
var glideUser = GlideUser.getUserByID(userId);
var userName = glideUser.getFullName();
var availablePhoneNumbers = new SNC.UserNotificationDevices(glideUser.getID()+"").getCurrentlyAvailableVoiceDevices();
var phoneNumber = (availablePhoneNumbers.length > 0 ? availablePhoneNumbers[0].getNumber() : "");
this.userInfo[userId] = {
name: userName,
phone: phoneNumber
};
}
},

/*
* Retrieves the userId of the on-call memeber working in the given point of time
* in the given rota, returns empty srting if it doesn't find anyone
*/
findUser: function (group, rota, day, roster, from, to) {
// Find the the timeslot of the given group, rota for the given day which includes the input timeslot
for (var t in this.schedule.groups[group].rotas[rota].days[day].timeslots) {
var tss = this.getIncludingTimeSlots(t, rota);

if (this.arrayUtil.contains(tss, from) && this.arrayUtil.contains(tss, to)) {
if (typeof this.schedule.groups[group].rotas[rota].days[day].timeslots[t].rosters[roster] != "undefined") {
return this.schedule.groups[group].rotas[rota].days[day].timeslots[t].rosters[roster] + "";
}
}
}
return "";
},

/*
* Returns an array of time points that are inside the given timeslot for the passed rota
*/
getIncludingTimeSlots: function (timeslot, rotaId) {
var result = [];
var ends = timeslot.split(',');
this.log.debug('[getIncludingTimeSlots] ends before sort: ' + ends);
ends.sort();
// ends.sort(this._timeComparator);
this.log.debug('[getIncludingTimeSlots] ends after sort: ' + ends);

if (ends.length == 2) {
var fromIdx = this.arrayUtil.indexOf(this.shiftTimeslots.rotas[rotaId].timespans, ends[0]);
var toIdx = this.arrayUtil.indexOf(this.shiftTimeslots.rotas[rotaId].timespans, ends[1]);
for (var i = fromIdx; i <= toIdx; i++)
result.push(this.shiftTimeslots.rotas[rotaId].timespans[i]);
}
return result;
},

/*
* Returns the day name given a date in user defined format. AjaxSchedulePage returns results in user time format.
* It is immaterial to consider tz as we in this case need to parse the date and get day of week.
* Hence getting day of week in UTC after setting in UTC the received date in user defined format.
*/
getDayOfWeek: function(dateInUserDateTimeFormat) {
var gdt = new GlideDateTime();
gdt.setValueUTC(dateInUserDateTimeFormat, gs.getDateTimeFormat());
switch (gdt.getDayOfWeekUTC()) {
case 1:
return gs.getMessage("Monday");
case 2:
return gs.getMessage("Tuesday");
case 3:
return gs.getMessage("Wednesday");
case 4:
return gs.getMessage("Thursday");
case 5:
return gs.getMessage("Friday");
case 6:
return gs.getMessage("Saturday");
case 7:
return gs.getMessage("Sunday");
}
},

/*
* @Param Object rosters - containing key-value pairs like 'rosterIndex' : { name : 'rosterName', order : 'rosterOrder', id : 'rosterID'}
* @return array of objects { name : 'rosterName', order : 'rosterOrder', id : 'rosterID'} sorted by order value ascendingy
*/
sortRosters: function (rosters) {
var rostersArray = [];
for (var r in rosters) {
rostersArray.push(rosters[r]);
}
rostersArray.sort(function (a, b) {
return a.order - b.order;
});
return rostersArray;
},

/*
* Renders the schedule object as a html table
*/
getReport: function () {
return this.getHTML(false);
},

/*
* @Param String time1 (asDisplayed)
* @Param String time1 (asDisplayed)
* @return boolean
*/
isOneSecondDifference: function (time1, time2) {
var duration1 = new GlideDuration(time1);
var duration2 = new GlideDuration(time2);
var difference = new GlideDateTime(duration2.subtract(duration1)).getNumericValue();

// if the subtracted durations is not 1000 milliseconds (1 second)
if (difference != 1000)
return false;
return true;
},

/*
* Renders the schedule object as a html table
* @Param boolean purposeOfReminder - true if getHTML is called for the purpose of sending on-call email reminder
*/
getHTML: function (purposeOfReminder) {
var header = "";
var table = "";
var tableRow = "";
var flushTableRow = true;
var makeHeader = true;

this.log.debug("[purposeOfReminder] groups: " + JSON.stringify(this.schedule.groups));

for (var g in this.schedule.groups) {
var group = GlideGroup.get(g);
var title = gs.getMessage("On-Call Schedules");
if (Object.keys(this.schedule.groups).length == 1)
title = gs.getMessage("{0} On-Call Schedule", group.getName());

if (makeHeader)
header = "" + this.getSanitizedHTML(title) + "" + gs.getMessage("Roster") + "" + gs.getMessage("Shift") + "";

for (var r in this.schedule.groups[g].rotas) {
var noOfRosterForRota = 0;
for (var prop in this.rosterPerRota.rotas[r].rosters)
if (this.rosterPerRota.rotas[r].rosters.hasOwnProperty(prop))
noOfRosterForRota++;

var rotaPlaceHolder = '$';

tableRow += "" + this.getSanitizedHTML(this.shiftTimeslots.rotas[r].name) + "";

var sortedRosters = this.sortRosters(this.rosterPerRota.rotas[r].rosters);

//Loop through the rosters
for (var rosterIndex in sortedRosters) {
// To deal with empty rows we need to postpone giving the rowspan values until we know how many rows we hide
var noRowsToHide = 0;
var roster = sortedRosters[rosterIndex].id;
var rosterPlaceHolder = '@';

tableRow += "" + this.rosterPerRota.rotas[r].rosters[roster].name + "";
for (var j = 1; j < this.shiftTimeslots.rotas[r].timespans.length; j++) {

var isEmptyRow = true;
var isOneSecond = false;
for (var k = 0; k < this.daysToReport.length; k++) {
var d = this.daysToReport[k];
if (makeHeader)
header += "" + d.split(" ")[0] + "

" + this.getDayOfWeek(d) + "";

if (k == 0) {
isOneSecond = this.isOneSecondDifference(this.shiftTimeslots.rotas[r].timespans[j - 1], this.shiftTimeslots.rotas[r].timespans[j]);
if (!isOneSecond)
tableRow += "" + this.shiftTimeslots.rotas[r].timespans[j - 1] + "-" + this.shiftTimeslots.rotas[r].timespans[j] + "";
}

if (!isOneSecond) {
var userId = this.findUser(g, r, d, roster, this.shiftTimeslots.rotas[r].timespans[j - 1], this.shiftTimeslots.rotas[r].timespans[j]);
var userInfo = (userId) ? this.userInfo[userId] : "";

if (userInfo) {
tableRow += "" : ">") + this.getSanitizedHTML(userInfo.name) +"

"+ userInfo.phone + "";
isEmptyRow = false;
} else
tableRow += "";
}
} // END DAY
// Check if the Row has any information then don't hide it
if (isEmptyRow || isOneSecond) {
noRowsToHide++;
if (header && !table) {
table = "" + header + "";
flushTableRow = false;
}
} else
table += (header ? "" + header + "" : "") + "" + tableRow + "";
makeHeader = false;
header = "";
if (flushTableRow)
tableRow = "";
else
flushTableRow = true;
} //END TIMESPAN
tableRow = "";

// Now that the loop through days and timespans is done we know the actual rowspans
table = table.replace(rotaPlaceHolder, (this.shiftTimeslots.rotas[r].timespans.length - (noRowsToHide + 1)) * noOfRosterForRota);
table = table.replace(rosterPlaceHolder, this.shiftTimeslots.rotas[r].timespans.length - (noRowsToHide + 1));

} // END ROSTERS
} // END ROTAS
table += tableRow;
}
var html = "";
if (!JSUtil.nil(table))
html = "" + table + "
";
return html;
},

/*
* Builds Schedule table for specific rota and adds users from that table to object users
* @Param GlideRecord rotaGR
* @Param String rosterID
* @Param GlideDateTime startTime
* @Param GlideDateTime endTime
* @Param Object users contains key-value pairs like 'userID' : 'GlideRecord("sys_user")'
*/
getUsers: function (rotaGR, rosterID, startTime, endTime, users) {
this.log.debug("[getUsers] startTime: " + startTime);
this.log.debug("[getUsers] endTime: " + endTime);
var rotaID = rotaGR.getUniqueValue();
var groupID = rotaGR.group;
var userID;

if (!JSUtil.isEmpty(this.schedule.groups)) {
for (var day in this.schedule.groups[groupID].rotas[rotaID].days) {
for (var period in this.schedule.groups[groupID].rotas[rotaID].days[day].timeslots) {
userID = this.schedule.groups[groupID].rotas[rotaID].days[day].timeslots[period].rosters[rosterID];
if (!users[userID] && JSUtil.notNil(userID)) {
var userGR = new GlideRecord('sys_user');
userGR.get(userID);
users[userID] = userGR;
}
}
}
}
},

validGroupSysIds: function (dirtyGroupStr) {
this.log.debug("[validGroupSysIds] dirtyGroupStr: " + dirtyGroupStr);
if (JSUtil.nil(dirtyGroupStr))
return "";
var groups = dirtyGroupStr.match(/[0-9A-F]{32}/gi);
if (groups && groups.length > 0)
return groups.join(",");
return "";
},

getGroupNames: function (groupSysIdsStr) {
this.log.debug("[getGroupNames] groupSysIdsStr: " + groupSysIdsStr);
if (JSUtil.nil(groupSysIdsStr))
return "";
var groupNames = [];
var groupSysIds = groupSysIdsStr.split(",");
if (!groupSysIds || groupSysIds.length < 1)
return "";

var gr = new GlideRecord("sys_user_group");
for (var i = 0 ; i < groupSysIds.length; i++) {
gr.initialize();
gr.get(groupSysIds[i]);
groupNames.push(gr.getValue("name"));
}
return groupNames.join(", ");
},

_dateComparator: function(a, b) {
var systemDateTimeFormat = gs.getProperty("glide.sys.date_format") + " " + gs.getProperty("glide.sys.time_format");
var userDateTimeFormat = gs.getUser().getDateFormat() + " " + gs.getUser().getTimeFormat();
var dateTimeFormat = JSUtil.nil(userDateTimeFormat) ? (JSUtil.nil(systemDateTimeFormat) ? FormattedScheduleReport.UTC_DATE_FORMAT : systemDateTimeFormat) : userDateTimeFormat;
var gdt1 = new GlideDateTime();
gdt1.setValueUTC(a, dateTimeFormat);
var gdt2 = new GlideDateTime();
gdt2.setValueUTC(b, dateTimeFormat);
return gdt1.getNumericValue() - gdt2.getNumericValue();
},

_timeComparator: function(a, b) {
var systemTimeFormat = gs.getProperty("glide.sys.time_format");
var userTimeFormat = gs.getUser().getTimeFormat();
var timeFormat = JSUtil.nil(userTimeFormat) ? (JSUtil.nil(systemTimeFormat) ? FormattedScheduleReport.UTC_TIME_FORMAT : systemTimeFormat) : userTimeFormat;
var gt1 = new GlideTime();
gt1.setValueUTC(a, timeFormat);
var gt2 = new GlideTime();
gt2.setValueUTC(b, timeFormat);
return gt1.getNumericValue() - gt2.getNumericValue();
},

/**
* Get the time string from a schedule date time object in the user's timezone and format
*/
_getTimeDisplayFromScheduleDateTime: function (scheduleDateTime) {
return scheduleDateTime.getGlideDateTime().getDisplayValueInternal().split(" ")[1];
},

/**
* Get the time string from a schedule date time object in the user's timezone and format
*/
_getTimeValueFromScheduleDateTime: function (scheduleDateTime) {
return scheduleDateTime.toString().split(" ")[1];
},

getSanitizedHTML: function(text) {
if (!SNC.GlideHTMLSanitizer)
return JSUtil.escapeText(text);

return JSUtil.escapeText(SNC.GlideHTMLSanitizer.sanitize(text));
},

type: 'FormattedScheduleReport'
};


nowKB.com