Skip to content

Commit

Permalink
Merge pull request #261 from SalesforceFoundation/feature/scheduler-c…
Browse files Browse the repository at this point in the history
…hanges

Feature/scheduler changes
  • Loading branch information
Nicolas Campbell authored Feb 22, 2017
2 parents 0a8f3c8 + 095875d commit f67fd95
Show file tree
Hide file tree
Showing 16 changed files with 621 additions and 105 deletions.
4 changes: 2 additions & 2 deletions src/classes/VOL_BATCH_Recurrence_TEST.cls
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public class VOL_BATCH_Recurrence_TEST {
createRecurringJob();
List<Volunteer_Shift__c> shifts = [SELECT Id FROM Volunteer_Shift__c];
integer cShifts = shifts.size();
system.assert(cShifts >= 0 && cShifts <= 5);
system.assert(cShifts >= 0 && cShifts <= 10);

// now increase our future recurrence limit to a larger size and run the batch
VOL_SharedCode.VolunteersSettings.Recurring_Job_Future_Months__c = 2;
Expand All @@ -74,7 +74,7 @@ public class VOL_BATCH_Recurrence_TEST {
Test.stopTest();
shifts = [SELECT Id FROM Volunteer_Shift__c];
integer cShiftsNew = shifts.size() - cShifts;
system.assert(cShiftsNew >=3 && cShiftsNew <= 5);
system.assert(cShiftsNew >=4 && cShiftsNew <= 5);
}

}
101 changes: 68 additions & 33 deletions src/classes/VOL_JRS.cls
Original file line number Diff line number Diff line change
Expand Up @@ -35,25 +35,63 @@
public with sharing class VOL_JRS {

//******************************************************************************************************
// for the specified JRS's, delete all shifts that have no committed & completed hours.
// called from the Before Delete JRS trigger.
public static void DeleteListJRS(list<Job_Recurrence_Schedule__c> listJRS) {

// get a set of the JRS ID's for querying
set<ID> setJRSId = new set<ID>();
for (Job_Recurrence_Schedule__c jrs : listJRS) {
setJRSId.add(jrs.Id);
}

// get all shifts associated with these JRS's that we should delete
list<Volunteer_Shift__c> listShift = new list<Volunteer_Shift__c>();
listShift = [select Id, Total_Volunteers__c from Volunteer_Shift__c
where ((Total_Volunteers__c = null or Total_Volunteers__c = 0) and
Job_Recurrence_Schedule__c in : setJRSId)];

delete listShift;
}
// for the specified JRS's, delete all shifts that have no hours, and detach Shifts that have hours.
// called from the Before Delete JRS trigger.
public static void DeleteListJRS(list<Job_Recurrence_Schedule__c> listJRS) {

// get a set of the JRS ID's for querying
set<ID> setJRSId = new set<ID>();
for (Job_Recurrence_Schedule__c jrs : listJRS) {
setJRSId.add(jrs.Id);
}

list<Volunteer_Shift__c> listShiftUpdate = new list<Volunteer_Shift__c>();
list<Volunteer_Shift__c> listShiftDelete = new list<Volunteer_Shift__c>();

// get all shifts associated with these JRS's
for (list<Volunteer_Shift__c> listShift : [select Id, Job_Recurrence_Schedule__r.Name, System_Note__c,
(select Id from Volunteer_Hours__r)
from Volunteer_Shift__c
where Job_Recurrence_Schedule__c in : setJRSId]) {

calcDeleteOrDetachJRSShifts(listShift, listShiftDelete, listShiftUpdate);
}

if (listShiftUpdate.size() > 0)
update listShiftUpdate;
if (listShiftDelete.size() > 0)
delete listShiftDelete;
}

//******************************************************************************************************
// Given a list of Shifts, add those who have no Hours to the supplied delete list, and those that have
// Hours are detached from their JRS (updating their System Note), and put on the update list.
// The caller is responsible for committing to the database.
private static void calcDeleteOrDetachJRSShifts(
list<Volunteer_Shift__c> listShift,
list<Volunteer_Shift__c> listShiftDelete,
list<Volunteer_Shift__c> listShiftUpdate) {

for (Volunteer_Shift__c shift : listShift) {
Integer count = 0;
// note that to avoid query limits, we avoid looking at Volunteer_Hours__r.size(), and
// must leverage soql for loops that internally use queryMore.
for (Volunteer_Hours__c hr : shift.Volunteer_Hours__r) {
count++;
break;
}
// Shifts without any hours get deleted
if (count == 0) {
listShiftDelete.add(shift);
} else { // Shifts with hours get detached and updated
shift.System_Note__c += ' ' + label.labelJRSShiftRemovedSystemNote + ' ' + shift.Job_Recurrence_Schedule__r.Name + '.';
shift.Job_Recurrence_Schedule__c = null;
listShiftUpdate.add(shift);
}
}
// we don't commit the shifts, leaving the caller to do it.
}

//******************************************************************************************************
// given a list of recurring schedules, does all the work to delete any shifts that
// no longer match, and creates new shifts into the future.
Expand All @@ -70,16 +108,20 @@ public with sharing class VOL_JRS {
setJRSId.add(jrs.Id);
}

// get all shifts associated with these JRS's
// get appropriate shifts associated with these JRS's
list<Volunteer_Shift__c> listShift = new list<Volunteer_Shift__c>();
if (fReviewAllShifts) {
listShift = [select Id, Start_Date_Time__c, Job_Recurrence_Schedule__c,
Total_Volunteers__c, Desired_Number_of_Volunteers__c, Volunteer_Job__c, Description__c, System_Note__c
Total_Volunteers__c, Desired_Number_of_Volunteers__c, Volunteer_Job__c, Description__c,
System_Note__c, Job_Recurrence_Schedule__r.Name,
(select Id from Volunteer_Hours__r)
from Volunteer_Shift__c where Job_Recurrence_Schedule__c in : setJRSId
order by Start_Date_Time__c];
} else {
listShift = [select Id, Start_Date_Time__c, Job_Recurrence_Schedule__c,
Total_Volunteers__c, Desired_Number_of_Volunteers__c, Volunteer_Job__c, Description__c, System_Note__c
Total_Volunteers__c, Desired_Number_of_Volunteers__c, Volunteer_Job__c, Description__c,
System_Note__c, Job_Recurrence_Schedule__r.Name,
(select Id from Volunteer_Hours__r)
from Volunteer_Shift__c where Job_Recurrence_Schedule__c in : setJRSId and Start_Date_Time__c >= TODAY
order by Start_Date_Time__c];
}
Expand Down Expand Up @@ -177,7 +219,8 @@ public with sharing class VOL_JRS {
if (VOL_SharedCode.VolunteersSettings.Recurring_Job_Future_Months__c != null) {
nMonths = integer.valueOf(VOL_SharedCode.VolunteersSettings.Recurring_Job_Future_Months__c);
}
dtStop = dtStop.addMonths(nMonths);
// stop on the last day of the future month
dtStop = dtStop.addMonths(nMonths+1).toStartOfMonth().addDays(-1);

if (jrs.Schedule_End_Date__c != null)
dtStop = jrs.Schedule_End_Date__c;
Expand Down Expand Up @@ -208,7 +251,7 @@ public with sharing class VOL_JRS {
// see if we already have a shift for that date
Volunteer_Shift__c shiftExisting = mapDtShift.get(dtLast);

// if we have the shift, update its Job, time and duration
// if we have the shift, update its Duration, Start Time, Desired Number of Volunteers, and Description
if (shiftExisting != null) {
shiftExisting.Duration__c = jrs.Duration__c;
shiftExisting.Start_Date_Time__c = datetime.newInstance(dtLast, jrs.Schedule_Start_Date_Time__c.time());
Expand All @@ -235,16 +278,8 @@ public with sharing class VOL_JRS {
// we've finished identifying shifts to update and those to create.
// now figure out if there are any to delete or remove from the jrs.
if (fReviewAllShifts) {
for (Volunteer_Shift__c shift: mapDtShift.values()) {
if (shift.Total_Volunteers__c > 0) {
shift.Job_Recurrence_Schedule__c = null;
shift.System_Note__c += ' ' + label.labelJRSShiftRemovedSystemNote + ' ' + jrs.Name + '.';
listShiftUpdate.add(shift);
} else {
listShiftDelete.add(shift);
}
}
}
calcDeleteOrDetachJRSShifts(mapDtShift.values(), listShiftDelete, listShiftUpdate);
}

// we don't commit the shifts, leaving the caller to do it.
}
Expand Down
146 changes: 146 additions & 0 deletions src/classes/VOL_JRS_TEST.cls
Original file line number Diff line number Diff line change
Expand Up @@ -248,4 +248,150 @@ public with sharing class VOL_JRS_TEST {
system.assertEquals(0, cShiftsInitial);
system.assert(cShifts == cShiftsNew || cShifts == cShiftsNew-1 || cShifts == cShiftsNew+1);
}

//******************************************************************************************************
// test that deleting a JRS deletes Shifts with no Hours, and just detaches Shifts with Hours.
public static testmethod void testJRSDelete() {

// create test data
Account acc = new Account(name='account1');
insert acc;
Contact con = new Contact(Lastname='foo', AccountId=acc.Id);
insert con;
Campaign cmp = new Campaign(recordtypeid=VOL_SharedCode.recordtypeIdVolunteersCampaign,
name='Job Calendar Test Campaign', IsActive=true);
insert cmp;
Volunteer_Job__c job = new Volunteer_Job__c(name='Job1', campaign__c=cmp.Id);
insert job;
Job_Recurrence_Schedule__c jrs = new Job_Recurrence_Schedule__c(Volunteer_Job__c = job.Id);
jrs.Days_of_Week__c = 'Monday';
jrs.Duration__c = 1;
jrs.Schedule_Start_Date_Time__c = system.today().addMonths(-1).toStartOfMonth();
jrs.Schedule_End_Date__c = system.today().addmonths(2).toStartOfMonth().addDays(-1);
jrs.Weekly_Occurrence__c = 'Every';
jrs.Desired_Number_of_Volunteers__c = 5;
jrs.Description__c = 'initial description';
insert jrs;

list<Volunteer_Shift__c> listShift = [select Id, Volunteer_Job__c, Start_Date_Time__c from Volunteer_Shift__c];
integer countTotalShifts = listShift.size();
system.assert(countTotalShifts >= 12); // we should get at least 4 Mondays each month.

// now create Hours for half the shifts
list<Volunteer_Hours__c> listHours = new list<Volunteer_Hours__c>();
integer countShiftsWithHours = 0;
for (integer i = 0; i < listShift.size(); i++) {
if (Math.mod(i, 2) == 0) {
Volunteer_Shift__c shift = listShift[i];
countShiftsWithHours++;
integer cHoursLDV = 1;
// for the first shift, we create a bunch of hours, to ensure our delete handler uses soql-for-loops
// on the subquery it uses!
if (countShiftsWithHours == 1)
cHoursLDV = 2000;
for (integer j = 0; j < cHoursLDV; j++) {
Volunteer_Hours__c hr = new Volunteer_Hours__c(
Contact__c = con.Id,
Volunteer_Job__c = shift.Volunteer_Job__c,
Volunteer_Shift__c = shift.Id,
Status__c = 'Prospect',
Start_Date__c = shift.Start_Date_Time__c.Date(),
Planned_Start_Date_Time__c = shift.Start_Date_Time__c,
Hours_Worked__c = 0,
Number_Of_Volunteers__c = 0
);
listHours.add(hr);
}
}
}
system.assert(countTotalShifts > countShiftsWithHours);
insert listHours;

Test.startTest();
delete jrs;
Test.stopTest();

// verify some Shifts deleted and some detached
listShift = [select Id, Volunteer_Job__c, Start_Date_Time__c, Job_Recurrence_Schedule__c, System_Note__c from Volunteer_Shift__c];
system.assertEquals(countShiftsWithHours, listShift.size());
for (Volunteer_Shift__c shift : listShift) {
system.assertEquals(null, shift.Job_Recurrence_Schedule__c);
system.assert(shift.System_Note__c.contains(label.labelJRSShiftRemovedSystemNote));
}

}
//******************************************************************************************************
// test that updating a JRS deletes Shifts with no Hours, and just detaches Shifts with Hours.
public static testmethod void testJRSUpdate() {

// create test data
Account acc = new Account(name='account1');
insert acc;
Contact con = new Contact(Lastname='foo', AccountId=acc.Id);
insert con;
Campaign cmp = new Campaign(recordtypeid=VOL_SharedCode.recordtypeIdVolunteersCampaign,
name='Job Calendar Test Campaign', IsActive=true);
insert cmp;
Volunteer_Job__c job = new Volunteer_Job__c(name='Job1', campaign__c=cmp.Id);
insert job;
Job_Recurrence_Schedule__c jrs = new Job_Recurrence_Schedule__c(Volunteer_Job__c = job.Id);
jrs.Days_of_Week__c = 'Monday;Wednesday';
jrs.Duration__c = 1;
jrs.Schedule_Start_Date_Time__c = system.today().addMonths(-1).toStartOfMonth();
jrs.Schedule_End_Date__c = system.today().addmonths(2).toStartOfMonth().addDays(-1);
jrs.Weekly_Occurrence__c = 'Every';
jrs.Desired_Number_of_Volunteers__c = 5;
jrs.Description__c = 'initial description';
insert jrs;

list<Volunteer_Shift__c> listShift = [select Id, Volunteer_Job__c, Start_Date_Time__c from Volunteer_Shift__c];
integer countTotalShifts = listShift.size();
system.assert(countTotalShifts >= 24); // we should get at least 4 Mondays & Wednesdays each month.

// now create Hours for half the shifts
list<Volunteer_Hours__c> listHours = new list<Volunteer_Hours__c>();
integer countShiftsWithHours = 0;
for (integer i = 0; i < listShift.size(); i++) {
if (Math.mod(i, 3) == 0) {
Volunteer_Shift__c shift = listShift[i];
countShiftsWithHours++;
integer cHoursLDV = 1;
// for the first shift, we create a bunch of hours, to ensure our delete handler uses soql-for-loops
// on the subquery it uses!
if (countShiftsWithHours == 1)
cHoursLDV = 2000;
for (integer j = 0; j < cHoursLDV; j++) {
Volunteer_Hours__c hr = new Volunteer_Hours__c(
Contact__c = con.Id,
Volunteer_Job__c = shift.Volunteer_Job__c,
Volunteer_Shift__c = shift.Id,
Status__c = 'Prospect',
Start_Date__c = shift.Start_Date_Time__c.Date(),
Planned_Start_Date_Time__c = shift.Start_Date_Time__c,
Hours_Worked__c = 0,
Number_Of_Volunteers__c = 0
);
listHours.add(hr);
}
}
}
system.assert(countTotalShifts > countShiftsWithHours);
insert listHours;

Test.startTest();
jrs.Days_of_Week__c = 'Monday;Friday';
update jrs;
Test.stopTest();

// verify some Shifts deleted and some detached
listShift = [select Id, Volunteer_Job__c, Start_Date_Time__c, Job_Recurrence_Schedule__c, System_Note__c
from Volunteer_Shift__c where Job_Recurrence_Schedule__c = null];
system.assert(listShift.size() > 0);
for (Volunteer_Shift__c shift : listShift) {
system.assertEquals(null, shift.Job_Recurrence_Schedule__c);
system.assert(shift.System_Note__c.contains(label.labelJRSShiftRemovedSystemNote));
}

}

}
1 change: 1 addition & 0 deletions src/classes/VOL_SharedCode.cls
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,7 @@ global with sharing class VOL_SharedCode {
str = Label.labelShowHelp;
str = Label.labelFindVolunteersCriteria;
str = Label.labelFindVolunteersHelpAssign;
str = Label.labelVolunteersWizardNewCampaignTitle;
}


Expand Down
Loading

0 comments on commit f67fd95

Please sign in to comment.