SATURDAY, MAY 19, 2012

‘Copy’ UI action for Change requests (Part 2!)

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
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;
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
Packages.com.glide.ui.SysAttachment.copy('change_task', taskID, 'change_task', tasks.sys_id);
}
}

function copyCI(chgID) {
//Copy over the affected CI list
var cis = new GlideRecord('task_ci');
cis.addQuery('task', chgID);
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
Packages.com.glide.ui.SysAttachment.copy('change_request', chgID, 'change_request', rec.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;
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
Packages.com.glide.ui.SysAttachment.copy('change_task', taskID, 'change_task', tasks.sys_id);
}
}

function copyCI(chgID) {
//Copy over the affected CI list
var cis = new GlideRecord('task_ci');
cis.addQuery('task', chgID);
cis.query();

while(cis.next()){
var newCI = cis;
newCI.task =
rec.sys_id;
cis.insert();
}
}

Comments

Posted On
Jan 26, 2011
Posted By
Peter Oneppo

Mark,

This is great. We just implemented it for one of our clients, but we ran into a small snag. We were getting duplicate numbers for the copied records. I think the problem is related to the fact that the customer has the check box for “Assign a task number only upon insert (prevents unused numbers)” under “System Properties -> System” selected.

We solved this problem by adding one line of code to the above script:

var newChange = current;

newChange.number = getNextObjNumberPadded(); //new line to fix duplicate number problem

newChange.requested_by_date = ‘NULL’;

Thanks!

Peter

Posted On
Jan 26, 2011
Posted By
Mark Stanger

Hey, thanks so much for the feedback. I’ve updated the scripts above to include this fix. Good catch!

Posted On
Mar 20, 2011
Posted By
Jim Coyne

Thanks for the article, came in handy with a client. I was a little confused at first when I saw you were making changes to the “newChange” variable but then used “current.insert();” to make the insert of the record. I then realized that “var newChange = current;” actually keeps a live connection between both objects, so any change to one affects the other. I’m no JavaScript expert, but I guess it may not even be worth creating the “newChange” variable at all and just use “current”.

Posted On
Oct 06, 2011
Posted By
Manny Tsoplakis

Hi, this works pretty well but when i implement this, the change is copying over with approved as the “approval”. It is also copying over the journal entries in the activity list. Also, is there a way to copy the existing record without adding the CI? thanks

Posted On
Oct 06, 2011
Posted By
Mark Stanger

You can override any value on the change request you generate by adding a new ‘newChange’ line in the section of code that copies the change request. For approval you could do something like this…

newChange.approval = 'not_requested';

If you don’t want to copy the associated CIs then just comment out this line in the code…

copyCI(chgID);
Posted On
Jan 03, 2012
Posted By
Scott Hetzel

Mark,

I’m using the first script above and when copying change_tasks it gives me the number prefix defined for the change_request table. I get RFC1234 instead of the desired CTASK1234.

Thanks,
Scott

Posted On
Jan 03, 2012
Posted By
Mark Stanger

Good catch Scott. The numbering is controlled by the ‘getNextObjNumberPadded’ line. The issue in this case is that we’re dealing with the change request context and the change tasks…and that function doesn’t understand both of them. The only way I know of to get around this is to use a Packages call in place of the standard call. I’ve updated the scripts above with this fix. Let me know how it works for you.

newTask.number = Packages.com.glide.db.NumberManager.getNumber('change_task');
Posted On
Jan 04, 2012
Posted By
Scott Hetzel

Mark.. That worked, perfect.

Thanks for the help! (also…nice meeting you and Jacob in San Diego in early December)

Posted On
Jan 04, 2012
Posted By
Mark Stanger

I’m glad it worked! It was great to meet you too.

Posted On
Feb 08, 2012
Posted By
Paul Davidson

Hi Mark,

I was able to follow your logic to create additional copy modules for the other “Related Lists” that we use on our Change tickets. To ensure appropriate logging, I was careful to manually set all of the table columns like sys_created_by, sys_created_on, sys_updated_by, sys_updated_on, etc.

In your copyCI() script you set the ‘task_group’ table’s “task” column to the current.sys_id. I see that the task column is a reference field, so I’m assuming it is referencing the newly created Change ticket’s sys_id. I guess I’m asking if the current object at this point is the Change ticket or the ‘task_group’ record?

What about the sys_id column within the ‘task_group’ table? Are newly created records getting the same sys_id from the existing record that it is copying from? Do I need to manually set the sys_id within ‘task_group’ table?, or is it getting a unique value at Insert?

It is nice that people like you share your effort gained Knowledge with the rest of us, so we can have a starting point for learning ourselves.

Thanks again!
-Paul

Posted On
Feb 08, 2012
Posted By
Mark Stanger

Hey Paul,

I assume you’re talking about ‘task_ci’, not ‘task_group. If so, the ‘current.sys_id’ line refers to the new change record being created. The sys_id of the ‘task_ci’ record will be automatically generated so you shouldn’t need to worry about setting it.

Thanks for the feedback on SNCGuru! Let me know if you’ve got any suggestions.

Leave a Reply


Notify me of followup comments via e-mail. You can also subscribe without commenting.

Latest Comments

  • Jim Coyne: I’m not sure exactly what you are looking for, but can you use “window.location” in your...
  • Ian: Might want to check the single quotes around ITIL in the condition line, they gave an error for me until I...
  • Mark Stanger: That’s correct. This returns instance URLs. I don’t have an equivalent currently that...
  • ND: Hi Mark, This is very useful information. I am looking for similar method to find URL of a site created by us. We...