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
theitdeptrockstheitdeptrocks 

Reset field to '0' on first day of each quarter

Hi all, I have a trigger that increments a number field on my Opportunity object for each new Case that is created for that Opp. The field name is 'Cases this Quarter.' How would I go about resetting this count to '0' on the first day of each quarter? Thanks in advance!
Best Answer chosen by Admin (Salesforce Developers) 
VinOKVinOK

Oops..  sorry..  I didn't mean to confuse you with the System Log > Execute Apex tool..   So let me be very explicit....

 

First create a class (Setup > Develop > Apex Classes > New) and paste in the contents of your resetCasesthisQuarter class.  

 

Then create another class and fill that in with the contents of your resetCasesthisQuarterSched class. Although you should modify it slightly like below:

 

 

global class resetCasesthisQuarterSched implements Schedulable{
   global void execute(SchedulableContext SC) {
      resetCasesthisQuarter r = new resetCasesthisQuarter();
      r.getPlansToReset();
      r.resetCount();
   } 
}

 

 

then you can schedule the resetCasesthisQuarterSched  class to run the first of every month.

 

 

Alternatively...  if you wanted to one off force the code to run you could open the System Log window and in the Execute Apex section you could put in :

 

 

resetCasesthisQuarter r = new resetCasesthisQuarter();
r.getPlansToReset();
r.resetCount();

 and click run, which would force it to run.  Doing this would actually update your opportunity records too.   (I know earlier I said nothing would happen, but I read the Help and Training on that page wrong..  :-)  )

 

Hope that clears things up.

 

 

 

All Answers

VinOKVinOK

Well you can always write a schedulable apex class and have it run on the first of every month that could go through every opportunity class and reset that number to 0.  You probably should also make this code batchable too if the number of opportunity records you need to reset is "large".

 

 

FYI..

I'm guessing that the relationship between your case object and opportunity is not Master-Detail.  If it is though....   A Rollup Summary field would probably be the way to go.

theitdeptrockstheitdeptrocks
You are correct that the relationship between our Cases and Opportunity is not Master-Detail. I originally wanted to create a Rollup Summary field, but found the option to be greyed out. SF support informed me that Master-Detail relationships can only be created for custom objects. I'm going to do some research on schedulable apex classes to see what I can find out. Is "large" 600-800 records?
VinOKVinOK

For 600-800 records you can probably perform all your logic in one run without hitting any governor limits, so you can probably get away with not implementing "batchable apex" right now.  

 

If this number ever gets close to 10k you may way to re-consider as your Apex code will just suddenly stop working due to governor limits.  if you have extra time, you should probably at least read up on governor limits and Batchable Apex just to know what they are.

theitdeptrockstheitdeptrocks

Thanks VinOK,

 

I think I've got it setup, but will see in about 20 minutes.

 

I'm concerned about two things:

1)  Will it work as is? - We'll find out soon enough

2)  Is my update statement the right way to update each record?

 

 

public with sharing class resetCasesthisQuarter {
	private List<Opportunity> PlansToReset;
    
	
	public list<Opportunity> getPlansToReset(){
		PlansToReset = [Select id, Cases_this_Quarter__c 
						from Opportunity 
						where Cases_this_Quarter__c = 1 AND 
								(StageName = 'Live' OR 
								 StageName = 'Live (Plan Termination)' OR 
								 StageName = 'Live (Service Termination')];
		return PlansToReset;
	}
	
	public void resetCount(){
		integer x = PlansToReset.size();
		integer y = 0;
		for(y=0; y<=x; y++){
			PlansToReset[x].Cases_this_Quarter__c=0;
			update PlansToReset[x];
		}
	}
}

 

 

VinOKVinOK

you should probably take the "Update" out of the loop as each update call counts against the governor limit, and I believe that scheduled Apex runs with the "Anonymous Block, Visualforce Controller, or WSDL Method" governor limits, so you can only call update 100 times max.

 

you should probably replace your resetCount method with something that looks more like this.

 

 

public void resetCount(){
   for( Opportunity currOpty: PlansToReset ){
      currOpty.Cases_this_Quarter__c = 0;
   }

   update PlansToReset;
}

 The idea is to collect all updated records into a list and then call update once with a big list.

 

 

 

theitdeptrockstheitdeptrocks

Ok great!  I didn't know a 'for' could be done like that (I'm more admin that developer, but learning!).

 

I have updated my code - can a class be manually ran or do I have to reschedule and wait?

VinOKVinOK

 

well, if you wanted to test that code runs you can force it by clicking on the System Log link next to the setup link and type in code into the Execute Apex box...

BUT....  it's really only meant to test code, as any record inserts / updates don't get committed to the database.

So.. you should probably just reschedule and wait.

 

 

 

Sorry..  I think i read the Help and Training page incorrectly.   Using the System Log > Execute Apex box actually executes the apex code and saves any DML inserts / updates / deletes that are made.

theitdeptrockstheitdeptrocks

Drats... it didn't work.

 

Here's the class I have scheduled:

 

global class resetCasesthisQuarterSched implements Schedulable{
   global void execute(SchedulableContext SC) {
      resetCasesthisQuarter r = new resetCasesthisQuarter(); 
   } 
}

Putting that into the "System Log" execute area returns:  Compile Error: line 1, column 14: Global type must be contained inside of a global class

 

Then for confirmation, my resetCasesthisQuarter class looks like this:

public with sharing class resetCasesthisQuarter {
	private List<Opportunity> PlansToReset;
	
	public list<Opportunity> getPlansToReset(){
		PlansToReset = [Select id, Cases_this_Quarter__c 
						from Opportunity 
						where 	(StageName = 'Live' OR 
							 StageName = 'Live (Plan Termination)' OR 
							 StageName = 'Live (Service Termination')];
		return PlansToReset;
	} 
	
	public void resetCount(){
	   for( Opportunity currOpty: PlansToReset ){
	      currOpty.Cases_this_Quarter__c = 0;
	   }
	
	   update PlansToReset;
	}
}

Putting that into the "System Log" execute area returns:  Last execution at 07/22 16:11:12 in 30 milliseconds.

 

And then a log entry:  

19.0 APEX_CODE,DEBUG;APEX_PROFILING,INFO;CALLOUT,INFO;DB,INFO;VALIDATION,INFO;WORKFLOW,INFO
Execute Anonymous: public with sharing class resetCasesthisQuarter {
Execute Anonymous: private List<Opportunity> PlansToReset;
Execute Anonymous:
Execute Anonymous: public list<Opportunity> getPlansToReset(){
Execute Anonymous: PlansToReset = [Select id, Cases_this_Quarter__c
Execute Anonymous: from Opportunity
Execute Anonymous: where Cases_this_Quarter__c = 1 AND
Execute Anonymous: (StageName = 'Live' OR
Execute Anonymous: StageName = 'Live (Plan Termination)' OR
Execute Anonymous: StageName = 'Live (Service Termination')];
Execute Anonymous: return PlansToReset;
Execute Anonymous: }
Execute Anonymous:
Execute Anonymous: public void resetCount(){
Execute Anonymous: for( Opportunity currOpty: PlansToReset ){
Execute Anonymous: currOpty.Cases_this_Quarter__c = 0;
Execute Anonymous: }
Execute Anonymous:
Execute Anonymous: update PlansToReset;
Execute Anonymous: }
Execute Anonymous: }
16:11:12.618|EXECUTION_STARTED
16:11:12.618|CODE_UNIT_STARTED|[EXTERNAL]|execute_anonymous_apex
16:11:12.620|CUMULATIVE_LIMIT_USAGE
16:11:12.620|CUMULATIVE_LIMIT_USAGE_END

16:11:12.620|CODE_UNIT_FINISHED|execute_anonymous_apex
16:11:12.620|EXECUTION_FINISHED

 


 


 

 

 

 

VinOKVinOK

Oops..  sorry..  I didn't mean to confuse you with the System Log > Execute Apex tool..   So let me be very explicit....

 

First create a class (Setup > Develop > Apex Classes > New) and paste in the contents of your resetCasesthisQuarter class.  

 

Then create another class and fill that in with the contents of your resetCasesthisQuarterSched class. Although you should modify it slightly like below:

 

 

global class resetCasesthisQuarterSched implements Schedulable{
   global void execute(SchedulableContext SC) {
      resetCasesthisQuarter r = new resetCasesthisQuarter();
      r.getPlansToReset();
      r.resetCount();
   } 
}

 

 

then you can schedule the resetCasesthisQuarterSched  class to run the first of every month.

 

 

Alternatively...  if you wanted to one off force the code to run you could open the System Log window and in the Execute Apex section you could put in :

 

 

resetCasesthisQuarter r = new resetCasesthisQuarter();
r.getPlansToReset();
r.resetCount();

 and click run, which would force it to run.  Doing this would actually update your opportunity records too.   (I know earlier I said nothing would happen, but I read the Help and Training on that page wrong..  :-)  )

 

Hope that clears things up.

 

 

 

This was selected as the best answer
theitdeptrockstheitdeptrocks
Hey VinOK, I didn't get a chance to reply on Friday, but everything is working as it should. The only issue I am having now is that the Apex Scheduler doesn't allow for quarterly (monthly, weekly, and by day) runs. Since I want to reset my case count on the first of each quarter, I think I'll update my if statement to look at the current month (if January, April, July, October)to determine if the rest of the code should be ran. Thanks for all of your help and explanations!
VinOKVinOK

Awesome.  Glad I could help