Apex Trigger Example
Using an Apex Change Event Trigger to Predict Account Status
Because Apex change event triggers run asynchronously, they typically contain time-intensive processes that run after the database transaction is completed. This trigger example covers a more common real-world scenario than the trigger given in the quick start. It captures case changes and predicts the account trust status using an Apex class. The prediction is based on counting the account cases and checking case fields. In other scenarios, you might have complex prediction algorithms that are more resource intensive.
- A platform event named Red_Account__e with two fields: Account_Id__c of type Text and Rating__c of type Text
- The SLAViolation__c custom field on the Case object of type Picklist with values Yes and No
1trigger CaseChangeEventTrigger on CaseChangeEvent (after insert) {
2
3 List<CaseChangeEvent> changes = Trigger.new;
4
5 Set<String> caseIds = new Set<String>();
6
7 for (CaseChangeEvent change : changes) {
8 // Get all Record Ids for this change and add to the set
9 List<String> recordIds = change.ChangeEventHeader.getRecordIds();
10 caseIds.addAll(recordIds);
11 }
12
13 // Perform heavy (slow) computation determining Red Account
14 // status based on these Case changes
15 RedAccountPredictor predictor = new RedAccountPredictor();
16 Map<String, boolean> accountsToRedAccountStatus =
17 predictor.predictForCases(new List<String>(caseIds));
18
19 // Publish platform events for predicted red accounts
20 List<Red_Account__e> redAccountEvents = new List<Red_Account__e>();
21 for (String acctId : accountsToRedAccountStatus.keySet()) {
22 String rating = accountsToRedAccountStatus.get(acctId) ? 'Red' : 'Green';
23 if (rating=='Red') {
24 redAccountEvents.add(new Red_Account__e(Account_Id__c=acctId,
25 Rating__c=rating));
26 }
27 }
28 System.debug('RED_ACCT: ' + redAccountEvents);
29 if (redAccountEvents.size() > 0) {
30 EventBus.publish(redAccountEvents);
31 }
32}The RedAccountPredictor class performs the prediction of the account trust level. The first method that the trigger calls is predictForCases, which calls other methods in this class. The method returns a map of account ID to a Boolean value for account status.
1public class RedAccountPredictor {
2
3 private static final Integer MAX_CASES_EXPECTED = 2;
4
5 public RedAccountPredictor() { }
6
7 // First method to be called for performing account status prediction.
8 // Get the account IDs related to the passed-in case IDs
9 // and call a predictor method.
10 // Return a map of account ID to account status Boolean.
11 public Map<String, boolean> predictForCases(List<String> caseId) {
12 List<Case> casesMatchingIds =
13 [SELECT Id, Account.Id FROM Case WHERE Id IN :caseId];
14 if (null != casesMatchingIds && casesMatchingIds.size() > 0) {
15 List<String> accountIds = new List<String>();
16 for (Case c : casesMatchingIds) {
17 accountIds.add(c.Account.Id);
18 }
19 return predictForAccounts(accountIds);
20 } else {
21 return new Map<String, boolean>();
22 }
23 }
24
25 // Perform slow, resource intensive calcuation to determine.
26 // If Account is in RED status (think Einsein predictions, etc.)
27 public Map<String, boolean> predictForAccounts(List<String> acctIds) {
28 List<Case> casesForAccounts =
29 [SELECT Id, Account.Id, Status, CaseNumber, Priority,
30 IsEscalated, SLAViolation__c
31 FROM Case
32 WHERE AccountId IN :acctIds AND Status !='Closed'];
33 Map<String, List<Case>> accountsToCases = new Map<String, List<Case>>();
34 for (Case c : casesForAccounts) {
35 if (null == c.Account.Id) continue;
36 if (!accountsToCases.containsKey(c.Account.Id)) {
37 accountsToCases.put(c.Account.Id, new List<Case>());
38 }
39 accountsToCases.get(c.Account.Id).add(c);
40 }
41 Map<String, boolean> results = new Map<String, boolean>();
42 for (String acctId : accountsToCases.keySet()) {
43 results.put(acctId, predict(accountsToCases.get(acctId)));
44 }
45 return results;
46 }
47
48 // Perform the account status prediction.
49 // Return true if account is red; otherwise, return false.
50 private boolean predict(List<Case> casesForAccount) {
51 boolean isEscalated = false;
52 boolean hasSlaViolation = false;
53 boolean hasHighPiority = false;
54 boolean allStatusesResolved = true;
55
56 for (Case openCase : casesForAccount) {
57 isEscalated |= openCase.IsEscalated;
58 hasSlaViolation |= (openCase.SLAViolation__c == 'Yes');
59 hasHighPiority |= openCase.Priority == 'High';
60 allStatusesResolved &= (openCase.Status == 'Closed'
61 || openCase.Status == 'Part Received');
62 }
63 if (allStatusesResolved) {
64 return false;
65 }
66 if (casesForAccount.size() > MAX_CASES_EXPECTED) {
67 return true;
68 } else if (isEscalated || hasSlaViolation) {
69 return true;
70 } else if (hasHighPiority) {
71 return true;
72 }
73 return false;
74 }
75}