0

Hoping to avail myself of the great minds in this group...

I am creating code that syncs two custom fields. One of these fields is on Opportunity and the other is on Contact.

The sync will happen by way of an OpportunityContactRole record, such that the custom field on a given Opportunity record will have its value set to the value of the field on the Contact for whichever of the Opportunity's OpportunityContactRole records is flagged with IsPrimary.

I can accomplish this in a fairly straightforward way, for example, like:

List<OpportunityContactRole> contactRoles = new List<OpportunityContactRole>();
List<Opportunity> opportunitiesToUpdate = new List<Opportunity>();

contactRoles = ([
    SELECT 
        OpportunityId, 
        Contact.Custom_Field__c, 
        IsPrimary 
            FROM 
            OpportunityContactRole
                WHERE 
                Condition 1 AND Condition 2]);

for(OpportunityContactRole ocr : contactRoles) {
    Opportunity opportunity = new Opportunity(Id = ocr.OpportunityId);
    if(ocr.IsPrimary == true) {
        opportunity.Custom_Field__c = ocr.Contact.Custom_Field__c;
        opportunitiesToUpdate.add(opportunity);
    }
}

update opportunitiesToUpdate;

The problem I have been struggling with for two days, however, is the additional requirement I've been given, which states:

  • In the event no OpportunityContactRole record for a given opportunity has been designated as IsPrimary, then the OpportunityContactRole record that was created first should be used as if it were IsPrimary.

I have no idea how to do this. Can anyone get me started in the right direction? Do I need to build some sort of helper to process time between System.now() and a record's CreatedDate?

Maybe the answer is right in front of me, but I have done a lot of searching and am still stuck. Please let me know if I can provide any more background or other information, or if I can ask this question in a better way.

Thanks for reading!

  • 3
    Why not just ORDER BY CreatedDate? Then the first element is the earliest one. – David Reed Dec 11 '19 at 1:58
  • Thanks @DavidReed! What is the best way to go about processing the non-IsPrimaryrecords? Given the query result that I'm iterating over from my example above (contactRoles) and the 1:M relationship between Opportunity and OpportunityContactRole, if, say, half of the records were IsPrimary, I'd now have to avoid updating the Opportunity records that other non-IsPrimary child records may share as a parent. – NewberrySummer Dec 11 '19 at 10:25
2

As David has suggested, ordering by CreatedDate should be the key to solving this issue. Once you've done that there are multiple ways to solve the problem but this is the clearest one for me:

List<OpportunityContactRole> contactRoles = new List<OpportunityContactRole>();
List<Opportunity> opportunitiesToUpdate = new List<Opportunity>();
Set<Id> opportunitiesWithPriorityContact = new Set<Id>();
Set<Id> updatedOpportunities = new Set<Id>();

// Order by CreatedDate so the first in the list is the first created
contactRoles = ([
    SELECT OpportunityId, 
           Contact.Custom_Field__c, 
           IsPrimary 
    FROM OpportunityContactRole
    WHERE Condition 1 AND 
          Condition 2
    ORDER BY CreatedDate ASC
]);

for(OpportunityContactRole ocr : contactRoles) {
    if(ocr.IsPrimary == true) {
        opportunitiesWithPriorityContact.add(ocr.OpportunityId);
    }
}

for(OpportunityContactRole ocr : contactRoles) {
    Opportunity opportunity = new Opportunity(Id = ocr.OpportunityId);
    Boolean validRecord = false;

    // only update if it hasn't been updated before
    if(!updatedOpportunities.contains(ocr.OpportunityId)) {
        // search for primary only if the opportunity has at least one
        if(opportunitiesWithPriorityContact.contains(ocr.OpportunityId)) {
            if(ocr.IsPrimary == true) {
                validRecord = true;
            }
        // if there is no primary then use the first it finds in the loop
        } else {
            validRecord = true;
        }
    }

    // using a boolean to prevent duplicate code
    if(validRecord) {
        opportunity.Custom_Field__c = ocr.Contact.Custom_Field__c;
        opportunitiesToUpdate.add(opportunity);
        updatedOpportunities.add(ocr.OpportunityId);
    }
}

update opportunitiesToUpdate;

Let me know how you get on.

Edit: changed from using list.contains opportunitiesToUpdate to using a seperate set

  • 1
    +1, nice solution. I think you're going to run into trouble with List.contains(), though. At the point where you add the value to opportunitiesToUpdate, it has the custom field set, but at the point where you check membership, it does not, which will produce incorrect results. I think you'll want a Map<Id, Opportunity> instead. – David Reed Dec 11 '19 at 15:47
  • @David, Yes, good point, my first draft used an additional set but thought I could remove. I'll update to include it back in. – will-bowditch Dec 11 '19 at 21:05
  • I was able to get this implemented and working perfectly. I deployed last night. Thank you, to both yourself and @DavidReed. I will update the question to include this additional requirement of ensuring opportunities with IsPrimary records aren't overwritten with values from other sibling, non-IsPrimary records. Cheers! – NewberrySummer Dec 17 '19 at 14:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.