Activities / Journal Entries Synchronization/Bi-Directional update between Parent & Child Tables
I have gone through many articles & community queries on how to copy the activities/journal entries between parent & child tables [Bi-Directional]. Either it can be RITM & SCTASK (Or) CHANGE REQUEST & CHANGE TASK (Or) PROBLEM & PROBLEM TASK etc.,
But I didn't find any comprehensive solution. So thought of coming up with a permanent solution for it. The approach that I followed in order to complete this is from the OOTB Asset-CI Synchronization. Here you go.
Step#1
Create a field called "Skip Sync" under "Task" table as below.
Step#2
Create a Script Include called "activitySyncUtils" and copy the below code. Make sure it is accessible from all applications.
var activitySyncUtils = Class.create();
activitySyncUtils.prototype = {
initialize: function(crec, prec) {
this.current = crec;
this.previous = prec;
this.parentChilds = [{
"parent": "sc_req_item",
"child": "sc_task",
"enablesync": true,
"syncdirection": "childparent",
"encodedQuery": {
"parent": "request_item=" + this.current.getUniqueValue(),
"child": "sys_id=" + this.current.request_item.toString()
},
"updatetype": "comments_and_worknotes"
}, {
"parent": "change_request",
"child": "change_task",
"enablesync": true,
"syncdirection": "both",
"encodedQuery": {
"parent": "change_request=" + this.current.getUniqueValue(),
"child": "sys_id=" + this.current.change_request.toString()
},
"updatetype": "worknotes"
}, {
"parent": "problem",
"child": "problem_task",
"enablesync": true,
"syncdirection": "parentchild",
"encodedQuery": {
"parent": "problem_id=" + this.current.getUniqueValue(),
"child": ""
},
"updatetype": "comments"
}];
this.updateWithComments = "";
if (this.current.work_notes.changes()) {
this.updateWithComments = this.current.work_notes.getJournalEntry(1).toString().split("(Work notes)")[1];
} else {
this.updateWithComments = this.current.comments.getJournalEntry(1).toString().split("(Additional comments)")[1];
}
this.updateWithComments = this.updateWithComments.replaceAll("\n", "");
},
process: function() {
var canSync = false;
var parentTable = "";
var childTable = "";
var encQuery = "";
var syncDirection = "";
var updateType = "";
for (var p = 0; p < this.parentChilds.length; p++) {
if (this.parentChilds[p].parent == this.current.sys_class_name.toString() || this.parentChilds[p].child == this.current.sys_class_name.toString()) {
parentTable = this.parentChilds[p].parent;
childTable = this.parentChilds[p].child;
canSync = this.parentChilds[p].enablesync;
if (this.current.sys_class_name == parentTable) {
encQuery = this.parentChilds[p].encodedQuery.parent;
} else {
encQuery = this.parentChilds[p].encodedQuery.child;
}
syncDirection = this.parentChilds[p].syncdirection;
updateType = this.parentChilds[p].updatetype;
break;
}
}
var isUpdateTypeCanUpdate = false;
if (this.current.work_notes.changes() && updateType == "worknotes" || this.current.work_notes.changes() && updateType == "comments_and_worknotes") {
isUpdateTypeCanUpdate = true;
} else if (this.current.comments.changes() && updateType == "comments" || this.current.comments.changes() && updateType == "comments_and_worknotes") {
isUpdateTypeCanUpdate = true;
}
if (isUpdateTypeCanUpdate) {
if (JSUtil.notNil(parentTable) && JSUtil.notNil(childTable)) {
if (canSync && JSUtil.notNil(encQuery)) {
if (syncDirection == "both") {
this.syncBiDirectional(childTable, parentTable, encQuery);
} else if (syncDirection == "parentchild" && this.current.sys_class_name == parentTable) {
this.syncParentToChild(childTable, encQuery);
} else if (syncDirection == "childparent" && this.current.sys_class_name == childTable) {
this.syncChildToParent(parentTable, encQuery);
}
}
}
}
},
syncChildToParent: function(parentTable, encQuery) {
try {
var pRecord = new GlideRecord(parentTable);
pRecord.addEncodedQuery(encQuery);
pRecord.query();
if (pRecord.next()) {
pRecord.u_skip_sync = true;
if (this.current.work_notes.changes()) {
pRecord.work_notes = "Update from " + this.current.number.toString() + " - " + this.updateWithComments;
} else {
pRecord.comments = "Update from " + this.current.number.toString() + " - " + this.updateWithComments;
}
pRecord.update();
}
} catch (e) {
}
},
syncParentToChild: function(childTable, encQuery) {
try {
var cRecord = new GlideRecord(childTable);
cRecord.addEncodedQuery(encQuery);
cRecord.query();
while (cRecord.next()) {
cRecord.u_skip_sync = true;
if (this.current.work_notes.changes()) {
cRecord.work_notes = "Update from " + this.current.number.toString() + " - " + this.updateWithComments;
} else {
cRecord.comments = "Update from " + this.current.number.toString() + " - " + this.updateWithComments;
}
cRecord.update();
}
} catch (e) {
}
},
syncBiDirectional: function(childTable, parentTable, encQuery) {
try {
if (this.current.sys_class_name == childTable && !this.current.u_skip_sync) {
var pRecord = new GlideRecord(parentTable);
pRecord.addEncodedQuery(encQuery);
pRecord.query();
if (pRecord.next()) {
pRecord.u_skip_sync = true;
if (this.current.work_notes.changes()) {
pRecord.work_notes = "Update from " + this.current.number.toString() + " - " + this.updateWithComments;
} else {
pRecord.comments = "Update from " + this.current.number.toString() + " - " + this.updateWithComments;
}
pRecord.update();
}
} else if (this.current.sys_class_name == parentTable && !this.current.u_skip_sync) {
var cRecord = new GlideRecord(childTable);
cRecord.addEncodedQuery(encQuery);
cRecord.query();
while (cRecord.next()) {
cRecord.u_skip_sync = true;
if (this.current.work_notes.changes()) {
cRecord.work_notes = "Update from " + this.current.number.toString() + " - " + this.updateWithComments;
} else {
cRecord.comments = "Update from " + this.current.number.toString() + " - " + this.updateWithComments;
}
cRecord.update();
}
}
} catch (e) {
}
},
type: 'activitySyncUtils'
};
Features of the above Class
- Properties of the JSON
- enablesync - Whether to enable the journal entry updates on a specific parent & child. Available values are true/false.
- syncdirection - Whether to update only from parent to child (or) only from child to parent (or) both. Available values are "parentchild"/"childparent"/"both"
- updatetype - Whether to update only comments (or) only work notes (or) both comments & work notes. Available values are "comments"/"worknotes"/"comments_and_worknotes"
- enablesync - Whether to enable the journal entry updates on a specific parent & child. Available values are true/false.
Step#3
Create a Business Rule like this
Name : Synchronize Activities - After
Table: task
when: after
Copy the below Script to "Advanced"
(function executeRule(current, previous /*null when async*/ ) {
// Add your code here
var task = new GlideRecord(current.sys_class_name);
task.addQuery("sys_id", current.getUniqueValue());
task.query();
if (task.next()) {
task.setWorkflow(false);
task.u_skip_sync = false;
task.update();
}
})(current, previous);
Step#4
Create another Business Rule like this
Name: Synchronize Activities - Before
Table: task
when: before
Copy the below Script to "Advanced"
(function executeRule(current, previous /*null when async*/ ) {
// Add your code here
new activitySyncUtils(current, previous).process();
})(current, previous);
That's all, you are done and now the bi-directional journal entry update will start updating the comments/work notes between parent & child tables. As I mentioned earlier, this is enabled on
- RITM <==> SCTASK
- CHANGE REQUEST <==> CHANGE TASK
- PROBLEM <==> PROBLEM TASK
Sample updates
Change Request & Change Task
YOU CAN DOWNLOAD THE UPDATE SET FROM HERE
FAQ:
1. Can I disable the bi-directional update after committing this update set?
Yes, you can disable completely / partially as well.
- Go to Script include -activitySyncUtils
- Update the JSON Payload element called "enablesync" to "false" wherever is needed
2. How can I add other Parent & Child Tables apart from change/request item/problem to enable the bi-directional updates?
You can add those tables as long as it extends to the Task table via the JSON from the Business rule.
3. I am not using the OOTB Labels (Additional comments/Work notes) for Activities, but these are also journal entries. How can I handle this kind of scenario?
From the "activitySyncUtils" Script include, you can modify the script logic within the "initialize()" like this. But before doing this, make sure how you are getting the journal entry when you use getJournalEntry(1) so that you can split that accordingly.
var updateWithComments = "";
if (current.work_notes.changes()) {
updateWithComments = current.work_notes.getJournalEntry(1).toString().split("(<your activity label>)")[1];
} else {
updateWithComments = current.comments.getJournalEntry(1).toString().split("(<your activity label>)")[1];
}
updateWithComments = updateWithComments.replaceAll("\n", "");
Feel free to provide your feedback /queries here and Like/Share if you think its useful.
https://www.servicenow.com/community/itsm-articles/activities-journal-entries-synchronization-bi-directional-update/ta-p/2882636