+ Start a Discussion
SeanCenoSeanCeno 

Send Email Notification On Task To A Contact's Internal Owner

All,
I'm writing a trigger to send an email notification to our Internal Owner (a custom field - Internal_Owner__c) of the Contact if a User chooses the Type 'Inbound Queue'. I'm unsure how to:
1.) Add in the SOQL 'Where Task.Type = 'Inbound Queue'.
2.) Send the notification to our Internal Owner of the Contact instead of whom the task is assigned to. Not sure if I need to create a formula field.

Here is my code thus far:

trigger LogACall_Send_Email on Task (before insert) {
    Set<Id> ownerIds = new Set<Id>();
    
    for(Task tsk: Trigger.New)
        ownerIds.add(tsk.ownerId);

    Map<Id, User> userMap = new Map<Id,User>([select Name, Email from User where Id in :ownerIds]);
    for(Task tsk : Trigger.New) {
        User theUser = userMap.get(tsk.ownerId);
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
        String[] toAddresses = new String[] {theUser.Email};
        mail.setToAddresses(toAddresses);
        mail.setSubject('Inbound Queue Call');
        String template = 'Hello {0}, \n\nAn Inbound Queue call was taken for you. Here are the details. \n\n';
        template+= 'Subject - {1}\n';
        template+= 'Due Date - {2}\n';
        template+= 'Type - {3}\n';
        template+= 'Comments - {4}\n';
        String duedate = '';
        if (tsk.ActivityDate==null)
            duedate = '';
        else
            duedate = tsk.ActivityDate.format();
        List<String> args = new List<String>();
        args.add(theUser.Name);
        args.add(tsk.Subject);
        args.add(duedate);
        args.add(tsk.type);
        args.add(tsk.Description);

        String formattedHtml = String.format(template, args);
       
        mail.setPlainTextBody(formattedHtml);
        Messaging.SendEmail(new Messaging.SingleEmailMessage[] {mail});
    }
}
Best Answer chosen by SeanCeno
Pankaj_GanwaniPankaj_Ganwani
Hi,
 
trigger LogACall_Send_Email on Task (before insert){

    Set<Id> setContactId = new Set<Id>();

    List<Task> lstTask = new List<Task>();
     

    //Iterate over Inserted Tasks and collect WhoIds

    for(Task objTask : Trigger.new){

        if(objTask.WhoId !=null && objTask.WhoId.getSobjectType() == Contact.SobjectType && objTask.Type == 'Inbound Queue'){

            setContactId.add(objTask.WhoId);

            lstTask.add(objTask);

        }

    }
 

    Map<id, Contact> mapIdToContact = new Map<Id,Contact>([select Id, Internal_Owner__c, Internal_Owner__r.Email, Internal_Owner__r.Name from Contact where Id In : setContactId AND Internal_Owner__c!=null]);
     

    List<Messaging.SingleEmailMessage> mailToSend = new List<Messaging.SingleEmailMessage>();

    String template = 'Hello {0}, \n\nAn Inbound Queue call was taken for you. Here are the details. \n\n';

    template+= 'Subject - {1}\n';

    template+= 'Due Date - {2}\n';

    template+= 'Type - {3}\n';

    template+= 'Comments - {4}\n';

     

    for(Task tsk : lstTask){

        //Send Email to Internal Owner of the Contact

        if(mapIdToContact.containskey(tsk.WhoId)){

            Contact ObjContact = mapIdToContact.get(tsk.WhoId);

            //User theUser = userMap.get(tsk.ownerId);

            Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();

            String[] toAddresses = new String[] {objContact.Internal_Owner__r.Email};

            mail.setToAddresses(toAddresses);

            mail.setSubject('Inbound Queue Call');

             

            String duedate = '';

            if (tsk.ActivityDate==null)

                duedate = '';

            else

                duedate = tsk.ActivityDate.format();

            List<String> args = new List<String>();

            args.add(objContact.Internal_Owner__r.Name);

            args.add(tsk.Subject);

            args.add(duedate);

            args.add(tsk.type);

            args.add(tsk.Description);

             

            String formattedHtml = String.format(template, args);

            mail.setPlainTextBody(formattedHtml);

            mailToSend.add(mail);

        }

         

        if(mailToSend.size() > 0)

            Messaging.SendEmail(mailToSend);

    }

}

 

All Answers

SlashApex (Luis Luciani)SlashApex (Luis Luciani)
Hi Sean,

Here is your updated code. I put comments on it explaining my steps. You were 3 lines away from having this!
 
trigger LogACall_Send_Email on Task (before insert)
{
    Set<Id> ownerIds = new Set<Id>();
    
    for(Task tsk: Trigger.New)
        ownerIds.add(tsk.ownerId);

    Map<Id, User> userMap = new Map<Id,User>([select Name, Email from User where Id in :ownerIds]);
    
    //EDIT: Creating a list to hold the emails that we will be sending, and then sending them all together
    List<Messaging.SingleEmailMessage> mailToSend = new List<Messaging.SingleEmailMessage>();
    //EDIT: Moved the template declaration out of the loop, since we only need to declare it once
    String template = 'Hello {0}, \n\nAn Inbound Queue call was taken for you. Here are the details. \n\n';
            template+= 'Subject - {1}\n';
            template+= 'Due Date - {2}\n';
            template+= 'Type - {3}\n';
            template+= 'Comments - {4}\n';

    for(Task tsk : Trigger.New) 
    {
        //EDIT: Only doing this operation if the task's type is equal to Inbound Queue
        if(tsk.Type = 'Inbound Queue')
        {
            User theUser = userMap.get(tsk.ownerId);
            Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
            String[] toAddresses = new String[] {theUser.Email};
            mail.setToAddresses(toAddresses);
            mail.setSubject('Inbound Queue Call');
            
            String duedate = '';
            if (tsk.ActivityDate==null)
                duedate = '';
            else
                duedate = tsk.ActivityDate.format();
            List<String> args = new List<String>();
            args.add(theUser.Name);
            args.add(tsk.Subject);
            args.add(duedate);
            args.add(tsk.type);
            args.add(tsk.Description);

            String formattedHtml = String.format(template, args);
           
            mail.setPlainTextBody(formattedHtml);
            mailToSend.add(mail);
        }
    }

    //EDIT: Sending all emails at once, if any.
    if(mailToSend.size() > 0)
        Messaging.SendEmail(mailToSend);
}

Good Luck!
Pankaj_GanwaniPankaj_Ganwani
Hi,

Can you please follow the below mentioned steps:

1. Iterate over inserted Task records and collect the WhoIds in a set where WhoId is not null && WhoId type is contact && Task.Type = 'Inbound Queue'. Use one more list to store Task records(filtered Task where WhoId is not null && WhoId type is contact && Task.Type = 'Inbound Queue')
2. fetch the Contact and their related Internal Owners using below mentioned query:
Map<id, Contact> mapIdToContact = new Map<Id,Contact>([select Id, Internal_Owner__c,Internal_Owner__r.Email, Internal_Owner__r.Name from Contact where Id In : setcollected in step 1 AND Internal_Owner__c!=null]);
3. Iterate over filetered list collected in step 1 and do the following:
for(Task objTask : filteredlist)
{
      if(mapIdToContact.containskey(objTask.WhoId))
{
      Contact objContact = mapIdToContact.get(WhoId);
      Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
      String[] toAddresses = new String[] {objContact.Internal_Owner__r.Email};
      //similar code upto line 24...
      At line no 25 use
      args.add(objContact.Internal_Owner__r.Email);
     //similar code...
}
}

Please let me know if this helps you out.
SeanCenoSeanCeno
Hey guys thanks for the responses.

Luis, your code helped with the 1st problem and now the email only sends when type = 'Inbound Queue', thanks for that!

Pankaj, I'm trying to add this to my code, but I'm still not sure how to properly write it.
1.) In the first step, I'm not sure how to make sure the Who.Type = ContactId
2.) And then I'm getting a "Variable does not exist: WhoId" on line 25.

Here is my code thus far:
 
trigger LogACall_Send_Email on Task (before insert){
	Set<Id> ownerIds = new Set<Id>();
    //Iterate over Inserted Tasks and collect WhoIds
    List<Task> taskRecords = new List<Task>([Select Id, WhoId, Who.Type From Task Where WhoId != null AND Task.Type = 'Inbound Queue']);
	
    for(Task tsk: Trigger.New)
        ownerIds.add(tsk.ownerId);
    
    Map<Id, User> userMap = new Map<Id,User>([select Name, Email from User where Id in :ownerIds]);
    //Fetch Contact and their related Internal Owners
    Map<id, Contact> mapIdToContact = new Map<Id,Contact>([select Id, Internal_Owner__c, Internal_Owner__r.Email, Internal_Owner__r.Name from Contact where Id In : taskRecords AND Internal_Owner__c!=null]);

    List<Messaging.SingleEmailMessage> mailToSend = new List<Messaging.SingleEmailMessage>();
    String template = 'Hello {0}, \n\nAn Inbound Queue call was taken for you. Here are the details. \n\n';
            template+= 'Subject - {1}\n';
            template+= 'Due Date - {2}\n';
            template+= 'Type - {3}\n';
            template+= 'Comments - {4}\n';
    for(Task tsk : Trigger.New){
        if(tsk.Type == 'Inbound Queue') 
        {
            //Assign to Internal Owner
            if(mapIdToContact.containskey(objTask.WhoId))
            {
                Contact ObjContact = mapIdToContact.get(WhoId);
                //User theUser = userMap.get(tsk.ownerId);
                Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                String[] toAddresses = new String[] {objContact.Internal_Owner__r.Email};
                mail.setToAddresses(toAddresses);
                mail.setSubject('Inbound Queue Call');
                
                String duedate = '';
                if (tsk.ActivityDate==null)
                    duedate = '';
                else
                    duedate = tsk.ActivityDate.format();
                List<String> args = new List<String>();
                args.add(objContact.Internal_Owner__r.Email);
                args.add(tsk.Subject);
                args.add(duedate);
                args.add(tsk.type);
                args.add(tsk.Description);
                
                String formattedHtml = String.format(template, args);
                mail.setPlainTextBody(formattedHtml);
                mailToSend.add(mail);
            }
        }
    }
    
    //EDIT: Sending all emails at once, if any.
    if(mailToSend.size() > 0)
        Messaging.SendEmail(mailToSend);
}

 
Pankaj_GanwaniPankaj_Ganwani
Hi,

At line no.4, you can do following:

for(Task objTask : Trigger.new)
{
      if(objTask.WhoId!=null && (objTask.WhoId).getSobjectType == Contact.SobjectType && objTask.Type == 'Inbound Queue')
{
      setContactId.add(objTask.WhoId);//declare this set
      lstTask.add(objTask);//declare this list
}

}

Map<id, Contact> mapIdToContact = new Map<Id,Contact>([select Id, Internal_Owner__c, Internal_Owner__r.Email, Internal_Owner__r.Name from Contact where Id In : setContactId ANDInternal_Owner__c!=null]);

Now, iterate over lstTask(filtered list not the complete list which is coming from trigger to avoid irrelevent iterations)


for(Task objTask : lstTask)
{
      if(mapIdToContact.containskey(objTask.WhoId))
{
      Contact objContact = mapIdToContact.get(objTask.WhoId);
      Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
      String[] toAddresses = new String[] {objContact.Internal_Owner__r.Email};
      //similar code upto line 24...
      At line no 25 use
      args.add(objContact.Internal_Owner__r.Email);
     //similar code...
}
}

I think you need to send email to internal owners of the Contact not the assignTo(owner). If so, you can remove line 6 to 9 from the code.
SeanCenoSeanCeno
I'm getting a "Variable does not exist: setContactId" at line 13 and a "Variable does not exist: 1stTask" at line 23.
 
trigger LogACall_Send_Email on Task (before insert){
	Set<Id> ownerIds = new Set<Id>();
    List<Task> taskRecords = new List<Task>([Select Id, WhoId, Who.Type From Task Where WhoId != null AND Task.Type = 'Inbound Queue']);
    
    //Iterate over Inserted Tasks and collect WhoIds
    for(Task objTask : Trigger.new){
        if(objTask.WhoId!=null && (objTask.WhoId).getSobjectType == Contact.SobjectType && objTask.Type == 'Inbound Queue'){
            setContactId.add(objTask.WhoId);//declare this set
            lstTask.add(objTask);//declare this list
        }
    }
    
    Map<id, Contact> mapIdToContact = new Map<Id,Contact>([select Id, Internal_Owner__c, Internal_Owner__r.Email, Internal_Owner__r.Name from Contact where Id In : setContactId AND Internal_Owner__c!=null]);
    Map<Id, User> userMap = new Map<Id,User>([select Name, Email from User where Id in :ownerIds]);
    
    List<Messaging.SingleEmailMessage> mailToSend = new List<Messaging.SingleEmailMessage>();
    String template = 'Hello {0}, \n\nAn Inbound Queue call was taken for you. Here are the details. \n\n';
    template+= 'Subject - {1}\n';
    template+= 'Due Date - {2}\n';
    template+= 'Type - {3}\n';
    template+= 'Comments - {4}\n';
    
    for(Task tsk : lstTask){
        //Send Email to Internal Owner of the Contact
        if(mapIdToContact.containskey(taskRecords.WhoId)){
            Contact ObjContact = mapIdToContact.get('WhoId');
            //User theUser = userMap.get(tsk.ownerId);
            Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
            String[] toAddresses = new String[] {objContact.Internal_Owner__r.Email};
            mail.setToAddresses(toAddresses);
            mail.setSubject('Inbound Queue Call');
            
            String duedate = '';
            if (tsk.ActivityDate==null)
                duedate = '';
            else
                duedate = tsk.ActivityDate.format();
            List<String> args = new List<String>();
            args.add(objContact.Internal_Owner__r.Email);
            args.add(tsk.Subject);
            args.add(duedate);
            args.add(tsk.type);
            args.add(tsk.Description);
            
            String formattedHtml = String.format(template, args);
            mail.setPlainTextBody(formattedHtml);
            mailToSend.add(mail);
        }
        
        if(mailToSend.size() > 0)
            Messaging.SendEmail(mailToSend);
    }
}

 
Pankaj_GanwaniPankaj_Ganwani
Hi,

Please declare setContactId and lstTask before using them like you have declared ownerIds at line no.2
SeanCenoSeanCeno
Oh god didn't see those comments. My bad.

Now I'm getting "Initial term of field expression must be a concrete SObject: Id at line 10
 
trigger LogACall_Send_Email on Task (before insert){
	Set<Id> ownerIds = new Set<Id>();
    Set<Id> setContactId = new Set<Id>();
    List<Task> lstTask = new List<Task>();
    
    List<Task> taskRecords = new List<Task>([Select Id, WhoId, Who.Type From Task Where WhoId != null AND Task.Type = 'Inbound Queue']);
    
    //Iterate over Inserted Tasks and collect WhoIds
    for(Task objTask : Trigger.new){
        if(objTask.WhoId !=null && (objTask.WhoId).getSobjectType == Contact.SobjectType && objTask.Type == 'Inbound Queue'){
            setContactId.add(objTask.WhoId);
            lstTask.add(objTask);
        }
    }
    
    Map<id, Contact> mapIdToContact = new Map<Id,Contact>([select Id, Internal_Owner__c, Internal_Owner__r.Email, Internal_Owner__r.Name from Contact where Id In : setContactId AND Internal_Owner__c!=null]);
    Map<Id, User> userMap = new Map<Id,User>([select Name, Email from User where Id in :ownerIds]);
    
    List<Messaging.SingleEmailMessage> mailToSend = new List<Messaging.SingleEmailMessage>();
    String template = 'Hello {0}, \n\nAn Inbound Queue call was taken for you. Here are the details. \n\n';
    template+= 'Subject - {1}\n';
    template+= 'Due Date - {2}\n';
    template+= 'Type - {3}\n';
    template+= 'Comments - {4}\n';
    
    for(Task tsk : lstTask){
        //Send Email to Internal Owner of the Contact
        if(mapIdToContact.containskey(taskRecords.WhoId)){
            Contact ObjContact = mapIdToContact.get('WhoId');
            //User theUser = userMap.get(tsk.ownerId);
            Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
            String[] toAddresses = new String[] {objContact.Internal_Owner__r.Email};
            mail.setToAddresses(toAddresses);
            mail.setSubject('Inbound Queue Call');
            
            String duedate = '';
            if (tsk.ActivityDate==null)
                duedate = '';
            else
                duedate = tsk.ActivityDate.format();
            List<String> args = new List<String>();
            args.add(objContact.Internal_Owner__r.Email);
            args.add(tsk.Subject);
            args.add(duedate);
            args.add(tsk.type);
            args.add(tsk.Description);
            
            String formattedHtml = String.format(template, args);
            mail.setPlainTextBody(formattedHtml);
            mailToSend.add(mail);
        }
        
        if(mailToSend.size() > 0)
            Messaging.SendEmail(mailToSend);
    }
}



 
Pankaj_GanwaniPankaj_Ganwani
Hi,

At line no. 10 replace this (objTask.WhoId).getSobjectType == Contact.SobjectType with this one objTask.WhoId.getSobjectType() == Contact.SobjectType()

at line no. 28, use mapIdToContact.containskey(objTask.WhoId)
at line no. 29, use mapIdToContact.get(objTask.WhoId)
SeanCenoSeanCeno
Hey Pankaj,
Still getting errors. Lines 28 & 29 will compile if I use mapIdToContact.containskey('WhoId').

Line 10 = Method does not exist or incorrect signature: Contact.SobjectType() so I changed it to Contact.SobjectType and it compiles.
Line 28 & 29 = Variable does not exist: objTask.WhoId
Pankaj_GanwaniPankaj_Ganwani
Hi,

Use
tsk.WhoId instead of using objTask.WhoId since you are using tsk as loop variable.
SeanCenoSeanCeno
Ah, that makes sense. You my friend are a genius. Here is the final working code if anybody needs to replicate:
 
trigger LogACall_Send_Email on Task (before insert){
	Set<Id> ownerIds = new Set<Id>();
    Set<Id> setContactId = new Set<Id>();
    List<Task> lstTask = new List<Task>();
    
    List<Task> taskRecords = new List<Task>([Select Id, WhoId, Who.Type From Task Where WhoId != null AND Task.Type = 'Inbound Queue']);
    
    //Iterate over Inserted Tasks and collect WhoIds
    for(Task objTask : Trigger.new){
        if(objTask.WhoId !=null && objTask.WhoId.getSobjectType() == Contact.SobjectType && objTask.Type == 'Inbound Queue'){
            setContactId.add(objTask.WhoId);
            lstTask.add(objTask);
        }
    }
    
    Map<id, Contact> mapIdToContact = new Map<Id,Contact>([select Id, Internal_Owner__c, Internal_Owner__r.Email, Internal_Owner__r.Name from Contact where Id In : setContactId AND Internal_Owner__c!=null]);
    Map<Id, User> userMap = new Map<Id,User>([select Name, Email from User where Id in :ownerIds]);
    
    List<Messaging.SingleEmailMessage> mailToSend = new List<Messaging.SingleEmailMessage>();
    String template = 'Hello {0}, \n\nAn Inbound Queue call was taken for you. Here are the details. \n\n';
    template+= 'Subject - {1}\n';
    template+= 'Due Date - {2}\n';
    template+= 'Type - {3}\n';
    template+= 'Comments - {4}\n';
    
    for(Task tsk : lstTask){
        //Send Email to Internal Owner of the Contact
        if(mapIdToContact.containskey(tsk.WhoId)){
            Contact ObjContact = mapIdToContact.get(tsk.WhoId);
            //User theUser = userMap.get(tsk.ownerId);
            Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
            String[] toAddresses = new String[] {objContact.Internal_Owner__r.Email};
            mail.setToAddresses(toAddresses);
            mail.setSubject('Inbound Queue Call');
            
            String duedate = '';
            if (tsk.ActivityDate==null)
                duedate = '';
            else
                duedate = tsk.ActivityDate.format();
            List<String> args = new List<String>();
            args.add(objContact.Internal_Owner__r.Name);
            args.add(tsk.Subject);
            args.add(duedate);
            args.add(tsk.type);
            args.add(tsk.Description);
            
            String formattedHtml = String.format(template, args);
            mail.setPlainTextBody(formattedHtml);
            mailToSend.add(mail);
        }
        
        if(mailToSend.size() > 0)
            Messaging.SendEmail(mailToSend);
    }
}

 
Pankaj_GanwaniPankaj_Ganwani
Hi,

Still, there is redundancy in the code. At line no 2, the set which you have declared in order to contain details of all the owners of Tasks, does not make any use.
SeanCenoSeanCeno
Good point. Feel free to post the working code, and I will mark it as "Best Answer".
Pankaj_GanwaniPankaj_Ganwani
Hi,
 
trigger LogACall_Send_Email on Task (before insert){

    Set<Id> setContactId = new Set<Id>();

    List<Task> lstTask = new List<Task>();
     

    //Iterate over Inserted Tasks and collect WhoIds

    for(Task objTask : Trigger.new){

        if(objTask.WhoId !=null && objTask.WhoId.getSobjectType() == Contact.SobjectType && objTask.Type == 'Inbound Queue'){

            setContactId.add(objTask.WhoId);

            lstTask.add(objTask);

        }

    }
 

    Map<id, Contact> mapIdToContact = new Map<Id,Contact>([select Id, Internal_Owner__c, Internal_Owner__r.Email, Internal_Owner__r.Name from Contact where Id In : setContactId AND Internal_Owner__c!=null]);
     

    List<Messaging.SingleEmailMessage> mailToSend = new List<Messaging.SingleEmailMessage>();

    String template = 'Hello {0}, \n\nAn Inbound Queue call was taken for you. Here are the details. \n\n';

    template+= 'Subject - {1}\n';

    template+= 'Due Date - {2}\n';

    template+= 'Type - {3}\n';

    template+= 'Comments - {4}\n';

     

    for(Task tsk : lstTask){

        //Send Email to Internal Owner of the Contact

        if(mapIdToContact.containskey(tsk.WhoId)){

            Contact ObjContact = mapIdToContact.get(tsk.WhoId);

            //User theUser = userMap.get(tsk.ownerId);

            Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();

            String[] toAddresses = new String[] {objContact.Internal_Owner__r.Email};

            mail.setToAddresses(toAddresses);

            mail.setSubject('Inbound Queue Call');

             

            String duedate = '';

            if (tsk.ActivityDate==null)

                duedate = '';

            else

                duedate = tsk.ActivityDate.format();

            List<String> args = new List<String>();

            args.add(objContact.Internal_Owner__r.Name);

            args.add(tsk.Subject);

            args.add(duedate);

            args.add(tsk.type);

            args.add(tsk.Description);

             

            String formattedHtml = String.format(template, args);

            mail.setPlainTextBody(formattedHtml);

            mailToSend.add(mail);

        }

         

        if(mailToSend.size() > 0)

            Messaging.SendEmail(mailToSend);

    }

}

 
This was selected as the best answer
SeanCenoSeanCeno
Thanks again for all the help!
Pankaj_GanwaniPankaj_Ganwani
Hi,

You are welcome. I like to help others and to be a helping hand.
Pankaj_GanwaniPankaj_Ganwani
Hi,

One more suggestion,

For checking emptiness of the list, always use isEmpty() method instead of using size() > 0 as it consumes more CPU time.
SeanCenoSeanCeno
That’s a great catch, thanks!
SeanCenoSeanCeno
Pankaj,

Sorry to revisit this but I'm having a hell of a time trying to get the send single message part of the trigger covered in my test class. I'm only at 37%. Any way you can help me with that part? The following snippet throws the error:
Method does not exist or incorrect signature: [Messaging.SingleEmailMessage].handleInboundEmail(Messaging.InboundEmail, Messaging.InboundEnvelope)

At this line:
Messaging.SingleEmailMessage mailToSend = new Messaging.SingleEmailMessage();
 
static testMethod void testLogACall_Send_Email() {
        // Create a new email and envelope object
        Messaging.InboundEmail email  = new Messaging.InboundEmail();
        Messaging.InboundEnvelope env = new Messaging.InboundEnvelope();
                
        contact c = new contact();
        c.FirstName = 'Test';
        c.LastName = 'Test';
        c.Last_Completed_Event__c = DateTime.ValueOf('2014-02-01 09:10:00');
        insert c;
        
        Task t = new Task();
        t.ActivityDate = Date.ValueOf('2014-02-01 09:00:00');
        t.Description='Advisor Meeting';
        t.Subject = 'Advisor Meeting';
        t.whoId = c.Id;
        insert t;
        
        email.plainTextBody = 'This should become a note';
        email.fromAddress ='test@test.com';
        String contactEmail = 'jsmith@salesforce.com';
        email.ccAddresses = new String[] {'Jon Smith <' + contactEmail + '>'};
        email.subject = 'Dummy Account Name 123';
        
        Messaging.SingleEmailMessage mailToSend = new Messaging.SingleEmailMessage();
        
        Test.startTest();
        Messaging.InboundEmailResult result = mailToSend.handleInboundEmail(email, env);
        Test.stopTest();
        System.assert (result.success, 'InboundEmailResult returned a failure message');
        
        task [] tskDb = [Select Id, WhoId, Who.Type From Task Where WhoId != null AND Task.Type = 'Inbound Queue'];
        System.assertEquals (1, tskDb.size(),'Task was not inserted');
        Contact [] cDb = [select firstname,lastname from Contact where email=:contactEmail];
        System.assertEquals (1, cDb.size(),'Contact was not inserted!');
        Contact a = CDb[0];
        System.assertEquals ('Jon', a.firstName);
        System.assertEquals ('Smith', a.LastName);
    }

 
Pankaj_GanwaniPankaj_Ganwani
Hi,

At line no. 25 you are creating an instance of Messaging.SingleEmailMessage class named as mailToSend. At line no. 28 you are using mailToSend.handleInboundEmail(email, env); which is 100% incorrect since Messaging.SingleEmailMessage does not contain such method.

Within Test.startTest() and Test.StopTest() you should create an instance of class(which is your email service) and use that instance to call this method.


Test.startTest();
classname obj = new classname();
obj.handleInboudEmail(email,env);
Test.stopTest();
 
SeanCenoSeanCeno
When validating my deployment, I'm getting this error. We have set the WhoId != null so any idea why?
System.DmlException: Insert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, LogACall_Send_Email: execution of BeforeInsert caused by: System.QueryException: Non-selective query against large object type (more than 100000 rows). Consider an indexed filter or contact salesforce.com about custom indexing. Even if a field is indexed a filter might still not be selective when: 1. The filter value includes null (for instance binding with a list that contains null) 2. Data skew exists whereby the number of matching rows is very large (for instance, filtering for a particular foreign key value that occurs many times) Trigger.LogACall_Send_Email: line 5, column 1: [] 
Stack Trace: Class.Test_Triggers.testLogACall_Send_Email: line 196, column 1

Line 5 is:
List<Task> taskRecords = new List<Task>([Select Id, WhoId, Who.Type From Task Where WhoId != null AND Task.Type = 'Inbound Queue']);

Line 196 in my test class is the insert of the task:
static testMethod void testLogACall_Send_Email() {
        
        // Create a new email and envelope object
        contact c = new contact();
        c.FirstName = 'Test';
        c.LastName = 'Test2';
        c.Last_Completed_Event__c = DateTime.ValueOf('2014-02-01 09:10:00');
        insert c;
        
        Test.startTest();
        Task t = new Task();
        t.OwnerId = '005F0000002eRJ4';
        t.Type = 'Inbound Queue';
        t.Subject = 'Test - Follow Up';
        t.ActivityDate = system.Today();
        t.Priority='Normal';
        t.Status = 'Not Started';
        t.whoId = c.Id;
        insert t; //Line 196 where error occurs
        
        t.Status = 'Completed';
        update t;
        
        System.assertEquals (t.Status,'Completed');
        
        Messaging.SingleEmailMessage email  = new Messaging.SingleEmailMessage();
        email.TargetObjectID = t.WhoId;
        email.plainTextBody = 'This should become a note';
        String contactEmail = 'jsmith@salesforce.com';
        email.ccAddresses = new String[] {'Jon Smith <' + contactEmail + '>'};
        email.subject = 'Inbound Queue Call';
        email.setSubject('Inbound Queue Call');
        
        //Messaging.sendEmailResult[] sendEmailResults = Messaging.sendEmail(new Messaging.Email[] {email});
        //System.assertEquals(1, sendEmailResults.size()); // the trigger should send exactly 1 email
        //System.assertEquals(true, sendEmailResults[0].success);
        
        Test.stopTest();
}
Pankaj_GanwaniPankaj_Ganwani
Hi,

Can you please deploy the code which has been selected as Best answer here?

 
SeanCenoSeanCeno
Hey, That's what I was validating. The test class seems to cause the error. Sent from my HTC
Pankaj_GanwaniPankaj_Ganwani
Hi,

Please remove code snippet from line no 24 to 36 in your test class since there is no need of this. Have you used seeAllData = true annotation in test class? If so, please remove it from there.
SeanCenoSeanCeno
Deployed to production. This error occurs when saving a task.
 
LogACall_Send_Email: execution of BeforeInsert

caused by: System.QueryException: Non-selective query against large object type (more than 100000 rows). Consider an indexed filter or contact salesforce.com about custom indexing.
Even if a field is indexed a filter might still not be selective when:
1. The filter value includes null (for instance binding with a list that contains null) 2. Data skew exists whereby the number of matching rows is very large (for instance, filtering for a particular foreign key value that occurs many times)

Trigger.LogACall_Send_Email: line 5, column 1


 
Pankaj_GanwaniPankaj_Ganwani
Hi,

Are you still using this line in your code:
List<Task> taskRecords = new List<Task>([Select Id, WhoId, Who.Type From Task Where WhoId !=null AND Task.Type = 'Inbound Queue']);

If so, Please remove this line and redeploy the artifacts to Production.
SeanCenoSeanCeno
Wow. Totally spaced removing that line. Thanks again!
Pankaj_GanwaniPankaj_Ganwani
Hi Sean,

So, now is it working fine? If you want you can contact me on facebook:
https://www.facebook.com/pankaj.ganwani.9

I can discuss on your concerns.

Thanks,
Panakj
SeanCenoSeanCeno
It’s validated and ready to be deployed to production, but I may wait until after business hours so tasks can still be created by my users in case there’s any issue. I’ll let you know, thanks!
SeanCenoSeanCeno
Sorry to keep bugging you with this. 2 more questions.

1.) The page break '\n' in the template works in the Sandbox, but not in Production.

Sandbox Email Example:
Hello Sean Briceno, 

An Inbound Queue call was taken for you. Here are the details. 

Contact - Buzz Lightyear
Subject - Test - Follow Up
Call Date - 4/14/2015
Type - Inbound Queue
Comments - Test

Production Email Example:
 
Hello Nicholas Erpelding, 

An Inbound Queue call was taken for you. Here are the details. 

Contact - Buzz Lightyear Subject - quick question on sending in transfer of shares form Call Date - 4/14/2015 Type - Inbound Queue Comments - null

2.) Is there a way to make the Contact the task is related to a hyperlink to the Contact's page? Here is what I have so far, but it's not working.
 
List<Messaging.SingleEmailMessage> mailToSend = new List<Messaging.SingleEmailMessage>();
    String template = 'Hello {0}, \n\nAn Inbound Queue call was taken for you. Here are the details. \n\n';
    template+= 'Contact -'<a hrf="'+URL.getSalesforcebaseURL()+'/'+contact.Id'" '+{1}+'</a>'\n';
    template+= 'Subject - {2}\n';
    template+= 'Call Date - {3}\n';
    template+= 'Type - {4}\n';
    template+= 'Comments - {5}\n';

    List<String> args = new List<String>();
    args.add(objContact.Internal_Owner__r.Name);
    args.add(objContact.Name);
    args.add(tsk.Subject);
    args.add(duedate);
    args.add(tsk.type);
    args.add(tsk.Description);

 
Pankaj_GanwaniPankaj_Ganwani
Hi,

At line no. 3 Use:
'Contact -'<a href="'+URL.getSalesforcebaseURL()+'/'+contact.Id'" >'+{1}+'</a>'\n';
SeanCenoSeanCeno
Hmm I'm getting this error:
expecting a semi-colon, found 'href'
SeanCenoSeanCeno
This is as close as I can get to compiling:
template+= 'Contact - '+ '<a href="'+URL.getSalesforceBaseUrl()+'/'+contact.Id+'">'+{1}+'</a>\n';
I get an error:
unexpected token: '{'
Pankaj_GanwaniPankaj_Ganwani
Hi,

Please use this line instead:
template += 'Contact - '+ '<a href="'+URL.getSalesforceBaseUrl().toExternalForm()+'/'+contact.Id+'">{1}</a>\n';
SeanCenoSeanCeno
Unfortunately that displays this:
Contact - <a href="https://cs8.salesforce.com/Id">Buzz Lightyear</a>

I've been trying to modify it with this code, but no luck so far as it displays this:
Contact - https://cs8.salesforce.com/WhoId
 
List<Messaging.SingleEmailMessage> mailToSend = new List<Messaging.SingleEmailMessage>();
    String template = 'Hello {0}, \n\nAn Inbound Queue call was taken for you. Here are the details. \n\n';
    String contactLink = +URL.getSalesforceBaseUrl().toExternalForm()+'/'+task.WhoId;
    template+= 'Contact - {1}\n';
    template+= 'Subject - {2}\n';
    template+= 'Call Date - {3}\n';
    template+= 'Type - {4}\n';
    template+= 'Comments - {5}\n';

            List<String> args = new List<String>();
            args.add(objContact.Internal_Owner__r.Name);
            args.add(contactLink);
            args.add(tsk.Subject);
            args.add(duedate);
            args.add(tsk.type);
            args.add(tsk.Description);



 
Pankaj_GanwaniPankaj_Ganwani
Hi,

I just tried below mentioned code in dev console, It is working fine:
String template = '';
Contact contact = new Contact(Id = '003280000024cmi');
template += 'Contact - '+ '<a href="'+URL.getSalesforceBaseUrl().toExternalForm()+'/'+contact.Id+'">Test</a>\n';
system.debug('===='+template);

Please replace contact.Id with task.WhoId and then try.
SeanCenoSeanCeno
Hmm still getting this in my email:
Contact - <a href="https://cs8.salesforce.com/Id">Test</a>
Code:
String template = 'Hello {0}, \n\nAn Inbound Queue call was taken for you. Here are the details. \n\n';
   template += 'Contact - '+ '<a href="'+URL.getSalesforceBaseUrl().toExternalForm()+'/'+contact.Id+'">Test</a>\n';
   template+= 'Subject - {2}\n';
   template+= 'Call Date - {3}\n';
   template+= 'Type - {4}\n';
   template+= 'Comments - {5}\n';

   List<String> args = new List<String>();
    args.add(objContact.Internal_Owner__r.Name);
    args.add(objContact.Name);
    args.add(tsk.Subject);
    args.add(duedate);
    args.add(tsk.type);
    args.add(tsk.Description);


 
Pankaj_GanwaniPankaj_Ganwani
Hi Sean,

Can you please put the system.debug statement to check what you are getting there?
template += 'Contact - '+ '<a href="'+URL.getSalesforceBaseUrl().toExternalForm()+'/'+contact.Id+'">Test</a>\n';
System.debug('========================'+template);
SeanCenoSeanCeno
Here are the results,
 
11:59:28:492 USER_DEBUG [18]|DEBUG|========================Hello {0}, 
11:59:27:000 USER_DEBUG An Inbound Queue call was taken for you. Here are the details. 
11:59:27:000 USER_DEBUG Contact - <a href="https://cs8.salesforce.com/Id">Test</a>

 
Pankaj_GanwaniPankaj_Ganwani
Hi Sean,

Can you please paste your complete code of the trigger, if possible?
SeanCenoSeanCeno
trigger LogACall_Send_Email on Task (before update){
    Set<Id> setContactId = new Set<Id>();
    List<Task> lstTask = new List<Task>();
    
    //Iterate over Inserted Tasks and collect WhoIds
    for(Task objTask : Trigger.new){
        if(objTask.WhoId !=null && objTask.WhoId.getSobjectType() == Contact.SobjectType && objTask.Type == 'Inbound Queue'){
            setContactId.add(objTask.WhoId);
            lstTask.add(objTask);
        }
    }
    
    Map<id, Contact> mapIdToContact = new Map<Id,Contact>([select Id, Name, Internal_Owner__c, Internal_Owner__r.Email, Internal_Owner__r.Name from Contact where Id In : setContactId AND Internal_Owner__c != null]);    
    
    List<Messaging.SingleEmailMessage> mailToSend = new List<Messaging.SingleEmailMessage>();
    String template = 'Hello {0}, \n\nAn Inbound Queue call was taken for you. Here are the details. \n\n';
    template += 'Contact - '+ '<a href="'+URL.getSalesforceBaseUrl().toExternalForm()+'/'+contact.Id+'">{1}</a>\n';
    System.debug('========================'+template);
    template+= 'Subject - {2}\n';
    template+= 'Call Date - {3}\n';
    template+= 'Type - {4}\n';
    template+= 'Comments - {5}\n';
    
    for(Task tsk : lstTask){
        //Send Email to Internal Owner of the Contact
        if(mapIdToContact.containskey(tsk.WhoId)){
            Contact ObjContact = mapIdToContact.get(tsk.WhoId);
            //User theUser = userMap.get(tsk.ownerId);
            Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
            String[] toAddresses = new String[] {objContact.Internal_Owner__r.Email};
            mail.setToAddresses(toAddresses);
            mail.setSubject('Inbound Queue Call');
            
            String duedate = '';
            if (tsk.ActivityDate==null)
                duedate = '';
            else
                duedate = tsk.ActivityDate.format();
            List<String> args = new List<String>();
            args.add(objContact.Internal_Owner__r.Name);
            args.add(objContact.Name);
            args.add(tsk.Subject);
            args.add(duedate);
            args.add(tsk.type);
            args.add(tsk.Description);
            
            String formattedHtml = String.format(template, args);
            mail.setPlainTextBody(formattedHtml);
            mailToSend.add(mail);
        }
        
        if(mailToSend.size() > 0)
            Messaging.SendEmail(mailToSend);
    }
}
Pankaj_GanwaniPankaj_Ganwani
Hi Sean,

Please try below mentioned code. I made some corrections in there, the contact.Id does not seem to be declared anywhere in the code, so I have added one more string parameter there.
 
trigger LogACall_Send_Email on Task (before update){

    Set<Id> setContactId = new Set<Id>();

    List<Task> lstTask = new List<Task>();

     

    //Iterate over Inserted Tasks and collect WhoIds

    for(Task objTask : Trigger.new){

        if(objTask.WhoId !=null && objTask.WhoId.getSobjectType() == Contact.SobjectType && objTask.Type == 'Inbound Queue'){

            setContactId.add(objTask.WhoId);

            lstTask.add(objTask);

        }

    }

     

    Map<id, Contact> mapIdToContact = new Map<Id,Contact>([select Id, Name, Internal_Owner__c, Internal_Owner__r.Email, Internal_Owner__r.Name from Contact where Id In : setContactId AND Internal_Owner__c != null]);   

     

    List<Messaging.SingleEmailMessage> mailToSend = new List<Messaging.SingleEmailMessage>();

    String template = 'Hello {0}, \n\nAn Inbound Queue call was taken for you. Here are the details. \n\n';

    template += 'Contact - '+ '<a href="'+URL.getSalesforceBaseUrl().toExternalForm()+'/{1}\'">\'{2}\'</a>\n';

    template+= 'Subject - {3}\n';

    template+= 'Call Date - {4}\n';

    template+= 'Type - {5}\n';

    template+= 'Comments - {6}\n';

     

    for(Task tsk : lstTask){

        //Send Email to Internal Owner of the Contact

        if(mapIdToContact.containskey(tsk.WhoId)){

            Contact ObjContact = mapIdToContact.get(tsk.WhoId);

            //User theUser = userMap.get(tsk.ownerId);

            Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();

            String[] toAddresses = new String[] {objContact.Internal_Owner__r.Email};

            mail.setToAddresses(toAddresses);
            mail.setSubject('Inbound Queue Call');

             

            String duedate = '';

            if (tsk.ActivityDate==null)

                duedate = '';

            else

                duedate = tsk.ActivityDate.format();

            List<String> args = new List<String>();

            args.add(objContact.Internal_Owner__r.Name);
			args.add(objContact.Id);
            args.add(objContact.Name);

            args.add(tsk.Subject);

            args.add(duedate);

            args.add(tsk.type);

            args.add(tsk.Description);

             

            String formattedHtml = String.format(template, args);

           mail.setPlainTextBody(formattedHtml);

            mailToSend.add(mail);

        }

         

        if(mailToSend.size() > 0)

            Messaging.SendEmail(mailToSend);

    }

}

 
SeanCenoSeanCeno
Thanks for the response. Although that helped grab the contactId, I'm still getting a full link displayed in the message. Also, the other fields aren't displaying.
 
Hello Sean Briceno, 

An Inbound Queue call was taken for you. Here are the details. 

Contact - <a href="https://cs8.salesforce.com/003L000000QeRdyIAF">Buzz Lightyear</a>
Subject - {3}
Call Date - {4}
Type - {5}
Comments - {6}

 
Pankaj_GanwaniPankaj_Ganwani
Hi,

Please replace the code between line no. 35 to 41 with the following:
 
template+= 'Subject - \'{3}\'\n';
template+= 'Call Date - \'{4}\'\n';
template+= 'Type - \'{5}\'\n';
template+= 'Comments - \'{6}\'\n';

 
SeanCenoSeanCeno
Ah yes, that fixed the the fields not showing up. Still no way to just have the Contact Name be the link?
 
Hello Sean Briceno, 

An Inbound Queue call was taken for you. Here are the details. 

Contact - <a href="https://cs8.salesforce.com/003L000000QeRdyIAF">Buzz Lightyear</a>
Subject - Test - Follow Up
Call Date - 4/23/2015
Type - Inbound Queue
Comments - Test

 
Pankaj_GanwaniPankaj_Ganwani
Hi,

I got the issue. At line no.92 write this line instead:
mail.setHtmlBody(formattedHtml);

Since we are using setPlainTextbody function so it was not being rendered in the form of html.
SeanCenoSeanCeno
YES! Wow good catch. I also changed the line breaks from '\n' to '<br/>' to work with HTML format. Thanks again for all the help!

 
Elle BenamiElle Benami
Hi Everyone,
Is this possible, but instead of being triggered for a new task, being sent every day at a set time, and to include all open tasks of the recipient (regardless of when they were opened or who owns the record they're related to)?
Thanks,
Elle