• Gleb Vrevsky
  • NEWBIE
  • 30 Points
  • Member since 2015
  • eCommerce Project Manager
  • Scandiweb


  • Chatter
    Feed
  • 0
    Best Answers
  • 0
    Likes Received
  • 0
    Likes Given
  • 4
    Questions
  • 5
    Replies
We have a custom object - Employees. I have an Apex class to send an email to all employees. It is scheduled to be sent once a month, using "Schedule Apex". But it fails with an error: "Scheduler: failed to execute scheduled job: jobId: 707G000003Axx2S, class: common.apex.async.AsyncApexJobObject, reason: Too many Email Invocations: 11"

Obviously, it fails due to governor limits (more than 10 sendEmails). Please, find my Apex class below. What is the best way to make it work for more than 100 employees? Should I rework it as a Batch Apex or...?

Any example would be much appreciated.
global class EmployeeMonthlyEmail implements Schedulable {
    
    global void execute(SchedulableContext ctx) {
        List<Employee__c> empList = 
            [SELECT Id, Name, FirstName__c, LastName__c, Email__c, 
                    Birthdate__c, MobilePhone__c, PersonalEmail__c, Skype__c
             FROM Employee__c
             WHERE Status__c != 'Disabled'
            ];
        
        for(Employee__c emp:empList){
            sendEmailToEmployee(emp);
        }
    }
    
    public static Boolean sendEmailToEmployee(Employee__c emp){
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
        
        mail.setToAddresses(new String[]{emp.Email__c});
        mail.setReplyTo('inese@scandiweb.com');
        mail.setSenderDisplayName('Scandiweb Salesforce');
        mail.setSubject('Employee Info - ' + emp.Name);
        mail.setBccSender(false);
        mail.setUseSignature(false);
        mail.setCharset('UTF-8');
        
        String emailText = 
            '<p>Hello ' + emp.FirstName__c +',</p>'+
            
            '<p>Please, check information we have, and let us know if we should update it.'+
            '<br />You are welcome to email inese@scandiweb.com with new information'+
            '<br />Thank you in advance ;)</p>'+
            
            '<p>First Name: '+ emp.FirstName__c + '<br />'+
            'Last Name: '+ emp.LastName__c + '<br />'+
            'Birthdate: '+ emp.Birthdate__c + '<br />'+
            'Mobile Phone: '+ emp.MobilePhone__c + '<br />'+
            'Personal Email: '+ emp.PersonalEmail__c + '<br />'+
            'Skype: '+ emp.Skype__c + '</p>';
        
        List<CourseEnrollment__c> courseList = 
            [SELECT Course__r.Name, Status__c, DateExpectedOn__c, DateCompletedOn__c
             FROM CourseEnrollment__c
             WHERE Employee__c = :emp.Id
             ORDER BY Status__c DESC
            ];
        
        emailText = emailText + mailTextEmployeeCourseList(courseList);
        
        emailText = emailText + 
            '</p>'+
            '<p>Please excuse us for any issues, we are still in process of getting better ;)</p>';
        
        mail.setHtmlBody(emailText);
    
        try {
          List<Messaging.SendEmailResult> results = 
              Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
          
            if (!results.get(0).isSuccess()) {
                return false;
            } else {
                return true;
            }
        } catch (Exception e) {
          //System.debug('Exception has occurred: ' + e.getMessage());
            return false;
        }
    }
    
    public static String mailTextEmployeeCourseList(List<CourseEnrollment__c> courseList) {
        String text = '';
        
        text = text + '<p>Course List:';
        if (courseList.size() >= 1)
        {
            for (CourseEnrollment__c ce : courseList)
            {
                text = text + 
                    '<br />' + ce.Course__r.Name + ' (' + ce.Status__c + ')';
                
                if (ce.Status__c == 'Completed' && ce.DateCompletedOn__c != null) {
                    text = text + 
                      ' - ' + ce.DateCompletedOn__c.day() + '/' + ce.DateCompletedOn__c.month() + '/' + ce.DateCompletedOn__c.year();
                } else if (ce.Status__c == 'Expected' && ce.DateExpectedOn__c != null) {
                    text = text + 
                      ' - ' + ce.DateExpectedOn__c.day() + '/' + ce.DateExpectedOn__c.month() + '/' + ce.DateExpectedOn__c.year();
                }
                
            }
        } else {
            text = text + 
                '<br />There are no Courses assigned to you ;(';
        }
        
        return text;
    }
}

 
Hello!

I have a Process which starts only when a record is created. Example of process: http://www.screencast.com/t/5okrck1Or
Time-dependent action is configured, which is sending out an email before object overdue date. Everything works fine, except of: scheduled action is executed in spite of conditions are met or not. E.g. criteria for Executing Actions is set to Object Status Equal to "Not Paid". If object status is changed to e.g. "Paid" before scheduled actions, it is still sending out an email. 

Process configuration seems to be fine...or should I start the process when a record is created or edited + "Do you want to execute the actions only when specified changes are made to the record?" set to YES ? 

From documentation:
"Time-dependent actions remain in the workflow queue only as long as the workflow rule criteria are still valid. If a record no longer matches the rule criteria, Salesforce removes the time-dependent actions queued for that record.
For example, an opportunity workflow rule can specify:A criteria set to “Opportunity: Status not equals to Closed Won, Closed Lost”
An associated time-dependent action with a time trigger set to 7 days before the opportunity close date
If a record that matches the criteria is created on July 1 and the Close Date is set to July 30, the time-dependent action is scheduled for July 23. However, if the opportunity is set to “Closed Won” or “Closed Lost” before July 23, the time-dependent action is removed from the queue."

Thanks a lot in advance.
Hello!

Hope you could help me with the developing test class - have tried several ideas, nothing actually works.
Initial task: output on visualforce page record A grouped by record B. 
To achieve wished result (result looks smth like ->  http://www.screencast.com/t/AocRaNSytH) custom controller was created.
Controller sample code:
public with sharing class DisplayGroupedCourses {

  public List<CourseEnrollment__c> courses {get;set;}
  public List<CourseEnrollment__c> courses_all {get;set;}
  public List<Employee__c> employees {get;set;}
  public Integer emplCount {get;set;}
  public Integer courseCount {get;set;}
  public String[] names {get;set;}

  public void load() {

    // Get courses with status completed
    courses = [SELECT Course__r.name, Course__r.id, Employee__r.name, Status__c, 
               DateExpectedOn__c, DateCompletedOn__c 
               FROM CourseEnrollment__c 
               WHERE Status__c = 'Completed'
               ORDER BY Employee__r.name ASC, DateCompletedOn__c DESC
              ];
    // Get all employees   
    employees = [SELECT Id, Name, Email__c, MobilePhone__c, Skype__c, 
                 PersonalEmail__c, EntryDate__c, Photo__c, EmployeeRole__r.name   
                 FROM Employee__c
                 WHERE Status__c != 'Disabled'
                 ORDER BY EntryDate__c ASC
                ];

    <!-- OTHER querie !-->

    // Dynamically create set of unique names from the query
    Set<String> nameSet = new Set<String>();
      for (CourseEnrollment__c c : courses) {
          nameSet.add(c.Employee__r.name);          
      }

    // Convert the names set into a string array  
    names = new String[nameSet.size()];
    Integer i = 0;
    for (String name : nameSet) { 
      names[i] = name;
      i++;
    }
  }
}

All works great, the only thing left - create a test class. Could someone give me ideas where to start or provide some sample code to cover class above with tests? I was trying to fill in created lists with sample data, but can't figure out what is the right way to do it.
I have started with:
 
@isTest
    public class DisplayGroupedCoursesTest { 
        static testMethod void myUnitTest() { 
            Employee__c e = new Employee__c();
            e.name = 'glebs vrevskis';
            insert e;
            //REFERENCE TO MY METHOD SHOULD GO HERE? 
            
            system.assertEquals('glebs vrevskis', e.name); 
        }
    }

 Any help is much appreciated.
Hello!

Task: I am trying to create pretty much simple visualforce page. Table with 5 columns. Column 1 = Employee name. Column 2-5 = Employee completed courses.

Problem: Faced a problem creating columns for employee courses. These columns should output the TRUE or FALSE value e.g. if employee has completed a course or not. The current implementation looks like:
     
<apex:page standardController="CourseEnrollment__c" recordSetVar="records" id="thePage" cache="false"> 
<apex:form id="theForm"> 
    <apex:pageBlock id="thePageBlock"> 
        <apex:pageBlockTable value="{!records}" var="record" id="thePageBlockTable"> 
            <apex:column >
                <apex:outputField value="{!record.Employee__r.Name}" id="EmployeeNameDOM" /> 
                <apex:facet name="header">Name</apex:facet>
            </apex:column>
            <apex:column>
                <apex:outputText value="{!IF(!record.Course__r.Status == 'Completed','Completed','Expected')}" id="CourseNameDOM" /> 
                <apex:facet name="header">Course status</apex:facet>
            </apex:column>
        </apex:pageBlockTable> 
    </apex:pageBlock> 
</apex:form>
</apex:page>
The main object is CourseEnrollment, Employee and Course are related objects (master-details relation -> http://www.screencast.com/t/nLStStlAjzr)
Code above is just a test, trying to make IF statement work - no luck (also tried to use CONTAINS).

Question:
What is the right way to compare custom objects values on visualforce page? Is it worth to use custom controller, and get all required data already inside the custom class, not to complicate visualforce page structure?

 
We have a custom object - Employees. I have an Apex class to send an email to all employees. It is scheduled to be sent once a month, using "Schedule Apex". But it fails with an error: "Scheduler: failed to execute scheduled job: jobId: 707G000003Axx2S, class: common.apex.async.AsyncApexJobObject, reason: Too many Email Invocations: 11"

Obviously, it fails due to governor limits (more than 10 sendEmails). Please, find my Apex class below. What is the best way to make it work for more than 100 employees? Should I rework it as a Batch Apex or...?

Any example would be much appreciated.
global class EmployeeMonthlyEmail implements Schedulable {
    
    global void execute(SchedulableContext ctx) {
        List<Employee__c> empList = 
            [SELECT Id, Name, FirstName__c, LastName__c, Email__c, 
                    Birthdate__c, MobilePhone__c, PersonalEmail__c, Skype__c
             FROM Employee__c
             WHERE Status__c != 'Disabled'
            ];
        
        for(Employee__c emp:empList){
            sendEmailToEmployee(emp);
        }
    }
    
    public static Boolean sendEmailToEmployee(Employee__c emp){
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
        
        mail.setToAddresses(new String[]{emp.Email__c});
        mail.setReplyTo('inese@scandiweb.com');
        mail.setSenderDisplayName('Scandiweb Salesforce');
        mail.setSubject('Employee Info - ' + emp.Name);
        mail.setBccSender(false);
        mail.setUseSignature(false);
        mail.setCharset('UTF-8');
        
        String emailText = 
            '<p>Hello ' + emp.FirstName__c +',</p>'+
            
            '<p>Please, check information we have, and let us know if we should update it.'+
            '<br />You are welcome to email inese@scandiweb.com with new information'+
            '<br />Thank you in advance ;)</p>'+
            
            '<p>First Name: '+ emp.FirstName__c + '<br />'+
            'Last Name: '+ emp.LastName__c + '<br />'+
            'Birthdate: '+ emp.Birthdate__c + '<br />'+
            'Mobile Phone: '+ emp.MobilePhone__c + '<br />'+
            'Personal Email: '+ emp.PersonalEmail__c + '<br />'+
            'Skype: '+ emp.Skype__c + '</p>';
        
        List<CourseEnrollment__c> courseList = 
            [SELECT Course__r.Name, Status__c, DateExpectedOn__c, DateCompletedOn__c
             FROM CourseEnrollment__c
             WHERE Employee__c = :emp.Id
             ORDER BY Status__c DESC
            ];
        
        emailText = emailText + mailTextEmployeeCourseList(courseList);
        
        emailText = emailText + 
            '</p>'+
            '<p>Please excuse us for any issues, we are still in process of getting better ;)</p>';
        
        mail.setHtmlBody(emailText);
    
        try {
          List<Messaging.SendEmailResult> results = 
              Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
          
            if (!results.get(0).isSuccess()) {
                return false;
            } else {
                return true;
            }
        } catch (Exception e) {
          //System.debug('Exception has occurred: ' + e.getMessage());
            return false;
        }
    }
    
    public static String mailTextEmployeeCourseList(List<CourseEnrollment__c> courseList) {
        String text = '';
        
        text = text + '<p>Course List:';
        if (courseList.size() >= 1)
        {
            for (CourseEnrollment__c ce : courseList)
            {
                text = text + 
                    '<br />' + ce.Course__r.Name + ' (' + ce.Status__c + ')';
                
                if (ce.Status__c == 'Completed' && ce.DateCompletedOn__c != null) {
                    text = text + 
                      ' - ' + ce.DateCompletedOn__c.day() + '/' + ce.DateCompletedOn__c.month() + '/' + ce.DateCompletedOn__c.year();
                } else if (ce.Status__c == 'Expected' && ce.DateExpectedOn__c != null) {
                    text = text + 
                      ' - ' + ce.DateExpectedOn__c.day() + '/' + ce.DateExpectedOn__c.month() + '/' + ce.DateExpectedOn__c.year();
                }
                
            }
        } else {
            text = text + 
                '<br />There are no Courses assigned to you ;(';
        }
        
        return text;
    }
}

 
Hello!

I have a Process which starts only when a record is created. Example of process: http://www.screencast.com/t/5okrck1Or
Time-dependent action is configured, which is sending out an email before object overdue date. Everything works fine, except of: scheduled action is executed in spite of conditions are met or not. E.g. criteria for Executing Actions is set to Object Status Equal to "Not Paid". If object status is changed to e.g. "Paid" before scheduled actions, it is still sending out an email. 

Process configuration seems to be fine...or should I start the process when a record is created or edited + "Do you want to execute the actions only when specified changes are made to the record?" set to YES ? 

From documentation:
"Time-dependent actions remain in the workflow queue only as long as the workflow rule criteria are still valid. If a record no longer matches the rule criteria, Salesforce removes the time-dependent actions queued for that record.
For example, an opportunity workflow rule can specify:A criteria set to “Opportunity: Status not equals to Closed Won, Closed Lost”
An associated time-dependent action with a time trigger set to 7 days before the opportunity close date
If a record that matches the criteria is created on July 1 and the Close Date is set to July 30, the time-dependent action is scheduled for July 23. However, if the opportunity is set to “Closed Won” or “Closed Lost” before July 23, the time-dependent action is removed from the queue."

Thanks a lot in advance.
Hello!

Hope you could help me with the developing test class - have tried several ideas, nothing actually works.
Initial task: output on visualforce page record A grouped by record B. 
To achieve wished result (result looks smth like ->  http://www.screencast.com/t/AocRaNSytH) custom controller was created.
Controller sample code:
public with sharing class DisplayGroupedCourses {

  public List<CourseEnrollment__c> courses {get;set;}
  public List<CourseEnrollment__c> courses_all {get;set;}
  public List<Employee__c> employees {get;set;}
  public Integer emplCount {get;set;}
  public Integer courseCount {get;set;}
  public String[] names {get;set;}

  public void load() {

    // Get courses with status completed
    courses = [SELECT Course__r.name, Course__r.id, Employee__r.name, Status__c, 
               DateExpectedOn__c, DateCompletedOn__c 
               FROM CourseEnrollment__c 
               WHERE Status__c = 'Completed'
               ORDER BY Employee__r.name ASC, DateCompletedOn__c DESC
              ];
    // Get all employees   
    employees = [SELECT Id, Name, Email__c, MobilePhone__c, Skype__c, 
                 PersonalEmail__c, EntryDate__c, Photo__c, EmployeeRole__r.name   
                 FROM Employee__c
                 WHERE Status__c != 'Disabled'
                 ORDER BY EntryDate__c ASC
                ];

    <!-- OTHER querie !-->

    // Dynamically create set of unique names from the query
    Set<String> nameSet = new Set<String>();
      for (CourseEnrollment__c c : courses) {
          nameSet.add(c.Employee__r.name);          
      }

    // Convert the names set into a string array  
    names = new String[nameSet.size()];
    Integer i = 0;
    for (String name : nameSet) { 
      names[i] = name;
      i++;
    }
  }
}

All works great, the only thing left - create a test class. Could someone give me ideas where to start or provide some sample code to cover class above with tests? I was trying to fill in created lists with sample data, but can't figure out what is the right way to do it.
I have started with:
 
@isTest
    public class DisplayGroupedCoursesTest { 
        static testMethod void myUnitTest() { 
            Employee__c e = new Employee__c();
            e.name = 'glebs vrevskis';
            insert e;
            //REFERENCE TO MY METHOD SHOULD GO HERE? 
            
            system.assertEquals('glebs vrevskis', e.name); 
        }
    }

 Any help is much appreciated.