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
- Starting Point: Users attach documents (e.g., screenshots, logs, PDFs) to a Call record (
new_call
table). - Transition: When the call record is transferred to an Incident (
incident
table), its attachments should also appear in the Incident without manual intervention. - Avoiding Duplication: By copying attachments only when they are added, we prevent the repeated copying of the same attachments on subsequent operations.
GlideSysAttachment.copy()
?
Why Not Just Use - 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
-
Navigate to System Definition > Business Rules.
-
Click New and configure:
- Name: Copy Attachment to Incident (or a descriptive name)
- Table:
sys_attachment
- When: After
- Insert: Checked
- Advanced: Checked
-
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 anygs.error()
messages. Address issues if any appear.
3. Deployment Considerations
- Error Handling:
The script usesgs.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., usingGlideRecord.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.