Name

sn_risk_advanced.RiskAssessmentScoringUtilsBase

Description

Utility function for risk assessment scoring(Not for customizations)

Script

var RiskAssessmentScoringUtilsBase = Class.create();
RiskAssessmentScoringUtilsBase.prototype = {
  initialize: function() {},

  autoCalculateResidualAssessment: function(asmt) {
      return this._autoCalculateResidualAssessment(asmt);
  },

  copyScoreToAssessmentSummaryFieldsForControl: function(asmt) {
      this._copyScoreToAssessmentSummaryFieldsForControl(asmt);
  },

  copyScoreToAssessmentSummaryFieldsForInherent: function(asmt) {
      this._copyScoreToAssessmentSummaryFieldsForInherent(asmt);
  },

  copyScoreToAssessmentSummaryFieldsForResidual: function(asmt) {
      this._copyScoreToAssessmentSummaryFieldsForResidual(asmt);
  },

  copyScoreToAssessmentSummaryFieldsForTarget: function(asmt) {
      this._copyScoreToAssessmentSummaryFieldsForTarget(asmt);
  },

  getRatingScore: function(asmtType, score) {
      return this._getRatingScore(asmtType, score);
  },

  shouldConsiderControlWeightage: function(ramId) {
      return this._shouldConsiderControlWeightage(ramId);
  },

  // The assessment instance scores and ales shall be calculated using logic/script only if there are no corresponding root responses which are mandatory and the required fields are not filled
  _checkIfAsmtInstanceScoreNeedsToBeCalculated: function(asmt_instance_id, asmt_type, asmtTypeContri) {
      var count = 0;
      var rootResponses = new GlideAggregate('sn_risk_advanced_risk_assessment_instance_response');
      rootResponses.addAggregate('COUNT');
      rootResponses.addNullQuery('parent_instance_response');
      rootResponses.addQuery('assessment_instance_id', asmt_instance_id);
      rootResponses.addQuery('assessment_type', asmt_type);
      rootResponses.addQuery('factor.mandatory_response', true);
      if (asmtTypeContri == '1' || asmtTypeContri == '2') {
          if (asmtTypeContri == '1') {
              // If assessment type contribution is Quantitative, if any mandatory root response(Quantitative/Both) has quantitative response empty?
              rootResponses.addEncodedQuery('quantitative_responseISEMPTY^factor.factor_contributionIN1,3');
          } else {
              // If assessment type contribution is Qualitative, if any mandatory root response(Qualitative/Both) has qualitative response empty?
              rootResponses.addEncodedQuery('qualitative_responseISEMPTY^factor.factor_contributionIN2,3');
          }

          rootResponses.setGroup(false);
          rootResponses.query();

          if (rootResponses.next()) {
              count = parseInt(rootResponses.getAggregate('COUNT'));
          }

      } else {
          // If assessment type contribution is BOTH, if any mandatory root response(BOTH/Qual/Quant) has qualitative or quantitative response empty?

          rootResponses.addEncodedQuery('qualitative_responseISEMPTY^factor.factor_contributionIN2,3');
          rootResponses.setGroup(false);
          rootResponses.query();
          if (rootResponses.next()) {
              count = parseInt(rootResponses.getAggregate('COUNT'));
          }

          if (count == 0) {
              var responses = new GlideAggregate('sn_risk_advanced_risk_assessment_instance_response');
              responses.addAggregate('COUNT');
              responses.addNullQuery('parent_instance_response');
              responses.addQuery('assessment_instance_id', asmt_instance_id);
              responses.addQuery('assessment_type', asmt_type);
              responses.addQuery('factor.mandatory_response', true);
              responses.addEncodedQuery('quantitative_responseISEMPTY^factor.factor_contributionIN1,3');
              responses.setGroup(false);
              responses.query();
              if (responses.next()) {
                  count = parseInt(responses.getAggregate('COUNT'));
              }
          }
      }
      return (count == 0);
  },

  // To get scoring logic, script and contribution for asmt type corresponding to the root response
  _getAssessmentTypeContribution: function(risk_asmt_meth_id, asmtTypeClass, asmtTypeObject) {
      var asmtTypeRec = new GlideRecord('sn_risk_advanced_assessment_type');
      asmtTypeRec.addQuery('risk_assessment_methodology', risk_asmt_meth_id);
      asmtTypeRec.addQuery('sys_class_name', asmtTypeClass);
      asmtTypeRec.query();
      if (asmtTypeRec.next()) {
          asmtTypeObject.contribution = asmtTypeRec.assessment_contribution + '';
          asmtTypeObject.quantitative_scoring_logic = asmtTypeRec.quantitative_scoring_logic + '';
          asmtTypeObject.qualitative_scoring_logic = asmtTypeRec.qualitative_scoring_logic + '';
          asmtTypeObject.id = asmtTypeRec.getUniqueValue();
      }
  },

  // To calculate and set Assessment instance scores when Root responses are set
  calculateAssessmentInstanceScores: function(asmtResponse) {
      return this._calculateAssessmentInstanceScores(asmtResponse);
  },


  _calculateAssessmentInstanceScores: function(asmtResponse) {
      var asmt_instance_id = asmtResponse.assessment_instance_id + '';
      var risk_asmt_meth_id = asmtResponse.assessment_instance_id.risk_assessment_methodology + '';
      var asmt_type = asmtResponse.assessment_type + '';
      var assessmentTypeClassName;
      var scoreFieldToSet;
      var ratingFieldToSet;
      var aleFieldToSet;
      // This object will contain the logic, scripts and contribution for asmt type
      var asmtTypeObject = {};
      // IF asmt type for response is inherent, calculate inherent scores
      if (asmtResponse.assessment_type == '1') {
          assessmentTypeClassName = 'sn_risk_advanced_inherent_assessment';
          aleFieldToSet = 'inherent_computed_ale';
          scoreFieldToSet = 'inherent_computed_score';
          ratingFieldToSet = 'inherent_computed_risk';
      } else if (asmtResponse.assessment_type == '2') {
          // If asmt type for response is control, calculate control scores
          assessmentTypeClassName = 'sn_risk_advanced_control_assessment';
          ratingFieldToSet = 'control_computed_effectiveness';
          scoreFieldToSet = 'control_computed_score';
      } else if (asmtResponse.assessment_type == '3') {
          // If asmt type for response is residual, calculate residual scores
          assessmentTypeClassName = 'sn_risk_advanced_residual_assessment';
          aleFieldToSet = 'residual_computed_ale';
          ratingFieldToSet = 'residual_computed_risk';
          scoreFieldToSet = 'residual_computed_score';
      } else if (asmtResponse.assessment_type == '4') {
          // If asmt type for response is target, calculate residual scores
          assessmentTypeClassName = 'sn_risk_advanced_target_assessment';
          aleFieldToSet = 'target_computed_ale';
          ratingFieldToSet = 'target_computed_risk';
          scoreFieldToSet = 'target_computed_score';
      }


      // Get Scoring logics, scripts and assessment contribution for Assessment type
      this._getAssessmentTypeContribution(risk_asmt_meth_id, assessmentTypeClassName, asmtTypeObject);

      var asmtInstance = new GlideRecord('sn_risk_advanced_risk_assessment_instance');
      asmtInstance.get(asmt_instance_id);

      if (this._checkIfAsmtInstanceScoreNeedsToBeCalculated(asmt_instance_id, asmt_type, asmtTypeObject.contribution)) {

          if (asmtTypeObject.contribution == '3' || asmtTypeObject.contribution == '2') {
              // If asmt type has contri of BOTH/Qual, get score
              var qualitative_scoring_formula = asmtTypeObject.qualitative_scoring_logic;
              var qualitative_score = this._getAsmtTypeScore(qualitative_scoring_formula, 'qualitative_response', asmt_instance_id, asmt_type, '2', asmtTypeObject.id);

              // Get rating from score
              var rating = this._getRatingScore(asmtTypeObject.id, qualitative_score);
              // Set corresponding score field in instance
              if (asmtInstance.getValue(ratingFieldToSet) === null && rating != '') {
                  asmtInstance.setValue(ratingFieldToSet, rating);
              } else if (asmtInstance.getValue(ratingFieldToSet) !== null && rating == '') {
                  asmtInstance.setValue(ratingFieldToSet, rating);
              } else if (asmtInstance.getValue(ratingFieldToSet) != rating) {
                  asmtInstance.setValue(ratingFieldToSet, rating);
              }


              if (asmtInstance.getValue(scoreFieldToSet) === null && qualitative_score != '') {
                  asmtInstance.setValue(scoreFieldToSet, qualitative_score);
              } else if (asmtInstance.getValue(scoreFieldToSet) !== null && qualitative_score == '') {
                  asmtInstance.setValue(scoreFieldToSet, qualitative_score);
              } else if (asmtInstance.getValue(scoreFieldToSet) != qualitative_score) {
                  asmtInstance.setValue(scoreFieldToSet, qualitative_score);
              }
          }

          if ((asmtTypeObject.contribution == '3' || asmtTypeObject.contribution == '1') && asmtResponse.assessment_type != '2') {
              // If asmt type has contri of BOTH/Quant, get ale
              var quantitative_scoring_formula = asmtTypeObject.quantitative_scoring_logic;
              var quant_score = this._getAsmtTypeScore(quantitative_scoring_formula, 'quantitative_response', asmt_instance_id, asmt_type, '1', asmtTypeObject.id);
              var refCurrCode = asmtInstance.inherent_computed_ale.getReferenceCurrencyCode();
              // Set corresponding ale field in insatnce
              if (quant_score == '' && asmtInstance.getValue(aleFieldToSet) != '0') {
                  asmtInstance.setValue(aleFieldToSet, '');
              } else if (asmtInstance.getValue(aleFieldToSet) != quant_score) {
                  asmtInstance.setValue(aleFieldToSet, refCurrCode + ";" + quant_score);
              }

          }
      } else if (asmtTypeObject.contribution == '3') {
          // If assessment type contribution is BOTH, set score and ale to ''
          if (asmtInstance.getValue(ratingFieldToSet) !== null) {
              asmtInstance.setValue(ratingFieldToSet, '');
          }
          if (asmtResponse.assessment_type != '2' && (asmtInstance.getValue(aleFieldToSet) != '0')) {
              asmtInstance.setValue(aleFieldToSet, '');
          }
          if (asmtInstance.getValue(scoreFieldToSet) !== null) {
              asmtInstance.setValue(scoreFieldToSet, '');
          }
      } else if (asmtTypeObject.contribution == '2') {
          // If assessment type contribution is Qualitative, set score to ''
          if (asmtInstance.getValue(ratingFieldToSet) !== null) {
              asmtInstance.setValue(ratingFieldToSet, '');
          }
          if (asmtInstance.getValue(scoreFieldToSet) !== null) {
              asmtInstance.setValue(scoreFieldToSet, '');
          }
      } else {
          // If assessment type contribution is Quantitative, set ale to ''
          if (asmtInstance.getValue(aleFieldToSet) != '0') {
              asmtInstance.setValue(aleFieldToSet, '');
          }
      }
      asmtInstance.update();
  },

  // To get rating given a score value
  _getRatingScore: function(asmtType, score) {
      if (score === '') {
          return '';
      }
      var rating = '';
      var ratings = new GlideRecord('sn_risk_advanced_rating_criteria');
      ratings.addQuery('assessment_type', asmtType);
      ratings.orderByDesc('lower_interval');
      ratings.addQuery('lower_interval', '<=', score);
      ratings.setLimit(1);
      ratings.query();
      if (ratings.next()) {
          rating = ratings.getUniqueValue();
      } else {
          // If the score is less than least lower interval, return the least one
          ratings = new GlideRecord('sn_risk_advanced_rating_criteria');
          ratings.addQuery('assessment_type', asmtType);
          ratings.orderBy('lower_interval');
          ratings.setLimit(1);
          ratings.query();
          if (ratings.next()) {
              rating = ratings.getUniqueValue();
          }
      }
      return rating;
  },

  // To get scores/ale from root responses given a scoring logic
  _getAsmtTypeScore: function(scoring_logic, qualOrQuantField, asmt_instance_id, asmt_type, qualOrQuant, asmt_type_id) {
      var score;
      switch (scoring_logic) {
          case 'SUM':
          case 'MAX':
          case 'MIN':
          case 'AVG':
              score = this._getAggregateofRootResponseScores(scoring_logic, qualOrQuantField, asmt_instance_id, asmt_type);
              break;
          case 'WEIGHTED-AVG':
              var sum = this._getAggregateofRootResponseScores('SUM', qualOrQuantField, asmt_instance_id, asmt_type);
              if (sum == '') {
                  score = '';
              } else {
                  score = this._getWeightedAverageofRootResponseScores(sum, asmt_instance_id, qualOrQuant, asmt_type, qualOrQuantField);
              }
              break;
          case 'PRODUCT':
              score = this._getProductOfRootResponseScores(asmt_instance_id, asmt_type, qualOrQuantField);
              break;
          case 'SCRIPT':
              score = this._executeAndGetScoresForAsmtInstance(asmt_instance_id, qualOrQuant, asmt_type, asmt_type_id);
      }
      return score;
  },


  // To execute and calculate the score from script 
  _executeAndGetScoresForAsmtInstance: function(asmt_instance_id, qualOrQuant, asmt_type, asmt_type_id) {
      var score = '';
      var response;
      var evaluator = new GlideScopedEvaluator();
      // Put values from all root response into the script
      var rootResponses = new GlideRecord('sn_risk_advanced_risk_assessment_instance_response');
      rootResponses.addQuery('assessment_instance_id', asmt_instance_id);
      rootResponses.addQuery('assessment_type', asmt_type);
      rootResponses.addEncodedQuery('factor.factor_contributionIN3,' + qualOrQuant);
      rootResponses.addNullQuery('parent_instance_response');
      rootResponses.query();
      while (rootResponses.next()) {
          if (qualOrQuant == '1') {
              if (rootResponses.getValue('quantitative_response') === null) {
                  evaluator.putVariable(rootResponses.factor.number + '', 0);
              } else {
                  evaluator.putVariable(rootResponses.factor.number + '', parseFloat(rootResponses.quantitative_response + ''));
              }
          } else {
              if (rootResponses.getValue('qualitative_response') === null) {
                  evaluator.putVariable(rootResponses.factor.number + '', 0);
              } else {
                  evaluator.putVariable(rootResponses.factor.number + '', parseFloat(rootResponses.qualitative_response + ''));
              }
          }
      }

      evaluator.putVariable('score', null);
      evaluator.putVariable('result', {});
      evaluator.putVariable('asmtId', asmt_instance_id);

      var assessmentType = new GlideRecord('sn_risk_advanced_assessment_type');
      assessmentType.get(asmt_type_id);

      if (qualOrQuant == '2') {
          evaluator.evaluateScript(assessmentType, 'qualitative_script', null);
      } else {
          evaluator.evaluateScript(assessmentType, 'quantitative_script', null);
      }
      var result = evaluator.getVariable('result');

      if (result.error) {
          gs.info(gs.getMessage("Divide by zero error occured in assessment instance for ID: {0} and assessment type ID: {1}", [asmt_instance_id, asmt_type_id]));
      } else {
          // If there is no error, then get the score
          score = evaluator.getVariable('score');
      }
      return score;
  },



  // To calculate the product of root responses for a given asmt instance and type
  _getProductOfRootResponseScores: function(asmt_instance_id, asmt_type, qualOrQuantField) {
      var product = 1;
      // Get number of eligible child responses
      var rootResponses = this._getEligibleRootResponsesForCalculation(asmt_instance_id, asmt_type, qualOrQuantField);
      if (!rootResponses.hasNext()) {
          return '';
      }
      while (rootResponses.next()) {
          product *= rootResponses.getValue(qualOrQuantField);
      }
      return product + '';
  },

  // To calculate weighted average of root responses
  _getWeightedAverageofRootResponseScores: function(sum, asmt_instance_id, qualOrQuant, asmt_type, qualOrQuantField) {
      if (sum == 0) {
          return '0';
      }
      var sumOfWeights = 0;
      // Get iterator of eligible root responses
      var rootResponses = this._getEligibleRootResponsesForCalculation(asmt_instance_id, asmt_type, qualOrQuantField);
      if (!rootResponses.hasNext()) {
          return '';
      }
      while (rootResponses.next()) {
          if (qualOrQuant == '2') {
              // If response is qualitative, use qual weighting factor
              if (rootResponses.isValidField('control') && rootResponses.control != '') {
                  // If control asessment and type is individual control, use control weighting
                  var controlWeight = rootResponses.control.weighting;
                  if (gs.nil(controlWeight))
                      controlWeight = 100;
                  sumOfWeights += controlWeight / 100;
              } else
                  sumOfWeights += rootResponses.factor.weighting_factor / 100;
          } else {
              // If response is quant, use quant weighting factor
              sumOfWeights += rootResponses.factor.quantitative_weighting_factor / 100;
          }
      }
      if (sumOfWeights == 0) {
          return '';
      }
      return (sum / sumOfWeights) + '';
  },

  // To get GlideRecord iterator to do aggregation of root responses for weighted avg and product
  _getEligibleRootResponsesForCalculation: function(asmt_instance_id, asmt_type, qualOrQuantField) {
      var rootResponses = new GlideRecord('sn_risk_advanced_risk_assessment_instance_response');
      rootResponses.addQuery('assessment_instance_id', asmt_instance_id);
      rootResponses.addQuery('assessment_type', asmt_type);
      rootResponses.addNotNullQuery(qualOrQuantField);
      rootResponses.addNullQuery('parent_instance_response');
      rootResponses.query();
      return rootResponses;
  },

  // To get aggregated score/ale from root responses under asmt instance
  _getAggregateofRootResponseScores: function(scoring_logic, qualOrQuantField, asmt_instance_id, asmt_type) {
      var rootResponses = new GlideAggregate('sn_risk_advanced_risk_assessment_instance_response');
      rootResponses.addAggregate(scoring_logic, qualOrQuantField);
      rootResponses.addQuery('assessment_instance_id', asmt_instance_id);
      rootResponses.addQuery('assessment_type', asmt_type);
      rootResponses.addNullQuery('parent_instance_response');
      rootResponses.setGroup(false);
      rootResponses.query();
      var aggregateVar;
      if (rootResponses.next()) {
          aggregateVar = rootResponses.getAggregate(scoring_logic, qualOrQuantField);
      }
      if (isNaN(aggregateVar)) {
          aggregateVar = '';
      }
      return aggregateVar;
  },


  // To calculate and set parent response scores
  calculateParentResponseScores: function(asmtResponse) {
      return this._calculateParentResponseScores(asmtResponse);
  },


  _calculateParentResponseScores: function(asmtResponse) {
      // Get response factor contribution
      var responseContribution = asmtResponse.factor.factor_contribution + '';
      // Get parent response ID and parent response instance
      var parent_response_id = asmtResponse.parent_instance_response;
      var parentResponse = new GlideRecord('sn_risk_advanced_risk_assessment_instance_response');
      parentResponse.get(parent_response_id);

      // Get parent response factor contribution
      var parentResponseFactorContribution = parentResponse.factor.factor_contribution + '';

      // Check if parent response has to be calculated using logic/script
      if (this._parentResponseScoreToBeCalculated(parent_response_id, parentResponseFactorContribution)) {
          var parentResponseFactorId = parentResponse.factor + '';

          // Parent response can only have factor as group factor
          var groupFactor = new GlideRecord('sn_risk_advanced_group_factor');
          groupFactor.get(parentResponseFactorId);

          // If factor contri is Quantitative or Both, calculate quantitative score
          if (parentResponseFactorContribution == '1' || parentResponseFactorContribution == '3') {

              var quantitative_formula = groupFactor.quantitative_formula + '';
              var quantitative_score = this._getParentResponseScore(parent_response_id, quantitative_formula, 'quantitative_response', '1', groupFactor);
              if (quantitative_score != '') {
                  quantitative_score = (quantitative_score * groupFactor.quantitative_weighting_factor) / 100;
              }
              if (quantitative_score == '' && !parentResponse.quantitative_response.nil() || quantitative_score != '' && parentResponse.quantitative_response.nil() || quantitative_score != parentResponse.getValue('quantitative_response')) {
                  parentResponse.quantitative_response = quantitative_score;
              }

          }

          // If factor contribution is Qualitative or Both, calculate qualitative score
          if (parentResponseFactorContribution == '2' || parentResponseFactorContribution == '3') {
              var qualitative_formula = groupFactor.qualitative_formula + '';
              var qualitative_score = this._getParentResponseScore(parent_response_id, qualitative_formula, 'qualitative_response', '2', groupFactor);
              // Transform the parent response
              qualitative_score = this._getTransformedScore(parentResponse, qualitative_score);
              if (qualitative_score != '') {
                  if (parentResponse.getValue('assessment_type') == '2' && this._shouldConsiderControlWeightage(parentResponse.assessment_instance_id.risk_assessment_methodology + '')) {
                      //If it is individual control assessment consider control weightage instead of group factor weightage
                      var controlWeightage = parentResponse.control.weighting;
                      if (gs.nil(controlWeightage))
                          controlWeightage = 100;
                      qualitative_score = qualitative_score * (controlWeightage) / 100;
                  } else
                      qualitative_score = (qualitative_score * groupFactor.weighting_factor) / 100;
              }
              if (qualitative_score == '' && !parentResponse.qualitative_response.nil() || qualitative_score != '' && parentResponse.qualitative_response.nil() || qualitative_score != parentResponse.getValue('qualitative_response')) {
                  parentResponse.qualitative_response = qualitative_score;
              }

          }
      } else if (parentResponseFactorContribution == '3') {
          // If mandatory child responses are unanswered, make parent response scores ''. This is being done so that if any mandatory child has become '', existing parent response scores become invalid     

          if (!parentResponse.qualitative_response.nil()) {
              parentResponse.qualitative_response = '';
          }
          if (!parentResponse.quantitative_response.nil()) {
              parentResponse.quantitative_response = '';
          }
      } else if (parentResponseFactorContribution == '2') {
          // If parent FC is qualitative, 
          if (!parentResponse.qualitative_response.nil()) {
              parentResponse.qualitative_response = '';
          }
      } else {
          if (!parentResponse.quantitative_response.nil()) {
              parentResponse.quantitative_response = '';
          }
      }
      // This update will happen only if there is a change in GlideElement. So if the fields were empty before as well, no update will happen.
      if (parentResponse.qualitative_response.changes() || parentResponse.quantitative_response.changes()) {
          parentResponse.update();
      }
  },

  _shouldConsiderControlWeightage: function(ramId) {
      var asmtType = new GlideRecord('sn_risk_advanced_control_assessment');
      asmtType.addQuery('risk_assessment_methodology', ramId);
      asmtType.query();
      return asmtType.next() && asmtType.getValue('control_assessment_methodology') == 'individual_control_assessment' && asmtType.getValue('qualitative_scoring_logic') == 'WEIGHTED-AVG';
  },

  // To get aggregated scores for child responses
  _getParentResponseScore: function(parentResponseId, formula, qualOrQuantField, qualOrQuant, groupFactor) {
      var score;
      switch (formula) {
          case 'SUM':
          case 'MAX':
          case 'MIN':
          case 'AVG':
              score = this._getAggregateofChildScores(parentResponseId, qualOrQuantField, formula);
              break;
          case 'WEIGHTED-AVG':
              var sum = this._getAggregateofChildScores(parentResponseId, qualOrQuantField, 'SUM');
              if (sum == '') {
                  score = '';
              } else {
                  score = this._getWeightedAverageofChildScores(sum, parentResponseId, qualOrQuant);
              }
              break;
          case 'PRODUCT':
              score = this._getProductOfchildScores(parentResponseId, qualOrQuant, qualOrQuantField);
              break;
          case 'SCRIPT':
              score = this._executeAndGetScores(parentResponseId, qualOrQuant, groupFactor);
      }
      return score;
  },


  // To execute and calculate the score from script 
  _executeAndGetScores: function(parentResponseId, qualOrQuant, groupFactor) {
      var score = '';
      var response;
      var evaluator = new GlideScopedEvaluator();
      // Put values from all child response into the script
      var childResponses = new GlideRecord('sn_risk_advanced_risk_assessment_instance_response');
      childResponses.addQuery('parent_instance_response', parentResponseId);
      childResponses.addEncodedQuery('factor.factor_contributionIN3,' + qualOrQuant);
      childResponses.query();
      while (childResponses.next()) {
          if (qualOrQuant == '1') {
              if (childResponses.getValue('quantitative_response') === null) {
                  evaluator.putVariable(childResponses.factor.number + '', 0);
              } else {
                  evaluator.putVariable(childResponses.factor.number + '', parseFloat(childResponses.quantitative_response + ''));
              }
          } else {
              if (childResponses.getValue('qualitative_response') === null) {
                  evaluator.putVariable(childResponses.factor.number + '', 0);
              } else {
                  evaluator.putVariable(childResponses.factor.number + '', parseFloat(childResponses.qualitative_response + ''));
              }
          }
      }
      evaluator.putVariable('score', null);
      evaluator.putVariable('result', {});
      if (qualOrQuant == '2') {
          evaluator.evaluateScript(groupFactor, 'qualitative_script', null);
      } else {
          evaluator.evaluateScript(groupFactor, 'quantitative_script', null);
      }
      var result = evaluator.getVariable('result');
      if (result.error) {
          gs.info(gs.getMessage("Divide by zero error occured in assessment instance response for ID: {0}", parentResponseId));
      } else {
          // If there is no error, then get the score
          score = evaluator.getVariable('score');
      }
      return score;
  },


  // To get GlideRecord object for all child responses which should be used to calculate the parent score
  _getEligibleChildResponsesForCalculation: function(parentResponseId, qualOrQuant) {
      var childResponses = new GlideRecord('sn_risk_advanced_risk_assessment_instance_response');
      childResponses.addQuery('parent_instance_response', parentResponseId);
      childResponses.addEncodedQuery('factor.factor_contributionIN3,' + qualOrQuant);
      if (qualOrQuant == '2') {
          childResponses.addNotNullQuery('qualitative_response');
      } else {
          childResponses.addNotNullQuery('quantitative_response');
      }
      childResponses.query();
      return childResponses;
  },

  // To get product of child responses
  _getProductOfchildScores: function(parentResponseId, qualOrQuant, qualOrQuantField) {
      var product = 1;
      // Get eligible child responses iterator
      var childResponses = this._getEligibleChildResponsesForCalculation(parentResponseId, qualOrQuant);
      if (!childResponses.hasNext()) {
          return '';
      }
      while (childResponses.next()) {
          product *= childResponses.getValue(qualOrQuantField);
      }
      return product + '';
  },

  // To get weighted average of child responses
  _getWeightedAverageofChildScores: function(sum, parentResponseId, qualOrQuant) {
      if (sum == 0) {
          return '0';
      }
      var sumOfWeights = 0;
      // Get eligible child responses iterator
      var childResponses = this._getEligibleChildResponsesForCalculation(parentResponseId, qualOrQuant);
      if (!childResponses.hasNext()) {
          return '';
      }
      while (childResponses.next()) {
          if (qualOrQuant == '2') {
              sumOfWeights += childResponses.factor.weighting_factor / 100;
          } else {
              sumOfWeights += childResponses.factor.quantitative_weighting_factor / 100;
          }
      }
      // Add exception message if sum of weights is 0
      if (sumOfWeights == 0) {
          return '';
      }
      return (sum / sumOfWeights) + '';
  },


  // To get Aggregate of child scores for different aggrgeration formula
  _getAggregateofChildScores: function(parentResponseId, qualOrQuantField, aggregate) {
      var childResponses = new GlideAggregate('sn_risk_advanced_risk_assessment_instance_response');
      childResponses.addAggregate(aggregate, qualOrQuantField);
      childResponses.addQuery('parent_instance_response', parentResponseId);
      childResponses.setGroup(false);
      childResponses.query();
      var aggregateVar;
      if (childResponses.next()) {
          aggregateVar = childResponses.getAggregate(aggregate, qualOrQuantField);
      }
      if (isNaN(aggregateVar)) {
          aggregateVar = '';
      }
      return aggregateVar;
  },

  // Is it required to calculate the parent response scores
  _parentResponseScoreToBeCalculated: function(parentResponseId, parentResponseFactorContribution) {
      // If count of mandatory child responses with no response > 0, Do not calculate
      var responses = new GlideAggregate('sn_risk_advanced_risk_assessment_instance_response');
      responses.addAggregate('COUNT');
      responses.addQuery('parent_instance_response', parentResponseId);
      responses.addQuery('factor.mandatory_response', true);
      // If Parent Fac is BOTH, all mandatory child should be filled
      if (parentResponseFactorContribution == '3') {
          responses.addEncodedQuery('factor.factor_contributionIN1,2,3');
      } else {
          // If PF is Qual, all mandatory Qual and Both should be filled
          // If PF is Quant, all mandatory Quant and both should be filled 
          responses.addEncodedQuery('factor.factor_contributionIN3,' + parentResponseFactorContribution);
      }
      responses.addNullQuery('factor_response');
      responses.setGroup(false);
      responses.query();
      var count = 0;
      if (responses.next()) {
          count = parseInt(responses.getAggregate('COUNT'));
      }
      return (count == 0);
  },

  // To update the responses in database
  commitResponseToDB: function(responseId, responseObject) {
      return this._commitResponseToDB(responseId, responseObject);
  },


  _commitResponseToDB: function(responseId, responseObject) {
      var asmtResponse = new GlideRecord('sn_risk_advanced_risk_assessment_instance_response');
      asmtResponse.get(responseId);
      if (responseObject.hasOwnProperty('additional_comments')) {
          asmtResponse.additional_comments = responseObject.additional_comments;
      }
      if (responseObject.hasOwnProperty('factor_response')) {
          asmtResponse.factor_response = responseObject.factor_response;
      }
      return asmtResponse.update();
  },


  // To calculate qualitative response score
  calculateQualitativeResponse: function(asmtResponse) {
      return this._calculateQualitativeResponse(asmtResponse);
  },

  _calculateQualitativeResponse: function(asmtResponse) {
      var response = asmtResponse.factor_response + '';
      // If response is '', set qualitative response to ''
      if (response == '') {
          asmtResponse.qualitative_response = '';
      } else {
          var score = this._getQuantOrQualScore(asmtResponse);
          score = this._getTransformedScore(asmtResponse, score);
          // Get score for percentage type user response if no transformation happened
          if (!asmtResponse.factor.transform_score && asmtResponse.factor.sys_class_name != "sn_risk_advanced_group_factor" &&
              asmtResponse.factor.user_response == '5') {
              score = score / 100;
          }
          // Get weighted score
          if (asmtResponse.assessment_type + '' == '2' && this._shouldConsiderControlWeightage(asmtResponse.assessment_instance_id.risk_assessment_methodology + '')) {
              // If assessment is individual control assessment and is not based on a group factor then consider control weightage else consider factor weightage
              if (gs.nil(asmtResponse.getValue('parent_instance_response'))) {
                  var controlWeightage = asmtResponse.control.weighting;
                  if (gs.nil(controlWeightage))
                      controlWeightage = 100;
                  asmtResponse.qualitative_response = score * (controlWeightage) / 100;
                  return;
              }
          }
          asmtResponse.qualitative_response = score * asmtResponse.factor.weighting_factor / 100;
      }
  },

  // Transform the score based on transformation criteria
  _getTransformedScore: function(asmtResponse, score) {
      if (gs.nil(score)) {
          return '';
      }
      var transformedScore = score;
      // If transform score flag is checked, then do transformation
      if (asmtResponse.factor.transform_score) {
          var criteria = new GlideRecord('sn_risk_advanced_transformation_criteria');
          criteria.addQuery('factor', asmtResponse.factor);
          criteria.orderByDesc('lower_interval');
          criteria.addQuery('lower_interval', '<=', score);
          criteria.setLimit(1);
          criteria.query();
          if (criteria.next()) {
              transformedScore = criteria.score + '';
              asmtResponse.transformation_criteria = criteria.getUniqueValue();
          } else {
              // If the score is less than least lower interval, return the least one
              criteria = new GlideRecord('sn_risk_advanced_transformation_criteria');
              criteria.addQuery('factor', asmtResponse.factor);
              criteria.orderBy('lower_interval');
              criteria.setLimit(1);
              criteria.query();
              if (criteria.next()) {
                  transformedScore = criteria.score + '';
                  asmtResponse.transformation_criteria = criteria.getUniqueValue();
              }
          }
      }
      return transformedScore;
  },

  // To calculate qualitative response score
  calculateQuantitativeResponse: function(asmtResponse) {
      return this._calculateQuantitativeResponse(asmtResponse);
  },

  _calculateQuantitativeResponse: function(asmtResponse) {
      var response = asmtResponse.factor_response + '';
      // If response if empty, set quant response to ''
      if (response == '') {
          asmtResponse.quantitative_response = '';
      } else {
          var score = this._getQuantOrQualScore(asmtResponse);
          // Get score for percentage type user response
          if (asmtResponse.factor.sys_class_name != 'sn_risk_advanced_group_factor' && asmtResponse.factor.user_response == '5') {
              score = score / 100;
          }
          // Get weighted score
          asmtResponse.quantitative_response = score * asmtResponse.factor.quantitative_weighting_factor / 100;
      }

  },


  // To get Quantitative score for asmt response
  _getQuantOrQualScore: function(asmtResponse) {
      var score;
      var UserResponseType = null;
      var response = asmtResponse.factor_response + '';
      if (asmtResponse.factor.sys_class_name == 'sn_risk_advanced_manual_factor' || asmtResponse.factor.sys_class_name == 'sn_risk_advanced_automated_scripted_factor' || asmtResponse.factor.sys_class_name == 'sn_risk_advanced_automated_query_factor') {
          // If Manual factor, then assign response type
          UserResponseType = asmtResponse.factor.user_response + '';
      }
      if (UserResponseType == '4') {
          // If currency then get reference currency value
          score = global.getCurrencyFilter('', '', response) + '';
      } else {
          score = response;
      }
      return score;
  },

  _copyScoreToAssessmentSummaryFieldsForInherent: function(asmt) {
      if (asmt.final_inherent_score.changes()) {
          var gr = new GlideRecord("sn_risk_advanced_inherent_assessment");
          gr.addQuery("risk_assessment_methodology", asmt.risk_assessment_methodology);
          gr.query();
          if (gr.next()) {
              asmt.summary_inherent_risk = this._getRatingScore(gr.getUniqueValue(), asmt.final_inherent_score + '');
          }
      }
  },

  _copyScoreToAssessmentSummaryFieldsForControl: function(asmt) {
      if (asmt.final_control_score.changes()) {
          var gr1 = new GlideRecord("sn_risk_advanced_control_assessment");
          gr1.addQuery("risk_assessment_methodology", asmt.risk_assessment_methodology);
          gr1.query();
          if (gr1.next()) {
              asmt.summary_control_effectiveness = this._getRatingScore(gr1.getUniqueValue(), asmt.final_control_score + '');
          }
      }
  },

  _copyScoreToAssessmentSummaryFieldsForResidual: function(asmt) {
      if (asmt.final_residual_score.changes()) {
          var gr2 = new GlideRecord("sn_risk_advanced_residual_assessment");
          gr2.addQuery("risk_assessment_methodology", asmt.risk_assessment_methodology);
          gr2.query();
          if (gr2.next()) {
              asmt.summary_residual_risk = this._getRatingScore(gr2.getUniqueValue(), asmt.final_residual_score + '');
          }
      }
  },

  _copyScoreToAssessmentSummaryFieldsForTarget: function(asmt) {
      if (asmt.final_target_score.changes()) {
          var gr = new GlideRecord("sn_risk_advanced_target_assessment");
          gr.addQuery("risk_assessment_methodology", asmt.risk_assessment_methodology);
          gr.query();
          if (gr.next()) {
              asmt.summary_target_risk = this._getRatingScore(gr.getUniqueValue(), asmt.final_target_score + '');
          }
      }
  },

  // To calculate Quantitative score for residual asmt
  _autoCalculateResidualAssessment: function(asmt) {
      var gr = new GlideRecord("sn_risk_advanced_residual_assessment");
      gr.addQuery("risk_assessment_methodology", current.risk_assessment_methodology);
      gr.query();

      if (gr.next()) {
          if (gr.getValue('calculate_based_on') == 'inherent_control') {
              if (asmt.final_inherent_score.nil()) {
                  asmt.residual_computed_score = "";
                  asmt.residual_computed_risk = "";
              } else if (asmt.final_control_score.nil()) {
                  asmt.residual_computed_score = asmt.final_inherent_score;
                  asmt.residual_computed_risk = this.getRatingScore(gr.getUniqueValue(), asmt.residual_computed_score + '');
              } else {
                  if (gr.getValue('qualitative_scoring_type') == "inherent_control_difference") {
                      asmt.residual_computed_score = (parseFloat(asmt.getValue("final_inherent_score")) - parseFloat(asmt.getValue("final_control_score")));
                  } else if (gr.getValue('qualitative_scoring_type') == "inherent_control_division") {
                      if (parseFloat(asmt.getValue("final_control_score")) != 0) {
                          asmt.residual_computed_score = (parseFloat(asmt.getValue("final_inherent_score")) / parseFloat(asmt.getValue("final_control_score")));
                      }
                  } else if (gr.getValue('qualitative_scoring_type') == "lookup_matrix") {
                      var matrix = new GlideRecord("sn_risk_advanced_residual_assessment_matrix");
                      matrix.addQuery("inherent_risk", asmt.summary_inherent_risk);
                      matrix.addQuery("control_effectiveness", asmt.summary_control_effectiveness);
                      matrix.addQuery("sn_risk_advanced_assessment_type", gr.getUniqueValue());
                      matrix.query();

                      if (matrix.hasNext()) {
                          matrix.next();
                          asmt.residual_computed_score = matrix.score + '';
                      }
                  } else if (gr.getValue('qualitative_scoring_type') == "script") {
                      var evaluator = new GlideScopedEvaluator();
                      evaluator.putVariable('score', null);
                      evaluator.putVariable('result', {});
                      evaluator.putVariable('asmtId', asmt.getUniqueValue());
                      evaluator.putVariable('inherent_score', parseFloat(asmt.getValue("final_inherent_score")));
                      evaluator.putVariable('control_effectiveness', parseFloat(asmt.getValue("final_control_score")));
                      evaluator.evaluateScript(gr, 'qualitative_scoring_logic_script', null);
                      var result = evaluator.getVariable('result');
                      if (result.error) {
                          gs.info(gs.getMessage("Error occured in assessment instance for ID: {0} in the residual scoring script", asmt.getUniqueValue()));
                      } else {
                          // If there is no error, then get the score
                          score = evaluator.getVariable('score');
                          asmt.residual_computed_score = parseFloat(score);
                      }
                  }
                  asmt.residual_computed_risk = this.getRatingScore(gr.getUniqueValue(), asmt.residual_computed_score + '');
              }
              return;
          }
      }
  },

  type: 'RiskAssessmentScoringUtilsBase'
};

Sys ID

2d498bd4536100100b39ddeeff7b124c

Offical Documentation

Official Docs: