Change management Archives - ServiceNow Guru https://servicenowguru.com/tag/change-management/ ServiceNow Consulting Scripting Administration Development Tue, 28 May 2024 21:11:15 +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 Change management Archives - ServiceNow Guru https://servicenowguru.com/tag/change-management/ 32 32 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
Overriding ServiceNow Form View Inheritance https://servicenowguru.com/client-scripts-scripting/overriding-servicenow-form-view-inheritance/ https://servicenowguru.com/client-scripts-scripting/overriding-servicenow-form-view-inheritance/#comments Fri, 20 Sep 2013 13:59:41 +0000 https://servicenowguru.wpengine.com/?p=5034 6+ years ago when I was an admin first being introduced to ServiceNow, I remember being blown away by how simple it was to personalize forms, add fields, and design custom form views. Custom views are very easy to make, but I’m always hesitant to recommend them due to the unintended complexity they can cause.

The post Overriding ServiceNow Form View Inheritance appeared first on ServiceNow Guru.

]]>
6+ years ago when I was an admin first being introduced to ServiceNow, I remember being blown away by how simple it was to personalize forms, add fields, and design custom form views. Custom views are very easy to make, but I’m always hesitant to recommend them due to the unintended complexity they can cause. The biggest issue with the ‘view’ concept in ServiceNow is that the system insists on forcing that form view onto every record referenced from within it. This issue exists in every ServiceNow instance today. In this article, I’ll explain why this can be a huge problem, and finally, a good way to break the ServiceNow form view inheritance cycle!

ServiceNow Form View Inheritance

The problem:

Here’s an example that most likely exists in your system (and certainly does in ServiceNow demo systems) to help illustrate the problem. If you go to your system and open an emergency change record, you’ll probably notice that it displays the ‘Emergency’ view in the top-left corner of the change form. That’s great, but when you click the reference icon for the ‘Requested by’ or ‘Assigned to’ fields, you’ll see that the system applies that view name (even though it doesn’t exist for that table) to the referenced record form. Then if you click on the ‘Company’ or ‘Department’ referenced record on the user form, you’ll notice that the ‘Emergency’ view name is carried over there as well!

It’s important to note, that the system is not actually creating new views as this happens. It’s simply cascading the previous form view to any other record you navigate to. The label of ‘Emergency’ is actually the smaller, cosmetic-only, issue. The larger issue is when you decide personalize that user or department form as an admin. You’ll most likely end up creating a brand-new view named ‘Emergency’ that applies to your user table! Now you’ve got an extra view and your changes won’t show up in the right place anyway because you meant to customize the ‘Default’ view instead…and you better hope nobody decides to reference that new, garbage view in a module or list definition and have it end up propagating the problem even further.

So, in short, this is a HUGE problem, and one that I’ve struggled with for the past 6+ years. It’s one ‘feature’ of ServiceNow that I’ve never understood, and this alone always makes me very hesitant to recommend the use of views to any of my clients. Fortunately there are a couple of things you can do to work around this…one that’s been around for as long as views have, and one that I came up with yesterday that easily allows you to override view inheritance for any table in the system.

Option 1: View rules

I’m a big fan of view rules and I’ve used them (primarily in change management) for a long time. They allow you to force a particular view on a particular table, for a particular subset of records. To use the change example again, you could set up a view rule to force the Default, Emergency, and Routine form views to display Comprehensive, Emergency, and Routine changes in their own respective views with their own sets of fields and related lists.

This is extremely handy, and can help break view inheritance because it forces a particular view for records in a table. The downside of this is that it’s not really practical to set up a view rule for every record table referenced from the change table just so you can overcome this problem. You could end up with a separate view rule for hundreds of tables!

Option 2: A better way…

Shown below is an example script that we’ve started using at Crossfuze in our Change turnkey product. This script runs on load of a particular table (Change request for example) and manipulates the form to remove any trace of a given view from the reference or related list links. A single client script can solve this issue for you on any table where you choose to set up custom views!

‘Override view inheritance’ Client Script
Name: Override view inheritance
Table: Change request [change_request] Type: OnLoad
Script:

function onLoad() {
//Override all non-default change view inheritance for records referenced on change
overrideViewInheritance('routineChange', '');
overrideViewInheritance('emergencyChange', '');
}function overrideViewInheritance(oldView, newView) {
try{
//Override reference field link view inheritance
if($('sysparm_view').value == oldView){
$('sysparm_view').value = newView;
}

//Override related list link view inheritance
$('related_lists_wrapper').select('a.linked').each(function(elmt) {
elmt.href = elmt.href.gsub('sysparm_view=' + oldView,'sysparm_view=' + newView);
});
}catch(e){}
}

The post Overriding ServiceNow Form View Inheritance appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/client-scripts-scripting/overriding-servicenow-form-view-inheritance/feed/ 9
Show a Task Workflow Timeline https://servicenowguru.com/ui-actions-system-ui/show-task-workflow-timeline/ https://servicenowguru.com/ui-actions-system-ui/show-task-workflow-timeline/#comments Wed, 16 May 2012 11:04:29 +0000 https://servicenowguru.wpengine.com/?p=4420 Having a blast from Knowledge 12 this week! It’s been fun this year being able to relax a little bit more and take some more time to talk with people. I look forward to meeting more of you over the next couple of days. Today, I’m writing in response to a user on the ServiceNow

The post Show a Task Workflow Timeline appeared first on ServiceNow Guru.

]]>
Having a blast from Knowledge 12 this week! It’s been fun this year being able to relax a little bit more and take some more time to talk with people. I look forward to meeting more of you over the next couple of days.

Today, I’m writing in response to a user on the ServiceNow community, and a few users in the advanced admin course I was observing earlier in the week. In the advanced admin class, they show how you can click a UI action on the change form to show a workflow in progress. They also show you how you can view a timeline for a workflow context. These UI actions (and corresponding popups) can provide valuable information to both administrators and technicians to determine the status and process of a particular workflow. The only problem (in the case of the timeline) is that it can’t be accessed from the change directly. You have to navigate to the workflow context record first.

In this post I’ll show you how to create a UI action script to display a workflow timeline popup directly from any task form in the system.

Change Request Timeline

If you’re familiar with the default UI action in the system, the code below should look familiar. It is initiated from the client, gets the sys_id of the workflow context, constructs a URL to the timeline, and then pops a new window up showing the timeline. The real trick in this case is getting the correct sys_id from the workflow context record. You should be able to create the following as a UI action on your task table. It will display for any itil user on a task that has an associated workflow.

‘Show Workflow Timeline’ UI Action
Name: Show Workflow Timeline
Table: Task
Order: 200
Show Insert: False
Client: True
OnClick: showTimeline();
Form link: True
Condition: !current.isNewRecord() && (new Workflow().hasWorkflow(current)) && gs.hasRole(‘itil’)
Script:

function showTimeline() {
var wf = new GlideRecord('wf_context');
wf.addQuery('id', g_form.getUniqueValue());
wf.query();
if(wf.next()){
var url = new GlideURL('show_schedule_page.do');
url.addParam('sysparm_stack', 'no');
url.addParam('sysparm_page_schedule_type', 'workflow_context');
url.addParam('sysparm_timeline_history', wf.sys_id);
var w = getTopWindow();
w.popupOpenFocus(url.getURL(), 'show_workflow_timeline', 950, 700, '', false, false);
}
}

The post Show a Task Workflow Timeline appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/ui-actions-system-ui/show-task-workflow-timeline/feed/ 4
Building a CI Change Calendar Macro – On The Fly https://servicenowguru.com/reporting/ci-change-calendar-macro/ https://servicenowguru.com/reporting/ci-change-calendar-macro/#comments Wed, 20 Jul 2011 12:39:19 +0000 https://servicenowguru.wpengine.com/?p=3920 A co-worker asked me for some help yesterday fulfilling an interesting requirement. He needed to set up a configuration item reference field or catalog variable that would display a calendar macro that, when clicked, would open a popup window containing a change calendar report for that configuration item. I figured this idea might be of

The post Building a CI Change Calendar Macro – On The Fly appeared first on ServiceNow Guru.

]]>
A co-worker asked me for some help yesterday fulfilling an interesting requirement. He needed to set up a configuration item reference field or catalog variable that would display a calendar macro that, when clicked, would open a popup window containing a change calendar report for that configuration item. I figured this idea might be of use to others so I’m posting it here!

CI Change Calendar Report Macro Popup

I’ve written before about UI macros, and different types of popups on SNGuru. I’ve also written about how your change management process can be a valuable input into your other ITIL processes such as incident management. You can refer to those posts if you’re interested in more detail on some of the ideas used in this article.

The real magic of this solution is how you can create this calendar report ‘on-the fly’ simply by constructing the correct URL. This is something that a colleague of mine, Bobby Edmonds, tipped me off to a couple of years ago. The URL contains several ‘sysparm_’ parameters that point to the fields on the report form. Once you know those, you just have to pass them the correct values. The URL for the calendar report needs to look something like this…

sys_report_template.do?sysparm_table=change_request&sysparm_type=calendar&sysparm_cal_field=end_date&sysparm_from_list=true&sysparm_manual_labor=true&sysparm_query=^end_date%3Ejavascript:gs.endOfLastMonth()'^cmdb_ci=YOUR_CI_SYS_ID_HERE
I plan on writing a post about creating on-the-fly reports soon, but you can piece together the necessary URL parameters for a report by poking around any existing report in your system with a DOM inspector to see what parameters are necessary.

You might be asking, “Why don’t you just point directly to the existing change calendar report URL?”. You could certainly do that, and avoid all of the above, but the requirement in this case is to have the calendar report filtered based on the CI chosen in the field, so it needs to be dynamic.

Because this URL will be initiated from a UI macro (for this example) we’ll need to escape the ampersands and you can see that I’ve done that in the code below. The rest of the code is almost identical to the out-of-box ‘user_show_incidents’ UI macro that you see on the incident ‘Caller’ field. Once the URL is constructed we can use ‘popupOpen’ to display the report in a popup!

Your UI macro should look like this. Once set up, it can be added to the CI reference field of your choice by adding the ‘ref_contributions=ci_show_change_calendar’ attribute to the dictionary entry ‘Attributes’ field of that CI reference field.

‘ci_show_change_calendar’ UI macro
Name: ci_show_change_calendar
Description:
Show CI change calendar popup link
Activate by:
– set the active field in this macro to true
– adding the attribute to a dictionary field: ref_contributions=ci_show_change_calendar
Script:

<!--?xml version="1.0" encoding="utf-8" ?-->



<script>
function showCIChangeCalendar(reference) {
   //Construct the calendar report URL
   var url = 'sys_report_template.do?sysparm_table=change_request$[AMP]sysparm_type=calendar$[AMP]sysparm_cal_field=end_date$[AMP]sysparm_from_list=true$[AMP]sysparm_manual_labor=true$[AMP]sysparm_query=^end_date%3Ejavascript:gs.endOfLastMonth()^cmdb_ci=';
   
   var s = reference.split('.');
   var referenceField = s[1];
   var v = g_form.getValue(referenceField);
   url = url + v;
   //Open the popup 
   var features = "width=700,height=650,toolbar=no,status=no,directories=no,menubar=no,resizable=yes,scrollbars=1";
   popupOpen(url, 'show_calendar', 700, 650, features, true);
}
</script>

 

Add a Change Calendar Macro to a Catalog Reference Variable

You can use a similar technique to add a calendar macro icon next to a catalog reference variable. The big, additional challenge in the service catalog is that reference field macros are not supported for catalog variables. In order to add one you have two options. The first would be to create a UI page or UI macro-type variable similar to the example above. The big drawback would be that the icon wouldn’t appear right next to the reference variable without some clever client scripting.
The second option (which I chose) is to use a catalog client script to construct the macro on-the-fly and place it in the correct spot next to the reference field. This technique is based on a previous solution I wrote about in another SNCGuru article about placing macros next to non-reference fields.

Your catalog client script should look like this. Just add the name of your CI reference variable in the ‘ciVarName’ line below.

‘Add CI Change Calendar Macro’ Catalog Client Script
Name: Add CI Change Calendar Macro
Applies to: Catalog item or variable set of your choice
Type: onLoad
Script (Pre-Fuji version):

function onLoad() {
//The reference variable name to put the icon next to
var ciVarName = 'cmdb_ci';//Add the icon decoration to the reference field
var ciEl = 'lookup.' + g_form.getControl(ciVarName).id;
$(ciEl).insert({
after: '<a id="cmdb_ci_calendar_macro" style="padding-right: 4px;"></a><img title="CI Change Calendar" src="images/icons/dayview.gifx" alt="CI Change Calendar" />'
});
//Add the 'onclick' event
$('cmdb_ci_calendar_macro').observe('click', showCIChangeCalendar);

function showCIChangeCalendar(){
//Construct the calendar report URL
var url = 'sys_report_template.do?sysparm_table=change_request&sysparm_type=calendar&sysparm_cal_field=end_date&sysparm_from_list=true&sysparm_manual_labor=true&sysparm_query=^end_date%3Ejavascript:gs.endOfLastMonth()';
if(g_form.getValue(ciVarName) != ''){
url = url + '^cmdb_ci=' + g_form.getValue(ciVarName);
}

//Open the popup
var features = "width=700,height=650,toolbar=no,status=no,directories=no,menubar=no,resizable=yes,scrollbars=1";
popupOpen(url, 'show_calendar', 700, 650, features, true);
}
}

Script (Fuji version):

function onLoad() {
//The reference variable name to put the icon next to
var ciVarName = 'cmdb_ci';

//Add the icon decoration to the reference field
var ciEl = 'lookup.' + g_form.getControl(ciVarName).id;
$(ciEl).insert({
after: '<a id="cmdb_ci_calendar_macro" class="icon-calendar btn btn-default sn-tooltip-basic" style="padding: 6px 10px; vertical-align: text-bottom;" title="" data-original-title="CI Change Calendar"></a>'
});
//Add the 'onclick' event
$('cmdb_ci_calendar_macro').observe('click', showCIChangeCalendar);

function showCIChangeCalendar(){
//Construct the calendar report URL
var url = 'sys_report_template.do?sysparm_table=change_request&sysparm_type=calendar&sysparm_cal_field=end_date&sysparm_from_list=true&sysparm_manual_labor=true&sysparm_query=^end_date%3Ejavascript:gs.endOfLastMonth()';
if(g_form.getValue(ciVarName) != ''){
url = url + '^cmdb_ci=' + g_form.getValue(ciVarName);
}

//Open the popup
var features = "width=700,height=650,toolbar=no,status=no,directories=no,menubar=no,resizable=yes,scrollbars=1";
popupOpen(url, 'show_calendar', 700, 650, features, true);
}
}

When you’re done, your catalog variable will have a calendar link next to it that looks like this…
CI Change Calendar Macro Catalog Variable

The post Building a CI Change Calendar Macro – On The Fly appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/reporting/ci-change-calendar-macro/feed/ 5
Track Affected CIs in One Place https://servicenowguru.com/cmdb/track-affected-cis-one-place/ https://servicenowguru.com/cmdb/track-affected-cis-one-place/#comments Fri, 27 May 2011 12:47:53 +0000 https://servicenowguru.wpengine.com/?p=3740 It should come as no surprise to you that, if used properly, your CMDB can be an extremely valuable input to your incident/problem/change processes. This is true not only of the actual CIs, but also the ‘Affected CI’ records that you create. ServiceNow gives you a couple of different places to track this information. The

The post Track Affected CIs in One Place appeared first on ServiceNow Guru.

]]>
It should come as no surprise to you that, if used properly, your CMDB can be an extremely valuable input to your incident/problem/change processes. This is true not only of the actual CIs, but also the ‘Affected CI’ records that you create. ServiceNow gives you a couple of different places to track this information. The first is the ‘Configuration Item’ field available to all Task types in the system. You can add this field by personalizing the form for any task. The second is the ‘Affected CIs’ (task_ci) many-to-many table. This can be added to any task form in your system by personalizing the related lists for that form.

This setup allows you to track a primary CI or Business Service against a given task in the field on the form, and it also allows you to track multiple Affected CIs against a task if necessary in the related list. What I don’t like about this setup is that these are managed independently so there’s not a single place to see ALL of the Affected CIs in your environment. My solution to this problem has always been to centralize all of this information into the ‘Affected CIs’ related list by copying the ‘Configuration Item’ field value into it. This simple idea gives you a much better look into your Affected CIs for reporting, and allows for more proactive troubleshooting through CI Business Service Maps as an input into your task processes.

This customization is pretty simple and can be accomplished through the application of two business rules. The first business rule sits on the ‘Task’ table and should be configured as follows…

‘Sync CI with Affected CIs’ Business Rule
Name: Sync CI with Affected CIs
Table: Task
When: After
Insert: True
Update: True
Condition: current.cmdb_ci.changes()
Script:

if(previous.cmdb_ci){
   removeCI();
}
if(current.cmdb_ci){
   addCI();
}

function removeCI(){
   //Get Affected CI records for this task and delete the CI if necessary
   var rec = new GlideRecord('task_ci');
   rec.addQuery('task', current.sys_id);
   rec.addQuery('ci_item', previous.cmdb_ci);
   rec.query();
   while(rec.next()){
      //Delete the Affected CI record
      rec.deleteRecord();
   }
}

function addCI(){
   //Get Affected CI records for this task and add the CI if necessary
   var rec = new GlideRecord('task_ci');
   rec.addQuery('task', current.sys_id);
   rec.addQuery('ci_item', current.cmdb_ci);
   rec.query();
   if(!rec.next()){
      //Create a new Affected CI record
      var ci = new GlideRecord('task_ci');
      ci.initialize();
      ci.task = current.sys_id;
      ci.ci_item = current.cmdb_ci;
      ci.insert();
   }
}

The second business rule sits on the ‘CIs Affected’ (task_ci) table and should be configured as follows…

‘Prevent removal/update of primary CI’ Business Rule
Name: Prevent removal/update of primary CI
Table: CIs Affected (task_ci)
When: Before
Update: True
Delete: True
Script:

//Check the parent task to make sure that the CI is not listed there
if(current.ci_item && current.operation() == 'delete' && current.ci_item == current.task.cmdb_ci){
   //Disallow removal
   gs.addInfoMessage('You cannot remove this CI since it is the primary CI on the associated task.</br>Please change or remove the CI from the task form.');
   current.setAbortAction(true);
}
if(current.ci_item.changes() && current.operation() == 'update' && previous.ci_item == current.task.cmdb_ci){
   //Disallow modification
   gs.addInfoMessage('You cannot change this CI since it is the primary CI on the associated task.</br>Please change or remove the CI from the task form.');
   current.setAbortAction(true);
}

Harnessing the power of the CIs Affected (task_ci) table

Once this is done, any change to the ‘Configuration Item’ field on a task record will automatically result in the creation of an ‘Affected CIs’ entry for that same CI. Now you can set up all of your reports for this area against this table. One of the most powerful things you can do with this data is to set up a BSM Map Action icon that can be displayed to show people from within a BSM map which CIs are being affected by different tasks in the system. There’s a great example on the ServiceNow Wiki that shows you how you can do this.

Affected CIs BSM Map Actions

The post Track Affected CIs in One Place appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/cmdb/track-affected-cis-one-place/feed/ 7
Sending iCalendar Invites from any Task table https://servicenowguru.com/email-notifications-system-definition/sending-calendar-invites-task-table/ https://servicenowguru.com/email-notifications-system-definition/sending-calendar-invites-task-table/#comments Thu, 09 Dec 2010 13:08:36 +0000 https://servicenowguru.wpengine.com/?p=1707 ServiceNow has a pretty simple one-way Outlook calendar integration that you can use to send out iCal meeting requests and updates to assignees involved with a Change request. I’m often asked if this functionality can be used for meeting invites in other tables as well. The answer is Yes! This hasn’t really been documented because

The post Sending iCalendar Invites from any Task table appeared first on ServiceNow Guru.

]]>
ServiceNow has a pretty simple one-way Outlook calendar integration that you can use to send out iCal meeting requests and updates to assignees involved with a Change request. I’m often asked if this functionality can be used for meeting invites in other tables as well. The answer is Yes! This hasn’t really been documented because it relies on some legacy technology that really isn’t that prominent in the system anymore. In this article, I’ll show you the pieces that make this calendar integration work by showing you how to set the integration up for the change task table.

Please make sure you can send invites from change requests in your system before attempting this custom setup. If you can’t get change requests to work you’ll need to contact ServiceNow support.

The out-of-box iCalendar integration for Change requests relies on a few different pieces. Most of the pieces are actually standard email notification business rules, events, and templates. The real mystery to this integration lies in the start/end date mappings for the meeting request. These are important because they define the fields on the task that the email template pulls its dates from. Once you get this set up correctly all you’ve really got to do is set up some standard email notifications with a template that I’ll show you below.

Setting up the start/end date field mappings

Note: Since we’re basing our setup off of the out-of-box Change request setup you can use the ‘icalendar.change_request’ map as a reference while setting this up for another table.

–Navigate to the legacy ‘Import Export Map’ table by typing ‘sys_impex_map.list’ in your left navigation filter.
–We’ll create a new entry that ends up looking like this…

–Create the map entry as shown in the image above
–Click the ‘New’ button on the ‘Field maps’ related list to create 2 field maps for your start and end dates
–Select the ‘Mapping to a database field’ option from the wizard

–Map your start and end dates as shown in the images below. For the Change task table, I’ve decided to use the ‘Expected start’ field for my start date and the ‘Due date’ field my end date. You MUST use ‘dtstart’ and ‘dtend’ for the external names for iCalendar integrations.


Once you’ve got the legacy Import/Export map configured, the rest is really just a standard email notification setup. You’ll need an event trigger, an event, and a notification that responds to that event. I’m not going to go over the specifics of setting up an email notification here. You can reference the ServiceNow wiki for that.

The out-of-box Change request setup uses a section of code in the ‘change events’ business rule. Even though you can trigger an event from lots of different places in the system, I think the business rule setup is going to work the best for most scenarios. The great thing about borrowing the code from the Change request table is that it already takes care of all of the different conditions for you. All you have to do is modify the field and event names in the script to go with your custom setup. I’ve modified the below to work with the ‘Expected start’ and ‘Due date’ fields from the Change task table. I’ve also modified the script to trigger 2 new events (which you’ll need to create), called ‘change_task.calendar.notify’ and ‘change_task.calendar.notify.remove’.

‘change_task send icalendar’ business rule
Name: change_task send icalendar
Table: change_task
When: after
Insert/Update: true
Script:

if (current.expected_start.changes() || current.due_date.changes() || current.assigned_to.changes()) {
if (!current.expected_start.nil() && !current.due_date.nil() && !current.assigned_to.nil()) {
gs.eventQueue("change_task.calendar.notify", current, current.assigned_to, previous.assigned_to);
}// Remove from previous assigned to, due to assigned_to changing
if (!previous.assigned_to.nil()) {
if (!current.assigned_to.nil() && current.assigned_to.changes() &&
(!previous.expected_start.nil() && !previous.due_date.nil())) {
gs.eventQueue("change_task.calendar.notify.remove", current, current.assigned_to, previous.assigned_to);
}
}
// Remove old calendar from current assigned to, due to date changing
else if (!current.assigned_to.nil()) {
if ((current.expected_start.changes() && !previous.due_date.nil()) ||
(current.expected_start.changes() && !previous.due_date.nil())) {
gs.eventQueue("change_task.calendar.notify.remove", current, current.assigned_to, current.assigned_to);
}
}
}

Once you set up your event trigger and corresponding events, you need to set up your notifications to be triggered by those events.
The final step then (and probably the most important) is to set up your email messages or templates. Again, I’m not going to go into specifics here about how to set up an email notification. It doesn’t matter if set up your message in the notification or template, but since the out-of-box Change request iCal setup has this in email templates I’ve decided to do the same for this one for consistency. The ‘Subject’ here is strictly a label. The real subject is actually populated through the ‘Summary’ line in the body of the email.

If you’ve set up your variable mappings correctly in the steps above you shouldn’t have to customize this piece at all. Just paste the contents of each of the sections below into the ‘Message’ box on your notification or template setup, set the correct table, and go. You’ll most likely want to have 2 notifications, one for an event request and one for cancellation.

If you need a guide you can look at the ‘Notify Change Calendar’ and ‘Notify Change Calendar Remove’ Email Notifications on the ‘change_request’ table (along with their associated templates).

‘change_task.calendar.integration’ Email Templates

Calendar invite template

Name: change_task.calendar.integration
Table: change_task
Subject: SPECIAL CASE TEMPLATE — Push change tasks into an outlook calendar
Message:

BEGIN:VCALENDAR
PRODID:-//Service-now.com//Outlook 11.0
MIMEDIR//EN
VERSION:2.0
METHOD:REQUEST
BEGIN:VEVENT
ATTENDEE;ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:${to}
ORGANIZER:MAILTO:${from}
DTSTART:${dtstart}
DTEND:${dtend}
LOCATION:${location}
TRANSP:OPAQUE
SEQUENCE:${sys_mod_count}
UID:${uid}
DTSTAMP:${dtstamp}
SUMMARY:${summary}
DESCRIPTION:${description}
PRIORITY:${priority}
X-MICROSOFT-CDO-IMPORTANCE:${priority}
STATUS:CONFIRMED
CLASS:PUBLIC
END:VEVENT
END:VCALENDAR

Calendar cancellation template

Name: change_task.calendar.integration.remove
Table: change_task
Subject: SPECIAL CASE TEMPLATE — Push change tasks into an outlook calendar
Message:

BEGIN:VCALENDAR
PRODID:-//Service-now.com//Outlook 11.0 MIMEDIR//EN
VERSION:2.0
METHOD:CANCEL
BEGIN:VEVENT
ATTENDEE;ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:${to}
ORGANIZER:MAILTO:${from}
LOCATION:${location}
TRANSP:OPAQUE
DTSTART:${dtstart}
DTEND:${dtend}
SEQUENCE:${sys_mod_count}
UID:${uid}
DTSTAMP:${dtstamp}
DESCRIPTION:${description}
SUMMARY:${summary}
PRIORITY:${priority}
X-MICROSOFT-CDO-IMPORTANCE:${priority}
STATUS:CANCELLED
CLASS:PUBLIC
END:VEVENT
END:VCALENDAR

That’s it! If you’ve done everything correctly you should be able to send calendar invites from your change task table just like you can for change requests.

Can I send To Do Tasks or Notes too?

ServiceNow can send them if you modify the email template with the correct ‘VTODO’ or ‘VJOURNAL’ tags, but most email clients (including all from Microsoft) don’t support it. So, you can send all day, but chances are your email client won’t handle it!

The post Sending iCalendar Invites from any Task table appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/email-notifications-system-definition/sending-calendar-invites-task-table/feed/ 34
Change Management Workflow Approval Scripts in ServiceNow https://servicenowguru.com/scripting/change-management-workflow-approval-scripts-servicenowcom/ https://servicenowguru.com/scripting/change-management-workflow-approval-scripts-servicenowcom/#comments Wed, 20 Oct 2010 16:25:52 +0000 https://servicenowguru.wpengine.com/?p=2193 Service-now.com provides a really cool Graphical Workflow engine to help manage workflows for different tasks. This graphical workflow engine is particularly useful when working with approvals for Change Requests. As a ServiceNow consultant I’ve found that change approvals usually fall into just a few different types but new administrators and consultants sometimes don’t know the

The post Change Management Workflow Approval Scripts in ServiceNow appeared first on ServiceNow Guru.

]]>
Service-now.com provides a really cool Graphical Workflow engine to help manage workflows for different tasks. This graphical workflow engine is particularly useful when working with approvals for Change Requests. As a ServiceNow consultant I’ve found that change approvals usually fall into just a few different types but new administrators and consultants sometimes don’t know the best way to implement approvals. In this post I’ll share some of the common change workflow approval methods and scripts I’ve seen used before. I wouldn’t be surprised to see this list grow over time and I know I haven’t seen all of the common methods. If you have something you’ve used before please comment on this post or use the ‘Contact’ link above to send in your suggestion to share.

Simple Approvers Selection (Select Specific Group)

The simplest (and most common) type of approval is to select specific groups or users to approve the change. The screenshot below shows how you can use the standard reference field lookup to select a group to add as approvers to the change request. This method is commonly used when you want to have a specific group (like the CAB) approve at some point in your workflow.

Simple Approvers Selection (Drill to Related Records)

Another common method is to drill into the change request itself and pull the approvers from a field on the change. This method is commonly used when you want to have something like a manager approval for the person requesting the change (as shown below) or maybe when you want to have the assignment group on the change request approve.

Advanced Approvers Script (Approval Groups for all Change CIs)

The ServiceNow Graphical Workflow mechanism also allows you be even more complex in the selection of approvals for your change requests. You can use script with GlideRecord queries to return any users or groups you want to have approve. The one thing to keep in mind with these scripts is that you need to return group records if your workflow activity is a Group Approval type and user records if your workflow activity is a User Approval type.
One common request I’ve seen before that requires a script is to add approval groups based on the Configuration Items associated to the change request. Chances are you’ll have multiple CIs associated on the ‘Affected CIs’ related list for the change. This script queries for those records and then adds the groups from the ‘Approval Group’ field on each CI.

//Initialize an answer array to be returned
var answer = [];

//Add the primary CI approval group to the array
answer.push(current.cmdb_ci.change_control);

//Add the Affected CIs list approval groups to the array
var affCIs = new GlideRecord('task_ci');
affCIs.addQuery('task', current.sys_id);
affCIs.query();
while(affCIs.next()){
answer.push(affCIs.ci_item.change_control);
}

Advanced Approvers Script (Approval Groups for all Impacted Change CIs)

There are some rare cases where clients want to pull approval groups from other CIs that will be impacted by the change based on CI relationships. For these cases you can use the CIUtils2 script I created to walk the CI relationship tree and find the impacted CIs that you need to add approvals for. In order to use these scripts you’ll need to add the CIUtils2 script include to your system first. You’ll also want to use this type of approval method sparingly since it involves a sometimes resource-intensive traversal of your CMDB (if you have hundreds of thousands of CIs). It’s also pretty expensive process-wise as well. Do you really want to require approval from every single group in your company when your network team needs to make a change to the core router? Maybe so, maybe not, but either way you’re probably talking about a lot of approvals because that CI impacts so many other CIs in your environment.

Impacted CIs approval scripts
–This first example returns Approval Groups for all of the Business Services impacted by the change request–

//Initialize a variable for the CIUtils2 Script include
var ciu = new CIUtils2();
//Initialize an answer array to be returned
var answer = [];//Add any impacted business services for the change
var allCIs = ciu.cisAffectedByTask(current);

//Query for all CIs and return Approval Groups
for (var i = 0; i < allCIs.length; i++) {
var cis = new GlideRecord('cmdb_ci');
cis.addQuery('sys_id', allCIs[i]);
cis.query();
if (cis.next()) {
answer.push(cis.change_control);
}
}

–This example returns Approval Groups for all of the Business Services impacted by the change request AND Approval Groups for all directly Affected CIs on the change request. It utilizes the ‘CheckDuplicates‘ function I wrote to eliminate some unnecessary processing.–

//Initialize a variable for the CIUtils2 Script include
var ciu = new CIUtils2();
//Initialize an answer array to be returned
var answer = [];
//Initialize an array to gather and store all impacted CIs
var allCIs = [];

//Add the primary CI to the CI array
allCIs.push(current.cmdb_ci);

//Add the Affected CIs list to the CI array
var affCIs = new GlideRecord('task_ci');
affCIs.addQuery('task',current.sys_id);
affCIs.query();
while (affCIs.next()) {
allCIs.push(affCIs.ci_item);
}

//For each directly affected CI on the change, add any impacted business services
for (var i = 0; i < allCIs.length; i++) {
allCIs = allCIs.concat(ciu.cisAffectedByCI(allCIs[i]));
}

//Remove duplicate CIs from the array
allCIs = checkDuplicates(allCIs);

//Query for all CIs and return Approval Groups
for (var j = 0; j < allCIs.length; j++) {
var cis = new GlideRecord('cmdb_ci');
cis.addQuery('sys_id', allCIs[j]);
cis.query();
while (cis.next()) {
answer.push(cis.change_control);
}
}

function checkDuplicates(a) {
//Check all values in the incoming array and eliminate any duplicates
var r = []; //Create a new array to be returned with unique values
//Iterate through all values in the array passed to this function
o:for(var i = 0, n = a.length; i < n; i++){
//Iterate through any values in the array to be returned
for(var x = 0, y = r.length; x < y; x++){
//Compare the current value in the return array with the current value in the incoming array
if(r[x]==a[i]){
//If they match, then the incoming array value is a duplicate and should be skipped
continue o;
}
}
//If the value hasn't already been added to the return array (not a duplicate) then add it
r[r.length] = a[i];
}
//Return the reconstructed array of unique values
return r;
}

Ensuring someone approves

One of the big problems you may encounter is what happens if the approvers don’t exist or aren’t active. The behavior in ServiceNow is to mark the approval activity as ‘Approved’ if no approvers are returned by the activity.

Change admin failsafe approval activity
–This script looks to see if an approver is returned, and if not it pulls in the ‘Change admins’ group as approvers for the approval activity.–

// Set the variable 'answer' to a comma-separated list of user/group ids or an array of user/group ids to add as approvers.
//
// For example:
// var answer = [];
// answer.push('id1');
// answer.push('id2');var answer = [];
//Requested by and assignment group manager approval
if(current.requested_by.manager.active){
answer.push(current.requested_by.manager.sys_id);
}
if(current.assignment_group.manager.active){
answer.push(current.assignment_group.manager.sys_id);
}

//Ensure 'Change Admins' group gets added if no other approvers (optional)
if(answer.length == 0){
var grpName = 'Change Admins';
//Query for change admins group
var cGrp = new GlideRecord('sys_user_group');
cGrp.get('name', grpName);
answer.push(cGrp.sys_id);
}

Making a specific person the key approver

Some approval scenarios may also require that a specific individual has the full approval/rejection responsibility even as a member of a group. Even though others can approve or reject, a single person has the final say. Once they approve or reject, we move on from the activity. The following script can be used in an ‘Approval – User’ workflow activity to facilitate this type of setup.

Key Person Approval workflow activity
This script should be placed in the ‘Approval Script’ field in a ‘Approval – User’ workflow activity. The ‘Approval Script’ field displays when the ‘Wait for’ field is set to ‘Condition based on script’ as shown in the following screenshot.

//'keyApproverID' must approve or reject to advance approval
//Set the 'keyApproverID' variable to the sys_id of the user record of the key approver
var keyApproverID = '97000fcc0a0a0a6e0104ca999f619e5b';
if (approvalIDs) {
if (approvalIDs['approved'].indexOf(keyApproverID) > -1) {
answer = 'approved';
}
if (approvalIDs['rejected'].indexOf(keyApproverID) > -1) {
answer = 'rejected';
}
}

The post Change Management Workflow Approval Scripts in ServiceNow appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/scripting/change-management-workflow-approval-scripts-servicenowcom/feed/ 18
Wait for Closure of all Tasks in Graphical Workflow https://servicenowguru.com/graphical-workflow/wait-closure-tasks-graphical-workflow/ https://servicenowguru.com/graphical-workflow/wait-closure-tasks-graphical-workflow/#comments Mon, 16 Aug 2010 14:57:14 +0000 https://servicenowguru.wpengine.com/?p=1909 A common Graphical Workflow requirement in ServiceNow is to tell the workflow to wait for some trigger before continuing. The ‘Wait For condition’ activity is available out-of-box and is very simple to configure. Usually when working with Service Requests or Change Requests I am asked how you can set up the workflow to wait for

The post Wait for Closure of all Tasks in Graphical Workflow appeared first on ServiceNow Guru.

]]>
A common Graphical Workflow requirement in ServiceNow is to tell the workflow to wait for some trigger before continuing. The ‘Wait For condition’ activity is available out-of-box and is very simple to configure. Usually when working with Service Requests or Change Requests I am asked how you can set up the workflow to wait for completion of all associated tasks before closing the Request Item or Change Request ticket. This can be easily accomplished by using a script in your ‘Wait For condition’ activity to query for any associated tasks that are still marked as ‘Active’. If the task is marked as ‘Active’ then it hasn’t been closed yet and the workflow should wait for that closure. Here are some sample scripts that I’ve used before to wait for task completion on both Request Items and Change Requests.


The scripts below can be pasted into the ‘Wait For condition’ activity script field as shown here…

Request Item ‘Wait For’ Scripts
Request Item – Wait for Closure of All Tasks:

//Query for all tasks to see if they are inactive
var rec = new GlideRecord('sc_task');
rec.addQuery('request_item', current.sys_id);
rec.addQuery('active', true);
rec.query();
if(rec.hasNext()){
answer = false;
}
else{
//Continue
answer = true;
}

Request Item – Wait for Closure of Workflow-generated Tasks:

//Query for all tasks to see if they are inactive
var rec = new GlideRecord('sc_task');
rec.addQuery('request_item', current.sys_id);
rec.addNotNullQuery('wf_activity');
rec.addQuery('active', true);
rec.query();
if(rec.hasNext()){
answer = false;
}
else{
//Continue
answer = true;
}
Change Request ‘Wait For’ Scripts
Change Request – Wait for Closure of All Tasks:

//Query for all tasks to see if they are inactive
var rec = new GlideRecord('change_task');
rec.addQuery('change_request', current.sys_id);
rec.addQuery('active', true);
rec.query();
if(rec.hasNext()){
answer = false;
}
else{
//Continue
answer = true;
}

Change Request – Wait for Closure of Workflow-generated Tasks:

//Query for all tasks to see if they are inactive
var rec = new GlideRecord('change_task');
rec.addQuery('change_request', current.sys_id);
rec.addNotNullQuery('wf_activity');
rec.addQuery('active', true);
rec.query();
if(rec.hasNext()){
answer = false;
}
else{
//Continue
answer = true;
}
Whether or not a task is checked in a workflow depends on the ‘Parent’ association as defined in the ‘SNC – Run parent workflows’ business rule on the task table. You may have to modify that business rule or create a modified version of it for the tables you’re working with. The logic behind the business rule is that it waits for certain closure conditions on child tasks and then runs workflows on any associated parents to check for things like ‘Wait for’ conditions. If your child tasks don’t meet these conditions then the parent task workflow will not be triggered when updates to the tasks are made.

The post Wait for Closure of all Tasks in Graphical Workflow appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/graphical-workflow/wait-closure-tasks-graphical-workflow/feed/ 24
‘Copy’ UI action for Change requests (Part 2!) https://servicenowguru.com/ui-actions-system-ui/copy-ui-action-change-requests-part-2/ https://servicenowguru.com/ui-actions-system-ui/copy-ui-action-change-requests-part-2/#comments Tue, 27 Jul 2010 16:42:41 +0000 https://servicenowguru.wpengine.com/?p=1838 A few months ago I wrote about copying change requests using a UI action. While that method works great, it does require you to specify each and every field and value that you want to populate into the new change request. If you’ve got a lot of fields to copy over then you might end

The post ‘Copy’ UI action for Change requests (Part 2!) appeared first on ServiceNow Guru.

]]>
A few months ago I wrote about copying change requests using a UI action. While that method works great, it does require you to specify each and every field and value that you want to populate into the new change request. If you’ve got a lot of fields to copy over then you might end up with a pretty big script and a lot of items to copy over. You also need to be aware of any new fields that get added after you create the script and make sure that they get copied if necessary.

The following method works in much the same way, but it copies by performing an insert against the current record (rather than starting from a brand new change record and supplying each value). Because of this, you’re concerned about overriding any of the values (such as start and end dates) that you DON’T want to be copied over from the record you are copying. This method works better if you know you want to copy over all (or the majority) of the field values from a given change.

Copy Change UI action
Name: Copy change
Form button: True
Table: Change Request
Condition: gs.hasRole(“itil”) && current.isValidRecord()
Script:

copyChange();
function copyChange() {
	//Get the current sys_id value for querying
	var chgID = current.sys_id.toString();
	//Initialize new change for insertion
	var newChange = current;
	newChange.number = getNextObjNumberPadded(); //Get next change number
	newChange.requested_by_date = 'NULL';
	newChange.start_date = 'NULL';
	newChange.end_date = 'NULL';
	newChange.calendar_duration = 'NULL';
	newChange.opened_at = current.opened_at;
	newChange.opened_by = current.opened_by;
	newChange.sys_created_on = current.sys_created_on;
	newChange.sys_created_by = current.sys_created_by;
	current.insert();
	
	//Copy attachments for this change
	if (typeof GlideSysAttachment != 'undefined')
		GlideSysAttachment.copy('change_request', chgID, 'change_request', newChange.sys_id);
	else
		Packages.com.glide.ui.SysAttachment.copy('change_request', chgID, 'change_request', newChange.sys_id);
	
	//Copy associated tasks and CIs
	copyTask(chgID);
	copyCI(chgID);
	gs.addInfoMessage('Change ticket ' + newChange.number + ' created.')
	action.setRedirectURL(newChange);
}

function copyTask(chgID) {
	//Find the current change tasks and copy them
	var tasks = new GlideRecord('change_task');
	tasks.addQuery('change_request', chgID);
	tasks.query();
	while(tasks.next()){
		var taskID = tasks.sys_id.toString();
		var newTask = tasks;
		if (typeof GlideNumberManager != 'undefined')
			newTask.number = GlideNumberManager.getNumber('change_task');
		else
			newTask.number = Packages.com.glide.db.NumberManager.getNumber('change_task'); //Get next change task number
		newTask.change_request = current.sys_id;
		tasks.insert();
		
		//Copy attachments for this task
		if (typeof GlideSysAttachment != 'undefined')
			GlideSysAttachment.copy('change_task', taskID, 'change_task', tasks.sys_id);
		else
			Packages.com.glide.ui.SysAttachment.copy('change_task', taskID, 'change_task', tasks.sys_id);
	}
}

function copyCI(chgID) {
	//Get the task record being copied
	var tskRec = new GlideRecord('task');
	if(tskRec.get(chgID)){
		//Copy over the affected CI list
		var cis = new GlideRecord('task_ci');
		cis.addQuery('task', chgID);
                cis.addNullQuery('u_ci_group'); //Added to ensure that copying does not duplicate Group CIs
		if(gs.getProperty('change.conflict.mode')=='advanced' && tskRec.cmdb_ci!=''){
			//Prevent duplicate CI from being added
			cis.addQuery('ci_item', '!=', tskRec.cmdb_ci.toString());
		}
		cis.query();
		while(cis.next()){
			var newCI = cis;
			newCI.task = current.sys_id;
			cis.insert();
		}
	}
}

One other method for copying a change (or any other ticket) is to reference the record to copy and copy from the referenced record. You could use the script below to copy a change that is pulled from a reference field on any form. Just provide the name of the field to grab the change from in line 3.

Copy change UI action (For copying a referenced Change Request

//Get the current sys_id value for querying
//Provide the name of the reference field to copy change from
var chgID = current.your_change_reference_field.toString();
var rec = new GlideRecord('change_request');
rec.get(chgID);
copyChange();

function copyChange() {
	//Initialize new change for insertion
	var newChange = rec;
	newChange.number = getNextObjNumberPadded(); //Get next change number
	newChange.requested_by_date = 'NULL';
	newChange.start_date = 'NULL';
	newChange.end_date = 'NULL';
	newChange.calendar_duration = 'NULL';
	newChange.opened_at = current.opened_at;
	newChange.opened_by = current.opened_by;
	newChange.sys_created_on = current.sys_created_on;
	newChange.sys_created_by = current.sys_created_by;
	rec.insert();
	
	//Copy attachments for this change
	if (typeof GlideSysAttachment != 'undefined')
		GlideSysAttachment.copy('change_request', chgID, 'change_request', newChange.sys_id);
	else
		Packages.com.glide.ui.SysAttachment.copy('change_request', chgID, 'change_request', newChange.sys_id);
	
	//Copy associated tasks and CIs
	copyTask(chgID);
	copyCI(chgID);
	gs.addInfoMessage('Change ticket ' + newChange.number + ' created.');
	action.setRedirectURL(rec);
}

function copyTask(chgID) {
	//Find the current change tasks and copy them
	var tasks = new GlideRecord('change_task');
	tasks.addQuery('change_request', chgID);
	tasks.query();
	while(tasks.next()){
		var taskID = tasks.sys_id.toString();
		var newTask = tasks;
		if (typeof GlideNumberManager != 'undefined')
			newTask.number = GlideNumberManager.getNumber('change_task');
		else
			newTask.number = Packages.com.glide.db.NumberManager.getNumber('change_task'); //Get next change task number
		newTask.change_request = rec.sys_id;
		tasks.insert();
		
		//Copy attachments for this task
		if (typeof GlideSysAttachment != 'undefined')
			GlideSysAttachment.copy('change_task', taskID, 'change_task', tasks.sys_id);
		else
			Packages.com.glide.ui.SysAttachment.copy('change_task', taskID, 'change_task', tasks.sys_id);
	}
}

function copyCI(chgID) {
	//Get the task record being copied
	var tskRec = new GlideRecord('task');
	if(tskRec.get(chgID)){
		//Copy over the affected CI list
		var cis = new GlideRecord('task_ci');
		cis.addQuery('task', chgID);
                cis.addNullQuery('u_ci_group'); //Added to ensure that copying does not duplicate Group CIs
		if(gs.getProperty('change.conflict.mode')=='advanced' && tskRec.cmdb_ci!=''){
			//Prevent duplicate CI from being added
			cis.addQuery('ci_item', '!=', tskRec.cmdb_ci.toString());
		}
		cis.query();
		
		while(cis.next()){
			var newCI = cis;
			newCI.task =
			rec.sys_id;
			cis.insert();
		}
	}
}

The post ‘Copy’ UI action for Change requests (Part 2!) appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/ui-actions-system-ui/copy-ui-action-change-requests-part-2/feed/ 25
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.

]]>