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
Mattias NordinMattias Nordin 

Apex trigger to copy primary contact from the contact roles list on an opportunity

Im trying to use a apex script that i have found. But it seems like it does not take care of the problem of opportunities not having a primary contact. If the primary contact is not defined it throws the following error.

 

Error: Invalid Data.
Review all error messages below to correct your data.
Apex trigger trgMnCopyPrimaryContact caused an unexpected exception, contact your administrator: trgMnCopyPrimaryContact: execution of BeforeUpdate caused by: System.QueryException: List has no rows for assignment to SObject: Trigger.trgMnCopyPrimaryContact: line 6, column 13

 

trigger trgMnCopyPrimaryContact on Opportunity (before update) {

   for (Opportunity o : Trigger.new) {

       OpportunityContactRole contactRole =
            [select ContactID from OpportunityContactRole where IsPrimary = true and OpportunityId = :o.id];

       if (contactRole != null) {
         o.Opportunity_contact__c = contactRole.ContactID;
       }

   }

 }

 

 

Im not a java developer and wonder if someone can help me to add functionality that does not fail if the primary contact is missing.

Best Answer chosen by Admin (Salesforce Developers) 
Mattias NordinMattias Nordin

I ended up with the following code that seems to work exactly as i want. You can't use "IsPrimary = true" since all opportunities that has been created by a lead beeing converted will have a contact role but the isPrimary will be FALSE.

When opportunities are created manually from the contact object then the isPrimary = TRUE.

 

Please vote for this idea to get this fixed ASAP: http://sites.force.com/ideaexchange/apex/ideaview?id=08730000000HKbnAAG

 

The following code picks the isPrimary contact if it has been defined or picks the fist added contact if not =)

 

 

trigger trgMnCopyPrimaryContact on Opportunity (before update) {
// THIS TRIGGER WILL OVERWRITE ANY CONTACT DEFINED IN THE CUSTOM FIELD OPPORTUNITY_CONTACT__C ON THE OPPORTUNITY OBJECT.
// SET THIS FIELD TO READ ONLY OR CHANGE THE FUNCTIONALITY BELOW TO AVIOD DATA BEEING OVERWRITTEN BY MISTAKE...

   for (Opportunity o : Trigger.new) {
   // o.NextStep = 'DEBUG: Running function...';

 


       // CREATE ARRAY OF ALL CONTACT ROLES ON THIS OPPORTUNITY. THE REASON WHY WE DONT PICK THE PRIMARY CONTACT ROLE ONLY
       // IS BECAUSE THE PRIMARY FLAG IS NOT SET WHEN LEADS ARE CONVERTED TO OPPORTUNITIES. ONLY WHEN CREATING OPPORTUNITIES
       // MANUALLY FROM THE CONTACT OBJECT THE IS PRIMARY CHECKBOX IS CHECKED...


       OpportunityContactRole[] contactRoleArray =
       [select ContactID, isPrimary from OpportunityContactRole where OpportunityId = :o.id ORDER BY isPrimary DESC, createdDate];
       if (contactRoleArray.size() > 0) {
         
           // IF PRIMARY IS DEFINED THEN THIS WILL BE THE FIRST OBJECT. IF NOT THE FIRST ADDED CONTACT ROLE WILL BE ADDED...
           o.Opportunity_contact__c = contactRoleArray[0].ContactID;
          
       }else{
      
           // IF NO CONTACT ROLES EXIST RETURN NULL...
           o.Opportunity_contact__c = null;
           // o.NextStep = 'DEBUG; CONTACT = null ';
       }
   }
 }


 

 

 

 

All Answers

forcedotcomforcedotcom

Hi Mattias - the problem is that unfortunately there is no 'HasPrimaryContact' field (or similar) against the Opportunity which would allow you to quickly check if there is one prior to getting the Id in the SOQL query. Because of this you'll always encounter errors as the query could return no value.

 

You have a couple of options:

 

1) You could write a seperate trigger to check if there is a primary contact first, which updates a boolean field on the opp. You could then use this value in your current trigger to check prior to your SOQL query

 

2) You could include a SOQL count() before your query, which would elimiate the error but it will also increase your SOQL count by 1. eg:

 

 

if([select count() from OpportunityContactRole where IsPrimary = true and OpportunityId = :o.id] > 0) {
OpportunityContactRole contactRole =[select ContactID from OpportunityContactRole where IsPrimary = true and OpportunityId = :o.id];
}

 

 

You might also want to check out my recent post which may help http://www.forcedotcom.com/2010/08/auto-update-opportunity-contact-role.html

Satya.KonaSatya.Kona

//this will assign contact to opportunity if there is a primary contact role else does nothing (leaves o.Opportunity_contact__c blank)

//please let me know if this works and if this is wat u ar looking for

//

trigger trgMnCopyPrimaryContact on Opportunity (before update) {

   for (Opportunity o : Trigger.new) {

       Integer i = [select count() from OpportunityContactRole where OpportunityId = :o.id and IsPrimary = true];
      
       if(i==1) {
       OpportunityContactRole contactRole =
            [select ContactID from OpportunityContactRole where OpportunityId = :o.id and IsPrimary = true];
       
       o.op_Contact_c__c = contactRole.ContactID;
       } 

      }

   }

Mattias NordinMattias Nordin

I ended up with the following code that seems to work exactly as i want. You can't use "IsPrimary = true" since all opportunities that has been created by a lead beeing converted will have a contact role but the isPrimary will be FALSE.

When opportunities are created manually from the contact object then the isPrimary = TRUE.

 

Please vote for this idea to get this fixed ASAP: http://sites.force.com/ideaexchange/apex/ideaview?id=08730000000HKbnAAG

 

The following code picks the isPrimary contact if it has been defined or picks the fist added contact if not =)

 

 

trigger trgMnCopyPrimaryContact on Opportunity (before update) {
// THIS TRIGGER WILL OVERWRITE ANY CONTACT DEFINED IN THE CUSTOM FIELD OPPORTUNITY_CONTACT__C ON THE OPPORTUNITY OBJECT.
// SET THIS FIELD TO READ ONLY OR CHANGE THE FUNCTIONALITY BELOW TO AVIOD DATA BEEING OVERWRITTEN BY MISTAKE...

   for (Opportunity o : Trigger.new) {
   // o.NextStep = 'DEBUG: Running function...';

 


       // CREATE ARRAY OF ALL CONTACT ROLES ON THIS OPPORTUNITY. THE REASON WHY WE DONT PICK THE PRIMARY CONTACT ROLE ONLY
       // IS BECAUSE THE PRIMARY FLAG IS NOT SET WHEN LEADS ARE CONVERTED TO OPPORTUNITIES. ONLY WHEN CREATING OPPORTUNITIES
       // MANUALLY FROM THE CONTACT OBJECT THE IS PRIMARY CHECKBOX IS CHECKED...


       OpportunityContactRole[] contactRoleArray =
       [select ContactID, isPrimary from OpportunityContactRole where OpportunityId = :o.id ORDER BY isPrimary DESC, createdDate];
       if (contactRoleArray.size() > 0) {
         
           // IF PRIMARY IS DEFINED THEN THIS WILL BE THE FIRST OBJECT. IF NOT THE FIRST ADDED CONTACT ROLE WILL BE ADDED...
           o.Opportunity_contact__c = contactRoleArray[0].ContactID;
          
       }else{
      
           // IF NO CONTACT ROLES EXIST RETURN NULL...
           o.Opportunity_contact__c = null;
           // o.NextStep = 'DEBUG; CONTACT = null ';
       }
   }
 }


 

 

 

 

This was selected as the best answer
FairweatherFairweather

I'm trying to get this to work in my org. Did you get it to work successfully and update whenever the primary contact was changed?

 

What field type should the field OPPORTUNITY_CONTACT_C be? 

 

Any other set up / security settings I need to be aware of?

Bob_zBob_z

How would i retrieve the contact email address going through the opportunity ?

 

I want to post the email address on an opportunity custom field. 

Bob_zBob_z

How do i get the contact email address through a opportunity contact role?

JIDzenpriseJIDzenprise

Make it a lookup field

JIDzenpriseJIDzenprise

Bob_poliquin wrote:

How do i get the contact email address through a opportunity contact role?


Once you have this trigger in place (or even without it, actually) you can create a workflow rule to populate an email field.

BobBob

How would I add two custom fields to the trigger to be filled in? In the section below. The two custom fields are

Primary_Contact_Role_Email__c

Contact_First_Name__c

 

   
           // IF PRIMARY IS DEFINED THEN THIS WILL BE THE FIRST OBJECT. IF NOT THE FIRST ADDED CONTACT ROLE WILL BE ADDED...
           o.Opportunity_contact__c = contactRoleArray[0].ContactID;
          
       }else{

JIDzenpriseJIDzenprise

I added phone and email via Workflow rule.

BobBob

Yes, that's what I have but I was trying to add it to the trigger to get it all in one shot.

MOrtizMOrtiz

How do you add the email with a workflow?

Bob_zBob_z
I wanted to do the same but it cannot be done with a workflow rule.
MOrtizMOrtiz

Here is my workaround with the info gathered here:

 

1. Custom Email Field

2. Custom Primary Contact Field

3. Trigger which updates the two custom fields with the Primary Contact in Contact Role.

trigger PrimaryContact on Opportunity (before update) {

   for (Opportunity o : Trigger.new) {
 
     if (o.StageName == 'Closed Won') {
      
       OpportunityContactRole ContactRole =
            [SELECT ContactID from OpportunityContactRole where IsPrimary = True and OpportunityId = :o.id]; 
       Contact c =
            [SELECT Name, Email FROM Contact WHERE ID = :ContactRole.ContactID];
       o.Primary_Contact__c = c.Name;
       o.Primary_Email__c = c.Email;
       }
     }
   }

 

4. Workflow rule using Custom Email Field

Dan WardDan Ward

Mattias Nordin
Thank you for your solution, what do i need to do to get the contact name rather than the ID coming through?