Skip to main content
New tool CRON Expression Builder — preview next run times before you schedule Apex. Open the builder →
3D Salesforce data connector illustrating accurate Last Activity Date tracking for shared activities.
Apex

Fix Salesforce Last Activity Date for Shared Activities

With Shared Activities on, Last Activity Date only moves for the primary contact on a Task or Event, so every secondary contact looks cold when they are not. Here is how to roll the real dates up from TaskRelation and EventRelation and report on one accurate field.

The short answer

With Shared Activities on, LastActivityDate only moves for the contact designated primary through WhoId, so every secondary contact on the same Task or Event looks cold. Roll the real dates up from TaskRelation and EventRelation into custom date fields, then consolidate them with a formula field to report on.

Key takeaways The standard LastActivityDate is unreliable once Shared Activities is enabled, because it only updates for the primary contact on Tasks and Events. That skews reporting for re-engagement campaigns, sales follow-up and lead scoring. The fix is custom rollup fields (Last_Task_Date__c, Last_Event_Date__c) on the Contact object, aggregating data from TaskRelation and EventRelation. A consolidated formula field (Last_Activity_Date_All__c) combines them into one accurate date for the last activity involving any associated contact. Replace LastActivityDate with Last_Activity_Date_All__c everywhere reporting and automation depends on contact engagement recency.

The "Last Activity Date" limitation with Shared Activities

The LastActivityDate field on the Contact object drives re-engagement campaigns, lead scoring and pipeline reviews for sales, marketing and operations teams. Turn on the "Allow Users to Relate Multiple Contacts to Tasks and Events" setting, better known as Shared Activities, and the field stops being reliable. It reflects activity for the primary contact on a Task or Event and ignores every secondary contact.

Why Shared Activities breaks Last Activity Date

Shared Activities lets one Task or Event record relate to several Contacts. The junction objects hold those relationships: TaskRelation for Tasks, EventRelation for Events. One contact is designated the primary through the WhoId field, and the rest are stored as secondary relations.

Salesforce only updates the standard LastActivityDate when the activity is linked to that primary contact. Secondary contacts on the same Task or Event never have theirs updated, which leaves the reporting built on the field incomplete.

Salesforce documents the limitation, and it shows up in reporting in a few ways:

  • Re-engagement campaigns: marketing might exclude contacts who were on a recent activity as a secondary contact.
  • Sales follow-up: reps might overlook contacts whose LastActivityDate looks stale and miss a timely follow-up.
  • Lead scoring: contacts may score lower because their engagement looks less recent than it is.
  • Account activity tracking: an account's overall engagement can be misreported when the recent activity involved secondary contacts.

To get around this, aggregate the activity dates from TaskRelation and EventRelation yourself so every associated contact is counted. That means custom rollup fields plus one formula field to consolidate them.

Step 1: create custom rollup date fields

Create two custom Date fields on the Contact object to hold the latest Task and Event dates.

  1. Last_Task_Date__c: the most recent activity date from Tasks associated with the Contact.
  2. Last_Event_Date__c: the most recent activity date from Events associated with the Contact.

Note: this example assumes a tool like Rollup Helper from the AppExchange for the rollups. The same principles work with Apex triggers or other automation if you prefer.

Rollup configuration for Last_Task_Date__c:

  • Rollup Object (Parent): Contact
  • Source Object (Child): TaskRelation
  • Rollup Type: MAX
  • Field to Aggregate: Task:ActivityDate
  • Filter Criteria: IsWhat = false AND Task:Status = 'Completed' (keeps it to completed tasks related to contacts)
  • Result Field: Last_Task_Date__c

Rollup configuration for Last_Event_Date__c:

  • Rollup Object (Parent): Contact
  • Source Object (Child): EventRelation
  • Rollup Type: MAX
  • Field to Aggregate: Event:ActivityDate (or Event:EndDateTime, depending on how you define event activity)
  • Filter Criteria: IsWhat = false AND Event:IsPast = true (keeps it to events related to contacts that have already happened)
  • Result Field: Last_Event_Date__c

A few things to watch with the rollups:

  • Recalculation: most rollup tools recalculate in real time or let you schedule it. Run a manual recalculation straight after setup to backfill existing records.
  • Testing: always test the configuration in a sandbox before it goes to production.
  • Permissions: check that Last_Task_Date__c and Last_Event_Date__c are visible to the profiles and permission sets that need them.

Step 2: consolidate with a formula field

With Last_Task_Date__c and Last_Event_Date__c populated, create a formula field that picks the most recent activity date across tasks and events.

  1. Create a new Formula field of type Date on the Contact object named Last_Activity_Date_All__c.

  2. Use the following formula:

    IF(
        AND(NOT(ISBLANK(Last_Task_Date__c)), NOT(ISBLANK(Last_Event_Date__c))),
        IF(Last_Task_Date__c >= Last_Event_Date__c,
            Last_Task_Date__c,
            Last_Event_Date__c
        ),
        IF(NOT(ISBLANK(Last_Task_Date__c)),
            Last_Task_Date__c,
            IF(NOT(ISBLANK(Last_Event_Date__c)),
                Last_Event_Date__c,
                NULL
            )
        )
    )
    

The formula returns the later of the two dates when both are populated, whichever one has a value when only one does, and NULL when neither does, which keeps a zero date off contacts with no recorded activity.

Step 3: report on the consolidated field

Swap the standard LastActivityDate for Last_Activity_Date_All__c in your reports, list views and dashboards, and the engagement picture starts matching what actually happened.

Example reports:

  • Contacts without recent activity: filter a Contact report where Last_Activity_Date_All__c is older than 30, 60 or 90 days.
  • Activity recency by owner: group a Contact report by OwnerId and add Last_Activity_Date_All__c with a MAX aggregation.
  • Account engagement overview: filter contacts by AccountId and sort by Last_Activity_Date_All__c in descending order.
  • Contacts never contacted: filter where Last_Activity_Date_All__c is blank.

Originally reported by salesforceben.com

Newsletter

One email every Tuesday

New guides, tool updates, and the release-note changes that break things.

No spam. Unsubscribe in one click.

Comments

Loading comments...

Leave a Comment