Find your content:

Search form

You are here

Trigger on OpportunityTeamMember has an error

 
Share

Since there is no out of the box solution to send email notifications to users when they are added/removed from an Opportunity team, I wrote a trigger. So far I have code to handle the addition. This saves without errors but I get the following error when I test the trigger.

execution of AfterInsert caused by: System.EmailException: SendEmail failed. First exception on row 0; first error: INVALID_EMAIL_ADDRESS, Invalid to address : null: []: 

This is my trigger:

trigger notify_opportunity_team_members on OpportunityTeamMember (after delete, after insert) {

    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
    for (OpportunityTeamMember o : trigger.new) { 
        String[] toAddresses = new String[]{o.User.Email};
        mail.setToAddresses(toAddresses);

        mail.setSubject('Addition to an Opportunity Team : ');
        mail.setUseSignature(false);
        mail.setPlainTextBody(o.User.FirstName + ' ' + o.User.LastName + ' has been added to the Opportunity Team of: ' + o.Opportunity);
        mail.setHtmlBody(o.User.FirstName + ' ' + o.User.LastName + ' has been added to the Opportunity Team of: <b>' + o.Opportunity);
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });

    }
}

What am I doing wrong? Can't we get the user's email using o.User.Email?

Also I need to send this email to all the Opportunity team members, how can I get all their emails? Thanks.


Attribution to: Richard N

Possible Suggestion/Solution #1

Only "simple fields" are available in the trigger out of the box. In this case it means you can get UserId (because that's actual field on the OpportunityTeamMember) but not User.Name, User.Email, User.Manager.UserRole.Name ;)

  1. Loop through all Opp Team Members, collect their Ids
  2. Run only one query to get info about all users
  3. Use that info

The code will be a bit complex so you can save on emails being sent ;)

Map<Id, List<Id>> opportunityToUserIdsMap = new Map<Id, List<Id>>(); // stores data similar to "opp 123" => "users John Doe and Jane Smith were added"
Set<Id> userIds = new Set<Id>();

// 1
for(OpportunityTeamMember otm : trigger.new){
    userIds.add(otm.UserId);

    if(!opportunityToUserIdsMap.containsKey(otm.OpportunityId)){
        opportunityToUserIdsMap.put(otm.OpportunityId, new List<Id>{otm.UserId});
    } else {
        opportunityToUserIdsMap.get(otm.OpportunityId).add(otm.UserId);
    }
}

// 2 - only 1 query for users. And only one for Opportunities (unless you don't want to display their names in email?)
Map<Id, User> affectedUsers = new Map<Id, User>([SELECT Id, Name, Email FROM User WHERE Id IN :userIds]);
Map<Id, Opportunity> affectedOpps = new Map<Id, Opportunity>([SELECT Id, Name, Account.Name FROM Opportunity WHERE Id IN :opportunityToUserIdsMap.keyset()]);

// 3
for(Id oppId : opportunityToUserIdsMap.keyset()){
    // we have Opportunity Id, we can use it to retrieve more data about it
    Opportunity o = affectedOpps.get(oppId);
    List<String> emails = new List<String>();

    // and we can use it to get list of user ids which can then be used to match to emails
    for(Id userId : opportunityToUserIdsMap.get(oppId)){
        User u = affectedUsers.get(userId);
        emails.add(u.Email);
    }

    System.debug('Email about changes to Team under ' + o.Name + ' should be sent to: ' + emails);
    // generate emails here but only keep adding them to the Messaging.SingleEmailMessage[]
}
// and perform the email sending call (might have multiple emails in it) outside of the loop ;)

Attribution to: eyescream
This content is remixed from stackoverflow or stackexchange. Please visit https://salesforce.stackexchange.com/questions/5023

My Block Status

My Block Content