Archive for October, 2009

Associate Email to Salesforce Task to Opportunity

I use the Email to Salesforce functionality every single day.  This feature allows you to get a random email address similar to (this is just a sample) that you bcc on emails and Tasks get created in your system that are auto-associated to your Leads/Contacts.

There is an option to associate the Task to Opportunities, but instead of creating a Task and associating it to the Lead/Contact AND the Opportunity, 2 tasks are created: one against the Lead/Contact and another against the Opportunity.  I have no idea why it was designed this way, but it was.  Given that, I don’t use the option to associate the Task to Opportunities.

After months of manually assigning to Opportunities after I send the email, I got fed up and wrote a trigger that senses an email to salesforce record and auto associates it to the nearest Open Opportunity.  I thought I’d share it with y’all.

This code assumes the following:

  • You are using Email to Salesforce
  • You have the Email to Salesforce option for Leads & Contacts enabled and the one for Opportunities disabled
  • You associate Contacts to your Opportunities via the OpportunityContactRole object


trigger Tasks on Task (before insert) {

	if(Trigger.isBefore && Trigger.isInsert){
		Tasks t = new Tasks();


public class Tasks {

    // Default Constructor
    public Tasks()
    // Associates a new Task generated by Email to Salesforce to an open opportunity, if one exists for the Account
    public void AssociateOpportunity(Task[] tasks) 
        * Variables
		list<Task> l_Tasks = new list<Task>(); // Tasks we'll be updating
		set<ID> s_ContactIDs = new set<ID>(); // Set of Contact IDs
        * Initial Loop
		for(Task t:tasks) {
			// Add Task to working list and collect the Contact ID
			if (t.WhatId == null && t.Subject.startsWith('Email:') && t.WhoId != null) {
				// only for Contacts
				if (String.valueOf(t.WhoId).startsWith('003')){
        * Create Maps
        // Maps Contact ID to an Opportunity ID
		map<ID, ID> map_cID_to_oID = new map<ID, ID>();
			// Query for the Contact's Open Opportunities. Sort by CloseDate DESC so the Task gets assigned to the earliest Opportunity as it loops
			for (OpportunityContactRole ocr:[select Id, OpportunityId, ContactId
											 from OpportunityContactRole 
											 where ContactId in :s_ContactIDs 
											 AND Opportunity.IsClosed = false
											 order by Opportunity.CloseDate DESC
											 ]) {
				map_cID_to_oID.put(ocr.ContactId, ocr.OpportunityId);
        * Process Records
		for (Task t:l_Tasks) {
			// If the Contact has an Opportunity mapped to it, update the Task with that Opportunity
			if (map_cID_to_oID.get(t.WhoId) != null) {
				t.WhatId = map_cID_to_oID.get(t.WhoId);

Test Class

private class Tasks_Test {

    static testMethod void AssociateOpportunity_Test() {
        // Create a Lead
        Lead l = new Lead();
	        l.FirstName = 'Test';
	        l.LastName = 'Lead';
	        l.Company = 'Test Company';
	        l.Email = ''; 
        insert l;
        // Create an Account
        Account a = new Account();
        	a.Name = 'Test Account';
        insert a;
        // Create a Contact
        Contact c = new Contact();
	        c.FirstName = 'Test';
	        c.LastName = 'Contact';
	        c.AccountId = a.Id;
	        c.Email = ''; 
        insert c;
        // Create Opportunities
        list<Opportunity> l_Opps = new list<Opportunity>();
        Opportunity o = new Opportunity();
        	o.AccountId =;
        	o.Name = 'Test Opportunity';
        	o.CloseDate =;
        	o.StageName = 'Qualified';
        	o.Description = 'Test Opportunity Description';
        Opportunity o2 = new Opportunity();
        	o2.AccountId =;
        	o2.Name = 'Test Opportunity';
        	o2.CloseDate =;
        	o2.StageName = 'Qualified';
        	o2.Description = 'Test Opportunity Description';
        Opportunity o3 = new Opportunity();
        	o3.AccountId =;
        	o3.Name = 'Test Opportunity';
        	o3.CloseDate =;
        	o3.StageName = 'Closed Won';
        	o3.Description = 'Test Opportunity Description';
        insert l_Opps;
        // Create Opportunity Contact Roles
        list<OpportunityContactRole> l_Ocr = new list<OpportunityContactRole>();
    	OpportunityContactRole ocr1 = new OpportunityContactRole();
    		ocr1.ContactId =;
    		ocr1.OpportunityId =;
			ocr1.IsPrimary = true;
			ocr1.Role = 'Decision Maker';
		OpportunityContactRole ocr2 = new OpportunityContactRole();
    		ocr2.ContactId =;
    		ocr2.OpportunityId =;
			ocr2.IsPrimary = true;
			ocr2.Role = 'Decision Maker';
		insert l_Ocr;
        /* Create Tasks for Test Cases */
        list<Task> l_Tasks = new list<Task>();
        // Task associated to Lead, not Contact
        Task t1 = new Task();
        	t1.Subject = 'Email: something';
        	t1.Status = 'Completed';
        	t1.WhoId =;
        	t1.ActivityDate =;
    	// Task with wrong subject
    	Task t2 = new Task();
        	t2.Subject = 'something';
        	t2.Status = 'Completed';
        	t2.WhoId =;
        	t2.ActivityDate =;
    	// Task with no WhoId
    	Task t3 = new Task();
        	t3.Subject = 'something';
        	t3.Status = 'Completed';
        	t3.ActivityDate =;
    	// Task with a What ID already
    	Task t4 = new Task();
        	t4.Subject = 'something';
        	t4.Status = 'Completed';
        	t4.WhoId =;
        	t4.WhatId =;
        	t4.ActivityDate =;
    	// Task that should get triggered fully
    	Task t5 = new Task();
        	t5.Subject = 'Email: something';
        	t5.Status = 'Completed';
        	t5.WhoId =;
        	t5.ActivityDate =;
    	insert l_Tasks;
 		/* Asserts */
 		// Task 1 should not have a What ID populated
 		Task t = [select Id, WhoId, WhatId from Task where Id = limit 1];
 		system.assertEquals(t.WhatId, null);
 		// Task 2 should not have a What ID populated
 		t = [select Id, WhoId, WhatId from Task where Id = limit 1];
 		system.assertEquals(t.WhatId, null);
 		// Task 3 should not have a What ID populated
 		t = [select Id, WhoId, WhatId from Task where Id = limit 1];
 		system.assertEquals(t.WhatId, null);
 		// Task 4 should have the same What ID it had originally populated
 		t = [select Id, WhoId, WhatId from Task where Id = limit 1];
 		// Task 5 is the one that should've had the Opportunity ID auto populated
 		t = [select Id, WhoId, WhatId from Task where Id = limit 1];

Let me know if you have any suggestions.

Comments (4) comments feed