[SalesForce] Too many Email Invocations: 11

I am deactivating no of users and when user is deactivated then all of the open opportunities of user has been assigned to their Manager.

But I am getting below error message:
Error Message:

Apex script unhandled exception by user/organization: / Source
organization: (null) Failed to invoke future method 'public static
void changeOwner(List)' on class 'ChangeOpportunityOwner' for job
id ''

caused by: System.LimitException: Too many Email Invocations: 11

Below is the Apex Code:

public without sharing class ChangeOpportunityOwner {       
  @Future
  public static void changeOwner(List<Id> usrIds){
    Boolean isUpdate=false;
    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
    Map<Id, Boolean> mapUserToActive = new Map<Id, Boolean>();
    try{
       for (User u1 : [SELECT Id, isActive FROM User]) { 
         mapUserToActive.put(u1.Id, u1.isActive);
       }
       List <user>usrs= [Select Id,Name,ManagerId,IsActive from user where Id=:usrIds];
       List<Opportunity> finalOptList=new List<Opportunity>();
       for(User u:usrs){   
         List<Opportunity>optys=[Select Id,OwnerId,StageName from Opportunity where OwnerId=:u.Id]; 
         for(Opportunity opt:optys){
           if(opt.StageName!='Closed Won' && opt.StageName!='Closed Lost')
           {
              if (mapUserToActive.get(u.ManagerId)== true &&                 mapUserToActive.get(u.ManagerId)!=null) {
                 opt.OwnerId=u.ManagerId;
              }
              else{ 
                  User usr= [Select Id,Name,ManagerId from user where Name='Manager’];
                  opt.OwnerId=usr.Id;
               }
      finalOptList.add(opt);
    // Set list of people who should get the email
      List<String> sendTo = new List<String>();
      sendTo.add(u.ManagerId);
      mail.setToAddresses(sendTo);
    //  Set who the email is sent from
      mail.setReplyTo('flow@test.com');
      mail.setSenderDisplayName('Support');
     // Set email contents - you can use variables!
      mail.setSubject('Open Opportunities transferred to you');
      String body = 'Dear Manager,<br> ';
      body +='<br>';
      body += 'Please note that '+u.name+'  has been deactivated as a Salesforce User hence all of its <br> Open Opportunities has been assigned to you if any.<br>';
      body +='<br>';
      body +='Kind regards,<br>';
      body +='<br>';
      body +='SFDC Support';
      mail.setHtmlBody(body);
isUpdate=true;
}
}
if(isUpdate)
Messaging.sendEmail(new Messaging.SingleEmailMessage[]{mail},false);
}
if(isUpdate)
{
update finalOptList;
}
}
catch(Exception e)
{
system.debug('An Exception Occured: '+e.getMessage());
}
}


@InvocableMethod(label='Change Opportunity Owner' description='It Change the owner of opportunity when owner is inactive')
public static void changeOwnerFuture(List<Id> usrIds)
{
changeOwner(usrIds);
}
} 

Can somebody please help me what is wrong in apex code?
The opportunity reassignment email that I am sending via apex,it crossed the limit…

Below updated code fix the Too many email invocation error but when I deactivate multiple users and when manager receives an email,it displays only first user information.
Lets say I am deactivating 3 users A,B,C has same manager and when manager receives an email,total 3 mails received but in all emails it displays User A's information.
Updated Code:

public without sharing class ChangeOpportunityOwner {       
@Future
public static void changeOwner(Id[] usrIds){
boolean isUpdate=false;
boolean isUpdate1=false;
boolean isUpdate2=false;
Id genericManagerId = [SELECT Id FROM User WHERE Name = 'Dhananjay Patil'].Id;
String genericName=[Select Id,Name from User where Id=:genericManagerId].Name;
Map<Id,User> usrMap=new Map<Id,User>([Select Id,Name,Manager.Name from User where Id=:usrIds]);
Opportunity[] opps = [SELECT Owner.Name,Owner.Manager.Name,Owner.ManagerId,Owner.Manager.IsActive FROM Opportunity WHERE OwnerId =:usrIds AND IsClosed = FALSE];
Set<Id> managerIds = new Set<Id>();
for(Opportunity opp: opps) {
if(opp.Owner.ManagerId != null && opp.Owner.Manager.IsActive) {
opp.OwnerId = opp.Owner.ManagerId;
managerIds.add(opp.OwnerId);
isUpdate=true;
} else {
opp.OwnerId = genericManagerId;
managerIds.add(opp.OwnerId);
isUpdate1=true;
}

}

Messaging.Email[] messages = new Messaging.Email[0];
Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage();
Id templateId=[Select Id,DeveloperName,body,HtmlValue from EmailTemplate where DeveloperName=:'Assign_Opportunities_to_New_Owner'].Id;
EmailTemplate templ=[Select Id,HtmlValue,body from EmailTemplate where Id=:templateId];
OrgWideEmailAddress owa=[Select Id,Address,DisplayName from OrgWideEmailAddress where Address='test@test.com'];

for(User u1:usrMap.values()){ 
message.setSubject('Open Opportunities transferred to you');
message.setTargetObjectId(u1.managerId);
message.setOrgWideEmailAddressId(owa.Id);
message.setSaveAsActivity(false);
message.setTemplateId(templateId);
//Setting PlainText Body
string test = '';
test=templ.body.replace('{!Opportunity.OwnerFullName}',u1.Name);
message.setPlainTextBody(test);
messages.add(message);
isUpdate2=true;
// } 
}
If((isUpdate||isUpdate1)&&isUpdate2){
Messaging.sendEmail(messages, false);
Database.update(opps, false);
}
}
@InvocableMethod(label='Change Opportunity Owner' description='It Changes the owner of opportunity when owner is inactive')
public static void changeOwnerFuture(Id[] usrIds)
{
changeOwner(usrIds);
}
}

Best Answer

You can only call Messaging.sendEmail 10 times per transaction, but you can put many Messaging.Email items in a list.

Messaging.Email[] messages = new Messaging.Email[0];
for(...) {
  Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
  // Set mail parameters
  messages.add(mail);
}
Messaging.sendEmail(messages);

Realistically, you've massively over-engineered your code. Here's how I would optimize your code:

@future public static void changeOwners(Id[] userIds) {
  Id genericManagerId = [SELECT Id FROM User WHERE Name = 'Manager'].Id;
  Opportunity[] opps = [SELECT Owner.ManagerId, Owner.Manager.IsActive FROM Opportunity WHERE OwnerId = :userIds AND IsClosed = FALSE];
  Set<Id> managerIds = new Set<Id>();
  for(Opportunity opp: opps) {
    if(opp.Owner.ManagerId != null && opp.Owner.Manager.IsActive) {
      opp.OwnerId = opp.Owner.ManagerId;
      managerIds.add(opp.OwnerId);
    } else {
      opp.OwnerId = genericManagerId;
    }
  }
  Messaging.Email[] messages = new Messaging.Email[0];
  for(Id managerId: managerIds) {
    Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage();
    message.setSubject('You have new opportunities.');
    message.setTargetObjectId(managerId);
    message.setSaveAsActivity(false);
    message.setPlainTextBody('You have new opportunities, etc, etc.');
    messages.add(message);
  }
  Messaging.sendEmail(messages, false);
  Database.update(opps, false);
}
Related Topic