Business rules Archives - ServiceNow Guru https://servicenowguru.com/category/business-rules-scripting/ ServiceNow Consulting Scripting Administration Development Tue, 28 May 2024 21:20:54 +0000 en-US hourly 1 https://wordpress.org/?v=6.8.2 https://servicenowguru.com/wp-content/uploads/2024/05/cropped-SNGuru-Icon-32x32.png Business rules Archives - ServiceNow Guru https://servicenowguru.com/category/business-rules-scripting/ 32 32 Display Messages With UI Notifications https://servicenowguru.com/ui-scripts-system-ui/display-messages-ui-notification/ https://servicenowguru.com/ui-scripts-system-ui/display-messages-ui-notification/#comments Tue, 18 Feb 2014 13:29:41 +0000 https://servicenowguru.wpengine.com/?p=5143 Some time ago we showed you how to refresh the left navigation pane via a script. The server-side solution in that article utilizes ServiceNow’s UINotification object/class. On versions prior to Calgary, this is the Java class Packages.com.glide.ui.UINotification.  There’s another use for UI notifications. In addition to refreshing the left navigator, the object can also be

The post Display Messages With UI Notifications appeared first on ServiceNow Guru.

]]>
Some time ago we showed you how to refresh the left navigation pane via a script. The server-side solution in that article utilizes ServiceNow’s UINotification object/class. On versions prior to Calgary, this is the Java class Packages.com.glide.ui.UINotification

There’s another use for UI notifications. In addition to refreshing the left navigator, the object can also be used to display informational messages. You’ve probably already seen these in your own instance. When you change update sets, the system reminds you by using a UI Notification similar to the one shown below:

Update Set Change

If you’re like me, you may have wondered how these notifications are created. Displaying a custom UI notification is slightly more complex than using the addInfoMessage() method provided by the GlideSystem (server) and GlideForm (client) classes. In return, it gives you some capabilities that aren’t otherwise available. These include options to set fade-in and fade-out effects and the ability to make the notification persistent or to close it after a user-specified interval. If you want to learn more about the various types of notifications available in ServiceNow, check out our UI Info and Error Message Cheat Sheet.

The example below uses the UINotification object to display a list of all fields that have been changed on an incident. I’d like to take credit for this solution, but it was actually my colleague Jim Coyne who did the heavy lifting. I’m most especially indebted to him for compiling the all-important list of option properties that determine how the notification behaves.

As with any undocumented API, ServiceNow could make changes to the UINotification class at any time that might break the functionality we’re describing. Also, ServiceNow Technical Support won’t give you any help if you have trouble with this. Be sure to keep those things in mind if you decide to use custom UI Notifications in your instance.

Creating The Notification

To create the UI Notification and its options, use a global UI Script:

‘Popup UI Notification’ UI Script
Name: Popup UI Notification
Active: true
Global: true
Description: Adds a popup UI Notification to the DOM. Can be called from any server-side script. For an example, see ‘Changed Fields UI Notification’ business rule on the Incident table.
Script:

//Ensure that the CustomEvent object exists
var intervalTest = setInterval(function(){
   if(typeof CustomEvent != 'undefined'){
      clearInterval(intervalTest);
      setCustomEvents();
   }
},500);

//Add the UI Notification to the DOM using the CustomEvent object
function setCustomEvents(){
   //Set the name of the notification
   var notifName = "demo_notification";
   CustomEvent.observe('glide:ui_notification.' + notifName,
   function(notification){
      var msgText = notification.getAttribute('text1'); //get the message text from the attribute passed in by the business rule
      var options = {}; //create a new object to store all of the message attributes.
      options.text = "<span style='color:red;'>" + msgText + "</span>";
      options.sticky = true;
      options.closeDelay = 5000;
      options.fadeIn = 500;
      options.fadeOut = 500;
      new NotificationMessage(options); //display the UI Notification
   });
}

The CustomEvent object takes two arguments:

  1. The ‘glide:ui_notification.‘ object. This object includes a mandatory property that defines its name. In our example we’ve used a variable to store the name, but it could also be added to the object as an explicit value. In that case, it would be written ‘glide:ui_notification.demo_notification’ (including the quotes).
  2. A function that defines the notification’s contents and display options. The display options determine how the notification will behave. The options should be defined as properties in their own options object within the function. Available options properties include:
    text: Required. The text of the message. You can include rich text formatting via standard HTML tags.
    sticky: Optional. If true, the notification persists until the user closes it by clicking the [x] icon. If false or omitted, the notification automatically closes after the specified closeDelay.
    closeDelay: Optional. Automatically closes the message after this number of milliseconds. If omitted, the message closes after about three seconds. If the sticky property is true, this option is ignored.
    fadeIn: Optional: Sets the fade-in time for the notification in milliseconds
    fadeOut: Optional: Sets the fade-out time for the notification in milliseconds if the sticky property is false/omitted. If the sticky property is true, this option is ignored.
Note that if fadeIn, fadeOut and closeDelay intervals are specified, they are additive. That is, the notification will be displayed for the total amount of time specified in all three properties.

The function uses the getAttribute() method to retrieve the information passed in from the server-side script which triggers the notification. If multiple attributes have been set in the server-side script, the UI Script will need additional getAttribute() method calls to retrieve each one.

Displaying the Notification

To display the notification, we simply instantiate, (create a new instance of) the UINotification object in a Business Rule (it may work in other server-side scripts but I haven’t tested that). For our example we’ll use a business rule that runs after the incident has been updated. Our script borrows code from a previous post about checking for modified fields on forms or records.

‘Changed Fields UI Notification’ Business Rule
Name: Changed Fields UI Notification
Table: Incident [incident] Active: true
When: after
Update: true
Script:

//First, get an ArrayList of all fields changed on the record
if (typeof GlideScriptRecordUtil != 'undefined') {
   var gru = GlideScriptRecordUtil.get(current);
} else {
   var gru = Packages.com.glide.script.GlideRecordUtil.get(current);
}

var changedFields = gru.getChangedFields(); //Get changed fields with friendly names

//Next, pass the values into the UI Notification we defined in the UI Script
var notification;
if(typeof UINotification != 'undefined') {
   notification = new UINotification('demo_notification'); //Calgary and later releases use the UINotification object call
} else {
   notification = new Packages.com.glide.ui.UINotification('demo_notification'); //For pre-Calgary releases, use the Java Package call
}

//create the message to be displayed
var messageText = gs.getUser().getDisplayName();
messageText += ' modified the following fields on : ';
messageText += current.number;

/*
Assign the message text to the 'text1' attribute. Additional attributes can be passed into the UI notification. Just be sure each one is accounted for in the UI Script via getAttribute() method calls.
*/
notification.setAttribute('text1', messageText + changedFields);
notification.send();

The key items in the business rule’s script are:

  • The new UINotification object call that specifies the object we defined in the UI Script (‘demo_notification’)
  • The setAttribute() method that passes specific information into the notification
  • The send() method that triggers the actual display of the notification.

To see the notification, open any Incident and modify one or more fields, then Save or Update it.

Incident field changes

Once the UI Script is defined, it can be reused as many times as needed. Just call it from any business rule and pass information to it using the setAttribute() method.

The post Display Messages With UI Notifications appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/ui-scripts-system-ui/display-messages-ui-notification/feed/ 19
Prevent Redundant Approval Requests in ServiceNow https://servicenowguru.com/business-rules-scripting/prevent-redundant-approval-requests-servicenow/ https://servicenowguru.com/business-rules-scripting/prevent-redundant-approval-requests-servicenow/#comments Tue, 17 Dec 2013 13:18:45 +0000 https://servicenowguru.wpengine.com/?p=5084 If you’re like many of the customers I’ve worked with, you may have dealt with the frustration of having excess or redundant approval requests come to you from ServiceNow. This happens very often simply because the same user may be responsible for various different tasks in the system. For example, on a change request, I

The post Prevent Redundant Approval Requests in ServiceNow appeared first on ServiceNow Guru.

]]>
If you’re like many of the customers I’ve worked with, you may have dealt with the frustration of having excess or redundant approval requests come to you from ServiceNow. This happens very often simply because the same user may be responsible for various different tasks in the system. For example, on a change request, I may be asked to approve as the ‘Requested by’ person’s manager, then again because I own one of the affected CIs, and then again because I’m a member of the Change Advisory Board! While this behavior may be desirable in certain situations, most of the time it’s completely redundant and annoying to end users. If I’ve already indicated my approval on a change as a manager, why should I be asked to approve again later? I’ve come up with what I think is a pretty effective solution to this problem that I’ll share here in this article.

Redundant Approvals

The Solution…

At least in my mind, in order for this solution to be successful, it needs to meet a few criteria…

  1. First and foremost, the user should not be notified of an approval request if they’ve already approved a record.
  2. We need to maintain the history of the approval record for audit purposes and accurately reflect what happened to the redundant approval requests. This means that deleting or aborting record insertion is out of the question!
  3. We cannot negatively impact the approval workflow by interrupting the normal approval process.

The first two criteria need to be met at the same time. Preventing the approval request could be done in a couple of different ways. One way would be to somehow manipulate the workflow activity to check if the user has previously approved. The drawback to this approach is that it requires hacking the workflow approval activities…which would be a significant upgrade risk, or adding custom code to every single approval activity in every single workflow…which would be a maintenance nightmare.

That leaves us with intercepting the creation/update of the approval records in a business rule. Using a ‘before’ insert/update business rule we can evaluate every ‘requested’ approval record, run a script to determine if the approval is for a user that has already approved this item, and then adjust the approval record by setting the approval state to ‘No Longer Required’…all before we trigger any update or notification to the approving user. We can also add some approval comments so that we can accurately reflect why the approval isn’t required anymore. Adding the following business rule to the ‘Approval [sysapproval_approver]’ table accomplishes that for us.

‘Duplicate Approval Requests Not Required’ Business Rule
Name: Duplicate Approval Requests Not Required
Table: Approval [sysapproval_approver] When: Before
Insert/Update: true
Condition: current.state.changesTo(‘requested’)
Script:

//Check to see if user has previously approved
approveDuplicateApproval();function approveDuplicateApproval(){
//Must have link to record being approved
if(current.document_id || current.sysapproval){
//Query for approval records for this user/record
var app = new GlideRecord('sysapproval_approver');
//Handle empty document_id and sysapproval fields
if(!current.document_id.nil()){
app.addQuery('document_id', current.document_id);
}
else if(!current.sysapproval.nil()){
app.addQuery('sysapproval', current.sysapproval);
}
app.addQuery('approver', current.approver);
app.addQuery('state', 'approved');
//Optionally restrict to current workflow
//app.addQuery('wf_activity.workflow_version', current.wf_activity.workflow_version);
app.query();
if(app.next()){
//If previous approval is found set this approval to 'approved'
current.state = 'not_required';
current.comments = "Approval marked by system as 'Not Longer Required' due to a previous approval on the same record by the same user.";
}
}
}

While the above script works great in manipulating the approval records on its own, it fails in the third criteria mentioned above…it can negatively impact the workflow in certain situations. This is because the workflow processing that happens after an approval status is changed isn’t seeing the true value of the approval request to process the workflow approval activity correctly. In my testing this couldn’t be done in a ‘before’ or ‘after’ business rule due to the timing of the updates. What is needed is to run the workflow checks one more time, and ensure that it happens after all of the above manipulation happens. The best way I could find to do this is through a separate ‘async’ business rule. The only downside to this async business rule is that it may not process immediately depending on the load on your system. Generally it should process within a few seconds though so for all practical intents and purposes it’s a non-issue.

‘Run parent workflows (Not Required)’ Business Rule
Name: Run parent workflows (Not Required)
Table: Approval [sysapproval_approver] When: async
Priority: 200 (This is important to be set to something greater than 100 to ensure that this runs ASAP!)
Insert/Update: true
Condition: current.state == ‘not_required’
Script:

// Run any workflows for our parent so they can check the approval states
runWorkflow_userApprove();function runWorkflow_userApprove() {
var id = current.sysapproval.nil() ? current.document_id : current.getValue('sysapproval');
var table = current.source_table.nil() ? 'task' : current.source_table;
if (id != null && table != null ) {
var gr = new GlideRecord(table);
if (gr.get(id)) {
new Workflow().runFlows(gr, 'update');
}
}
}

With the above solution in place, you should have created a very effective way to prevent the issue of redundant approval requests for the same user against the same record in ServiceNow.

The post Prevent Redundant Approval Requests in ServiceNow appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/business-rules-scripting/prevent-redundant-approval-requests-servicenow/feed/ 29
Schedule-based Date/Time Addition https://servicenowguru.com/business-rules-scripting/schedule-time-addition/ https://servicenowguru.com/business-rules-scripting/schedule-time-addition/#comments Wed, 26 Oct 2011 15:53:50 +0000 https://servicenowguru.wpengine.com/?p=4076 I was recently asked to help a colleague figure out some date calculations based on a schedule. The requirement was to calculate a future date based on the existing value of a date/time field. I decided to document this solution (and come up with a solution for a similar problem…date addition from the current date/time

The post Schedule-based Date/Time Addition appeared first on ServiceNow Guru.

]]>
I was recently asked to help a colleague figure out some date calculations based on a schedule. The requirement was to calculate a future date based on the existing value of a date/time field. I decided to document this solution (and come up with a solution for a similar problem…date addition from the current date/time based on a schedule). Working with dates and schedules can really be a pain if you don’t have good examples to work from so hopefully these help somebody at some point. Read on for the full scripts.

These scripts are designed to work in a ‘before’ business rule. To get them to work in a UI action you need to include ‘current.update();’ as the last line.

Add time based on schedule to current time

//Get a schedule by name to calculate duration
var schedRec = new GlideRecord('cmn_schedule');
schedRec.get('name', '8-5 weekdays');
if (typeof GlideSchedule != 'undefined')
   var sched = new GlideSchedule(schedRec.sys_id);
else
   var sched = new Packages.com.glide.schedules.Schedule(schedRec.sys_id);

//Get the current date/time in correct format for duration calculation
var currentDateTime = new GlideDateTime();
currentDateTime.setDisplayValue(gs.nowDateTime());

//Set the amount of time to add (in seconds)
var timeToAdd = 86400;
durToAdd = new GlideDuration(timeToAdd*1000);
var newDateTime = sched.add(currentDateTime, durToAdd, '');

//Set the 'requested_by_date' field to the new date/time
current.requested_by_date = newDateTime;

Add time based on schedule to current field value

//Get a schedule by name to calculate duration
var schedRec = new GlideRecord('cmn_schedule');
schedRec.get('name', '8-5 weekdays');
if (typeof GlideSchedule != 'undefined')
   var sched = new GlideSchedule(schedRec.sys_id);
else
   var sched = new Packages.com.glide.schedules.Schedule(schedRec.sys_id);

//Get the current date/time in correct format for duration calculation
var currentDateTime = current.requested_by_date.getGlideObject();

//Set the amount of time to add (in seconds)
var timeToAdd = 86400;
durToAdd = new GlideDuration(timeToAdd*1000);
var newDateTime = sched.add(currentDateTime, durToAdd, '');

//Set the 'requested_by_date' field to the new date/time
current.requested_by_date = newDateTime;

The post Schedule-based Date/Time Addition appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/business-rules-scripting/schedule-time-addition/feed/ 25
Hide Empty Variables on a Standard Form https://servicenowguru.com/client-scripts-scripting/hide-empty-variables-standard-form/ https://servicenowguru.com/client-scripts-scripting/hide-empty-variables-standard-form/#comments Thu, 16 Jun 2011 16:31:17 +0000 https://servicenowguru.wpengine.com/?p=3826 Service catalog variables can be a challenge to deal with on standard forms when they are displayed in a variable editor. I’ve written before about different ways that you can solve one of these challenges…making the variables read only, so that they can’t be modified after the initial submission through the service catalog interface. Another

The post Hide Empty Variables on a Standard Form appeared first on ServiceNow Guru.

]]>
Service catalog variables can be a challenge to deal with on standard forms when they are displayed in a variable editor. I’ve written before about different ways that you can solve one of these challenges…making the variables read only, so that they can’t be modified after the initial submission through the service catalog interface. Another common problem I’ve seen is that you can end up with a lot of variables that end up empty in the variable editor on your request item or task because they were optional or hidden on the front-end catalog form. If the variables are empty and you aren’t going to have users interact with them on the standard forms then there isn’t much use in having these variables show up at all in the variable editor.

Until now there really hasn’t been a good way to deal with this issue because of the challenges of dealing with so many different variable types in client-side JavaScript. A couple of days ago one of my colleagues, Jacob Kimball, suggested to me that we might be able to overcome this issue by using a ‘display’ business rule to collect the blank variable information at the server and then pass those variable names to the client. So, you can thank Jacob Kimball for the brilliance of this solution. I’m just spreading the love. :)

As explained above, the key to making this work is a ‘display’ business rule. The business rule runs before the display of any record in the table (tasks in this case) and queries the ‘sc_item_option_mtom’ and ‘question_answer’ tables to collect any variable names for empty variables. Then it passes this information in the ‘g_scratchpad’ object to the client to hide the variables on the form.

Here is how you could set up the business rule. The script is designed to hide any empty variables for any task records whether they are generated from a record producer or as a catalog item.

‘Hide Empty Variables’ Business Rule
Name: Hide Empty Variables
Table: Task
When: display
Condition: !RP.isPopup()
Script:

//Initialize the scratchpad variable
g_scratchpad.emptyVars = '';

//Check to see if a variable pool exists
var count = 0;
for(vars in current.variable_pool){
   count++;
   break;
}

//If a variable pool exists then collect empty variable names
if(count > 0){
   var emptyVars = [];
   var table = current.getTableName();
   //Query for the empty variables for this record
   //Catalog item and task variables pull from 'sc_item_option_mtom' table
   if(table == 'sc_req_item' || table == 'sc_task'){
      var itemVars = new GlideRecord('sc_item_option_mtom');
      if(table == 'sc_req_item'){
         itemVars.addQuery('request_item', current.sys_id);
      }
      if(table == 'sc_task'){
         itemVars.addQuery('request_item', current.request_item.sys_id);
      }
      itemVars.addNullQuery('sc_item_option.value');
      //Exclude Label and Container variables
      itemVars.addQuery('sc_item_option.item_option_new.type', '!=', 11);
      itemVars.addQuery('sc_item_option.item_option_new.type', '!=', 19);
      itemVars.addQuery('sc_item_option.item_option_new.type', '!=', 20);
      itemVars.query();
      while(itemVars.next()){
         //Add variable names to the emptyVars array
         emptyVars.push(itemVars.sc_item_option.item_option_new.name.toString());
      }
   }
   else{
      //All other variables pulled from 'question_answer' table
      var producerVars = new GlideRecord('question_answer');
      producerVars.addQuery('table_sys_id', current.sys_id);
      producerVars.addNullQuery('value');
      //Exclude Label and Container variables
      producerVars.addQuery('question.type', '!=', 11);
      producerVars.addQuery('question.type', '!=', 19);
      producerVars.addQuery('question.type', '!=', 20);
      producerVars.query();
      while(producerVars.next()){
         //Add variable names to the emptyVars array
         emptyVars.push(producerVars.question.name.toString());
      }
   }

   //Store the result in the scratchpad
   g_scratchpad.emptyVars = emptyVars.join();
}

Once you’ve got the empty variable names collected all you have to do is set up a client script to grab the ‘g_scratchpad’ variable, split out any empty variable names, and hide each one. The client script is pretty simple since the heavy lifting is being done in the business rule. Just make sure that you check the ‘Inherited’ checkbox if you decide to set this up on the task table!

‘Hide Empty Variables’ Client Script
Name: Hide Empty Variables
Type: OnLoad
Table: Task
Inherited: True
Script:

function onLoad() {
   //Hide all empty variables using the scratchpad object passed from 'Hide Empty Variables' business rule
   if(g_scratchpad.emptyVars != ''){
      var emptyVars = g_scratchpad.emptyVars.split(',');
      for(i = 0; i < emptyVars.length; i++){
         g_form.setDisplay('variables.' + emptyVars[i], false);
      }
   }
}

The post Hide Empty Variables on a Standard Form appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/client-scripts-scripting/hide-empty-variables-standard-form/feed/ 75
Prevent Circular Relationships in ServiceNow https://servicenowguru.com/business-rules-scripting/prevent-circular-relationships-servicenow/ https://servicenowguru.com/business-rules-scripting/prevent-circular-relationships-servicenow/#comments Wed, 16 Mar 2011 12:52:59 +0000 https://servicenowguru.wpengine.com/?p=3457 Almost any database will have situations where a record in a table can relate back to other records in that same table. This happens in ServiceNow in several places and it’s common enough that you may find yourself building additional functionality that works this way. The classic case is the ‘Parent’ field. A good example

The post Prevent Circular Relationships in ServiceNow appeared first on ServiceNow Guru.

]]>
Almost any database will have situations where a record in a table can relate back to other records in that same table. This happens in ServiceNow in several places and it’s common enough that you may find yourself building additional functionality that works this way.
The classic case is the ‘Parent’ field. A good example is the ‘Parent’ field on the ‘Location (cmn_location)’ table. Each location record in ServiceNow can be related to a ‘Parent’ location. This allows you to build a nice hierarchy of locations within your Service-now instance. Unless you work in one of those very rare places that implements a completely flat location structure, this parent-child relationship is critical for representing locations in your system.

This same type of setup is used in other places (such as task tables) where a given record can result in the generation of another record of the same type. You may have scenarios where a change request can be the parent of other change request or where a major incident becomes the parent of other child incidents. In this post, I’ll address the problem of circular relationships that can exist when you’re working with parent-child relationships in ServiceNow.

In order to represent a parent-child relationship, you simply have to set up a reference field (or use an existing one) on the table of your choice. The thing you really have to be careful of with these fields is that they also allow users to relate a record to itself, making itself its own parent! While that scenario seems like it would be obvious to avoid, it’s not so easy to see situations where you might be making a record its own parent a few levels up. You’ll encounter problems when you need to query the parent hierarchy to get a list of the parents of a particular record. What ends up happening if a record has been made a parent of itself at some level is that your query runs in an endless loop and you end up killing the performance of your system or potentially making it completely unusable.
In order to prevent this type of problem, I’ve created the following ‘Prevent circular relationship’ script that you can use in a business rule for any table/field combination where you might have this problem in your system. It’s very simple to use. Simply construct the business rule as shown below and indicate the name of the field to check for circular relationships.

‘Prevent circular relationship’ business rule
Name: Prevent circular relationship
When: Before
Insert/Update: True
Condition: current.parent.changes() && !current.parent.nil()
Script:

(function executeRule(current, previous /*null when async*/) {	
	var fieldName = 'parent'; //Specify the name of the field to check for circular relationship on
	var firstParent = current[fieldName].getRefRecord();
	
	//If we have a valid record in the parent field check circular relationships
	if(firstParent && firstParent.isValidRecord()){
		checkCircularRelationship(firstParent, current, fieldName);
	}	
})(current, previous);


function checkCircularRelationship(parentRec, child, fieldName){
	var depth = 0;
	//Safety check to prevent endless loop in the event that a circular relationship already exists
	//Increase the current depth
	depth++;
	//If we have gone more than the allowed depth then abort
	if(depth > 20){
		current.setAbortAction(true);
		gs.addInfoMessage('Possible circular relationship already exists.  Aborting check to prevent endless loop.');
		return null;
	}
	
	//If the current parent being evaluated matches the child then abort
	if(parentRec.sys_id == child.sys_id){
		//Abort the submission
		gs.addErrorMessage('Relating to the chosen parent value creates a circular relationship.<br/>  Please choose a different parent.');
		current.setAbortAction(true);
	}
	else{
		//Check for next parent
		var nextParent = parentRec[fieldName].getRefRecord();
		if(nextParent && nextParent.isValidRecord()){
			//Test the next level
			checkCircularRelationship(nextParent, child, fieldName);
		}
	}
}

The post Prevent Circular Relationships in ServiceNow appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/business-rules-scripting/prevent-circular-relationships-servicenow/feed/ 9
Calendar or Schedule-based Incident Autoclose https://servicenowguru.com/business-rules-scripting/calendar-based-incident-autoclose/ https://servicenowguru.com/business-rules-scripting/calendar-based-incident-autoclose/#comments Mon, 24 Jan 2011 16:43:03 +0000 https://servicenowguru.wpengine.com/?p=2097 Service-now provides the ability to automatically move incidents marked as ‘Resolved’ into a ‘Closed’ state after a certain number of days. In my experience I’ve found that this type of resolution/closure workflow is really the best way to configure your incident management setup because it allows end-users the ability to reopen incidents within a certain

The post Calendar or Schedule-based Incident Autoclose appeared first on ServiceNow Guru.

]]>
Service-now provides the ability to automatically move incidents marked as ‘Resolved’ into a ‘Closed’ state after a certain number of days. In my experience I’ve found that this type of resolution/closure workflow is really the best way to configure your incident management setup because it allows end-users the ability to reopen incidents within a certain window (while they’re still marked as ‘Resolved’) but it also ensures that eventually all of the incident tickets move to a ‘Closed’ state where they won’t be reopened so that you can accurately calculate SLAs and reporting metrics.

The key piece to this auto close functionality is the ‘incident autoclose’ business rule on the ‘Incident’ table. It works in conjunction with the property shown here – that sets the number of days after which a resolved incident will be moved to a closed state. The ‘incident autoclose’ script works great but it is based off of a basic date calculation that doesn’t take into account any business hours or holidays. Shown below are some modified versions of the ‘incident autoclose’ script that take into account the default system calendar (in the case of calendar-based autoclose), or your choice of any system schedule set up in your system (in the case of schedule-based autoclose).

Calendar-based Incident Autoclose Script

NOTE: Calendars have been replaced in favor of schedules in Service-now. This calendar-based script is provided simply for reference purposes for those who may be using this script already. I would advise you to use the schedule-based script below if at all possible because it allows you to specify a specific schedule for the calculation (giving you much more flexibility and control) rather than simply using the default system calendar.
// This script automatically closes incidents that are resolved
// and haven't been updated in a chosen number of days. You
// can set this number as a property in System Properties.
// If you want a comment placed in the incident, uncomment the
// 'gr.comments' line below.

var ps = gs.getProperty('glide.ui.autoclose.time');
var pn = parseInt(ps);

if (pn > 0) {
   var gr = new GlideRecord('incident');
   gr.addQuery('state', 6);
   gr.query();
   while(gr.next()){
      //Close any records that have not been updated in 'pn' number of days
      //Date difference calculated based on default system calendar
      var dif = gs.calDateDiff(gs.nowDateTime(), gr.sys_updated_on, true);
      if(dif >= pn*86400){
         gr.state = 7;
         //gr.comments = 'Incident automatically closed after ' + pn + ' days in the Resolved state.';
         gr.active = false;
         gr.update();
      }
   }
}

Schedule-based Incident Autoclose Script

This schedule-based script is the BEST solution if you are setting up any kind of autoclose functionality in your system. The use of schedules gives you much more flexibility to determine exactly how much time should pass before closing out a resolved ticket. Schedules can be managed by navigating to ‘System Scheduler->Schedules’ in the left navigation pane of your Service-now instance. One important thing to note is that ‘Number of days’ is actually the number of 24 hour periods!
// This script automatically closes incidents that are resolved
// and haven't been updated in a chosen number of days. You
// can set this number as a property in System Properties.
// If you want a comment placed in the incident, uncomment the
// 'gr.comments' line below.

var ps = gs.getProperty('glide.ui.autoclose.time');
var pn = parseInt(ps);

if(pn > 0){
   //Get a schedule by name to calculate duration
   var schedRec = new GlideRecord('cmn_schedule');
   schedRec.get('name', '8-5 weekdays');
   if (typeof GlideSchedule != 'undefined')
      var sched = new GlideSchedule(schedRec.sys_id);
   else
      var sched = new Packages.com.glide.schedules.Schedule(schedRec.sys_id);

   //Get the current date/time in correct format for duration calculation
   var actualDateTime = new GlideDateTime();
   actualDateTime.setDisplayValue(gs.nowDateTime());

   //Query for resolved incident records
   var gr = new GlideRecord('incident');
   gr.addQuery('state', 6);
   gr.query();
   while(gr.next()){
      //Close any records that have not been updated in 'pn' number of days
      //Date difference calculated based on specified schedule
      var dif = sched.duration(gr.sys_updated_on.getGlideObject(), actualDateTime).getDayPart();
      if(dif >= pn){
         gr.state = 7;
         gr.active = false;
         //gr.comments = 'Incident automatically closed after ' + pn + ' days in the Resolved state.';
         gr.update();
      }
   }
}

Schedule-based Incident Autoclose Script (BY HOURS!)

The fact that ‘Number of days’ is actually the number of 24 hour periods ends up being pretty confusing for people sometimes. As I’ve dealt with this issue several times now, I think the best way to do incident autoclose based on a schedule is to do the autoclose on an hourly basis. This eliminates the ‘Number of days’ confusion and it also gives you the added benefit of a bit more granularity. If you use this script, you should also make sure to change the ‘glide.ui.autoclose.time’ property description to hours instead of days.
// This script automatically closes incidents that are resolved
// and haven't been updated in a chosen number of hours. You
// can set this number as a property in System Properties.
// If you want a comment placed in the incident, uncomment the
// 'gr.comments' line below.

var ps = gs.getProperty('glide.ui.autoclose.time');
var pn = parseInt(ps);

if(pn > 0){
   //Get a schedule by name to calculate duration
   var schedRec = new GlideRecord('cmn_schedule');
   schedRec.get('name', '8-5 weekdays excluding holidays');
   if (typeof GlideSchedule != 'undefined')
      var sched = new GlideSchedule(schedRec.sys_id);
   else
      var sched = new Packages.com.glide.schedules.Schedule(schedRec.sys_id);
   
   //Get the current date/time in correct format for duration calculation
   var actualDateTime = new GlideDateTime();
   actualDateTime.setDisplayValue(gs.nowDateTime());
   
   //Query for resolved incident records
   var gr = new GlideRecord('incident');
   gr.addQuery('state', 6);
   gr.query();
   while(gr.next()){
      //Close any records that have not been updated in 'pn' number of hours
      //Date difference calculated based on specified schedule
      var difDay = sched.duration(gr.sys_updated_on.getGlideObject(), actualDateTime).getDayPart()*24;
      var difHour = sched.duration(gr.sys_updated_on.getGlideObject(), actualDateTime).getDurationValue().split(':')[0].substr(-2);
      var dif = difDay + parseInt(difHour.replace(/^[0]+/g,""));
     
      if(dif >= pn){
         gr.state = 7;
         gr.active = false;
         //gr.comments = 'Incident automatically closed after ' + pn + ' hours in the Resolved state.';
         gr.update();
      }
   }
}

The post Calendar or Schedule-based Incident Autoclose appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/business-rules-scripting/calendar-based-incident-autoclose/feed/ 27
Checking for Modified or Changed Fields in Script https://servicenowguru.com/client-scripts-scripting/checking-modified-fields-script/ https://servicenowguru.com/client-scripts-scripting/checking-modified-fields-script/#comments Thu, 20 Jan 2011 13:35:43 +0000 https://servicenowguru.wpengine.com/?p=3175 Working in Service-now, you’ll find that a lot of scripting tasks come down to identifying which fields changed on a form (client-side) or record (server-side). In this post, I’ll show you some different techniques to identify changed fields in both client-side, and server-side scripts. I’ll also show you a way that you can capture changed

The post Checking for Modified or Changed Fields in Script appeared first on ServiceNow Guru.

]]>
Working in Service-now, you’ll find that a lot of scripting tasks come down to identifying which fields changed on a form (client-side) or record (server-side). In this post, I’ll show you some different techniques to identify changed fields in both client-side, and server-side scripts. I’ll also show you a way that you can capture changed fields and values and print them in an email notification…without having to check every potential field in a record.

ServiceNow - Changed Fields

Identifying modified or changed fields in Client scripts

When looking at a form in Service-now, it’s usually pretty easy to identify the fields that have changed since the form loaded (as seen in the screen shot above). The green field indicators give a simple visual cue to the user. Behind the UI, the system is taking cues as well and firing off ‘onChange’ events and setting field parameters as a result. When you write on ‘onChange’ client script or a UI policy, you can tie into the events and take actions as a result. If at all possible, you should stick to ‘onChange’ client scripts or UI policies.
There are some special situations (typically on submission of a record) where you don’t have a special event that tells you something changed. In an ‘onSubmit’ scenario, you have to check for these changes yourself. A great example of this can be used is the ‘Dirty form’ navigation property (glide.ui.dirty_form_support) that can be turned on to display a message to users if they navigate away from a ‘dirty’ or changed form without first saving the record.

Client Script examples
If you just want to do a check to see if any value on the entire form changed (a dirty form), you can use ‘g_form.modified’ in an ‘onSubmit’ client script like this…

function onSubmit() {
  if(g_form.modified){
    return confirm("Values on the form have changed.\nDo you really want to save this record?");
  }
}

If you just want to check for changes to a few specific fields then you could use something like this in an ‘onSubmit’ client script…

function onSubmit() {
  var field1 = g_form.getControl('caller_id'); //Get the 'Caller' field control
  var field2 = g_form.getControl('short_description'); //Get the 'Short description' field control
 
  //See if the 'changed' attribute on either field is true
  if(field1.changed || field2.changed){
    return confirm("Values on the form have changed.\nDo you really want to save this record?");
  }
}

Checking for changes to variables is a bit more complex because the underlying element changes names. This script will check an individual variable…

var el_id = g_form.getControl("requested_for").id;
var orig_val = gel("sys_original."+ el_id).value;
var new_val = g_form.getValue("requested_for");
if(orig_val != new_val){
   alert("It's changed");
}

This script will check for all of the variables on the form to see if they have the ‘changed’ CSS class. It will return a count, so any count greater than 0 means that something on the form has been modified…

if($$('.changed').length > 0){
   return confirm("Values on the form have changed.\nDo you really want to save this record?");
}


Identifying modified or changed fields in Business rules

Business rules are server-side scripts, so there’s no form, no green indicator, and no ‘onChange’ event to tell you what happened. In these scenarios, business rules just care about what’s on its way to the record in the database.
Service-now includes some simple convenience methods for identifying changes to a record or field in a business rule. You may have seen these used in the ‘Condition’ field on many of the out-of-box business rules. All of the methods below return true or false based on changes to the ‘current’ record.

Record change: current.changes();
Field change: current.YOUR_FIELD_NAME_HERE.changes();
Field changes TO a value: current.YOUR_FIELD_NAME_HERE.changesTo(VALUE);
Field changes FROM a value: current.YOUR_FIELD_NAME_HERE.changesFrom(VALUE);

But what if you don’t know in advance what might change? What if you just need to return a list of changed fields or values? This might be one field, or 30 fields! I recently discovered the ‘GlideScriptRecordUtil’ class can get this type of information for you. You can use the following methods in a ‘before’ or ‘after’ business rule.

var gru = GlideScriptRecordUtil.get(current);
gru.getChangedFields(); //Returns an arrayList of changed field elements with friendly names
gru.getChangedFieldNames(); //Returns an arrayList of changed field elements with database names
gru.getChanges(); //Returns an arrayList of all change values from changed fields
Business Rule example
You can paste this script into a ‘before’ business rule on any table to display information messages about changed fields. This isn’t terribly useful, but does do a good job of showing how you can process changed field results.

//Display an information message for each change to the record
var gru = GlideScriptRecordUtil.get(current);
var changedFields = gru.getChangedFields(); //Get changed fields with friendly names
var changedValues = gru.getChanges(); //Get changed field values
//Convert to JavaScript Arrays
gs.include('j2js');
changedFields = j2js(changedFields);
changedValues = j2js(changedValues);

//Process the changed fields
for(var i = 0; i < changedValues.length; i++){
   var val = changedValues[i];
   if(val.getED().getType() == -1 && val.getJournalEntry(1)){
      //Print out last journal entry for journal fields
      gs.addInfoMessage(val.getJournalEntry(1));
   }
   else{
      //Print out changed field and value for everything else
      gs.addInfoMessage(changedFields[i] + ': ' + val.getDisplayValue());
   }
}

ServiceNow Changed Fields Business Rule

Printing all record changes in an email notification

One common use case I’ve seen for collecting all record changes in an update is to send all of those changes in an email notification. Without the use of the ‘GlideScriptRecordUtil’ methods described above, this really wasn’t practical. Here’s a solution I’ve come up with that allows you to accomplish this…

The first part is gathering these changes when you trigger your email notification event. Because the changes need to be captured at the time the record is updated or inserted, this needs to happen in a business rule. This script example collects all changed fields on an incident record and passes them as an event parameter to the ‘Incident Assigned to my Group’ notification.

//Collect all changed fields
var gru = GlideScriptRecordUtil.get(current);
var fields = gru.getChangedFieldNames(); //Get changed fields with database names
//Convert to JavaScript Array
gs.include('j2js');
fields = j2js(fields);

if (!current.assignment_group.nil() && current.assignment_group.changes()) {
   gs.eventQueue("incident.assigned.to.group", current, fields , '');
}

Once you’ve passed the event (with the changed fields parameter) to your notification, you can set up a mail script to iterate through the changed fields and print the results to the outgoing email.

One thing you’ll want to be careful of here is read access to the fields and values you’re printing out. There’s not a simple way to determine who should be able to read these fields in an email notification so if you’re sending an email to end users, you probably want to specify individual fields in a more targeted technique.
Changed fields:
<mail_script>
//Process the changed fields
var fields = event.parm1.split(',');
for(x in fields){
   //Get the field label, value, and type
   var field = fields[x];
   var fieldLabel = eval('current.' + field + '.getLabel()');
   var fieldVal = eval('current.' + field+ '.getDisplayValue()');
   var fieldType = eval('current.' + field + '.getED().getType()');

   if(fieldType == -1 && eval('current.' + field + '.getJournalEntry(1)')){
      //Print out last journal entry for journal fields
      template.print(eval('current.' + field + '.getJournalEntry(1)'));
   }
   else{
      //Print out changed field and value for everything else
      template.print(fieldLabel + ': ' + fieldVal + '\n');
   }
}
</mail_script>

The result of this mail script will look something like this…
Email Changed Fields

The post Checking for Modified or Changed Fields in Script appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/client-scripts-scripting/checking-modified-fields-script/feed/ 40
Fixing the Fatal Flaw of Before Query Business Rules https://servicenowguru.com/business-rules-scripting/fixing-before-query-business-rules-flaw/ https://servicenowguru.com/business-rules-scripting/fixing-before-query-business-rules-flaw/#comments Wed, 05 Jan 2011 16:30:07 +0000 https://servicenowguru.wpengine.com/?p=3137 While this customization may still be necessary in certain situations, ServiceNow handles this issue in Aspen builds or later without any customization. I’ve written before on SNCGuru about how ‘before query’ business rules can (and should) be used to secure row-level read access to records in Service-now. While this usually works perfectly, there is one

The post Fixing the Fatal Flaw of Before Query Business Rules appeared first on ServiceNow Guru.

]]>
While this customization may still be necessary in certain situations, ServiceNow handles this issue in Aspen builds or later without any customization.

I’ve written before on SNCGuru about how ‘before query’ business rules can (and should) be used to secure row-level read access to records in Service-now. While this usually works perfectly, there is one issue that I’ve seen come up continually that there hasn’t been a good fix for. Over the past few weeks, I’ve seen several incidents and questions about inactive users disappearing from reference fields in Service-now systems. You may have noticed this yourself when you’ve de-activated users or groups in your system. The culprit in these cases is the ‘user query’ or ‘group query’ business rule.

The recommended (but really not great) solution up until this point is to turn the business rule off and use a reference qualifier on the reference field that you need to see the user in. The reason this solution is a bad one is that there are over 300 user reference and list fields in your system! Not only is that a big pain (and a bad idea) to add that reference qualifier to all of those places, but it also does nothing for the countless places (modules, filters, reports, etc.) that have UI elements that work like reference fields but cannot be filtered with a reference qualifier! This isn’t a new problem, but I’ve come up with a new (and extremely simple) solution.

‘Before Query’ business rules usually serve one of two purposes…

  1. Preventing read access to a group of records (security)
  2. Removing records from view so you don’t have to look at irrelevant data in lookups

The problem I’ll touch on in this post arises when you use ‘before query’ business rules to deal with the second point above. A prime example is the ‘user query’ business rule on the ‘User (sys_user)’ table out-of-box. That business rule looks like this…

if (!gs.hasRole("admin")) {
   current.addQuery("active", "true");
}

What this business rule says, in essence, is that users with the ‘admin’ role can read all user records in the system, and everybody else can only read active user records. On the surface, this seems like it’s exactly what you want. There’s no reason for inactive user records to be getting in the way all of the time for your technicians and end users to have to filter through. Even if they can deal with that extra nuisance, you don’t want somebody accidentally logging a ticket for, or assigning a CI to, the ‘John Smith’ that left your company 2 years ago.

Although the goal is a good one, the end result can actually end up being a big mess. If you’ve ever had one of your ‘itil’ users open up an old incident for that long-since-inactive ‘John Smith’ account you’ve seen what happens. ANY of the potentially thousands of reference field entries or filter conditions that reference that inactive user will appear to be blank for anybody without the ‘admin’ role!

Here’s an example screenshot I created of an incident record. In this case, the same user (ITIL User) opened up an incident and is also the Caller and Assignee. Right after the incident was created however, ITIL User was made inactive. The next non-admin user to open the incident would see a record with blank entries in reference fields wherever the user was listed. The values (sys_ids) are still there, but the system isn’t able to query for the inactive user because the user performing the query isn’t an admin! This results in what looks like an empty field because there isn’t a display value shown for the referenced record.

Service-now user_query Problem

How to fix the problem…

So, the obvious question becomes, “How do I fix it?”. The fix is actually a pretty simple one, but I think it helps to understand how ‘before query’ business rules work first. When the system performs a query to display a list of records, a lookup filter, or a reference field display value, it constructs the query the same way you would if you were to write a GlideRecord query in your own script.

current = new GlideRecord('sys_user');
current.addQuery(QUERY_CONDITIONS_HERE);
current.query();

A ‘before query’ business rule on a given table gets run immediately before the ‘current.query()’ line…when the query actually gets executed. So a ‘before query’ business rule gets run before the query execution, but NOT before the initial query conditions get built. What this means is that you have a chance in your ‘before query’ business rule to evaluate whatever query conditions exist and make certain decisions about how to modify the query!
For this particular case, it means that I can check to see if the query is a generic query to bring up a list, or a specific query on a SYS_ID value to pull up a specific record. When the system queries for user records to display in reference fields, filters, and report conditions the query actually looks like this at the time it hits the ‘user_query’ business rule.

current = new GlideRecord('sys_user');
current.addQuery('sys_id', USER_RECORD_SYS_ID);

Notice the absence of the ‘current.query()’ line. When a ‘before query’ business rule is run, that line hasn’t happened yet. Poking around the GlideRecord API a bit, I was able to find a method that allowed me to get the encoded query and evaluate it. What I discovered is that any time the system makes queries for reference fields, etc. the encoded query string always starts with ‘sys_id=’.

So, here’s the fix. Simply wrap your ‘addQuery’ lines in your ‘before query’ business rule in an ‘if’ statement as shown below. Any specific record queries (like those for reference fields, filters, and report conditions) will be allowed. Any other list query (like a standard list of users in a record list or reference popup) will be filtered just like normal!

if (!gs.hasRole("admin")) {
   //Check to see if this is a query for a specific ID
   if (current.getEncodedQuery().indexOf('sys_id=') != 0) {
      current.addQuery("active", "true");
   }
}

The post Fixing the Fatal Flaw of Before Query Business Rules appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/business-rules-scripting/fixing-before-query-business-rules-flaw/feed/ 22
Calculating Change Dates From Change Tasks https://servicenowguru.com/business-rules-scripting/calculating-change-dates-change-tasks/ Mon, 21 Jun 2010 17:47:01 +0000 https://servicenowguru.wpengine.com/?p=1755 Change management in Service-now includes the use of a parent ‘Change request’ ticket and (usually) the use of multiple ‘Change task’ tickets that can be assigned to separate groups or individuals to perform specific pieces of work. Because these change task tickets should factor into the overall plan for the parent change request, it often

The post Calculating Change Dates From Change Tasks appeared first on ServiceNow Guru.

]]>
Change management in Service-now includes the use of a parent ‘Change request’ ticket and (usually) the use of multiple ‘Change task’ tickets that can be assigned to separate groups or individuals to perform specific pieces of work. Because these change task tickets should factor into the overall plan for the parent change request, it often makes sense to take the expected start and end dates of change tasks into account when setting a planned start and end date for the parent change request. The purpose of this script is to allow you to automate the calculation of the planned start and end dates for the parent change request based on updates made to the child change tasks. Whenever an update is made to the expected start or end dates of a change task, this script will run and evaluate the start and/or end dates of the other change tasks for the same change request. The earliest start date and latest end date from the tasks are then populated as the overall planned start and end dates for the change request.

Sample Title

Since there is no ‘Expected end’ field out-of-box, this script assumes that you’ve created a date/time field named ‘u_expected_end’ on the ‘Change task’ table. ‘Expected start’ and ‘Expected end (u_expected_end)’ on the ‘Change task’ table are the basis for calculating ‘Planned start date (start_date)’ and ‘Planned end date (end_date)’ on the ‘Change request’ table.

Name: Calculate change request dates
Table: Change task (change_task)
When: After Insert/Update
Condition: current.expected_start.changes() || current.u_expected_end.changes()
Script:

calcChangeDates();

function calcChangeDates(){
//updateChange flag so we do not update unless necessary
var updateChange = false;

//Get the parent change to be updated
var chg = new GlideRecord('change_request');
chg.get(current.change_request);

//Query start times
var ts = new GlideRecord('change_task');
ts.addQuery('change_request', current.change_request);
ts.orderBy('expected_start');
ts.query();
while(ts.next()){
if(ts.expected_start && ts.expected_start.getNumericValue() != chg.start_date.dateNumericValue()) {
chg.start_date = ts.expected_start;
updateChange = true;
break;
}
}

//Query end times
var te = new GlideRecord('change_task');
te.addQuery('change_request', current.change_request);
te.orderByDesc('u_expected_end');
te.query();
while(te.next()){
if(te.u_expected_end && te.u_expected_end.getNumericValue() != chg.end_date.dateNumericValue()) {
chg.end_date = te.u_expected_end;
updateChange = true;
break;
}
}

//Update the parent change request if necessary
if(updateChange == true){
chg.update();
}
}

The post Calculating Change Dates From Change Tasks appeared first on ServiceNow Guru.

]]>
Readonly Variables on a Standard Form https://servicenowguru.com/client-scripts-scripting/variables-form-readonly/ https://servicenowguru.com/client-scripts-scripting/variables-form-readonly/#comments Sat, 22 May 2010 22:18:38 +0000 https://servicenowguru.wpengine.com/?p=1706 As of the ServiceNow Calgary release, this functionality is no longer necessary and, in fact, can cause some issues due to an unresolved bug in ServiceNow code. The best-practice method for making variables read only on standard forms post-Calgary is to use catalog UI policies and catalog client scripts along with the ‘Applies to’ checkboxes

The post Readonly Variables on a Standard Form appeared first on ServiceNow Guru.

]]>
As of the ServiceNow Calgary release, this functionality is no longer necessary and, in fact, can cause some issues due to an unresolved bug in ServiceNow code. The best-practice method for making variables read only on standard forms post-Calgary is to use catalog UI policies and catalog client scripts along with the ‘Applies to’ checkboxes available on those forms. You can also make catalog variables read only all of the time for specific roles by using the ‘Write roles’ field on a catalog item form.

A while ago I helped to answer a forum posting for someone who was looking for a way to present catalog variables to an end-user on a Request Item form but restrict the editing of those variables. At the time, I came up with a solution that worked, but that I really wasn’t happy with. I recently came across another forum posting where the poster wanted to do something similar. The difference for the most recent poster was that the variables were to be shown on an Incident form (which had been generated by a record producer). There were some subtle differences in the way variables were presented on the different forms that made my original script unusable for the incident form. So, I’ve had to revisit this topic to come up with a better solution and I’ve decided to compile all of the different options for making variables read only on a regular form. These solutions can be used in a variety of places, but will most often be applied to the Catalog Item, Catalog Task, Incident, or Change Request tables. Enjoy!


Check out this post if you’re interested in hiding empty variables in a variable editor on a standard form!

Locking down variables by role without a script…

Its probably best to avoid writing any script at all if you can to lock down access to variables. Service-now allows you to add roles to any variable in the system for this purpose. If you want to lock down variables without using a script, the solution can be found here. This solution is very simple but often doesn’t give you the type of flexibility that you need to restrict access to variables. It also requires you to set access to each and every variable in the system individually.

Locking down variables via business rules…

Probably the simplest way of locking down variables on a standard form via script is to create a business rule that checks to see if the variables have changed and then to abort the submission of the task record if they have changed. To do this, you just have to create a ‘before’ business rule on the table you want to restrict the editing of variables. The business rule should have a condition of ‘current.variable_pool.changes()’. You can put whatever you want in the script field but it should include ‘current.setAbortAction(“true”);’. If the user changes variable values and tries to submit the record they would get an alert telling them that they are not allowed to change variables. Here’s a sample…

Abort on Variable Change Business rule
Name: Abort on Variable Change
Table: Requested Item (or whatever table you want to restrict changes to)
When: Before
Update: True
Condition: current.variable_pool.changes()
Script:

//Add an information message, abort the submission, and reload the page
gs.addInfoMessage('You are not allowed to change variable values on this record.');
current.setAbortAction(true);
action.setRedirectURL(current);

The other option is to simply not show the variables at all and instead dump them into the work notes or comments fields. Here’s a script I found on the forums that takes all of the variables for a given item and sends them to the work notes field on that same item.

Copy Variables to Work Notes Business Rule
Name: Copy Variables to Work Notes
Table: Requested Item
When: Before
Insert: True
Script:

var wn = 'Variables:';
for (key in current.variables) {
var v = current.variables[key];
wn += '\n' + v.getGlideObject().getQuestion().getLabel() + ': ' + v.getDisplayValue();
}
current.work_notes = wn;

Locking down variables via client scripting…

Service-now actually provides a simple way to make a variable on a standard task form read only via client scripting. If you just need to disable one or two variables on a single item then this is probably the best scripting option. The solution is documented here.

More often than not however, if you are disabling variables at all, you are disabling them entirely or disabling them for a certain group of users. For that case, you could use a script like this one to lock down all of the variables on a form. This script looks for the ‘Variables’ label on the variable formatter and disables the variables associated with it. It is designed to work on any task table in Service-now.

All Variables Readonly Client Script
Name: All Variables Readonly
Table: Incident, Change request, Request item, Catalog task, wherever!
Type: onLoad

function onLoad(){
try{
//Get the 'Variables' section
var ve = $('variable_map').up('table');
//Disable all elements within with a class of 'cat_item_option'
ve.select('.cat_item_option', '.slushselectmtm', '.questionsetreference', '.form-control', '.checkbox').each(function(elmt){
elmt.disabled = true;
});
//Remove any reference or calendar icons
ve.select('img[src*=reference_list.gifx]', 'img[src*=small_calendar.gifx]').each(function(img){
img.hide();
});
//Hide list collector icons
ve.select('img[src*=arrow]').each(function(img){
img.up('table').hide();
});
}
catch(e){}
}

The post Readonly Variables on a Standard Form appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/client-scripts-scripting/variables-form-readonly/feed/ 82