I have used the Lightning Experience UI to send an Email from a Record Homepage via Activity-Tab. This looks like this (bottom-right!):
Now I want (vice versa) to store an incoming email received by an Apex Email Service in exactly the manner as Salesforce is doing that for outbound emails. At least I want to push I as close as possible or practical.
Note: I want that fancy stuff, too, like "Reply", "Forward", etc
I have reverse engineered what strange things Salesforce is doing for outbound emails:
- A Task of
TaskSubtype
= 'Email' will be created - The
WhatId
ofTask
is pointing to the record from which I have sent the email - An
EmailMessage
will be created with it'sActivityId
set to the Task-Id - Attachments will be stored as
ContentDocument
havingContentDocumentsLink
to theEmailMessage
, not to the Task
Now translating this recipe for inbound emails, my code should look something like this (which works fine, all but one line):
global class pfBCQuoteDocumentEmailService implements Messaging.InboundEmailHandler {
global Messaging.InboundEmailResult handleInboundEmail(Messaging.InboundEmail email, Messaging.InboundEnvelope envelope) {
Messaging.InboundEmailResult result = new Messaging.InboundEmailresult();
try {
String tibisId = email.Subject.substringAfter('_').substringBefore('_');
pfBCQuote__c quote = (pfBCQuote__c)(xs.query(''
+ ' select * from pfBCQuote__c where pfBCTibisID__c = \''+tibisId+'\' '
).get(0));
String closedLabel = (String)(xs.query(''
+ ' select * from TaskStatus where IsClosed = true limit 1 '
).get(0).get('ApiName'));
Task createTask = new Task(
TaskSubtype = 'Email'
,Subject = email.Subject + ' [EmailService]'
,ActivityDate = Date.today()
,Status = closedLabel
,WhatId = quote.Id
,Description = email.plainTextBody
);
insert createTask;
EmailMessage createEmail = new EmailMessage(
Subject = email.Subject + ' [EmailService]'
// ,ActivityId = createTask.Id // NOT WRITABLE !
,Status = '1'
,CcAddress = xt.implode(',',email.ccAddresses)
,FromAddress = email.fromAddress
,FromName = email.fromName
,Headers = JSON.serialize( email.headers )
,HtmlBody = email.htmlBody
,TextBody = email.plainTextBody
,Incoming = true
,MessageDate = DateTime.now()
);
insert createEmail;
xt.logMail('pfBCQuoteDocumentEmailService.handleInboundEmail()',new Object[]{email,quote} );
} catch(Exception e) {
xt.logMail('ERROR :: pfBCQuoteDocumentEmailService.handleInboundEmail()',e);
}
return result;
}
}
Sidenotes to the code (irrelevant to my main question)
- xs.query() just selects stuff with * without all the hassel
- pfBCQuote__c is the object to which I want to add the received Email
- pfBCTibisID__c is a field on the record matching to a fragment of the incoming emails subject to get the appropriate record on which the email should be put in the activity tab.
- xt.logMail() is simply send debug-emails to myself as alternative to creep through the logs – just ignore it.
What does NOT work is setting the ActivityId on the EmailMessage.
Insert failed. First exception on row 0; first error:
INSUFFICIENT_ACCESS_OR_READONLY, You cannot edit this field:
[ActivityId]
Now my question is: What is the BEST way to store incoming emails to work in the Activity-Tab best?
- Should I only create the Task and go without the EmailMessage?
- Is there a different way to link Task and EmailMessage?
- How should I put received Attachments?
- is there a way to do that more out-of-the-box with less reverse-engineering and try-to-replicate-what-salesforce-does?
- is there specific documentation how Salesforce stores Emails on Record-Level?
- Has anyone done something similar and is willing to share some code-snippets or conceptual approaches?
- How would you store attachments?
ContentDocument
+Version orAttachment
What is working so far
- Activity gets create with the right icon
What is still missing
- fancy Reply, Forward stuff
- handling of email attachments
UPDATE
I did some more research and found insufficient documentation – but at least a bit: https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_emailmessage.htm
Doing like my reverse-engineered recipe seems just to be right:
ActivityId
Type reference
Properties Create, Filter, Group, Nillable, Sort
ID of the activity that is associated with the email. Usually
represents an open task that is created for the case owner when a new
unread email message is received. ActivityId can only be specified for
emails on cases. It’s auto-created for other entities.
It looks like another castration of Apex compared to the API. Using the API the field ActivityId
is usable on insert – only not updateable. Using APEX we get a punch and cannot even set it once on insert. We just can't set it at all. Hence we can't use it at all 🙁 Why that?
Also I think the only appropriate way to deal attachments is via ContentVersion
with a link via something like Messaging.EmailFileAttachment
. Going with the old Attachment
just seems totally wrong. But I'm not sure on how to glue my EmailMessage to the ContentVersion. I will investigate that further, also happy to get hints from you!
Best Answer
From what I understand, You don't need to create a task to insert an EmailMessage. Email message has
relatedToId
field which you can use to directly link it to any desired record. As relatedToId is polymorphic like ParentId for Attachments, you should be fine.Source: https://developer.salesforce.com/docs/atlas.en-us.212.0.api.meta/api/sforce_api_objects_emailmessage.htm
fancy Reply, Forward stuff : This can be easily handled by creating a record of
EmailMessageRelation
. It can only be created if email-to-case is enabled in your org.SRC : https://developer.salesforce.com/docs/atlas.en-us.212.0.api.meta/api/sforce_api_objects_emailmessage.htm
SRC: Send and Receive Email in Account,Lead,Opportunity,Quotes,Contact and maintain email loop
2. handling of email attachments : I don't fancy using standard Salesforce Attachment. Because attaching them to different records means cloning it and you cant' controll access(Determined by parent always). Creating a content version and linking it to email message looks the best solution to me. I recently created a utility method for linking email attachments to emailMessage as content document , you can refer it.