← Script library

Fix Scripts

Batch Reopen Stale Resolved Incidents

Reopen incidents that have been resolved but not closed within a configurable number of days, with notification to assignees and optional audit logging.

JavaScript
(function() {

  // Configuration
  var config = {
    resolvedState: '6',              // Resolved state value
    reopenState: '2',                // State to reopen to (In Progress)
    staleDays: 14,                   // Days after resolution before reopening
    runAsSystem: true,              // Execute as system user
    notifyAssignees: true,           // Send notification to assigned users
    logToAudit: true,               // Create audit records for reopened incidents
    limit: 1000                     // Safety limit
  };

  // Optional: Exclude certain assignment groups from reopening
  var excludedGroups = [
    'System Administration',
    'Retired Records'
  ];

  // Calculate the cutoff date
  var cutoffDate = new GlideDateTime();
  cutoffDate.addDaysLocalTime(-config.staleDays);

  // Build the query
  var gr = new GlideRecord('incident');
  gr.addQuery('state', config.resolvedState);
  gr.addQuery('sys_updated_on', '<', cutoffDate);
  gr.addQuery('active', 'true');

  // Exclude specific groups if configured
  if (excludedGroups.length > 0) {
    var excludedGroupIds = [];
    var grGroup = new GlideRecord('sys_user_group');
    grGroup.addQuery('name', 'IN', excludedGroups.join(','));
    grGroup.query();
    while (grGroup.next()) {
      excludedGroupIds.push(grGroup.sys_id.toString());
    }
    if (excludedGroupIds.length > 0) {
      gr.addQuery('assignment_group', 'NOT IN', excludedGroupIds.join(','));
    }
  }

  gr.setLimit(config.limit);
  gr.query();

  var count = 0;
  var errors = 0;

  while (gr.next()) {
    try {
      // Log current state before change
      var oldState = gr.state.getDisplayValue();
      var oldActive = gr.active.toString();

      // Reopen the incident
      gr.state = config.reopenState;
      gr.active = true;
      gr.work_notes = 'Automatically reopened by fix script: resolved ' +
                     config.staleDays + ' days ago without being closed. ' +
                     'Previous state: ' + oldState;

      gr.update();
      count++;

      // Optional notification
      if (config.notifyAssignees && gr.assigned_to.nil() === false) {
        var notify = new global.utils.NotificationHelper();
        if (notify && notify.notifyUser) {
          notify.notifyUser(gr.assigned_to, 'incident_reopened', gr);
        }
      }

      // Optional audit log
      if (config.logToAudit) {
        var audit = new GlideRecord('sys_audit');
        audit.initialize();
        audit.documentkey = gr.sys_id;
        audit.table_name = 'incident';
        audit.field_label = 'State';  // Approximate
        audit.setValue('new_value', config.reopenState);
        audit.setValue('old_value', config.resolvedState);
        audit.setValue('sys_created_by', 'system');
        audit.insert();
      }

      gs.info('Reopened stale incident: ' + gr.number);

    } catch (e) {
      errors++;
      gs.error('Failed to reopen incident ' + gr.number + ': ' + e.message);
    }
  }

  gs.info('Fix Script Complete: Reopened ' + count + ' stale incidents. Errors: ' + errors);
  gs.info('Run date: ' + new GlideDateTime().getDisplayValue() +
           ', Stale threshold: ' + config.staleDays + ' days');

})();

How to use it

1. Back up the incident table before running in production 2. Update config object with your organization's state values and thresholds 3. Test in dev environment first with a low limit (e.g., 10) 4. Run from System Definition > Fix Scripts 5. Monitor output for count and errors 6. Verify reopened incidents in the UI after running 7. Consider scheduling this as a scheduled job for ongoing maintenance 8. Adjust excludedGroups array to protect sensitive records

Adapt the table names, fields, and conditions to your instance. Test the behavior in a development environment before using it in production.