Name

sn_change_cab.CABDefinitionSNC

Description

SeriviceNow logic for CAB Definition

Script

var CABDefinitionSNC = Class.create();
CABDefinitionSNC.prototype = Object.extendsObject(CABAbstractDefMeet, {
  DEFAULT_DAYS: 7776000000, // 90 days in millis
  ONE_DAY: 86400000, // 1 day in millis
  
  // Fields to copy from def to meeting.  Included here to allow override.
  COPY_FIELDS: CAB.COPY_FIELDS,
  
  initialize: function(_gr, _gs) {
  	CABAbstractDefMeet.prototype.initialize.apply(this, arguments);
  	
  	if (!this._gr.create_meetings_for.nil())
  		this._createFor = new GlideDuration((this._gr.create_meetings_for - 0) * this.ONE_DAY).getDurationValue();
      else
          this._createFor = new GlideDuration(this.DEFAULT_DAYS).getDurationValue();
  },
  
  /**
   * Refresh the exisitng meetings in line with the cab def and spans and create any new meetings for the duration
   */
  refreshMeetings: function() {
  	// Get the latest Board members and Delegates into arrays as we'll need these
  	// when creating/updating the meeetings
  	var cabManager = [this.getManager()]; // Get this into an array as it helps when determining what changes we need to make
  	var cabBoard = this.getAllBoardMembers();

  	// Update existing meetings
  	var spansStart = new GlideDateTime();
  	var spansEnd = this._getLastMeetingEnd();
  	
  	// Grab all the future meetings, update if this is active, remove otherwise.
  	if (spansEnd.after(spansStart)) {
  		if (this._gr.active) {
  			this._updateMeetingsBetween(spansStart, spansEnd, cabManager, cabBoard);
  		} else {
  			this._removeMeetingsBetween(spansStart, spansEnd);
  			return;  // No need to generate anything if it's inactive.
  		}
  	}
  	
  	// Generate meetings for the duration
  	spansStart = this._getLastMeetingEnd();
  	spansStart.add(1000);
  	spansEnd = new GlideDateTime();
  	spansEnd.add(new GlideDuration(this._createFor));
  	
  	// Generate future meetings.
  	if (spansEnd.after(spansStart))
  		this._generateMeetingsBetween(spansStart, spansEnd, cabManager, cabBoard);
  },
  
  getNextMeeting: function() {
  	return this.getNextMeetingStartingAfter(new GlideDateTime());
  },
  
  getNextMeetingStartingAfter: function(startsAfter) {
  	if (!this._gr || !this._gr.getUniqueValue())
  		return null;
  	
  	var cabMeetingGr = new GlideRecord(CAB.MEETING);
  	cabMeetingGr.addActiveQuery();
  	cabMeetingGr.addQuery(CAB.DEFINITION, this._gr.getUniqueValue());
  	if (startsAfter && startsAfter instanceof GlideDateTime)
  		cabMeetingGr.addQuery("start", ">", startsAfter.getValue());
  	cabMeetingGr.orderBy("start");
  	cabMeetingGr.query();
  	
  	if (!cabMeetingGr.next())
  		return null;
  	
  	return cabMeetingGr;
  },

  /**
   * Find all the meetings for this definition between the star tand end
   */
  _findMeetingsBetween: function (spansStart, spansEnd) {
  	var meeting = new GlideRecord(CAB.MEETING);
  	if (!spansStart || !spansEnd)
  		return meeting;
  	
  	meeting.addQuery("start",">=", spansStart);
  	meeting.addQuery("start","<=", spansEnd);
  	meeting.addQuery("start", ">", new GlideDateTime());
  	meeting.addQuery("cab_definition", this._gr.getUniqueValue());
  	meeting.addNotNullQuery("cmn_schedule_span_origin");
  	meeting.orderBy("start");
  	meeting.query();
  	
  	return meeting;
  },
  
  /**
   * Remove all meetings between the given dates.
   */
  _removeMeetingsBetween: function(spansStart, spansEnd) {
  	if (!spansStart || !spansEnd)
  		return;
  	
  	var meeting = this._findMeetingsBetween(spansStart, spansEnd);
  	while (meeting.next())
  		meeting.deleteRecord();
  },
  
  _updateMeetingsBetween: function(spansStart, spansEnd) {
  	if (!spansStart || !spansEnd)
  		return;
  	
  	var now = new GlideDateTime();
  	var meeting = this._findMeetingsBetween(spansStart, spansEnd);
  	
  	if (meeting.getRowCount() == 0)
  		return;
  	
  	if (this._log.atLevel() == global.GSLog.DEBUG) this._log.debug("[_updateMeetingsBetween] Meeting count: " + meeting.getRowCount());
  	
  	var schedule = this._getSchedule();
  	var timeMap = schedule.getTimeMap(spansStart, spansEnd);
  	timeMap.buildMap(schedule.getTimeZone());
  	
  	// Skip the first element in the timeMap if it spans now 
  	if (schedule.isInSchedule(now) && timeMap.hasNext())
  		timeMap.next();

  	var cabMeeting = new CABMeeting(meeting);
  	while (meeting.next()) {
  		if (this._log.atLevel() == global.GSLog.DEBUG) this._log.debug("[_updateMeetingsBetween] Working on: " + meeting.getUniqueValue());
  	
  		// If there are no more entries in the time map delete any further meetings.
  		if (!timeMap.hasNext()) {
  			meeting.deleteRecord();
  			continue;
  		}

  		var timeSpan;
  		if (!cabMeeting.isDisconnected())
  			timeSpan = timeMap.next();

  		if (!timeMap.hasNext()) {
  			var lastSpan = this._getCompleteSpan(timeSpan);
  			if (lastSpan)
  				timeSpan = lastSpan;
  		}
  		
  		if (!cabMeeting.isDisconnected())
  			cabMeeting.refresh(this, timeSpan);
  		else
  			cabMeeting.refresh(this, null);
  	}

  	this._updateChangeRangeOnMeetings();
  },
  
  _generateMeetingsBetween: function(spansStart, spansEnd, cabManager, cabBoard) {
  	if (!spansStart || !spansEnd)
  		return;
  	
  	var schedule = this._getSchedule();
  	var timeMap = schedule.getTimeMap(spansStart, spansEnd);
  	timeMap.buildMap(schedule.getTimeZone());
  	
  	// Skip the first element in the timeMap if it spans now 
  	if (schedule.isInSchedule(new GlideDateTime()) && timeMap.hasNext())
  		timeMap.next();
  			
  	var timeSpan;
  	while (timeMap.hasNext()) {
  		timeSpan = timeMap.next();

  		/* If this is our last span we need to do a little extra work to make sure we have a complete span
  		   This is to cater for when we refreshing meetings during the scheduled meeting time in which case
  		   we may have a partial span */
  		if (!timeMap.hasNext()) {
  			var lastSpan = this._getCompleteSpan(timeSpan);
  			if (lastSpan)
  				timeSpan = lastSpan;
  		}

  		this._cabDomUtil.runInRecordsDomain((function(){
  			var cabMeeting = CABMeeting.newMeeting(this, timeSpan);
  			cabMeeting.insertUpdate();
  		}).bind(this));
  	}
  	
  	this._updateChangeRangeOnMeetings();
  },
  
  _updateChangeRangeOnMeetings: function() {
  	var meetingGr = new GlideRecord(CAB.MEETING);
  	meetingGr.addQuery("state", "pending");
  	meetingGr.addQuery("cab_definition", this._gr.getUniqueValue());
  	meetingGr.addQuery("start", ">", new GlideDateTime());
  	meetingGr.addNotNullQuery("cmn_schedule_span_origin");
  	meetingGr.query();
  	
  	while (meetingGr.next()) {
  		var cabMeeting = new CABMeeting(meetingGr);
  		var modifiedFields = cabMeeting._getModifiedFields();
  		if (modifiedFields["change_range_start"] ||
  		    modifiedFields["change_range_end"] ||
  		    modifiedFields["ignore_change_date_range"])
  			continue;
  		
  		cabMeeting.setChangeRangeDates();
  		cabMeeting.ignoreModifiedFields();
  		cabMeeting.update();
  	}		
  },
  
  _getCompleteSpan: function(span) {
  	if (!span)
  		return null;
  	
  	// Get the start time of our the span...
  	var spanStart = span.getStart().getGlideDateTime();
  	/* ...and create a new date which is 30 days ahead of the start time
  	   which should ensure we get a complete span - no one will have a 30 day meeting... */
  	var futureDate = new GlideDateTime(spanStart);
  	futureDate.addDaysUTC(30);
  	
  	var schedule = this._getSchedule();
  	var timeMap = schedule.getTimeMap(spanStart, futureDate);
  	timeMap.buildMap(schedule.getTimeZone());
  	
  	return timeMap.next();
  },
  
  /**
   * Returns the schedule for this cab definition
   */
  _schedCache: {},
  _getSchedule: function() {
  	var id = this._gr.getUniqueValue()+"";
  	if (this._schedCache[id])
  		return this._schedCache[id];
  	else
  		this._schedCache = {};
  	
  	this._schedCache[id] = new GlideSchedule(id);
  	return this._schedCache[id];
  },
  
  /**
   * Returns the end time of the last generated, unmodified meeting.
   */
  _getLastMeetingEnd: function() {
  	var cmGa = new GlideAggregate(CAB.MEETING);
  	cmGa.addAggregate("MAX", "end");
  	cmGa.addNotNullQuery("cmn_schedule_span_origin");
  	cmGa.addQuery("cab_definition", this._gr.getUniqueValue());
  	cmGa.orderByDesc("end");
  	cmGa.query();
  	
  	// If there isn't one, just use now.
  	if (!cmGa.next())
  		return new GlideDateTime();
  	
  	return new GlideDateTime(cmGa.getAggregate("MAX", "end"));
  },
  	
  /*
   * Create a new span for a start and end time for a specified type
   */
  createNewSpan: function (startTime, endTime, type, spanName) {
  	var timeZonePrefix = "TZID=" + gs.getSession().getTimeZoneName() + ";";

  	var startSDT = new GlideScheduleDateTime(timeZonePrefix + startTime.getDisplayValue());
  	var endSDT = new GlideScheduleDateTime(timeZonePrefix + endTime.getDisplayValue());

  	var span = new GlideRecord('cmn_schedule_span');
  	if (!span.canCreate()) {

  		if (this._log.atLevel() == global.GSLog.DEBUG)
  			this._log.debug("[createNewSpan] cannot create span");

  		return null;
  	}

  	span.setValue('name', spanName);
  	span.setValue('start_date_time', startSDT.getValue());
  	span.setValue('end_date_time', endSDT.getValue());
  	span.setValue('type', type);
  	span.setValue('schedule', this._gr.getUniqueValue());
  	return span.insert();	
  },
  
  type: 'CABDefinitionSNC'
});

Sys ID

0325a6f3eb32120034d1eeea1206fe9a

Offical Documentation

Official Docs: