function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Shawn Reichner 33Shawn Reichner 33 

Too many queueable jobs in queue 2

Hello awesome developer helpers!
I have a problem on my hands around getting enough code coverage for the below code I am working on.
The Scenario: When a CPQ quote is being worked and it is a primary quote I have a process builder that keeps fields updated between the primary quote and the opportunity.
One of these fields is a multi select picklist called opportunity manufacturer, and what should be populating in this field is the manufacturers of each product on the quote only listed once (hence the set to hold those values).
So when this process builder process kicks off, fields are updated and the below "AggregateManufacturerInfo" class is called from the process. This class then grabs the list of Opp IDs from the process and calls a queueable class to do the work listed above for the manufactures and populating the multi select picklist.
Here is where I had to build out the code more as with CPQ it seems to make callouts to heroku when calculation or a document is generated, and I was getting the "Too Many queueable jobs in the queue" error. So I added in the try catch block on the following code "AggregateManQueueableJob" class to check if Limits.getQueueableJobs() is greater than 0 to catch this and if the limit is greater than 0, then call a scheduled class to get around the limit of 2 per queue error.
Here is the problem my friends, this is my first scheduled class, and the test class below saves and covers all of my code except for the calling of the scheduled class in the try / catch block, and my question is what can I change or how can I get that portion covered in my test class?
How can I fake.mock more than one queueable job in the queue to get my test class to test both scenarios? My current coverage is 100% on all classes except for "AggregateManQueuableJob" which is currently at 71%. Any help you can give would be greatly appreciated :)
Initial class called from process :AggregateManufcaturerInfo"
 
public class AggregateManufacturerInfo {

    @InvocableMethod
    public static void setOppMan(LIST<ID> OppIds){
        System.enqueueJob(new AggregateManQueueableJob(oppIds));
    }
}

Queueable class called from above class:
 
public class AggregateManQueueableJob implements Queueable {
    
    public List<Id> oppList;

    public AggregateManQueueableJob (List<Id> oppIds){
        oppList = oppIds;
    }

    public void execute(QueueableContext qc){
        System.debug(oppList);
        
        Id OppId = oppList[0];
        
        Opportunity opp = [SELECT ID, Opportunity_Manufacturer_s__c FROM Opportunity
                          WHERE ID=: OppId];
        opp.Opportunity_Manufacturer_s__c = null;
        List<SBQQ__QuoteLine__c> lines = new List<SBQQ__QuoteLine__c>();
        lines = [SELECT ID, SBQQ__Product__r.Reported_Manufacturer__c, SBQQ__Quote__r.SBQQ__Opportunity2__r.Id
                FROM SBQQ__QuoteLIne__c WHERE SBQQ__Quote__r.SBQQ__Opportunity2__r.ID =: OppId AND 
                SBQQ__Quote__r.SBQQ__Primary__c = True];
        
        if(lines.size()>0){
            Set<String> mans = new Set<String>();
            for(SBQQ__QuoteLine__c linesToProcess : lines){
                if(!String.isBlank(linesToProcess.SBQQ__Product__r.Reported_Manufacturer__c)){
                    if(linesToProcess.SBQQ__Product__r.Reported_Manufacturer__c != 'Critical Start' && linesToProcess.SBQQ__Product__r.Reported_Manufacturer__c != 'INSTALL'){
                   mans.add(linesToProcess.SBQQ__Product__r.Reported_Manufacturer__c);
                    }
                }
            }
            
            if(!mans.isEmpty()){
                for(String man : mans){
                    if(!String.isBlank(opp.Opportunity_Manufacturer_s__c)){
                        opp.Opportunity_Manufacturer_s__c = opp.Opportunity_Manufacturer_s__c + ';' + man;
                    }
                    else{
                        opp.Opportunity_Manufacturer_s__c = man;
                    }
                }
            }
        }
        
        try{
            If(Limits.getQueueableJobs() > 0){
                String hour = String.valueOf(Datetime.now().hour());
                String min = String.valueOf(Datetime.now().minute());
                String ss = String.valueOf(Datetime.now().second() + 5);
                String nextFireTime = ss + '' + min + '' + hour + '**?';
                
                errorAggregateSched schProc = new errorAggregateSched();
                schProc.ProcessIds = oppList;
                System.schedule('errorAggregateSched', nextFireTime, schProc);
            }else{
            update opp;
            }
            
        }catch(Exception e){
            system.debug('Exception:::'+e.getMessage());
        }
        
    }

}

And the scheduled class that would be called if the limits error occurs:
 
public class errorAggregateSched  implements Schedulable {
    
    public List<ID> ProcessIds;
    
    public void execute(SchedulableContext sc) {
        System.enqueueJob(new AggregateManQueueableJob(ProcessIds));
        System.abortJob(sc.getTriggerId());
    }

}

And finally my test class:
 
@isTest(SeeAllData=True)
public class AggregateManufacturerInfo_Test {

     public static testMethod void tm1() {
        List<SBQQ__QuoteLine__c> quoteLines = new List<SBQQ__QuoteLine__c>();
        List<ID> oppsToProcess = new List<ID>();
        //First Test with Good Product and Reported Manufacturer
        Account a = new Account();
            a.Name = 'Test Account1';
            a.Website = 'www.test1class.com';
            a.Type = 'Prospect';
            a.Sub_type__c = 'End User';
            insert a;
        Contact c = new Contact();
            c.FirstName = 'TestClass1';
            c.LastName = 'TesterClass1';
            c.Email = 'testclass1@tester.com';
            c.Phone = '1112223398';
            c.Title = 'Test Title';
            c.AccountId = a.Id;
            insert c;
       // ID standardPB = test.getStandardPricebookId();
        Opportunity opp = new Opportunity();
            opp.Name = 'Test Opp 1';
            opp.Amount = 1000;
            opp.AccountId = a.Id;
            opp.Bill_To_Account__c = a.Id;
            opp.Bill_To_Contact__c = c.Id;
            Opp.CloseDate = Date.today();
            opp.StageName = '1-Qualification';
      //    opp.Pricebook2Id = standardPB;
            insert opp;
        Product2 p = new Product2();
            p.Name = 'TestProduct1';
            p.ProductCode = 'TestProduct1';
            p.Product_Code_Unique__c = 'TestProduct1';
            p.Department__c = 'MDR';
            p.Type__c = 'Service';
            p.Sales_Type__c = 'For Sale';
            p.IsActive = true;
            p.SBQQ__DefaultQuantity__c = 1;
            p.Manufacturer__c = 'Critical Start';
            p.Reported_Manufacturer__c = 'DEVO';
            p.SBQQ__PricingMethod__c = 'List';
            insert p;
        SBQQ__Quote__c qt = new SBQQ__Quote__c();
            qt.SBQQ__Type__c = 'Quote';
            qt.SBQQ__Status__c = 'Draft';
            qt.SBQQ__Opportunity2__c = opp.Id;
            qt.SBQQ__Primary__c = true;
            qt.SBQQ__Account__c = a.Id;
            qt.SBQQ__PrimaryContact__c = c.Id;
        //  qt.SBQQ__PricebookId__c = standardPB;
            insert qt;
        SBQQ__QuoteLine__c ql = new SBQQ__QuoteLine__c();
            ql.SBQQ__Product__c = p.Id;
            ql.Department__c = 'MDR';
            ql.Category__c = 'MDR-Bundle';
            ql.SBQQ__Quantity__c = 1;
            ql.SBQQ__Number__c = 1;
            ql.SBQQ__ListPrice__c = 100;
            ql.SBQQ__ProrateMultiplier__c = 1;
            ql.SBQQ__ProratedListPrice__c = 100;
            ql.SBQQ__SpecialPrice__c = 100;
            ql.SBQQ__ProratedPrice__c = 100;
            ql.SBQQ__NetPrice__c = 100;
            ql.SBQQ__DefaultSubscriptionTerm__c = 12;
            ql.SBQQ__SubscriptionPricing__c = 'Fixed Price';
            ql.SBQQ__ProductSubscriptionType__c = 'Renewable';
            ql.SBQQ__SubscriptionType__c = 'Renewable';
            ql.SBQQ__Quote__c = qt.Id;
          //  quoteLines.add(ql);
        //	oppsToProcess.add(opp.Id);
           insert ql;
        oppsToProcess.add(opp.Id);
        
       Test.startTest();
            AggregateManufacturerInfo.setOppMan(OppsToProcess);
         
                String CRON_EXP = '0 0 0 15 3 ? 2022';
                String hour = String.valueOf(Datetime.now().hour());
                String min = String.valueOf(Datetime.now().minute() + 1);
                String ss = String.valueOf(Datetime.now().second() + 5);
                String nextFireTime = ss + '' + min + '' + hour + '**?';
                
                errorAggregateSched schProc = new errorAggregateSched();
                schProc.ProcessIds = oppsToProcess;
                System.schedule('errorAggregateSched', CRON_EXP, schProc);
        Test.stopTest();
    }
    
}

Thanks again for any help you can provide as I am at my wits end here lol!