I have a series of accounting objects in Salesforce. The accounts object has a master-detail relationship with a custom Transaction object. This Transaction object should hold all payments and charges made to the account. Charges happen weekly and are based on a field stored in a different object.
I'm trying to write an apex class that, on a weekly basis, will go through and create new Transaction records that reflect the weekly charges. However, the class I've written is running but (as far as I can tell) isn't doing anything.
I'm still new to Object-oriented programming and certainly new to Apex. Any help and references on where to learn more about what I'm doing wrong would be appreciated!
global class scheduledWeekly implements Schedulable {
public static String CRON_EXP = '0 50 21 * * ? *';
global static String scheduleIt() {
scheduledWeekly sw = new scheduledWeekly();
return System.schedule('Weekly Billing', CRON_EXP, sw);
}
global void execute(SchedulableContext sc) {
List <Transaction__c> trans_insert = new List <Transaction__c>();
Child__c ac = [
Select Family_Account__c
From Child__c
Where Enrolled__c = true];
Account at = [
Select Name
, Weekly_Tuition__c
From Account
Where Name = :ac.Family_Account__c
and Weekly_Tuition__c > 0
];
Account ae = [
Select Name
, Extra_Curricular_Tuition_Fee__c
From Account
Where Name = :ac.Family_Account__c
and Extra_Curricular_Tuition_Fee__c > 0
];
for(Account account: [
Select Name
From Account
Where Name = :ac.Family_Account__c
and Weekly_Tuition__c > 0
]) {Transaction__c nt = new Transaction__c();
nt.Family_Account__c = at.Name;
nt.Notes__c = 'Automated Charge';
nt.Transaction_Date__c = System.Today();
nt.Transaction_Type__c = 'Tuition Charge';
nt.Amount__c = at.Weekly_Tuition__c * -1;
trans_insert.add(nt);}
//database.insert(nt);
for(Account account: [
Select Name
From Account
Where Name = :ac.Family_Account__c
and Extra_Curricular_Tuition_Fee__c > 0
]) {Transaction__c ne = new Transaction__c();
ne.Family_Account__c = ae.Name;
ne.Notes__c = 'Automated Charge';
ne.Transaction_Date__c = System.Today();
ne.Transaction_Type__c = 'Tuition Charge';
ne.Amount__c = ae.Extra_Curricular_Tuition_Fee__c * -1;
trans_insert.add(ne);}
insert trans_insert;
}
}
Here's the code I got working, finally!!! For simplicity's sake, I divided the two transactions I was trying to write into two separate classes (easier to debug that way). Here's the class I have working so far.
global class scheduledWeekly implements Schedulable {
//Variables of CRON_EXP in the order they appear
//Seconds: 0-59 - No special characters allowed
//Minutes: 0-59 - No special characters allowed
//Hours: 0-23 - Special Characters: , - * /
//Day of Month: 1-31 - Special Characters: , - * ? / L W
//Month: 1-12 - Special Characters: , - * ? / L W
//Day of Week: 1-7 - Special Characters: , - * ? / L #
//Year (optional): null or 1970-2099
//Special Character Descriptions
//, delimits values
//- specifies a range
//* specifies all possible values
//? no specific value
//use a slash(/) to indicate a starting point followed by an increment
//L is the Last
//W is the closest Weekday
//# specifies the nth day in the format of day_of_week by day of month
public static String CRON_EXP = '0 39 15 * * ? *';
global static String scheduleIt() {
scheduledWeekly sw = new scheduledWeekly();
return System.schedule('Weekly Billing', CRON_EXP, sw);
}
global void execute(SchedulableContext sc) {
List <Transaction__c> trans_insert = new List <Transaction__c>();
Account[] at = [
Select Name
, Weekly_Tuition__c
From Account
Where Id in (Select Family_Account__c
From Child__c
Where Enrolled__c = true)
and Weekly_Tuition__c > 0
];
system.debug('Manual Debug >>>>>>>>>>>>>>>>>Active Accounts with Tuition:' + at);
System.debug('Manual Debug >>>>>>>>>>>>>>>>> ' + at.size() + ' records found with tuition charges needed.');
if(at.size() > 0)
{
System.debug('Manual Debug >>>>>>>>>>>>>>>>> at.size found to be greater than 0.');
for(Account account: [
Select Name
, Weekly_Tuition__c
From Account
Where Id in (Select Family_Account__c
From Child__c
Where Enrolled__c = true)
and Weekly_Tuition__c > 0
]) {Transaction__c newTuitionCharge = new Transaction__c();
newTuitionCharge.Family_Account__c = account.Id;
newTuitionCharge.Notes__c = 'Automated Charge';
newTuitionCharge.Transaction_Date__c = System.Today();
newTuitionCharge.Transaction_Type__c = 'Tuition Charge';
newTuitionCharge.Amount__c = account.Weekly_Tuition__c * -1;
system.debug('Manual Debug >>>>>>>>>>>>>>>>>Family_Account: ' + newTuitionCharge.Family_Account__c);
system.debug('Manual Debug >>>>>>>>>>>>>>>>>Tuition Amount: ' + newTuitionCharge.Amount__c);
trans_insert.add(newTuitionCharge);
system.debug('Manual Debug >>>>>>>>>>>>>>>>>newTuitionCharge added to trans_insert list.');}
}
system.debug('Manual Debug >>>>>>>>>>>>>>>>>' + trans_insert.size() + ' rows ready to be saved.');
Database.SaveResult[] srList = Database.insert(trans_insert,false);
for(Database.SaveResult sr : srList){
if(sr.isSuccess()){
system.debug('Successfully inserted Transaction. Transaction ID: ' + sr.getID());
}
else{
for(Database.Error err : sr.getErrors()){
System.debug('The following error has occurred.');
System.debug(err.getStatusCode() + ': ' + err.getMessage());
System.debug('Transaction fields that affected this error: ' + err.getFields());
}
}
}
}
}
Are you certain your code is executing? According to the documentation Salesforce CRON Expressions only want 6 parameters and you have 7. The 6 part CRON expression is documented here:
http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_objects_crontrigger.htm
Do you see a row for it at /a0e ("Scheduled Apex" in the setup side menu)? Does the "Next Scheduled Run" column and match your expectation? You can delete that row from the admin screen and then rerun scheduledWeekly.scheduleIt()
to reschedule if necessary.
If the problem is in your code, you need to collect a debug log and parse through it for clues. You can add various System.debug()
calls to get a better picture of what your code is doing when you parse through the log. What is the size of trans_insert
for instance? Zero would be a big clue and I'd then ask what's the size of ac
, at
, and ae
?
Also, change that last line to
Database.SaveResult[] srList = Database.insert(trans_insert);
Then you can comb log errors from the insert. In my experience, validation rules often bubble up unexpectedly during insert/update operations and you can get the necessary details from Database.SaveResult[]
like so:
I do find it a little hard to find my debug lines buried in the other log noise so I've gotten in the habit of using a little text decoration to make it easy to find what interests me in the log:
System.debug('\n\n\n####### ' + something_interesting + ' ########\n\n\n');
Side note: I'm fairly certain you can capture the results of a real job execution by monitoring your user record at "Debug Logs" /setup/ui/listApexTraces.apexp but that can be annoying and time consuming. It is easier to setup a second method named execute2()
and move your execute(SchedulableContext sc)
code there. execute()
calls execute2()
obviously. The beauty is that you can run scheduledWeekly.execute2();
on demand to easily capture a debug log.