Emails in Cases in Lightning
- 1.1 Video
- 1.2 General Email Functionality in Cases
- 1.2.1 Email Issues
- 1.2.2 Interesting Things
- 1.2.3 The Salesforce Way
- 1.2.4 Email Button / Send Email Quick Actions
- 2 Using Code to Send Emails
- 2.1.1 My Decision Tree
- 2.1.2 Code Gotchas
- 2.1.3 My Code
- 2.2 A Fix
- 2.3 Testing This Code
I don’t think I’ve come across any part of Salesforce that is so hard to understand, set up, manage and build a great user experience.
I may actually give up before I get very far with it. YEP, I GAVE UP. Wait! I’m back, and I think I’ve conquered it! But no, there are STILL issues!
I did a Video a few months ago for regular emails https://www.youtube.com/watch?v=nhhoGRyBnxU and an video on Emails in Cases before that https://www.youtube.com/watch?v=AKQruEHMZ5I to try to surface some of the issues. Now I’m trying to get it set up for real.
Video
General Email Functionality in Cases
Email Issues
If an email comes into the Case, you may have an alert set to notify the Case Owner, but it does not show up in the Feed until you refresh the whole Case.
You can’t use Case Merge fields in Lightning Email Templates unless you just type them in when you are in Email Templates. But if you create the Template from the Case itself, then you can insert Merge Fields from Cases. Bizarre.
The Email History in the Activity component goes to Tasks, not to Enhanced Email! Why? Bizarre!
The Cases object is font-a-palooza. There are so many fonts on the page, in the email, in the feed and in the enhanced email it just makes it look completely ugly.
The complete and utter bizarre ugliness that you can have visible on the screen if you reply to an email from the Enhanced Email popup plus the feed is just ludicrous.
Inserting an Email Template with a Letterhead allows the letterhead to be edited (See this NO FIX KI)
Interesting Things
Handlebars merge format works in Custom Email Templates
Classic Merge Format works in Quick Text used in Lightning Emails.
Quick Text is good. Just use Quick Text.
Validated From is an Email Address and is either
Org Wide Default Email address
Email-to-Case Email address - but see Email To Case as to why you don’t want to use that.
The User Email
To can be an ID or an Email Address.
The Salesforce Way
So basically what Salesforce is saying in the way they have set up Cases, is that when handling cases you only ever going to have a very simple case with a couple of fields on it and you only ever going to email the person who submitted the Case back and forth until the case of resolved and the Case can be closed.
If you have any other Case functionality other than that, then don’t bother trying to set up Cases with configuration only, you are going to need to do a heap of work to try to overcome what Salesforce thinks is the only way to handle Cases.
In my case the functionality I need is
A heap of custom fields - where the fields change through the course of the Case process - this is currently handled by Record Types and Page Layouts, but one day in many years time it may be able to be handled by Dynamic Forms and Dynamic Actions.
Emails that MUST only come from the Org Wide Email Address and the Case Owner or User’s Name must never be on the Email.
Emails go to the client (Case Contact) OR to a third party. AND we must never cross the streams, so the way Salesforce insists that the email thread is maintained and you will always just reply to the same email is NEVER going to work.
Email Button / Send Email Quick Actions
You have to create an Email button yourself the standard Email action just does not work in Cases. See Help Article.
Even following the help article, the Email button on its own will NOT override the From or the To or the Subject. Cases thinks it knows best and just does what it thinks is best. This is NOT best.
You can set the To and Subject with a convoluted work-around in Macros, but that means using Macros and Macros are just not good. See my video on Macros https://www.youtube.com/watch?v=kW5a7zSk1j8
Use the JUNCTIONIDLIST formula to try to set the TO in the Email Quick Action. Eg
JUNCTIONIDLIST(
Case.ContactId)
You can only use Custom Email Templates, not even VF Email Templates or Lightning Email Templates as the default Email Template from the Button, but if you use Code, you can use Lightning Email Templates at least!
When you create Email Quick Actions and add them to the Case Feed, they also get added to two places on the Case Feed that is completely inappropriate!
The only way around this would be to have some way of making the code work for ALL scenarios from the one button. And I just can’t work out a way that can work.
OMG this is by DESIGN! (or at least it is documented that this will happen! UNBELIEVABLE! From the Help Doc “For each email in the case feed, agents see a dropdown menu with reply options. If you have multiple email actions in the case feed publisher, agents see groups of reply actions in the dropdown menu”. So they actually do NOT want you to EVER send a brand new email, they are only ever Replys? Sure, but then why do the Reply Actions appear in the Forward menu? Stupid. Stupid Stupid.
Using Code to Send Emails
Salesforce Documentation for Quick Action Defaults Handler https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_interface_QuickAction_QuickActionDefaultsHandler.htm
Email Message reference https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_emailmessage.htm
Sample Code - simple setup to just set the Email Template. Build upon it from there https://gist.github.com/douglascayers/6e32a277a945f0ab25c449a283ad4dba
A good Code example http://smukov.github.io/blog/2019/11/24/Automatic-Email-Template-Selection-in-Case-Feed/
My Decision Tree
Name | When | Button Name | From Address | To Address | Signature | Template | Subject | Include Body | REF Included | Attachments |
To Client | Any time | Email to Client | Primary Contact.Email | Sender.Title | BLANK | Your Case - {subject} {EmailThreadId} | No | Yes | Add any required | |
Reply to existing email | Any time | Reply or Reply All from Email | From Address | Sender.Title | BLANK | Re: Existing Subject | Yes | Yes | No | |
|
| Reply | From Address | Sender.Title | BLANK | Re: Existing Subject | Yes | Yes | No | |
Forward an existing email | Any time | Forward from Email | Enter a valid address | Sender.Title | BLANK | FW: Existing Subject | Yes | Yes | Included | |
To Third Party | Any time | Third Party Email | ThirdParty.Email | Sender.Title | THIRD PARTY BLANK | Case for {ClientName} - {Subject} {EmailThreadId} | No | Yes | Add any required |
Code Gotchas
Make sure you set the setting “Enable Default Email Templates or the Default Handler for Email Action” in Support Settings and choose the Apex Class there.
When inserting Email Templates via Code, they can NOT include Attachments, but you can use the public link to the File in a Hyperlink on the Email, and that is a better way to send attachments anyway.
The ID of the Email Template for Lightning Email Templates is just the ID in the URL, but if you want to use the Developer Name you will have to use SOQL or Salesforce Inspector to see the Developer Name.
When you do code, ensure you cater for the standard Reply, Reply All and Forward scenarios on the Enhanced Email also.
My Code
global class EmailDefaults implements QuickAction.QuickActionDefaultsHandler {
global void onInitDefaults(QuickAction.QuickActionDefaults[] defaultsList) {
for (Integer i = 0; i < defaultsList.size(); i++) {
QuickAction.QuickActionDefaults defaults = defaultsList.get(i);
// Check if the quick action is the standard case feed `SendEmail` action
if (
defaults instanceof QuickAction.SendEmailQuickActionDefaults &&
defaults.getTargetSObject().getSObjectType() == EmailMessage.sObjectType &&
defaults.getActionType().equals('SendEmail')
) {
String actionName = defaults.getActionName();
Id contextId = defaults.getContextId();
// check if the related object is a Case
// and process it in the same way no matter if it's
// a `SendEmail`, `Reply`, or `Reply All` action
if (
(actionName.equals('Case.Send_Email') ||
actionName.equals('Case.ReplyEmail') ||
actionName.equals('Case.ThirdPartyEmail') ||
actionName.equals('EmailMessage._Reply') ||
actionName.equals('EmailMessage._Forward') ||
actionName.equals('EmailMessage._ReplyAll')) &&
contextId != null &&
contextId.getSobjectType() == Case.sObjectType
) {
applySendEmailDefaultsForCase((QuickAction.SendEmailQuickActionDefaults) defaults, actionName);
break;
}
}
}
}
private void applySendEmailDefaultsForCase(QuickAction.SendEmailQuickActionDefaults sendEmailDefaults, string sendType) {
Case c = [
SELECT ContactId, LinkedContID__c, ThirdParty__c
FROM Case
WHERE Id = :sendEmailDefaults.getContextId()
];
EmailMessage emailMessage = (EmailMessage) sendEmailDefaults.getTargetSObject();
if (sendType.contains('Reply')) {
sendEmailDefaults.setTemplateId(getTemplateId('developerName'));
sendEmailDefaults.setInsertTemplateBody(true);
sendEmailDefaults.setIgnoreTemplateSubject(true);
emailMessage.ValidatedFromAddress = 'orgwidemail@example.com';
}
else if (sendType.contains('Forward')) {
sendEmailDefaults.setTemplateId(getTemplateId('developerName'));
sendEmailDefaults.setInsertTemplateBody(true);
sendEmailDefaults.setIgnoreTemplateSubject(true);
emailMessage.ValidatedFromAddress = 'orgwidemail@example.com';
}
else if (sendType.contains('ThirdParty')) {
String[] toIdsThirdParty = new String[]{c.ThirdParty__c};
sendEmailDefaults.setTemplateId(getTemplateId('developerName'));
sendEmailDefaults.setInsertTemplateBody(true);
sendEmailDefaults.setIgnoreTemplateSubject(false);
emailMessage.ToIds = toIdsThirdParty;
emailMessage.ToAddress = '';
emailMessage.ValidatedFromAddress = 'orgwidemail@example.com';
emailMessage.HTMLBody = '';
}
else{
//This is the default. New Email to Case Contacts only.
String[] toIds = new String[]{c.ContactId, c.LinkedContID__c};
sendEmailDefaults.setTemplateId(getTemplateId('developerName'));
sendEmailDefaults.setInsertTemplateBody(true);
sendEmailDefaults.setIgnoreTemplateSubject(false);
emailMessage.ToIds = toIds;
emailMessage.ToAddress = '';
emailMessage.ValidatedFromAddress = 'orgwidemail@example.com';
emailMessage.HTMLBody = '';
}
}
private ID getTemplateId( String templateName ) {
ID templateId = null;
List<EmailTemplate> templates = new List<EmailTemplate>([
SELECT id
FROM EmailTemplate
WHERE developerName = :templateName
LIMIT 1
]);
if ( templates.size() > 0 ) {
templateId = templates[0].id;
} else {
System.debug( LoggingLevel.ERROR, 'Unable to locate email template using name "' + templateName + '"' );
}
return templateId;
}
}
A Fix
I fixed it! I had to simplify my requirements a bit, and it’s the best of a really bad situation.
I changed the button name to Email
If the Case is at Status - “Ready for Service” then the “ThirdParty” code above will be used.
I changed
else if (sendType.contains('ThirdParty')) {
else if (c.Status.contains('Service')) {
and added Status to the SOQL query
If the Case is at any other Status then the default Email to Client Code will be used.
If they want to Reply to an Email they choose the email in the list and click Reply. AND there is only ONE REPLY BUTTON now!!!!
If they want to do something out of the ordinary then they need to carefully change the email to be what they want - change the To, the Subject, and insert a new Template.
OMG that took so much work and took so long to work through.
Bottom Line - NEVER USE MORE THAN ONE Email Button on Cases! EVER!
Testing This Code
I am SOO stuck on testing this code.
This is the extent of the Code Coverage I am getting following many of the examples of test classes found on Github. THIS SHOULD NOT BE THIS HARD. I am trying to install this solution in an org that has only this code and I can not get anywhere near the 75% code coverage.
The "suggested" way to test is Test Class for QuickAction.QuickActionDefaultsHandler
My test Class currently creates a Closed Case and does a Reply Email and the code coverage is not even looking at the Status of the Case let alone hitting the bit of code that actually sets the default templates.