function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
The_London_ScottThe_London_Scott 

How can I update Tasks via API in an org with 'Shared Activities' enabled?

I've adapted Scott Hammeter's dandy code (http://sfdc.arrowpointe.com/2009/10/06/associate-email-to-salesforce-task-to-opportunity/) so I can relate the tasks created by inbound Email-to-Salesforce emails to active campaigns rather than to open opportunities. The heart of the solution is a constructor in a class called by a beforeInsert task trigger. The construct queries for campaigns related to the contact identified in the task's WhoId:

// Associates a new Task generated by Email to Salesforce to an open opportunity, if one exists for the Account
    public void AssociateCampaign(Task[] tasks)
    {
       
        /***************
        * Variables
        ***************/
        list<Task> l_Tasks = new list<Task>(); // Tasks to update
        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')){
                    l_Tasks.add(t);
                    s_ContactIDs.add(t.WhoId);
                }
            }
           
        }
       
        /***************
        * Create Maps
        ***************/
        // Maps Contact ID to a Campaign ID
        map<ID, ID> map_cID_to_camID = new map<ID, ID>();
            // Query for the Contact's active Campaigns. Sort CampaignMember by CreatedDate DESC so the Task gets assigned to the campaign to which contact was most recently added
            for (CampaignMember cm:[select Id, CampaignId, ContactId, CreatedDate
                                             from CampaignMember
                                             where ContactId in :s_ContactIDs
                                             AND Campaign.IsActive = TRUE
                                             order by CreatedDate DESC
                                             ]) {
                map_cID_to_camID.put(cm.ContactId, cm.CampaignId);
                System.debug('ContactId - CampaignId:' + map_cId_to_camId);
            }
           
        /***************
        * Process Records
        ***************/
        for (Task t:l_Tasks) {
           
            // If the Contact has a campaign mapped to it, update the Task with that campaign
            if (map_cID_to_camID.get(t.WhoId) != null) {
                t.WhatId = map_cID_to_camID.get(t.WhoId);
              
            }
        }

This works as intended in orgs where the Shared Activities feature has not been enabled: a task created from an Email-to-Salesforce email has the contact's most recent active campaign in the WhatId (label: "Related To") field.

In orgs with Shared Activities enabled, the code fails to do this and WhatId remains null. Research makes me think that the code needs to insert a TaskRelation object for each Task object, with values
AccountId = cm.Contact.AccountId
IsWhat = TRUE
TaskId = Task.Id
RelationId = cm.CampaignId.

Has anyone else experienced this issue of needing to insert or update TaskRelation objects in order to populate Task.WhatId through the API?
Ashish_SFDCAshish_SFDC
Hi Scott, 


On an initial INSERT, I would populate the Task and respective WhoId. If there were any other records I needed to related, I would use the TaskRelation object using the newly insert Task record's Id.

What happens on the back end, with Shared Activities on, is whatever you marked as the WhoId gets a TaskRelation record behind the scenes. How do I know that? Well, let me go over the UPDATE scenario.

On an update of a Task you cannot change the WhoId or the WhatId - throws an exception. However, if you remove the TaskRelation records that contain the WhatId or WhoId, the next TaskRelation record will take its place.

Having said that, if you need to remove all of the other related records to a Task, be sure to exclude the WhoId and the WhatId.

To relate another record to a Task, you simply add another TaskRelation record.

http://salesforce.stackexchange.com/questions/25710/how-can-i-update-tasks-via-api-in-an-org-with-shared-activities-enabled


Regards,
Ashish