0

An apex class is triggered by a process builder when a field value is changed. I am trying to send an email to all owners of records of a related object and I get this error. It works when there is only one record when there are multiple records I get this error.

global class sendPaymentNotification {

    global class dataWrapper {
        @InvocableVariable(label='Insert InvoiceId' required=true)
        public String InvoiceId;
        @InvocableVariable(label='Email Template Id' required=true)
        public String emailTemplateId;
    }

    @InvocableMethod(Label='Send Payment Received Email to User')
    global static void sendEmail(List<dataWrapper> passedData) {
        //Send Email
        //
        //set Ids
        Set<Id> userIds = new Set<Id>();

        for(dataWrapper dw: passedData) {

            //Get the Template id for email
            EmailTemplate selectedTemplate = [SELECT Id FROM EmailTemplate WHERE Id =: dw.emailTemplateId];

            //query invoice distributions
            //
            //
             //get invoiceDist emails
             //
              Invoice_Distribution__c pull = [SELECT OwnerId FROM Invoice_Distribution__c where Invoice__c = :dw.InvoiceId and recordowner__c != ''];




            //Create Email object and send
            Messaging.SingleEmailMessage mailToSend = new Messaging.SingleEmailMessage();
            //bring in object Id of Invoice Distributions for emailing
            mailToSend.setTargetObjectId(Pull.OwnerId);
            mailToSend.setTemplateId(selectedTemplate.Id);
            mailToSend.setSaveAsActivity(false);
            List<Messaging.SendEmailResult> sendResults = Messaging.sendEmail(new List<Messaging.Email> { MailToSend });
            System.debug(sendResults[0]);

        }

    }

}
2

that error is because you have assigned to a single sobject (Invoice_Distribution__c pull) the result of a query that returns more than one record.

My suggestion would be to check why your query returns more than one result or limit the query to 1.

Besides that, you have issues in your code. You have done two queries inside a loop, which is not recommended, a code that could guide you is as follows (might not compile, but could give you an idea)

   @InvocableMethod(Label='Send Payment Received Email to User')
    global static void sendEmail(List<dataWrapper> passedData) {
        //Send Email
        //
        //set Ids
        Set<Id> userIds = new Set<Id>();

        // let's group all ids we will need
        Set<Id> templateIds = new Set<Id>();
        Set<Id> invoiceIds = new Set<Id>();
        for(dataWrapper dw: passedData) {
            templateIds.add(dw.emailTemplateId);
            invoiceIds.add(dw.InvoiceId);
        }

        // Get Template and Invoice_Distributions
        Map<Id,EmailTemplate> selectedTemplates = new Map<Id, EmailTemplate>([SELECT Id FROM EmailTemplate WHERE Id IN :templateIds]); // if you trust the Id received is not even needed.
        Map<Id,Id> invoiceToOwnerIdMap = new Map<Id, Id>();

        for (Invoice_Distribution__c forID : [SELECT OwnerId, Invoice__c FROM Invoice_Distribution__c WHERE Invoice__c IN :invoiceIds AND RecordOwner__c != '']) {
            invoiceToOwnerIdMap.put(forID.Invoice__c, forID.OwnerId);
        }


        List<Messaging.Email> emailsToSend = new List<Messaging.Email>();
        for(dataWrapper dw: passedData) {
            if (selectedTemplates.containsKey(dw.emailTemplateId) && invoiceToOwnerIdMap.containsKey(dw.InvoiceId)) {
                //Create Email object and send
                Messaging.SingleEmailMessage mailToSend = new Messaging.SingleEmailMessage();
                //bring in object Id of Invoice Distributions for emailing
                mailToSend.setTargetObjectId(invoiceToOwnerIdMap.get(dw.InvoiceId));
                mailToSend.setTemplateId(dw.emailTemplateId);
                mailToSend.setSaveAsActivity(false);
                emailsToSend.add(mailToSend);
            }
        }

        List<Messaging.SendEmailResult> sendResults = Messaging.sendEmail(emailsToSend);
        System.debug(sendResults[0]);

    }

| improve this answer | |
1

You didn't mention in which one of your SOQL queries this exception occurs, but I assume it happens in either of them.

To resolve this, you must either LIMIT your SOQL queries to return only 1 record, or assign the query output into a List and then select which of the retrieved records you'd like to use, per your use case.

Also, you'll want to run the queries outside of the loop. Running SOQL queries inside a loop is the worst practice. Same goes for sending out the email from inside the loop.

| improve this answer | |
  • The first filters by Id, so would always be unique, actually. – sfdcfox May 1 at 23:35
  • 1
    @sfdcfox Would be unique, yes, but the query returns a list, whereas the variable is expecting one item. A LIMIT 1 on the end would be necessary. – DavidSchach May 1 at 23:39
  • @DavidSchach No, it's a "shortcut" that's allowed in Apex. A query result of one row can be assigned to a variable. – sfdcfox May 1 at 23:42
  • Thank you noted! I actually want to send an email to every result in the invoice distribution query. – J. William Conesa Purman May 2 at 0:03
  • @J.WilliamConesaPurman No problem, but for that you need to gather all outbound emails into a list and then perform mass send on that list, outside the loop. There's a governor limit for sending out emails which you'll exceed if sending from inside the loop. – EranV May 2 at 13:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.