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

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