From b6a351c9048a704d358ebd14fdfdd416675b66d6 Mon Sep 17 00:00:00 2001 From: DhruvBhatheja <70469942+DhruvBhatheja@users.noreply.github.com> Date: Thu, 9 Oct 2025 12:27:30 +0530 Subject: [PATCH 1/4] Readme.md --- .../Readme.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Server-Side Components/Background Scripts/Merge Duplicate User Records Automatically/Readme.md diff --git a/Server-Side Components/Background Scripts/Merge Duplicate User Records Automatically/Readme.md b/Server-Side Components/Background Scripts/Merge Duplicate User Records Automatically/Readme.md new file mode 100644 index 0000000000..97019e91b0 --- /dev/null +++ b/Server-Side Components/Background Scripts/Merge Duplicate User Records Automatically/Readme.md @@ -0,0 +1,17 @@ +Merge Duplicate User Records Automatically +Automatically detects and merges duplicate User records in ServiceNow based on the email address. + +Ensures data integrity and prevents duplicate entries. + +Reassigns related records (e.g., incidents) to a master record. + +Deactivates duplicate users. + +In this UseCase +Multiple users exist with the same email address. + +The first record found is treated as the master user. + +All duplicates are: Reassigned in related records (e.g., tasks, incidents). + +Marked inactive. From 980e9b02641a3e473492a491090b20321280ed58 Mon Sep 17 00:00:00 2001 From: DhruvBhatheja <70469942+DhruvBhatheja@users.noreply.github.com> Date: Thu, 9 Oct 2025 12:32:16 +0530 Subject: [PATCH 2/4] script.js --- .../script.js | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 Server-Side Components/Background Scripts/Merge Duplicate User Records Automatically/script.js diff --git a/Server-Side Components/Background Scripts/Merge Duplicate User Records Automatically/script.js b/Server-Side Components/Background Scripts/Merge Duplicate User Records Automatically/script.js new file mode 100644 index 0000000000..1147d1a041 --- /dev/null +++ b/Server-Side Components/Background Scripts/Merge Duplicate User Records Automatically/script.js @@ -0,0 +1,43 @@ +(function() { + var userEmailMap = {}; + var userGR = new GlideRecord('sys_user'); + userGR.addNotNullQuery('email'); + userGR.addQuery('active', true); + userGR.query(); + + var duplicatesFound = 0; + + while (userGR.next()) { + var email = userGR.email.toLowerCase(); + + if (!userEmailMap[email]) { + // First user with this email + userEmailMap[email] = userGR.sys_id.toString(); + } else { + // Duplicate found then merge with master + duplicatesFound++; + var masterSysId = userEmailMap[email]; + + // Reassign related incidents + var incGR = new GlideRecord('incident'); + incGR.addQuery('caller_id', userGR.sys_id); + incGR.query(); + var incidentsReassigned = 0; + while (incGR.next()) { + incGR.caller_id = masterSysId; + incGR.update(); + incidentsReassigned++; + } + + // Deactivate duplicate + userGR.active = false; + userGR.u_merged_to = masterSysId; // optional custom field + userGR.update(); + + gs.info("Merged duplicate user '" + userGR.name + "' into master. Reassigned " + incidentsReassigned + " incidents."); + } + } + + gs.info("Total duplicates merged: " + duplicatesFound); + +})(); From 9f569105d9e7538cd38fc8125e4325395f6ca00e Mon Sep 17 00:00:00 2001 From: DhruvBhatheja <70469942+DhruvBhatheja@users.noreply.github.com> Date: Fri, 10 Oct 2025 17:42:49 +0530 Subject: [PATCH 3/4] Update Readme.md --- .../Merge Duplicate User Records Automatically/Readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Server-Side Components/Background Scripts/Merge Duplicate User Records Automatically/Readme.md b/Server-Side Components/Background Scripts/Merge Duplicate User Records Automatically/Readme.md index 97019e91b0..ea27ce92e6 100644 --- a/Server-Side Components/Background Scripts/Merge Duplicate User Records Automatically/Readme.md +++ b/Server-Side Components/Background Scripts/Merge Duplicate User Records Automatically/Readme.md @@ -1,4 +1,6 @@ Merge Duplicate User Records Automatically + + Automatically detects and merges duplicate User records in ServiceNow based on the email address. Ensures data integrity and prevents duplicate entries. From 0ca4fe0d95ab2780e41d0d84389a3993543bcef5 Mon Sep 17 00:00:00 2001 From: DhruvBhatheja <70469942+DhruvBhatheja@users.noreply.github.com> Date: Fri, 10 Oct 2025 18:53:08 +0530 Subject: [PATCH 4/4] Update script.js --- .../script.js | 70 +++++++++++++------ 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/Server-Side Components/Background Scripts/Merge Duplicate User Records Automatically/script.js b/Server-Side Components/Background Scripts/Merge Duplicate User Records Automatically/script.js index 1147d1a041..e00e4aba78 100644 --- a/Server-Side Components/Background Scripts/Merge Duplicate User Records Automatically/script.js +++ b/Server-Side Components/Background Scripts/Merge Duplicate User Records Automatically/script.js @@ -1,43 +1,71 @@ (function() { - var userEmailMap = {}; + + var userEmailMap = {}; + var userCreatedMap = {}; + var duplicatesFound = 0; + var userGR = new GlideRecord('sys_user'); userGR.addNotNullQuery('email'); userGR.addQuery('active', true); + userGR.orderBy('email'); // Group by email userGR.query(); - var duplicatesFound = 0; - while (userGR.next()) { var email = userGR.email.toLowerCase(); + var createdOn = new GlideDateTime(userGR.sys_created_on).getNumericValue(); // Convert to timestamp if (!userEmailMap[email]) { - // First user with this email + // First user found for this email temporarily mark as master userEmailMap[email] = userGR.sys_id.toString(); + userCreatedMap[email] = createdOn; } else { - // Duplicate found then merge with master - duplicatesFound++; + // Another user with same email found var masterSysId = userEmailMap[email]; + var masterCreatedOn = userCreatedMap[email]; + + var masterIsOlder = createdOn > masterCreatedOn ? true : false; - // Reassign related incidents - var incGR = new GlideRecord('incident'); - incGR.addQuery('caller_id', userGR.sys_id); - incGR.query(); - var incidentsReassigned = 0; - while (incGR.next()) { - incGR.caller_id = masterSysId; - incGR.update(); - incidentsReassigned++; + if (masterIsOlder) { + // If this user was created later, we keep the existing master + // earliest created record will be accepted + mergeDuplicateUser(userGR, masterSysId); + } else { + // If this user was created earlier, this becomes the new master + var oldMasterGR = new GlideRecord('sys_user'); + if (oldMasterGR.get(masterSysId)) { + mergeDuplicateUser(oldMasterGR, userGR.sys_id); // merge old master into new master + } + // Update maps + userEmailMap[email] = userGR.sys_id.toString(); + userCreatedMap[email] = createdOn; } - // Deactivate duplicate - userGR.active = false; - userGR.u_merged_to = masterSysId; // optional custom field - userGR.update(); - - gs.info("Merged duplicate user '" + userGR.name + "' into master. Reassigned " + incidentsReassigned + " incidents."); + duplicatesFound++; } } gs.info("Total duplicates merged: " + duplicatesFound); + + + function mergeDuplicateUser(duplicateGR, masterSysId) { + // Reassign related incidents + var incGR = new GlideRecord('incident'); + incGR.addQuery('caller_id', duplicateGR.sys_id); + incGR.query(); + var count = 0; + while (incGR.next()) { + incGR.caller_id = masterSysId; + incGR.update(); + count++; + } + + // Deactivate user + duplicateGR.active = false; + duplicateGR.u_merged_to = masterSysId; // optional custom field + duplicateGR.update(); + + gs.info("Merged duplicate user '" + duplicateGR.name + "' → master: " + masterSysId + ". Reassigned " + count + " incidents."); + } + })();