Use PDIs? Take our 5-minute survey to help shape the PDI roadmap.

Auto-reminder and auto-close after 10 business days for pending approvals and On Hold tickets

Deepika Gangra1
Tera Expert

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:

  1. For records in:
    • Pending Approval
    • On Hold
  2. 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–Thursday
    if (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 day
        pastDate.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 approvals
            var 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 RITM
            gr_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 Request
            var grReq = new GlideRecord('sc_request');
            grReq.addQuery('sys_id', gr_SRonhold.request);
            grReq.query();

            while (grReq.next()) {
                grReq.state = '4';
                grReq.update();
            }

            // Update Catalog Tasks
            var 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)");

    }
6 REPLIES 6

AlpUtkuM
Mega Sage

You could basically use flow designer to deliver this requirement.

 

Please refer to the following video : https://www.youtube.com/watch?v=nYCr1BcUhDI&list=PLzDI891zP4LVUuWhpEFFmNqfMl4KYdjnP&index=5

In this latest tutorial, we dive deep into ServiceNow Flow Designer's powerful 'Do the Following Until' flow logic! 🌊 In this video, we explore the incredible capabilities of this feature, demonstrating how it can revolutionize your workflow automation. The 'Do the Following Until' logic in Flow...

Tanushree Maiti
Tera Patron

Hi @Deepika Gangra1 

 

Run a Daily scheduled job with following script:

 

Sample script:

 

var scheduleSysId = 'YOUR_SCHEDULE_SYS_ID_HERE';

var dc = new DurationCalculator();

dc.setSchedule(scheduleSysId);

var gr = new GlideRecordSecure('task');

gr.addEncodedQuery('sys_class_name=incident^ORsys_class_name=sc_req_item^stateIN-5,3'); // -5 = Pending Approval, 3 = On Hold (Adjust values to match your instance)

gr.query();

while (gr.next()) {

    var createdOn = gr.sys_created_on;

    var gdtCreated = new GlideDateTime(createdOn);

    var gdtNow = new GlideDateTime();   

    dc.calcDuration(gdtCreated, gdtNow);

    var elapsedSeconds = dc.getSeconds();

    var elapsedBusinessDays = elapsedSeconds / 86400; // 24 hours converted to seconds

    if (elapsedBusinessDays >= 1 && elapsedBusinessDays < 10) {

       if (elapsedBusinessDays == 3 || elapsedBusinessDays == 5 || elapsedBusinessDays == 8 ) //e.g remind on days 3, 5, and 8

            {

            gs.eventQueue('ticket.reminder', gr, gr.sys_id, '');  //Replace your actual notification name

        }

    }

       else if (elapsedBusinessDays >= 10) {

        gr.state = 7; // State value for 'Closed'

        gr.comments = 'Ticket automatically closed due to inactivity after 10 business days.';

        gr.update();

    }

}

 

Please Accept the solution if it assisted you with your question & Mark this response as Helpful.
Regards
Tanushree Maiti
ServiceNow Technical Architect
LinkedIn: https://www.linkedin.com/in/tanushreemaiti