You need to sign in to do that
Don't have an account?
Mitch Morrison
Apex Trigger on Contact to Update Related Events
Hi all, I have had some trouble trying to create a trigger that updates a field on related event records whenever a specific contact field is updated. I tried using a map of lists, but have found that a can't add an Event type as the value of the list. I wanted to make this map to link all updating contacts to their related events. Then, I would have to find a way to loop through the map, updating the event field. Here is my code:
Trigger IncrementValue on Contact (before update) {
//Create a list of all updating contact IDs
List<Id> contactIds = new List<Id>();
for(Contact con : Trigger.new) {
contactIds.add(con.Id);
}
//Create a map of all updating contact IDs and their related event IDs
Map<Id, List<Event>> contactEvents = new Map<Id, List<Event>>();
for(Event ev : [SELECT WhoId FROM Event WHERE WhoId IN :contactIds]) {
if(contactEvents.containsKey(ev.WhoId)) {
List<Event> eventId = contactEvents.get(ev.WhoId);
eventId.add(ev.Activity_ID__c);
contactEvents.put(ev.WhoId, eventId);
}
else {
contactEvents.put(ev.WhoId, new List<Event> {ev.Activity_ID__c});
}
}
List<Id> eventUpdate = new List<Id>();
for(Contact con : Trigger.new) {
if(contactEvents.containsKey(con.Id)) {
return; //increment sales (Need help looping through map)
}
}
}
Here are my errors:
eventId.add(ev.Activity_ID__c); - Method does not exist or incorrect signature: void add(String) from the type List<Event>
contactEvents.put(ev.WhoId, new List<Event> {ev.Activity_ID__c}); -
Initial expression is of incorrect type, expected: Event but was: String
Thank you for any help and will obviously answer any questions if my explanation was confusing!
Trigger IncrementValue on Contact (before update) {
//Create a list of all updating contact IDs
List<Id> contactIds = new List<Id>();
for(Contact con : Trigger.new) {
contactIds.add(con.Id);
}
//Create a map of all updating contact IDs and their related event IDs
Map<Id, List<Event>> contactEvents = new Map<Id, List<Event>>();
for(Event ev : [SELECT WhoId FROM Event WHERE WhoId IN :contactIds]) {
if(contactEvents.containsKey(ev.WhoId)) {
List<Event> eventId = contactEvents.get(ev.WhoId);
eventId.add(ev.Activity_ID__c);
contactEvents.put(ev.WhoId, eventId);
}
else {
contactEvents.put(ev.WhoId, new List<Event> {ev.Activity_ID__c});
}
}
List<Id> eventUpdate = new List<Id>();
for(Contact con : Trigger.new) {
if(contactEvents.containsKey(con.Id)) {
return; //increment sales (Need help looping through map)
}
}
}
Here are my errors:
eventId.add(ev.Activity_ID__c); - Method does not exist or incorrect signature: void add(String) from the type List<Event>
contactEvents.put(ev.WhoId, new List<Event> {ev.Activity_ID__c}); -
Initial expression is of incorrect type, expected: Event but was: String
Thank you for any help and will obviously answer any questions if my explanation was confusing!
Trigger:
Test Class:
However I have some points that I want to raise in your trigger.
- The reason that you weren't seeing the changes in the Event.Sales__c because you are not updating the event list after making the changes and adding it to the list. That explains line 38 in the trigger.
- Like I mentioned before, on line 13 - that if condition will never be true. You need to do the appropriate to populate the contactEvents map. Only then it'll get into that IF block. I couldn't do it because am not fully aware of the requirement.
- The test coverage as I post this, is 85%.
- I added a fair number of assert statements, the more the better.
Hope it helps. :)All Answers
- I have gone through your problem and implemented it in my Org and your problem is that you are adding a string "ev.Activity_ID__c" to List of Event "eventId ".
I hope you find the above solution helpful. If it does, please mark as Best Answer to help others too.
Thanks and Regards,
Deepali Kulshrestha.
1. I believe the if statement should be true if there are multiple events for a contact. The else statement adds the key, value record to the map for the first event change for a contact. After that, the if statement should just append any other events to the list in the map tied to the contact.
2. Yep, that makes sense
3. It is just an auto number, but it looks like I don't need it anyway
Here's my new code. There are no problems, but my test didn't work. I got the error - 'Invalid field Contact for Event'.
Trigger IncrementSales on Contact (before update) {
//Create a list of all updating contact IDs
List<Id> contactIds = new List<Id>();
for(Contact con : Trigger.new) {
contactIds.add(con.Id);
}
//Create a map of all updating contact IDs and their related event IDs
Map<Id, List<Event>> contactEvents = new Map<Id, List<Event>>();
for(Event ev : [SELECT WhoId, Sales__c FROM Event WHERE WhoId IN :contactIds]) {
if(contactEvents.containsKey(ev.WhoId)) {
List<Event> eventId = contactEvents.get(ev.WhoId);
eventId.add(ev);
contactEvents.put(ev.WhoId, eventId);
}
else {
contactEvents.put(ev.WhoId, new List<Event> {ev});
}
}
for(Contact con : Trigger.new) {
Contact oldCon = Trigger.oldMap.get(con.Id);
if(con.Sales__c != oldCon.Sales__c) {
if(contactEvents.containsKey(con.Id)) {
List<Event> eventChanges = new List<Event>();
for(Event ev : contactEvents.get(con.Id)) {
eventChanges.add(ev);
}
for(Event ev : eventChanges) {
ev.Sales += (con.Sales__c - oldCon.Sales__c);
}
}
}
}
}
Thanks again!!
@isTest
Public class TestIncrementSales {
Static testMethod void updateContact() {
Contact contactNew = new Contact();
contactNew.FirstName = 'Dave';
contactNew.LastName = 'Matthews';
contactNew.Sales__c = 10;
insert contactNew;
Event eventNew = new Event();
eventNew.Subject = 'Meeting Test';
eventNew.WhoId = contactNew.Id;
eventNew.ActivityDate = system.today();
eventNew.StartDateTime =
datetime.newInstance(2019, 6, 28, 12, 00, 0);
eventNew.EndDateTime =
datetime.newInstance(2019, 6, 28, 14, 00, 0);
eventNew.Type = 'Meeting - Lunch';
eventNew.Sales__c = 0;
insert eventNew;
contactNew.SalesConnect__YTD_Sales_with_Partnerships__c = 15;
update contactNew;
}
}
The Sales__c for contact should be $15 after the test and the Sales__c for the event should be $5 (which is the change in sales for the contact)
Thanks!
Trigger:
Test Class:
However I have some points that I want to raise in your trigger.
- The reason that you weren't seeing the changes in the Event.Sales__c because you are not updating the event list after making the changes and adding it to the list. That explains line 38 in the trigger.
- Like I mentioned before, on line 13 - that if condition will never be true. You need to do the appropriate to populate the contactEvents map. Only then it'll get into that IF block. I couldn't do it because am not fully aware of the requirement.
- The test coverage as I post this, is 85%.
- I added a fair number of assert statements, the more the better.
Hope it helps. :)For every event that is tied to an updating contact:
add a key, value (contact ID, list<event>) pairing to the map
if the contact is already in the map, then add the next event to the list (this would happen for contacts tied to multiple events)
Hopefully, that makes more sense. I don't have much experience with using maps like this, so I really appreciate your help, Apuroop!
- Multiple changes contacts
- Multiple events attached to the contacts
This will hopefully prove that the map lists work properly and then we can push the trigger to our SF. I tried a test will multiple changes contacts but only one event attached to each. The test passed, but when I ran it in 'execute anonymous', I got this error: System.QueryException: List has more than 1 row for assignment to SObject
Here is the test (all I did was copy/paste your test for another contact & event):
public class TestIncrementSales {
public static testMethod void updateContact() {
//Contact 1
Contact contactNew = new Contact(FirstName = 'Dave', LastName = 'Matthews', Sales__c = 10); //Old Sales__c
insert contactNew;
Contact retCont = [SELECT Id, FirstName, LastName, Sales__c FROM Contact WHERE Id =:contactNew.Id];
System.assertEquals('Dave', retCont.FirstName);
System.assertEquals('Matthews', retCont.LastName);
System.assertEquals(10, retCont.Sales__c);
//Contact 2
Contact contactNew2 = new Contact(FirstName = 'Clay', LastName = 'Matthews', Sales__c = 100000); //Old Sales__c
insert contactNew2;
Contact retCont2 = [SELECT Id, FirstName, LastName, Sales__c FROM Contact WHERE Id =:contactNew2.Id];
System.assertEquals('Clay', retCont2.FirstName);
System.assertEquals('Matthews', retCont2.LastName);
System.assertEquals(100000, retCont2.Sales__c);
//Event 1
Event eventNew = new Event(Subject = 'Meeting Test', WhoId = contactNew.Id, ActivityDate = system.today(),
StartDateTime = datetime.newInstance(2019, 6, 28, 12, 00, 0),
EndDateTime = datetime.newInstance(2019, 6, 28, 14, 00, 0), Type = 'Meeting - Lunch', Sales__c = 0);
insert eventNew;
Event retEvent = [SELECT Id, Subject, WhoId, ActivityDate, Type, Sales__c FROM Event WHERE Id =: eventNew.Id];
System.assertEquals('Meeting Test', retEvent.Subject);
System.assertEquals(contactNew.Id, retEvent.WhoId);
System.assertEquals('Meeting - Lunch', retEvent.Type);
System.assertEquals(0, retEvent.Sales__c);
//Event 2
Event eventNew2 = new Event(Subject = 'Meeting Test 2', WhoId = contactNew.Id, ActivityDate = system.today(),
StartDateTime = datetime.newInstance(2018, 10, 28, 12, 00, 0),
EndDateTime = datetime.newInstance(2018, 10, 28, 14, 00, 0), Type = 'Meeting - Breakfast', Sales__c = 50000);
insert eventNew2;
Event retEvent2 = [SELECT Id, Subject, WhoId, ActivityDate, Type, Sales__c FROM Event WHERE Id =: eventNew2.Id];
System.assertEquals('Meeting Test 2', retEvent2.Subject);
System.assertEquals(contactNew.Id, retEvent2.WhoId);
System.assertEquals('Meeting - Breakfast', retEvent2.Type);
System.assertEquals(50000, retEvent2.Sales__c);
contactNew.Sales__c = 25; //New Sales__c
update contactNew;
contactNew2.Sales__c = 500000; //New Sales__c
update contactNew2;
retEvent = [SELECT Sales__c FROM Event WHERE WhoId =:contactNew.Id];
retEvent2 = [SELECT Sales__c FROM Event WHERE WhoId =:contactNew2.Id];
retCont = [SELECT Sales__c FROM Contact WHERE Id =:contactNew.Id];
retCont2 = [SELECT Sales__c FROM Contact WHERE Id =:contactNew2.Id];
System.assertEquals(15, retEvent.Sales__c);
System.assertEquals(450000, retEvent2.Sales__c);
System.assertEquals(25, retCont.Sales__c);
System.assertEquals(500000, retCont2.Sales__c);
}
}
Thanks for your help!!!
The error was that you are trying to query more than 1 record on line 52. In this test class, you are creating an Event1 for Contact1. Same goes with Event2 for Contact2, correct?
If the above statement is correct, you need to change the WhoId = contactNew.Id of eventNew2 to WhoId = contactNew2.Id. You are assigning the first contact to the second event. That's the reason it's pulling more than one record on line 52.
After this, change the assert statement on 42 - System.assertEquals(contactNew2.Id, retEvent2.WhoId);
So I want to be able to assign a contact to multiple events. If I do this, I'll get the same error as above. If there are multiple events connected to the contact, I want the trigger to update all of them. Is there just something I need to change in the test to handle multiple list rows for an object? Or do we need to rethink the trigger?
Thanks!!
Try this:
The test passed with flying colors! I think we are good to go. I really appreciate all of your help, I definitely couldn't have done this myself!
Mitch