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

376 lines
22 KiB
JavaScript
Raw Permalink 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 = _.first(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 () {
ticket.SLA.slaProgressBarValid = true;
}, 50);
ticket.SLA.items = _.sortBy(ticket.SLA.items, 'endTime');
var lastSlaItem = _.last(ticket.SLA.items);
var now = moment(), slaStartTime = moment(_.first(ticket.SLA.items).startTime), slaEndTime = moment(_.last(ticket.SLA.items).endTime), closedTime = ticket.resolvedDate ? moment(ticket.resolvedDate)
: (ticket.completedDate ? moment(ticket.completedDate) : (lastSlaItem && lastSlaItem.overallStopTime ? moment(lastSlaItem.overallStopTime) : (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: ticket.isOngoing() && (!lastSlaItem || lastSlaItem.measurementStatus !== SLAVO.MEASUREMENT_STATUS_MET),
timeToLimit: now.from(slaEndTime, true),
isSlaPassed: now.isAfter(slaEndTime),
autoRefresh: true
},
{
reachedPercent: Math.round(timeFromSlaStart.tillClosedDate / timeFromSlaStart.tillSlaEnd * 100),
condition: ticket.isOngoing() && (lastSlaItem && lastSlaItem.measurementStatus === SLAVO.MEASUREMENT_STATUS_MET),
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: ticket.isPaused(),
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: 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;
}
return ticket.SLA.items[0].downStartTime ? moment(ticket.SLA.items[0].downStartTime) : '';
}
else {
/* multiple SLA */
var oneOrMorePaused = false, oneOrMoreMet = false, allPaused = false;
_.forEach(ticket.SLA.items, function (item) {
if (item.measurementStatus === SLAVO.MEASUREMENT_STATUS_PENDING) {
oneOrMorePaused = true;
}
else if (item.measurementStatus === SLAVO.MEASUREMENT_STATUS_MET) {
oneOrMoreMet = true;
}
allPaused = allPaused ? allPaused : (item.measurementStatus === SLAVO.MEASUREMENT_STATUS_PENDING) || (item.measurementStatus === SLAVO.MEASUREMENT_STATUS_MET);
});
allPaused = allPaused && oneOrMorePaused; //There should be at least one in paused state and others could be in met state.
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()) {
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.isPaused()) {
denominator = ticket.SLA.slaEndTime.diff(ticket.SLA.slaStartTime);
}
else if (_.last(ticket.SLA.items).measurementStatus === SLAVO.MEASUREMENT_STATUS_MET) {
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, slaItemEndTime = 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);
/* set tooltip line 2 */
if (now.isAfter(slaItemEndTime)) {
/* original due */
messageOne = $filter('i18n')('sla.due') + ' ' + convertDateToLocale(slaItemEndTime);
}
else {
/* due in */
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:
/* now paused */
messageTwo = $filter('i18n')('sla.now.paused');
item.iconClass = 'pause_circle_o';
break;
case SLAVO.MEASUREMENT_STATUS_MET:
/* achieved early */
messageTwo = $filter('i18n')('sla.achieved') + ' ' + slaItemOverallStopTime.from(slaItemEndTime, true) + ' ' + $filter('i18n')('sla.early') + ' (' + convertDateToLocale(slaItemOverallStopTime) + ')';
break;
case SLAVO.MEASUREMENT_STATUS_MISSED:
/* achieved late */
messageTwo = $filter('i18n')('sla.achieved') + ' ' + slaItemOverallStopTime.from(slaItemEndTime, true) + ' ' + $filter('i18n')('sla.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.achieved') + ' ' + ticket.SLA.closedTime.from(slaItemEndTime, true) + ' ' + $filter('i18n')('sla.late') + ' (' + convertDateToLocale(ticket.SLA.closedTime) + ')';
item.iconClass = 'cross_circle_o';
}
else {
/* achieved early */
messageTwo = $filter('i18n')('sla.achieved') + ' ' + ticket.SLA.closedTime.from(slaItemEndTime, true) + ' ' + $filter('i18n')('sla.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(), slaFound;
if (ticket.isResolved()) {
ticket.SLA.reachTime = $filter('i18n')('sla.cancelled') + ' ' + calendarEndTime;
if (ticket.SLA.closedTime.isBefore(ticket.SLA.slaEndTime)) {
ticket.SLA.divClassType = 'success';
}
else {
ticket.SLA.divClassType = 'danger';
}
}
else if (ticket.isCancelled()) {
ticket.SLA.reachTime = $filter('i18n')('sla.cancelled') + ' ' + calendarEndTime;
ticket.SLA.divClassType = 'closed';
}
else if (ticket.SLA.allPaused) {
ticket.SLA.reachTime = $filter('i18n')('sla.now.paused');
slaFound = _.find(ticket.SLA.items, function (item) {
return moment(item.downStartTime).isBefore(moment(item.endTime)) && (item.measurementStatus != SLAVO.MEASUREMENT_STATUS_MET);
});
if (slaFound) {
if (slaFound.warningDate) {
ticket.SLA.divClassType = moment(slaFound.downStartTime).isBefore(moment(slaFound.warningDate)) ? 'success' : 'warning';
}
else {
ticket.SLA.divClassType = 'danger';
}
}
else {
ticket.SLA.divClassType = 'danger';
}
}
else {
if (ticket.SLA.slaRenderType === 'partial') {
ticket.SLA.reachTime = $filter('i18n')('sla.target.date.is') + ' ' + calendarEndTime;
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();
if (slaFound.warningDate) {
ticket.SLA.divClassType = now.isBefore(moment(slaFound.warningDate)) ? 'success' : 'warning';
}
else {
ticket.SLA.divClassType = 'danger';
}
}
else if (_.last(ticket.SLA.items).measurementStatus === SLAVO.MEASUREMENT_STATUS_MET) {
ticket.SLA.reachTime = $filter('i18n')('sla.achieved') + ' ' + moment(_.last(ticket.SLA.items).overallStopTime).calendar();
ticket.SLA.divClassType = 'success';
}
else {
ticket.SLA.reachTime = $filter('i18n')('sla.passed') + ' ' + moment(_.last(ticket.SLA.items).endTime).calendar();
ticket.SLA.divClassType = 'danger';
}
}
}
};
function convertDateToLocale(date) {
if (date.toDate) {
date = date.toDate();
}
return $filter('date')(date, 'mediumDate') + ' ' + $filter('date')(date, 'shortTime');
}
}]);
}());