Please use docs.servicenow.com for the latest documentation.

This site is for reference purposes only and may not be accurate for the latest ServiceNow version

Scripting in Business Rules

From Wiki Archive
Jump to: navigation, search
Note
Note: This article applies to Fuji and earlier releases. For more current information, see Business Rules at http://docs.servicenow.com

The ServiceNow Wiki is no longer being updated. Visit http://docs.servicenow.com for the latest product documentation.

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.

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.

<source lang="javascript"> var gr = new GlideRecord('incident'); gr.query(); while (gr.next()) {

 //do something

} </source>

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.

<source lang="javascript"> myFunction();

function myFunction() {

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

} </source>

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: <source lang="javascript">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);
 }

} </source> 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.

<source lang="javascript">// 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); }</source>


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: <source lang="javascript">var name = gs.getXMLText("<name>joe</name>", "//name");</source> returns the string 'joe'.

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

<source lang="javascript">var name = gs.getXMLText(current.payload, "//name");</source> For information on XPATH, visit schools.

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.

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:

<source lang=javascript> if( current.operation() == "update" ) {

 current.updates ++;

} if( current.operation() == "insert") {

 current.updates = 0;

} </source>

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: <source lang="javascript"> //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 </source>

To access the form scratchpad data from a client script: <source lang="javascript"> //from client script if (g_scratchpad.someName == "someValue") {

 //do something special

} </source>

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. <source lang="javascript">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

} </source>

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. <source lang="javascript"> 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 

} </source>

An Example Script: Locking User Accounts

The following business rule script locks user accounts if the user is not active in the LDAP directory or the user does not have self-service, itil, or admin access to the ServiceNow instance: <source lang="javascript"> // 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()); 

}</source>

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:

<source lang="javascript"> 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);

} </source> 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.