You need to sign in to do that
Don't have an account?
Mitch Morrison
Need help with Contact/Event trigger
I am trying to update a custom event field, sales_within_1_year__c, when a custom contact field, sales__c, changes. For example:
Event 1: tied to Contact A & Contact B; sales_within_1_year = $0
Event 2: tied to Contact A & Contact C; sales_within_1_year = $10
Contact A sales changes from $100 to $200.
Event 1: new sales_within_1_year = $100
Event 2: new sales_within_1_year = $110
If the sales for Contact B or C changed, it would only affect the event they are tied to.
I thought my trigger would work, and it does when I change a contact's sales number individually. But when I bulk update contacts, none of the sales_within_1_year numbers change. I was wondering if someone could help me through this. Thanks!
Here is my trigger:
trigger EventSales on Contact (before update) {
//Create a list of contact IDs that have updating sales
List<Id> contactIds = new List<Id>();
for(Contact con : trigger.new) {
if(Trigger.newMap.get(con.Id).Sales__c == NULL) return;
else if(trigger.newMap.get(con.Id).Sales__c == 0) return;
else if(trigger.oldMap.get(con.Id).Sales__c == NULL) {
contactIds.add(con.Id);
}
else if(trigger.newMap.get(con.Id).Sales__c !=
trigger.oldMap.get(con.Id).Sales__c) {
contactIds.add(con.Id);
}
}
//Create a list of event relations (contact-event pairs) for updating contacts
List<EventRelation> evRel = [SELECT EventId, RelationId
FROM EventRelation
WHERE RelationId IN :contactIds];
//Create a map of the updating contacts and their related events
Map<Id, List<Id>> contactEvents = new Map<Id, List<Id>>();
//Create a list of event Ids that are going to be updated
List<Id> eventIds = new List<Id>();
//Put the contact Id and event Id in the map for events that have updating contacts
for(EventRelation rel :evRel) {
if(contactEvents.containsKey(rel.RelationId)) {//If the contact is already in the map
List<Id> relatedEv = contactEvents.get(rel.RelationId);//Grab the list of events tied to the contact
relatedEv.add(rel.EventId);//Add the new event to the list
contactEvents.put(rel.RelationId, relatedEv);//Put the updated list into the map
}
else {//If the contact isn't in the map
contactEvents.put(rel.RelationId, new List<Id>{rel.EventId});//Put the contact and event into the map
}
//Add the updating event to the eventIds list
if(eventIds.contains(rel.EventId)) return;
else eventIds.add(rel.EventId);
}
//Create a list of events that are going to be updated
List<Event> listEventsMaker = [SELECT Id, Sales_within_1_year__c
FROM Event
WHERE Id IN :eventIds
AND EndDateTime = LAST_N_DAYS:365];
List<Event> listEvents = new List<Event>();
for(Event ev :listEventsMaker) {
if(listEvents.contains(ev)) return;
else listEvents.add(ev);
}
//Keep a list of events to update
List<Event> changedEvents = new List<Event>();
//Update the sales amounts for the events
for(Id con :contactEvents.keySet()) {//For each contact in the map
List<Id> thisContact = new List<Id>();//Create a list of events tied to this specific contact
Contact oldCon = trigger.oldMap.get(con); //create a record for the contact pre-update
Contact newCon = trigger.newMap.get(con); //create a record for the contact post-update
if(newCon.Sales__c == NULL) return;
else if(newCon.Sales__c == 0) return;
else if(oldCon.Sales__c == NULL) {
for(Id event :contactEvents.get(con)) {
thisContact.add(event);
}
for(Event ev :listEventsMaker) {
if(thisContact.contains(ev.Id)) {
changedEvents.add(ev);
if(ev.Sales_within_1_Year__c == NULL) {
ev.Sales_within_1_Year__c = newCon.Sales__c;
}
else ev.Sales_within_1_Year__c += newCon.Sales__c;
}
}
}
else if(oldCon.Sales__c
!= newCon.Sales__c) {
for(Id event :contactEvents.get(con)) {
thisContact.add(event);
}
for(Event ev :listEventsMaker) {
if(thisContact.contains(ev.Id)) {
changedEvents.add(ev);
if(ev.Sales_within_1_Year__c == NULL) {
ev.Sales_within_1_year__c = (newCon.Sales__c
- oldCon.Sales__c);
}
else ev.Sales_within_1_Year__c += (newCon.Sales__c
- oldCon.Sales__c);
}
}
}
}
update changedEvents;
}
Event 1: tied to Contact A & Contact B; sales_within_1_year = $0
Event 2: tied to Contact A & Contact C; sales_within_1_year = $10
Contact A sales changes from $100 to $200.
Event 1: new sales_within_1_year = $100
Event 2: new sales_within_1_year = $110
If the sales for Contact B or C changed, it would only affect the event they are tied to.
I thought my trigger would work, and it does when I change a contact's sales number individually. But when I bulk update contacts, none of the sales_within_1_year numbers change. I was wondering if someone could help me through this. Thanks!
Here is my trigger:
trigger EventSales on Contact (before update) {
//Create a list of contact IDs that have updating sales
List<Id> contactIds = new List<Id>();
for(Contact con : trigger.new) {
if(Trigger.newMap.get(con.Id).Sales__c == NULL) return;
else if(trigger.newMap.get(con.Id).Sales__c == 0) return;
else if(trigger.oldMap.get(con.Id).Sales__c == NULL) {
contactIds.add(con.Id);
}
else if(trigger.newMap.get(con.Id).Sales__c !=
trigger.oldMap.get(con.Id).Sales__c) {
contactIds.add(con.Id);
}
}
//Create a list of event relations (contact-event pairs) for updating contacts
List<EventRelation> evRel = [SELECT EventId, RelationId
FROM EventRelation
WHERE RelationId IN :contactIds];
//Create a map of the updating contacts and their related events
Map<Id, List<Id>> contactEvents = new Map<Id, List<Id>>();
//Create a list of event Ids that are going to be updated
List<Id> eventIds = new List<Id>();
//Put the contact Id and event Id in the map for events that have updating contacts
for(EventRelation rel :evRel) {
if(contactEvents.containsKey(rel.RelationId)) {//If the contact is already in the map
List<Id> relatedEv = contactEvents.get(rel.RelationId);//Grab the list of events tied to the contact
relatedEv.add(rel.EventId);//Add the new event to the list
contactEvents.put(rel.RelationId, relatedEv);//Put the updated list into the map
}
else {//If the contact isn't in the map
contactEvents.put(rel.RelationId, new List<Id>{rel.EventId});//Put the contact and event into the map
}
//Add the updating event to the eventIds list
if(eventIds.contains(rel.EventId)) return;
else eventIds.add(rel.EventId);
}
//Create a list of events that are going to be updated
List<Event> listEventsMaker = [SELECT Id, Sales_within_1_year__c
FROM Event
WHERE Id IN :eventIds
AND EndDateTime = LAST_N_DAYS:365];
List<Event> listEvents = new List<Event>();
for(Event ev :listEventsMaker) {
if(listEvents.contains(ev)) return;
else listEvents.add(ev);
}
//Keep a list of events to update
List<Event> changedEvents = new List<Event>();
//Update the sales amounts for the events
for(Id con :contactEvents.keySet()) {//For each contact in the map
List<Id> thisContact = new List<Id>();//Create a list of events tied to this specific contact
Contact oldCon = trigger.oldMap.get(con); //create a record for the contact pre-update
Contact newCon = trigger.newMap.get(con); //create a record for the contact post-update
if(newCon.Sales__c == NULL) return;
else if(newCon.Sales__c == 0) return;
else if(oldCon.Sales__c == NULL) {
for(Id event :contactEvents.get(con)) {
thisContact.add(event);
}
for(Event ev :listEventsMaker) {
if(thisContact.contains(ev.Id)) {
changedEvents.add(ev);
if(ev.Sales_within_1_Year__c == NULL) {
ev.Sales_within_1_Year__c = newCon.Sales__c;
}
else ev.Sales_within_1_Year__c += newCon.Sales__c;
}
}
}
else if(oldCon.Sales__c
!= newCon.Sales__c) {
for(Id event :contactEvents.get(con)) {
thisContact.add(event);
}
for(Event ev :listEventsMaker) {
if(thisContact.contains(ev.Id)) {
changedEvents.add(ev);
if(ev.Sales_within_1_Year__c == NULL) {
ev.Sales_within_1_year__c = (newCon.Sales__c
- oldCon.Sales__c);
}
else ev.Sales_within_1_Year__c += (newCon.Sales__c
- oldCon.Sales__c);
}
}
}
}
update changedEvents;
}
I am concerned that you are updating this value on Events and not in some other manner. My primary concern is that Event records are archived after 1 year so you lose visibility to them after 365 days. My secondary concern is that any given Org may have many Event records and you will have to update multiple Events each time a change is made to Contact. Since I do not know your business requirements I cannot speak to whether another solution is better suited.
If, after taking my previous two points into consideration, you decide that it still makes sense to do this calculation and apply the values across multiple Event records then I highly recommend that you modify the logic in a way that allows you to process the Updates to Event records asynchronously. This will reduce some of the screen lag time that may happen for Users that have many Event records linked to a COntact that they are updating in the interface. I also recommend that you create a batch job to run daily and re-calculate the Sales_within_1_Year__c values. This is because "things" happen from time-to-time that may cause your trigger logic to fail. Eecuting a scheduled batch job will ensure that your data remains up-to-date.
-greg
All Answers
I am concerned that you are updating this value on Events and not in some other manner. My primary concern is that Event records are archived after 1 year so you lose visibility to them after 365 days. My secondary concern is that any given Org may have many Event records and you will have to update multiple Events each time a change is made to Contact. Since I do not know your business requirements I cannot speak to whether another solution is better suited.
If, after taking my previous two points into consideration, you decide that it still makes sense to do this calculation and apply the values across multiple Event records then I highly recommend that you modify the logic in a way that allows you to process the Updates to Event records asynchronously. This will reduce some of the screen lag time that may happen for Users that have many Event records linked to a COntact that they are updating in the interface. I also recommend that you create a batch job to run daily and re-calculate the Sales_within_1_Year__c values. This is because "things" happen from time-to-time that may cause your trigger logic to fail. Eecuting a scheduled batch job will ensure that your data remains up-to-date.
-greg
The values for sales__c on the contacts get imported from an external database every morning, and I assume this is done in batches (I will have to check), So no users are updating any fields individually. If you still think we should modify the logic asynchronously, how would I go about doing that?
Thanks for you help!
-greg
I really don't have any explanation or ideas on what is happening, but hopefully it can be fixed.