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
SeanCenoSeanCeno 

First Completed Event Date?

All,

I have written code that finds the Last Completed Event Date, but I'm hoping someone can help me figure out how to add a method to find the First Completed Event Date. The trouble is I don't think there's a way to write a comparison argument to solve it as there is not standard date field in Salesforce that would represent the inception date of an org.

Here is my code thus far:
public class LastCompletedEventDate{
    //Grab list of contacts
    protected final Contact[] contactNewList = new Contact[] {};
    protected final Contact[] contactOldList = new Contact[] {};
    
    public LastCompletedEventDate(Contact[] contactOldList, Contact[] contactNewList) {
        this.contactNewList.addAll(contactNewList == null ? new Contact[] {} : contactNewList);
        this.contactOldList.addAll(contactOldList == null ? new Contact[] {} : contactOldList);
    }
    
    public void execute() {
        // Find all events associated to contacts
        Event[] eventList = [select ActivityDate, WhoId, EndDateTime, Subject, ActivityDateTime from Event where WhoId in :contactNewList];
        
        Map<Id, Contact> contactMap = new Map<Id, Contact>(contactNewList);
                for(Contact contact : contactNewList) {
                    contact.Last_Completed_Event__c = null;
        }
        
        //create if else statement to display most current completed event date
        for(Event event : eventList) {
            Contact contact = contactMap.get(event.WhoId);
            if(Contact == null)
                continue;
            if(Event.EndDateTime < Date.Today())
                Contact.Last_Completed_Event__c = Event.EndDateTime;
        }
    }
}
 
trigger LastCompletedEventDate on Contact (before update) {
    if(Contact_RollupTrades.inprog != true) {
        Set<ID> sID = new Set<ID>(trigger.newMap.keySet());
        new LastCompletedEventDate(trigger.old, trigger.new).execute();
    }
}
Best Answer chosen by SeanCeno
Pankaj_GanwaniPankaj_Ganwani
Hi Sean,

Sorry for late reply. I totally missed it to see that you have trigger on before update not on after. In this case, we cannot perform dml operation on the same object since the object records we are going to perfom operations on, are read only. So, what you can do something like this:

public void execute2() 
{
    Map<Id, Contact> mapConIdToFirstDate = new Map<Id, Contact>(contactNewList);
    for(Event objEvent: [select WhoId, EndDateTime, Subject, ActivityDateTime, ActivityDate from Event where WhoId in :contactNewList AND isDeleted = False AND WhoId!=NULL AND EndDateTime < TODAY ORDER BY EndDateTime DESC All Rows])
    {
        if(mapConIdToFirstDate.containskey(objEvent.WhoId))
              mapConIdToFirstDate.get(objEvent.WhoId).First_Completed_Event__c = objEvent.ActivityDate;
    }        
}

Can you please try with above mentioned code?

 

All Answers

Pankaj_GanwaniPankaj_Ganwani
Hi Sean,

You can use below mentioned code to find the first event date which is completed corresponding to the Contact:
Map<Id, Date> mapConIdToFirstDate = new Map<Id,Date>();
for(Event objEvent: [select ActivityDate, WhoId, EndDateTime, Subject, ActivityDateTime, ActivityDate from Event where WhoId in :contactNewList AND Status=:'Completed' AND WhoId!=NULL ORDER BY DESC])
{
      mapConIdToFirstDate.put(objEvent.WhoId, objEvent.ActivityDate);
}

system.debug('===================='+mapConIdToFirstDate);

What I have done is simply sorted the list in descending order and then storing it in a map. At last the map will contain the mapping of contact id and its corresponding first completed event date.
SeanCenoSeanCeno
Hey Pankaj,

Thanks for the response. A couple of things. There isn't a Status field in Events, only in Tasks so I think using a comparison argument such as
if(Event.EndDateTime < Date.Today()) is needed. I also added the field EndDateTime to the Order By claus as it was missing. I like the idea of using the Order By, but is there a way to select the first one in the list? Then the next step I believe would be to get it to resolve into my custom field First_Completed_Event__c. Here is my code thus far. The new code starts in the method 'execute2':
 
public class LastCompletedEventDate{
    //Grab list of contacts
    protected final Contact[] contactNewList = new Contact[] {};
    protected final Contact[] contactOldList = new Contact[] {};
    
    public LastCompletedEventDate(Contact[] contactOldList, Contact[] contactNewList) {
        this.contactNewList.addAll(contactNewList == null ? new Contact[] {} : contactNewList);
        this.contactOldList.addAll(contactOldList == null ? new Contact[] {} : contactOldList);
    }
    
    public void execute() {
        // Find all events associated to contacts
        Event[] eventList = [select ActivityDate, WhoId, EndDateTime, Subject, ActivityDateTime from Event where WhoId in :contactNewList];
        
        Map<Id, Contact> contactMap = new Map<Id, Contact>(contactNewList);
                for(Contact contact : contactNewList) {
                    contact.Last_Completed_Event__c = null;
        }
        
        //create if else statement to display most current completed event date
        for(Event event : eventList) {
            Contact contact = contactMap.get(event.WhoId);
            if(Contact == null)
                continue;
            if(Event.EndDateTime < Date.Today())
                Contact.Last_Completed_Event__c = Event.EndDateTime;
        }
    }
    
    public void execute2() {
        Map<Id, Date> mapConIdToFirstDate = new Map<Id,Date>();
        for(Event objEvent: [select ActivityDate, WhoId, EndDateTime, Subject, ActivityDateTime from Event where WhoId in :contactNewList AND WhoId!=NULL ORDER BY EndDateTime DESC]) {
            mapConIdToFirstDate.put(objEvent.WhoId, objEvent.ActivityDate);
        }
        system.debug('===================='+mapConIdToFirstDate);
    }
}

Would it be possible to use the Order By claus in the first method and then just add another if statement to the for loop such as:
if(Event.EndDateTime < Date.Today() && Event.EndDateTime [0] //is the first in the desc list?)
contact.First_Completed_Event__c = Event.EndDateTime;

 
Pankaj_GanwaniPankaj_Ganwani
Hi,

You can directly put filter in SOQL to pull only those event records where end date is less than today. Please refer below SOQL:

Map<Id, Date> mapConIdToFirstDate = new Map<Id,Date>();
for(Event objEvent: [select WhoId, EndDateTime, Subject, ActivityDateTime, ActivityDate from Event where WhoId in :contactNewList AND WhoId!=NULL AND EndDateTime < TODAY ORDER BY EndDateTime DESC])
{
      mapConIdToFirstDate.put(objEvent.WhoId, objEvent.ActivityDate);
}

system.debug('===================='+mapConIdToFirstDate);
SeanCenoSeanCeno
In the debug log, it is returning the correct value. Now how do I get this result to resolve into my First_Completed_Event__c field on the Contact page?
 
16:41:01:882 USER_DEBUG [37]|DEBUG|===================={003L000000QeRdjIAF=2015-03-09 00:00:00}

 
Pankaj_GanwaniPankaj_Ganwani
Hi,

You can update the contact's First_Completed_Event__c field with this Activitydate value.

Map<Id, Date> mapConIdToFirstDate = new Map<Id,Date>();
for(Event objEvent: [select WhoId, EndDateTime, Subject, ActivityDateTime, ActivityDate from Event where WhoId in :contactNewList AND WhoId!=NULL AND EndDateTime < TODAY ORDER BY EndDateTime DESC])
{
      mapConIdToFirstDate.put(objEvent.WhoId, new Contact(Id = WhoId, First_Completed_Event__c =objEvent.ActivityDate));
}
update mapConIdToFirstDate.values();
SeanCenoSeanCeno
I found an error while updating the field:
 
Error: Invalid Data. 
Review all error messages below to correct your data.
Apex trigger LastCompletedEventDate caused an unexpected exception, contact your administrator: LastCompletedEventDate: execution of BeforeUpdate caused by: System.DmlException: Update failed. First exception on row 0 with id 003L000000QeRdjIAF; first error: SELF_REFERENCE_FROM_TRIGGER, Object (id = 003L000000QeRdj) is currently in trigger LastCompletedEventDate, therefore it cannot recursively update itself: []: Class.LastCompletedEventDate.execute2: line 35, column 1

I've been trying to edit my trigger but I'm getting an:
 
Line 7: Incompatible key type Schema.SObjectField for Map<Id,Contact>

Here is my trigger code:
trigger LastCompletedEventDate on Contact (before update) {
    if(Contact_RollupTrades.inprog != true) {
        Set<ID> sID = new Set<ID>(trigger.newMap.keySet());
        new LastCompletedEventDate(trigger.old, trigger.new).execute();
    }
    if(trigger.newMap.get(Event.WhoId) != null){
        trigger.newMap.get(Event.WhoId).First_Completed_Event__c = Event.EndDateTime;
    } else {
        mapConIdToFirstDate.put(Event.WhoId, new Contact(Id = Event.WhoId, First_Completed_Event__c = Event.EndDateTime));
    }
}

Class:
public class LastCompletedEventDate{
    //Grab list of contacts
    protected final Contact[] contactNewList = new Contact[] {};
    protected final Contact[] contactOldList = new Contact[] {};
    
    public LastCompletedEventDate(Contact[] contactOldList, Contact[] contactNewList) {
        this.contactNewList.addAll(contactNewList == null ? new Contact[] {} : contactNewList);
        this.contactOldList.addAll(contactOldList == null ? new Contact[] {} : contactOldList);
    }
    
    public void execute() {
        // Find all events associated to contacts
        Event[] eventList = [select ActivityDate, WhoId, EndDateTime, Subject, ActivityDateTime from Event where WhoId in :contactNewList];
        
        Map<Id, Contact> contactMap = new Map<Id, Contact>(contactNewList);
                for(Contact contact : contactNewList) {
                    contact.Last_Completed_Event__c = null;
        }
        
        //create if else statement to display most current completed event date
        for(Event event : eventList) {
            Contact contact = contactMap.get(event.WhoId);
            if(Contact == null)
                continue;
            if(Event.EndDateTime < Date.Today())
                Contact.Last_Completed_Event__c = Event.EndDateTime;
        }
    }
    
    public void execute2() {
        Map<Id, Contact> mapConIdToFirstDate = new Map<Id, Contact>();
        for(Event objEvent: [select WhoId, EndDateTime, Subject, ActivityDateTime, ActivityDate from Event where WhoId in :contactNewList AND isDeleted = False AND WhoId!=NULL AND EndDateTime < TODAY ORDER BY EndDateTime DESC All Rows]){
            mapConIdToFirstDate.put(objEvent.WhoId, new Contact(Id = objEvent.WhoId, First_Completed_Event__c = objEvent.EndDateTime));
        }
        update mapConIdToFirstDate.values();
        system.debug('===================='+mapConIdToFirstDate);
    }
}
Pankaj_GanwaniPankaj_Ganwani
Hi Sean,

Sorry for late reply. I totally missed it to see that you have trigger on before update not on after. In this case, we cannot perform dml operation on the same object since the object records we are going to perfom operations on, are read only. So, what you can do something like this:

public void execute2() 
{
    Map<Id, Contact> mapConIdToFirstDate = new Map<Id, Contact>(contactNewList);
    for(Event objEvent: [select WhoId, EndDateTime, Subject, ActivityDateTime, ActivityDate from Event where WhoId in :contactNewList AND isDeleted = False AND WhoId!=NULL AND EndDateTime < TODAY ORDER BY EndDateTime DESC All Rows])
    {
        if(mapConIdToFirstDate.containskey(objEvent.WhoId))
              mapConIdToFirstDate.get(objEvent.WhoId).First_Completed_Event__c = objEvent.ActivityDate;
    }        
}

Can you please try with above mentioned code?

 
This was selected as the best answer
SeanCenoSeanCeno
Boom. Seems to be working perfectly. Thanks again!