0

I have One schedulable class that calls a method that have multiple if conditions to be executed

FOR(Contact cont : contacts){

                        if(cont.Account.Due_date_of_last_open_invoice__c == system.today() + 14) {

                            template = DueInvoice;
                        } 

                        if(cont.Account.Due_date_of_last_open_invoice__c == system.today()) {

                            template = DueInvoice;
                        } 

                        if(cont.Account.Due_date_of_last_open_invoice__c == system.today() + 7){

                            template = DueInvoice;
                        }
                        if(cont.Account.Due_date_of_last_open_invoice__c == system.today() - 7){

                            template = BlockingOutstanding;
                        }
                        if(cont.Account.Due_date_of_last_open_invoice__c == system.today() - 14) {

                             template = BlockingAccount;
                        }
                         if(cont.Account.Due_date_of_last_open_invoice__c == system.today() - 28) {

                             template = DeactivatingAccount;
                    }

and sometimes the method hits the governor limits. I'm sure the root cause is when its rendering the VF page for which another action from the extension (init) is also called.

What is the best practice to prevent my code from hitting SOQL limit, is it better for me to have different methods that cover all conditions or make a queueable kind of thing that is scheduled in apex job that has a purpose to stop the code from running if governor limit is reached and will be continued in the next schedule?


    public List<OrgWideEmailAddress> orgWideMail = [SELECT Id FROM OrgWideEmailAddress];
    public EmailTemplate ExpiringContract = [SELECT Id, Name, Description, Subject FROM EmailTemplate WHERE Name =: 'New Renewal Notice' LIMIT 1];
    public EmailTemplate DueInvoice = [SELECT Id, Name, Description, Subject FROM EmailTemplate WHERE Name =: 'Invoice Due in 2 Weeks' LIMIT 1];
    public EmailTemplate BlockingOutstanding = [SELECT Id, Name, Description, Subject FROM EmailTemplate WHERE Name =: 'Blocking Outstanding' LIMIT 1];
    public EmailTemplate BlockingAccount = [SELECT Id, Name, Description, Subject FROM EmailTemplate WHERE Name =: 'Blocking Account' LIMIT 1];
    public EmailTemplate DeactivatingAccount = [SELECT Id, Name, Description, Subject FROM EmailTemplate WHERE Name =: 'Deactivated Contract' LIMIT 1];
    public EmailTemplate PerformanceReport = [SELECT Id, Name, Description, Subject FROM EmailTemplate WHERE Name =: 'New Performance Report PH' LIMIT 1];
    public Id emailtemplate;
    public String templateDescription;              // The template description will be assigned to the Task Subject of the task
    public String templateSubject;
    public Id invPDFattachmemt{get;set;}
    public Attachment attach{get;set;}
    public EmailTemplate template;
    public List<Invoice__c> linv;
    public String day = String.valueOf(date.today().day());
    public Decimal dday = [SELECT Id, GLRemainingQuota__c from Account WHERE Name =: 'Trash Account DO NOT DELETE'].GLRemainingQuota__c;  // used to simply being able to change the supposed sendout date of performance mail without changing and re-deploying the class

    public void autoEmailSendout(){
/**** Put here the sendouts that are active ****/
        this.sendInvoiceReminder();     

    }


public void sendInvoiceReminder(){
        String ReplyToEmail;

/**** Set Sender Name (as appears in client's mailbox) ****/
        String senderName = 'some name';
        String defaultReplyEmail = 'some email';   // used only if both Account Owner and Aftersales Managers are inactive in SF

/**** Set Template ****/

        List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMessage>();
        Set<Id> ids = new Set<Id>();

/**** Set filters and conditions (recipients and criteria -> To:, When?, who?, why?) ****/
        List<Contact> licontact = [SELECT Id FROM Contact
                            WHERE ContactForContracts__c = TRUE     // only contacts that are marked to receive contract info
                            AND Email <> ''
                            AND Account.ExcludeFromAutomatedSendouts__c = FALSE                 // Checkbox on account to exclude Account from sendouts
                            AND Account.Outstanding_Amount__c >:0     
                            AND 
                              (Account.Due_date_of_last_open_invoice__c =: system.today() + 14
                            OR Account.Due_date_of_last_open_invoice__c =: system.today() + 7
                            OR Account.Due_date_of_last_open_invoice__c =: system.today() - 7
                            OR Account.Due_date_of_last_open_invoice__c =: system.today()
                            OR Account.Due_date_of_last_open_invoice__c =: system.today() - 14
                            OR Account.Due_date_of_last_open_invoice__c =: system.today() - 28)
                            AND Account.AccountCountry__c = 'PH' AND Account.TypeOfCompany__c = 'Agent / Broker' 
                            AND (Account.LastInvoiceNoticeDate__c <: system.today() OR Account.LastInvoiceNoticeDate__c =: null)];  // avoids multiple sendouts in case the method is called several times on the same day
        system.debug('aaa' + licontact.size());


/**** Start process ****/

        IF(licontact.size() >0){
            /**** Set parameters ****/
            List<Contact> contacts = [SELECT Id, AccountId, Account.AccountManager__r.Email, Account.AccountManager__r.isActive, Account.Owner.Email, Account.Owner.isActive, Account.Due_date_of_last_open_invoice__c
                                            FROM Contact WHERE Id in: licontact];
            FOR(Contact cont : contacts){
                ids.add(cont.Account.Id);
            }
            List<Account> accounts = [SELECT LastInvoiceNoticeDate__c FROM Account WHERE Id in: ids];//this is for updating the last_invoice_date in account

            List<Invoice__c> linv = [SELECT Id, NetAmount__c, Tax__c, TotalAmount__c, ServicePeriodStartDate__c, ServicePeriodEndDate__c, Account__c, Account__r.Due_date_of_last_open_invoice__c
                                     , InvoiceAutoNumber__c, Contract__c, Account__r.Name, Account__r.AccountCountry__c, Account__r.AccountLegalName__c, Account__r.TaxID__c
                                     , Account__r.BillingStreet, Account__r.BillingCity, Account__r.BillingState, Account__r.BillingPostalCode
                                     , Account__r.BillingCountry, Date_sent__c, Date_1st_reminder_sent__c, Date_2nd_reminder_sent__c 
                                    FROM Invoice__c 
                                    WHERE Account__c in: ids];

             Account parent = [SELECT Id FROM Account where Name =:'Trash Account DO NOT DELETE'];


            /**** Create emails ****/


                    FOR(Contact cont : contacts){

                        if(cont.Account.Due_date_of_last_open_invoice__c == system.today() + 14) {

                            template = DueInvoice;
                        } 

                        if(cont.Account.Due_date_of_last_open_invoice__c == system.today()) {

                            template = DueInvoice;
                        } 

                        if(cont.Account.Due_date_of_last_open_invoice__c == system.today() + 7){

                            template = DueInvoice;
                        }
                        if(cont.Account.Due_date_of_last_open_invoice__c == system.today() - 7){

                            template = BlockingOutstanding;
                        }
                        if(cont.Account.Due_date_of_last_open_invoice__c == system.today() - 14) {

                             template = BlockingAccount;
                        }
                         if(cont.Account.Due_date_of_last_open_invoice__c == system.today() - 28) {

                             template = DeactivatingAccount;
                        }

                        emailtemplate = template.Id;
                        templateDescription = template.Description;              // The template description will be assigned to the Task Subject of the task
                        templateSubject = template.Subject; 


                        system.debug('**linv**' + linv.size());

                        FOR(Invoice__c inv : linv){

                            /// Create the pdf for attachment ///

                            if(inv.Account__c == cont.AccountId && inv.ServicePeriodStartDate__c == inv.Account__r.Due_date_of_last_open_invoice__c){

                                PageReference BillPDF = new PageReference('/apex/InvoicePDF' + inv.Account__r.AccountCountry__c);
                                String pdfname = 'temp';

                                BillPDF.getParameters().put('id', inv.Id);
                                attach = new Attachment();
                                Blob attachbody;
                                try{
                                    attachbody = BillPDF.getContentAsPDF();
                                } catch (VisualForceException e) {
                                    attachbody = Blob.valueof(pdfname);
                                    }

                                attach.Body = attachbody;
                                attach.Name = 'Invoice' + inv.InvoiceAutoNumber__c + '.pdf';
                                attach.IsPrivate = true;
                                attach.ParentId = parent.Id;
                                insert attach;

                                invPDFattachmemt = attach.Id;

                            /// Attach pdf to mail ///

                                Messaging.Emailfileattachment efa = new Messaging.Emailfileattachment();
                                efa.setFileName(attach.Name);
                                efa.setBody(attach.Body);


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

                                mail.setTemplateId(emailtemplate);
                                mail.setOrgWideEmailAddressId(orgWideMail[0].Id); 
                               // mail.setSenderDisplayName(senderName);
                                mail.setTargetObjectId(cont.Id); 
                                mail.setWhatId(inv.Id);
                                mail.setFileAttachments(new Messaging.EmailFileAttachment[] {efa});

                        /**** Set the email address to which the clients replies 
                              ** Aftersales manager by default
                                  ** Account Owner if AS is inactive
                                  ** default email address otherwise                    ****/
                                IF(cont.Account.AccountManager__r.Email <> null && cont.Account.AccountManager__r.isActive == TRUE)
                                ReplyToEmail = cont.Account.AccountManager__r.Email;
                                ELSE IF(cont.Account.Owner.Email <> null && cont.Account.Owner.isActive == TRUE)
                                ReplyToEmail = cont.Account.Owner.Email;
                                ELSE
                                    ReplyToEmail = defaultReplyEmail;      
                                mail.setReplyTo(ReplyToEmail);       

                                mails.add(mail);
                            }
                        }
                    }


            system.debug(mails.size());
            system.debug(mails);

        /**** Send emails ****/
            IF(mails.size() >0){
                Messaging.sendEmail(mails, false);

                /***** updates Account date of last sendout to avoid multiple sendouts ****/
                FOR(Account acc : accounts){
                    acc.LastInvoiceNoticeDate__c = system.today();
                }

                update accounts;



            }
        }
    }


}

my Extension class for the VF page

public with sharing class InvoiceControllerExtension{

    public Invoice__c inv{get;set;}
    public string contractId{get;set;}
    public string account_name{get;set;}
    public string account_country{get;set;}
    public string tax_id{get;set;}
    public string billing_street {get;set;}
    public string billing_city {get;set;}
    public string billing_state {get;set;}
    public string billing_zip {get;set;}
    public string billing_country {get;set;}
    public List<InvoiceLineItem__c> liili{get;set;}
    public Decimal rowsCliTable {get;set;}
    public decimal price {get;set;}
    public decimal vat {get;set;}
    public decimal total {get;set;}
    public string pagebreak{get;set;}

    public List<SelectOption> templateoptions = new List<selectOption>();
    public Id invPDFattachmemt{get;set;}
    public Attachment attach{get;set;}
    public Id template{get;set;}
    public Id MessageLink{get;set;}
    public Boolean confirm{get;set;}
    public string ReplyToEmail ;

    public InvoiceControllerExtension(ApexPages.StandardController ctr) {

        inv = (Invoice__c) ctr.getRecord();
        inv = [SELECT Name, NetAmount__c, Tax__c, TotalAmount__c, ServicePeriodStartDate__c, ServicePeriodEndDate__c, Account__c
                , InvoiceAutoNumber__c, Contract__c, Account__r.Name, Account__r.AccountCountry__c, Account__r.AccountLegalName__c, Account__r.TaxID__c
                , Account__r.BillingStreet, Account__r.BillingCity, Account__r.BillingState, Account__r.BillingPostalCode
                , Account__r.BillingCountry, Date_sent__c, Date_1st_reminder_sent__c, Date_2nd_reminder_sent__c
                FROM Invoice__c WHERE Id =: inv.Id];
        contractId = inv.Contract__c;

    }

     public PageReference init()
    {

        try{
            IF(inv.Account__r.AccountLegalName__c != null)
                account_name = inv.Account__r.AccountLegalName__c;
            ELSE account_name = inv.Account__r.Name;

            account_country = inv.Account__r.AccountCountry__c;
            tax_id          = inv.Account__r.TaxID__c; 
            billing_street  = inv.Account__r.BillingStreet;
            billing_city    = inv.Account__r.BillingCity;
            billing_state   = inv.Account__r.BillingState;
            billing_zip     = inv.Account__r.BillingPostalCode;
            billing_country = inv.Account__r.BillingCountry;             
            liili= [SELECT Id, Name, ContractLineItem__r.Quantity__c, NetAmount__c, ContractLineItem__r.TaxRate__c, TaxRate__c
                        , TotalAmount__c, ContractLineItem__r.Name from InvoiceLineItem__c 
                        WHERE Invoice__c =: inv.Id];
            price = 0;
            vat = 0;
            rowsCliTable = 0;

            FOR(InvoiceLineItem__c ili : liili){
                 rowsCliTable = rowsCliTable + (ili.ContractLineItem__r.Name.length()/40.0).round(System.RoundingMode.UP);        // affects 1 line per 40 characters for CLI name (over 40 char, text is wrapped on VF)
            }

            IF(rowsCliTable  > 12){ pagebreak = 'always';} ELSE{ pagebreak = 'avoid';}

            IF(liili != null){
                for (InvoiceLineItem__c ili : liili){
                    if(ili.NetAmount__c > 0 || ili.TaxRate__c > 0){
                        price = price + ili.NetAmount__c;
                        vat = vat + ili.NetAmount__c*(ili.TaxRate__c/100);
                    }
                }
                total = price + vat;
            }       
        }
        catch (System.CalloutException e){
            ApexPages.Message myMsg = new ApexPages.Message(ApexPages.Severity.FATAL,contractId);
            ApexPages.addMessage(myMsg);
        }

        return null;
    }
}
1
  • such complex schedulable should delegate its work to other async operations - batchable/queueable
    – cropredy
    Commented Oct 17, 2019 at 18:14

1 Answer 1

1

In my opinion, the better practice will be separating your Scheduler into 2 different schedulable classes.

According to the Interface segregation principle of SOLID, it's better have several specific schedulers (in your case) than one general-purpose with complex logic based on time intervals.

3
  • Hi Maria, thank you for your opinion. Commented Oct 23, 2019 at 7:30
  • Hi Maria, thank you for your opinion. I also wanted to debug the root cause of my SOQL 101 limit but to be frank its a bit difficult to me to find it since this class have a method that calls a VF page that also call several actions, can you help to give some ideas the best way to debug this? Commented Oct 23, 2019 at 7:37
  • I think the problem is when you rendering vf page in for-loop for invoices and, as per your words, it also calls several actions. Generally speaking, its nested loop, and that may set you to the limits. Commented Oct 23, 2019 at 8:33

You must log in to answer this question.

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