
- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
03-31-2024 02:19 AM - edited 04-22-2024 07:13 PM
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"
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.
- 2,254 Views

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Article is great! Thanks
It's my thinking if there is a way if we could eliminate the custom field creation by using a system property for enablesync (true/false) and use in BR's therefore removing code depedency?
Additionally, I think the table names can also be saved in as JSON property & be used to refer in the BR

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Thank you. I tried with the system property first, but it doesn't gave me good result as the complete sync is dependent on record to record. That is true that system property would be a good option for JSON. But I am also thinking to create a separate table for it like the Asset-CI synchronization so that people don't need to change within the code, rather deal with that table.
Thanks,
Narsing
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hey thanks for this @Narsing . I am a bit curious about step 3 though. It looks like you're just doing an extra GlideRecord lookup for the current record that the BR already has context of. Couldn't the BR in step 3 literally just be:
current.setWorkflow(false);
current.setValue('u_skip_sync', false);

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi @Nick Simonelli - Its an "after" BR and without using the current.update(), it won't update. But just to make sure it's not doing any recursive call, used the gliderecord here.
Thanks,
Narsing
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Ahh I did not pay enough attention @Narsing1 , thanks for pointing that out!
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
This is a great write-up and should be how requests function out of the box. My fulfillers no longer need to jump between tasks and the ritm.
Works perfectly, with the only change for my field labels.
Thank you
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Thanks for sharing Narsing
This is a great and works fine, but i have an issue with the labels changing according to the users selected language.
Does anyone have a solution to how the script include can be modified to handle "localized labels" ?
Best regards
Martin

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi,
Thank you. Can you try with System Localization -> Messages and create entries for all the languages.
Then use "gs.getMessage("<name>"). Here instead of Work notes/Additional comments, use gs.getMessage
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];
}
Thanks,
Narsing