I have junction object ProjectxOpp__c
which links Projects (MPM4_BASE__Milestone1_Project__c
) with Opportunities. It's a many to many relationship so the same Opportunity can be linked to multiple Projects.
When I update my Project record's MPM4_BASE__Deadline__c
field, I also want to update Opportunities which are linked to that Project by an ProjectxOpp__c
junction record.
If I update a batch of Projects & as a result, update a batch of related Opportunities, it's possible that I end up trying to update the same Opportunity twice. This causes a System.ListException
Duplicate id in list
I've seen several solutions on the site for dealing with this issue but they either suggest rewriting an earlier section of the code, to avoid picking up the same record twice or using a Map to store the Ids & I can't see how to apply the latter solution in my code.
Neither explain how to handle duplicates that you're expecting..
trigger WE_IMProjCmpltnUp on MPM4_BASE__Milestone1_Project__c (after insert, after update) {
Map<String, Schema.RecordTypeInfo> RT = MPM4_BASE__Milestone1_Project__c.SObjectType.getDescribe().getRecordTypeInfosByName();
List<VRTN__c> weRTs = VRTN__c.getall().values();
List<String> weRTNames = new List<String>();
Set<Id> validRecordTypeIds = new Set<Id>();
Set<Id> projects = new Set<Id>();
Map<Id,Date> deadlineDates = new Map<Id,Date>();
List<Opportunity> updOpps = new List<Opportunity>();
for(VRTN__c weRT : weRTs) {
try {
weRTNames.add(weRT.NAEUProjs__c);
} catch (System.StringException e) {
System.debug(System.LoggingLevel.ERROR,'Invalid Record Type Name ' + weRT.NAEUOpps__c);
}
}
for(String weRTN : weRTNames) {
if(RT.get(weRTN) != null){
validRecordTypeIds.add(RT.get(weRTN).getRecordTypeId());
}
}
If(Trigger.isInsert){
for(MPM4_BASE__Milestone1_Project__c p : Trigger.new){
if(validRecordTypeIds.contains(p.RecordTypeId) && p.MPM4_BASE__Deadline__c != null)
{
projects.add(p.Id);
deadlineDates.put(p.Id, p.MPM4_BASE__Deadline__c);
}
}
}
If(Trigger.isUpdate){
for(MPM4_BASE__Milestone1_Project__c p : Trigger.new){
if(validRecordTypeIds.contains(p.RecordTypeId) && p.MPM4_BASE__Deadline__c != null)
{
MPM4_BASE__Milestone1_Project__c oldP = Trigger.oldMap.get(p.Id);
if(oldP.MPM4_BASE__Deadline__c != p.MPM4_BASE__Deadline__c){
projects.add(p.Id);
deadlineDates.put(p.Id, p.MPM4_BASE__Deadline__c);
}
}
}
}
If(projects.size() > 0){
for(ProjectxOpp__c junc : [SELECT Project__c, Opportunity__r.Implementation_Revenue__c FROM ProjectxOpp__c
WHERE Project__c In :projects])
{
Opportunity o = junc.Opportunity__r;
Date newCompDate = deadlineDates.get(junc.Project__c);
if(newCompDate > o.Implementation_Revenue__c){
o.Implementation_Revenue__c = newCompDate;
}
updOpps.add(o);
}
update updOpps;
}
}
Best Answer
In your case you can have multiple
MPM4_BASE__Milestone1_Project__c
objects related to anOpportunity
and your aim is to record the latestMPM4_BASE__Deadline__c
date from any of them.Here is one way to do that:
The aim is to update each
Opportunity
once only: that can be done by using a map (calledm
here) keyed by the ID so there is only ever one instance ofOpportunity
kept even though many duplicates may be returned by the query. The map can be populated from your existing query: if there isn't an entry in the map it is added.I've also added a second map (called
updates
) because only some of theOpportunity
objects will need to updated.