- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago
Hello,
I am working on a project that involves making a UI button visible in the agent workspace based on a field in the hr_profile. During this implementation, I discovered that our opened_for does not always align with our hr_profile.
I found some relevant Business Rules that populate these fields (which I believe are OOB)
You will see that there is a business rule that updates the subject_person_hr_profile whenever the subject person changes. But the Business rules for hr_profile either set the opened_for based on the hr_profile OR set the hr_profile based on the opened_for.
In HR Agent Workspace we do not have an OOB field visible for agents to change the hr_profile. They do however, change the opened_for rather frequently. I looked in our audit table in our production instance and found that the opened_for changes after case creation hundreds of times per year. That means for all of those cases the opened_for and the hr_profile is out of sync.
Which is the best approach to resolve this inconsistency? Should I update the BRs to mimic the way the subject_person populates the hr_profile. Or should I surface the hr_profile field in the Agent workspace and make the opened_for read only so the agent has to change the hr_profile directly?
1) Populate profile
Before | Order 100 | Insert and Update
Condition: !current.opened_for.nil() && current.hr_profile.nil()
Script:
Condition: current.hr_profile.changes()
Script:
Condition: current.subject_person.changes()
Script:
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago
BR #1 only populates hr_profile when it's nil, so any subsequent change to opened_for leaves the two fields out of sync. Meanwhile BR #3 shows ServiceNow's own preferred pattern: whenever the person field changes, re-derive the profile.
I'd recommend Option A — add a BR that syncs hr_profile when opened_for changes, mirroring the subject_person pattern. Here's why:
UX disruption is the main argument against Option B. Your agents already have muscle memory around changing opened_for. Making it read-only and forcing them through hr_profile instead introduces training overhead, potential resistance, and a less intuitive workflow — opened_for is a user reference field agents understand, while hr_profile is an abstraction they shouldn't need to think about. The profile should be a derived value, not something agents manage directly.
The BR approach solves it at the platform level without touching the workspace layout at all. You'd essentially refactor BR #1 to behave like BR #3:
// "Sync HR Profile from Opened For"
// Before | Order 50 | Insert & Update
// Condition: current.opened_for.changes()
(function executeRule(current, previous) {
if (gs.nil(current.opened_for))
current.hr_profile = "";
else {
var hrProfile = new GlideRecord('sn_hr_core_profile');
hrProfile.addQuery('user', current.opened_for);
hrProfile.query();
if (hrProfile.next())
current.hr_profile = hrProfile.getUniqueValue();
}
})(current, previous);
The circular dependency is the thing to watch. BR #2 sets opened_for when hr_profile changes, and this new BR sets hr_profile when opened_for changes. That's a potential infinite loop. A few ways to handle it:
First, set the new BR to order 50 (before BR #2 at 100). Since both are "before" BRs operating on the same record in memory, the sequence matters. When an agent changes opened_for, your new BR derives hr_profile at order 50. Then BR #2 sees hr_profile.changes() at order 100 and sets opened_for back — but it should resolve to the same user, so no net conflict. Still, to be safe, you can tighten BR #2's condition:
// Add to BR #2's condition:
current.hr_profile.changes() && !current.opened_for.changes()
This way BR #2 only fires when someone changes the profile without also changing opened_for — meaning it only governs the scenario where hr_profile is changed directly (which today only happens at insert or via integrations).
One more thing to consider: you'll want to handle the case where opened_for is changed to a user who has no HR profile. Your current BR #1 silently leaves hr_profile as-is in that scenario, which would now mean stale data. The pattern above handles it by clearing hr_profile if opened_for is nil, but you may also want to clear it (or log a warning) when the profile lookup returns no results.
For the existing out-of-sync records in production, a one-time fix script querying cases where opened_for and hr_profile.user don't match would clean up the historical data.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago
BR #1 only populates hr_profile when it's nil, so any subsequent change to opened_for leaves the two fields out of sync. Meanwhile BR #3 shows ServiceNow's own preferred pattern: whenever the person field changes, re-derive the profile.
I'd recommend Option A — add a BR that syncs hr_profile when opened_for changes, mirroring the subject_person pattern. Here's why:
UX disruption is the main argument against Option B. Your agents already have muscle memory around changing opened_for. Making it read-only and forcing them through hr_profile instead introduces training overhead, potential resistance, and a less intuitive workflow — opened_for is a user reference field agents understand, while hr_profile is an abstraction they shouldn't need to think about. The profile should be a derived value, not something agents manage directly.
The BR approach solves it at the platform level without touching the workspace layout at all. You'd essentially refactor BR #1 to behave like BR #3:
// "Sync HR Profile from Opened For"
// Before | Order 50 | Insert & Update
// Condition: current.opened_for.changes()
(function executeRule(current, previous) {
if (gs.nil(current.opened_for))
current.hr_profile = "";
else {
var hrProfile = new GlideRecord('sn_hr_core_profile');
hrProfile.addQuery('user', current.opened_for);
hrProfile.query();
if (hrProfile.next())
current.hr_profile = hrProfile.getUniqueValue();
}
})(current, previous);
The circular dependency is the thing to watch. BR #2 sets opened_for when hr_profile changes, and this new BR sets hr_profile when opened_for changes. That's a potential infinite loop. A few ways to handle it:
First, set the new BR to order 50 (before BR #2 at 100). Since both are "before" BRs operating on the same record in memory, the sequence matters. When an agent changes opened_for, your new BR derives hr_profile at order 50. Then BR #2 sees hr_profile.changes() at order 100 and sets opened_for back — but it should resolve to the same user, so no net conflict. Still, to be safe, you can tighten BR #2's condition:
// Add to BR #2's condition:
current.hr_profile.changes() && !current.opened_for.changes()
This way BR #2 only fires when someone changes the profile without also changing opened_for — meaning it only governs the scenario where hr_profile is changed directly (which today only happens at insert or via integrations).
One more thing to consider: you'll want to handle the case where opened_for is changed to a user who has no HR profile. Your current BR #1 silently leaves hr_profile as-is in that scenario, which would now mean stale data. The pattern above handles it by clearing hr_profile if opened_for is nil, but you may also want to clear it (or log a warning) when the profile lookup returns no results.
For the existing out-of-sync records in production, a one-time fix script querying cases where opened_for and hr_profile.user don't match would clean up the historical data.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago
Hi @beckerlt
First check this KB :
Probable Solution:
1. If you add subject_person field in Default view of HR Case table , it will be visible to Agent . So your current & future cases-sorted.
2. For Legacy record, you have to run background script/fix ,to sync the value as per your requirement post getting proper approval as it has impact on reporting/ metrices.
