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

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

‘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
    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();
        }
    }
}
By | 2018-07-09T15:00:08-06:00 July 27th, 2010|Categories: UI actions|Tags: , , |25 Comments

About the Author:

25 Comments

  1. Peter Oneppo January 26, 2011 at 8:52 am - Reply

    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

    • Mark Stanger January 26, 2011 at 9:17 am - Reply

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

  2. Jim Coyne March 20, 2011 at 4:36 am - Reply

    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”.

  3. Manny Tsoplakis October 6, 2011 at 7:27 am - Reply

    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

    • Mark Stanger October 6, 2011 at 2:31 pm - Reply

      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);
  4. Scott Hetzel January 3, 2012 at 3:48 pm - Reply

    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

    • Mark Stanger January 3, 2012 at 5:23 pm - Reply

      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');
      • Scott Hetzel January 4, 2012 at 7:17 am - Reply

        Mark.. That worked, perfect.

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

        • Mark Stanger January 4, 2012 at 7:30 am - Reply

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

  5. Paul Davidson February 8, 2012 at 7:14 am - Reply

    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

    • Mark Stanger February 8, 2012 at 11:51 am - Reply

      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.

  6. Jonathan Parker November 16, 2012 at 7:19 am - Reply

    I am a new user of Service Now with a new job where I have to create a service ticket per Server that I am working on. Many times there are multiple servers with the same action, like for instance back-up “snap-shot” of a server. So I have to open 20 individual Service Now tickets for the exact same duplicate actions. I see here where there is a way for the Service Now administrators/developers to create a button to “clone” a ticket. I assume this is a “Custom” feature you all are speaking of? If so, how do I get this “feature” implemented?

    • Mark Stanger November 16, 2012 at 9:14 am - Reply

      @Jonathan The only way to implement it is to set up a copy action as described above. The action will need to query for, and copy both the parent ticket and any related child tickets. The code above gives an excellent model for accomplishing this. You just need to substitute your table and field names.

  7. deb November 20, 2012 at 9:34 am - Reply

    I’ve added this script, including the line regarding the Approval status,and it’s working fine, but I’d like to prevent the approval requests from being copied – how would I accomplish that?

    • Mark Stanger November 24, 2012 at 6:27 am - Reply

      There’s nothing in the script that copies approval requests. If they’re being copied or added, it’s probably due to an associated workflow or something else, not this script.

  8. Chris K September 13, 2013 at 12:22 pm - Reply

    Hi Everyone, is there a way to clone a change request that has custom variables in the template?

    • Chris K September 18, 2013 at 8:33 am - Reply

      Using the following script works fine but not all of the variables get cloned over

  9. sanjeev kumra January 15, 2014 at 5:20 am - Reply

    Hi All,

    How we copy request created from record producer.

  10. Mac August 7, 2014 at 5:27 am - Reply

    Hi Mark,

    I’m just testing your ‘Copy Change UI action’ script in our Dev Instance and it works almost perfectly 😉 Unfortunately, every change I copy is duplicating the Affected CIs.

    The only things I’ve changed are to comment out the copyTask(chgID); line as I don’t want to copy Change Tasks and I’ve added a couple of our specific user fields into the newchange section at the top.

    I’m not much good at scripting, so any pointers would be really appreciated!

    • Mark Stanger August 7, 2014 at 7:25 am - Reply

      First thing you should do is set the UI action up in a ServiceNow demo instance and make sure that it’s not doing the same thing there. That will tell you if the problem is with the UI action or something else unique to your system. Assuming the problem is unique to your system, I would check to see if you’re running multiple updates (using current.update()) in any business rules on your change table. You should also check the ‘task_ci’ table to see if it is using business rules to automatically insert affected CIs.

      • Mac August 7, 2014 at 8:17 am - Reply

        I always forget about those Demo Instances; sorry I’m fairly new at this ServiceNow stuff…

        Doesn’t happen on Demo, so must be something we’ve done. I’ll do some digging around in our Dev Instance to see if I can find the problem.

        BTW, I’ve learnt loads from your site, it’s really helped me out a lot. Thanks very much for the quick reply and for pointing me in the right direction!

        • Mark Stanger August 7, 2014 at 8:27 am - Reply

          Thanks for the comment. Good luck finding the issue in your system!

          • Mac August 8, 2014 at 7:14 am - Reply

            I debugged the Business Rules and found out what’s causing the problem. We have the Conflict Properties Mode set to ‘Advanced’, which fires the ‘Add CI in affected Cis List’ business rule. This is what is duplicating the main Config Item in the Affected CI’s list.

            Our Change Manager wants this property left on ‘Advanced’. Not being much of a scripter, would it be possible to add something to the business rule condition or the end of your script to stop the rule firing when we use the ‘copyChange’ function?

            • Mark Stanger August 8, 2014 at 7:44 am

              Try the updated scripts above and see if that works better for you. I’ve added checks for that property to make sure the UI action doesn’t copy the primary CI if the business rule is going to run.

  11. Mac August 8, 2014 at 8:22 am - Reply

    Genius Mark – works perfectly! Thanks very much for your help, I really appreciate it…

Leave A Comment