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
QD93QD93 

Apex Class and Trigger: Automatically create Opportunity Contact Roles with associated Contacts to Opportunity from Custom object

Hi,

I am looking for an Apex Class + Trigger to --> when I create an opportunity from a custom object record (very much like an account) with associated contacts, I'd like to automatically create X number of contact roles per contacts related to the custom object record and tie those Opportunities to the newly created Opportunity.

The account custom obejct record works very similar like the relationship for Account -- Opportunity -- Contact. 

I'd love to have the code also to prevent Duplicate Contact Roles (based on the contact Id) in the Opportunity Contact Role. Provided below is the Apex Trigger I have so far. The problem is the Trigger doesn't prevent duplicate Opportunity contact roles from being created, and I dont have an Apex class for it.

AccountSpecific__c --> custom object record
Accountspecificid__c --> custom field on the Contact
AccountSpecificOppId__c --> custom field on the Opportunity
trigger createOpportunityContactRoles on Opportunity (after insert) {

    Set<Id> accSpecificIds = new Set<Id>();
    List<OpportunityContactRole> ocrList = new List<OpportunityContactRole>();
    Map<Id, List<Contact>> accountSpecificContacts = new Map<Id, List<Contact>>();
    
    for(Opportunity o: Trigger.New) {
        if(o.AccountSpecificOppId__c != Null) 
            accSpecificIds.add(o.AccountSpecificOppId__c);
    }
    
    for(Contact con: [select id, AccountSpecificId__c from Contact 
                    where AccountSpecificId__c in: accSpecificIds]) {
        if(!accountSpecificContacts.containsKey(con.AccountSpecificId__c)) 
            accountSpecificContacts.put(con.AccountSpecificId__c, new List<Contact>());
        accountSpecificContacts.get(con.AccountSpecificId__c).add(con);
    }
    
    for(Opportunity opp: Trigger.New) {
        if(accountSpecificContacts.containskey(opp.AccountSpecificOppId__c) 
            && accountSpecificContacts.get(opp.AccountSpecificOppId__c) != NULL) {
            Boolean isFirstContact = true;
            for(Contact c: accountSpecificContacts.get(opp.AccountSpecificOppId__c)) {
                OpportunityContactRole ocr = new OpportunityContactRole(ContactId = c.Id, 
                                                                        OpportunityId = opp.id);
                if(isFirstContact) {
                    ocr.IsPrimary = true;
                    isFirstContact = false;
                }
                ocrList.add(ocr);
            }
        }           
    }
    
    if(ocrList.size() > 0)
        insert ocrList;
}

 
Raj R.Raj R.
Try
Map<Id, Set<Id>> ocrMap = new Map<Id, Set<Id>>();

for(Opportunity opp: Trigger.New) {
        if(accountSpecificContacts.containskey(opp.AccountSpecificOppId__c) 
            && accountSpecificContacts.get(opp.AccountSpecificOppId__c) != NULL) {
            Boolean isFirstContact = true;
            for(Contact c: accountSpecificContacts.get(opp.AccountSpecificOppId__c)) {
               Set<id> contSet = null;
                if(ocrMap.containsKey(opp.id) {
                    contSet = ocrMap.get(opp.Id);
                }
                else {
                    contSet = new Set<id>();
                }
                // using set, the contact ID will be adding if it not present.
                contSet.add(c.Id);
                ocrMap.put(opp.Id, contSet);
            }
        }          
}

for(Id oppId : ocrMap.keySet()) {
  Set<id> contIdSet = ocrMap.get(oppId);
  for(Id contId : contIdSet) {
     OpportunityContactRole ocr = new OpportunityContactRole(ContactId = contId, 
                                                                        OpportunityId = oppId);
    ocrList.add(ocr);
  }
}

if(ocrList.size() > 0 {
   insert ocrList;
}

 
QD93QD93
Hi Raj,

Thanks for the code. Unfortunately, same issue is still occurrig, where I am still able to create duplicate Contact Roles if the Opportunity is created on the Contacted record instead of the custom object (account) record.... Please note the same contact role record 'Portfolio 1' were created and rleated to this opportunity record... 


User-added image
Raj R.Raj R.
QD93, 

Can you open the Developer Console and the Logs tab while performing the action? What is triggered? Workflow rules? Process Builders? 
QD93QD93
Hi Raj,

When I create an Opportunity record from the account cusotm object record's related contact record, the Apex trigger fires and creates the Duplicate Opportunity Contact Role. Here is what is in my Debug Log: 

User-added image

When I create the Opportunity from the account custom object record, No dupe is created. Here is my Debug Log:

User-added image

Please advise!

Below is my current Apex Trigger code:
 
trigger createOpportunityContactRoles on Opportunity (after insert) {

    Set<Id> accSpecificIds = new Set<Id>();
    List<OpportunityContactRole> ocrList = new List<OpportunityContactRole>();
    Map<Id, List<Contact>> accountSpecificContacts = new Map<Id, List<Contact>>();
    Map<Id, Set<Id>> ocrMap = new Map<Id, Set<Id>>();

    
    for(Opportunity o: Trigger.New) {
        if(o.AccountSpecificOppId__c != Null) 
            accSpecificIds.add(o.AccountSpecificOppId__c);
    }
    
    for(Contact con: [select id, AccountSpecificId__c from Contact 
                    where AccountSpecificId__c in: accSpecificIds]) {
        if(!accountSpecificContacts.containsKey(con.AccountSpecificId__c)) 
            accountSpecificContacts.put(con.AccountSpecificId__c, new List<Contact>());
        accountSpecificContacts.get(con.AccountSpecificId__c).add(con);
    }

for(Opportunity opp: Trigger.New) {
        if(accountSpecificContacts.containskey(opp.AccountSpecificOppId__c) 
            && accountSpecificContacts.get(opp.AccountSpecificOppId__c) != NULL) {
            Boolean isFirstContact = true;
            for(Contact c: accountSpecificContacts.get(opp.AccountSpecificOppId__c)) {
               Set<id> contSet = null;
                if(ocrMap.containsKey(opp.id) {
                    contSet = ocrMap.get(opp.Id);
                }
                else {
                    contSet = new Set<id>();
                }
                // using set, the contact ID will be adding if it not present.
                contSet.add(c.Id);
                ocrMap.put(opp.Id, contSet);
            }
        }          
}

for(Id oppId : ocrMap.keySet()) {
  Set<id> contIdSet = ocrMap.get(oppId);
  for(Id contId : contIdSet) {
     OpportunityContactRole ocr = new OpportunityContactRole(ContactId = contId, 
                                                                        OpportunityId = oppId);
    ocrList.add(ocr);
  }
}

if(ocrList.size() > 0 {
   insert ocrList;
}

 
QD93QD93
Hi Raj,

It appears I need a Code Coverage 75% for this Apex Trigger. How should I approach creating a Code Coverage for this via Apex Class? Please advise, thanks!
Raj R.Raj R.
QD93,

Here is what I recommend if you have not done it already.
  1. Create a Data Factory class as outlined by this trailhead https://trailhead.salesforce.com/en/modules/apex_testing/units/apex_testing_data 
  2. The data factory should create data so that all your logic can be tested (e.g. including all "if" statements)
  3. Create contacts (in data factory class)
  4. create accounts (in data factory class)
  5. create opportunities (in data factory class)
  6. As you insert Opportunities the after insert trigger should be triggered