Comparing variable references to the department table against a user's department table
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago - last edited 3 weeks ago
I've got a catalogue item where a user can input new different departments into an existing business application:
What I'm wanting to add is check if the path to the current Business Owner's department aligns to the proposed Business Owning Business Unit/Function or Business Owning Platform. If it doesn't then inform the user that the current business owner does not sit within the proposed Business owning Business Unit and/or Platform.
The 'New Business Owning Business Unit or Function' displays options where the Department type is 'Function' or 'Business Unit' and 'New Business Owning Platform' displays options where the Department type is 'Platform'. I was just wondering if anyone has come across this before or something similar.
Generally, a a department with a type of 'Platform' is usually a child of a department with a type of 'Business Unit' or 'Function'. However, though, it can sometimes be that the grandchild department has a type of 'Platform', the parent department has a type of null, but the grandparent has a type of 'Business Unit' or 'Function'
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago
You can handle this cleanly with an onChange client script + GlideAjax pattern.
On the onChange of the Proposed BU/Function or Proposed Platform field call a client-callable Script Include that walks the user’s department hierarchy and validates wether the selected department type Function / Business Unit / Platform exists in the users department chain.
If the Script Include returns invalid, use g_form.showFieldMsg() to display an error message and optionally clear the value to force correction.
High-level flow:
User selects Proposed BU/Function or Platform
onChange client s cript calls Script Include via GlideAjax
Script Include:
Gets the user’s department
Walks up the department hierarchy
Collects department types
Validates the selected department type against that hierarchy
Client script shows an inline error if it doesn’t match
This keeps the hierarchy logic server-side and the UX lightweight on the client. The same script Include can be reused for both fields.
Happy to share a onChange w/ Script example if needed.
@matthew_hughes - Please give a thumbs up or accepted solution if you feel it was Helpful!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago - last edited 3 weeks ago
Script Include with 2 inputs:
- User_sys_id or User_dept_type - you can set the input for the user department directly if it will always be valid and one of the department types you want to match against, otherwise just pass the user_id as the first input.
- New_type_sys_id - pass the sys_id of the dept to want to check the user's dept type against.
var DeptMatchUtil = Class.create();
DeptMatchUtil.prototype = {
intialize: function () {},
checkType: function (user_sys_id, new_type_sys_id) { // pass both inputs regardless of if they are a sys_id or type
if (!user_sys_id || !new_type_sys_id) // this if checks to make sure both inputs were received or it will return failed
return false;
var userType = this.getNearestType(user_sys_id); // if you are passing the user's business type directly then you can set this to the input rather than calling the function to identify it
var newType = this.getNearestType(new_type_sys_id);
if (!userType || !newType) // this if verifies that a nearest valid type were returned for both departments or it will return failed
return false;
return userType == newType;
},
getNearestType: function (deptSysID) {
var currentID = deptSysID;
var visited = {};
while (currentID) {
if(visited[currentID]) // this if statement and the setting of the visited value protect against bad data loops where two records are parents of each other and would loop indefinitely
break;
visited[currentID] = true;
var grDept = new GlideRecord('cmn_department');
if (!grDept.get(currentID)) // we have to get the department anyway so we can wrap it in an if check here just in case a bad sys_id gets passed somehow
break;
var type = grDept.getValue('type'); // check the field name here and make sure it is correct for your instance (oranization_type is quite common)
if (type == 'Business Unit' || type == 'Function') {
return type;
}
currentID = grDept.getValue('parent');
}
return null; // this returns null if a type is not found triggering the earlier if condition resulting in a failed response
},
type: 'DeptMatchUtil'
};
The script include works by creating two functions - the first half is the function that compares the two type found and return true if they are same. The second half is a function used by the first one to lookup the nearest dept type of either Business Unit or Function and returns it for comparison.
Given your initial post you would then call this script include in an onChange script for the field that contains the new proposed department you are checking against the user.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago
Hi @John Gilmore Thanks for looking into this for me. So how would I write an onChange client script to correlate with that script include code above? It should throw an error message if the selected proposed Business Unit/Function or Platform doesn't match with those types in the user's department's hierarchy.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago - last edited 3 weeks ago
@matthew_hughes No problem.
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
if (isLoading || newValue == '')
return;
var userDept = g_form.getValue('user.department'); //set this to get the sys_id of department of the user required for comparison in the script include. Its important to make sure you are passing sys_ids of departments on the cmn_departments table as that is what the script include expects.
if (!userDept) { //this if statement will cancel the onChange if the field containing the user is empty, you could also have it throw and error stating the user is required if this happens instead of just cancelling the script.
return;
}
var ga = new GlideAjax('DeptMatchUtil'); //this is the actual glide ajax call formatted just like a glide record call.
ga.addParam('sysparam_name', 'checkType'); //this tells the glide ajax call which function in the script include we want executed. If you review the glide ajax it has two functions 'checkType' and 'getNearestType'. The checkType function actually performs the check and uses the 'getNearestType' function to retrieve business types.
ga.addParam('sysparam_user_sys_id', userDept); //be sure to pass the correct variable here depending on if you are passing the business type value or the user_sys_id.
ga.addParam('sysparm_new_type_sys_id', g_form.getValue('new_owning_business_unit'); //make sure this is passing the sys_id of the new dept required for the check validation.
ga.getXMLAnswer(function (response) {
if (!response) { //since the script include I provided returns the result of a true/false expression we can simply use the condition of if the response is not true.
g_form.hideFieldMsg('new_owning_business_unit', true); //this clears any existing message on the new owning buriness unit field before setting the new message.
g_form.showFieldMsg(
'new_owning_business_unit', //this is the field the error message will associate with.
'Error message to show the user', //this is the actual message that will be shown to the user.
'error' //this is the message type which control the formatting such as color and font size of the message, this is the value to use the global formatting for errors similar to when a required field is not complete or is invalid.
);
}
});
}Let me know if this aligns with what you were looking for or if you were thinking of a different type of messaging. Keep in mind that it is very important to make sure that your variables align across the onChange and script include to make sure you are passing the correct value to the ajax call and referencing the correct responses in any conditional logic after the response.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago
Use an onChange Catalog Client Script that calls your client-callable Script Include with GlideAjax, then show a field error if it returns invalid.
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading) return;
var ownerDept = g_form.getValue('current_business_owner_dept'); // sys_id
var bu = g_form.getValue('new_bu_function'); // sys_id
var platform = g_form.getValue('new_platform'); // sys_id
if (!ownerDept) return;
g_form.hideAllFieldMsgs();
var ga = new GlideAjax('DeptHierarchyAjax'); // your Script Include (Client callable)
ga.addParam('sysparm_name', 'validateOwnerAlignment');
ga.addParam('sysparm_owner_dept', ownerDept);
ga.addParam('sysparm_proposed_bu', bu);
ga.addParam('sysparm_proposed_platform', platform);
ga.getXMLAnswer(function(ans){
var res = JSON.parse(ans || '{}');
if (res.valid === false) {
// show error on the field the user changed
g_form.showFieldMsg(control.name, res.message || 'Selection does not align to owner hierarchy.', 'error');
// optional: clear invalid choice
// g_form.setValue(control.name, '');
}
});
}In your Script Include, return JSON like: {"valid":false,"message":"Owner dept doesn’t align to proposed BU/Platform."}
@matthew_hughes - Please give a thumbs up or accepted solution if you feel it was Helpful!
