Tuesday, 30 May 2017

Email handler to update Opportunity Stage

Email handler to update Opportunity Stage


Email to Case in Salesforce is an excellent way to bring email data into your ORG and associate it with a current problem/question from existing customers. 

Web to Lead is also a really flexible way to bring in new business enquiries for potential new customers and process them in a structured way as Lead records.

But, what about Email to Opportunity?


It's possible!

Huge thanks to Jeff Douglas for his awesome blog post on the Messaging.InboundEmailHandler.

The Use Case

A customer of mine wanted to be able to copy in Salesforce via email in order to progress an Opportunity through it's sales cycle. 

So, an email with 'Closed Won' in the body would find and update the most relevant Opportunity to 'Closed Won'.

The Class

global class ProcessOpportunityStage implements Messaging.InboundEmailHandler {
    
/* Written by Iain Clements
* Logic >>
* Find Opportunity Reference in subject line of inbound email
* Search for existing Opp with reference
* If Opp Exists, search email body for suitable Stage
* Update Opp stage based on conditional check of email
*/ 
    
global Messaging.InboundEmailResult handleInboundEmail(Messaging.InboundEmail email,
Messaging.InboundEnvelope envelope) {
Messaging.InboundEmailResult result = new Messaging.InboundEmailresult();
                                                               
//Search for the Unique Reference in Subject Line    
String subject = email.subject;
Pattern idPattern = Pattern.compile('[A-Z][0-9][A-Z][0-9]');
Matcher matcher = idPattern.matcher(subject);
System.debug(matcher.find() + ' is ' + matcher.group(0));
String unique_ref = matcher.group(0);
    
String body = email.plainTextBody;
Opportunity opp = [SELECT Name, StageName FROM Opportunity WHERE Unique_Reference__c =: unique_ref];
                                                               
//Only act if email contains Key Phrases
if(body.contains('Closed Won')) {
opp.StageName = 'Closed Won';
}
else {
opp.StageName = 'Negotiating';
}
                                                               
//Perform update
update opp;
                                                               
 //only send result message during testing - comment when deployed
 result.message = 'You have successfully updated ' + opp.Name + ' stage to ' + opp.StageName;
                                                               
return result;
                                                               
}
        
} //end class

The Test Class

@isTest(SeeAllData=true)

private class ProcessOpportunityStageTest {
    static testMethod void testClosedWon() {

  // create a new email and envelope object
  Messaging.InboundEmail email = new Messaging.InboundEmail() ;
  Messaging.InboundEnvelope env = new Messaging.InboundEnvelope();

  // setup the data for the email
  email.subject = 'X1X1';
  email.fromname = 'FirstName LastName';
  email.plainTextBody = 'Closed Won';
  env.fromAddress = 'someaddress@email.com';

  // call the email service class and test it with the data in the testMethod
  ProcessOpportunityStage emailProcess = new ProcessOpportunityStage();
  emailProcess.handleInboundEmail(email, env);

  // query for the opportunity the email service updated
  Opportunity opp = [select id, Name, Unique_Reference__c, StageName from Opportunity
  where Unique_Reference__c = 'X1X1'];

  System.assertEquals(opp.StageName,'Closed Won');



}
    
    static testMethod void testOther() {

  // create a new email and envelope object
  Messaging.InboundEmail email = new Messaging.InboundEmail() ;
  Messaging.InboundEnvelope env = new Messaging.InboundEnvelope();

  // setup the data for the email
  email.subject = 'Test Job Applicant X1X1';
  email.fromname = 'FirstName LastName';
  email.plainTextBody = 'Anything else';
  env.fromAddress = 'someaddress@email.com';

  // call the email service class and test it with the data in the testMethod
  ProcessOpportunityStage emailProcess = new ProcessOpportunityStage();
  emailProcess.handleInboundEmail(email, env);

  // query for the opportunity the email service updated
  Opportunity opp = [select id, Name, Unique_Reference__c, StageName from Opportunity
    where Unique_Reference__c = 'X1X1'];

  System.assertEquals(opp.Unique_Reference__c,'X1X1');
  System.assertEquals(opp.StageName,'Negotiating');



}
    
       
}

Thoughts

The Test Class is currently using a real record from the environment which is not best practice. After testing this out, I will update this so that the Opportunity record is actually created within the Test Class.

If any more scenarios are needed for the class, I will move everything into a function.

The method of that function would take in the phrase from the email that we're scanning for - e.g. createHandler('Closed Won') and generate the rest of the required code from there.

Finally, I'm using the result.message response for testing but it may be better to store changes in a custom object for reporting purposes in future.

What else did I miss?

Can you think of another way of accomplishing this?

Image from Pexels.com, artist Pixabay