Copying Attachments Between Related Records - ben-vargas/servicenow-wiki GitHub Wiki

When a user first attaches files to a Call record, it’s often desirable to have these attachments readily available on the related Incident record after the call is transferred. Out-of-the-box options, like using GlideSysAttachment.copy(), copy all attachments every time it’s run, potentially causing duplication. Instead, a more targeted and robust solution ensures attachments are copied once at the time they are added, providing a seamless experience for both users and support staff.


The Use Case

  1. Starting Point: Users attach documents (e.g., screenshots, logs, PDFs) to a Call record (new_call table).
  2. Transition: When the call record is transferred to an Incident (incident table), its attachments should also appear in the Incident without manual intervention.
  3. Avoiding Duplication: By copying attachments only when they are added, we prevent the repeated copying of the same attachments on subsequent operations.

Why Not Just Use GlideSysAttachment.copy()?

  • Duplication Issue: GlideSysAttachment.copy() copies all attachments from a record each time it’s run. If invoked multiple times, it copies all attachments again, causing duplicates to pile up on the destination record.
  • Targeted Approach: Our solution focuses on new attachments only, ensuring each newly added attachment is copied exactly once when the call is already transferred.

The Proposed Solution

We’ll implement a Business Rule on sys_attachment that triggers after an attachment is inserted. This rule:

  • Checks if the attachment’s record is a Call that has been transferred.
  • If yes, it creates a corresponding attachment on the related Incident.
  • Copies over the attachment’s document data (i.e., the chunks of the file stored in sys_attachment_doc).

Key Points:

  • Runs after insertion: Ensures the attachment is fully saved before being copied.
  • Includes robust error handling and logging for easier troubleshooting.
  • Structured for clarity and maintainability.

Step-by-Step Implementation

1. Create the Business Rule

  1. Navigate to System Definition > Business Rules.

  2. Click New and configure:

    • Name: Copy Attachment to Incident (or a descriptive name)
    • Table: sys_attachment
    • When: After
    • Insert: Checked
    • Advanced: Checked
  3. Add the following script in the Script field:

    (function() {
        // Ensure this only runs on insert actions
        if (action.name != 'sysverb_insert') {
            return;
        }
    
        var callSysId = current.table_sys_id;
        var attachmentSysId = current.sys_id;
    
        try {
            // Retrieve the Call record associated with this attachment
            var call = new GlideRecord('new_call');
            if (!call.get(callSysId)) {
                gs.error('Attachment Copy Error: No Call record found for sys_id=' + callSysId +
                         ', attachment sys_id=' + attachmentSysId);
                return;
            }
    
            // If the call isn't transferred, no action required
            if (JSUtil.nil(call.transferred_to)) {
                return;
            }
    
            var incidentSysId = call.transferred_to;
    
            // Create a new attachment record on the related incident
            var newAttachment = new GlideRecord('sys_attachment');
            newAttachment.initialize();
            newAttachment.file_name = current.file_name;
            newAttachment.content_type = current.content_type;
            newAttachment.compressed = current.compressed;
            newAttachment.table_name = 'incident';
            newAttachment.size_bytes = current.size_bytes;
            newAttachment.size_compressed = current.size_compressed;
            newAttachment.table_sys_id = incidentSysId;
            var newAttachmentSysId = newAttachment.insert();
    
            if (!newAttachmentSysId) {
                gs.error("Attachment Copy Error: Could not create new attachment on incident. " +
                         "Original attachment sys_id=" + attachmentSysId + ", incident sys_id=" + incidentSysId);
                return;
            }
    
            // Copy the attachment's data chunks
            var attachmentDoc = new GlideRecord('sys_attachment_doc');
            attachmentDoc.addQuery('sys_attachment', attachmentSysId);
            attachmentDoc.query();
    
            while (attachmentDoc.next()) {
                var newAttachmentDoc = new GlideRecord('sys_attachment_doc');
                newAttachmentDoc.initialize();
                newAttachmentDoc.sys_attachment = newAttachmentSysId;
                newAttachmentDoc.position = attachmentDoc.position;
                newAttachmentDoc.length = attachmentDoc.length;
                newAttachmentDoc.data = attachmentDoc.data;
                var newAttachmentDocSysId = newAttachmentDoc.insert();
    
                if (!newAttachmentDocSysId) {
                    gs.error("Attachment Copy Error: Could not copy attachment doc from sys_id=" +
                             attachmentSysId + " to " + newAttachmentSysId);
                }
            }
        } catch (ex) {
            gs.error("Attachment Copy Exception: An error occurred while copying attachment sys_id=" +
                     attachmentSysId + ". Error: " + ex);
        }
    })();
    

2. Validate and Test

  • Development Testing:
    Add a test attachment to a Call that has been transferred to an Incident. Verify that a corresponding attachment is created on the Incident and that the file can be opened successfully.
  • Check Logs:
    Review the system logs (System Logs > All) for any gs.error() messages. Address issues if any appear.

3. Deployment Considerations

  • Error Handling:
    The script uses gs.error() to log errors. Check these logs regularly or use a reporting mechanism for better visibility.
  • Performance:
    For large attachments or high volumes, consider asynchronous processing (e.g., using GlideRecord.queue()) if performance becomes a concern.
  • Security & Permissions:
    Ensure that the system user running the business rule has necessary permissions to read the call and incident records and create attachments on the incident.

4. Future Enhancements

  • Conditional Logic:
    Expand conditions to handle other source/target tables or different field names.
  • Skip Already Copied Attachments:
    If needed, implement logic to mark attachments as copied or skip if already copied.
  • Notification or Logging Improvements:
    Consider sending an email or logging to a separate table if attachments fail to copy.

Conclusion

This approach provides a clean, reliable way to ensure that newly added attachments to a Call record seamlessly appear on the related Incident record. By focusing on copying only the newly inserted attachment and its associated documents, we avoid duplication and ensure a user-friendly experience, while error handling and logging facilitate troubleshooting and ongoing maintenance.