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.
Name: Copy change
Form button: True
Table: Change Request
Condition: gs.hasRole(“itil”) && current.isValidRecord()
Script:
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.
//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, 2011Posted By
Peter OneppoMark,
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, 2011Posted By
Mark StangerHey, thanks so much for the feedback. I’ve updated the scripts above to include this fix. Good catch!
Posted On
Mar 20, 2011Posted By
Jim CoyneThanks 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, 2011Posted By
Manny TsoplakisHi, 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, 2011Posted By
Mark StangerYou 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…
If you don’t want to copy the associated CIs then just comment out this line in the code…
Posted On
Jan 03, 2012Posted By
Scott HetzelMark,
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, 2012Posted By
Mark StangerGood 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.
Posted On
Jan 04, 2012Posted By
Scott HetzelMark.. That worked, perfect.
Thanks for the help! (also…nice meeting you and Jacob in San Diego in early December)
Posted On
Jan 04, 2012Posted By
Mark StangerI’m glad it worked! It was great to meet you too.