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
Stuart HARRISONStuart HARRISON 

Create renewal opportunity based on a future date

Ive been looking at Apex Triggers to clone an opportunity as a "renewal opportunity" - to be created 3 months prior to the policy end date.

can this be done with an Apex Trigger ?

We have a custom object (Policy) which is created by a custom button available once the opportunity is Closed - Won. The Policy holds the name of the original opportunity. 3 months prior to policy completion im after a new opportunity cloned from the original with the year section of the name changed (first 4 digits).

Possible ?
Abhishek BansalAbhishek Bansal
Hi Stuart,

As per my understanding, you need to create a new opportunity as soon as the policy is created on the source opportunity. The new opportunity will be the clone of the source opportunity with just some modification in the name. If this is what your requiremnet is, then,yes it is possible with the help of the apex trigger.
Let me know if you need any help with the apex trigger.

Thanks,
Abhishek Bansal.
Stuart HARRISONStuart HARRISON
Thanks for the reply. I have a working Apex Trigger to clone an opportunity as a renewal opportunity.

What I'd really like is for the opportunity not to be created straight away, but instead be created 3 months prior to the insured_to date. Im not sure i can trigger an apex trigger on a future date ?

Additionally I'd like for the clone to not add the "_copy" to the name - I'd prefer to change the first 4 digits to the insure from year but I cant seem to get any code for this to work:

trigger Renewal on Opportunity (before update) {
if(Trigger.isUpdate){

List<Opportunity> listOppor = new List<Opportunity>();
for (Opportunity o: Trigger.new){

    if (o.StageName == 'Closed - Won' && o.Renewal_Created__c == null){
        Opportunity oppNew = o.clone();
//        oppNew.Name = oppNew.Name + '_Renewal';
        oppNew.StageName = 'New';
        oppNew.Renewal_Indicator__c = true;
        OppNew.CloseDate = OppNew.CloseDate.addyears(1);
        OppNew.Insured_period_from__c = OppNew.Insured_period_from__c.addyears(1);

        OppNew.Insured_period_to__c = OppNew.Insured_period_to__c.addyears(1);
        OppNew.OwnerID = o.CreatedByID;
        o.Renewal_Created__c = 'Yes';
        Oppnew.Renewed_Opportunity__c = oppNew.Name;
        OppNew.Description = OppNew.Description + ' Original S6130 Line Size was : ' + OppNew.S6130_Line_Size__c;
        listOppor.add(oppNew);
    }// end if
    
}//end for

    if(listOppor.size() > 0){
       insert listOppor;
     }//end if
}//end if
Abhishek BansalAbhishek Bansal
Hi Stuart,

Yes, you are right, we can not schedule a trigger to execute on a future date but however we can craete a schedulable class that will run on a particular basis. In this case, we can write a scheduler class that will run daily and will clone the opportunity whose insured_to date will be after 3 months. In that scheduler class you can also adjust the name of the new opportunity as per your requirement. Let me know if you need any help with the scheduler class.

Thanks,
Abhishek Bansal.
Stuart HARRISONStuart HARRISON
Many thanks. Ive never tried creating a scheduler class ? not sure I would know where to start ?
 
Abhishek BansalAbhishek Bansal
Hi Stuart,

You can learn how to write the batch class and how to schedule it from the trailhead link given below:
https://trailhead.salesforce.com/en/modules/asynchronous_apex/units/async_apex_batch

If you want to me to write a scheduler class for this requirement then you can contact me on
Gmail: abhibansal2790@gmail.com
Skype: abhishek.bansal2790

Thanks,
Abhishek Bansal.
Stuart HARRISONStuart HARRISON
Ive created an apex class but:
1) I cant get the "add" function to save/compile (in bold)
2) I cant see this class in the list available for scheduling ?

Code is below - Im not sure its fully complete.

global class ScheduleRenewal implements Database.Batchable<SObject>
{
     global Database.queryLocator start(Database.BatchableContext ctx)
     {
        return Database.getQueryLocator([Select Opportunity.Name, Opportunity.StageName, opportunity.Renewal_Created__c, opportunity.Insured_period_from__c, opportunity.Insured_period_to__c From Opportunity where Opportunity.Insured_Period_to__c >= LAST_N_DAYS:60 AND Opportunity.Renewal_Created__c != 'YES']);

     }
     global void execute(Database.BatchableContext BC, List<opportunity> opportunityList)
     {
          //for creating new opportunity.
            List<Opportunity> newOpps=new List<Opportunity>();

            for(Opportunity c: opportunityList)
            {

    if (c.StageName == 'Closed - Won'){
        Opportunity opp = c.clone();
//        oppNew.Name = OppNew.Insured_period_from__c.year & oppNew.Name.substringadter('_');
            opp.Name = c.Name + '_Renewal';

            opp.StageName = 'New';
            opp.Renewal_Indicator__c = true;

            Opp.Insured_period_from__c = c.Insured_period_to__c;
            Opp.Insured_period_to__c = Opp.Insured_period_from__c.adddays(c.Insured_period_to__c.daysbetween(c.Insured_period_from__c));
            Opp.CloseDate = Opp.Insured_period_from__c.addmonths(-1);
       
            Opp.OwnerID = c.CreatedByID;
            c.Renewal_Created__c = 'Yes';
            Opp.Previously_won_or_lost__c = 'Won';
            Opp.Renewed_Opportunity__c = opp.Name;
            Opp.Renewal_Information__c = 'Original S6130 Line Size was : ' + Opp.S6130_Line_Size__c;
}
    if (c.StageName == 'Closed - Lost'){
        Opportunity opp = c.clone();
//        oppNew.Name = OppNew.Insured_period_from__c.year & oppNew.Name.substringadter('_');
            opp.Name = c.Name + '_Renewal';

            opp.StageName = 'New';
            opp.Renewal_Indicator__c = true;

            Opp.Insured_period_from__c = c.Insured_period_to__c;
            Opp.Insured_period_to__c = Opp.Insured_period_from__c.adddays(c.Insured_period_to__c.daysbetween(c.Insured_period_from__c));
            Opp.CloseDate = Opp.Insured_period_from__c.addmonths(-1);
       
            Opp.OwnerID = c.CreatedByID;
            c.Renewal_Created__c = 'Yes';
            Opp.Previously_won_or_lost__c = 'Lost';
            Opp.Renewed_Opportunity__c = opp.Name;
            Opp.Renewal_Information__c = 'Lost Reason : ' + c.Lost_Cancel_Reason__c + ' - comment : ' + c.Lost_Cancelled_Reason_Comment__c + ' - Pricing : ' + c.Lost_Due_to_Pricing_Currency__c + ' ' + c.Lost_Due_to_Pricing_amount__c;
            Opp.Lost_Cancel_Reason__c = null;
            Opp.Lost_Cancelled_Reason_Comment__c = null;
            Opp.Lost_Due_to_Pricing_Currency__c = null;
            Opp.Lost_Due_to_Pricing_amount__c = null;
  }

  //     newOpps.add(Opp);
            }
       insert newOpps;
     }
          global void finish(Database.BatchableContext BC)
     {

     }
}
Abhishek BansalAbhishek Bansal
Hi Stuart,

What is the error you are facing while saving the code highlighted in Bold and you need to create a scheduler class for this batch class in order to get it scheduled.
Please find help from link given below:
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_scheduler.htm

Thanks,
Abhishek Bansal.
Stuart HARRISONStuart HARRISON
if i uncomment the line i get
Error: Compile Error: Variable does not exist: Opp at line 57 column 20
Abhishek BansalAbhishek Bansal
Hi Stuart,

Since the variable is declared inside the if block so it would not be available outside the block. Please move this line inside the if block as mentioned below:
Opp.Lost_Cancelled_Reason_Comment__c = null;
Opp.Lost_Due_to_Pricing_Currency__c = null;
Opp.Lost_Due_to_Pricing_amount__c = null;
newOpps.add(Opp);

Let me know if you need any further help on this.
 
Thanks,
Abhishek Bansal.
 
Stuart HARRISONStuart HARRISON
Thanks for all your help so far :)

I have managed to create an apex class that saves, and have managed to add it into a schedule which has run - but it did nothing. Have i closed off the job before it actually gets to do anything ? Or am I not writing the opportunities at the end ? Its a hybrid from examples I have found and a trigeer that was working successfully. im not sure if i need to change the global database line ?

global class ScheduleRenewal implements Schedulable { 
   global void execute(SchedulableContext ctx) { 
}
     global Database.queryLocator start(Database.BatchableContext ctx)
     {
        return Database.getQueryLocator([Select Opportunity.Name, Opportunity.StageName, opportunity.Renewal_Created__c, opportunity.Insured_period_from__c, opportunity.Insured_period_to__c From Opportunity where Opportunity.Insured_Period_to__c >= LAST_N_DAYS:60 AND Opportunity.Renewal_Created__c != 'YES']);

     }
     global void execute(Database.BatchableContext BC, List<opportunity> opportunityList)
     {
          //for creating new opportunity.
            List<Opportunity> newOpps=new List<Opportunity>();

            for(Opportunity c: opportunityList)
            {

    if (c.StageName == 'Closed - Won'){
        Opportunity opp = c.clone();
//        oppNew.Name = OppNew.Insured_period_from__c.year & oppNew.Name.substringadter('_');
            opp.Name = c.Name + '_Renewal';

            opp.StageName = 'New';
            opp.Renewal_Indicator__c = true;

            Opp.Insured_period_from__c = c.Insured_period_to__c;
            Opp.Insured_period_to__c = Opp.Insured_period_from__c.adddays(c.Insured_period_to__c.daysbetween(c.Insured_period_from__c));
            Opp.CloseDate = Opp.Insured_period_from__c.addmonths(-1);
        
            Opp.OwnerID = c.CreatedByID;
            c.Renewal_Created__c = 'Yes';
            Opp.Previously_won_or_lost__c = 'Won';
            Opp.Renewed_Opportunity__c = opp.Name;
            Opp.Renewal_Information__c = 'Original S6130 Line Size was : ' + Opp.S6130_Line_Size__c;
            newOpps.add(Opp);
}
    if (c.StageName == 'Closed - Lost'){
        Opportunity opp = c.clone();
//        oppNew.Name = OppNew.Insured_period_from__c.year & oppNew.Name.substringadter('_');
            opp.Name = c.Name + '_Renewal';

            opp.StageName = 'New';
            opp.Renewal_Indicator__c = true;

            Opp.Insured_period_from__c = c.Insured_period_to__c;
            Opp.Insured_period_to__c = Opp.Insured_period_from__c.adddays(c.Insured_period_to__c.daysbetween(c.Insured_period_from__c));
            Opp.CloseDate = Opp.Insured_period_from__c.addmonths(-1);
        
            Opp.OwnerID = c.CreatedByID;
            c.Renewal_Created__c = 'Yes';
            Opp.Previously_won_or_lost__c = 'Lost';
            Opp.Renewed_Opportunity__c = opp.Name;
            Opp.Renewal_Information__c = 'Lost Reason : ' + c.Lost_Cancel_Reason__c + ' - comment : ' + c.Lost_Cancelled_Reason_Comment__c + ' - Pricing : ' + c.Lost_Due_to_Pricing_Currency__c + ' ' + c.Lost_Due_to_Pricing_amount__c;
            Opp.Lost_Cancel_Reason__c = null;
            Opp.Lost_Cancelled_Reason_Comment__c = null;
            Opp.Lost_Due_to_Pricing_Currency__c = null;
            Opp.Lost_Due_to_Pricing_amount__c = null;
            newOpps.add(Opp);
  }

  //     newOpps.add(Opp);
            }
       insert newOpps;
     }
          global void finish(Database.BatchableContext BC)
     {

     }
}
Abhishek BansalAbhishek Bansal
Make sure that your query is returning the results that needs to be processed. Other than that everything seems to be fine. You can use debug statements and find out what is not working.
Stuart HARRISONStuart HARRISON
Ive trimmed the query right back and am still getting nothing. Ive tried executing in the developer console and it says no osql queries and i cant see my messages anywhere. Is it stopping before it gets to the database query ?

global class ScheduleRenewal implements Schedulable { 
   global void execute(SchedulableContext ctx) { 
}

     global Database.queryLocator start(Database.BatchableContext ctx)
     {
        return Database.getQueryLocator([Select Opportunity.Name, Opportunity.StageName, opportunity.Renewal_Created__c, 
        opportunity.Insured_period_from__c, opportunity.Insured_period_to__c From Opportunity 
     ]);

     }
     global void execute(Database.BatchableContext BC, List<opportunity> opportunityList)
     {
          //for creating new opportunity.
            List<Opportunity> newOpps=new List<Opportunity>();

            for(Opportunity c: opportunityList)
            {

    System.Debug('message 1');
    System.Debug(c.StageName );
    
    if (c.StageName == 'Closed - Won'){
        Opportunity opp = c.clone();
//        oppNew.Name = OppNew.Insured_period_from__c.year & oppNew.Name.substringadter('_');
            opp.Name = c.Name + '_Renewal';

            opp.StageName = 'New';
            opp.Renewal_Indicator__c = true;

            Opp.Insured_period_from__c = c.Insured_period_to__c;
            Opp.Insured_period_to__c = Opp.Insured_period_from__c.adddays(c.Insured_period_to__c.daysbetween(c.Insured_period_from__c));
            Opp.CloseDate = Opp.Insured_period_from__c.addmonths(-1);
        
            Opp.OwnerID = c.CreatedByID;
            c.Renewal_Created__c = 'Yes';
            Opp.Previously_won_or_lost__c = 'Won';
            Opp.Renewed_Opportunity__c = opp.Name;
            Opp.Renewal_Information__c = 'Original S6130 Line Size was : ' + Opp.S6130_Line_Size__c;
            newOpps.add(Opp);
 System.Debug('message 2');
}
    if (c.StageName == 'Closed - Lost'){
        Opportunity opp = c.clone();
//        oppNew.Name = OppNew.Insured_period_from__c.year & oppNew.Name.substringadter('_');
            opp.Name = c.Name + '_Renewal';

            opp.StageName = 'New';
            opp.Renewal_Indicator__c = true;

            Opp.Insured_period_from__c = c.Insured_period_to__c;
            Opp.Insured_period_to__c = Opp.Insured_period_from__c.adddays(c.Insured_period_to__c.daysbetween(c.Insured_period_from__c));
            Opp.CloseDate = Opp.Insured_period_from__c.addmonths(-1);
        
            Opp.OwnerID = c.CreatedByID;
            c.Renewal_Created__c = 'Yes';
            Opp.Previously_won_or_lost__c = 'Lost';
            Opp.Renewed_Opportunity__c = opp.Name;
            Opp.Renewal_Information__c = 'Lost Reason : ' + c.Lost_Cancel_Reason__c + ' - comment : ' + c.Lost_Cancelled_Reason_Comment__c + ' - Pricing : ' + c.Lost_Due_to_Pricing_Currency__c + ' ' + c.Lost_Due_to_Pricing_amount__c;
            Opp.Lost_Cancel_Reason__c = null;
            Opp.Lost_Cancelled_Reason_Comment__c = null;
            Opp.Lost_Due_to_Pricing_Currency__c = null;
            Opp.Lost_Due_to_Pricing_amount__c = null;
            newOpps.add(Opp);
 System.Debug('message 3');
  }

             }
       insert newOpps;
     }
           global void finish(Database.BatchableContext BC)
     {
  System.Debug('message 4');
     }
}
Abhishek BansalAbhishek Bansal
Hi Stuart,

Your batch class seems absolutely fine. Just need to take care of the Schedule class and how to schedule the batch. Please follow the steps below:
Save you batch class with all the query filters as per your requirement. Find  the class below:
global class ScheduleRenewal implements Database.Batchable<SObject>
{
    global Database.queryLocator start(Database.BatchableContext ctx)
    {
        return Database.getQueryLocator([Select Opportunity.Name, Opportunity.StageName, opportunity.Renewal_Created__c, opportunity.Insured_period_from__c, opportunity.Insured_period_to__c From Opportunity where Opportunity.Insured_Period_to__c >= LAST_N_DAYS:60 AND Opportunity.Renewal_Created__c != 'YES']);
        
    }
    global void execute(Database.BatchableContext BC, List<opportunity> opportunityList)
    {
        //for creating new opportunity.
        List<Opportunity> newOpps=new List<Opportunity>();
        
        for(Opportunity c: opportunityList)
        {
            
            if (c.StageName == 'Closed - Won'){
                Opportunity opp = c.clone();
                //oppNew.Name = OppNew.Insured_period_from__c.year & oppNew.Name.substringadter('_');
                opp.Name = c.Name + '_Renewal';
                
                opp.StageName = 'New';
                opp.Renewal_Indicator__c = true;
                
                Opp.Insured_period_from__c = c.Insured_period_to__c;
                Opp.Insured_period_to__c = Opp.Insured_period_from__c.adddays(c.Insured_period_to__c.daysbetween(c.Insured_period_from__c));
                Opp.CloseDate = Opp.Insured_period_from__c.addmonths(-1);
                
                Opp.OwnerID = c.CreatedByID;
                c.Renewal_Created__c = 'Yes';
                Opp.Previously_won_or_lost__c = 'Won';
                Opp.Renewed_Opportunity__c = opp.Name;
                Opp.Renewal_Information__c = 'Original S6130 Line Size was : ' + Opp.S6130_Line_Size__c;
				newOpps.add(Opp);
            }
            if (c.StageName == 'Closed - Lost'){
                Opportunity opp = c.clone();
                //oppNew.Name = OppNew.Insured_period_from__c.year & oppNew.Name.substringadter('_');
                opp.Name = c.Name + '_Renewal';
                
                opp.StageName = 'New';
                opp.Renewal_Indicator__c = true;
                
                Opp.Insured_period_from__c = c.Insured_period_to__c;
                Opp.Insured_period_to__c = Opp.Insured_period_from__c.adddays(c.Insured_period_to__c.daysbetween(c.Insured_period_from__c));
                Opp.CloseDate = Opp.Insured_period_from__c.addmonths(-1);
                
                Opp.OwnerID = c.CreatedByID;
                c.Renewal_Created__c = 'Yes';
                Opp.Previously_won_or_lost__c = 'Lost';
                Opp.Renewed_Opportunity__c = opp.Name;
                Opp.Renewal_Information__c = 'Lost Reason : ' + c.Lost_Cancel_Reason__c + ' - comment : ' + c.Lost_Cancelled_Reason_Comment__c + ' - Pricing : ' + c.Lost_Due_to_Pricing_Currency__c + ' ' + c.Lost_Due_to_Pricing_amount__c;
                Opp.Lost_Cancel_Reason__c = null;
                Opp.Lost_Cancelled_Reason_Comment__c = null;
                Opp.Lost_Due_to_Pricing_Currency__c = null;
                Opp.Lost_Due_to_Pricing_amount__c = null;
				newOpps.add(Opp);
            }
        }
        insert newOpps;
    }
    global void finish(Database.BatchableContext BC)
    {
        
    }
}
Now create a new class i.e. Scheduler class with below code:
global class ScheduleBatchToCreateRenewalOpportunity implements Schedulable{
         
  global void execute(SchedulableContext sc) {  
      ScheduleRenewal batch = new ScheduleRenewal();
	  //the default batch size is 200, you can adjust it as per your requirement. For this I have make it 10
      Id batchId = database.executeBatch(batch, 10);
  }
}

Now run the following command in the Developer Console:
ScheduleBatchToCreateRenewalOpportunity sec = new ScheduleBatchToCreateRenewalOpportunity();  
String cronStr = '	0 0 13 1/1 * ? *';                            
System.schedule('Create Renewal Opportunity', cronStr, sec);
CronStr represents that how you want to schedule your batch. In this case, your batch will run daily at 1:00 PM. You can modify the crone expression by using the following link. Means if you want to change the running time of your batch then you can generate the crone string from link givenn below:
http://www.cronmaker.com/

Once you run the above statements in the developer console then your batch is successfully scheduled. You can check the scheduled jobs to see that your batch is scheduked to run daily at 1:00  PM with 'Create Renewal Opportunity' name.

Once the batch is run you can check the history of the batch in the Apex Jobs. This will show you how many batches have been processed by the class and how many are successfull.

Please let me know if you need any further information on this.

Thanks,
Abhishek Bansal.