Business Rules
Async Notification with Complex Conditions
Send notifications asynchronously based on complex business logic, preventing delays in record save operations.
(function executeRule(current, previous /*null when async*/) {
// Async Business Rules run in background after the transaction completes
// Use for notifications, integrations, and non-critical updates
// Note: 'previous' is null in async business rules
try {
// Configuration
var notificationEnabled = gs.getProperty('custom.notifications.enabled', 'true') === 'true';
if (!notificationEnabled) {
gs.info('Notifications disabled via system property');
return;
}
// Determine notification scenarios
var shouldNotify = false;
var notificationType = '';
var recipients = [];
var additionalInfo = {};
// Scenario 1: High-priority incident created
if (current.isNewRecord() && current.priority.toString() === '1') {
shouldNotify = true;
notificationType = 'high_priority_created';
// Notify assignment group manager
if (current.assignment_group) {
var groupManager = getGroupManager(current.assignment_group.toString());
if (groupManager) {
recipients.push(groupManager);
}
}
// Notify incident manager role
recipients = recipients.concat(getUsersWithRole('incident_manager'));
additionalInfo.reason = 'High-priority incident requires immediate attention';
}
// Scenario 2: Incident reassigned multiple times (potential issue)
if (!current.isNewRecord()) {
var reassignmentCount = getReassignmentCount(current.sys_id.toString());
if (reassignmentCount >= 3) {
shouldNotify = true;
notificationType = 'excessive_reassignments';
// Notify service delivery manager
recipients = recipients.concat(getUsersWithRole('service_delivery_manager'));
additionalInfo.reason = 'Incident reassigned ' + reassignmentCount + ' times';
additionalInfo.reassignmentCount = reassignmentCount;
}
}
// Scenario 3: SLA breach imminent (within 30 minutes)
if (current.sla_due) {
var slaDue = new GlideDateTime(current.sla_due);
var now = new GlideDateTime();
var timeUntilDue = GlideDateTime.subtract(now, slaDue);
var minutesUntilDue = timeUntilDue.getNumericValue() / (1000 * 60);
if (minutesUntilDue > 0 && minutesUntilDue <= 30) {
shouldNotify = true;
notificationType = 'sla_breach_imminent';
// Notify assigned user and their manager
if (current.assigned_to) {
recipients.push(current.assigned_to.toString());
var assigneeManager = current.assigned_to.manager.toString();
if (assigneeManager) {
recipients.push(assigneeManager);
}
}
additionalInfo.reason = 'SLA breach in ' + minutesUntilDue.toFixed(0) + ' minutes';
additionalInfo.minutesUntilDue = minutesUntilDue.toFixed(0);
}
}
// Scenario 4: VIP caller incident
if (current.caller_id && current.caller_id.vip.toString() === 'true') {
shouldNotify = true;
notificationType = 'vip_incident';
// Notify VIP support team
recipients = recipients.concat(getUsersInGroup('VIP Support'));
// Notify caller's account manager if exists
if (current.caller_id.u_account_manager) {
recipients.push(current.caller_id.u_account_manager.toString());
}
additionalInfo.reason = 'VIP caller requires priority attention';
}
// Scenario 5: Incident age threshold exceeded
if (current.opened_at) {
var opened = new GlideDateTime(current.opened_at);
var now = new GlideDateTime();
var age = GlideDateTime.subtract(opened, now);
var ageHours = Math.abs(age.getNumericValue()) / (1000 * 60 * 60);
// Notify if incident open > 48 hours
if (ageHours > 48 && current.state.toString() !== '6') { // Not resolved
shouldNotify = true;
notificationType = 'aging_incident';
// Notify assignment group and manager
if (current.assignment_group) {
recipients = recipients.concat(getGroupMembers(current.assignment_group.toString()));
}
additionalInfo.reason = 'Incident open for ' + ageHours.toFixed(0) + ' hours';
additionalInfo.ageHours = ageHours.toFixed(0);
}
}
// Send notifications if needed
if (shouldNotify && recipients.length > 0) {
// Remove duplicates
recipients = removeDuplicates(recipients);
// Send notification to each recipient
recipients.forEach(function(recipientId) {
sendNotification(recipientId, current, notificationType, additionalInfo);
});
gs.info('Sent ' + notificationType + ' notifications for incident ' +
current.number + ' to ' + recipients.length + ' recipients');
}
} catch (e) {
gs.error('Async notification BR error for incident ' + current.number + ': ' + e.message);
}
// Helper functions
function getGroupManager(groupId) {
var grGroup = new GlideRecord('sys_user_group');
if (grGroup.get(groupId) && grGroup.manager) {
return grGroup.manager.toString();
}
return null;
}
function getUsersWithRole(roleName) {
var users = [];
var grUserRole = new GlideRecord('sys_user_has_role');
grUserRole.addQuery('role.name', roleName);
grUserRole.addQuery('user.active', 'true');
grUserRole.query();
while (grUserRole.next()) {
users.push(grUserRole.user.toString());
}
return users;
}
function getUsersInGroup(groupName) {
var users = [];
var grGroup = new GlideRecord('sys_user_group');
grGroup.addQuery('name', groupName);
grGroup.query();
if (grGroup.next()) {
var grMember = new GlideRecord('sys_user_grmember');
grMember.addQuery('group', grGroup.sys_id);
grMember.query();
while (grMember.next()) {
users.push(grMember.user.toString());
}
}
return users;
}
function getGroupMembers(groupId) {
var users = [];
var grMember = new GlideRecord('sys_user_grmember');
grMember.addQuery('group', groupId);
grMember.query();
while (grMember.next()) {
users.push(grMember.user.toString());
}
return users;
}
function getReassignmentCount(incidentId) {
var ga = new GlideAggregate('sys_audit');
ga.addQuery('tablename', 'incident');
ga.addQuery('documentkey', incidentId);
ga.addQuery('fieldname', 'assignment_group');
ga.addAggregate('COUNT');
ga.query();
if (ga.next()) {
return parseInt(ga.getAggregate('COUNT'));
}
return 0;
}
function removeDuplicates(arr) {
return arr.filter(function(item, index) {
return arr.indexOf(item) === index;
});
}
function sendNotification(userId, incident, type, info) {
// Create notification using gs.eventQueue or direct email
gs.eventQueue('custom.incident.notification', incident, userId, type,
JSON.stringify(info));
// Alternative: Send email directly
/*
var grUser = new GlideRecord('sys_user');
if (grUser.get(userId) && grUser.email) {
var email = new GlideSysEmail();
email.setSubject('Incident Alert: ' + incident.number);
email.setBody('Alert Type: ' + type + '\nReason: ' + info.reason);
email.addAddress(grUser.email.toString());
email.send();
}
*/
}
})(current, previous);How to use it
1. Create an async Business Rule on incident table 2. Check "Insert" and "Update" checkboxes 3. Customize notification scenarios for your requirements 4. Create corresponding notification records or email templates 5. Test with various incident scenarios 6. Monitor system logs for notification delivery 7. Set up system property for enabling/disabling notifications 8. Consider notification throttling to prevent spam
Adapt the table names, fields, and conditions to your instance. Test the behavior in a development environment before using it in production.