…thoughts on ServiceNow and digital transformation

Post

ServiceNow HRSD PII Fields Report


Some environments require that data that is privacy-related or contains PII be catalogued such that we know what is stored, where it is stored and why it is stored. This article outlines functionality built for HRSD which produces a report of privacy related fields for each HR Service.

The end result is an html “report” which provides an overview of each service and the privacy-related data contained within it. For example:

Change of Beneficiaries Request – This service allows employees to change the beneficiaries for their health insurance benefits.

  • Fields on intake form
    • Beneficiary name
    • Beneficiary date of birth
  • Fields on provider form
    • Beneficiary last enrolled date
  • Associated forms
    • 342.44 Beneficiaries Change

UI Actions

The report is run via client UI Actions on the HR Service Configuration form.

Name: Show Privacy Report for this Service

Client: True

Onclick: showPrivacyReport()

Condition: gs.hasRole(‘sn_hr_core.admin’) (usually only HR admins will run this)

Script:

function showPrivacyReport() {

    var glideAjax = new GlideAjax("sn_hr_core.HRSDPrivacyReportUtils");
    glideAjax.addParam('sysparm_name', 'getServiceReportClient');
	glideAjax.addParam('sysparm_servicesysid', g_form.getUniqueValue());

    glideAjax.getXMLAnswer(function(response) {

            var gm = new GlideModal();

            //Sets the dialog title
            gm.setTitle('Privacy Report');
            gm.setWidth(800);

            //Opens the dialog
            gm.renderWithContent(response);
        }
	);
    }

Name: Show Privacy Report All Active Services

Client: True

Onclick: showPrivacyReportAll()

Condition: gs.hasRole(‘sn_hr_core.admin’) (usually only HR admins will run this)

Script:

function showPrivacyReportAll() {

    var glideAjax = new GlideAjax("sn_hr_core.HRSDPrivacyReportUtils");
    glideAjax.addParam('sysparm_name', 'getServiceReportAllActiveServices');

    glideAjax.getXMLAnswer(function(response) {

            var gm = new GlideModal();

            //Sets the dialog title
            gm.setTitle('Privacy Report');
            gm.setWidth(800);

            //Opens the dialog
            gm.renderWithContent(response);
        }
	);
    }

Customer Fields on HR Service Configuration Table

Create a new section in the HR Service Configuration table (sn_hr_core_service) for the new fields

  • Description for Privacy – u_description_privacy – string 4000
  • Exclude this service from the privacy report – u_exclude_from_privacy_report – true/false
  • Intake Form (Record Producer) Fields for Privacy Report -u_record_producer_fields – List
    • Reference table: item_option_new
    • Reference qualifier: javascript: ‘active=true^cat_item=’ + current.producer;
  • Include all Intake Form fields in Privacy Report – u_all_intake_fields_privacy_report – true/false
  • Provider Fields for Privacy Report – u_table_fields_privacy_report – List
    • Reference table: sys_dictionary
    • Reference qualifier: javascript:’active=true^name=task^ORname=sn_hr_core_case^ORname=’ + current.topic_detail.topic_category.coe
  • HR Forms for Privacy Report – u_hr_forms – list
    • Reference table: sn_hr_core_forms (this is a custom table extended from dl_matcher)

Custom Table to Store Forms

If you have a list of PDF forms for HR used in the organization, you can create a table to store them (extended from dl_matcher table). You could also store these in Document Templates or in a table extended from the CMDB.

Table Name: HR Forms sn_hr_core_forms

Extended from: dl_matcher

Fields

  • Name – u_name – string 100
  • Title – u_title – string 100

Script Include

Name: HRSDPrivacyReportUtils

Client callable: True

var HRSDPrivacyReportUtils = Class.create();
HRSDPrivacyReportUtils.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {

    //returns the html of the report for all HR Services
    getServiceReportAllActiveServices: function() {
        var html = '<ul>';
        var serviceGR = new GlideRecord('sn_hr_core_service');
        serviceGR.addActiveQuery();
        serviceGR.addQuery('u_exclude_from_privacy_report', false);
        serviceGR.query();
        while (serviceGR.next()) {
            html += this.getServiceReport(serviceGR);
        }
        html += '</ul>';
        return html;
    },

    //client callable (takes sys_id instead of gliderecord) method to get the html of the privacy report for a single HR service.
    getServiceReportClient: function() {
        var servicesysid = this.getParameter('sysparm_servicesysid');
        var serviceGR = new GlideRecord('sn_hr_core_service');
        serviceGR.get(servicesysid);
        var result = this.getServiceReport(serviceGR);
	
		return result;
    },

    //gets the html privacy report for a single HR Service
    getServiceReport: function(serviceGR) {
        var html = "<li>";

        html += "<strong>" + serviceGR.name.getDisplayValue() + "</strong>";

        var description = serviceGR.u_description_privacy.getDisplayValue();

        if (description) {
            html += " - " + description;
        }
        html += '<ul>';

        //record producer (intake form) fields selected for the privacy report on the HR Service Configuration form.
        if (serviceGR.u_record_producer_fields || serviceGR.u_all_intake_fields_privacy_report) {
            html += "<li> Fields included on intake form: ";
            if (serviceGR.u_all_intake_fields_privacy_report) {
                html += this.getVariables(null, serviceGR.getValue('producer'),null);
            } else {
                html += this.getVariables(serviceGR.getValue('u_record_producer_fields'), null,null);
            }
            html += "</li>";
        }
        // table fields selected for the privacy report on the HR Service Configuration form.
        if (serviceGR.u_table_fields_privacy_report) {
            html += "<li> Fields included in provider view: " + this.getFields(serviceGR.getValue('u_table_fields_privacy_report'));
            html += "</li>";
        }
        //federal hr forms selected for the privacy report on the HR Service Configuration form.
        if (serviceGR.u_federal_hr_forms) {

            var forms = this.getFederalHRForms(serviceGR);
            html += "<li> Forms associated with this service: " + forms;
            html += "</li>";
        }
        html += '</ul>';
        html += "</li>";
	
        return html;



    },

    //gets information on the variables 
    getVariables: function(variableSysIds, catItemSysId, variableSetSysId) {

		var result = '';
		//if(!variableSetSysId){
			result += '<ul>';
		//}
		
		
		//check if the catalog item has variable sets
		var variableSetsArr;
		var currentOrder = -1;
		if(catItemSysId){
			variableSetsArr = this.getVariableSetsForRecordProducer(catItemSysId);
		
			if(variableSetsArr){
				currentOrder = 0;
			
			}
		}
			
        var variablesGR = new GlideRecord('item_option_new');
        if (variableSysIds) {
            variablesGR.addQuery('sys_id', 'IN', variableSysIds);
        } else if (variableSetSysId){
			variablesGR.addActiveQuery();
            variablesGR.addQuery('variable_set', variableSetSysId);
		} else {
            variablesGR.addActiveQuery();
            variablesGR.addQuery('cat_item', catItemSysId);
			variablesGR.addEncodedQuery('typeNOT IN20,24,19,32'); //no containers or rich text
        }
        variablesGR.orderBy('order');
		
        variablesGR.query();
        while (variablesGR.next()) {

			if(currentOrder >= 0){
				
				
				if(variablesGR.getValue('order') > variableSetsArr[currentOrder].order){
				
					result += this.getVariableSet(variableSetsArr[currentOrder].sys_id);
				
					if(currentOrder == (variableSetsArr.length - 1)){
						currentOrder = -1;
					} else {
						currentOrder ++;
					}
				}
			}
            result += '<li>' + variablesGR.question_text.getDisplayValue() + ' (' + variablesGR.type.getDisplayValue();

            if (variablesGR.getValue('type') == '3') { //multiple choice
                result += ': ';
                result += this.getVariableChoices(variablesGR);
                result += ')';

            } else if (variablesGR.getValue('type') == '8') { //reference 
                result += ': List of values from the ';
                result += this.getTableLabel(variablesGR.reference.getDisplayValue());
                result += ' table)';
            } else if (variablesGR.getValue('type') == '21') { // list collector
                result += ' for multiple selections: List of values from the ';
                result += this.getTableLabel(variablesGR.list_table.getDisplayValue());
                result += ' table)';
            } else if (variablesGR.getValue('type') == '5') { // select box
                result += ': ';

                if (variablesGR.choice_table) {
                    result += this.getTableChoices(variablesGR.getValue('choice_table'), variablesGR.getValue('choice_field'));
                } else {
                    this.getVariableChoices(variablesGR)
                }
                result += ')';
            } else {
                result += ')';
            }
			result += '</li> ';

        }
		//if(!variableSetSysId){
			result += '</ul>';
		//}
		

		
        return result;
    },

    //gets the choices for a select variable
    getVariableChoices: function(variableGR) {

        var result = '';
        var choicesGR = new GlideRecord('question_choice');
        choicesGR.addQuery('question', variableGR.getUniqueValue());
        choicesGR.addQuery('inactive', false);
        choicesGR.orderBy('order');
        choicesGR.query();
        while (choicesGR.next()) {
            if (result) {
                result += ', ';
            }
            result += choicesGR.text.getDisplayValue();
        }
        return result;
    },
    //gets the label for a table
    getTableLabel: function(tableName) {
        var dictGR = new GlideRecord('sys_db_object');
        dictGR.get('name', tableName);
        return dictGR.label.getDisplayValue();
    },
    //gets the choices for a choice field
    getTableChoices: function(tableName, element) {

        var result = '';
        var choicesGR = new GlideRecord('sys_choice');
        choicesGR.addQuery('name', tableName);
        choicesGR.addQuery('element', element);
        choicesGR.addQuery('inactive', false);
        choicesGR.orderBy('order');
        choicesGR.query();
        while (choicesGR.next()) {
            if (result) {
                result += ', ';
            }
            result += choicesGR.label.getDisplayValue();
        }
        return result;
    },
    //gets information on the fields
    getFields: function(fieldList) {

        var result = '';


        var dictGR = new GlideRecord('sys_dictionary');
        dictGR.addQuery('sys_id', 'IN', fieldList);

        dictGR.query();
        while (dictGR.next()) {
            if (result) {
                result += '; ';
            }
            result += dictGR.column_label.getDisplayValue() + ' (' + dictGR.internal_type.getDisplayValue();

            if (dictGR.getValue('internal_type') == 'Choice') { //choice
                result += ': ';
                result += this.getTableChoices(dictGR.getValue('name'), dictGR.getValue('element'));
                result += ')';
            } else if (dictGR.getValue('internal_type') == 'reference') { //reference
                result += ': List of values from the ';
                result += dictGR.reference.getDisplayValue();
                result += ' table)';
            } else if (dictGR.getValue('internal_type') == 'glide_list') { //reference
                result += ' for multiple selections: List of values from the ';
                result += dictGR.reference.getDisplayValue();
                result += ' table)';
            } else {
                result += ')';
            }

        }
        // }
        return result;

    },
    //gets information on the forms
    getFederalHRForms: function(serviceGR) {
        result = '';
        var formsGR = new GlideRecord('sn_hr_core_forms');
        formsGR.addQuery('sys_id', 'IN', serviceGR.getValue('u_federal_hr_forms'));
        formsGR.query();
        while (formsGR.next()) {
            if (result) {
                result += '; ';
            }
            result += formsGR.u_name.getDisplayValue() + ' - ' + formsGR.u_title.getDisplayValue();
        }
        return result;
    },
    getVariableSet: function(variableSetSysId) {
        var result = '';

		var variableSetGR = new GlideRecord('item_option_new_set');
		variableSetGR.get(variableSetSysId);
		
        if (variableSetGR.getValue('type') == 'one_to_many') {
			result += '<li>';
            result += variableSetGR.title.getDisplayValue();
            result += " (Multi-row input) ";
			result += this.getVariables(null,null,variableSetGR.getUniqueValue());
			result += '</li>';
        }
		else {
			result += this.getVariables(null,null,variableSetGR.getUniqueValue());
			
		}
		return result;

    },
	getVariableSetsForRecordProducer: function(recordProducerSysId){
		var result = [];
		var setGR = new GlideRecord('io_set_item');
		setGR.addQuery('sc_cat_item',recordProducerSysId);
		setGR.query();
		while(setGR.next()){
			var obj = {};
			obj.sys_id = setGR.getValue('variable_set');
			obj.order = setGR.getValue('order');
			result.push(obj);
		}
		return result;

	},

    type: 'HRSDPrivacyReportUtils'
});