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
Ryan Thomas 317Ryan Thomas 317 

Copy New Opportunity Contact Role to custom object

I have a custom object named OppContactRoles with the following fields: contact,  primary, opportunity, role, dateadded.  Can someone help with a trigger to copy these fields from Opportunity Contact Roles to the OppContactRoles object?
Best Answer chosen by Ryan Thomas 317
Ajay K DubediAjay K Dubedi
Hi Ryan,

Maybe you can run a batch class which will be scheduled to be run every day at 00:00 AM. Instead of running trigger you can run this batch class every day. 
This may be the best solution for you.
Below are the batch class and its scheduler which will schedule the batch class every day at 00:00 AM and if any changes in the opportunities' contact role then it will create the duplicate in custom object. 

----------------------------------------Batch class----------------
global class Batch_copyOpportunityContactRole implements database.batchable<sObject>{
    
    global Database.QueryLocator start(Database.BatchableContext BC)
    {
        String query='Select id from Opportunity';
        return database.getquerylocator(query);
    }
    global void execute(Database.batchableContext bc, list <Opportunity> oppList){
   List<OpportunityContactRole>oppRole=new List<OpportunityContactRole>();
        oppRole=[Select ID,ContactID,OpportunityID,Role,Createddate,isprimary from OpportunityContactRole 
                where opportunityid in:oppList];
       Boolean isExist=false;
        
        system.debug(opprole.size());
         List<OppcontactRoles__c>oppcontact=new List<OppcontactRoles__c>();
        oppContact=[Select ID,Contact__c,Opportunity__c,Role__c,dateAdded__c,primary__c from OppcontactRoles__c 
                where opportunity__c in:oppList];        
                system.debug(oppcontact.size());
        List<OppcontactRoles__c>ocrInsert=new List<OppcontactRoles__c>();
        for(OpportunityContactRole o:oppRole){
                            isExist=false;

system.debug('o-->>'+o);
               for(OppcontactRoles__c oc:oppcontact){
                   system.debug('oc-->>'+oc);
             if(o.ContactId==oc.contact__c && o.OpportunityId==oc.opportunity__c && o.CreatedDate==oc.dateadded__c){
                 
                 isExist=true;}          
        }
             if(!isExist){
                 OppcontactRoles__c ocr=new OppcontactRoles__c();
             
                 ocr.Contact__C=o.ContactId;
                 ocr.opportunity__C=o.OpportunityId;
                 ocr.role__c=String.valueOf(o.Role);
                 ocr.primary__C=o.IsPrimary;
                 ocr.dateadded__C=o.CreatedDate;
                 ocrInsert.add(ocr);
             } }
        if(ocrInsert.size()>0){
            insert ocrInsert;
        } 
    
    }
    global void finish(Database.BatchableContext bc){}
}


-------------------Scheduler Class----------------------------------------------------
global class CopyOpportunityContactRole_Scheduler implements Schedulable{
    
    global void execute(SchedulableContext sc) {
  try{ 
           Batch_copyOpportunityContactRole b = new Batch_copyOpportunityContactRole(); 
           database.executebatch(b);
         
         CopyOpportunityContactRole_Scheduler s=new CopyOpportunityContactRole_Scheduler();
        String sch = '0 0 0 1/1 * ? *';
String jobID = System.schedule('Scheduled to copy opportunity contact role', sch, s); 
     }
catch(exception e)
    {
      System.debug('The following exception has occurred: ' + e.getMessage());
    }
    }
        
    }

If it completes your requirement then please mark this as Best Answer to help others.
Please let me know if you have any query.

Thanks!
Ajay Dubedi

All Answers

Ajay K DubediAjay K Dubedi
Hi Ryan,

There is no way that you can write a trigger directly on opportunity contact role, So you need to create a trigger on any other object and then insert into your custom object role.
Below I have written the trigger on an opportunity whenever an opportunity is updated the trigger fired and create the opportunity contact role in the custom object if the opportunity contact role is not already existed.
-------------------Trigger---------------------
trigger OpportunitycontactRoles on Opportunity(after update) {
    copyOpportunityContactRole.triggermethod(Trigger.New);

}
---------------Related class-----------------
public class copyOpportunityContactRole {
    public static void triggermethod(List<Opportunity> oppList){
       List<OpportunityContactRole>oppRole=new List<OpportunityContactRole>();
        oppRole=[Select ID,ContactID,OpportunityID,Role,Createddate,isprimary from OpportunityContactRole 
                where opportunityid in:oppList];
       Boolean isExist=false;
        
        system.debug(opprole.size());
         List<OppcontactRoles__c>oppcontact=new List<OppcontactRoles__c>();
        oppContact=[Select ID,Contact__c,Opportunity__c,Role__c,dateAdded__c,primary__c from OppcontactRoles__c 
                where opportunity__c in:oppList];        
                system.debug(oppcontact.size());
        List<OppcontactRoles__c>ocrInsert=new List<OppcontactRoles__c>();
        for(OpportunityContactRole o:oppRole){
                            isExist=false;

system.debug('o-->>'+o);
               for(OppcontactRoles__c oc:oppcontact){
                   system.debug('oc-->>'+oc);
             if(o.ContactId==oc.contact__c && o.OpportunityId==oc.opportunity__c && o.CreatedDate==oc.dateadded__c){
                 
                 isExist=true;}          
        }
             if(!isExist){
                 OppcontactRoles__c ocr=new OppcontactRoles__c();
                 ocr.Contact__C=o.ContactId;
                 ocr.opportunity__C=o.OpportunityId;
                 ocr.role__c=String.valueOf(o.Role);
                 ocr.primary__C=o.IsPrimary;
                 ocr.dateadded__C=o.CreatedDate;
                 ocrInsert.add(ocr);
             } }
        if(ocrInsert.size()>0){
            insert ocrInsert;
        } 
    }
}


If it completes your requirement then please mark this as Best Answer to help others.
Please let me know if you have any query.

Thanks 
Ajay Dubedi
Ryan Thomas 317Ryan Thomas 317
Thanks Ajay.

I'm getting the following error on this line:

Error: Compile Error: Invalid bind expression type of Opportunity for column of type String at line 10 column 20
       
oppcontact=[Select ID,contact__c,opportunity__c,Role__c,dateAdded__c,Primary__c from OppcontactRoles__c

My Custom Object Has these fields.  Do I need to change the type or add another field?  I don't have an ID field...

Object Details:
API Name: OppcontactRoles__c

Field Details:
Field | Field Name | Data Type
contact | contact__c | Text(155)
Created By | CreatedById | Lookup(User)
CreatedDate | CreatedDate__c | Date/Time
dateAdded | dateAdded__c | Date/Time
Last Modified By | LastModifiedById | Lookup(User)
opportunity | opportunity__c | Text(155)
Owner | OwnerId | Lookup(User, Group)
Primary | Primary__c | Checkbox
Role | Role__c | Text(155)
Startup Application Contact Role | name | Text(80)
Ajay K DubediAjay K Dubedi
Hi Ryan,

Yes, you need to make these following change in your field type.
contact | contact__c | Text(155)     change this field type to  Lookup(Contact).
opportunity | opportunity__c | Text(155)   change this field type to lookup(Opportunity).
This is because if you wanted to insert the related opportunity in that custom object, and also it will be a good solution if you insert only related opportunities and contacts records in the custom object so that it will be easy for you to track down.

If you don't want to create lookup relationship then it will be a little trickier to set contact name because we work with the IDs of the record for relating the records in the salesforce.

Note: after creating the trigger and after adding contact roles to the opportunity you need to update the opportunity once.

Thank You
Ajay Dubedi
Ryan Thomas 317Ryan Thomas 317
Thanks Ajay.  This works great.  One issue is there a way to auto-update the opportunity so that the trigger runs?  I could add a copyOpportunityContactRoles date field and populate it every night?  Trying to think of a way to automatically do this at least once a day?? Any thoughts?
Ajay K DubediAjay K Dubedi
Hi Ryan,

Maybe you can run a batch class which will be scheduled to be run every day at 00:00 AM. Instead of running trigger you can run this batch class every day. 
This may be the best solution for you.
Below are the batch class and its scheduler which will schedule the batch class every day at 00:00 AM and if any changes in the opportunities' contact role then it will create the duplicate in custom object. 

----------------------------------------Batch class----------------
global class Batch_copyOpportunityContactRole implements database.batchable<sObject>{
    
    global Database.QueryLocator start(Database.BatchableContext BC)
    {
        String query='Select id from Opportunity';
        return database.getquerylocator(query);
    }
    global void execute(Database.batchableContext bc, list <Opportunity> oppList){
   List<OpportunityContactRole>oppRole=new List<OpportunityContactRole>();
        oppRole=[Select ID,ContactID,OpportunityID,Role,Createddate,isprimary from OpportunityContactRole 
                where opportunityid in:oppList];
       Boolean isExist=false;
        
        system.debug(opprole.size());
         List<OppcontactRoles__c>oppcontact=new List<OppcontactRoles__c>();
        oppContact=[Select ID,Contact__c,Opportunity__c,Role__c,dateAdded__c,primary__c from OppcontactRoles__c 
                where opportunity__c in:oppList];        
                system.debug(oppcontact.size());
        List<OppcontactRoles__c>ocrInsert=new List<OppcontactRoles__c>();
        for(OpportunityContactRole o:oppRole){
                            isExist=false;

system.debug('o-->>'+o);
               for(OppcontactRoles__c oc:oppcontact){
                   system.debug('oc-->>'+oc);
             if(o.ContactId==oc.contact__c && o.OpportunityId==oc.opportunity__c && o.CreatedDate==oc.dateadded__c){
                 
                 isExist=true;}          
        }
             if(!isExist){
                 OppcontactRoles__c ocr=new OppcontactRoles__c();
             
                 ocr.Contact__C=o.ContactId;
                 ocr.opportunity__C=o.OpportunityId;
                 ocr.role__c=String.valueOf(o.Role);
                 ocr.primary__C=o.IsPrimary;
                 ocr.dateadded__C=o.CreatedDate;
                 ocrInsert.add(ocr);
             } }
        if(ocrInsert.size()>0){
            insert ocrInsert;
        } 
    
    }
    global void finish(Database.BatchableContext bc){}
}


-------------------Scheduler Class----------------------------------------------------
global class CopyOpportunityContactRole_Scheduler implements Schedulable{
    
    global void execute(SchedulableContext sc) {
  try{ 
           Batch_copyOpportunityContactRole b = new Batch_copyOpportunityContactRole(); 
           database.executebatch(b);
         
         CopyOpportunityContactRole_Scheduler s=new CopyOpportunityContactRole_Scheduler();
        String sch = '0 0 0 1/1 * ? *';
String jobID = System.schedule('Scheduled to copy opportunity contact role', sch, s); 
     }
catch(exception e)
    {
      System.debug('The following exception has occurred: ' + e.getMessage());
    }
    }
        
    }

If it completes your requirement then please mark this as Best Answer to help others.
Please let me know if you have any query.

Thanks!
Ajay Dubedi
This was selected as the best answer
Ryan Thomas 317Ryan Thomas 317
Thanks Ajay.  I have a related question that I may need to open another ticket.  We have Events with Shared Activity turned on.  How can I add/check if all the meeting participants get moved to OppcontactRoles__c?  Or is it better to move them to the contact roles and then have the scheduled job move them to the custom object?
Ryan Thomas 317Ryan Thomas 317
Any changes I'd need to make to ensure if someone is removed from OCR that they are removed from the custom object?