Auto-reminder and auto-close after 10 business days for pending approvals and On Hold tickets
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
56m ago
Hi All,
I have a requirement to implement a scheduled job in ServiceNow for both Incidents and Request Items (RITMs) with the following logic:
Requirement:
- For records in:
- Pending Approval
- On Hold
- The system should:
- Send reminders during the 10 business days
- If no action is taken within 10 business days, then:
- Auto-close the ticket
Business Rules:
- Business days = Sunday to Thursday
- Exclude weekends (Friday & Saturday)
- Exclude holidays (defined in schedule)
- Applies to:
- Incident
- sc_req_item (RITM)
Current Approach:
- I initially implemented a loop-based logic to calculate business days by excluding weekends
- However, this does not handle holidays
- I am now considering using GlideSchedule to:
- Calculate business duration
- Handle holidays properly
Challenges:
- How to accurately calculate 10 business days including holidays
- How to design reminder logic within those 10 days
- Ensuring performance efficiency in a scheduled job (avoiding heavy loops per record)
- Applying the same logic consistently across Incident and RITM
Scheduled job:// ================= DAY CHECK =================var gdtToday = new GlideDateTime();gdtToday.setStartOfDay();var day = parseInt(gdtToday.getDayOfWeekLocalTime(), 10);// Run only Sunday–Thursdayif (day != 5 && day != 6) {// ================= CALCULATE 10 BUSINESS DAYS =================var count = 0;var pastDate = new GlideDateTime(gdtToday);while (count < 10) {pastDate.addDaysLocalTime(-1);var d = parseInt(pastDate.getDayOfWeekLocalTime(), 10);// Skip Friday (5) and Saturday (6)if (d != 5 && d != 6) {count++;}}// VERY IMPORTANT → include full daypastDate.setDisplayValue(pastDate.getLocalDate() + " 23:59:59");gs.info("Auto-close running. Cutoff date: " + pastDate.getDisplayValue());// ================= INCIDENT =================var appr = new GlideRecord('incident');appr.addQuery('active', true);appr.addQuery('state', 3);appr.addQuery('sys_updated_on', '<=', pastDate);appr.addQuery('hold_reason', '6');appr.query();var incidentCount = 0;while (appr.next()) {gs.eventQueue('autocancel.incident', appr, "", "");appr.state = '8';appr.sys_updated_by = 'system';appr.comments = "This ticket has been automatically closed due to no activity over the past 10 business days (Sun–Thu). On Hold Reason: " + appr.getDisplayValue('hold_reason');appr.update();incidentCount++;}// ================= RITM =================var gr_SRonhold = new GlideRecord('sc_req_item');gr_SRonhold.addQuery('active', true);gr_SRonhold.addQuery('state', 17);gr_SRonhold.addQuery('sys_updated_on', '<=', pastDate);gr_SRonhold.addQuery('u_on_hold_reason', '2');gr_SRonhold.query();var ritmCount = 0;while (gr_SRonhold.next()) {var ritmSysId = gr_SRonhold.sys_id;// Reject approvalsvar notapproved = new GlideRecord('sysapproval_approver');notapproved.addQuery('source_table', 'sc_req_item');notapproved.addQuery('sysapproval', ritmSysId);notapproved.addQuery('state', 'requested');notapproved.query();while (notapproved.next()) {notapproved.state = 'rejected';notapproved.comments = "Auto-closed after 10 business days. On Hold Reason: " + gr_SRonhold.getDisplayValue('u_on_hold_reason');notapproved.update();}// Close RITMgr_SRonhold.state = '4';gr_SRonhold.comments = "This ticket has been automatically closed due to no activity over the past 10 business days. On Hold Reason: " + gr_SRonhold.getDisplayValue('u_on_hold_reason');gr_SRonhold.update();// Update Requestvar grReq = new GlideRecord('sc_request');grReq.addQuery('sys_id', gr_SRonhold.request);grReq.query();while (grReq.next()) {grReq.state = '4';grReq.update();}// Update Catalog Tasksvar grtask = new GlideRecord('sc_task');grtask.addQuery('request_item', ritmSysId);grtask.query();while (grtask.next()) {grtask.state = '4';grtask.update();}gs.eventQueue('autocancel.requestItem', gr_SRonhold, "", "");ritmCount++;}// ================= LOG =================gs.info("Auto-close completed. Incidents: " + incidentCount + ", RITMs: " + ritmCount);} else {gs.info("Auto-close job skipped (Weekend)");}
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
35m ago
Hi there @Deepika Gangra1
Right now your logic only excludes Fri/Sat, so holidays will still break the calculation.
I’d recommend:
- Create a business schedule (Sun–Thu + holidays)
- Use
DurationCalculatorto check if 10 business days elapsed - Run reminders separately (ex: day 3, 7, 9)
- Batch process records to avoid huge loops in scheduled jobs
Also, small optimization:
Instead of querying Requests and Tasks inside every RITM loop, consider using bulk updates or encoded queries to reduce DB hits.
Kind Regards,
Azar
Serivenow Rising Star ⭐
Developer @ KPMG.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
18m ago
Hi @Its_Azar ,
Thanks for your suggestion, it’s really helpful.
In my case, the requirement is slightly different — I need to send reminders continuously for all 10 business days (Sun–Thu, excluding weekends and holidays) for records in Pending Approval and On Hold state.
If there is still no action after the 10th business day, the ticket should be auto-closed.
So instead of sending reminders only on specific days (like Day 3, 7, 9), the expectation is to trigger reminders daily during the 10 business day window.
Could you please help with a sample script or best approach using DurationCalculator or GlideSchedule to handle this efficiently (especially avoiding loops and handling holidays correctly)?
Also, any recommendations on avoiding duplicate reminders (since this will run as a scheduled job) would be really helpful.
Thanks in advance!