- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
4 hours ago
As part of this Enhancement, we will do the development and deployment of an automation in that identifies and closes IT requests that have remained in an “Awaiting Approval” state for more than 45 days without any activity.
Automation will be built as mentioned below.
-Detect IT requests that have had no updates from the requestor within the 45‑day period.
-Detect requests where the assigned approver has taken no approval action (approve or reject) during that timeframe.
-Automatically transition qualifying requests to a Closed – Inactive status.
-Add a standardized system‑generated comment at closure to inform users of the reason for the auto‑closure and the appropriate next steps.
Auto‑Closure Comment:
“This request is proposed to be automatically closed due to inactivity while awaiting approval. No updates or approval actions were recorded during this period. If access or support is still required, the requestor may submit a new request.”
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 hours ago
Recommended: Scheduled Script Execution (runs daily, off-peak hours) targeting sc_req_item records in the "Awaiting Approval" state. A Flow Designer Scheduled Flow works too, but for batch operations like this, a scripted approach gives you tighter control over the inactivity detection logic across both journal entries and approval records.
Core logic breakdown:
1. Identify qualifying records — Query RITMs where the approval state is "Requested" and sys_updated_on is older than 45 days. But sys_updated_on alone isn't sufficient since system-level updates (like SLA breaches or workflow context updates) can bump that timestamp without any real human activity. So you'd cross-check against two things:
- Requestor activity: Query
sys_journal_fieldfor the RITM'ssys_id— filter onelement IN (comments, work_notes)and check if the most recent entry from the requestor (sys_created_by= caller) is older than 45 days. - Approver activity: Query
sysapproval_approverwheresysapproval = RITM sys_idandstate = requested. If all linked approval records have had no state change (no approve/reject) andsys_updated_onon the approver record is also beyond 45 days, the record qualifies.
2. Close qualifying records — Set the RITM state to "Closed Incomplete" (or a custom "Closed – Inactive" state if you've configured one), populate the close notes, and insert the standardized work note/comment.
3. Cancel pending approvals — Don't forget to also set the related sysapproval_approver records to "cancelled", otherwise you'll have orphaned approval records that could cause confusion in reports and approval dashboards.
4. Notification — Optionally trigger notifications to the requestor and the approver informing them of the auto-closure, beyond just the journal comment.
Here's the skeleton script for the Scheduled Script Execution:
(function autoCloseInactiveApprovals() {
var INACTIVITY_DAYS = 45;
var cutoffDate = new GlideDateTime();
cutoffDate.addDaysLocalTime(-INACTIVITY_DAYS);
var cutoffStr = cutoffDate.getValue();
var closureComment = 'This request is proposed to be automatically closed due to inactivity ' +
'while awaiting approval. No updates or approval actions were recorded during this period. ' +
'If access or support is still required, the requestor may submit a new request.';
// Find RITMs in Awaiting Approval with no system update in 45 days
var ritm = new GlideRecord('sc_req_item');
ritm.addQuery('approval', 'requested'); // Awaiting Approval
ritm.addQuery('sys_updated_on', '<', cutoffStr);
ritm.query();
while (ritm.next()) {
// Verify no recent journal activity from requestor
if (_hasRecentActivity(ritm.getUniqueValue(), cutoffStr)) {
continue;
}
// Verify no recent approval action
if (_hasRecentApprovalAction(ritm.getUniqueValue(), cutoffStr)) {
continue;
}
// Close the RITM
ritm.state = 4; // Closed Incomplete (adjust per your state model)
ritm.approval = 'cancelled';
ritm.close_notes = closureComment;
ritm.work_notes = closureComment;
ritm.update();
// Cancel orphaned approval records
_cancelApprovals(ritm.getUniqueValue());
}
function _hasRecentActivity(ritmSysId, cutoff) {
var journal = new GlideRecord('sys_journal_field');
journal.addQuery('name', 'sc_req_item');
journal.addQuery('element', 'IN', 'comments,work_notes');
journal.addQuery('sys_id', ritmSysId); // document ID
journal.addQuery('sys_created_on', '>=', cutoff);
journal.setLimit(1);
journal.query();
return journal.hasNext();
}
function _hasRecentApprovalAction(ritmSysId, cutoff) {
var appr = new GlideRecord('sysapproval_approver');
appr.addQuery('sysapproval', ritmSysId);
appr.addQuery('state', '!=', 'requested'); // Someone took action
appr.addQuery('sys_updated_on', '>=', cutoff);
appr.setLimit(1);
appr.query();
return appr.hasNext();
}
function _cancelApprovals(ritmSysId) {
var appr = new GlideRecord('sysapproval_approver');
appr.addQuery('sysapproval', ritmSysId);
appr.addQuery('state', 'requested');
appr.query();
while (appr.next()) {
appr.state = 'cancelled';
appr.update();
}
}
})();
A few things to watch out for:
The sys_journal_field query for matching document ID — the field is actually element_id (not sys_id) that holds the parent record's sys_id. Adjust that query to journal.addQuery('element_id', ritmSysId) in your instance. I kept it readable above but that's a common gotcha.
Also consider whether you want this to target sc_req_item only or also sc_request (parent request). Typically the approval hangs on the RITM, but if your flows attach approvals at the request level, adjust accordingly.
For the state model — "Closed – Inactive" isn't OOTB. If you want a distinct close reason visible in reporting, you could either add a custom state or use "Closed Incomplete" with a close code/category that distinguishes auto-closures from manual ones. That makes it easy to build a PA widget showing auto-closure volume over time.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 hours ago
Recommended: Scheduled Script Execution (runs daily, off-peak hours) targeting sc_req_item records in the "Awaiting Approval" state. A Flow Designer Scheduled Flow works too, but for batch operations like this, a scripted approach gives you tighter control over the inactivity detection logic across both journal entries and approval records.
Core logic breakdown:
1. Identify qualifying records — Query RITMs where the approval state is "Requested" and sys_updated_on is older than 45 days. But sys_updated_on alone isn't sufficient since system-level updates (like SLA breaches or workflow context updates) can bump that timestamp without any real human activity. So you'd cross-check against two things:
- Requestor activity: Query
sys_journal_fieldfor the RITM'ssys_id— filter onelement IN (comments, work_notes)and check if the most recent entry from the requestor (sys_created_by= caller) is older than 45 days. - Approver activity: Query
sysapproval_approverwheresysapproval = RITM sys_idandstate = requested. If all linked approval records have had no state change (no approve/reject) andsys_updated_onon the approver record is also beyond 45 days, the record qualifies.
2. Close qualifying records — Set the RITM state to "Closed Incomplete" (or a custom "Closed – Inactive" state if you've configured one), populate the close notes, and insert the standardized work note/comment.
3. Cancel pending approvals — Don't forget to also set the related sysapproval_approver records to "cancelled", otherwise you'll have orphaned approval records that could cause confusion in reports and approval dashboards.
4. Notification — Optionally trigger notifications to the requestor and the approver informing them of the auto-closure, beyond just the journal comment.
Here's the skeleton script for the Scheduled Script Execution:
(function autoCloseInactiveApprovals() {
var INACTIVITY_DAYS = 45;
var cutoffDate = new GlideDateTime();
cutoffDate.addDaysLocalTime(-INACTIVITY_DAYS);
var cutoffStr = cutoffDate.getValue();
var closureComment = 'This request is proposed to be automatically closed due to inactivity ' +
'while awaiting approval. No updates or approval actions were recorded during this period. ' +
'If access or support is still required, the requestor may submit a new request.';
// Find RITMs in Awaiting Approval with no system update in 45 days
var ritm = new GlideRecord('sc_req_item');
ritm.addQuery('approval', 'requested'); // Awaiting Approval
ritm.addQuery('sys_updated_on', '<', cutoffStr);
ritm.query();
while (ritm.next()) {
// Verify no recent journal activity from requestor
if (_hasRecentActivity(ritm.getUniqueValue(), cutoffStr)) {
continue;
}
// Verify no recent approval action
if (_hasRecentApprovalAction(ritm.getUniqueValue(), cutoffStr)) {
continue;
}
// Close the RITM
ritm.state = 4; // Closed Incomplete (adjust per your state model)
ritm.approval = 'cancelled';
ritm.close_notes = closureComment;
ritm.work_notes = closureComment;
ritm.update();
// Cancel orphaned approval records
_cancelApprovals(ritm.getUniqueValue());
}
function _hasRecentActivity(ritmSysId, cutoff) {
var journal = new GlideRecord('sys_journal_field');
journal.addQuery('name', 'sc_req_item');
journal.addQuery('element', 'IN', 'comments,work_notes');
journal.addQuery('sys_id', ritmSysId); // document ID
journal.addQuery('sys_created_on', '>=', cutoff);
journal.setLimit(1);
journal.query();
return journal.hasNext();
}
function _hasRecentApprovalAction(ritmSysId, cutoff) {
var appr = new GlideRecord('sysapproval_approver');
appr.addQuery('sysapproval', ritmSysId);
appr.addQuery('state', '!=', 'requested'); // Someone took action
appr.addQuery('sys_updated_on', '>=', cutoff);
appr.setLimit(1);
appr.query();
return appr.hasNext();
}
function _cancelApprovals(ritmSysId) {
var appr = new GlideRecord('sysapproval_approver');
appr.addQuery('sysapproval', ritmSysId);
appr.addQuery('state', 'requested');
appr.query();
while (appr.next()) {
appr.state = 'cancelled';
appr.update();
}
}
})();
A few things to watch out for:
The sys_journal_field query for matching document ID — the field is actually element_id (not sys_id) that holds the parent record's sys_id. Adjust that query to journal.addQuery('element_id', ritmSysId) in your instance. I kept it readable above but that's a common gotcha.
Also consider whether you want this to target sc_req_item only or also sc_request (parent request). Typically the approval hangs on the RITM, but if your flows attach approvals at the request level, adjust accordingly.
For the state model — "Closed – Inactive" isn't OOTB. If you want a distinct close reason visible in reporting, you could either add a custom state or use "Closed Incomplete" with a close code/category that distinguishes auto-closures from manual ones. That makes it easy to build a PA widget showing auto-closure volume over time.
