SmartIT_Extensions/BMC/smart-it-full-helix/scripts/app/common/sla-calculator-service.js

451 lines
27 KiB
JavaScript
Raw Blame History

"use strict";
(function () {
'use strict';
angular.module('myitsmApp')
.service('slaCalculatorService', ['$resource', '$log', '$filter', '$timeout', '$q', function ($resource, $log, $filter, $timeout, $q) {
var resource = $resource('/smartit/rest/sla/:id', {}, {
getSLAById: { method: 'GET', isArray: true }
});
this.getAndCalculateSLA = function (ticket) {
var that = this;
return resource.getSLAById({ id: ticket.id }).$promise.then(function (result) {
var slaJSON = result[0].items;
if (_.isUndefined(slaJSON)) {
return;
}
if (_.isEmpty(slaJSON) && ticket.status && ticket.serviceTargets && ticket.serviceTargets.length) {
slaJSON = ticket.serviceTargets;
}
that.processServiceTarget(ticket, slaJSON);
}).catch(function () {
$log.error('Error getting sla information.');
});
};
this.processServiceTarget = function (ticket, slaJSON) {
var that = this, SLA = slaJSON.map(function (sla) {
return new SLAVO().build(sla);
}), firstSLAItem = _.head(SLA), SLAConditions = [
{
run: function () {
if (!ticket.SLA) {
ticket.SLA = {
items: SLA,
slaRenderType: 'full'
};
}
else {
ticket.SLA.items = SLA;
}
that.calculate(ticket);
},
condition: firstSLAItem.slaType === 'sla' || _.isEmpty(firstSLAItem.slaType)
},
{
run: function () {
ticket.SLA = {
items: SLA,
slaRenderType: 'partial'
};
that.calculate(ticket);
},
condition: firstSLAItem.slaType === 'targetdate' || firstSLAItem.slaType === 'scheduleddate'
},
{
run: function () {
ticket.SLA = {
slaTextValid: true,
divClassType: 'partial',
reachTime: $filter('i18n')('sla.ticket.created.on') + ' ' + convertDateToLocale(firstSLAItem.endTime)
};
},
condition: firstSLAItem.slaType === 'createdate'
}
];
_.find(SLAConditions, 'condition').run();
};
this.calculate = function (ticket) {
/* wait for 50ms to let angular complete dirty checking */
$timeout(function () {
//As per discussion SLA story for below ticket types SLA bar will be hidden if ticket is cancelled.
//Reason to hide : SLA bar is stopped at modified date and ticket can be modified even after cancel from mid-tier.
if ((ticket.type === EntityVO.TYPE_PROBLEM || ticket.type === EntityVO.TYPE_KNOWNERROR)
&& ticket.status.value === 'Cancelled') {
ticket.SLA.slaProgressBarValid = false;
}
else {
ticket.SLA.slaProgressBarValid = true;
}
}, 50);
ticket.SLA.items = _.sortBy(ticket.SLA.items, ['endTime', 'statusColor']);
var lastSlaItem = _.last(ticket.SLA.items);
var now = moment(), slaStartTime = moment(_.head(ticket.SLA.items).startTime), slaEndTime = moment(_.last(ticket.SLA.items).endTime), closedTime;
if (ticket.SLA.slaRenderType === 'full') {
_.forEach(ticket.SLA.items, function (item) {
if (!closedTime && item.overallStopTime) {
closedTime = moment(item.overallStopTime);
}
else if (closedTime && item.overallStopTime) {
closedTime = moment(item.overallStopTime).isAfter(closedTime) ? moment(item.overallStopTime) : closedTime;
}
});
if (!closedTime) {
closedTime = now;
}
}
else {
closedTime = ticket.modifiedDate ? moment(ticket.modifiedDate) : now;
}
//completedDate for WO, resolvedDate for incident and fetching overallStopTimefrom last SLA for change.
if (ticket.isResolved() && ticket.type === EntityVO.TYPE_CHANGE) {
if (ticket.SLA.slaRenderType !== 'partial' && ticket.SLA.items && ticket.SLA.items.length) {
closedTime = moment(_.last(ticket.SLA.items).overallStopTime);
}
else {
closedTime = ticket.actualEndDate ? moment(ticket.actualEndDate) : now;
}
}
var downStartTime = setDownStartTime(ticket), timeFromSlaStart = {
tillNow: now.diff(slaStartTime),
tillSlaEnd: slaEndTime.diff(slaStartTime),
tillClosedDate: closedTime.diff(slaStartTime),
tillDownStartTime: downStartTime ? downStartTime.diff(slaStartTime) : ''
}, slaCalculator = [
{
reachedPercent: Math.round(timeFromSlaStart.tillNow / timeFromSlaStart.tillSlaEnd * 100),
condition: (lastSlaItem.slaType !== 'sla' && !ticket.isClosed()),
timeToLimit: now.from(slaEndTime, true),
isSlaPassed: now.isAfter(slaEndTime),
autoRefresh: true
},
{
reachedPercent: Math.round(timeFromSlaStart.tillNow / timeFromSlaStart.tillSlaEnd * 100),
condition: (lastSlaItem.slaType === 'sla')
&& (!ticket.SLA.allMet && !ticket.SLA.allPaused),
timeToLimit: now.from(slaEndTime, true),
isSlaPassed: now.isAfter(slaEndTime),
autoRefresh: true
},
{
reachedPercent: Math.round(timeFromSlaStart.tillClosedDate / timeFromSlaStart.tillSlaEnd * 100),
condition: (lastSlaItem.slaType === 'sla') && ticket.SLA.allMet,
timeToLimit: closedTime.from(slaEndTime, true),
isSlaPassed: closedTime.isAfter(slaEndTime),
autoRefresh: true
},
{
reachedPercent: timeFromSlaStart.tillDownStartTime ? Math.round(timeFromSlaStart.tillDownStartTime / timeFromSlaStart.tillSlaEnd * 100)
: Math.round(timeFromSlaStart.tillNow / timeFromSlaStart.tillSlaEnd * 100),
condition: (lastSlaItem.slaType === 'sla') && ticket.SLA.allPaused,
timeToLimit: downStartTime ? downStartTime.from(slaEndTime, true) : now.from(slaEndTime, true),
isSlaPassed: downStartTime ? downStartTime.isAfter(slaEndTime) : now.isAfter(slaEndTime),
autoRefresh: false
},
{
reachedPercent: Math.round(timeFromSlaStart.tillClosedDate / timeFromSlaStart.tillSlaEnd * 100),
condition: lastSlaItem.slaType !== 'sla' && ticket.isClosed(),
timeToLimit: closedTime.from(slaEndTime, true),
isSlaPassed: closedTime.isAfter(slaEndTime),
autoRefresh: false
}
];
var slaFound = _.find(slaCalculator, 'condition');
if (slaFound) {
ticket.SLA.reachedPercent = slaFound.reachedPercent;
ticket.SLA.timeToLimit = slaFound.timeToLimit;
ticket.SLA.isSlaPassed = slaFound.isSlaPassed;
ticket.SLA.autoRefresh = slaFound.autoRefresh;
ticket.SLA.slaStartTime = slaStartTime;
ticket.SLA.slaEndTime = slaEndTime;
ticket.SLA.closedTime = closedTime;
}
processSLATooltip(ticket);
processSLAOthersCalendar(ticket);
return $q.when(ticket);
};
var setDownStartTime = function (ticket) {
ticket.SLA.allPaused = false;
if (ticket.SLA.slaRenderType === 'partial') {
/* no SLM available */
return '';
}
else {
/* SLM is available */
if (ticket.SLA.items.length === 1) {
/* one SLA */
if (ticket.SLA.items[0].measurementStatus === SLAVO.MEASUREMENT_STATUS_PENDING) {
ticket.SLA.allPaused = true;
}
else if ((ticket.SLA.items[0].measurementStatus === SLAVO.MEASUREMENT_STATUS_MET) || (ticket.SLA.items[0].measurementStatus === SLAVO.MEASUREMENT_STATUS_MISSED)) {
ticket.SLA.allMet = true;
}
return ticket.SLA.items[0].downStartTime ? moment(ticket.SLA.items[0].downStartTime) : '';
}
else {
/* multiple SLA */
var oneOrMorePaused = false, oneOrMoreMet = false, allPaused = true, allMet = true;
_.forEach(ticket.SLA.items, function (item) {
if (item.measurementStatus === SLAVO.MEASUREMENT_STATUS_PENDING) {
oneOrMorePaused = true;
allMet = false;
}
else if ((item.measurementStatus === SLAVO.MEASUREMENT_STATUS_MET) || (item.measurementStatus === SLAVO.MEASUREMENT_STATUS_MISSED)) {
oneOrMoreMet = true;
}
else {
allPaused = false;
allMet = false;
}
});
allPaused = allPaused && oneOrMorePaused; //There should be at least one in paused state and others could be in met state.
allMet = allMet && oneOrMoreMet; //There should be at least one item in Met / Missed but achieved status
ticket.SLA.allMet = allMet;
if (allPaused && !oneOrMoreMet) {
/* all SLA stopped when ticket is in pending status */
ticket.SLA.allPaused = true;
return moment(_.find(ticket.SLA.items, { 'measurementStatus': SLAVO.MEASUREMENT_STATUS_PENDING }).downStartTime);
}
else if (allPaused) {
ticket.SLA.allPaused = true;
return moment(_.find(ticket.SLA.items, { 'measurementStatus': SLAVO.MEASUREMENT_STATUS_PENDING }).downStartTime);
}
else {
/* at least one SLA didn't stop */
_.each(ticket.SLA.items, function (item) {
if (item.measurementStatus === SLAVO.MEASUREMENT_STATUS_PENDING) {
item.iconClass = 'pause_circle_o';
}
});
return '';
}
}
}
};
var processSLATooltip = function (ticket) {
var now = moment(), denominator;
if (ticket.isClosed() && ticket.SLA.slaRenderType !== 'full') {
denominator = (ticket.SLA.closedTime).isAfter(ticket.SLA.slaEndTime) ? (ticket.SLA.closedTime).diff(ticket.SLA.slaStartTime) : ticket.SLA.slaEndTime.diff(ticket.SLA.slaStartTime);
}
else if (ticket.SLA.allPaused) {
denominator = ticket.SLA.slaEndTime.diff(ticket.SLA.slaStartTime);
}
else if (ticket.SLA.allMet) {
denominator = (ticket.SLA.closedTime).isAfter(ticket.SLA.slaEndTime) ? (ticket.SLA.closedTime).diff(ticket.SLA.slaStartTime) : ticket.SLA.slaEndTime.diff(ticket.SLA.slaStartTime);
}
else {
//the SLTs will get placed on the bar <20> spread out so that the final one is assumed to be the end of the bar
//if the time passes beyond the last SLA then the <20>end time<6D> of the bar will be <20>now<6F> and the bubbles will adjust
denominator = now.isAfter(ticket.SLA.slaEndTime) ? now.diff(ticket.SLA.slaStartTime) : ticket.SLA.slaEndTime.diff(ticket.SLA.slaStartTime);
}
_.each(ticket.SLA.items, function (item) {
var messageOne, messageTwo, defaultWarningPercentage = 90,
// DRSMX-77574: metMissedAmount added to fix SLA bar issue.
slaItemEndTime = (item.metMissedAmount && item.measurementStatus === SLAVO.MEASUREMENT_STATUS_MISSED) ? moment(item.metMissedAmount) : moment(item.endTime), slaItemOverallStopTime = item.overallStopTime ? moment(item.overallStopTime) : '';
item.position = slaItemEndTime.diff(ticket.SLA.slaStartTime) / denominator;
item.timeToLimit = item.measurementStatus === SLAVO.MEASUREMENT_STATUS_PENDING ? moment(item.downStartTime).from(slaItemEndTime, true) : now.from(slaItemEndTime, true);
if (item.slaType !== 'sla') {
if (ticket.isResolved()) {
item.slaStatusClass = ticket.SLA.isSlaPassed ? 'sla-icon_color-red' : 'sla-icon_color-green';
}
else {
item.slaStatusClass = ticket.SLA.isSlaPassed ? 'sla-icon_color-red'
: (ticket.SLA.reachedPercent < defaultWarningPercentage ? 'sla-icon_color-green' : 'sla-icon_color-orange');
}
}
/* set tooltip line 2 */
if (now.isAfter(slaItemEndTime)) {
/* original due */
messageOne = $filter('i18n')('sla.due') + ' ' + convertDateToLocale(slaItemEndTime);
}
else {
if (item.endTime === '' || item.endTime === null || item.endTime === undefined) {
messageOne = $filter('i18n')('sla.due.date') + ' ' + $filter('i18n')('common.labels.unknown');
}
else {
messageOne = $filter('i18n')('sla.due.in') + ' ' + item.timeToLimit + ' (' + convertDateToLocale(slaItemEndTime) + ')';
}
}
/* set tooltip line 3 */
if (ticket.SLA.slaRenderType === 'full') {
switch (item.measurementStatus) {
case SLAVO.MEASUREMENT_STATUS_PENDING:
item.iconClass = 'pause_circle_o';
break;
case SLAVO.MEASUREMENT_STATUS_MET:
/* achieved early */
if (slaItemOverallStopTime) {
messageTwo = $filter('i18n')('sla.status.achieved', slaItemOverallStopTime.from(slaItemEndTime, true)) + ' ' + $filter('i18n')('sla.status.early', convertDateToLocale(slaItemOverallStopTime));
}
break;
case SLAVO.MEASUREMENT_STATUS_MISSED:
/* achieved late */
if (slaItemOverallStopTime) {
messageTwo = $filter('i18n')('sla.status.achieved', slaItemOverallStopTime.from(slaItemEndTime, true)) + ' ' + $filter('i18n')('sla.status.late', convertDateToLocale(slaItemOverallStopTime));
}
break;
}
}
else {
item.tooltip = '';
item.iconClass = 'circle_o';
if (ticket.isPaused()) {
messageTwo = $filter('i18n')('sla.now.paused');
}
else if (ticket.isResolved()) {
if (ticket.SLA.isSlaPassed) {
/* achieved late */
messageTwo = $filter('i18n')('sla.status.achieved', ticket.SLA.closedTime.from(slaItemEndTime, true)) + ' ' + $filter('i18n')('sla.status.late', convertDateToLocale(ticket.SLA.closedTime));
item.iconClass = 'cross_circle_o';
}
else {
/* achieved early */
messageTwo = $filter('i18n')('sla.status.achieved', ticket.SLA.closedTime.from(slaItemEndTime, true)) + ' ' + $filter('i18n')('sla.status.early', convertDateToLocale(ticket.SLA.closedTime));
item.iconClass = 'check_circle_o';
}
}
else if (ticket.isCancelled()) {
if (ticket.SLA.isSlaPassed) {
item.iconClass = 'cross_circle_o';
}
else {
item.iconClass = 'circle_o';
}
}
}
var iconHtml = "";
if (item.iconClass === 'cross_circle_o') {
item.SLAStatus = $filter('i18n')('sla.passed');
iconHtml = ' <i class="icon-' + item.iconClass + '"></i> ' + '<span class="sla-status-info" >' + item.SLAStatus + '</span>';
}
else if (item.iconClass === 'check_circle_o') {
item.SLAStatus = $filter('i18n')('sla.achieved');
iconHtml = ' <i class="icon-' + item.iconClass + '"></i> ' + '<span class="sla-status-info">' + item.SLAStatus + '</span>';
}
else if (item.iconClass === 'pause_circle_o') {
item.SLAStatus = $filter('i18n')('sla.now.paused');
iconHtml = ' <i class="icon-' + item.iconClass + '"></i> ' + '<span class="sla-status-info">' + item.SLAStatus + '</span>';
}
/* set tooltip */
item.tooltip = '<p class="nowrap-line"><b>' + item.title + '</b></p>'
+ '<p>' + messageOne + iconHtml + '</p>';
item.tooltipText = item.title + ' ' + messageOne;
if (messageTwo) {
item.tooltip += '<p>' + messageTwo + '</p>';
item.tooltipText = item.tooltipText + ' ' + messageTwo;
}
item.SLADetailTooltipText = { 'title': item.title, 'message': messageOne };
if (messageTwo) {
item.SLADetailTooltipText.message += " " + messageTwo;
}
});
var timeObject = {};
_.each(ticket.SLA.items, function (item) {
if (!_.isUndefined(timeObject[item.endTime])) {
timeObject[item.endTime].tooltipHtml += "<br/>" + item.tooltip;
timeObject[item.endTime].tooltipCount += 1;
}
else {
timeObject[item.endTime] = { 'tooltipHtml': item.tooltip, 'tooltipCount': 1 };
}
});
ticket.SLA.items.map(function (ele) {
ele.tooltipCount = timeObject[ele.endTime].tooltipCount;
if (timeObject[ele.endTime].tooltipCount > 2) {
ele.tooltip = ele.tooltip
+ '<br/> <p>' + $filter('i18n')('sla.tooltip.showMore', (timeObject[ele.endTime].tooltipCount - 1)) + '</p> '
+ '<p> ' + $filter('i18n')('sla.tooltip.clickToSeeDetails') + ' </p>';
}
else {
ele.tooltip = timeObject[ele.endTime].tooltipHtml;
}
});
};
var processSLAOthersCalendar = function (ticket) {
var now = moment(), defaultWarningPercentage = 90, calendarEndTime = ticket.SLA.slaEndTime.calendar({ sameElse: 'lll' }), slaFound;
if (ticket.SLA.allPaused) {
ticket.SLA.reachTime = $filter('i18n')('sla.now.paused');
slaFound = _.find(ticket.SLA.items, function (item) {
return item.measurementStatus === SLAVO.MEASUREMENT_STATUS_PENDING;
});
if (slaFound) {
if (moment(slaFound.downStartTime).isAfter(moment(slaFound.endTime))) {
ticket.SLA.divClassType = 'danger';
}
else if (slaFound.warningDate) {
ticket.SLA.divClassType = moment(slaFound.downStartTime).isBefore(moment(slaFound.warningDate)) ? 'success' : 'warning';
}
else {
ticket.SLA.divClassType = 'success';
}
}
else {
ticket.SLA.divClassType = 'danger';
}
}
else {
if (ticket.SLA.slaRenderType === 'partial') {
ticket.SLA.reachTime = $filter('i18n')('sla.target.date.is') + ' ' + calendarEndTime;
if (ticket.isResolved()) {
ticket.SLA.divClassType = ticket.SLA.isSlaPassed ? 'danger' : 'success';
}
else {
ticket.SLA.divClassType = ticket.SLA.isSlaPassed ? 'danger'
: (ticket.SLA.reachedPercent < defaultWarningPercentage ? 'success' : 'warning');
}
}
else {
// find the first SLA which now is before its endTime and that are not met
slaFound = _.find(ticket.SLA.items, function (item) {
return now.isBefore(moment(item.endTime)) && (item.measurementStatus != SLAVO.MEASUREMENT_STATUS_MET);
});
if (slaFound) {
ticket.SLA.reachTime = $filter('i18n')('sla.next.sla.is') + ' ' + moment(slaFound.endTime).calendar({ sameElse: 'lll' });
}
else if (ticket.SLA.allMet) {
ticket.SLA.reachTime = $filter('i18n')('sla.achieved') + ' ' + moment(_.last(ticket.SLA.items).overallStopTime).calendar({ sameElse: 'lll' });
}
else {
var lastSLAPassed = ticket.SLA.items.filter(function (item) {
return item.measurementStatus === SLAVO.MEASUREMENT_STATUS_MISSED_GOAL;
});
lastSLAPassed = (lastSLAPassed && _.last(lastSLAPassed)) || _.last(ticket.SLA.items);
ticket.SLA.reachTime = $filter('i18n')('sla.passed') + ' ' + moment(lastSLAPassed.endTime).calendar({ sameElse: 'lll' });
}
//Consider worst case scenario to show the SLA bar color
slaFound = _.find(ticket.SLA.items, function (item) {
return (item.measurementStatus === SLAVO.MEASUREMENT_STATUS_MISSED) || (item.measurementStatus === SLAVO.MEASUREMENT_STATUS_MISSED_GOAL);
});
if (slaFound) {
ticket.SLA.divClassType = 'danger';
}
else {
slaFound = _.find(ticket.SLA.items, function (item) {
return (item.measurementStatus === SLAVO.MEASUREMENT_STATUS_PENDING) && moment(item.downStartTime).isAfter(moment(item.endTime));
});
if (slaFound) {
ticket.SLA.divClassType = 'danger';
}
else {
slaFound = _.find(ticket.SLA.items, function (item) {
return ((item.measurementStatus === SLAVO.MEASUREMENT_STATUS_PENDING) && moment(item.downStartTime).isAfter(moment(item.warningDate)))
|| ((item.measurementStatus !== SLAVO.MEASUREMENT_STATUS_PENDING && item.measurementStatus !== SLAVO.MEASUREMENT_STATUS_MET) && now.isAfter(item.warningDate));
});
if (slaFound) {
ticket.SLA.divClassType = 'warning';
}
else {
ticket.SLA.divClassType = 'success';
}
}
}
}
}
};
function convertDateToLocale(date) {
if (date.toDate) {
date = date.toDate();
}
return $filter('datePreConfigTimezone')(date, 'mediumDate') + ' ' + $filter('datePreConfigTimezone')(date, 'shortTime');
}
}]);
}());