Loading

Useful Scheduling Scripts

From ServiceNow Wiki
Home > Script > Useful Scripts > Useful Scheduling Scripts
Jump to: navigation, search

This is a searchable version of the Useful Scheduling Scripts. For an easy-to-navigate version, visit the Useful Scripts portal.



Role required
Functionality described here requires the Admin role.
As-is functionality
Caution: The customization described here was developed for use in specific ServiceNow instances, and is not supported by ServiceNow Customer Support. This method is provided as-is and should be tested thoroughly before implementation. Post all questions and comments regarding this customization to our community forum.

Name: Calculate Duration Given a Schedule

Type: Before Update/Insert Business Rule

Table: Incident

Description: A Business Duration calculates the Open to Close duration on an incident based on the particular schedule. If there is no schedule specified, the script will simply use the first schedule returned by the query.

The example below sets the Resolved Duration field when the incident state moves to Resolved:

Condition: current.incident_state.changesTo(6)

Parameters:

Script:

Note
Note: This API call changed in the Calgary release:
  • GlideSchedule replaces Packages.com.glide.schedules.Schedule

The new script object calls apply to the Calgary release and beyond. For releases prior to Calgary, substitute the packages call as described above. Packages calls are not valid beginning with the Calgary release. For more information, see Scripting API Changes.

if (current.incident_state == 6) { 
var dur = calcDurationSchedule(current.opened_at,current.sys_updated_on);
current.u_resolved_duration = dur;
 
function calcDurationSchedule(start, end) {
// Get the user
var usr = new GlideRecord('sys_user');
usr.get(gs.getUserID());
// Create schedule - pass in the sys_id of your standard work day schedule and pass in the users timezone
var sched = new GlideSchedule('08fcd0830a0a0b2600079f56b1adb9ae',usr.time_zone);
// Get duration based on schedule/timezone
return (sched.duration(start.getGlideObject(), end.getGlideObject()));
}
Role required
Functionality described here requires the Admin role.
As-is functionality
Caution: The customization described here was developed for use in specific ServiceNow instances, and is not supported by ServiceNow Customer Support. This method is provided as-is and should be tested thoroughly before implementation. Post all questions and comments regarding this customization to our community forum.

Name: Check Upcoming Termination Dates

Type: Scheduled Script

Table:

Description: This script checks nightly for termination dates on contracts coming up in 90, 50, or 10 days (depending on the contract duration field).

Parameters:

Script:

function contractNoticeDue() {
var gr = new GlideRecord("contract");
gr.addQuery("u_contract_status", "Active");
gr.query();
while (gr.next()) {
  if ((gr.u_termination_date <= gs.daysAgo(-90)) && (gr.u_contract_duration == "Long")) {
    gr.u_contract_status = "In review";
  } 
  else if ((gr.u_termination_date <= gs.daysAgo(-50)) && (gr.u_contract_duration == "Medium")) {
    gr.u_contract_status = "In review";
    } 
  else if ((gr.u_termination_date <= gs.daysAgo(-10)) && (gr.u_contract_duration == "Short")) {
    gr.u_contract_status = "In review";
    }
  gr.update();
  }
 
}

Contents

1 Overview

A business rule script specifies the actions that the business rule takes. Scripts commonly include predefined global variables to reference items in your system, such as the current record. Global variables are available to all business rules.

Use scripts in business rules to accomplish common tasks such as:

With scripts, you can also:

You can also utilize the system's scripting functionality available for server-side scripts. See the useful scripts portal for additional example scripts.

You can use options on the Business Rules form to build conditions, set field values, and display alert messages without needing to write a script starting with the Eureka release.

2 Using Predefined Global Variables

Use the following predefined global variables to reference the system in a business rule script:

  • current: The current record being referenced.
  • previous: The record before any changes were made, available on update and delete operations. This is not available on asynch operations.
  • g_scratchpad: Scratchpad object available on display rules, used to pass information to the client to be accessed from client scripts.
  • system (or gs): References to GlideSystem functions.

Variables, such as current, previous, and g_scratchpad are global across all business rules that run for a transaction. User-created variables are also globally scoped by default. If a new variable is declared in an order 100 business rule, the business rule that runs next at order 200 also has access to the variable. This may introduce unexpected behavior.

The recommended method for preventing such unexpected behavior is to always wrap your code in a function. This protects your variables from conflicting with system variables or global variables in other business rules that are not wrapped in a function. Additionally, variables such as current must be available when a function is invoked in order to be used.

This is an example of a script that is vulnerable to conflicts with other code. If the variable gr is used in other rules, the value of the variable may unexpectedly change.

var gr = new GlideRecord('incident');
gr.query();
while (gr.next()) {
 
  //do something
 
}

When this script is wrapped in a function, the variable is available only within the function and does not conflict with other functions using a variable named gr.

myFunction();
 
function myFunction() { 
  var gr = new GlideRecord('incident');
  gr.query();
  while (gr.next()) {
    //do something
  }
}

3 Comparing Two Date Fields

It is possible to compare two date fields or two date and time fields in a business rule, and abort a record insert or update if they are not correct. For example, you may want a start date to be before an end date.

The following is an example script:

if ((!current.u_date1.nil()) && (!current.u_date2.nil())) {
  var start = current.u_date1.getGlideObject().getNumericValue();
  var end = current.u_date2.getGlideObject().getNumericValue();
  if (start > end) {
    gs.addInfoMessage('start must be before end');
    current.u_date1.setError('start must be before end');
    current.setAbortAction(true);
  }
}

As a good practice, make the business rule a before rule for insert and update actions. In the example above:

  • u_date1 and u_date2 are the names of the two date fields. Replace these your own field names.
  • The first line checks that both fields actually have a value.
  • The next two lines create variables that have the dates' numerical values.
  • The next two lines create different alert messages for the end user: one at the top of the form and one by the u_date1 field in the form.
  • The last line aborts the insert or update if the date fields are not correct.

Here is a more complex example of the above comparison. If you have more than one pair of start and end dates, you can use arrays as shown. Additionally, this script requires the input dates to be within a certain range, in this case, no fewer than 30 days in the past and no more than 365 days in the future.

// Enter all start and end date fields you wish to check, as well as the previous values
// Make sure that you keep the placement in the sequence the same for all pairs
var startDate = new Array(current.start_date,current.work_start);
var prevStartDate = new Array(previous.start_date,previous.work_start);
var endDate = new Array(current.end_date,current.work_end);
var prevEndDate = new Array(previous.end_date,previous.work_end);
// the text string below is added to the front of ' start must be before end'
var userAlert = new Array('Planned','Work');
 
// Set the number of Previous Days you want to check
var pd = 30;
// Det the number of Future Days you want to check
var fd = 365;
 
// You shouldn't have to modify anything below this line
 
var nowdt = new GlideDateTime();
nowdt.setDisplayValue(gs.nowDateTime());
var nowMs = nowdt.getNumericValue();
var pdms = nowMs;
//subtract the product of previous days to get value in milliseconds
pdms -= pd*24*60*60*1000;
var fdms = nowMs;
//add the product of future days to get value in miliseconds
fdms += fd*24*60*60*1000;
var badDate = false;
 
//Iterate through all start and end date / time fields
for(x = 0; x < startDate.length; x++) {
  if ((!startDate[x].nil()) && (!endDate[x].nil())) {
  var start = startDate[x].getGlideObject().getNumericValue();
  var end = endDate[x].getGlideObject().getNumericValue();
    if (start > end) {
      gs.addInfoMessage(userAlert[x] + ' start must be before end');
      startDate[x].setError(userAlert[x] + ' start must be before end');
      badDate = true;
    }
    else if ((prevStartDate[x]) != (startDate[x])){
      if (start < pdms){
        gs.addInfoMessage(userAlert[x] + ' start must be fewer than ' + pd + ' days ago');
        startDate[x].setError(userAlert[x] + ' start must be fewer than ' + pd + ' days ago');
        badDate = true;
      }
    }
    else if ((prevEndDate[x]) != (endDate[x])){
      if (end > fdms){
        gs.addInfoMessage(userAlert[x] + ' end must be fewer than ' + fd + ' days ahead');
        endDate[x].setError(userAlert[x] + ' end must be fewer than ' + fd + ' days ahead');
        badDate = true;
      }
    }
  }
}
if(badDate == true){
current.setAbortAction(true);
}


4 Parsing XML Payloads

Fields that get inserted into the database in XML format, such as the payload of an ecc_event row, can be parsed with the system's getXMLText function. The getXMLText function takes a string and an XPATH expression. For example:

var name =  gs.getXMLText("<name>joe</name>", "//name");

returns the string 'joe'.

Assuming that the field "payload" contains XML, the function call might look like:

var name =  gs.getXMLText(current.payload, "//name");

For information on XPATH, visit schools.

5 Aborting a Database Action in a Business Rule

During a before business rule script, you can cancel or abort the current database action using the current.setAbortAction(true) method. For example, if the before business rule is executed during an insert action, and you have a condition in the script that calls current.setAbortAction(true), the new record stored in current is not created in the database.

6 Determining the Operation that Triggers the Business Rule

You can write a script for a business rule that is triggered on more than one database action. If you want the business rule script to dynamically branch depending on the action that triggered the event, you can use the operation() function. For example:

if( current.operation() == "update" ) {
  current.updates ++;
}
if( current.operation() == "insert") {
  current.updates = 0;
}

7 Scripting with Display Business Rules

Display rules are processed when a user requests a record form. The data is read from the database, display rules are executed, and the form is presented to the user. The current object is available and represents the record retrieved from the database. Any field changes are temporary since they are not yet submitted to the database. To the client, the form values appear to be the values from the database; there is no indication that the values were modified from a display rule. This is a similar concept to calculated fields.

The primary objective of display rules is to utilize a shared scratchpad object, g_scratchpad, which is also sent to the client as part of the form. This can be useful when you need to build client scripts that require server data that is not typically part of the record being displayed. In most cases, this would require a client script making a call back to the server. If the data can be determined prior to the form being displayed, it is more efficient to provide the data to the client on the initial load. The form scratchpad object is an empty object by default, and used only to store name:value pairs of data.

To populate the form scratchpad with data from a display rule:

//from display business rule
g_scratchpad.someName = "someValue";
g_scratchpad.anotherName = "anotherValue";
 
//if you want the client to have access to record fields not being displayed on the form
g_scratchpad.created_by = current.sys_created_by; 
 
//these are simple examples, in most cases you'll probably perform some other queries to test or get data

To access the form scratchpad data from a client script:

//from client script
if (g_scratchpad.someName == "someValue") {
  //do something special
}

8 Scripting an OR Condition

An OR condition can be added to any query part within a business rule with the addOrCondition() method. The example below shows a query for finding all the incidents that have either a 1 or a 2 priority. The first addQuery() condition is defined as a variable and is used in the OR condition.

var inc = new GlideRecord('incident');
var qc = inc.addQuery('priority', '1'); 
qc.addOrCondition('priority', '2');
inc.query();
while (inc.next()) {
   // processing for the incident goes here
}

The following script is a more complex example, using two query condition variables doing the equivalent of (priority = 1 OR priority = 2) AND (impact = 2 OR impact = 3). The results of the OR condition are run with two variables, qc1 and qc2. This allows you to manipulate the query condition object later in the script, such as inside an IF condition or WHILE loop.

 
var inc = new GlideRecord('incident');
var qc1 = inc.addQuery('priority', '1');
qc1.addOrCondition('priority', '2'); 
var qc2 = inc.addQuery('impact', '2'); 
qc2.addOrCondition('impact', '3'); 
inc.query(); 
while (inc.next()) {
   // processing for the incident goes here 
}

9 An Example Script: Locking User Accounts

The following business rule script locks user accounts if the user is not active in the LDAP directory and the user does not have self-service, itil, or admin access to the ServiceNow instance:

// Lock accounts if bcNetIDStatus != active in LDAP and user does not 
// have self-service, itil or admin role
var rls = current.accumulated_roles.toString(); 
if ( current.u_bcnetidstatus == 'active' && 
   ( rls.indexOf(',itil,')  > 0 || 
     rls.indexOf(',admin,') > 0 || 
     rls.indexOf(',ess,') > 0 ) ) { 
  current.locked_out = false; 
} 
else { 
  current.locked_out = true; 
} 
var gr = new GlideRecord ("sys_user"); 
gr.query(); 
while(gr.next()) { 
  gr.update(); 
  gs.print("updating " + gr.getDisplayValue()); 
}

10 An Example Script: A Default Before-Query Business Rule

You can use a query business rule that executes before the database query is made to prevent users from accessing certain records. Consider the following example from a default business rule that limits access to incident records.

  • Name: incident query
  • Table: Incident
  • When: before, query
  • Script:
if (!gs.hasRole("itil") && gs.isInteractive()) {
  var u = gs.getUserID();
  var qc = current.addQuery("caller_id", u).addOrCondition("opened_by", u).addOrCondition("watch_list", "CONTAINS", u);
  gs.print("query restricted to user: " + u);
}

This example prevents users from accessing incident records unless they have the itil role are listed in the Caller or Opened by field. So, for example, when self-service users open a list of incidents, they can only see the incidents they submitted.

Note
Note: You can also use access controls to restrict the records that users can see.
Role required
Functionality described here requires the Admin role.
As-is functionality
Caution: The customization described here was developed for use in specific ServiceNow instances, and is not supported by ServiceNow Customer Support. This method is provided as-is and should be tested thoroughly before implementation. Post all questions and comments regarding this customization to our community forum.

Name: Schedule Script for Weekdays

Type: Business Rules/Client Scripts

Table:

Description: This script schedules the script for weekdays. Insert any script where it says "Your Script Here."

Parameters:

Script:

var go = 'false'; 
var now = new Date(); 
 
// Correct time zone, which is by default GMT -7 
now.setHours (now.getHours() + 8); 
var day = now.getDay(); 
 
// No go on Saturday or Sunday 
if (day != 0 && day != 6) {
 
  // (your script here)
 
}
Role required
Functionality described here requires the Admin role.
As-is functionality
Caution: The customization described here was developed for use in specific ServiceNow instances, and is not supported by ServiceNow Customer Support. This method is provided as-is and should be tested thoroughly before implementation. Post all questions and comments regarding this customization to our community forum.

Name: Set Date Field According To Current Date

Type:

Table:

Description: This script sets a date field depending on the current day of the week. In this example, if the day is Monday through Wednesday, it sets the date to this coming Monday; otherwise it sets the date field to next Monday.

Parameters:

Script:

Note
Note: This API call changed in the Calgary release:
  • GlideDateTime replaces Packages.com.glide.glideobject.GlideDateTime

The new script object calls apply to the Calgary release and beyond. For releases prior to Calgary, substitute the packages call as described above. Packages calls are not valid beginning with the Calgary release. For more information, see Scripting API Changes.

function setCabDate() {
var today = new Date();
var thisDay = today.getDay(); //returns 0 for Sunday, 1 for Monday, etc. thru 6 for Saturday.
var thisMon = new GlideDateTime();
thisMon.setDisplayValue(gs.beginningOfThisWeek());
var nextMon = thisMon.getNumericValue();
nextMon += (1000*60*60*24*7);
 
 if ((thisDay < 4) && (thisDay > 0)) //if today is Mon thru Wed (thisDay = 1, 2, or 3), set cab to this coming Monday.
  current.u_req_cab_rev_date.setDateNumericValue(thisMon.getNumericValue());
else if ((thisDay >= 4) || (thisDay == 0)) //if today is Thurs thru Sun (thisDay = 4, 5, 6, or 0), set cab to next Monday.
  current.u_req_cab_rev_date.setDateNumericValue(nextMon);
 
}

To validate the input of all date/time fields, you can use the following in a validation script (System Definition > Validation Scripts). Because the date/time format is hard coded in this script, it must match your instance's date/time format. If your instance's date/time format changes, you must update your validation script.

Set the validation script's type to Date/Time. Then, with this validation script, if a user enters an incorrect format in a date/time field, they will receive an error message.

function validate(value) { 
// empty fields are still valid dates 
if(!value) 
return true; 
 
// We "should" have the global date format defined always defined. but there's always that edge case... 
if(typeof g_user_date_time_format !== 'undefined') 
return isDate(value, g_user_date_time_format); 
 
// if we don't have that defined, we can always try guessing 
return parseDate(value) !== null; 
}

DateTimeValidation1.png

Was this article helpful?
Yes, I found what I needed
No, I need more assistance
Views
Personal tools