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
DebbievdnDebbievdn 

Looking for a trigger to be able to make sure that Contact Role is completed before Opportunity is saved

bob_buzzardbob_buzzard
By 'Contact Role is completed', do you mean at least one Opportunity Contact Role is created?
DebbievdnDebbievdn
Hi Bob, that was quick! Yes this is what I am looking for. We require a contact role in our Opps, so that when things go through to our labs that they can also use the Contact Role. However people are just not scrolling down to the related lists and in many instances, not completing this section. I am an administrator, but have no clue have to do this as I have no Apex training. But always willing to learn something new...
bob_buzzardbob_buzzard
It should be pretty straightforward.  I'm assuming that by "before opporutnity is saved" you mean opportunity is closed won? As the opportunity has to be saved before you can define a contact role on it.  It should be something like the following (bulkified, so should handle bulk updates/batch apex etc).  There's probably the odd typo in there, just post the errors back here: 

trigger trg_OppContactRoleDefined_biu on Opportunity (before update)
{
    List<Opportunity> oppsToProcess=new List<Opportunity>();

    // figure out which opportunities have transitioned to close win
    for (Opportunity opp: trigger.new)
    {
       // check if transitioned to closed/won, otherwise ignore
       if ( (opp.StageName=='Closed Won') && (trigger.oldMap.get(opp.id).StageName!='Closed Won') )
       {
          oppsToProcess.add(opp);
       }
    }

    // build a map of the count of opportunity contact role keyed by opportunity id
    Map<Id, Integer> ocrCountByOppId>=new Map<Id, Integer>();
    List<AggregateResult> aggs=[select count(id) idCount, OpportunityId oppId from OpportunityContactRole group by OpportunityId];
    for (AggregateResult ar : aggs)
    {
        ocrCountByOppId.put(ar.get('oppId'), ar.get('idCount'));
    }

    // iterate the opportunities and for those that don't have an entry or the count is zero, add an error to stop the save
    for (Opportunity opp : oppsToProcess)
    {
       Integer ocrCount=ocrCountByOppId.get(opp.id);
       if (null==ocrCount || 0==ocrCount)
       {
           opp.addError('You must have at least one opportunity contact role to close win an opportunity');
        }
    }
}
DebbievdnDebbievdn
Hi Bob, WOW! amazing, will certainly investigate this, thanks so much for your help. I would actually require this at any stage of our Opp as we may need a Lab case done before the Opp can reach the stage of "Closed Won" and when a Lab case is created our Lab guys wish to have the Contact Role.
thanks Bob
Erica_GErica_G
Bob - our users are looking for the opportunity contact to be created upon initial creation of an opportunity. We thought of creating a new field on an opportunity that is a look-up to the contact object and when the opportunity is saved, then have a trigger to add that contact to the contact role related list. The only issue with this solution is that the users want to have an intuitive list of the possible contacts that are listed against the account the opportunity is assoicated to. Any ideas on how to solve this? We originally thought of using Lookup filters on that new field but that doesn't default show the contacts on the account, it just restrict users to searching through those contacts.  
Stephen CurryStephen Curry
Bob - I'm getting the following error: Incompatible key type Object for MAP<Id, Integer>. What am I missing?

Map<Id, Integer> ocrCountByOppId = new Map<Id, Integer>();
    
    List<AggregateResult> aggs = [SELECT count(id) idCount, OpportunityId oppId FROM OpportunityContactRole GROUP BY OpportunityId];
    
    for (AggregateResult ar : aggs) {
        ocrCountByOppId.put(ar.get('oppId'), ar.get('idCount'));
    }

bob_buzzardbob_buzzard
ar.get() returns a generic object - you'll need to cast it to the appropriate type.  Should be as simple as:

ocrCountByOppId.put((Id) ar.get('oppId'), (Integer) ar.get('idCount'));


Stephen CurryStephen Curry
That was it. Thanks!