Rushi Savarkar
Mega Sage
Mega Sage

Requirement Overview
The core objective is to automate the extraction of modified user attributes from the Lifecycle Event (LE) Case and its associated records. When a transfer occurs, the system must:

  • Identify the specific fields changed (e.g., Department, Job Code, Business Unit).
  • Compare the previous values (Old) with the current values (New).
  • Construct a secure JSON payload and transmit it to Okta to ensure downstream access is updated accurately

Technical Implementation

1. Data Extraction Logic

Script Include: IC_Transfer_FieldHistory_OKTA_Payload 

It takes the HR case sys_id, identifies the subject person, and retrieves old and new field values from ServiceNow field history records (sys_audit / history data) using IC_Transfer_GetFieldValues_FromHistory.
User-related changes (department, job code, employee type, title) and HR profile changes (region, company, country, workday location) are fetched from their respective tables.
All old and new values are consolidated into a structured JSON response for integration use.

Client Callable script include Code and scope is Human Resources: Lifecycle Events

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

    // Function to fetch all field changes (old and new values) for the subject person (sys_id)
    getAllFieldChanges: function(caseSysId) {
        // Initialize the result object
        var result = {
            "departmentNames": [],
            "departmentIds": [], // Array to store department IDs
            "businessUnitNames": [],
            "businessUnitIds": [],
            "jobCodeChanges": [],
            "empTypeChanges": [],
            "titleChanges": [],
            "hrProfileChanges": {
                "regionChanges": [],
                "companyChanges": [],
                "countryCodeChanges": [],
                "workdayLocationChanges": []
            }
        };

        if (!caseSysId) {
            gs.error('Sys_id is required to fetch field history');
            return 'Sys_id parameter is missing';
        }
        // Query to fetch the HR case by sys_id
        var leCase = new GlideRecord("sn_hr_le_case");
        if (leCase.get(caseSysId)) {
            var subPerson = leCase.subject_person;

            // Fetch and process department-related data
            var depChanges = new global.IC_Transfer_GetFieldValues_FromHistory().getFieldChanges('sys_user', subPerson, 'department');
            var departmentData = this.getDepartmentAndBusinessUnitData(depChanges);

            result.departmentNames = departmentData.names;
            result.departmentIds = departmentData.ids; // Add department IDs to result
            result.businessUnitNames = departmentData.businessUnitNames;
            result.businessUnitIds = departmentData.businessUnitIds;

            // Fetch and store other field changes
            result.jobCodeChanges = new global.IC_Transfer_GetFieldValues_FromHistory().getFieldChanges('sys_user', subPerson, 'u_job_code');
            result.empTypeChanges = new global.IC_Transfer_GetFieldValues_FromHistory().getFieldChanges('sys_user', subPerson, 'x_blglp_workday_in_employee_type');
            result.titleChanges = new global.IC_Transfer_GetFieldValues_FromHistory().getFieldChanges('sys_user', subPerson, 'title');

            // Fetch HR profile related fields
            var hrProfile = new GlideRecord("sn_hr_core_profile");
            hrProfile.addQuery('user', subPerson);
            hrProfile.query();
            if (hrProfile.next()) {
                // Store HR profile changes
                result.hrProfileChanges.regionChanges = new global.IC_Transfer_GetFieldValues_FromHistory().getFieldChanges('sn_hr_core_profile', hrProfile.sys_id, 'u_region');
                result.hrProfileChanges.companyChanges = new global.IC_Transfer_GetFieldValues_FromHistory().getFieldChanges('sn_hr_core_profile', hrProfile.sys_id, 'u_company_name');
                result.hrProfileChanges.countryCodeChanges = new global.IC_Transfer_GetFieldValues_FromHistory().getFieldChanges('sn_hr_core_profile', hrProfile.sys_id, 'u_country');
                result.hrProfileChanges.workdayLocationChanges = new global.IC_Transfer_GetFieldValues_FromHistory().getFieldChanges('sn_hr_core_profile', hrProfile.sys_id, 'u_workday_location');
            }

        } else {
            gs.error("No HR case found for sys_id: " + caseSysId);
        }

        // Return the result as a JSON object
        return result;
    },

    type: 'IC_FieldHistory_OKTA_Payload'
});

2. Script Include: IC_Transfer_GetFieldValues_FromHistory
This script is responsible for generating the history set and extracting the specific field transitions (old value and new value) from the sys_history_line table.

var IC_Transfer_GetFieldValues_FromHistory = Class.create();
IC_Transfer_GetFieldValues_FromHistory.prototype = {
    initialize: function() {},
	
    getFieldChanges: function(tableName, documentKey, fieldName) {
        var changes = [];
        var hw, createSet = new GlideRecord(tableName); //to create the history set
        createSet.get(documentKey);
        try {
            var historySetRec = new GlideHistorySet(tableName, documentKey);
            historySetRec.generate();
            GlideHistorySet(tableName, documentKey).refresh();
        } catch (e) {
            gs.info("COMPARISONTIME ERROR: " + e);
        }
        var historySet = new GlideRecord("sys_history_set");
        historySet.addQuery("table", tableName);
        historySet.addQuery("id", documentKey);
        historySet.query();
        if (historySet.next()) {
            var historyLine = new GlideRecord("sys_history_line");
            historyLine.addQuery('set', historySet.sys_id);
            historyLine.addQuery("field", fieldName);
            historyLine.addQuery("update_time", ">=", gs.minutesAgo(1440)); // 1440 minutes = 24 hours
            historyLine.orderByDesc("update_time");
            historyLine.query();
            if (historyLine.next()) {
                var oldValue = historyLine.getValue('old') || historyLine.getValue('new');
                var newValue = historyLine.getValue('new');
                changes.push(oldValue, newValue);
            } else {

                // If no history found, set both old and new to the current value
                var currentRecord = new GlideRecord(tableName);
                if (currentRecord.get(documentKey)) {
                    var currentValue = currentRecord.getValue(fieldName);
                    changes.push(currentValue, currentValue);
                }
            }
        }
        return changes;

    },
    type: 'IC_Transfer_GetFieldValues_FromHistory'
};

3. Payload Construction
Once the history is captured, a Flow Action compiles the data into the final JSON format required by Okta.
Code of flow action Payload

IC-Generate Payload: Generates a complete payload with both old and new user attributes using IC_Transfer_FieldHistory_OKTA_Payload script include

(function execute(inputs, outputs) {

var grHRCase = new GlideRecord(String(inputs.hr_case_table))
grHRCase.get(String(inputs.hr_case_sys_id));
grHRCase.query();

if(grHRCase.next()) {
var caseSysId =  String(inputs.hr_case_sys_id);
var getFields = new sn_hr_le.IC_Transfer_FieldHistory_OKTA_Payload();
var getFieldValues = getFields.getAllFieldChanges(caseSysId)
outputs.new_department = getFieldValues.departmentNames[1];
outputs.old_department = getFieldValues.departmentNames[0];
outputs.new_business_unit = getFieldValues.businessUnitNames[1];
outputs.old_business_unit = getFieldValues.businessUnitNames[0];
outputs.old_region = getFieldValues.hrProfileChanges.regionChanges[0];
outputs.new_region = getFieldValues.hrProfileChanges.regionChanges[1];
outputs.old_job_code = getFieldValues.jobCodeChanges[0];
outputs.new_job_code = getFieldValues.jobCodeChanges[1];

        var transferEffectiveDateTime = new GlideDateTime(grHRCase.u_effective_date);
        var transferEffectiveTime = String(transferEffectiveDateTime).replace(" ", "T") + "Z";
        var payload = {
                "user_id": String(grHRCase.subject_person.employee_number),
                "event": {
                    "id": String(grHRCase.number),
                    "type": String(inputs.event_type),
                    "status": String(inputs.status),
                    "details": {
                        "SNOW_RITM": String(inputs.provision_access_ritm),
                        "effective_time": String(transferEffectiveTime),
                        "old_user_attributes":{
                            "firstName": String(grHRCase.subject_person_hr_profile.legal_first_name),
                            "lastName": String(grHRCase.subject_person_hr_profile.u_legal_last_name),
                            "userType": String(getFieldValues.empTypeChanges[0].toUpperCase()),
                            "wd_jobCode": String(getFieldValues.jobCodeChanges[0]),
                            "title": String(getFieldValues.titleChanges[0]),
                            "division": String(getFieldValues.businessUnitNames[0]),
                            "wd_BusinessUnitID": String(getFieldValues.businessUnitIds[0]),
                            "department": String(getFieldValues.departmentNames[0]),
                            "wd_DepartmentID": String(getFieldValues.departmentIds[0]),
                            "wd_region": String(getFieldValues.hrProfileChanges.regionChanges[0]),
                            "countryCode": String(getFieldValues.hrProfileChanges.countryCodeChanges[0]),
                            "wd_location": String(getFieldValues.hrProfileChanges.workdayLocationChanges[0]),
                            "wd_company": String(getFieldValues.hrProfileChanges.companyChanges[0])
                        },
                        "new_user_attributes":{
                            "firstName": String(grHRCase.subject_person_hr_profile.legal_first_name),
                            "lastName": String(grHRCase.subject_person_hr_profile.u_legal_last_name),
                            "userType": String(getFieldValues.empTypeChanges[1].toUpperCase()),
                            "wd_jobCode": String(getFieldValues.jobCodeChanges[1]),
                            "title": String(getFieldValues.titleChanges[1]),
                            "division": String(getFieldValues.businessUnitNames[1]),
                            "wd_BusinessUnitID": String(getFieldValues.businessUnitIds[1]),
                            "department": String(getFieldValues.departmentNames[1]),
                            "wd_DepartmentID": String(getFieldValues.departmentIds[1]),
                            "wd_region": String(getFieldValues.hrProfileChanges.regionChanges[1]),
                            "countryCode": String(getFieldValues.hrProfileChanges.countryCodeChanges[1]),
                            "wd_location": String(getFieldValues.hrProfileChanges.workdayLocationChanges[1]),
                            "wd_company": String(getFieldValues.hrProfileChanges.companyChanges[1])
                        }
                        }
                    }
                } 
outputs.payload = JSON.stringify(payload);
}

})(inputs, outputs);

If my article helped you, please mark it as helpful.

Thank you!

Version history
Last update:
2 hours ago
Updated by:
Contributors