[SalesForce] SingleEmailMessage SetWhatId with Quote Id

I am currently recreating much of the OOB Quote functionality in Apex and I am almost complete. Now there is just one other hurdle, the Email Quote functionality.

Now, it doesn't seem like an issue…. at first.

I am able to create a QuoteDocument, set it up as an attachment, setup my email message with a template, and even create a task for the email being sent. However, as soon as I set the WhatId using the setWhatId method, I get the following error:

System.EmailException: SendEmail failed. First exception on row 0; first error: INVALID_ID_FIELD, Only accounts, assets, campaigns, cases, contracts, opportunities, orders, products, solutions and custom objects are allowed as whatId.: []

Now, the error message states that the OOB Quote object that I am using cannot be linked to a Task using the WhatId. Now, I would have gladly stopped there, but the problem is, there is a lot of functionality that contradicts this error.

Here are some red flags that make me believe there should be some way to set the WhatId with a Quote Id.

Red Flags

Red Flag #1: Email Quote button not only creates an e-mail but a Task, which is shown on the Activity History related list on the Quote detail page.


The standard Email Quote button does create a Task when the e-mail is sent. The only way it would show up on the Quote's detail page, in the Activity History related list no less, is if that Task was linked via the WhatId. Furthermore, you can create a new Task for the Quote via the Open Activities related list.
Activity Related List with Task

Red Flag #2: Task detail page shows Related To field going to a Quote


When I clicked on the Task created by the Email Quote button, I looked at the Related To field (which is the WhatId), it links to the Quote I created.
Task Detail Part One
Now, I thought that maybe it was a mistake, so I chose to edit the Task. But in doing so, it just verified that it was linking to the Quote.
Task Detail Part 2

Red Flag #3 (the major one): When I query the Task and inspect the WhatId, it IS the Quote Id!


So to completely verify that the Task's WhatId was being set with a Quote Id, I decided to query the Task using Execute Anonymous. So I entered the following code:

Task taskLinkingToQuote = [SELECT WhatId FROM Task 
    WHERE Id = '00Tj0000009PsWZ' LIMIT 1];
System.Debug(LoggingLevel.Info, taskLinkingToQuote);

But, lo and behold, the WhatId was set the Quote.

Debug Log Linking To Quote

So, somehow, it seems that the Email Quote button can not only create a Task but, it can link the Quote to it via the WhatId.


So now all I can say about the WhatId issue is W.T.F.!? (Sorry, couldn't resist.) This functionality should work in Apex. Hell, there is even code out right now that depends on it, like this one.

Apparently, it worked before. There has even been an issue raised about it here.

So has anyone run into and resolved this issue? Is there anyway around this issue? I haven't tried simply creating a new Task yet, but I wouldn't want to go through the effort and run into the same error.

I would simply like to have the Task appear on the correct detail page – the Quote's. I currently have to suffice with the Opportunity, but I'd like to get the functionality to operate as if it is OOB.

Best Answer

So I finally got around to fixing the issue.

Just as @crop1645 said, if I simply INSERT the Task via a DML statement, I will be able to create a Task with a Quote's Id as the WhatId.

So before, as a recap, I tried something like this:

Messaging.SingleEmailMessage newEmail = new Messaging.SingleEmailMessage();
newEmail.setToAddresses(new List<String> { emailAddress });
newEmail.setTargetObjectId(quoteBeingUsed.ContactId);
newEmail.setWhatId(quotesId);
newEmail.setTemplateId(TemplateId);
newEmail.setFileAttachments(new List<Messaging.EmailFileAttachment> { newAttachment });
Messaging.sendEmail(new List<Messaging.SingleEmailMessage> { newEmail });

This resulted in throwing the following exception:

System.EmailException: SendEmail failed. First exception on row 0; first error: INVALID_ID_FIELD, Only accounts, assets, campaigns, cases, contracts, opportunities, orders, products, solutions and custom objects are allowed as whatId.: []

Now how I solved it was simple. However, I wanted to make sure I got the Task information exactly like it was done by the Email Quote button. I changed the WhatId for the SingleEmailMessage to the related OpportunityId and after the sendEmail method I followed it with:

Task newQuoteEmailTask = new Task(Subject = 'Email: ' + newEmail.getSubject(), 
     OwnerId = UserInfo.getUserId(), WhatId = quotesId, WhoId = quoteBeingUsed.ContactId,
     Status = 'Completed', ActivityDate = Date.today(), Priority = 'Normal', 
     Description = GenerateTaskComment(newEmail));
INSERT newQuoteEmailTask; 

The Description's method is the following:

private static String GenerateTaskComment(Messaging.SingleEmailMessage newEmail)
{
    List<String> listOfFileNames = new List<String>();

    for(Messaging.EmailFileAttachment singleAttachment : newEmail.getFileAttachments())
        listOfFileNames.add(singleAttachment.getFileName());

    String endOfDescription = '_____________________________________________________________________'
    + '\nPowered by salesforce.com\nhttp://www.salesforce.com/';

    return 'Additional To: '+ String.join(newEmail.getToAddresses() != null ? newEmail.getToAddresses() : new List<String>(), ',')
        + '\nCC: ' + String.join(newEmail.getCcAddresses() != null ? newEmail.getCcAddresses() : new List<String>(), ',') 
        + '\nBCC: ' + String.join(newEmail.getBccAddresses() != null ? newEmail.getBccAddresses() : new List<String>(), ',') 
        + '\nAttachment'+ (listOfFileNames.size() > 1 ? 's' : '') + ': ' + String.join(listOfFileNames, ',')
        + '\n\nSubject: ' + newEmail.getSubject()
        + '\nBody:\n' + newEmail.getPlainTextBody()
        + '\n\n' + endOfDescription;
}
Related Topic