Name

global.RiskCalculatorSNC

Description

SNC Class containing common components for Calculating a change request s risk and impact based on defined Risk Conditions. Customize using script include RiskCalculator

Script

var RiskCalculatorSNC = Class.create();

RiskCalculatorSNC.LOG_PROPERTY = "com.snc.change_management.risk_conditions.log.level";

RiskCalculatorSNC.CHG_RISK_ASMT_PLUGIN = "com.snc.change_management.risk_assessment";
RiskCalculatorSNC.LEGACY_CHG_RISK_ASMT_PLUGIN = "com.snc.change.risk_assessment";
RiskCalculatorSNC.CHG_RISK_ML_PLUGIN = "com.snc.change_management.ml.risk";
RiskCalculatorSNC.CHG_RISK_PROBABILITY_PLUGIN = "com.snc.change_management.success_probability";
RiskCalculatorSNC._setChangeRiskDetails = function(field, value, changeRequestGr) {
  changeRequestGr = changeRequestGr || this.changeRequestGr;
  if (!changeRequestGr || !changeRequestGr.getUniqueValue() || !this.changeRiskDetailsGr)
  	return;

  value = ChangeCommon.isNil(value) ? "" : value;
  if (this.changeRiskDetailsGr.isValidField(field)) {
  	var currFieldValue = this.changeRiskDetailsGr.getValue(field);
  	currFieldValue = ChangeCommon.isNil(currFieldValue) ? "" : currFieldValue;

  	if (this.changeRiskDetailsGr.isValidField(field + "_prev"))
  		this.changeRiskDetailsGr.setValue(field + "_prev", currFieldValue);

  	this.changeRiskDetailsGr.setValue(field, value);

  	if (!this._dryRun)
  		this.updateChangeRiskDetails();
  }
};
RiskCalculatorSNC._initChangeRiskDetails = function(changeRequestGr) {
  changeRequestGr = changeRequestGr || this.changeRequestGr;
  if (!changeRequestGr || !changeRequestGr.getUniqueValue())
  	return;

  if (!this.changeRiskDetailsGr) {
  	this.changeRiskDetailsGr = new GlideRecord("change_risk_details");
  	this.changeRiskDetailsGr.addQuery("change_request", changeRequestGr.getUniqueValue());
  	this.changeRiskDetailsGr.query();
  	if (!this.changeRiskDetailsGr.next())
  		this.changeRiskDetailsGr.setValue("change_request", changeRequestGr.getUniqueValue());
  }
};
RiskCalculatorSNC.updateChangeRiskDetails = function() {
  if (!this.changeRiskDetailsGr)
  	return;

  this.changeRiskDetailsGr.update();
};

RiskCalculatorSNC.prototype = {

  initialize: function(changeRequestGr, changeRiskDetailsGr) {
  	this._log = new GSLog(RiskCalculatorSNC.LOG_PROPERTY, this.type);
  	// Requery to handle calls from scoped application
  	if (changeRequestGr) {
  		if (changeRequestGr.operation() !== "insert") {
  			this._originalChangeRequestGr = changeRequestGr;
  			this.changeRequestGr = new GlideRecord(changeRequestGr.getTableName());
  			if (this.changeRequestGr.get(changeRequestGr.getUniqueValue()))
  				this._copyChanges(changeRequestGr);
  		} else
  			this.changeRequestGr = changeRequestGr;
  	}
  	this._dryRun = !(changeRequestGr && changeRequestGr.isValidRecord());
  	this.hideNotification = false;

  	this._riskHelper = new global.ChangeRiskDetailsHelper();
  	if (changeRiskDetailsGr)
  		this.changeRiskDetailsGr = changeRiskDetailsGr;
  	else
  		this._initChangeRiskDetails(this.changeRequestGr);
  },

  /**
  * ALL Risk Evaluation should use this method. This function will update the change_request gliderecord
  * unless dryRun has been set to true. Only call in before business rule if dryRun has been set to true.
  *
  * return evaluatedRiskImpact with properties: riskCondition, riskAssessment, riskML and riskEvaluation
  */
  evaluateRiskImpact: function() {
  	var evaluatedRiskImpact = {};

  	// Risk Assessment plugin active
  	if (GlidePluginManager.isActive(RiskCalculatorSNC.CHG_RISK_ASMT_PLUGIN)) {
  		var changeRiskAsmt = new global.ChangeRiskAsmt(this.changeRequestGr, this._originalChangeRequestGr, this.changeRiskDetailsGr);
  		changeRiskAsmt.setHideNotification(this.hideNotification);
  		if (changeRiskAsmt.hasAssessment() && !changeRiskAsmt.hasCompletedAssessment())
  			evaluatedRiskImpact.errorMsg = gs.getMessage("Complete Risk Assessment to calculate risk");
  		else
  			evaluatedRiskImpact = this._dryRun ? changeRiskAsmt.setChangeRiskImpact() : changeRiskAsmt.updateChangeRiskImpact();
  	} else {
  		// Legacy Risk Assessment plugin active
  		if (GlidePluginManager.isActive(RiskCalculatorSNC.LEGACY_CHG_RISK_ASMT_PLUGIN))
  			evaluatedRiskImpact = this._dryRun ? this.legacySetChangeRiskImpact() : this.legacyUpdateChangeRiskImpact();
  		else
  			evaluatedRiskImpact = this._dryRun ? this.setChangeRiskImpact() : this.updateChangeRiskImpact();
  	}

  	if (this._log.atLevel(global.GSLog.DEBUG))
  		this._log.debug("[evaluateRiskImpact] dryRun: " + this._dryRun + " evaluatedRiskImpact: " + JSON.stringify(evaluatedRiskImpact));

  	evaluatedRiskImpact = this._removeLegacyValues(evaluatedRiskImpact);

  	if (!evaluatedRiskImpact.errorMsg)
  		this._showInfoMessage(evaluatedRiskImpact);

  	return evaluatedRiskImpact;
  },

  _showInfoMessage: function(evaluatedRiskImpact) {
  	if (this._riskHelper.getViewDetailsEnabled() === "legacy" && !this.hideNotification) {
  		if (evaluatedRiskImpact.riskAssessment && evaluatedRiskImpact.riskAssessment.msg)
  			gs.addInfoMessage(evaluatedRiskImpact.riskAssessment.msg);
  		if (evaluatedRiskImpact.riskCondition && evaluatedRiskImpact.riskCondition.msg)
  			gs.addInfoMessage(evaluatedRiskImpact.riskCondition.msg);
  		if (evaluatedRiskImpact.riskML && evaluatedRiskImpact.riskML.msg)
  			gs.addInfoMessage(evaluatedRiskImpact.riskML.msg);
  		if (evaluatedRiskImpact.calculatedRiskScore && evaluatedRiskImpact.calculatedRiskScore.msg)
  			gs.addInfoMessage(evaluatedRiskImpact.calculatedRiskScore.msg);
  		if (evaluatedRiskImpact.riskEvaluation && evaluatedRiskImpact.riskEvaluation.risk && evaluatedRiskImpact.riskEvaluation.risk.updatedMsg)
  			gs.addInfoMessage(evaluatedRiskImpact.riskEvaluation.risk.updatedMsg);
  		return;
  	}

  	var msgString = "";

  	if (evaluatedRiskImpact && evaluatedRiskImpact.riskCondition && evaluatedRiskImpact.riskCondition.impact && (evaluatedRiskImpact.riskCondition.impact.display_value || evaluatedRiskImpact.riskCondition.impact.value))
  		evaluatedRiskImpact.riskCondition.msg += "; " + gs.getMessage("Impact: {0}{1}{2}", [new RiskCalculator().getStartColorTag(), evaluatedRiskImpact.riskCondition.impact.display_value || evaluatedRiskImpact.riskCondition.impact.value, new RiskCalculator().getEndColorTag()]);

  	var isRiskUpdated = this._isRiskUpdated(evaluatedRiskImpact);
  	var isImpactUpdated = this._isImpactUpdated(evaluatedRiskImpact);
  	if (!isRiskUpdated && !isImpactUpdated)
  		msgString = gs.getMessage('Risk and Impact remain unchanged');
  	else if (isRiskUpdated && !isImpactUpdated)
  		msgString = gs.getMessage('Risk has been set to {0}{1}{2} and Impact remains unchanged', [this.getStartColorTag(), evaluatedRiskImpact.riskEvaluation.risk.display_value, this.getEndColorTag()]);
  	else if (!isRiskUpdated && isImpactUpdated)
  		msgString = gs.getMessage('Risk remains unchanged and Impact has been set to {0}{1}{2}', [this.getStartColorTag(), evaluatedRiskImpact.riskEvaluation.impact.display_value, this.getEndColorTag()]);
  	else
  		msgString = gs.getMessage('Risk has been set to {0}{1}{2} and Impact has been set to {0}{3}{2}', [this.getStartColorTag(), evaluatedRiskImpact.riskEvaluation.risk.display_value, this.getEndColorTag(), evaluatedRiskImpact.riskEvaluation.impact.display_value]);

  	this._setChangeRiskDetails("risk_changed", isRiskUpdated, this.changeRequestGr);
  	this._setChangeRiskDetails("impact_changed", isImpactUpdated, this.changeRequestGr);

  	if (!this.hideNotification) {
  		if (this._riskHelper.showDetailsLink()) {
  			var sanitizedMsgString = SNC.GlideHTMLSanitizer.sanitize(msgString);
  			var sanitizedAriaLabel = SNC.GlideHTMLSanitizer.sanitize(gs.getMessage("View details about risk and impact calculation"));
  			var sanitizedLinkLabel = SNC.GlideHTMLSanitizer.sanitize(gs.getMessage("View details"));
  			gs.addInfoMessageNoSanitization(sanitizedMsgString + ". <a id='chg_risk_details_link' href='javascript:void(0);' onclick='openRiskDetails()' role='button' aria-label='" + sanitizedAriaLabel + "'>" + sanitizedLinkLabel + "</a>");
  		} else
  			gs.addInfoMessage(msgString);
  	} else
  		evaluatedRiskImpact.riskEvaluation.msg = msgString;
  },

  _isRiskUpdated: function(evaluatedRiskImpact) {
  	return evaluatedRiskImpact && evaluatedRiskImpact.riskEvaluation && evaluatedRiskImpact.riskEvaluation.risk && evaluatedRiskImpact.riskEvaluation.risk.updated;
  },

  _isImpactUpdated: function(evaluatedRiskImpact) {
  	return evaluatedRiskImpact && evaluatedRiskImpact.riskEvaluation && evaluatedRiskImpact.riskEvaluation.impact && evaluatedRiskImpact.riskEvaluation.impact.updated;
  },

  legacySetChangeRiskImpact: function() {
  	var evaluatedRiskImpact = {};
  	var riskAssessmentCalculator = new RiskAssessmentCalculator(this.changeRequestGr, this._originalChangeRequestGr, this.changeRiskDetailsGr);
  	var assessmentMatch = riskAssessmentCalculator.checkForMatchingAssessment(this.changeRequestGr.sys_class_name, this.changeRequestGr) + "";
  	if (!assessmentMatch) {
  		this._setChangeRiskDetails("assessment_master", "", this.changeRequestGr);
  		this._setChangeRiskDetails("legacy_risk_assessment", "", this.changeRequestGr);
  		evaluatedRiskImpact = riskAssessmentCalculator.calculateRisk(this.changeRequestGr);
  		if (evaluatedRiskImpact && evaluatedRiskImpact.riskCondition && evaluatedRiskImpact.riskCondition.sys_id) {
  			this._setRiskCondition(evaluatedRiskImpact.riskCondition.sys_id, evaluatedRiskImpact.riskCondition.risk.value, evaluatedRiskImpact.riskCondition.impact.value);
  			this._setChangeRiskDetails("risk_condition_has_run", true, this.changeRequestGr);
  		}
  	}
  	else {
  		var riskAssessment = riskAssessmentCalculator.checkForAssessmentInstance(this.changeRequestGr.sys_id) + "";

  		if (this._log.atLevel(global.GSLog.DEBUG))
  			this._log.debug("[legacyUpdateChangeRiskImpact] riskAssessment: " + riskAssessment + " assessmentMatch: " + assessmentMatch);

  		// the correct assessment has been taken
  		if (riskAssessment && riskAssessment === assessmentMatch)
  			evaluatedRiskImpact = riskAssessmentCalculator.calculateRisk(this.changeRequestGr);

  		if (!evaluatedRiskImpact.riskAssessment)
  			evaluatedRiskImpact.riskAssessment = {};

  		if (riskAssessment && riskAssessment !== assessmentMatch)
  			evaluatedRiskImpact.errorMsg = gs.getMessage("Incorrect risk assessment taken, please fill out a new assessment");

  		if (!riskAssessment)
  			evaluatedRiskImpact.errorMsg = gs.getMessage("A risk assessment is required, please fill out a risk assessment");

  		if (evaluatedRiskImpact && evaluatedRiskImpact.riskAssessment && evaluatedRiskImpact.riskAssessment.risk) {
  			this._setChangeRiskDetails("assessment_master", evaluatedRiskImpact.riskAssessment.sys_id);
  			this._setChangeRiskDetails("legacy_risk_assessment", evaluatedRiskImpact.riskAssessment.risk.value);
  			this._setChangeRiskDetails("legacy_risk_assessment_has_run", true, this.changeRequestGr);
  		}
  	}

  	if (this._log.atLevel(global.GSLog.DEBUG))
  		this._log.debug("[legacySetChangeRiskImpact] evaluatedRiskImpact: " + JSON.stringify(evaluatedRiskImpact));

  	return evaluatedRiskImpact;
  },

  legacyUpdateChangeRiskImpact: function() {
  	var evaluatedRiskImpact = this.legacySetChangeRiskImpact();

  	if (this._log.atLevel(global.GSLog.DEBUG))
  		this._log.debug("[legacyUpdateChangeRiskImpact] dryRun: " + this._dryRun + " evaluatedRiskImpact: " + JSON.stringify(evaluatedRiskImpact));

  	if (this._shouldUpdateRecord(evaluatedRiskImpact))
  		this.changeRequestGr.update();

  	if (!evaluatedRiskImpact.errorMsg)
  		this._showInfoMessage(evaluatedRiskImpact);

  	return evaluatedRiskImpact;
  },

  /**
  * Evaluates riskConditions ONLY. Does not update gliderecord so can be used by before business rule if required.
  *
  * return evaluatedRiskImpact with properties: riskCondition and riskEvaluation
  */
  setChangeRiskImpact: function() {
  	var evaluatedRiskImpact = this.calculateRiskConditions();

  	if (this._log.atLevel(global.GSLog.DEBUG))
  		this._log.debug("[setChangeRiskImpact] evaluatedRiskImpact: " + JSON.stringify(evaluatedRiskImpact));

  	return this.setRiskImpact(evaluatedRiskImpact);
  },

  /**
  * Sets the evaluated risk and impact on a given Change Request Gliderecord
  *
  * return evaluatedRiskImpact with properties: riskCondition and riskEvaluation
  */
  setRiskImpact: function(evaluatedRiskImpact, changeRequestGr) {
  	changeRequestGr = changeRequestGr || this.changeRequestGr;
  	evaluatedRiskImpact = evaluatedRiskImpact || {};

  	if (!changeRequestGr || !evaluatedRiskImpact.riskEvaluation)
  		return evaluatedRiskImpact;

  	if (evaluatedRiskImpact.riskEvaluation.risk && (evaluatedRiskImpact.riskEvaluation.risk.updated || changeRequestGr.getElement("risk").changes()))
  		changeRequestGr.risk = evaluatedRiskImpact.riskEvaluation.risk.value;

  	if (evaluatedRiskImpact.riskEvaluation.impact && (evaluatedRiskImpact.riskEvaluation.impact.updated || changeRequestGr.getElement("impact").changes()))
  		changeRequestGr.impact = evaluatedRiskImpact.riskEvaluation.impact.value;

  	if (this._log.atLevel(global.GSLog.DEBUG))
  		this._log.debug("[setRiskImpact] changeRequestGr.changes: " + changeRequestGr.changes());

  	// When it runs by business rule and either Risk Assessment plugin is installed,
  	// the current RiskCalculator instance is not aware of changes to change_risk_details because both Legacy Risk Assessment and Risk Assessment v2 create a
  	// new RiskCalculator instance, so resetting the record
  	if (evaluatedRiskImpact.riskCondition && evaluatedRiskImpact.riskCondition.risk && evaluatedRiskImpact.riskCondition.impact) {
  		if (this._dryRun && this._isRiskAssessmentPluginInstalled()) {
  			this._setRiskCondition(evaluatedRiskImpact.riskCondition.sys_id, evaluatedRiskImpact.riskCondition.risk.value, evaluatedRiskImpact.riskCondition.impact.value);
  			this._setChangeRiskDetails("risk_condition_has_run", true, this.changeRequestGr);
  		}
  	}
  	else
  		this._clearRiskCondition();
  	return evaluatedRiskImpact;
  },

  getColor: function() {
  	return "red";
  },

  getStartColorTag: function() {
  	return "<font color='" + this.getColor() + "'>";
  },

  getEndColorTag: function() {
  	return "</font>";
  },

  setDryRun: function(dryRun) {
  	this._dryRun = dryRun || false;

  	if (this._log.atLevel(global.GSLog.DEBUG))
  		this._log.debug("[setDryRun] this._dryRun: " + this._dryRun);

  	return this;
  },

  // Because of requery to handle calls from scoped application, need to copy changes to current
  _copyChanges: function(changeRequestGr) {
  	var elements = GlideTableDescriptor.get(changeRequestGr.getTableName()).getSchemaList();
  	var length = elements.size();
  	for (var i = 0; i < length; i++) {
  		var elementDescriptor = elements.get(i);
  		var column = elementDescriptor.getName();
  		if (!column || column.startsWith("sys_") || !changeRequestGr.getElement(column).changes())
  			continue;
  		this.changeRequestGr.setValue(column, changeRequestGr.getValue(column));
  	}
  	// Since this is used in before business_rule, ensure reference to current is updated
  	changeRequestGr = this.changeRequestGr;
  },

  _removeLegacyValues: function(evaluatedRiskImpact) {
  	var result = {};

  	if (evaluatedRiskImpact.errorMsg)
  		result.errorMsg = evaluatedRiskImpact.errorMsg;

  	if (evaluatedRiskImpact.riskCondition)
  		result.riskCondition = evaluatedRiskImpact.riskCondition;

  	if (evaluatedRiskImpact.riskAssessment)
  		result.riskAssessment = evaluatedRiskImpact.riskAssessment;

  	if (evaluatedRiskImpact.riskML)
  		result.riskML = evaluatedRiskImpact.riskML;

  	if (evaluatedRiskImpact.riskEvaluation)
  		result.riskEvaluation = evaluatedRiskImpact.riskEvaluation;
  	
  	if (evaluatedRiskImpact.calculatedRiskScore)
  		result.calculatedRiskScore = evaluatedRiskImpact.calculatedRiskScore;

  	if (this._log.atLevel(global.GSLog.DEBUG))
  		this._log.debug("[_removeLegacyValues] result: " + JSON.stringify(result));

  	return result;
  },

  calculateRisk: function() {
  	var evaluatedRiskImpact = this.getRiskList();

  	if (GlidePluginManager.isActive(RiskCalculatorSNC.CHG_RISK_ML_PLUGIN)) {
  		var changeRiskML = new sn_chg_ml_risk.ChangeMLRiskCalculator();
  		if (changeRiskML.isSolutionEnabled() && changeRiskML.hasActiveSolution()) {
  			var predictResult = new sn_chg_ml_risk.ChangeMLRiskCalculator().getPredictedValue(this.changeRequestGr);
  			if (predictResult && predictResult.risk) {
  				evaluatedRiskImpact.riskML = predictResult;
  				evaluatedRiskImpact.riskML.risk.updated = parseInt(evaluatedRiskImpact.riskML.risk.value) !== parseInt(this.changeRequestGr.risk) || this.changeRequestGr.risk.changes();
  				this._setChangeRiskDetails("risk_ml", evaluatedRiskImpact.riskML.risk.value);
  				this._setChangeRiskDetails("ml_capability_definition_base", predictResult.mlDefSysID);
  			}
  			else
  				// ML could not predict a value for the current record
  				this._setChangeRiskDetails("risk_ml", "");
  			this._setChangeRiskDetails("risk_ml_has_run", true, this.changeRequestGr);
  		}
  		else {
  			// Clear ML values
  			this._setChangeRiskDetails("risk_ml_has_run", false, this.changeRequestGr);
  			this._setChangeRiskDetails("risk_ml", "");
  			this._setChangeRiskDetails("ml_capability_definition_base", "");
  		}
  		this._setChangeRiskDetails("risk_ml_set", changeRiskML.applyRiskValue(), this.changeRequestGr);
  	}
  	if (GlidePluginManager.isActive(RiskCalculatorSNC.CHG_RISK_PROBABILITY_PLUGIN)) {
  		var successProbability = new sn_chg_probability.SuccessProbabilityUtils(this.changeRequestGr);
  		var evaluatedImpact;
  		if (evaluatedRiskImpact && evaluatedRiskImpact.riskCondition && evaluatedRiskImpact.riskCondition.impact && evaluatedRiskImpact.riskCondition.impact.value)
  			evaluatedImpact = evaluatedRiskImpact.riskCondition.impact.value;
  		else
  			evaluatedImpact = this.changeRequestGr.getValue("impact");
  		evaluatedRiskImpact.calculatedRiskScore = {};
  		evaluatedRiskImpact.calculatedRiskScore.msg = gs.getMessage("Calculated Risk Score evaluated: Risk: Not Set");
  		if (successProbability.evaluateProbabilityModules()){
  			var changeRiskProbability = new sn_chg_probability.ChangeProbabilityRiskCalculator();
  			var calculatedRiskScore = changeRiskProbability.calculateRisk(this.changeRequestGr, evaluatedImpact);
  			if (calculatedRiskScore && calculatedRiskScore.risk) {
  				evaluatedRiskImpact.calculatedRiskScore = calculatedRiskScore;
  				evaluatedRiskImpact.calculatedRiskScore.risk.updated = parseInt(calculatedRiskScore.risk.value) !== parseInt(this.changeRequestGr.risk) || this.changeRequestGr.risk.changes();
  				evaluatedRiskImpact.calculatedRiskScore.msg = gs.getMessage("Calculated Risk Score evaluated: Risk: {0}{1}{2}", [this.getStartColorTag(), evaluatedRiskImpact.calculatedRiskScore.risk.display_value || evaluatedRiskImpact.calculatedRiskScore.risk.value, this.getEndColorTag()]);
  				this._setChangeRiskDetails("calculated_risk_score_risk", evaluatedRiskImpact.calculatedRiskScore.risk.value);
  				this._setChangeRiskDetails("calculated_risk_score", calculatedRiskScore.sys_id);
  			}
  			else{
  				// No record found in lookup table
  				this._setChangeRiskDetails("calculated_risk_score_risk", "");
  				this._setChangeRiskDetails("calculated_risk_score", "");
  			}
  			this._setChangeRiskDetails("calculated_risk_score_has_run", true, this.changeRequestGr);
  		}
  		else{
  			// Clear Prob values as something went wrong in setting probability value
  			this._setChangeRiskDetails("calculated_risk_score_has_run", false, this.changeRequestGr);
  			this._setChangeRiskDetails("calculated_risk_score", "");
  			this._setChangeRiskDetails("calculated_risk_score_risk", "");
  		}
  	}
  	
  	return !evaluatedRiskImpact || !evaluatedRiskImpact.riskCondition || !evaluatedRiskImpact.riskCondition.match ? "" : evaluatedRiskImpact;
  },

  calculateRiskConditions: function() {
  	var evaluatedRiskImpact = this.calculateRisk();
  	if (!evaluatedRiskImpact || !evaluatedRiskImpact.riskCondition)
  		return {};

  	evaluatedRiskImpact.riskCondition.msg = evaluatedRiskImpact.riskCondition.msg ? evaluatedRiskImpact.riskCondition.msg += ": " + gs.getMessage("Risk Condition calculated") : gs.getMessage("Risk Condition calculated");

  	if (evaluatedRiskImpact.riskCondition &&
  		evaluatedRiskImpact.riskCondition.name &&
  		evaluatedRiskImpact.riskCondition.name.value) {
  		var escapedName = GlideStringUtil.escapeHTML(evaluatedRiskImpact.riskCondition.name.display_value || evaluatedRiskImpact.riskCondition.name.value).replaceAll('"', '"');
  		var escapedDescription = "";
  		if (evaluatedRiskImpact.riskCondition.description && evaluatedRiskImpact.riskCondition.description.value)
  			escapedDescription = GlideStringUtil.escapeHTML(evaluatedRiskImpact.riskCondition.description.display_value || evaluatedRiskImpact.riskCondition.description.value).replaceAll('"', '"');
  		evaluatedRiskImpact.riskCondition.msg += ": " + '<strong><span style="cursor:default;" title="' + escapedDescription + '">' + escapedName + "</span></strong>";
  	}

  	evaluatedRiskImpact = evaluatedRiskImpact || {};
  	evaluatedRiskImpact.riskEvaluation = evaluatedRiskImpact.riskEvaluation || {};

  	if (evaluatedRiskImpact.riskCondition.risk && (evaluatedRiskImpact.riskCondition.risk.value || evaluatedRiskImpact.riskCondition.risk.value === 0) && !isNaN(evaluatedRiskImpact.riskCondition.risk.value)) {
  		evaluatedRiskImpact.riskEvaluation.risk = evaluatedRiskImpact.riskCondition.risk;
  		evaluatedRiskImpact.riskEvaluation.risk.updated = parseInt(evaluatedRiskImpact.riskCondition.risk.value) !== parseInt(this.changeRequestGr.risk) || this.changeRequestGr.risk.changes();
  		evaluatedRiskImpact.riskCondition.msg += "; " + gs.getMessage("Risk: {0}{1}{2}", [this.getStartColorTag(), evaluatedRiskImpact.riskCondition.risk.display_value || evaluatedRiskImpact.riskCondition.risk.value, this.getEndColorTag()]);
  	}
  	
  	if (evaluatedRiskImpact.calculatedRiskScore &&
  		evaluatedRiskImpact.calculatedRiskScore.risk &&
  		evaluatedRiskImpact.calculatedRiskScore.risk.value && (
  			!evaluatedRiskImpact.riskEvaluation ||
  			!evaluatedRiskImpact.riskEvaluation.risk ||
  			!evaluatedRiskImpact.riskEvaluation.risk.value ||
  				parseInt(evaluatedRiskImpact.calculatedRiskScore.risk.value) < parseInt(evaluatedRiskImpact.riskEvaluation.risk.value)))
  		evaluatedRiskImpact.riskEvaluation.risk = evaluatedRiskImpact.calculatedRiskScore.risk;
  	
  	if (evaluatedRiskImpact.riskML &&
  		evaluatedRiskImpact.riskML.apply &&
  		evaluatedRiskImpact.riskML.risk &&
  		evaluatedRiskImpact.riskML.risk.value && (
  			!evaluatedRiskImpact.riskEvaluation ||
  			!evaluatedRiskImpact.riskEvaluation.risk ||
  			!evaluatedRiskImpact.riskEvaluation.risk.value ||
  				parseInt(evaluatedRiskImpact.riskML.risk.value) < parseInt(evaluatedRiskImpact.riskEvaluation.risk.value)))
  		evaluatedRiskImpact.riskEvaluation.risk = evaluatedRiskImpact.riskML.risk;
  	
  	if ((!evaluatedRiskImpact.riskCondition || !evaluatedRiskImpact.riskCondition.risk || !evaluatedRiskImpact.riskCondition.risk.value)
  		&& (!evaluatedRiskImpact.riskEvaluation || !evaluatedRiskImpact.riskEvaluation.risk || !evaluatedRiskImpact.riskEvaluation.risk.value)
  		&& (!evaluatedRiskImpact.calculatedRiskScore || !evaluatedRiskImpact.calculatedRiskScore.risk || !evaluatedRiskImpact.calculatedRiskScore.risk.value)
  		&& (!evaluatedRiskImpact.riskML || !evaluatedRiskImpact.riskML.risk || !evaluatedRiskImpact.riskML.risk.value)) {
  		if (GlidePluginManager.isActive(RiskCalculatorSNC.CHG_RISK_ASMT_PLUGIN))
  			evaluatedRiskImpact.riskEvaluation.risk = {};
  		else {
  			// If the Risk is "Leave Alone" then populate risk with the change request risk value
  			evaluatedRiskImpact.riskEvaluation.risk = {
  				value: this.changeRequestGr.risk.nil() ? "" : this.changeRequestGr.getValue("risk"),
  				display_value: this.changeRequestGr.risk.getDisplayValue(),
  				updated: false
  			};
  		if (evaluatedRiskImpact.riskCondition)
  			evaluatedRiskImpact.riskCondition.msg += "; " + gs.getMessage("Risk unchanged");
  		}
  	}

  	// risk conditions may also set impact
  	if (evaluatedRiskImpact.riskCondition.impact && evaluatedRiskImpact.riskCondition.impact.value) {
  		evaluatedRiskImpact.riskEvaluation.impact = evaluatedRiskImpact.riskCondition.impact;
  		evaluatedRiskImpact.riskEvaluation.impact.updated = this.changeRequestGr.impact + "" !== evaluatedRiskImpact.riskCondition.impact.value + "" || this.changeRequestGr.impact.changes();
  		evaluatedRiskImpact.riskCondition.msg += "; " + gs.getMessage("Impact: {0}{1}{2}", [this.getStartColorTag(),  evaluatedRiskImpact.riskCondition.impact.display_value || evaluatedRiskImpact.riskCondition.impact.value, this.getEndColorTag()]);
  	} else {
  		// If the Impact is "Leave Alone" then populate impact with the change request impact value
  		evaluatedRiskImpact.riskEvaluation.impact = {
  			value: this.changeRequestGr.getValue("impact"),
  			display_value: this.changeRequestGr.impact.getDisplayValue(),
  			updated: false
  		};
  		evaluatedRiskImpact.riskCondition.msg += "; " + gs.getMessage("Impact unchanged");
  	}

  	if (this._log.atLevel(global.GSLog.DEBUG))
  		this._log.debug("[calculateRiskConditions] evaluatedRiskImpact: " + JSON.stringify(evaluatedRiskImpact));

  	return evaluatedRiskImpact;
  },

  showRiskAssessment: function() {
  	// Risk Assessment plugin active
  	if (GlidePluginManager.isActive(RiskCalculatorSNC.CHG_RISK_ASMT_PLUGIN))
  		return new global.ChangeRiskAsmt(this.changeRequestGr).showRiskAssessment();
  	else
  		return false;
  },

  showCalculateRisk: function(changeRequestGr) {
  	changeRequestGr = changeRequestGr || this.changeRequestGr;

  	if (!changeRequestGr)
  		return false;

  	// Show Calculate Risk if Risk Assessment completed
  	if (this.hasCompletedAssessment(changeRequestGr))
  		return true;

  	var isUIAction = gs.getProperty("glide.ui.risk_calculate_rule") === "ui_action";
  	var isValidData = !changeRequestGr.isNewRecord() && changeRequestGr.isValidRecord() && this.hasRiskConditions();
  	var hasAssessment = this.hasAssessment(changeRequestGr);

  	return isUIAction && isValidData && !hasAssessment;
  },

  hasAssessment: function(changeRequestGr) {
  	changeRequestGr = changeRequestGr || this.changeRequestGr;
  	if (!changeRequestGr)
  		return false;

  	// Risk Assessment plugin active
  	if (GlidePluginManager.isActive(RiskCalculatorSNC.CHG_RISK_ASMT_PLUGIN)) {
  		var changeRiskAsmt = new global.ChangeRiskAsmt(changeRequestGr);
  		return changeRiskAsmt.hasAssessment();
  	}
  	return false;
  },

  hasCompletedAssessment: function(changeRequestGr) {
  	changeRequestGr = changeRequestGr || this.changeRequestGr;
  	if (!changeRequestGr)
  		return false;

  	// Risk Assessment plugin active
  	if (GlidePluginManager.isActive(RiskCalculatorSNC.CHG_RISK_ASMT_PLUGIN)) {
  		var changeRiskAsmt = new global.ChangeRiskAsmt(changeRequestGr);
  		return changeRiskAsmt.hasAssessment() && changeRiskAsmt.hasCompletedAssessment();
  	}
  	return false;
  },

  _shouldUpdateRecord: function(evaluatedRiskImpact) {
  	return !evaluatedRiskImpact.errorMsg && (this.changeRequestGr.changes() || evaluatedRiskImpact.riskEvaluation.risk.updated || evaluatedRiskImpact.riskEvaluation.impact.updated);
  },

  updateChangeRiskImpact: function() {
  	var evaluatedRiskImpact = this.setChangeRiskImpact();
  	if (!evaluatedRiskImpact || !evaluatedRiskImpact.riskEvaluation)
  		return;

  	if (this._shouldUpdateRecord(evaluatedRiskImpact))
  		this.changeRequestGr.update();

  	if (this._log.atLevel(global.GSLog.DEBUG))
  		this._log.debug("[updateChangeRiskImpact] evaluatedRiskImpact: " + JSON.stringify(evaluatedRiskImpact));

  	return evaluatedRiskImpact;
  },

  getRiskList: function() {
  	return this._evaluateRiskConditions();
  },

  hasRiskConditions: function() {
  	return this._getRiskConditionsGr().hasNext();
  },

  getGlideRecord: function() {
  	return this.changeRequestGr;
  },

  setHideNotification: function(hideNotification) {
  	this.hideNotification = hideNotification;
  	return this;
  },

  _setRiskCondition: function(ruleSysId, riskValue, impactValue){
  	this._setChangeRiskDetails("risk_condition", ruleSysId);
  	this._setChangeRiskDetails("risk_condition_risk", riskValue);
  	this._setChangeRiskDetails("risk_condition_impact", impactValue);
  },

  _clearRiskCondition: function() {
  	this._setRiskCondition("", "", "");
  },

  _evaluateRiskConditions: function() {
  	var evaluatedRiskImpact = {
  		riskCondition: {
  			active: false,
  			match: false
  		}
  	};

  	var evaluator;
  	var riskConditionGr = this._getRiskConditionsGr();

  	// First risk condition to match wins
  	while (riskConditionGr.next() && !evaluatedRiskImpact.riskCondition.match) {
  		evaluatedRiskImpact.riskCondition.active = true;

  		if (riskConditionGr.use_advanced_condition) {
  			if (!evaluator) {
  				evaluator = new GlideScopedEvaluator();
  				evaluator.putVariable("current", this.changeRequestGr);
  				evaluator.putVariable("answer", false);
  			}
  			evaluator.evaluateScript(riskConditionGr, "advanced_condition");
  			evaluatedRiskImpact.riskCondition.match = evaluator.getVariable("answer");
  		} else
  			evaluatedRiskImpact.riskCondition.match = GlideFilter.checkRecord(this.changeRequestGr, riskConditionGr.condition);

  		if (evaluatedRiskImpact.riskCondition.match) {
  			evaluatedRiskImpact.riskCondition.sys_id = riskConditionGr.getUniqueValue();

  			evaluatedRiskImpact.riskCondition.name = {
  				value: riskConditionGr.getValue("name") || "",
  				display_value: riskConditionGr.name.getDisplayValue() || ""
  			};

  			evaluatedRiskImpact.riskCondition.order = {
  				value: riskConditionGr.getValue("order") || "",
  				display_value: riskConditionGr.order.getDisplayValue() || ""
  			};

  			evaluatedRiskImpact.riskCondition.description = {
  				value: riskConditionGr.getValue("description") || "",
  				display_value: riskConditionGr.description.getDisplayValue() || ""
  			};

  			evaluatedRiskImpact.riskCondition.useScriptValues = {
  				value: riskConditionGr.getValue("use_script_values") || "",
  				display_value: riskConditionGr.use_script_values.getDisplayValue() || ""
  			};

  			if (riskConditionGr.use_script_values) {
  				if (!evaluator) {
  					evaluator = new GlideScopedEvaluator();
  					evaluator.putVariable("current", this.changeRequestGr);
  				}
  				evaluator.evaluateScript(riskConditionGr, "script_values");

  				evaluatedRiskImpact.riskCondition.risk = {
  					value: this.changeRequestGr.risk.nil() ? "" : this.changeRequestGr.getValue("risk") || "",
  					display_value: this.changeRequestGr.risk.getDisplayValue() || ""
  				};

  				evaluatedRiskImpact.riskCondition.impact = {
  					value: this.changeRequestGr.impact.nil() ? "" : this.changeRequestGr.getValue("impact") || "",
  					display_value: this.changeRequestGr.impact.getDisplayValue() || ""
  				};
  			} else {
  				evaluatedRiskImpact.riskCondition.risk = {
  					value: riskConditionGr.getValue("risk") || "",
  					display_value: riskConditionGr.risk.getDisplayValue() || ""
  				};
  				evaluatedRiskImpact.riskCondition.impact = {
  					value: riskConditionGr.getValue("impact") || "",
  					display_value: riskConditionGr.impact.getDisplayValue() || ""
  				};
  			}

  			this._setRiskCondition(evaluatedRiskImpact.riskCondition.sys_id, evaluatedRiskImpact.riskCondition.risk.value, evaluatedRiskImpact.riskCondition.impact.value);
  			this._setChangeRiskDetails("risk_condition_has_run", true, this.changeRequestGr);

  			// Populate legacy values (as customers may have called and expect JSON legacy values)
  			evaluatedRiskImpact.hasValue = evaluatedRiskImpact.riskCondition.match;
  			evaluatedRiskImpact.name = evaluatedRiskImpact.riskCondition.name.value || "";
  			evaluatedRiskImpact.order = evaluatedRiskImpact.riskCondition.order.value || 0;
  			evaluatedRiskImpact.description = evaluatedRiskImpact.riskCondition.description.value || "";
  			evaluatedRiskImpact.impact = evaluatedRiskImpact.riskCondition.impact.value || "";
  			evaluatedRiskImpact.impactLabel = evaluatedRiskImpact.riskCondition.impact.display_value || "";
  			evaluatedRiskImpact.risk = evaluatedRiskImpact.riskCondition.risk.value || "";
  			evaluatedRiskImpact.label = evaluatedRiskImpact.riskCondition.risk.display_value || "";
  			// End of legacy values
  		}
  	}

  	if (evaluatedRiskImpact.riskCondition.active && !evaluatedRiskImpact.riskCondition.match && !this._dryRun) {
  		evaluatedRiskImpact.riskCondition.msg = gs.getMessage("No matching Risk Conditions - Risk and Impact unchanged");
  		this._clearRiskCondition();
  	} else if (!evaluatedRiskImpact.riskCondition.active) {
  		evaluatedRiskImpact.riskCondition.msg = gs.getMessage("No active Risk Conditions - Risk and Impact unchanged");
  		this._clearRiskCondition();
  	}

  	if (this._log.atLevel(global.GSLog.DEBUG))
  		this._log.debug("[_evaluateRiskConditions] evaluatedRiskImpact: " + JSON.stringify(evaluatedRiskImpact));

  	return evaluatedRiskImpact;
  },

  _getRiskConditionsGr: function() {
  	if (this.riskConditionGr)
  		this.riskConditionGr.restoreLocation();
  	else {
  		this.riskConditionGr = new GlideRecord("risk_conditions");
  		this.riskConditionGr.addActiveQuery();
  		this.riskConditionGr.orderBy("order");
  		this.riskConditionGr.query();
  	}
  	return this.riskConditionGr;
  },

  _initChangeRiskDetails: RiskCalculatorSNC._initChangeRiskDetails,
  _setChangeRiskDetails: RiskCalculatorSNC._setChangeRiskDetails,
  updateChangeRiskDetails: RiskCalculatorSNC.updateChangeRiskDetails,

  type: "RiskCalculatorSNC"
};

Sys ID

f2d53036d7333200532c24837e610357

Offical Documentation

Official Docs: