0

To begin, I'd like to explain that the following class works in tandem with a process that detects incoming email messages to a case and then sends the ID of that email message to the class.

The class should then get the Email Ids and send out an email to every follower of that case.

This class and process are working together beautifully in our sandbox environment, but something is going very awry in our production environment, as we haven't received any emails to our test follower accounts.

Can anyone spot something(s) that might cause this discrepancy, or should I dive further into Salesforce configurations to find the answer?

global class CaseEmailFollowersNotify { @InvocableMethod(label='Case Email to Followers Notify' description='emails followers of new case email') global static void GetCaseCommentIds(List<Id> EmailIds) { for(emailmessage em : [SELECT Id, ParentId, FromAddress, TextBody FROM EmailMessage WHERE Id in: EmailIds]) { string tbody = em.TextBody; string tbody2 = tbody; if(tbody.length() > 60) { tbody2 = tbody.substring(0, 59); } tbody2 = tbody2 + '...'; List<EntitySubscription> e = [select Id, ParentId, SubscriberId from EntitySubscription where ParentId =: em.ParentId]; for(EntitySubscription es : e) { List<User> u = [select Email, Id from User where Id =: es.SubscriberId]; Case ca = [select Subject, CaseNumber from Case where Id =: es.ParentId limit 1]; for(User us : u) { Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage(); mail.setHTMLBody('blahbalhblah'); mail.setTargetObjectId(us.Id); mail.setWhatId(es.ParentId); mail.setSubject('Case Following New Email'); mail.setSenderDisplayName('Case Following Update'); mail.saveAsActivity = false; Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail }); } } } } } 

Any help is sorely appreciated! Please note that the string manipulation is entirely to do with our email formatting and only exists to edit down the incoming email's contents to something that can be sent back out and abbreviated.

5
  • 1
    Check Email Deliverability settings. If everything looks correct, then check Email logs to see if the email was queued for send. Commented Apr 19, 2021 at 19:37
  • You do have a bulkification issue in the code - all but the first query are in loops and will very quickly fail via violating SOQL query limits if multiple IDs are received in the preceeding queries/as input. You really should update this code to apply standard bulkification techniques. Commented Apr 19, 2021 at 20:30
  • Look at the logs to see if your code is failing due to limits being exceeded. Commented Apr 19, 2021 at 20:33
  • check your debug logs in prod and verify the invocable method is actually being called from your source PB or Flow . A common mistake is you deployed the PB/Flow and forgot to activate it (!) Commented Apr 19, 2021 at 22:55
  • Have you set a debug log? Please edit your post to include the relevant details. Commented Apr 20, 2021 at 3:13

1 Answer 1

1

On production:

  1. Check that the Process Builder is activated
  2. Look through the debug logs to see if any governor limits are being exceeded

In the meantime, here's a bulkified version of the code (it doesn't address ensuring that you don't exceed per-transaction sendEmail call limits - for that you need some async processing):

public class CaseEmailFollowersNotify { @InvocableMethod(Label='Case Email to Followers Notify' Description='emails followers of new case email') public static void getCaseCommentIds(List<Id> emailIds) { // Get all the email messages using the given email IDs List<EmailMessage> emailMessages = [ SELECT Id, ParentId, FromAddress, TextBody FROM EmailMessage WHERE Id IN :emailIds ]; // Collect all the IDs for the cases associated with the email messages Set<Id> caseIds = new Set<Id>(); for (EmailMessage emailMessage : emailMessages) { caseIds.add(emailMessage.ParentId); } // Query all entity subscriptions for these cases List<EntitySubscription> entitySubscriptions = [ SELECT Id, ParentId, SubscriberId FROM EntitySubscription WHERE ParentId IN :caseIds ]; // Build up a structure holding entity subscriptions by Case ID whilst also collecting all // User IDs Map<Id, List<EntitySubscription>> entitySubscriptionsByCaseId = new Map<Id, List<EntitySubscription>>(); Set<Id> userIds = new Set<Id>(); for (EntitySubscription entitySubscription : entitySubscriptions) { List<EntitySubscription> entitySubscriptionsForCase = entitySubscriptionsByCaseId.get(entitySubscription.ParentId); if (entitySubscriptionsForCase != null) { entitySubscriptionsForCase.add(entitySubscription); } else { entitySubscriptionsByCaseId.put(entitySubscription.ParentId, new List<EntitySubscription> { entitySubscription }); } userIds.add(entitySubscription.SubscriberId); } // Neither of these are actually used here, but I have them to // demonstrate how you would actually manage this data in case // you actually need these objects in your full code Map<Id, Case> casesById = new Map<Id, Case>([SELECT Subject, CaseNumber FROM Case WHERE Id IN :caseIds]); Map<Id, User> usersById = new Map<Id, User>([SELECT Email, Id FROM User WHERE Id IN :userIds]); // Now process the messages for (EmailMessage emailMessage : emailMessages) { String tbody = emailMessage.TextBody.left(60); if (tbody.size() != emailMessage.TextBody.size()) { tbody += '...'; } // This code is likely to fail due to exceeding the maximum number of calls allowed to sendEmail // per transaction (see https://developer.salesforce.com/docs/atlas.en-us.salesforce_app_limits_cheatsheet.meta/salesforce_app_limits_cheatsheet/salesforce_app_limits_platform_apexgov.htm) // You likely need to handle this via async logic, breaking this down into smaller transactions for (EntitySubscription entitySubscription : entitySubscriptionsByCaseId.get(emailMessage.ParentId)) { // Neither of these variables are actually used, but are included to demonstrate how you would // deal with this data if you do need it in your complete code User user = usersById.get(entitySubscription.SubscriberId); Case case = casesById.get(emailMessage.ParentId); // Create and send the email Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage(); mail.setHtmlBody('blahbalhblah'); mail.setTargetObjectId(entitySubscription.SubscriberId); mail.setWhatId(entitySubscription.ParentId); mail.setSubject('Case Following New Email'); mail.setSenderDisplayName('Case Following Update'); mail.saveAsActivity = false; Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail }); } } } } 

I've assumed you don't actually need to have global code since this invokable method isn't being exposed across a namespace boundary. I could be wrong in that assumption. However 99.999% of the time code posted in questions here doesn't need to be global.

1
  • You're amazing and thank you so much for your help! I'll use this to help me complete this project; I'm really new to apex and Salesforce, so your openness to help is amazing and very much appreciated! Commented Apr 20, 2021 at 15:49

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.