1

An email should be sent to the primary contact of all Accounts, containing a table with the Closed Won/Open/Closed Lost summarized Opportunity Amounts, but no matter how much i try and change the code i still do not get the email. Can anyone tell me what's wrong with my code?

These are my apex classes with the code:

public with sharing class OppAmountForAccount implements Database.Batchable<sObject> {

    public List<Contact> contList;
    public List<Id> primaryContIdList;
    public List<Id> accountIdList;
    public String acctName;
    public Decimal wonAmount;
    public Decimal lostAmount;
    public Decimal openAmount;

    public Database.QueryLocator start(Database.BatchableContext bc) {
        String query = 'SELECT Id, AccountId, Name, Email, Is_Primary_Contact__c FROM Contact';
        return Database.getQueryLocator(query);
    }

    public void execute(Database.BatchableContext BC, List<Contact> contacts) {
        List<Contact> contList = New List<Contact>();  
        List<Id> accountIdList = new List<Id>();
        List<Id> primaryContIdList = new List<Id>();

        for(Contact con : contacts){
            if(con.Is_Primary_Contact__c == TRUE){
                contList.add(con);
                accountIdList.add(con.AccountId);
                primaryContIdList.add(con.Id);                
            }

            if(accountIdList != null){
                List<Account> accountList = [SELECT Id, Name, Open_Opps_Amount__c, Closed_Won_Opps_Amount__c, Closed_Lost_Opps_Amount__c
                                             FROM Account WHERE Id =: accountIdList];

                for(Account acct : accountList){
                    acctName = acct.Name;
                    wonAmount = acct.Closed_Won_Opps_Amount__c;
                    lostAmount = acct.Closed_Lost_Opps_Amount__c;
                    openAmount = acct.Open_Opps_Amount__c;

                    // Prepare and send email
                    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                    List<String> sendTo = new List<String>();
                    sendTo.add(contList[0].Email); // Assuming there's only one primary contact per account
                    mail.setToAddresses(sendTo);
                    mail.setReplyTo('[email protected]');
                    mail.setSenderDisplayName('Scheduled Apex Batch');
                    mail.setSubject(acctName + ' : Opportunities Amount, based on Stage!');
                    mail.setHtmlBody(
                        '<p>Below is a table of opportunities related to the account: '+ acctName +'.</p>' +
                        '<table style="border-collapse: collapse;" border="1">' +
                        '<tbody>' +
                        '<tr style="height: 18px;">' +
                        '<td style="width: 30%; height: 18px; text-align: center;"><strong>Account</strong></td>' +
                        '<td style="width: 30%; height: 18px; text-align: center;"><strong>Stage</strong></td>' +
                        '<td style="width: 30%; height: 18px; text-align: center;"><strong>Amount</strong></td>' +
                        '</tr>' +
                        '<tr style="height: 18px;">' +
                        '<td style="width: 30%; height: 18px;">'+ acctName +'</td>' +
                        '<td style="width: 30%; height: 18px; text-align: left;">Open</td>' +
                        '<td style="width: 30%; height: 18px;">'+ openAmount +'</td>' +
                        '</tr>' +
                        '<tr style="height: 18px;">' +
                        '<td style="width: 30%; height: 18px;">'+ acctName +'</td>' +
                        '<td style="width: 30%; height: 18px; text-align: left;">Closed Won</td>' +
                        '<td style="width: 30%; height: 18px;">'+ wonAmount +'</td>' +
                        '</tr>' +
                        '<tr style="height: 18px;">' +
                        '<td style="width: 30%; height: 18px;">'+ acctName +'</td>' +
                        '<td style="width: 30%; height: 18px; text-align: left;">Closed Lost</td>' +
                        '<td style="width: 30%; height: 18px;">'+ lostAmount +'</td>' +
                        '</tr>' +
                        '</tbody>' +
                        '</table>'
                    );
                    Messaging.sendEmail(new List<Messaging.SingleEmailMessage>{mail});
                }
            }
        }
    }

    public void finish(Database.BatchableContext BC){
        System.debug('finish');
    }
}

and here is the schedule class to execute the batch class with cron extensions:

global class ScheduleOppAmountForAccount implements Schedulable {

    global void execute(SchedulableContext sc) {
        OppAmountForAccount batchJob = new OppAmountForAccount();

        Id batchJobId = Database.executeBatch(batchJob);

        // Code to Schedule in Anonymous Window

        ScheduleOppAmountForAccount sDaily = new ScheduleOppAmountForAccount();
        String dailyCron = '0 0 10 1/1 * ? *';
        String jobIdD = System.schedule('Daily at 10:00 AM', dailyCron, sDaily);

        ScheduleOppAmountForAccount sWeekly = new ScheduleOppAmountForAccount();
        String weeklyCron = '0 0 13 ? * FRI *';
        String jobIdW = System.schedule('Weekly (Friday at 1:00 PM)', weeklyCron, sWeekly);

        ScheduleOppAmountForAccount sMonthly = new ScheduleOppAmountForAccount();
        String monthlyCron = '0 0 18 ? 1/1 FRI⌗4 *';
        String jobIdM = System.schedule('Monthly (Last Friday of the Month at 6:00 PM)', monthlyCron, sMonthly);
    }
}
New contributor
Florin Hreniuc is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
1
  • Check your debug logs to see if you're running into any errors. With the query inside of your loop (and Messaging.sendEmail() inside of a nested loop), I wouldn't be surprised if you're running into some governor limits. The async query limit is 200, so you might be fine there (because the default batch size is 200, so that's the max number of queries that your code could possibly run), but it's always better to keep queries outside of loops whenever possible.
    – Derek F
    May 15 at 12:53

1 Answer 1

1

You're using Messaging.sendEmail inside a loop. You are limited to 10 invocations of this method per transaction. Add all of the emails you want to send into a list, then send the emails at the end. Likewise, you have a query inside a loop that should be bulkified. While you're granted 200 queries per asynchronous call, it would be more efficient to query all the data you need at once. I would also suggest that you filter the initial query so you only retrieve primary contacts, which will allow the batch to complete in far less time. As a final note, you can use an inline query for compile safety.

public Database.QueryLocator start(Database.BatchableContext bc) {
    return Database.getQueryLocator([
        SELECT AccountId, Name, Email FROM Contact
        WHERE Is_Primary_Contact__c = TRUE AND AccountId <> NULL
    ]);
}
1
  • I tried to do what you told me, but I'm still not receiving any emails. I checked in Apex Jobs, and it's showing 'Attempt to de-reference a null object' again, and the status in the logs shows success, I don't understand anymore, i don't think i will be ever able to solve this problem... May 15 at 19:34

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .