Name

global.SLATimelineAPI

Description

No description available

Script

var SLATimelineAPI = Class.create();
SLATimelineAPI.prototype = {
   // sys_properties
  SLA_ENGINE_ASYNC: 'com.snc.sla.engine.async',
  SLA_TASK_SLA_CONTROLLER_LOG: 'com.snc.sla.task_sla_controller.log',
  SLA_TASK_SLA_CONTROLLER_TIMERS: 'com.snc.sla.task_sla_controller.timers',
  SLA_TASK_SLA_DEFAULT_CONDITIONCLASS: 'com.snc.sla.default_conditionclass',
  SLA_RETROACTIVE_PAUSE: 'com.snc.sla.retroactive_pause',
  SLA_DATABASE_LOG: 'com.snc.sla.log.destination',
  SLA_SERVICE_OFFERING: 'com.snc.service.offering.field',
    // constants
  BASE_CONDITIONCLASSNAME: 'SLAConditionBase',

  SLA_SCHEDULE_SOURCE: 'com.snc.sla.schedule.source',
  SLA_TIMEZONE_SOURCE: 'com.snc.sla.timezone.source',
  // internal stage choice values
  STAGE_STARTING:    'starting',
  STATE_IN_PROGRESS : 'in_progress',
  STATE_PAUSED      : 'paused',
  STATE_CANCELLED   : 'cancelled',
  STATE_COMPLETED   : 'completed',

  initialize: function(slaGR, taskGR) {
  	this.stateChanges = {};
  	
  	var taskTable;
  	var slaTable;
  	if (slaGR)
  		slaTable = new TableUtils(slaGR.getRecordClassName());
  	if (taskGR)
  		taskTable = new TableUtils(taskGR.getRecordClassName());

  	//task_sla record
  	if (slaTable.getAbsoluteBase() == 'task_sla') {
  		this.slaGR = slaGR.sla.getRefRecord();
  		if(slaGR.task)
  			this.taskGR = slaGR.task.getRefRecord();
  		else if(taskTable && taskTable.getAbsoluteBase() == 'task')
  			this.taskGR = taskGR;
  		this.taskSLAgr = slaGR;
  	}
  	//task and sla records
  	else if (slaTable.getAbsoluteBase() == 'contract_sla' &&
             taskTable && taskTable.getAbsoluteBase() == 'task') {
  		this.slaGR = slaGR;
  		this.taskGR = taskGR;
  	}
  	if (this.slaGR) {
  		this.sv_offering = false;
  		if (this.slaGR.isValidField('sys_class_name') && this.slaGR.getValue('sys_class_name')=='service_offering_sla')
  			this.sv_offering = true;
  		if(slaTable.getAbsoluteBase() == 'contract_sla')
  			this._processNewSLA();
  	}

  	this.slaContractUtil = new SLAContractUtil();
  	//Default Service Offering field
      this.serviceOfferingField = gs.getProperty(this.SLA_SERVICE_OFFERING, 'cmdb_ci');
  },

  getScheduleGR : function(sla, task) {
  	var contractSlaGR = new GlideRecord('contract_sla');
  	if (!contractSlaGR.get(sla))
  		return null;
  	
  	var scheduleGR = new GlideRecord('cmn_schedule');
  	var schSource = contractSlaGR.schedule_source + ''; // default value: 'sla.schedule', others: 'task.cmdb_ci.schedule'
  	var tzSource = contractSlaGR.timezone_source + '';  // default value: 'task.caller_id.time_zone', others: various.. see SLATimezone.source()
  	var gr = new GlideRecord('task_sla');
  	gr.setValue('task',task);
  	gr.setValue('sla',sla);
  	var scheduleId = SLASchedule.source(schSource, gr);
  	if (scheduleId)
  		scheduleGR.get(scheduleId);
  	var tz = SLATimezone.source(tzSource, gr);  // i.e. gr.task.caller_id.time_zone
  	if (!tz)
  		// explicitly use the system timezone, if an empty/NULL value was returned above
  	tz = gs.getSysTimeZone();

  	return new GlideSchedule(scheduleGR.sys_id,tz);
  },
  
  getServOfStatus: function(sla, task) {
  	var soc = new GlideRecord("service_offering_commitment");
  	if (!soc.isValid()|| !task.getValue(this.serviceOfferingField) )
  		return false;
  	soc.addQuery("service_commitment.type", "SLA");
  	soc.addQuery("service_commitment.sla", sla.sys_id);
  	soc.addQuery("service_offering", task.getValue(this.serviceOfferingField));
  	soc.query();
  	if (!soc.hasNext())
  		return false;
  	else
  		return true;
  },

  _processNewSLA: function() {
  	if(this._processNewSLA_criticalSection(this.slaGR, this.taskGR))
  		this.taskSLAgr = this._checkNewSLA();
  },
  _checkNewSLA: function() {
  	var task_sla = new GlideRecord("task_sla");
  	task_sla.setValue("sla", this.slaGR.sys_id);
  	var slac = this.newSLACondition(task_sla.sla, this.taskGR);
  	var startMatches = slac.attach();

  	this.stateChanges["attach"] = startMatches;

  	if(!startMatches)
  		return null;
  	var taskSLAgr = new GlideRecord("task_sla");
  	taskSLAgr.sla = this.slaGR.sys_id;
  	if(this.taskGR.sys_id)
  		taskSLAgr.task = this.taskGR.sys_id;
  	taskSLAgr.stage = this.STATE_IN_PROGRESS;
  	return taskSLAgr;
  },

  //check whether to start a new sla, if not, return
  _processNewSLA_criticalSection : function(sla, task){
  	if (sla.collection != task.getRecordClassName())
  		return null;
  	if (!this._hasSLAContract(sla.collection))
  		return null;
  	if (this.sv_offering)
  		return this.getServOfStatus(sla, task);
  	return true;
  },

  getTaskSLA: function(){
  	return this.taskSLAgr;
  },
  
  getStateChanges: function() {
  	return this.stateChanges;
  },
  
  setStateChanges: function(stateChanges) {
  	if (!stateChanges)
  		return;
  	
  	var currentStateChanges = this.getStateChanges();
  	for (var stateChange in stateChanges)
  		if (stateChange in currentStateChanges)
  			currentStateChanges[stateChange] = stateChanges[stateChange];
  },
  
  getNewTaskSLA: function(){
  	return this.newTaskSLAgr;
  },
  
  setProcessReset: function() {
  	this.processReset = true;
  },
  
  getRelDurationEndDate: function(start_date, slaGR, task) {
  	var dc = new DurationCalculator();

  	//Retrieve record in case the GlideRecord passed in is a task record.
  	var retrievedTask = new GlideRecord(task.getRecordClassName());
  	if (retrievedTask.get(task.getUniqueValue())) {
  		dc.setStartDateTime(start_date);
  		if (slaGR.sla.schedule) 
  			dc.setSchedule(slaGR.sla.schedule, slaGR.sla.timezone);
  		  
  		var ocurrent = null;
  		if (typeof current !== 'undefined')
  			ocurrent = current;

  		 // Set "current" to point to either the "task_sla" record or the "table" record associated with the "SLA Definition"		  
  		 if (slaGR.relative_duration_works_on == "SLA record")
  			 current = slaGR;
  		 else 
  			 current = retrievedTask;

  		 // Perform the relative calculation using the revised value of "current"
  		 dc.calcRelativeDuration(slaGR.sla.duration_type);
       
  		 // Reset "current" to point back to its original value 
  		 if (ocurrent)
  			 current = ocurrent;
  	 }  

       return dc.getEndDateTime();
  },

  
  
  _updateState : function(newState) {
  	this.taskSLAgr.stage = newState;
  },

  //for sla contract add-on
  // returns true if sla has to be processed, false otherwise
  _hasSLAContract: function(classname) {
  	// if legacy property exists but does not include current table, ignore contract
  	if (this.slaContractUtil.ignoreContract(classname))
  		return true;
  	var contractGR = this.taskGR.contract;
  	// if SLA does not have contract, it is only processed if task's contract is set to process non-contractual SLAs
  	if (!this.slaContractUtil.slaHasContract(this.slaGR.sys_id)){
  		if ((!contractGR && !this.slaContractUtil.hasContractProperty()) // not cancel if task doesn't have contract either (for non-legacy instances having the contract table property)
  			|| this.slaContractUtil.processNonContractualSLAs(contractGR)) // or if task attached to contract allows non-contractual SLAs
  			return true;
  	}
  	// if task SLA has contract, check it is still the same contract as the task
  	if (this.slaContractUtil.isContractAttachedToSLA(contractGR.sys_id, this.slaGR.sys_id))
  		return true;

  	return false;
  },

  checkExistingSLA : function(dur_retroactive) {
  	if (!this._stopCancel(dur_retroactive))
  		this._pauseUnpause();
  },

  _pauseUnpause: function() {
  	// a "relative-duration" SLA cannot pause, whatever conditions might be in the SLA Definition record
  	if (this.slaGR.duration_type != '')
  		return false;

  	var slac = this.newSLACondition(this.taskSLAgr.sla, this.taskGR);
  	var pauseMatches = slac.pause();
  	var resumeMatches = slac.resume();
  	
  	this.stateChanges["pause"] = pauseMatches;
  	this.stateChanges["resume"] = resumeMatches;
  	
      if (this.taskSLAgr.stage == this.STATE_IN_PROGRESS && pauseMatches && !resumeMatches)
  		this._updateState(this.STATE_PAUSED);
  	else if (this.taskSLAgr.stage == this.STATE_PAUSED && !pauseMatches && resumeMatches)
  		this._updateState(this.STATE_IN_PROGRESS);
  },

  _stopCancel: function(dur_retroactive) {
  	var slac = this.newSLACondition(this.taskSLAgr.sla, this.taskGR);
  	var completeMatches = slac.complete();
  	this.stateChanges["complete"] = completeMatches;

  	var resetMatches = false;
      if (!this.processReset) {
  		resetMatches = slac.reattach(this.sla);
  		this.stateChanges["reattach"] = resetMatches;
  	} else
  		this.stateChanges["reattach"] = true;
  	
      if ((completeMatches || resetMatches) && !dur_retroactive) {
  		this._updateState(this.STATE_COMPLETED);
       // Re-evaluate conditions for this specific taskSLA,
       //      to allow a 'Complete and reapply' mode of operation
          if (resetMatches && !dur_retroactive) {
              var newTaskSLA = this._checkNewSLA();
  			// and just in case it should need to transition to Paused state immediately upon creation
  			// make sure the "updateTime" is correct as it may not be if we've been doing a retroactive calculation
  			this.newTaskSLAgr = newTaskSLA;
  		 }
  		 return true; // state was changed
      } else {
          var cancelMatches = slac.cancel();
  		this.stateChanges["cancel"] = cancelMatches;
          if (cancelMatches && !dur_retroactive) {
  			this._updateState(this.STATE_CANCELLED);
  			return true; // state was changed
  		}
  	}
  	
  	return false;
  },
  /*
  get current sla class
   */
  newSLACondition: function(slaGR, taskGR) {
  	// Use the same class, as default, during any one instance of the TaskSLAController
  	if (!this._outerScope)
  		this._outerScope = JSUtil.getGlobal();
  	if (!this._SLADefaultConditionClass) {
  		this._SLADefaultConditionClass = this._outerScope[this.SLADefaultConditionClassName];
  		// check that this._SLADefaultConditionClass is defined, and looks valid as an SLA Condition Class
  		if (!this._isValidSLAConditionClass(this._SLADefaultConditionClass)) {
  			this._SLADefaultConditionClass = this._outerScope[this.BASE_CONDITIONCLASSNAME];
  			this.SLADefaultConditionClassName = this.BASE_CONDITIONCLASSNAME;
  		}
  	}

  	var slaConditionClass = this._SLADefaultConditionClass;
  	// if the SLA Definition references a specific Condition Class then use that
  	// (as long as it looks valid)
  	if (JSUtil.notNil(slaGR.condition_class) && slaGR.condition_class.active) {
  		slaConditionClass = this._outerScope[slaGR.condition_class.class_name.name];

  		if (!this._isValidSLAConditionClass(slaConditionClass)) {
  			slaConditionClass = this._SLADefaultConditionClass;
  		}
  	}
  	var sco = new slaConditionClass(slaGR, taskGR);
  	return sco;
  },
  // does klass look valid as an SLAConditionClass?
  _isValidSLAConditionClass: function(klass) {
  	if (typeof klass == 'undefined')
  		return false;

  	var conditionMethods = ['attach', 'pause', 'complete', 'reattach', 'cancel'];
  	for (var i = 0; i < conditionMethods.length; i++)
  		if (typeof klass.prototype[conditionMethods[i]] == 'undefined')
  		return false;
  	return true;
  },

  type: 'SLATimelineAPI'
};

Sys ID

b0ff9866933002002c68530b547ffbbd

Offical Documentation

Official Docs: