Sunday, July 06, 2014

ServiceNow: gs.dateDiff() returns the wrong results (sorta)

Do you see what is wrong with this code?
var date1 = new GlideDateTime(); 
var date2 = new GlideDateTime(); 
var date3 = new GlideDateTime();
date1.setDisplayValueInternal('2011-08-26 17:12:28'); 
date2.setDisplayValueInternal('2011-08-30 12:33:10'); 
date3.setDisplayValueInternal('2011-08-30 12:43:10'); 
gs.print(gs.dateDiff(date1, date2, true)); 
gs.print(gs.dateDiff(date1, date3, true));
If you run the code, you'll see that gs.dateDiff() returns the same value for the difference between date1/date2 and date1/date3 even though there is a 10 minute difference.  Why is it rounding to the full number of days?  

Well, it turns out that the default string conversion is not what gs.dateDiff() is expecting.  Instead, it is expecting the date/time string in the user's display format.  

(so if your default date / timeformat matches the internal, then you'd actually get the correct results and wonder what I'm complaining about.) 

Instead of just failing, gs.dateDiff() seems to do the math where it can (e.g. the difference in days). This seems a poor choice in that you are giving the impression of valid results when the results are not accurate.

What you need to do to make sure you get the correct results is the following:

gs.print(gs.dateDiff(date1.getDisplayValue(), date2.getDisplayValue(), true));
gs.print(gs.dateDiff(date1.getDisplayValue(), date3.getDisplayValue(), true));
getDisplayValue() converts the internal date format to the one expected in the execution context.

Saturday, July 05, 2014

Servicenow: obvious in hindsight

Another 'duh' moment.

The use of gs.addInfoMessage() will not work with business rules set as 'async'. If you want the message, you need to wait and use 'after'.

Yeah, this is obvious in hindsight as the execution of the BR may not immediately occur so you can't get the message to appear on the next loaded page. 

Sunday, June 29, 2014

ServiceNow Survey Wizards: Redirect at the end of the survey

You would think it would be really simple to specify where you want to go when you are done with a Survey created by a Survey Wizard.  You'd be mostly right.

If you select the Survey Wizard in question, you'll notice that there is nothing on the page, because, well, that'd be too easy.  You'll need to Personalize the form and add 'Redirect' to the form. After that, it is a 40 character (beware long urls) string field that you can put in a url that you will be redirected to at the end of the survey.

Do not waste your time by trying to use a redirect panel. They don't seem to work. If you do go down the redirect panel path and wonder why they aren't showing up on the main survey wizard panel under 'survey panels', that's because you need to personalize the form and add 'redirect panels' as its own tab. 

What would have been nicer is if everything were hidden. That way, you'd know to go look.  Rolling 1d4 to see if the field should be hidden seems bordering on cruel and inhumane.

If you are on Eureka, it looks like there is a newer survey mechanism so all this may be moot.

Tuesday, June 24, 2014

[LDAP: error code 12 - Unavailable Critical Extension] in ServiceNow Eureka

The new Eureka release for ServiceNow supports persistent search for non Active Directory servers. Woo!

However, you enable the listener and you get the error: "[LDAP: error code 12 - Unavailable Critical Extension]"  Boo!

Turns out that when you configured your LDAP server ages ago, you may have set the 'Vendor' field to be 'Active Directory' because, hey, that was the default or you wanted to see if changing the vendor field did anything.  Since it didn't, you may have forgotten to change it back.  It doesn't help that the server configuration page doesn't show this field (so you probably want to add it back to the page).

After changing the vendor to 'Other' (the only other choice), the error doesn't seem to be occurring now.  Now, the fun part to see how persistent search interacts on imports.

Saturday, March 15, 2014

ServiceNow – Why is GlideRecord insert() returning a null

You’re trying to save your spiffy new record but var incSid = current.insert() is returning null.  Boo. 

To make matters more fun, this code is an Inbound Email Action.  One might suspect that the guy responsible for anything with the letters e-m-a-i-l in ServiceNow must really like creating puzzles or hate wasting disk space on silly things known as error messages.

The first thought is that it could be an ACL issue.  You don't have create rights to the table so that must be why it is failing.  The only problem with that theory is that you are an admin and  so theoretically the code is running in your authorization context.

So what now?  Bad data maybe?  Ok, so off to our friend 'Background Scripts'.  Let's try a field that doesn't exist
var x = new GlideRecord('incident');
x.u_something_that_cant_exist = true;

Well, that returned a sys_id and not null. But it did seem to return some log messages.

So, this was a fruitful path to investigate. I went ahead and replaced incident with the table in question and voila, we get an error message when running the background script:
Background message, type:error, message: Data Policy Exception:  user is mandatory 
*** Script: null
and there we go. The problem was that it wasn't a bad field but missing data in a mandatory field.

Monday, February 10, 2014

ServiceNow - find the bug

You have a spiffy email notification. Instead of it firing, you get
For input string: "${description}
  So let's go look at the 'What it will contain'  and we have
Your incident ${number} has updated information:
Short description: ${short_description}
Updated Information: ${comments)
Original Description: ${description}
Priority: ${priority}

Find the bug?


Yeah, I know. Syntax checkers are for the weak and feeble.

Sunday, December 08, 2013

ServiceNow - when the "true|false" type isn't

Let's say you have a system property defined in ServiceNow with a type of "true | false".

To access the property, you find the gs.getProperty() call at  You then have something like:
var isValue = gs.getProperty("");

if (isValue) {
  gs.log("is true");
} else {
  gs.log("is false");
What's the bug in the code?
gs.log(typeof isValue)
returns "string".

gs.getProperty() will always return a string value.  Instead of it being documented at the link above, you can see this in the table dictionary definition and it is documented at

Maybe I'm just old but I really don't like dynamically typed languages, especially when you have almost no debugging environment to speak of.