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
Megan Harris 18Megan Harris 18 

apex help: CPU time limit Error

Hello,
I'm still learning Apex and am having issues this this particular trigger.  We want to automate the creation of a renewal opportunity when an opportunity is moved to Closed won.  

Requirements
1. Opportunity is Won
2. Create a Renewal Opportunity with a close date of +365 days
3. Clone the primary quotes under the original Opportunity and associate them to the renewal Opportunity
4. Clone the Lines under the Original primary quotes and assoicate them to the cloned "renewal" quotes

Here is my code so far.  The error I'm running into is the CPU time limit error, and was gatting the too many SOQL queries earlier.  Your help is greatly appreciated!  

trigger Renewals2 on Opportunity (before update) {
    
    List <SCMC__Customer_Quotation__c> finalCustomerQuotations = new list <SCMC__Customer_Quotation__c>();
    List <SCMC__Customer_Quotation_Line__c> finalCQLines = new list <SCMC__Customer_Quotation_Line__c>();
    Map <ID,Opportunity> OrgOpptoRenewOpp = new Map <ID,Opportunity>();
    Map <ID,SCMC__Customer_Quotation__c> OrgCQtoRenewCQ = new Map <ID,SCMC__Customer_Quotation__c>();
    List <Opportunity> renewalOpps = new list <Opportunity>();
    
    for(Opportunity O : trigger.new){
        if (O.StageName == 'Closed - Won'){
            //Create a renewal Opportunities            
            Opportunity newOpp   = new Opportunity();
            newOpp.name          = O.name+'_RENEWAL';
            newOpp.Stagename     = 'Decision - 60%';
            newOpp.CloseDate     = O.CloseDate + 365;
            newOpp.Description   = 'Renewal';
            newOpp.OwnerId       = O.OwnerId;
            newOpp.AccountId     = O.AccountId;
            renewalOpps.add(newOpp);
            Insert renewalOpps;
            OrgOpptoRenewOpp.put(O.Id, newOpp);
            
            system.debug('This is the Org Opp id Map to new Opp ' + O.Id +' ' + newOpp);

            //Find the primary CQs
                List <SCMC__Customer_Quotation__c> OriginalCQs = [SELECT ID,
                                                                 SCMC__Customer_Account__c,
                                                                 SCMC__Sales_Rep__c,
                                                                 SCMC__Opportunity__c
                                                            FROM SCMC__Customer_Quotation__c
                                                           WHERE SCMC__Opportunity__c = :O.Id
                                                             AND Primary__c = true];
            System.debug('Number of primary CQs = '+ OriginalCQs.size());
            
            //Find the primary CQLines
            List <SCMC__Customer_Quotation_Line__c> OriginalLines = [SELECT ID,
                                                                               SCMC__Item_Number__c,
                                                                               SCMC__Quantity__c,
                                                                               SCMC__Extend_Description__c,
                                                                               SCMC__Lookup_Manufacturer__c,
                                                                               SCMC__Customer_Quotation__r.id
                                                                          FROM SCMC__Customer_Quotation_Line__c
                                                                         WHERE SCMC__Customer_Quotation__c IN :OriginalCQs];
            
            System.debug('Number of CQLines = ' + OriginalLines.size());
            
            //Clone Quotes and Lines
            
            for(SCMC__Customer_Quotation__c OriginalQuote : OriginalCQs){
                
                SCMC__Customer_Quotation__c renewalCQ = new SCMC__Customer_Quotation__c();
                renewalCQ.SCMC__Customer_Account__c     = OriginalQuote.SCMC__Customer_Account__c;
                renewalCQ.SCMC__Sales_Rep__c            = OriginalQuote.SCMC__Sales_Rep__c;
                renewalCQ.SCMC__Opportunity__c          = OrgOpptoRenewOpp.get(OriginalQuote.SCMC__Opportunity__c).Id;
                renewalCQ.Primary__c                    = TRUE;
                renewalCQ.SCMC__Quotation_Expires_On__c = Date.today() + 365;
                renewalCQ.RecordTypeId                  = '012C0000000Qb0x';
                finalCustomerQuotations.add(renewalCQ);
                OrgCQtoRenewCQ.put(OriginalQuote.Id, renewalCQ);
                
                system.debug('map of ORG CQ to Renewal CQ ' + OriginalQuote.Id + ' ' + renewalCQ);
                
            }
            
            Insert finalCustomerQuotations;
            
            for(SCMC__Customer_Quotation_Line__c OriginalLine : OriginalLines){
                SCMC__Customer_Quotation_Line__c renewalLine = new SCMC__Customer_Quotation_Line__c();
                renewalLine.SCMC__Item_Number__c             = OriginalLine.SCMC__Item_Number__c;
                renewalLine.SCMC__Quantity__c                = OriginalLine.SCMC__Quantity__c;
                renewalLine.SCMC__Extend_Description__c      = OriginalLine.SCMC__Extend_Description__c;
                renewalLine.SCMC__Lookup_Manufacturer__c     = OriginalLine.SCMC__Lookup_Manufacturer__c;
                renewalLine.SCMC__Customer_Quotation__c      = OrgCQtoRenewCQ.get(OriginalLine.SCMC__Customer_Quotation__c).id;
                finalCQLines.add(renewalLine);
                
            }
    
        }
        
        
        Insert finalCQLines;
        
        system.debug('number of renewal opps created '+ renewalOpps.size());
        system.debug('number of renewal CQs created '+  finalCustomerQuotations.size());
        system.debug('number of renewal opps created '+ finalCQLines.size());
        
    }
    
}
Chad.PfrommerChad.Pfrommer
Hi Megan - the first thing that I notice is that you've got a couple DML statements and SOQL queries inside of your outer for loop.  Triggers can be called with up to 200 records (pretty sure that's the number) and therefore should be "bulkified", meaning they'll function equally no matter how many records are in Trigger.new.  Your two SOQL queries will be executed for each loop iteration (for each Opp that has StageName == 'Closed - Won'), and then your two insert statements within that loop will also get executed every iteration.  And if those inserts end up firing other triggers/workflows/automation (your insert of the new Opp will cause this same trigger to fire again, for example, even though it won't do anything because the StageName won't be 'Closed - Won' for that newly inserted opp), all that other logic will get executed once in each loop iteration.  This can add up, and that's why we "bulkify" triggers.

Another thing is that I'm assuming you want to do this stuff only when the StageName is changed to Closed - Won.  Right now this logic will fire any time anyone updates an Opp that has StageName 'Closed - Won', whether the StageName was changed or not.  You probably want to include a check to make sure it was actually changed from some other value by getting the previous values from Trigger.old.

Those are the things that stick out to me after initial review.
Amit Chaudhary 8Amit Chaudhary 8
Try to remove DML and SOQL from for loop
trigger Renewals2 on Opportunity (before update) {
    
    List <SCMC__Customer_Quotation__c> finalCustomerQuotations = new list <SCMC__Customer_Quotation__c>();
    List <SCMC__Customer_Quotation_Line__c> finalCQLines = new list <SCMC__Customer_Quotation_Line__c>();
    Map <ID,Opportunity> OrgOpptoRenewOpp = new Map <ID,Opportunity>();
    Map <ID,SCMC__Customer_Quotation__c> OrgCQtoRenewCQ = new Map <ID,SCMC__Customer_Quotation__c>();
    List <Opportunity> renewalOpps = new list <Opportunity>();
    
	
	Set<Id> setOppID = new Set<Id>();
	
    for(Opportunity O : trigger.new)
	{
        if (O.StageName == 'Closed - Won')
		{
			setOppID.add(O.id);
			
            //Create a renewal Opportunities            
            Opportunity newOpp   = new Opportunity();
            newOpp.name          = O.name+'_RENEWAL';
            newOpp.Stagename     = 'Decision - 60%';
            newOpp.CloseDate     = O.CloseDate + 365;
            newOpp.Description   = 'Renewal';
            newOpp.OwnerId       = O.OwnerId;
            newOpp.AccountId     = O.AccountId;
            renewalOpps.add(newOpp);
			
            OrgOpptoRenewOpp.put(O.Id, newOpp);
		}
	}	
	
	if(setOppID.size() > 0)
	{
		if(renewalOpps.size() > 0 ){
            Insert renewalOpps;
		}
		
		//Find the primary CQs
		List <SCMC__Customer_Quotation__c> OriginalCQs = [	SELECT ID,
                                                                 SCMC__Customer_Account__c,
                                                                 SCMC__Sales_Rep__c,
                                                                 SCMC__Opportunity__c
                                                            FROM SCMC__Customer_Quotation__c
															WHERE SCMC__Opportunity__c = :O.Id
                                                            AND Primary__c = true];
															
            System.debug('Number of primary CQs = '+ OriginalCQs.size());
            
            //Find the primary CQLines
		List <SCMC__Customer_Quotation_Line__c> OriginalLines = [SELECT ID,
																	   SCMC__Item_Number__c,
																	   SCMC__Quantity__c,
																	   SCMC__Extend_Description__c,
																	   SCMC__Lookup_Manufacturer__c,
																	   SCMC__Customer_Quotation__r.id
																FROM SCMC__Customer_Quotation_Line__c
																WHERE SCMC__Customer_Quotation__c IN :OriginalCQs];
            
		System.debug('Number of CQLines = ' + OriginalLines.size());
            
		//Clone Quotes and Lines
            
		for(SCMC__Customer_Quotation__c OriginalQuote : OriginalCQs)
		{
			SCMC__Customer_Quotation__c renewalCQ = new SCMC__Customer_Quotation__c();
			renewalCQ.SCMC__Customer_Account__c     = OriginalQuote.SCMC__Customer_Account__c;
			renewalCQ.SCMC__Sales_Rep__c            = OriginalQuote.SCMC__Sales_Rep__c;
			renewalCQ.SCMC__Opportunity__c          = OrgOpptoRenewOpp.get(OriginalQuote.SCMC__Opportunity__c).Id;
			renewalCQ.Primary__c                    = TRUE;
			renewalCQ.SCMC__Quotation_Expires_On__c = Date.today() + 365;
			renewalCQ.RecordTypeId                  = '012C0000000Qb0x';
			finalCustomerQuotations.add(renewalCQ);
			OrgCQtoRenewCQ.put(OriginalQuote.Id, renewalCQ);
			system.debug('map of ORG CQ to Renewal CQ ' + OriginalQuote.Id + ' ' + renewalCQ);
		}
		Insert finalCustomerQuotations;
            
		for(SCMC__Customer_Quotation_Line__c OriginalLine : OriginalLines)
		{
			SCMC__Customer_Quotation_Line__c renewalLine = new SCMC__Customer_Quotation_Line__c();
			renewalLine.SCMC__Item_Number__c             = OriginalLine.SCMC__Item_Number__c;
			renewalLine.SCMC__Quantity__c                = OriginalLine.SCMC__Quantity__c;
			renewalLine.SCMC__Extend_Description__c      = OriginalLine.SCMC__Extend_Description__c;
			renewalLine.SCMC__Lookup_Manufacturer__c     = OriginalLine.SCMC__Lookup_Manufacturer__c;
			renewalLine.SCMC__Customer_Quotation__c      = OrgCQtoRenewCQ.get(OriginalLine.SCMC__Customer_Quotation__c).id;
			finalCQLines.add(renewalLine);
			
		}
		Insert finalCQLines;
		
		system.debug('number of renewal opps created '+ renewalOpps.size());
		system.debug('number of renewal CQs created '+  finalCustomerQuotations.size());
		system.debug('number of renewal opps created '+ finalCQLines.size());
    
    }
    
}


Let us know if this will help you
Megan Harris 18Megan Harris 18
Thank you both for your responses.  While, I know all of this in my head, I was having a hard time actually executing.  I think I have the bulkification worked, out, but now I'm having null pointer issues...go figure

The 2 lines in bold and underline are the ones they say are throwing hte null pointer error...any ideas on how to best dereference the null object there?  

Thanks for all your help!

            if(OriginalCQs.size()>0 ){
            for(SCMC__Customer_Quotation__c OriginalQuote : OriginalCQs){
                if(OriginalQuote.SCMC__Opportunity__c != null){
                SCMC__Customer_Quotation__c renewalCQ = new SCMC__Customer_Quotation__c();
                renewalCQ.SCMC__Customer_Account__c     = OriginalQuote.SCMC__Customer_Account__c;
                renewalCQ.SCMC__Sales_Rep__c            = OriginalQuote.SCMC__Sales_Rep__c;
                renewalCQ.SCMC__Opportunity__c          = oppToRenewal.get(OriginalQuote.SCMC__Opportunity__c).Id;
                renewalCQ.Primary__c                    = TRUE;
                renewalCQ.SCMC__Quotation_Expires_On__c = Date.today() + 365;
                renewalCQ.RecordTypeId                  = '012C0000000Qb0x';        
                renewalCQs.add(renewalCQ);
                cqToRenewalCQ.put(OriginalQuote.id, renewalCQ);}
            }}
            if(renewalCQs.size()>0 && renewalCQs != null){
            Insert renewalCQs;
            system.debug('Number of renewal CQs created = '+ renewalCQs.size());
Amit Chaudhary 8Amit Chaudhary 8
Try to update your code like this
trigger Renewals2 on Opportunity (before update) {
    
    List <SCMC__Customer_Quotation__c> finalCustomerQuotations = new list <SCMC__Customer_Quotation__c>();
    List <SCMC__Customer_Quotation_Line__c> finalCQLines = new list <SCMC__Customer_Quotation_Line__c>();
    Map <ID,Opportunity> OrgOpptoRenewOpp = new Map <ID,Opportunity>();
    Map <ID,SCMC__Customer_Quotation__c> OrgCQtoRenewCQ = new Map <ID,SCMC__Customer_Quotation__c>();
    List <Opportunity> renewalOpps = new list <Opportunity>();
    
	
	Set<Id> setOppID = new Set<Id>();
	
    for(Opportunity O : trigger.new)
	{
        if (O.StageName == 'Closed - Won')
		{
			setOppID.add(O.id);
			
            //Create a renewal Opportunities            
            Opportunity newOpp   = new Opportunity();
            newOpp.name          = O.name+'_RENEWAL';
            newOpp.Stagename     = 'Decision - 60%';
            newOpp.CloseDate     = O.CloseDate + 365;
            newOpp.Description   = 'Renewal';
            newOpp.OwnerId       = O.OwnerId;
            newOpp.AccountId     = O.AccountId;
            renewalOpps.add(newOpp);
			
            OrgOpptoRenewOpp.put(O.Id, newOpp);
		}
	}	
	
	if(setOppID.size() > 0)
	{
		if(renewalOpps.size() > 0 ){
            Insert renewalOpps;
		}
		
		//Find the primary CQs
		List <SCMC__Customer_Quotation__c> OriginalCQs = [	SELECT ID,
                                                                 SCMC__Customer_Account__c,
                                                                 SCMC__Sales_Rep__c,
                                                                 SCMC__Opportunity__c
                                                            FROM SCMC__Customer_Quotation__c
															WHERE SCMC__Opportunity__c in :setOppID
                                                            AND Primary__c = true];
															
            System.debug('Number of primary CQs = '+ OriginalCQs.size());
            
            //Find the primary CQLines
		List <SCMC__Customer_Quotation_Line__c> OriginalLines = [SELECT ID,
																	   SCMC__Item_Number__c,
																	   SCMC__Quantity__c,
																	   SCMC__Extend_Description__c,
																	   SCMC__Lookup_Manufacturer__c,
																	   SCMC__Customer_Quotation__r.id
																FROM SCMC__Customer_Quotation_Line__c
																WHERE SCMC__Customer_Quotation__c IN :OriginalCQs];
            
		System.debug('Number of CQLines = ' + OriginalLines.size());
            
		//Clone Quotes and Lines
            
		for(SCMC__Customer_Quotation__c OriginalQuote : OriginalCQs)
		{
			if(OrgOpptoRenewOpp.containsKey(OriginalQuote.SCMC__Opportunity__c))
			{
				SCMC__Customer_Quotation__c renewalCQ = new SCMC__Customer_Quotation__c();
				renewalCQ.SCMC__Customer_Account__c     = OriginalQuote.SCMC__Customer_Account__c;
				renewalCQ.SCMC__Sales_Rep__c            = OriginalQuote.SCMC__Sales_Rep__c;
				renewalCQ.SCMC__Opportunity__c          = OrgOpptoRenewOpp.get(OriginalQuote.SCMC__Opportunity__c).Id;
				renewalCQ.Primary__c                    = TRUE;
				renewalCQ.SCMC__Quotation_Expires_On__c = Date.today() + 365;
				renewalCQ.RecordTypeId                  = '012C0000000Qb0x';
				finalCustomerQuotations.add(renewalCQ);
				OrgCQtoRenewCQ.put(OriginalQuote.Id, renewalCQ);
				system.debug('map of ORG CQ to Renewal CQ ' + OriginalQuote.Id + ' ' + renewalCQ);
			}	
		}
		Insert finalCustomerQuotations;
            
		for(SCMC__Customer_Quotation_Line__c OriginalLine : OriginalLines)
		{
			if(OrgCQtoRenewCQ.containsKey(OriginalLine.SCMC__Customer_Quotation__c))
			{
				SCMC__Customer_Quotation_Line__c renewalLine = new SCMC__Customer_Quotation_Line__c();
				renewalLine.SCMC__Item_Number__c             = OriginalLine.SCMC__Item_Number__c;
				renewalLine.SCMC__Quantity__c                = OriginalLine.SCMC__Quantity__c;
				renewalLine.SCMC__Extend_Description__c      = OriginalLine.SCMC__Extend_Description__c;
				renewalLine.SCMC__Lookup_Manufacturer__c     = OriginalLine.SCMC__Lookup_Manufacturer__c;
				renewalLine.SCMC__Customer_Quotation__c      = OrgCQtoRenewCQ.get(OriginalLine.SCMC__Customer_Quotation__c).id;
				finalCQLines.add(renewalLine);
			}	
		}
		Insert finalCQLines;
		
		system.debug('number of renewal opps created '+ renewalOpps.size());
		system.debug('number of renewal CQs created '+  finalCustomerQuotations.size());
		system.debug('number of renewal opps created '+ finalCQLines.size());
    
    }
    
}

Let us know if this will help you
Chad.PfrommerChad.Pfrommer
Hi Megan - while I'm not going to write your code for you ;) I'm happy to try to help.

The first line:
oppToRenewal.get(OriginalQuote.SCMC__Opportunity__c).Id
If there's no value stored in the "oppToRenewal" map for the key "OriginalQuote.SCMC__Opportunity__c" then you'll get null back.  If you try to access ".Id" on null - NullPointerException.

The second line:
Insert renewalCQs;
Seems odd that would be giving you an NPE since you're checking to make sure "renewalCQs" isn't null right before.  Having said that, the way your "if" conditional is written the "renewalCQs.size() > 0" part will be evaluated before the "renewalCQs != null" part (left to right in this case), so if renewalCQs is null then you'll get an NPE there (calling the "size" method on a null reference).
Megan Harris 18Megan Harris 18
Thank you both for all of the help!  Chad - I appreciate the explaination more that if you would write the code because I really do want to learn it.  I've got this working now and have a test class with 100% coverage, yay!