├── README.md ├── Make All Fields Read Only.js ├── paging.js ├── User Functions.js ├── Controlling Tabs.js ├── ESS User Redirect.js ├── Quorum Approval.js ├── Working With Journals.js ├── Mass Email Address Update.js ├── Array Query with SysIDs.js ├── Reboot a Workflow ├── Callback Example.js ├── Custom URL.js ├── REST Example.js ├── Create H&S User on vaccine submission.js ├── ESC link for notifications.js ├── KM Expiration Reminder.js ├── past date validation.js ├── Start and End Date Validation.js ├── Tickets from same location.js ├── XML Example 1 ├── Requested Item Due Date.js ├── Two Step UI Action.js ├── Rest_Call.py ├── Convert Duration to Hours, Minutes, Seconds.js ├── System Cleanup.js ├── Change Control Cutoff.js ├── Clean System.js ├── AJAX Example.js ├── Advanced Ref Qualifier.js ├── rollback flow approvals.js ├── Oncall PopUp.xml ├── cc_VaccinationAjaxProcessor.js ├── Billable Time Rollup.js ├── CSV Field to Files Import.js ├── Personal Email.js ├── Send email to personal email ├── AJAX Example JSON return.js ├── Validate second vaccination dose date.js └── XML Example 2.js /README.md: -------------------------------------------------------------------------------- 1 | # ServiceNow 2 | Useful ServiceNow scripts by Sonnyducks 3 | -------------------------------------------------------------------------------- /Make All Fields Read Only.js: -------------------------------------------------------------------------------- 1 | //client script 2 | var fields = g_form.getEditableFields(); 3 | for (var x = 0; x < fields.length; x++) { 4 | g_form.setReadOnly(fields[x], true); 5 | } 6 | -------------------------------------------------------------------------------- /paging.js: -------------------------------------------------------------------------------- 1 | var userCount = 6770; 2 | var usersPerPage = 5000; 3 | var pages = Math.ceil((userCount / usersPerPage)); 4 | 5 | var i = 0; 6 | while (i' with the name of the tab you want to hide 6 | var section = $$('span[tab_caption=""]')[0].select('span[id*=section.]')[0]; 7 | section.hide(); 8 | -------------------------------------------------------------------------------- /ESS User Redirect.js: -------------------------------------------------------------------------------- 1 | USE: UI Script; if a user has no roles force them to the ESS site 2 | 3 | addLoadEvent(ESSUserRedirect); 4 | function ESSUserRedirect() { 5 | if(g_user.userName != "guest"){ 6 | if(!g_user.hasRoles()){ 7 | if(document.URL.indexOf('ess')==-1) { 8 | var url = "https://" + window.location.host + "/ess"; 9 | window.location.href = url; 10 | } 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Quorum Approval.js: -------------------------------------------------------------------------------- 1 | //USE: Workflow approval activity script 2 | 3 | var percentApproved = counts.approved / counts.total * 100; 4 | var percentRejected = counts.rejected / counts.total * 100; 5 | var percentResponded = (counts.approved + counts.rejected) / counts.total * 100; 6 | if (percentResponded > 50) { 7 | if (percentApproved > 50) 8 | answer = 'approved'; 9 | 10 | if (percentRejected > 50) 11 | answer = 'rejected'; 12 | } 13 | -------------------------------------------------------------------------------- /Working With Journals.js: -------------------------------------------------------------------------------- 1 | //get last update 2 | current.work_notes.getJournalEntry(-1); 3 | 4 | //process all journal updates 5 | var notes = current.work_notes.getJournalEntry(-1); //gets all journal entries as a string where each entry is delimited by '\n\n' 6 | var na = notes.split("\n\n");                       //stores each entry into an array of strings 7 | 8 | for (var i = 0; i < na.length; i++)                 //loop thru array and do something 9 |   gs.print(na[i]); 10 | -------------------------------------------------------------------------------- /Mass Email Address Update.js: -------------------------------------------------------------------------------- 1 | //Use: Fix script, to update all email domains in the user table 2 | //match, relace using regular expression 3 | 4 | var usr = new GlideRecord('sys_user'); 5 | usr.addEncodedQuery('emailLIKEbad_domain.com'); 6 | usr.query(); 7 | while (usr.next()) { 8 | //gs.print('OLD EMAIL: ' + usr.email); 9 | var r = new SNC.Regex('/bad_domain/'); 10 | usr.email = r.replaceAll(usr.email, 'new_domain'); 11 | usr.update(); 12 | //gs.print('NEW EMAIL: ' + usr.email); 13 | } 14 | -------------------------------------------------------------------------------- /Array Query with SysIDs.js: -------------------------------------------------------------------------------- 1 | //This finds all locations that are supported by a contract 2 | 3 | var arrayUtil = new ArrayUtil(); 4 | var locIDs = new Array(''); 5 | var i = 0; 6 | var co = new GlideRecord('service_contract'); 7 | co.addQuery('u_po_number', parent.u_contract_number); 8 | co.query(); 9 | while (co.next()) { 10 | if (arrayUtil.indexOf(locIDs, co.u_location.sys_id) == -1){ 11 | locIDs[i] = co.u_location.sys_id; 12 | i++; 13 | } 14 | } 15 | 16 | gFile.addEncodedQuery('sys_idIN'+locIDs); //some made up var 17 | -------------------------------------------------------------------------------- /Reboot a Workflow: -------------------------------------------------------------------------------- 1 | //This was used to 'Reboot a Change' 2 | 3 | //reset workflow 4 | new Workflow().restartWorkflow(current, false); 5 | 6 | //remove all approvals 7 | var approvals = new GlideRecord('sysapproval_approver'); 8 | approvals.addQuery('document_id', current.sys_id); 9 | approvals.deleteMultiple(); 10 | 11 | //update change 12 | current.comments = 'Change reset by user.'; 13 | current.state = -12; 14 | current.update(); 15 | //action.setRedirectURL(current); 16 | gs.addInfoMessage('Workflow has been reset. All approvals have been deleted.'); 17 | -------------------------------------------------------------------------------- /Callback Example.js: -------------------------------------------------------------------------------- 1 | //USE: Client script, fill urgency from a custom field on the CI whenever the CI changes. 2 | 3 | function onChange(control, oldValue, newValue, isLoading, isTemplate) { 4 | if (isLoading || newValue == '') { 5 | return; 6 | } 7 | 8 | //Type appropriate comment here, and begin script below 9 | var ci = g_form.getReference('cmdb_ci', setUrgency); 10 | 11 | } 12 | 13 | function setUrgency(ci) { 14 | if (ci.u_bus_criticality != '') { 15 | g_form.setValue('u_change_urgency', ci.u_bus_criticality); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Custom URL.js: -------------------------------------------------------------------------------- 1 | //USE: Mail Script to create custom URL, for when URI_REF won't work...typically need custom text for link. 2 | //Click here to view Incident: ${URI_REF} 3 | //Click here to view Related Problem: ${problem_id.URI_REF} 4 | 5 | var tbl = current.getTableName(); 6 | var sysID = current.sys_id; 7 | var link = createLinkForObject(tbl,sysID); 8 | template.print(link); 9 | function createLinkForObject(strTableName, strSysID){ 10 | return 'LINK TEXT HERE'; 11 | } 12 | -------------------------------------------------------------------------------- /REST Example.js: -------------------------------------------------------------------------------- 1 | var refNumber = 'INC0000001'; 2 | var request = new sn_ws.RESTMessageV2(); 3 | request.setEndpoint('https://YOUR_INSTANCE.service-now.com/api/now/table/incident?sysparm_query=&sysparm_limit=1&number=' + refNumber); 4 | request.setHttpMethod('GET'); 5 | 6 | //Eg. UserName="admin", Password="admin" for this code sample. *NEEDS UPDATING 7 | var user = 'admin'; 8 | var password = 'admin'; 9 | 10 | request.setBasicAuth(user,password); 11 | request.setRequestHeader("Accept","application/json"); 12 | 13 | var response = request.execute(); 14 | gs.log(response.getBody()); 15 | -------------------------------------------------------------------------------- /Create H&S User on vaccine submission.js: -------------------------------------------------------------------------------- 1 | //CC: Force a H&S User created when a vaccination is submitted. 2 | var hsUsr = new GlideRecord('sn_imt_core_health_and_safety_user'); 3 | hsUsr.addQuery('user', userId); 4 | hsUsr.setLimit(1); 5 | hsUsr.query(); 6 | 7 | if (hsUsr.next()) { 8 | //user has a H&S User record, do nothing 9 | } else { 10 | //create a H&S User record 11 | var newHSU = new GlideRecord('sn_imt_core_health_and_safety_user'); 12 | newHSU.initialize(); 13 | newHSU.setValue('user', userId); 14 | newHSU.insert(); 15 | } 16 | -------------------------------------------------------------------------------- /ESC link for notifications.js: -------------------------------------------------------------------------------- 1 | //NOTE: this URL is for HR Cases. 2 | //ENHANCEMENT: Need to adjust for other ticket types. 3 | 4 | (function runMailScript(/* GlideRecord */ current, /* TemplatePrinter */ template, 5 | 6 | /* Optional EmailOutbound */ email, /* Optional GlideRecord */ email_action, 7 | 8 | /* Optional GlideRecord */ event) { 9 | 10 | var url = 'Link'; 11 | 12 | template.print(url); 13 | 14 | })(current, template, email, email_action, event); 15 | -------------------------------------------------------------------------------- /KM Expiration Reminder.js: -------------------------------------------------------------------------------- 1 | //Scheduled Job 2 | 3 | setKnowledgeReminder(30); 4 | 5 | function setKnowledgeReminder(numDays) { 6 | var ps = numDays; 7 | var pn = parseInt(ps); 8 | var queryTime = new GlideDate(); 9 | queryTime.addDaysUTC(pn); 10 | //gs.print('Searching for knowledge expiring on ' + queryTime); 11 | 12 | if (pn > 0) { 13 | var gr = new GlideRecord('kb_knowledge'); 14 | gr.addQuery('workflow_state', 'published'); 15 | gr.addQuery('valid_to', '=', queryTime); 16 | gr.query(); 17 | while(gr.next()) { 18 | //gs.print('Knowledge document found: ' + gr.number); 19 | gs.eventQueue("km.reminder.7days", gr, gr.number, gr.author.name); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /past date validation.js: -------------------------------------------------------------------------------- 1 | // Go-Live Date can't be a Date in the past 2 | if (isLoading || newValue === '') { 3 | return; 4 | } 5 | 6 | // current date 7 | var currentDateObj = new Date(); 8 | var currentDateStr = formatDate(currentDateObj, g_user_date_format); 9 | var currentDateNum = getDateFromFormat(currentDateStr, g_user_date_format); 10 | 11 | // entered date 12 | var startDateNum = getDateFromFormat(newValue, g_user_date_format); 13 | 14 | if (startDateNum < currentDateNum) { 15 | g_form.clearValue('target_go_live_date'); 16 | g_form.showFieldMsg("target_go_live_date", "Past date not allowed."); 17 | 18 | return false; 19 | } 20 | -------------------------------------------------------------------------------- /Start and End Date Validation.js: -------------------------------------------------------------------------------- 1 | /* 2 | Business Rule: 3 | Table: Change Request 4 | When: Before 5 | Insert - true 6 | Update - true 7 | Condition - current.work_start.changes() || current.work_end.changes() 8 | */ 9 | 10 | if ((!current.work_start.nil()) && (!current.work_end.nil())) { 11 | var start = current.work_start.getGlideObject().getNumericValue(); 12 | var end = current.work_end.getGlideObject().getNumericValue(); 13 | if (start > end) { 14 | current.work_start.setError('start must be before end'); 15 | current.work_end.setError('start must be before end'); 16 | gs.addErrorMessage('Work Start date must be before Work End date'); 17 | current.setAbortAction(true); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Tickets from same location.js: -------------------------------------------------------------------------------- 1 | //USE: Relationship script for call; find all task records from callers at the same location 2 | 3 | var location = parent.caller.location; 4 | if( JSUtil.notNil(location) ) { 5 | var inc = "sys_class_name=incident^active=true^ref_incident.caller_id.location="+location; 6 | var prb = "sys_class_name=problem^active=true^opened_by.location="+location; 7 |  var chg = "sys_class_name=change_request^active=true^ref_change_request.requested_by.location="+location; 8 |  var scr = "sys_class_name=sc_request^active=true^ref_sc_request.requested_for.location="+location; 9 |   10 |  current.addEncodedQuery(inc+"^NQ"+prb+"^NQ"+chg+"^NQ"+scr); 11 | } 12 | else 13 |  current.addEncodedQuery("sys_idISEMPTY"); 14 | -------------------------------------------------------------------------------- /XML Example 1: -------------------------------------------------------------------------------- 1 | try { 2 | 3 | var test = "Dallas11111Frisco22222Rockwall33333"; 4 | 5 | 6 | var xmlDoc = new XMLDocument2(); 7 | xmlDoc.parseXML(test); 8 | 9 | //var node = xmlDoc.getNode('//locations'); 10 | var node = xmlDoc.getDocumentElement(); 11 | var loc = node.getChildNodeIterator(); 12 | 13 | gs.print(node); 14 | 15 | while (loc.hasNext()) { 16 | var n = loc.next(); 17 | var rec = new XMLDocument2(); 18 | rec.parseXML(n); 19 | 20 | var location = rec.getNode('//name').getTextContent(); 21 | gs.print('DEBUG: ' + location); 22 | } 23 | 24 | } 25 | catch(ex) { 26 | var message = ex.getMessage(); 27 | } 28 | -------------------------------------------------------------------------------- /Requested Item Due Date.js: -------------------------------------------------------------------------------- 1 | //use this to dynamically set the due date of a requested item based off the delivery time in the catalog item definition. 2 | //OOTB ServiceNow calculates due date by the lengh of the workflow stages 3 | //this was a fix script test, need to clean up and put in a business rule or workflow (replace recID w/ current) 4 | 5 | var recID = '2bace2c8dbe12780dca43e0b7c9619cc'; 6 | reqItem = new GlideRecord('sc_req_item'); 7 | reqItem.get('2bace2c8dbe12780dca43e0b7c9619cc'); 8 | 9 | var tod = new GlideDateTime(); 10 | var leadTime = reqItem.cat_item.delivery_time.getGlideObject(); 11 | var dueDate = new GlideDateTime(); 12 | dueDate.add(leadTime.getNumericValue()); 13 | 14 | gs.print('Today: ' + tod.getDisplayValue()); 15 | gs.print('Duration: ' + leadTime); 16 | gs.print('Due Date: ' + dueDate.getDisplayValue()); 17 | 18 | 19 | reqItem.setValue('due_date', dueDate); 20 | reqItem.update(); 21 | 22 | -------------------------------------------------------------------------------- /Two Step UI Action.js: -------------------------------------------------------------------------------- 1 | //USE: Have a UI Acton run both a client script and a server-side script. 2 | //EXAMPLE: Have a pop-up window confirm that the user wants to close a request. 3 | 4 | //UI Acton Setup 5 | //UI Action: Cancel Request 6 | //Action_name: cancel_request 7 | //Client = true 8 | //Onclick = cancelTicket(); 9 | 10 | 11 | function cancelTicket(){ 12 | var answer=confirm("Are you sure you want to cancel this record?"); 13 | if (answer==true) { 14 | gsftSubmit(null, g_form.getFormElement(), 'cancel_request'); //MUST call the 'Action name' set in this UI Action 15 | } else { 16 | return false; 17 | } 18 | } 19 | 20 | //Code that runs without 'onclick' 21 | //Ensure call to server-side function with no browser errors 22 | if (typeof window == 'undefined') 23 | serverClose(); 24 | 25 | function serverClose(){ 26 | current.stage = 'Closed'; 27 | current.state = 3; 28 | current.update(); 29 | } 30 | -------------------------------------------------------------------------------- /Rest_Call.py: -------------------------------------------------------------------------------- 1 | #Need to install requests package for python 2 | #easy_install requests 3 | import requests 4 | 5 | # Set the request parameters 6 | url = 'https://.service-now.com/api/now/table/x_79876_land_manag_land_request' 7 | 8 | # Eg. User name="admin", Password="admin" for this code sample. 9 | user = 'admin' 10 | pwd = 'admin' 11 | 12 | # Set proper headers 13 | headers = {"Content-Type":"application/json","Accept":"application/json"} 14 | 15 | # Do the HTTP request 16 | response = requests.post(url, auth=(user, pwd), headers=headers ,data="{\"type\":\"New Well\",\"short_description\":\"New well for Billings, Montana\"}") 17 | 18 | # Check for HTTP codes other than 200 19 | if response.status_code != 200: 20 | print('Status:', response.status_code, 'Headers:', response.headers, 'Error Response:',response.json()) 21 | exit() 22 | 23 | # Decode the JSON response into a dictionary and use the data 24 | data = response.json() 25 | print(data) 26 | -------------------------------------------------------------------------------- /Convert Duration to Hours, Minutes, Seconds.js: -------------------------------------------------------------------------------- 1 | //check if there is a schedule, if so use business time left else use actual time left 2 | var businessHours = false; 3 | if (!current.sla.schedule.nil()) { 4 | var gdt = new GlideDateTime(current.business_time_left); 5 | businessHours = true; 6 | } else { 7 | var gdt = new GlideDateTime(current.time_left); 8 | } 9 | 10 | var seconds = gdt.getNumericValue()/1000 //convert milliseconds to seconds 11 | var timeLeftString = secondsToString(seconds); 12 | 13 | function secondsToString(seconds, br) { 14 | var numdays = Math.floor((seconds % 31536000) / 86400); 15 | var numhours = Math.floor(((seconds % 31536000) % 86400) / 3600); 16 | var numminutes = Math.floor((((seconds % 31536000) % 86400) % 3600) / 60); 17 | var returnString = numdays + " days " + numhours + " hours " + numminutes + " minutes"; 18 | if (br == true) { 19 | returnString += ' (business hours)'; 20 | } 21 | 22 | return returnString; 23 | } 24 | -------------------------------------------------------------------------------- /System Cleanup.js: -------------------------------------------------------------------------------- 1 | //purge table data 2 | purgeTable('task'); 3 | purgeTable('new_call'); 4 | purgeTable('task_sla'); 5 | purgeTable('sys_watermark'); 6 | purgeTable('sys_email'); 7 | //purgeTable('sys_audit'); 8 | 9 | //reset counters 10 | resetCounter('task', 20001); 11 | resetCounter('incident', 10001); 12 | resetCounter('new_call', 1001); 13 | resetCounter('problem', 40001); 14 | resetCounter('problem_task', 10001); 15 | resetCounter('change_request', 30001); 16 | resetCounter('change_task', 10001); 17 | resetCounter('sc_request', 10001); 18 | resetCounter('sc_req_item', 10001); 19 | resetCounter('sc_task', 10001); 20 | resetCounter('hr_task', 10001); 21 | resetCounter('hr_case', 10001); 22 | 23 | function purgeTable(table) { 24 | var gr = new GlideRecord(table); 25 | gr.query(); 26 | gr.deleteMultiple(); 27 | } 28 | 29 | function resetCounter(table, num) { 30 | var counter = new GlideRecord('sys_number_counter'); 31 | counter.addQuery('table', table); 32 | counter.query(); 33 | while (counter.next()) { 34 | counter.number = num; 35 | counter.update(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Change Control Cutoff.js: -------------------------------------------------------------------------------- 1 | //USE: Check if a change has been submitted BEFORE change control and is scheduled to occur AFTER change control 2 | //Test Code...change dates to something recent 3 | var submit = new GlideDateTime("2015-11-01 08:00:00"); 4 | var planStart = new GlideDateTime("2015-11-04 15:00:00"); 5 | var changeCutoff = getChangeCutoffDate(submit); 6 | gs.print('Change cutoff time is ' + changeCutoff); 7 | 8 | if (submit <= changeCutoff && planStart > changeCutoff) { 9 | gs.print('Change is OK'); 10 | } else { 11 | gs.print('Change is too late'); 12 | } 13 | 14 | function getChangeCutoffDate(d) { 15 | var changeCutoffDay = 3; //1=Monday...7=Sunday 16 | var returnDate = new GlideDateTime(d); 17 | var day = returnDate.getDayOfWeekLocalTime(); 18 | if (day == 7) {day = 0;} //if Sunday set to 0 instead of 7 19 | gs.print('Change start day of the week is ' + day); 20 | 21 | if (day <= changeCutoffDay ) { 22 | return returnDate.getInternalMidnight(changeCutoffDay); 23 | } 24 | 25 | if (day > changeCutoffDay) { 26 | returnDate.addWeeksLocalTime(1); 27 | return returnDate.getInternalMidnight(changeCutoffDay); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Clean System.js: -------------------------------------------------------------------------------- 1 | //purge table data 2 | purgeTable('task_sla'); 3 | purgeTable('sysapproval_approver'); 4 | purgeTable('sys_email'); 5 | 6 | //purge ticket data 7 | purgeTable('incident_task'); 8 | purgeTable('incident'); 9 | purgeTable('problem_task'); 10 | purgeTable('problem'); 11 | purgeTable('change_task'); 12 | purgeTable('change_request'); 13 | purgeTable('sc_task'); 14 | purgeTable('sc_req_item'); 15 | purgeTable('sc_request'); 16 | 17 | //reset counters 18 | resetCounter('incident', 1); 19 | resetCounter('incident_task', 1); 20 | resetCounter('problem', 1); 21 | resetCounter('problem_task', 1); 22 | resetCounter('change_request', 1); 23 | resetCounter('change_task', 1); 24 | resetCounter('sc_request', 1); 25 | resetCounter('sc_req_item', 1); 26 | resetCounter('sc_task', 1); 27 | 28 | 29 | function purgeTable(table) { 30 | var gr = new GlideRecord(table); 31 | gr.query(); 32 | gr.deleteMultiple(); 33 | } 34 | 35 | function resetCounter(table, num) { 36 | var counter = new GlideRecord('sys_number_counter'); 37 | counter.addQuery('table', table); 38 | counter.query(); 39 | while (counter.next()) { 40 | counter.number = num; 41 | counter.update(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /AJAX Example.js: -------------------------------------------------------------------------------- 1 | //Example AJAX call to get number of related incidents for a change. 2 | 3 | //Client Script: 4 | function onLoad() { 5 | 6 | var chgSysId = g_form.getUniqueValue(); 7 | 8 | var ga = new GlideAjax('ChangeManagementRelatedRecords'); 9 | ga.addParam('sysparm_name','getIncidentCount'); 10 | ga.addParam('sysparm_change_id', chgSysId); 11 | ga.getXML(GetRelatedIncidentCount); 12 | 13 | function GetRelatedIncidentCount(response) { 14 | var answer = response.responseXML.documentElement.getAttribute("answer"); 15 | alert('Related Incidents: ' + answer); 16 | } 17 | } 18 | 19 | 20 | //Script Include: 21 | var ChangeManagementRelatedRecords = Class.create(); 22 | ChangeManagementRelatedRecords.prototype = Object.extendsObject(AbstractAjaxProcessor, { 23 | 24 | getIncidentCount: function() { 25 | var changeID = this.getParameter('sysparm_change_id'); 26 | var incident = new GlideRecord('incident'); 27 | incident.addQuery('rfc', changeID); 28 | incident.query(); 29 | return incident.getRowCount() 30 | }, 31 |   32 |   _privateFunction: function() { // this function is not client callable 33 |   34 | } 35 |   36 | }); 37 | -------------------------------------------------------------------------------- /Advanced Ref Qualifier.js: -------------------------------------------------------------------------------- 1 | //In this example a custom field is added to the group tabled called tower (reference field to a custom table). 2 | //The script includes gets all groups the current user is a member of and gets the associated tower for each group. 3 | //A comma seperated list is returned to the calling function. 4 | 5 | //USE: Refence Qual: 6 | javascript:'u_towerIN' + new TowerUtils().getMyTowers() 7 | 8 | 9 | //Script Include: 10 | var TowerUtils = Class.create(); 11 | TowerUtils.prototype = { 12 | initialize: function() { 13 | }, 14 | 15 | //returns a comma seperated list of sysIDs based on the users assignment group membership 16 | getMyTowers:function() { 17 | var user = gs.getUserID(); 18 | var membership = new GlideRecord('sys_user_grmember'); 19 | var queryString = ''; 20 | membership.addQuery('user', user); 21 | membership.query(); 22 | while (membership.next()) { 23 | if (queryString.length > 0) { 24 | //build a comma separated string of towers if there is more than one 25 | queryString += (',' + membership.group.u_tower.sys_id); 26 | } else { 27 | queryString += membership.group.u_tower.sys_id; 28 | } 29 | } 30 | return queryString; 31 | }, 32 | type: 'TowerUtils' 33 | }; 34 | -------------------------------------------------------------------------------- /rollback flow approvals.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | (function executeRule(current, previous /*null when async*/ ) { 4 | 5 | // If RPA has a flow desinger execution associated with it, cancel it. 6 | var flowContext = new GlideRecord('sys_flow_context'); 7 | flowContext.addQuery('state', 'WAITING'); 8 | flowContext.addQuery('source_record', current.sys_id); 9 | flowContext.addQuery('name', 'STARTSWITH', 'RPA Approvals'); 10 | flowContext.query(); 11 | 12 | if (flowContext.next()) { 13 | var rpaContextID = flowContext.getValue('sys_id'); 14 | sn_fd.FlowAPI.cancel(rpaContextID, "Reset manually by " + gs.getUserName()); 15 | } 16 | 17 | // Cancel exiting approrvals 18 | var approval = new GlideRecord('sysapproval_approver'); 19 | approval.addQuery('document_id', current.sys_id); 20 | approval.query(); 21 | while (approval.next()) { 22 | approval.setValue('state', 'cancelled'); 23 | approval.setValue('comments', 'RPA approvals reset by ' + gs.getUserName()); 24 | approval.update(); 25 | } 26 | 27 | 28 | // Reset case so approval flow will kick off again. 29 | current.u_reset_approvals = false; 30 | current.approval = 'not requested'; 31 | current.state = 1; 32 | 33 | 34 | })(current, previous); 35 | -------------------------------------------------------------------------------- /Oncall PopUp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ${gs.getMessage('Show group on-call schedule')} 7 | 8 | 25 | 26 | -------------------------------------------------------------------------------- /cc_VaccinationAjaxProcessor.js: -------------------------------------------------------------------------------- 1 | var cc_VaccinationAjaxProcessor = Class.create(); 2 | cc_VaccinationAjaxProcessor.prototype = Object.extendsObject(global.AbstractAjaxProcessor, { 3 | secondDoseWaitValidation: function() { 4 | var dose1 = this.getParameter('sysparm_date_1'); 5 | var dose2 = this.getParameter('sysparm_date_2'); 6 | var vac = this.getParameter('sysparm_vacc_def'); 7 | 8 | var dose1Date = new GlideDateTime('1970-01-01 00:00:00'); 9 | dose1Date.add(dose1); 10 | 11 | var dose2Date = new GlideDateTime('1970-01-01 00:00:00'); 12 | dose2Date.add(dose2); 13 | 14 | var vacDef = new GlideRecord('sn_imt_vaccine_vaccine_response_definition'); 15 | vacDef.get('sys_id', vac); 16 | 17 | var waitPeriod = vacDef.getValue('days_between_doses'); 18 | var dueDate = new GlideDateTime(dose1Date); 19 | dueDate.addDaysLocalTime(waitPeriod); 20 | 21 | var diff = dose2Date.compareTo(dueDate); 22 | var message = 'ok'; 23 | 24 | if (diff == -1) { 25 | message = vacDef.getValue('manufacturer') + ' requires ' + vacDef.getValue('days_between_doses') + ' days between vaccinations.'; 26 | } 27 | 28 | return diff; 29 | }, 30 | 31 | type: 'cc_VaccinationAjaxProcessor' 32 | }); 33 | -------------------------------------------------------------------------------- /Billable Time Rollup.js: -------------------------------------------------------------------------------- 1 | //location: time_card table 2 | //use: use to calculate billable hours and billable percentage of a time sheet. billable hours and billable percent fields will need to be added to the time_sheet table. 3 | //condition: when total or "category" (custom field changes). If using a billable checkbox then adjust script below accordingly. 4 | 5 | 6 | setBillableFields(current.time_sheet.sys_id); 7 | 8 | function setBillableFields(tsID) { 9 | var sql = 'categoryINtask_work,Customer Contract Reconciliation,Customer Documentation,Customer Meeting,Customer Satisfaction,Customer Travel^time_sheet=' + tsID; 10 | 11 | var ts = new GlideRecord('time_sheet'); 12 | ts.get(tsID); 13 | 14 | var totalHours = ts.total_hours; 15 | 16 | var timeCard = new GlideRecord('time_card'); 17 | timeCard.addEncodedQuery(sql); 18 | timeCard.query(); 19 | 20 | var billableHours = 0; 21 | var billablePercent = 0; 22 | 23 | while (timeCard.next()) { 24 | var n = parseFloat(timeCard.getValue('total')); 25 | billableHours += n; 26 | } 27 | 28 | billablePercent = (billableHours/totalHours)*100; 29 | var pString = billablePercent.toFixed(2); 30 | 31 | if (pString == '' || billableHours == 0) 32 | pString = 0; 33 | 34 | //DEBUG 35 | //gs.log('Total Hours: ' + totalHours); 36 | //gs.log('Billable Hours: ' + billableHours); 37 | //gs.log('Billable %: ' + pString); 38 | 39 | ts.u_billable_hours = billableHours; 40 | ts.u_billable_percent = pString; 41 | ts.update(); 42 | } 43 | -------------------------------------------------------------------------------- /CSV Field to Files Import.js: -------------------------------------------------------------------------------- 1 | //PURPOSE: 2 | //A comma separated list was imported. After the record is imported, this script would pull apart a comma separated list and add them to a custom table that was a related list of the record being imported. 3 | 4 | //When: onAfter Import Script 5 | //Scenario: You have a Contract record that has a comma seperated list of products sold. Both custom tables. 6 | 7 | //Add related Product Sold 8 | log.info('DEBUG Processing Covered Products Sold for Contract'); 9 | 10 | //Split comma delimited list into an array 11 | log.info('DEBUG splitting ' + target.u_assoc_products_sold); 12 | ps = target.u_assoc_products_sold.split(','); 13 | 14 | //Cycle thru the products sold and create a a 'covered' record for each one 15 | for (var i=0; i < ps.length; i++) { 16 | //Get the current Product Sold 17 | //log.info('DEBUG Line ' + source.u_forign_id + ': processing for Product Sold ' + ps[i]); 18 | var grProductSold = new GlideRecord('u_products_sold'); 19 | grProductSold.addQuery("u_foreign_key", ps[i]); 20 | grProductSold.query(); 21 | 22 | //If found, create a 'covered' record 23 | if (grProductSold.next()) { 24 | log.info('DEBUG creating new "Covered Product Sold" for ' + grProductSold.u_part_number); 25 | var coveredPS = new GlideRecord('u_covered_products_sold'); 26 | coveredPS.initialize(); 27 | coveredPS.u_commitment = target.sys_id; 28 | coveredPS.u_product_sold = grProductSold.sys_id; 29 | coveredPS.insert(); 30 | log.info('DEBUG new record inserted ' + coveredPS.number); 31 | } else { 32 | log.info('DEBUG Product Sold not found: ' + ps[i]); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Personal Email.js: -------------------------------------------------------------------------------- 1 | //use in a business rule on the sys_email table for Outbox inserts. 2 | //needs HR module/HR Profileds installed. If not, update accordingly. 3 | //needs custom field for 'Email Preference' on the HR table also 4 | 5 | var original = current.recipients; 6 | var modArray = []; 7 | var modified = ""; 8 | 9 | var recips = original; 10 | var testArr = recips.split(','); 11 | //gs.info("# of recipients: " + testArr.length); 12 | 13 | for (var i = 0; i < testArr.length; i++) { 14 | //gs.info("EMAIL DEBUG: Processing " + testArr[i]); 15 | 16 | //get HR profile of each email recipient 17 | var hrProfile = new GlideRecord('sn_hr_core_profile'); 18 | hrProfile.addQuery('user.email', testArr[i]); 19 | hrProfile.query(); 20 | if (hrProfile.next()) { 21 | //debug stuff 22 | //gs.info("Profile found: " + hrProfile.getDisplayValue()); 23 | //gs.info("Personal email: " + hrProfile.getValue('personal_email')); 24 | //gs.info("Notify method: " + hrProfile.getValue("u_user_email_preference")); 25 | 26 | //test if on leave or wants to use personal email, if so replace 27 | var notifyMethod = hrProfile.getValue("u_user_email_preference"); 28 | var leaveStatus = hrProfile.getValue("leave_status"); 29 | if (notifyMethod == "Personal" || leaveStatus == "on_leave") { 30 | modArray.push(hrProfile.getValue('personal_email')); 31 | } else { 32 | //not on leave or pref personal email...use original email 33 | modArray.push(testArr[i]); 34 | } 35 | } else { 36 | //no profile found...use original email 37 | modArray.push(testArr[i]); 38 | } 39 | } 40 | 41 | current.recipients = modArray.toString(); 42 | -------------------------------------------------------------------------------- /Send email to personal email: -------------------------------------------------------------------------------- 1 | //add this to a business rule for the Email table 2 | //once a record is created in the outbox and is processed this will replace the users email with their personal email 3 | //NOTE: this was done as part of HR implementation so HR Profile was involved. 4 | 5 | (function executeRule(current, previous /*null when async*/ ) { 6 | 7 | var original = current.recipients; 8 | var modArray = []; 9 | var modified = ""; 10 | 11 | var recips = original; 12 | var testArr = recips.split(','); 13 | //gs.info("# of recipients: " + testArr.length); 14 | 15 | for (var i = 0; i < testArr.length; i++) { 16 | //gs.info("EMAIL DEBUG: Processing " + testArr[i]); 17 | 18 | //get HR profile of each email recipient 19 | var hrProfile = new GlideRecord('sn_hr_core_profile'); 20 | hrProfile.addQuery('user.email', testArr[i]); 21 | hrProfile.query(); 22 | if (hrProfile.next()) { 23 | //debug stuff 24 | //gs.info("Profile found: " + hrProfile.getDisplayValue()); 25 | //gs.info("Personal email: " + hrProfile.getValue('personal_email')); 26 | //gs.info("Notify method: " + hrProfile.getValue("u_user_email_preference")); 27 | 28 | //test if on leave or wants to use personal email, if so replace 29 | var notifyMethod = hrProfile.getValue("u_user_email_preference"); 30 | var leaveStatus = hrProfile.getValue("leave_status"); 31 | 32 | if (notifyMethod == "Personal" || leaveStatus == "on_leave") { 33 | modArray.push(hrProfile.getValue('personal_email')); 34 | } else { 35 | //not on leave or pref personal email...use original email 36 | modArray.push(testArr[i]); 37 | } 38 | } else { 39 | //no profile found...use original email 40 | modArray.push(testArr[i]); 41 | } 42 | } 43 | 44 | current.recipients = modArray.toString(); 45 | 46 | })(current, previous); 47 | -------------------------------------------------------------------------------- /AJAX Example JSON return.js: -------------------------------------------------------------------------------- 1 | //client script 2 | function onChange(control, oldValue, newValue, isLoading) { 3 | if (isLoading) { 4 | return; 5 | } 6 | 7 | var supervisorFields = ['supervisor_business_phone', 'supervisor_email']; 8 | 9 | for (var i = 0; i < supervisorFields.length; i++) { 10 | g_form.clearValue(supervisorFields[i]); 11 | g_form.hideFieldMsg(supervisorFields[i]); 12 | } 13 | 14 | var ga = new GlideAjax('OffboardingRecordProducerAjax'); 15 | ga.addParam('sysparm_name', 'getSupervisorInformation'); 16 | ga.addParam('sysparm_supervisor', g_form.getValue('u_offboarding_supervisor')); 17 | ga.getXMLAnswer(updateSupervisorFields); 18 | } 19 | 20 | function updateSupervisorFields(answer) { 21 | if (answer) { 22 | var supervisorInfo = JSON.parse(answer); //returning JSON, must parse. 23 | 24 | (supervisorInfo.email ? 25 | g_form.setValue('supervisor_email', supervisorInfo.email) : 26 | g_form.showFieldMsg('supervisor_email', 'There is no Email associated to the Employee selected.', 'info') 27 | ); 28 | 29 | (supervisorInfo.phone ? 30 | g_form.setValue('supervisor_business_phone', supervisorInfo.phone) : 31 | g_form.showFieldMsg('supervisor_business_phone', 'There is no Business Phone associated to the Supervisor selected.', 'info') 32 | ); 33 | } 34 | } 35 | 36 | //script include 37 | var OffboardingRecordProducerAjax = Class.create(); 38 | OffboardingRecordProducerAjax.prototype = Object.extendsObject(global.AbstractAjaxProcessor, { 39 | 40 | getSupervisorInformation: function() { 41 | var supervisorSysID = this.getParameter('sysparm_supervisor'); 42 | var gr_user = new GlideRecord('sys_user'); 43 | 44 | if (gr_user.get(supervisorSysID)) { 45 | var results = { 46 | "email": gr_user.getValue('email'), 47 | "phone": gr_user.getValue('phone'), 48 | }; 49 | return JSON.stringify(results); 50 | } 51 | }, 52 | type: 'OffboardingRecordProducerAjax' 53 | }); 54 | -------------------------------------------------------------------------------- /Validate second vaccination dose date.js: -------------------------------------------------------------------------------- 1 | function onChange(control, oldValue, newValue, isLoading, isTemplate) { 2 | var secondDateEpochMs = getDateFromFormat(newValue, g_user_date_format); 3 | if (secondDateEpochMs > new Date().getTime()) { 4 | g_form.clearValue('second_date_administered'); 5 | g_form.showFieldMsg('second_date_administered', getMessage("Date cannot be in the future"), 'error'); 6 | return; 7 | } 8 | 9 | var firstDate = g_form.getValue('first_date_administered'); 10 | if (firstDate && newValue) { 11 | var firstDateEpochMs = getDateFromFormat(firstDate, g_user_date_format); 12 | if (firstDateEpochMs >= secondDateEpochMs) { 13 | g_form.clearValue('second_date_administered'); 14 | g_form.showFieldMsg('second_date_administered', getMessage("Second dose must be after first dose"), 'error'); 15 | return; 16 | } 17 | } 18 | 19 | var ajaxProcessor = new GlideAjax('sn_imt_vaccine.VaccinationAjaxProcessor'); 20 | ajaxProcessor.addParam('sysparm_name', 'secondDoseDateValidation'); 21 | ajaxProcessor.addParam('sysparm_date_administered', secondDateEpochMs); 22 | ajaxProcessor.getXML(function(response) { 23 | var answer = response.responseXML.documentElement.getAttribute("answer"); 24 | if (answer === 'false') { 25 | g_form.clearValue('second_date_administered'); 26 | g_form.showFieldMsg('second_date_administered', getMessage("Date administered cannot be on or before existing response."), 'error'); 27 | } else { 28 | var ajaxProcessor2 = new GlideAjax('sn_imt_vaccine.cc_VaccinationAjaxProcessor'); 29 | ajaxProcessor2.addParam('sysparm_name', 'secondDoseWaitValidation'); 30 | ajaxProcessor2.addParam('sysparm_date_1', firstDateEpochMs); 31 | ajaxProcessor2.addParam('sysparm_date_2', secondDateEpochMs); 32 | ajaxProcessor2.addParam('sysparm_vacc_def', g_form.getValue('vaccine_response_definition')); 33 | ajaxProcessor2.getXML(function(response) { 34 | var answer = response.responseXML.documentElement.getAttribute("answer"); 35 | 36 | if (answer == -1) { 37 | g_form.clearValue('second_date_administered'); 38 | g_form.showFieldMsg('second_date_administered', "Date of second dose does not meet vaccination wait period.", 'error'); 39 | 40 | } 41 | }); 42 | } 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /XML Example 2.js: -------------------------------------------------------------------------------- 1 | try { 2 | var s = new sn_ws.SOAPMessageV2('AS Oracle Loctions', 'process'); 3 | 4 | //override authentication profile 5 | //authentication type ='basic' 6 | //r.setAuthentication(authentication type,profile name); 7 | 8 | s.setStringParameterNoEscape('Request.InstanceName', ''); 9 | var response = s.execute(); 10 | var responseBody = response.getBody(); 11 | var status = response.getStatusCode(); 12 | 13 | //gs.print('DEBUG: ' + status); 14 | //gs.print('DEBUG: ' + responseBody); 15 | var xmlDoc = new XMLDocument2(); 16 | xmlDoc.parseXML(responseBody); 17 | 18 | //Get content of the response node, that's where our data is 19 | var node = xmlDoc.getNode('//Response'); 20 | //gs.print('DEBUG: ' + node); 21 | var loc = node.getChildNodeIterator(); 22 | 23 | while (loc.hasNext()) { 24 | var n = loc.next(); 25 | var rec = new XMLDocument2(); 26 | rec.parseXML(n); 27 | 28 | //get location data 29 | var location_id = rec.getNode('//Location_id').getTextContent(); 30 | var location_code = rec.getNode('//Location_code').getTextContent(); 31 | var description = rec.getNode('//Description').getTextContent(); 32 | var inactive_date = rec.getNode('//Inactive_date').getTextContent(); 33 | var style = rec.getNode('//Style').getTextContent(); 34 | var address_1 = rec.getNode('//Address_line_1').getTextContent(); 35 | var address_2 = rec.getNode('//Address_line_2').getTextContent(); 36 | var address_3 = rec.getNode('//Address_line_3').getTextContent(); 37 | var city = rec.getNode('//Town_or_city').getTextContent(); 38 | var postal_code = rec.getNode('//Postal_code').getTextContent(); 39 | var region_1 = rec.getNode('//Region_1').getTextContent(); 40 | var region_2 = rec.getNode('//Region_2').getTextContent(); 41 | var telephone_1 = rec.getNode('//Telephone_number_1').getTextContent(); 42 | var telephone_2 = rec.getNode('//Telephone_number_2').getTextContent(); 43 | var telephone_3 = rec.getNode('//Telephone_number_3').getTextContent(); 44 | var country = rec.getNode('//Country').getTextContent(); 45 | 46 | //put location data into location import set table 47 | var newLoc = new GlideRecord('u_as_locations'); 48 | newLoc.initialize(); 49 | newLoc.setValue('u_location_id', location_id); 50 | newLoc.setValue('u_location_code', location_code); 51 | newLoc.setValue('u_city', city); 52 | newLoc.setValue('u_country', country); 53 | newLoc.setValue('u_description', description); 54 | newLoc.setValue('u_inactive_date', inactive_date); 55 | newLoc.setValue('u_postal_code', postal_code); 56 | newLoc.setValue('u_region_1', region_1); 57 | newLoc.setValue('u_region_2', region_2); 58 | newLoc.setValue('u_style', style); 59 | newLoc.setValue('u_telephone_1', telephone_1); 60 | newLoc.setValue('u_telephone_2', telephone_2); 61 | newLoc.setValue('u_telephone_3', telephone_3); 62 | newLoc.setValue('u_address_1', address_1); 63 | newLoc.setValue('u_address_2', address_2); 64 | newLoc.setValue('u_address_3', address_3); 65 | 66 | newLoc.setValue('u_import_source', 'Oracle WS Import'); 67 | newLoc.insert(); 68 | 69 | //gs.print('DEBUG: ' + region_1 + ":" + region_2); 70 | } 71 | 72 | 73 | } 74 | 75 | catch(ex) { 76 | var message = ex.getMessage(); 77 | } --------------------------------------------------------------------------------