Async Notification with Complex Conditions
Send notifications asynchronously based on complex business logic, preventing delays in record save operations.
Table: incident When: async
#async #notification #email #alerting #escalation #insert #update #complex-logic
Script Code
JavaScript
1(function executeRule(current, previous /*null when async*/) {
2
3 // Async Business Rules run in background after the transaction completes
4 // Use for notifications, integrations, and non-critical updates
5 // Note: 'previous' is null in async business rules
6
7 try {
8 // Configuration
9 var notificationEnabled = gs.getProperty('custom.notifications.enabled', 'true') === 'true';
10
11 if (!notificationEnabled) {
12 gs.info('Notifications disabled via system property');
13 return;
14 }
15
16 // Determine notification scenarios
17 var shouldNotify = false;
18 var notificationType = '';
19 var recipients = [];
20 var additionalInfo = {};
21
22 // Scenario 1: High-priority incident created
23 if (current.isNewRecord() && current.priority.toString() === '1') {
24 shouldNotify = true;
25 notificationType = 'high_priority_created';
26
27 // Notify assignment group manager
28 if (current.assignment_group) {
29 var groupManager = getGroupManager(current.assignment_group.toString());
30 if (groupManager) {
31 recipients.push(groupManager);
32 }
33 }
34
35 // Notify incident manager role
36 recipients = recipients.concat(getUsersWithRole('incident_manager'));
37
38 additionalInfo.reason = 'High-priority incident requires immediate attention';
39 }
40
41 // Scenario 2: Incident reassigned multiple times (potential issue)
42 if (!current.isNewRecord()) {
43 var reassignmentCount = getReassignmentCount(current.sys_id.toString());
44
45 if (reassignmentCount >= 3) {
46 shouldNotify = true;
47 notificationType = 'excessive_reassignments';
48
49 // Notify service delivery manager
50 recipients = recipients.concat(getUsersWithRole('service_delivery_manager'));
51
52 additionalInfo.reason = 'Incident reassigned ' + reassignmentCount + ' times';
53 additionalInfo.reassignmentCount = reassignmentCount;
54 }
55 }
56
57 // Scenario 3: SLA breach imminent (within 30 minutes)
58 if (current.sla_due) {
59 var slaDue = new GlideDateTime(current.sla_due);
60 var now = new GlideDateTime();
61 var timeUntilDue = GlideDateTime.subtract(now, slaDue);
62 var minutesUntilDue = timeUntilDue.getNumericValue() / (1000 * 60);
63
64 if (minutesUntilDue > 0 && minutesUntilDue <= 30) {
65 shouldNotify = true;
66 notificationType = 'sla_breach_imminent';
67
68 // Notify assigned user and their manager
69 if (current.assigned_to) {
70 recipients.push(current.assigned_to.toString());
71
72 var assigneeManager = current.assigned_to.manager.toString();
73 if (assigneeManager) {
74 recipients.push(assigneeManager);
75 }
76 }
77
78 additionalInfo.reason = 'SLA breach in ' + minutesUntilDue.toFixed(0) + ' minutes';
79 additionalInfo.minutesUntilDue = minutesUntilDue.toFixed(0);
80 }
81 }
82
83 // Scenario 4: VIP caller incident
84 if (current.caller_id && current.caller_id.vip.toString() === 'true') {
85 shouldNotify = true;
86 notificationType = 'vip_incident';
87
88 // Notify VIP support team
89 recipients = recipients.concat(getUsersInGroup('VIP Support'));
90
91 // Notify caller's account manager if exists
92 if (current.caller_id.u_account_manager) {
93 recipients.push(current.caller_id.u_account_manager.toString());
94 }
95
96 additionalInfo.reason = 'VIP caller requires priority attention';
97 }
98
99 // Scenario 5: Incident age threshold exceeded
100 if (current.opened_at) {
101 var opened = new GlideDateTime(current.opened_at);
102 var now = new GlideDateTime();
103 var age = GlideDateTime.subtract(opened, now);
104 var ageHours = Math.abs(age.getNumericValue()) / (1000 * 60 * 60);
105
106 // Notify if incident open > 48 hours
107 if (ageHours > 48 && current.state.toString() !== '6') { // Not resolved
108 shouldNotify = true;
109 notificationType = 'aging_incident';
110
111 // Notify assignment group and manager
112 if (current.assignment_group) {
113 recipients = recipients.concat(getGroupMembers(current.assignment_group.toString()));
114 }
115
116 additionalInfo.reason = 'Incident open for ' + ageHours.toFixed(0) + ' hours';
117 additionalInfo.ageHours = ageHours.toFixed(0);
118 }
119 }
120
121 // Send notifications if needed
122 if (shouldNotify && recipients.length > 0) {
123 // Remove duplicates
124 recipients = removeDuplicates(recipients);
125
126 // Send notification to each recipient
127 recipients.forEach(function(recipientId) {
128 sendNotification(recipientId, current, notificationType, additionalInfo);
129 });
130
131 gs.info('Sent ' + notificationType + ' notifications for incident ' +
132 current.number + ' to ' + recipients.length + ' recipients');
133 }
134
135 } catch (e) {
136 gs.error('Async notification BR error for incident ' + current.number + ': ' + e.message);
137 }
138
139 // Helper functions
140 function getGroupManager(groupId) {
141 var grGroup = new GlideRecord('sys_user_group');
142 if (grGroup.get(groupId) && grGroup.manager) {
143 return grGroup.manager.toString();
144 }
145 return null;
146 }
147
148 function getUsersWithRole(roleName) {
149 var users = [];
150 var grUserRole = new GlideRecord('sys_user_has_role');
151 grUserRole.addQuery('role.name', roleName);
152 grUserRole.addQuery('user.active', 'true');
153 grUserRole.query();
154
155 while (grUserRole.next()) {
156 users.push(grUserRole.user.toString());
157 }
158 return users;
159 }
160
161 function getUsersInGroup(groupName) {
162 var users = [];
163 var grGroup = new GlideRecord('sys_user_group');
164 grGroup.addQuery('name', groupName);
165 grGroup.query();
166
167 if (grGroup.next()) {
168 var grMember = new GlideRecord('sys_user_grmember');
169 grMember.addQuery('group', grGroup.sys_id);
170 grMember.query();
171
172 while (grMember.next()) {
173 users.push(grMember.user.toString());
174 }
175 }
176 return users;
177 }
178
179 function getGroupMembers(groupId) {
180 var users = [];
181 var grMember = new GlideRecord('sys_user_grmember');
182 grMember.addQuery('group', groupId);
183 grMember.query();
184
185 while (grMember.next()) {
186 users.push(grMember.user.toString());
187 }
188 return users;
189 }
190
191 function getReassignmentCount(incidentId) {
192 var ga = new GlideAggregate('sys_audit');
193 ga.addQuery('tablename', 'incident');
194 ga.addQuery('documentkey', incidentId);
195 ga.addQuery('fieldname', 'assignment_group');
196 ga.addAggregate('COUNT');
197 ga.query();
198
199 if (ga.next()) {
200 return parseInt(ga.getAggregate('COUNT'));
201 }
202 return 0;
203 }
204
205 function removeDuplicates(arr) {
206 return arr.filter(function(item, index) {
207 return arr.indexOf(item) === index;
208 });
209 }
210
211 function sendNotification(userId, incident, type, info) {
212 // Create notification using gs.eventQueue or direct email
213 gs.eventQueue('custom.incident.notification', incident, userId, type,
214 JSON.stringify(info));
215
216 // Alternative: Send email directly
217 /*
218 }
219
220})(current, previous);
221 email.setSubject('Incident Alert: ' + incident.number);
222 email.setBody('Alert Type: ' + type + '\nReason: ' + info.reason);
223 email.addAddress(grUser.email.toString());
224 email.send();
225 }
226 */
227 }
228
229})(current, previous);
How to Use
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
Related Scripts
Auto-assign Based on Category
Automatically assign tickets to the appropriate assignment group based on category.
Send Email Notifications
Send customized email notifications when specific conditions are met.
Populate Fields on Insert
Automatically populate fields with default values or calculated values when a record is created.