It would have been helpful had you included more of your code (at least an outline of your controller methods) to give it more context. That said having been said,
Blob b = pdf.getContent(); // ********* LIMITS ARE HIT HERE **********
Under today's current limits, this would be counted as a callout from a controller. At the time you asked the question, it would not have been. If this was being done inside of a batch class at that time, I believe it may have been counted as a callout. Again, it definitely would be today and would cause you problems with limits before any kind of soql limits were hit.
Since we don't know how you're querying accounts or the templateId, here's what you'd at a minimum want to do before calling your method.
Getting rid of these two queries in your method by calling them in your main controller once:
Id templateId = [SELECT Id
FROM EmailTemplate
WHERE DeveloperName = 'Template'].Id;
List<Account> accounts = [SELECT Id, Related_Lead__c FROM Account];
You can make these static variables and create static methods to both call and generate them using a singleton pattern.
Declare them in your controller:
public static Id templateId {private set;}
public static list<account>accounts {private set;}
Create methods:
public static Id gettemplateId () {
If(isEmpty(templateId)){
templateId = [SELECT Id
FROM EmailTemplate
WHERE DeveloperName = 'Template'].Id;
}
return templateId;
}
public static list<account> getaccounts() {
if(accounts.isEmpty()) {
accounts = [SELECT Id, Related_Lead__c FROM Account];
}
return accounts;
}
In both of the above examples, the query is only run if the variable is empty, otherwise the contents of the existing static variable are returned. This prevents the query from being executed more than once.
To answer your original question, you could reduce the SOQL queries by changing your method definition to look like this:
public static Boolean sendEmail(Account accnt, Id templateId ){
Then add code inside your method to set the incoming account and templateId to the ones you'll be using in your method. That will at least save you from querying for the TemplateId each time.
You could also change it to use the listaccounts so it doesn't happen inside of a for loop as below:
public static Boolean sendEmail(List<account> acctounts, Id templateId) {
which would more closely match your existing code.
Not that unless your previous query in your controller on Account returns Related_Lead__c
, you'll need to query Accounts again to get the Related_Lead__c
.
This wouldn't solve your issue if your class is failing on the getContentasPDF() line
unless your error codes are giving you a red herring. For that reason, I'd recommend you use your controller to kick off a batch class to run this code asynchronously with the scope for each batch class set to run within the getContentasPDF()
callout limits. Alternatively, you could also call a queuable class to do this as well.
Be aware that the following limits will apply:
Maximum size of callout request or response (HTTP request or Web services call): 6 MB for synchronous Apex or 12 MB for asynchronous Apex
The maximum number of asynchronous Apex method executions (batch Apex, future methods, Queueable Apex, and scheduled Apex) per a 24-hour period: The maximum number of asynchronous Apex method executions (batch Apex, future methods, Queueable Apex, and scheduled Apex) per a 24-hour period.
Total number of SOQL queries issued (This limit doesn’t apply to custom metadata types. In a single Apex transaction, custom metadata records can have unlimited SOQL queries.): Syncronous Limit - 100, Asynchronous Limit - 200.
Total number of callouts (HTTP requests or Web services calls) in a transaction: 100.
Using the API or Apex, you can send single emails to a maximum of 5,000 external email addresses per day based on Greenwich Mean Time (GMT).
It's possible that your controller class could have been failing because of the 6MB limit on synchrous callouts for a PDF being returned. Keep the last limit in mind should you have more than 5000 accounts with leads. Obviously, you'll want to keep your scope set to below 100.