View Javadoc
1   /**
2    * Copyright 2004-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.hr.time.shiftdiff.rule;
17  
18  import com.google.common.collect.ImmutableMap;
19  import org.joda.time.DateTime;
20  import org.joda.time.DateTimeZone;
21  import org.joda.time.LocalTime;
22  import org.junit.Assert;
23  import org.junit.Ignore;
24  import org.junit.Test;
25  import org.kuali.hr.KPMEWebTestCase;
26  import org.kuali.kpme.core.FunctionalTest;
27  import org.kuali.kpme.core.api.assignment.Assignment;
28  import org.kuali.kpme.core.api.assignment.AssignmentDescriptionKey;
29  import org.kuali.kpme.core.api.calendar.entry.CalendarEntry;
30  import org.kuali.kpme.core.calendar.CalendarBo;
31  import org.kuali.kpme.core.service.HrServiceLocator;
32  import org.kuali.kpme.core.util.HrContext;
33  import org.kuali.kpme.core.util.TKUtils;
34  import org.kuali.kpme.tklm.api.leave.block.LeaveBlock;
35  import org.kuali.kpme.tklm.api.time.timeblock.TimeBlock;
36  import org.kuali.kpme.tklm.time.rules.shiftdifferential.ShiftDifferentialRule;
37  import org.kuali.kpme.tklm.time.rules.shiftdifferential.service.ShiftDifferentialRuleService;
38  import org.kuali.kpme.tklm.time.service.TkServiceLocator;
39  import org.kuali.kpme.tklm.time.timesheet.TimesheetDocument;
40  import org.kuali.kpme.tklm.time.timesheet.TimesheetUtils;
41  import org.kuali.kpme.tklm.time.util.TkTimeBlockAggregate;
42  import org.kuali.kpme.tklm.utils.TkTestUtils;
43  
44  import java.math.BigDecimal;
45  import java.sql.Time;
46  import java.util.ArrayList;
47  import java.util.HashMap;
48  import java.util.List;
49  
50  /**
51   *
52   * @author djunk
53   *
54   */
55  @FunctionalTest
56  public class ShiftDifferentialRuleServiceProcessTest extends KPMEWebTestCase {
57  
58  
59  	public static final String USER_PRINCIPAL_ID = "admin";
60  	private DateTime JAN_AS_OF_DATE = new DateTime(2010, 1, 1, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
61  
62  
63  	/**
64  	 * Test with boundary carryover and overlapping rules.
65  	 *
66  	 * Rule 1:
67  	 *
68  	 * Runs on Tu, Wed, Th on the interval: [22:00, 4:00)
69  	 * Max Gap: 15 minutes
70  	 * Min Hours: 3
71  	 *
72  	 * Rule 2:
73  	 *
74  	 * Runs on Tu, Th on the interval: [23:00, 2:00)
75  	 * Max Gap: 2 hours
76  	 * Min Hours: 3
77  	 *
78  	 * Rule 3:
79  	 *
80  	 * Runs on W, Th on the interval: [5:00, 12:00)
81  	 * Max Gap: 15 minutes
82  	 * Min Hours: 7 hours
83  	 *
84  	 * Rule 4:
85  	 *
86  	 * Runs on W on the interval: [5:00, 12:00)
87  	 * Max Gap: 15 minutes
88  	 * Min Hours: 5
89  	 *
90  	 *
91  	 * |--------------+----+------------+------------|
92  	 * | Tu : 8/31/10 | XX | W : 9/1/10 | Th: 9/2/10 |
93  	 * |--------------+----+------------+------------|
94  	 * | 9:45p - 11:45| XX | Mid - 5a   | 5p - 11p   |
95  	 * |              | XX | 6a - Noon  |            |
96  	 * |--------------+----+------------+------------|
97       *
98       *
99       * Aug 31: 2h  : 21:45 - 23:45 (Tue) **
100      *                           [1: 5h 45m]  // [2: 2h 45m] - Not qualifying, min hours must be 3.
101      * Sep  1: 5h  : 00:00 - 05:00 (Wed) **
102      * Sep  1: 6h  : 06:00 - 12:00 (Wed) [4: 6h]
103      *
104      * Sep  1: 2h  : 22:00 - 24:00 (Wed)
105      * Sep  2: 1h  : 00:00 - 01:00 (Thu) [1: 3h]
106      *
107      * Sep  2: 6h  : 17:00 - 22:00 (Thu)
108      *
109      * 1: [22:00,  4:00) (Tue/Wed/Thu) minimum: 3h gap: 15m
110      * 2: [23:00,  2:00) (Tue/Thu)     minimum: 3h gap: 2h
111      * 3: [05:00, 12:00) (Wed/Thu)     minimum: 7h gap: 15m
112      * 4: [05:00, 12:00) (Wed)         minimum: 5h gap: 15m
113      *
114 	 */
115 	@SuppressWarnings("serial")
116 	@Test
117 	public void testProcessTimesheetBoundaryCarryoverOverlapCase() throws Exception {
118         DateTimeZone tz = HrServiceLocator.getTimezoneService().getUserTimezoneWithFallback();
119 		// Create the Rule    Sun,   Mon,   Tue,  Wed,   Thu,  Fri,  Sat
120 		boolean[] dayArray = {false, false, true, true, true, true, true};
121 		// Matches HR Job ID #1 (job # 30)
122 		Long jobNumber = 30L;
123 		Long workArea = 0L;
124 		this.createShiftDifferentialRule(
125 				"BWS-CAL", "REG", "PRM", "IN", "SD1", "SD1",// // changed from "SD1" to "IN" for changes of adding groupKeyCode to Job
126 				(new LocalTime(22, 0)),
127 				(new LocalTime(4, 0)),
128 				new BigDecimal(3), // minHours
129 				new BigDecimal("15.00"), // maxGap
130 				dayArray);
131 
132 
133 		// Timeblocks
134 
135 		// August
136 		DateTime beginPeriodDate = new DateTime(2010, 8, 15, 0, 0, 0, 0, tz);
137 		CalendarEntry endOfAugust =  HrServiceLocator.getCalendarEntryService().getCalendarEntryByIdAndPeriodEndDate("2", new DateTime(2010, 9, 1, 0, 0, 0, 0));
138 		DateTime start = new DateTime(2010, 8, 31, 21, 45, 0, 0, tz);
139 		List<TimeBlock> blocks = new ArrayList<TimeBlock>();
140 		TimesheetDocument tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", endOfAugust);
141         Assignment assignment = HrServiceLocator.getAssignmentService().getAssignment("admin", AssignmentDescriptionKey.get("IU-IN_30_30_30"), beginPeriodDate.toLocalDate());
142 		blocks.addAll(TkTestUtils.createUniformActualTimeBlocks(tdoc, assignment, "RGN", start, 1, new BigDecimal(2), BigDecimal.ZERO, "admin"));
143 		TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, endOfAugust, HrServiceLocator.getCalendarService().getCalendar(endOfAugust.getHrCalendarId()), true);
144 		tdoc.setTimeBlocks(blocks);
145 		TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
146 		TkTestUtils.verifyAggregateHourSumsFlatList("August Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(2));}},aggregate);
147 		TkServiceLocator.getTimeBlockService().saveOrUpdateTimeBlocks(new ArrayList<TimeBlock>(), aggregate.getFlattenedTimeBlockList(), "admin");
148 
149 
150 		// September
151 
152 		start = new DateTime(2010, 9, 1, 0, 0, 0, 0, tz);
153 		CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start.toLocalDate().toDateTimeAtStartOfDay());
154 		tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", payCalendarEntry);
155 		blocks = new ArrayList<TimeBlock>();
156 		blocks.addAll(TkTestUtils.createUniformTimeBlocks(start, 1, new BigDecimal("5"), "RGN", jobNumber, workArea));
157 		blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusHours(6), 1, new BigDecimal("6"), "RGN", jobNumber, workArea));
158         blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusHours(22), 1, new BigDecimal("2"), "RGN", jobNumber, workArea));
159         blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusDays(1), 1, new BigDecimal("1"), "RGN", jobNumber, workArea));
160 		blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusDays(1).plusHours(17), 1, new BigDecimal("6"), "RGN", jobNumber, workArea));
161 		blocks = setDocumentIdOnBlocks(blocks, tdoc.getDocumentId());
162         
163 		aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry, HrServiceLocator.getCalendarService().getCalendar(payCalendarEntry.getHrCalendarId()), true);
164 		
165 		TkTestUtils.verifyAggregateHourSumsFlatList("September Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(20));}},aggregate);
166 
167 		// Verify carry over and applied PRM bucket
168 		TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
169 		TkTestUtils.verifyAggregateHourSumsFlatList("September Post-Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal("8.75"));put("RGN", new BigDecimal(22));}},aggregate);
170 	}
171 
172     private List<TimeBlock> setDocumentIdOnBlocks(List<TimeBlock> blocks, String id) {
173         List<TimeBlock> updatedTimeBlocks = new ArrayList<TimeBlock>();
174         for (TimeBlock b : blocks) {
175             TimeBlock.Builder builder = TimeBlock.Builder.create(b);
176             builder.setDocumentId(id);
177             updatedTimeBlocks.add(builder.build());
178         }
179         return updatedTimeBlocks;
180     }
181 
182 
183 	/**
184 	 * Test where previous time sheet contains hours that should be added to
185 	 * the next pay periods first day shift.
186 	 *
187 	 * Runs on Tu, Th on the interval: [22:00, 4:00)
188 	 * Max Gap: 15 minutes
189 	 * Min Hours: 3
190 	 *
191 	 * |--------------+----+------------+-------------|
192 	 * | Tu : 8/31/10 | XX | W : 9/1/10 | Th : 9/2/10 |
193 	 * |--------------+----+------------+-------------|
194 	 * | 10pm - Mid   | XX | Mid - 5am  | 5pm - 11pm  |
195 	 * |--------------+----+------------+-------------|
196 	 *
197 	 * @throws Exception
198 	 */
199 	@SuppressWarnings("serial")
200 	@Test
201 	public void testProcessShiftTimesheeetBoundaryCarryoverCase() throws Exception {
202 		// Create the Rule    Sun,   Mon,   Tue,  Wed,   Thu,  Fri,  Sat
203 		boolean[] dayArray = {false, false, true, false, true, true, true};
204 		// Matches HR Job ID #1 (job # 30)
205 		Long jobNumber = 30L;
206 		Long workArea = 0L;
207 
208         DateTimeZone tz = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
209 		this.createShiftDifferentialRule(
210 				"BWS-CAL",
211 				"REG",
212 				"PRM",
213 				"IN",  // changed from "SD1" to "IN" for changes of adding groupKeyCode to Job
214 				"SD1",
215 				"SD1",
216 				(new LocalTime(22, 0)),
217 				(new LocalTime(5, 0)),
218 				new BigDecimal(3), // minHours
219 				new BigDecimal("0.25"), // maxGap
220 				dayArray);
221 
222 		// August
223 		DateTime endPeriodDate = new DateTime(2010, 9, 1, 0, 0, 0, 0);
224         CalendarEntry endOfAugust =  HrServiceLocator.getCalendarEntryService().getCalendarEntryByIdAndPeriodEndDate("2", endPeriodDate);
225 		DateTime start = new DateTime(2010, 8, 31, 22, 0, 0, 0, tz);
226 		List<TimeBlock> blocks = new ArrayList<TimeBlock>();
227 		TimesheetDocument tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", endOfAugust);
228         Assignment assignment = HrServiceLocator.getAssignmentService().getAssignment("admin", AssignmentDescriptionKey.get("IU-IN_30_30_30"), endOfAugust.getBeginPeriodFullDateTime().toLocalDate());
229 		blocks.addAll(TkTestUtils.createUniformActualTimeBlocks(tdoc, assignment, "RGN", start, 1, new BigDecimal(2), BigDecimal.ZERO, "admin"));
230 		TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, endOfAugust, HrServiceLocator.getCalendarService().getCalendar(endOfAugust.getHrCalendarId()), true);
231 
232 
233 
234 		tdoc.setTimeBlocks(blocks);
235 		TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
236 		TkTestUtils.verifyAggregateHourSumsFlatList("August Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(2));}},aggregate);
237 		TkServiceLocator.getTimeBlockService().saveOrUpdateTimeBlocks(new ArrayList<TimeBlock>(), aggregate.getFlattenedTimeBlockList(), "admin");
238 
239 
240 		// September
241 		start = new DateTime(2010, 9, 1, 0, 0, 0, 0, tz);
242 		CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start.toLocalDate().toDateTimeAtStartOfDay());
243 		tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", payCalendarEntry);
244 		blocks = new ArrayList<TimeBlock>();
245 		blocks.addAll(TkTestUtils.createUniformTimeBlocks(start, 1, new BigDecimal("5"), "RGN", jobNumber, workArea));
246 		aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry, HrServiceLocator.getCalendarService().getCalendar(payCalendarEntry.getHrCalendarId()), true);
247 		TkTestUtils.verifyAggregateHourSumsFlatList("September Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(5));}},aggregate);
248 
249 		// Verify carry over and applied PRM bucket
250 		TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
251 		TkTestUtils.verifyAggregateHourSumsFlatList("September Post-Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(7));put("RGN", new BigDecimal(7));}},aggregate);
252 	}
253 
254 	@SuppressWarnings("serial")
255 	@Test
256 	/**
257 	 * Runs on every day on the interval: [16:00, 24:00)
258 	 * Max Gap: 15 minutes
259 	 * Min Hours: 4
260 	 *
261 	 * Added some extra time blocks that are not in the shift interval, but
262 	 * close to the time blocks that are.
263 	 *
264 	 * @throws Exception
265 	 */
266 	public void testProcessShiftSimpleNoisyCase() throws Exception {
267 		// Create the Rule
268 		boolean[] dayArray = {true, true, true, true, true, true, true};
269 		// Matches HR Job ID #1 (job # 30)
270 		Long jobNumber = 30L;
271 		Long workArea = 0L;
272         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
273 		this.createShiftDifferentialRule(
274 				"BWS-CAL",
275 				"REG",
276 				"PRM",
277 				"IN", // changed from "SD1" to "IN" for changes of adding groupKeyCode to Job
278 				"SD1",
279 				"SD1",
280 				(new LocalTime(16, 0)),
281 				(new LocalTime(0, 0)),
282 				new BigDecimal(4), // minHours
283 				new BigDecimal("15"), // maxGap
284 				dayArray);
285 
286 		// Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
287 		DateTime start = new DateTime(2010, 3, 29, 14, 0, 0, 0, zone);
288 		List<TimeBlock> blocks = new ArrayList<TimeBlock>();
289 		CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
290 		blocks.addAll(TkTestUtils.createUniformTimeBlocks(start, 2, new BigDecimal("4"), "RGN", jobNumber, workArea));
291 		blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusHours(4).plusMinutes(15), 2, new BigDecimal("2"), "RGN", jobNumber, workArea));
292 		blocks.addAll(TkTestUtils.createUniformTimeBlocks(new DateTime(2010, 3, 29, 12, 58, 0, 0, zone), 2, new BigDecimal(1), "RGN", jobNumber, workArea));
293 		TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
294 
295 		// Verify pre-Rule Run
296 		TkTestUtils.verifyAggregateHourSums("admin", "Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(14));}},aggregate,2);
297 
298 		// Run Rule
299 		TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
300 		tdoc.setTimeBlocks(blocks);
301 		TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
302 
303 		// Verify post-Rule Run
304 		TkTestUtils.verifyAggregateHourSums("admin", "Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(8));put("RGN", new BigDecimal(14));}},aggregate,2);
305 	}
306 
307 	@SuppressWarnings("serial")
308 	@Test
309 	/**
310 	 * Runs on every day on the interval: [16:00, 24:00)
311 	 * Max Gap: 15 minutes
312 	 * Min Hours: 4
313 	 *
314 	 * @throws Exception
315 	 */
316 	public void testProcessShiftSimpleCase() throws Exception {
317 		// Create the Rule
318 		boolean[] dayArray = {true, true, true, true, true, true, true};
319 		// Matches HR Job ID #1 (job # 30)
320 		Long jobNumber = 30L;
321 		Long workArea = 0L;
322         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
323 		this.createShiftDifferentialRule(
324 				"BWS-CAL",
325 				"REG",
326 				"PRM",
327 				"IN", // changed from "SD1" to "IN" for changes of adding groupKeyCode to Job
328 				"SD1",
329 				"SD1",
330 				(new LocalTime(16, 0)), //4pm
331 				(new LocalTime(0, 0)),  //midnight
332 				new BigDecimal(4), // minHours
333 				new BigDecimal("15.00"), // maxGap minutes
334 				dayArray);
335 
336 		// Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
337 		DateTime start = new DateTime(2010, 3, 29, 14, 0, 0, 0, zone);
338 		List<TimeBlock> blocks = new ArrayList<TimeBlock>();
339 		CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
340 		blocks.addAll(TkTestUtils.createUniformTimeBlocks(start, 2, new BigDecimal("4"), "REG", jobNumber, workArea));
341 		blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusHours(4).plusMinutes(15), 2, new BigDecimal("2"), "REG", jobNumber, workArea));
342 		TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
343 
344 		// Verify pre-Rule Run
345 		TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("REG", new BigDecimal(12));}},aggregate,2);
346 
347 		// Run Rule
348 		TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
349 		tdoc.setTimeBlocks(blocks);
350 		TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
351 
352 		// Verify post-Rule Run
353 		TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(8));put("REG", new BigDecimal(12));}},aggregate,2);
354 	}
355 
356 	/**
357 	 * Stores the Shift Differential Rule in the database for testing.
358 	 *
359 	 * dayBooleans[] is a 7 element array of booleans, [0, 6] is [sun, sat]
360 	 */
361 	private void createShiftDifferentialRule(String pyCalendarGroup, String fromEarnGroup, String premiumEarnCode, String location, String payGrade, String hrSalGroup, LocalTime startTime, LocalTime endTime, BigDecimal minHours, BigDecimal maxGap, boolean dayBooleans[]) {
362 		Assert.assertTrue("Wrong number of day booleans", dayBooleans.length == 7);
363 
364 		ShiftDifferentialRuleService service = TkServiceLocator.getShiftDifferentialRuleService();
365 		ShiftDifferentialRule sdr = new ShiftDifferentialRule();
366 
367 		sdr.setBeginTime(new Time(startTime.toDateTimeToday().getMillis()));
368 		sdr.setEndTime(new Time(endTime.toDateTimeToday().getMillis()));
369 		sdr.setMinHours(minHours);
370 		sdr.setMaxGap(maxGap);
371 		sdr.setActive(true);
372 		sdr.setUserPrincipalId(USER_PRINCIPAL_ID);
373 		sdr.setEffectiveLocalDate(JAN_AS_OF_DATE.toLocalDate());
374 		sdr.setLocation(location);
375 		sdr.setPayGrade(payGrade);
376 		sdr.setHrSalGroup(hrSalGroup);
377 		sdr.setFromEarnGroup(fromEarnGroup);
378 		sdr.setPyCalendarGroup(pyCalendarGroup);
379 		sdr.setEarnCode(premiumEarnCode);
380         sdr.setRuleType("default");
381 
382 		for (int i=0; i<dayBooleans.length; i++) {
383 			switch(i) {
384 			case 0:
385 				sdr.setSunday(dayBooleans[i]);
386 				break;
387 			case 1:
388 				sdr.setMonday(dayBooleans[i]);
389 				break;
390 			case 2:
391 				sdr.setTuesday(dayBooleans[i]);
392 				break;
393 			case 3:
394 				sdr.setWednesday(dayBooleans[i]);
395 				break;
396 			case 4:
397 				sdr.setThursday(dayBooleans[i]);
398 				break;
399 			case 5:
400 				sdr.setFriday(dayBooleans[i]);
401 				break;
402 			case 6:
403 				sdr.setSaturday(dayBooleans[i]);
404 				break;
405 			}
406 		}
407 
408 		service.saveOrUpdate(sdr);
409 
410 		ShiftDifferentialRule sdrBack = service.getShiftDifferentialRule(sdr.getTkShiftDiffRuleId());
411 
412         DateTimeZone tz = HrServiceLocator.getTimezoneService().getUserTimezoneWithFallback();
413         LocalTime orig_start = new LocalTime(sdr.getBeginTime());
414 		LocalTime orig_end = new LocalTime(sdr.getEndTime());
415 
416 		LocalTime stored_start = new LocalTime(sdrBack.getBeginTime());
417 		LocalTime stored_end = new LocalTime(sdrBack.getEndTime());
418 
419 		Assert.assertTrue("Start times not equal.", orig_start.equals(stored_start));
420 		Assert.assertTrue("End times not equal.", orig_end.equals(stored_end));
421 	}
422 
423 
424     /**
425      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
426      *
427      * Create a single 24 hour timeblock that spans two different shift, both exceeding the min hours
428      */
429     @Test
430     public void overlapMultipleShiftsWithSameTimeBlock() {
431         // Create the Rule
432         boolean[] dayArray = {true, true, true, true, true, true, true};
433         // Matches HR Job ID #1 (job # 30)
434         Long jobNumber = 30L;
435         Long workArea = 0L;
436         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
437 
438         //3pm to 8am, 6 hour minimum, 90 minute max gap
439         this.createShiftDifferentialRule(
440                 "BWS-CAL",
441                 "REG",
442                 "PRM",
443                 "IN",
444                 "SD1",
445                 "SD1",
446                 (new LocalTime(15, 0)),
447                 (new LocalTime(8, 0)),
448                 new BigDecimal(6), // minHours
449                 new BigDecimal("90.00"), // maxGap
450                 dayArray);
451 
452         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
453         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
454         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
455 
456         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
457         DateTime tbStart = new DateTime(2010, 3, 29, 0, 0, 0, 0, zone);
458 
459         //24 time block (midnight to midnight)
460         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart, 1, new BigDecimal("24"), "REG", jobNumber, workArea));
461 
462         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
463 
464         // Verify pre-Rule Run
465         TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("REG", new BigDecimal(24));}},aggregate,2);
466 
467         // Run Rule
468         TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
469         tdoc.setTimeBlocks(blocks);
470         TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
471 
472         // Verify post-Rule Run
473         TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(17));put("REG", new BigDecimal(24));}},aggregate,2);
474 
475     }
476 
477     /**
478      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
479      *
480      * Create a two 24 hour timeblocks that span three different shifts, all exceeding the min hours
481      */
482     @Test
483     public void overlapMultipleShiftsWithMultipleTimeBlocks() {
484         // Create the Rule
485         boolean[] dayArray = {true, true, true, true, true, true, true};
486         // Matches HR Job ID #1 (job # 30)
487         Long jobNumber = 30L;
488         Long workArea = 0L;
489         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
490 
491         //3pm to 8am, 6 hour minimum, 90 minute max gap
492         this.createShiftDifferentialRule(
493                 "BWS-CAL",
494                 "REG",
495                 "PRM",
496                 "IN",
497                 "SD1",
498                 "SD1",
499                 (new LocalTime(15, 0)),
500                 (new LocalTime(8, 0)),
501                 new BigDecimal(6), // minHours
502                 new BigDecimal("90.00"), // maxGap
503                 dayArray);
504 
505         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
506         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
507         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
508 
509         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
510         DateTime tbStart = new DateTime(2010, 3, 29, 0, 0, 0, 0, zone);
511 
512         //24 time block (midnight to midnight)
513         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart, 1, new BigDecimal("24"), "REG", jobNumber, workArea));
514 
515         //24 time block (midnight to midnight)
516         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart.plusDays(1), 1, new BigDecimal("24"), "REG", jobNumber, workArea));
517 
518         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
519 
520         // Verify pre-Rule Run
521         TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("REG", new BigDecimal(48));}},aggregate,2);
522 
523         // Run Rule
524         TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
525         tdoc.setTimeBlocks(blocks);
526         TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
527 
528         // Verify post-Rule Run
529         //overlaps from 12a-8a (8 hours), 3p-8a (17 hours), and 3p - 12a (9 hours) == 34 hours
530         TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(34));put("REG", new BigDecimal(48));}},aggregate,2);
531 
532     }
533 
534     /**
535      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
536      *
537      * Create a single 24 hour timeblock that spans two different shift, both exceeding the min hours
538      */
539     @Test
540     public void overlapMultipleShiftsWithSameTimeBlockExceedingMinOnOneShift() {
541         // Create the Rule
542         boolean[] dayArray = {true, true, true, true, true, true, true};
543         // Matches HR Job ID #1 (job # 30)
544         Long jobNumber = 30L;
545         Long workArea = 0L;
546         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
547 
548         //3pm to 8am, 6 hour minimum, 90 minute max gap
549         this.createShiftDifferentialRule(
550                 "BWS-CAL",
551                 "REG",
552                 "PRM",
553                 "IN",
554                 "SD1",
555                 "SD1",
556                 (new LocalTime(15, 0)),
557                 (new LocalTime(8, 0)),
558                 new BigDecimal(6), // minHours
559                 new BigDecimal("90.00"), // maxGap
560                 dayArray);
561 
562         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
563         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
564         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
565 
566         //reg timeblock 3am - midnight
567         DateTime tbStart = new DateTime(2010, 3, 30, 3, 0, 0, 0, zone);
568         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart,   1, new BigDecimal("21"), "REG", jobNumber, workArea));
569 
570         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
571 
572         // Verify pre-Rule Run
573         TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
574                 .put("PRM", BigDecimal.ZERO)
575                 .put("REG", new BigDecimal(21)).build(), aggregate, 2);
576 
577         // Run Rule
578         TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
579         tdoc.setTimeBlocks(blocks);
580         TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
581 
582         // Verify post-Rule Run
583         TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
584                 .put("PRM", new BigDecimal(9))
585                 .put("REG", new BigDecimal(21)).build(),
586                 aggregate,
587                 2);
588 
589     }
590 
591     /**
592      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
593      *
594      * Create a single 24 hour timeblock that spans two different shift, both exceeding the min hours
595      */
596     @Test
597     public void overlapMultipleShiftsWithSameTimeBlockExceedingMinOnFirstShift() {
598         // Create the Rule
599         boolean[] dayArray = {true, true, true, true, true, true, true};
600         // Matches HR Job ID #1 (job # 30)
601         Long jobNumber = 30L;
602         Long workArea = 0L;
603         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
604 
605         //3pm to 8am, 6 hour minimum, 90 minute max gap
606         this.createShiftDifferentialRule(
607                 "BWS-CAL",
608                 "REG",
609                 "PRM",
610                 "IN",
611                 "SD1",
612                 "SD1",
613                 (new LocalTime(15, 0)),
614                 (new LocalTime(8, 0)),
615                 new BigDecimal(6), // minHours
616                 new BigDecimal("90.00"), // maxGap
617                 dayArray);
618 
619         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
620         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
621         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
622 
623         //reg timeblock 1am - 9pm
624         DateTime tbStart = new DateTime(2010, 3, 30, 1, 0, 0, 0, zone);
625         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart,   1, new BigDecimal("19"), "REG", jobNumber, workArea));
626 
627         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
628 
629         // Verify pre-Rule Run
630         TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
631                 .put("PRM", BigDecimal.ZERO)
632                 .put("REG", new BigDecimal(19)).build(), aggregate, 2);
633 
634         // Run Rule
635         TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
636         tdoc.setTimeBlocks(blocks);
637         TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
638 
639         // Verify post-Rule Run
640         TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
641                         .put("PRM", new BigDecimal(7))
642                         .put("REG", new BigDecimal(19)).build(),
643                 aggregate,
644                 2);
645 
646     }
647 
648     /**
649      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
650      *
651      * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
652      */
653     @Test
654     public void overlapMultipleShiftsWithSameTimeBlocNeitherExceedingMin() {
655         // Create the Rule
656         boolean[] dayArray = {true, true, true, true, true, true, true};
657         // Matches HR Job ID #1 (job # 30)
658         Long jobNumber = 30L;
659         Long workArea = 0L;
660         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
661 
662         //3pm to 8am, 6 hour minimum, 90 minute max gap
663         this.createShiftDifferentialRule(
664                 "BWS-CAL",
665                 "REG",
666                 "PRM",
667                 "IN",
668                 "SD1",
669                 "SD1",
670                 (new LocalTime(15, 0)),
671                 (new LocalTime(8, 0)),
672                 new BigDecimal(6), // minHours
673                 new BigDecimal("90.00"), // maxGap
674                 dayArray);
675 
676         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
677         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
678         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
679         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
680 
681         //reg timeblock 3am - 8pm
682         DateTime tbStart = new DateTime(2010, 3, 30, 3, 0, 0, 0, zone);
683         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart,   1, new BigDecimal("17"), "REG", jobNumber, workArea));
684 
685         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
686 
687         // Verify pre-Rule Run
688         TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
689                 .put("PRM", BigDecimal.ZERO)
690                 .put("REG", new BigDecimal(17)).build(), aggregate, 2);
691 
692         // Run Rule
693         TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
694         tdoc.setTimeBlocks(blocks);
695         TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
696 
697         // Verify post-Rule Run
698         TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
699                         .put("PRM", BigDecimal.ZERO)
700                         .put("REG", new BigDecimal(17)).build(),
701                 aggregate,
702                 2);
703     }
704 
705     /**
706      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
707      *
708      * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
709      */
710     @Test
711     public void multipleTimeBlocksOvernightExceedingMin() {
712         // Create the Rule
713         boolean[] dayArray = {true, true, true, true, true, true, true};
714         // Matches HR Job ID #1 (job # 30)
715         Long jobNumber = 30L;
716         Long workArea = 0L;
717         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
718 
719         //3pm to 8am, 6 hour minimum, 90 minute max gap
720         this.createShiftDifferentialRule(
721                 "BWS-CAL",
722                 "REG",
723                 "PRM",
724                 "IN",
725                 "SD1",
726                 "SD1",
727                 (new LocalTime(15, 0)),
728                 (new LocalTime(8, 0)),
729                 new BigDecimal(6), // minHours
730                 new BigDecimal("90.00"), // maxGap
731                 dayArray);
732 
733         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
734         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
735         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
736         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
737 
738         //reg timeblock 10pm - midnight
739         DateTime tbStart1 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
740         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
741         //reg timeblock midnight - 5am
742         DateTime tbStart2 = new DateTime(2010, 3, 31, 0, 0, 0, 0, zone);
743         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("5"), "REG", jobNumber, workArea));
744 
745         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
746 
747         // Verify pre-Rule Run
748         TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
749                 .put("PRM", BigDecimal.ZERO)
750                 .put("REG", BigDecimal.valueOf(7)).build(), aggregate, 2);
751 
752         // Run Rule
753         TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
754         tdoc.setTimeBlocks(blocks);
755         TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
756 
757         // Verify post-Rule Run
758         TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
759                         .put("PRM", BigDecimal.valueOf(7))
760                         .put("REG", BigDecimal.valueOf(7)).build(),
761                 aggregate,
762                 2);
763     }
764 
765     /**
766      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
767      *
768      * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
769      */
770     @Test
771     public void multipleTimeBlocksOvernightExceedingMinWithSixtyMinuteGap() {
772         // Create the Rule
773         boolean[] dayArray = {true, true, true, true, true, true, true};
774         // Matches HR Job ID #1 (job # 30)
775         Long jobNumber = 30L;
776         Long workArea = 0L;
777         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
778 
779         //3pm to 8am, 6 hour minimum, 90 minute max gap
780         this.createShiftDifferentialRule(
781                 "BWS-CAL",
782                 "REG",
783                 "PRM",
784                 "IN",
785                 "SD1",
786                 "SD1",
787                 (new LocalTime(15, 0)),
788                 (new LocalTime(8, 0)),
789                 new BigDecimal(6), // minHours
790                 new BigDecimal("90.00"), // maxGap
791                 dayArray);
792 
793         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
794         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
795         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
796         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
797 
798         //reg timeblock 10pm - midnight
799         DateTime tbStart1 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
800         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
801         //reg timeblock 1am - 5am
802         DateTime tbStart2 = new DateTime(2010, 3, 31, 1, 0, 0, 0, zone);
803         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("4"), "REG", jobNumber, workArea));
804 
805         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
806 
807         // Verify pre-Rule Run
808         TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
809                 .put("PRM", BigDecimal.ZERO)
810                 .put("REG", BigDecimal.valueOf(6)).build(), aggregate, 2);
811 
812         // Run Rule
813         TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
814         tdoc.setTimeBlocks(blocks);
815         TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
816 
817         // Verify post-Rule Run
818         TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
819                         .put("PRM", BigDecimal.valueOf(6))
820                         .put("REG", BigDecimal.valueOf(6)).build(),
821                 aggregate,
822                 2);
823     }
824 
825     /**
826      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
827      *
828      * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
829      */
830     @Test
831     public void multipleTimeBlocksOvernightExceedingMinWithNinetyMinuteGap() {
832         // Create the Rule
833         boolean[] dayArray = {true, true, true, true, true, true, true};
834         // Matches HR Job ID #1 (job # 30)
835         Long jobNumber = 30L;
836         Long workArea = 0L;
837         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
838 
839         //3pm to 8am, 6 hour minimum, 90 minute max gap
840         this.createShiftDifferentialRule(
841                 "BWS-CAL",
842                 "REG",
843                 "PRM",
844                 "IN",
845                 "SD1",
846                 "SD1",
847                 (new LocalTime(15, 0)),
848                 (new LocalTime(8, 0)),
849                 new BigDecimal(6), // minHours
850                 new BigDecimal("90.00"), // maxGap
851                 dayArray);
852 
853         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
854         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
855         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
856         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
857 
858         //reg timeblock 10pm - midnight
859         DateTime tbStart1 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
860         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
861         //reg timeblock 1:30am - 5:30pm
862         DateTime tbStart2 = new DateTime(2010, 3, 31, 1, 30, 0, 0, zone);
863         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("4"), "REG", jobNumber, workArea));
864 
865         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
866 
867         // Verify pre-Rule Run
868         TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
869                 .put("PRM", BigDecimal.ZERO)
870                 .put("REG", BigDecimal.valueOf(6)).build(), aggregate, 2);
871 
872         // Run Rule
873         TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
874         tdoc.setTimeBlocks(blocks);
875         TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
876 
877         // Verify post-Rule Run
878         TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
879                         .put("PRM", BigDecimal.valueOf(6))
880                         .put("REG", BigDecimal.valueOf(6)).build(),
881                 aggregate,
882                 2);
883     }
884 
885     /**
886      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
887      *
888      * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
889      */
890     @Test
891     public void multipleTimeBlocksOvernightExceedingMinButExceedingGap() {
892         // Create the Rule
893         boolean[] dayArray = {true, true, true, true, true, true, true};
894         // Matches HR Job ID #1 (job # 30)
895         Long jobNumber = 30L;
896         Long workArea = 0L;
897         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
898 
899         //3pm to 8am, 6 hour minimum, 90 minute max gap
900         this.createShiftDifferentialRule(
901                 "BWS-CAL",
902                 "REG",
903                 "PRM",
904                 "IN",
905                 "SD1",
906                 "SD1",
907                 (new LocalTime(15, 0)),
908                 (new LocalTime(8, 0)),
909                 new BigDecimal(6), // minHours
910                 new BigDecimal("90.00"), // maxGap
911                 dayArray);
912 
913         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
914         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
915         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
916         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
917 
918         //reg timeblock 10pm - midnight
919         DateTime tbStart1 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
920         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
921         //reg timeblock 1:36am - 5:36am
922         DateTime tbStart2 = new DateTime(2010, 3, 31, 1, 36, 0, 0, zone);
923         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("4"), "REG", jobNumber, workArea));
924 
925         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
926 
927         // Verify pre-Rule Run
928         TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
929                 .put("PRM", BigDecimal.ZERO)
930                 .put("REG", BigDecimal.valueOf(6)).build(), aggregate, 2);
931 
932         // Run Rule
933         TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
934         tdoc.setTimeBlocks(blocks);
935         TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
936 
937         // Verify post-Rule Run
938         TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
939                         .put("PRM", BigDecimal.valueOf(0))
940                         .put("REG", BigDecimal.valueOf(6)).build(),
941                 aggregate,
942                 2);
943     }
944 
945 
946     /**
947      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
948      *
949      * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
950      */
951     @Test
952     public void threeBlocksWithinSameShift() {
953         // Create the Rule
954         boolean[] dayArray = {true, true, true, true, true, true, true};
955         // Matches HR Job ID #1 (job # 30)
956         Long jobNumber = 30L;
957         Long workArea = 0L;
958         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
959 
960         //3pm to 8am, 6 hour minimum, 90 minute max gap
961         this.createShiftDifferentialRule(
962                 "BWS-CAL",
963                 "REG",
964                 "PRM",
965                 "IN",
966                 "SD1",
967                 "SD1",
968                 (new LocalTime(15, 0)),
969                 (new LocalTime(8, 0)),
970                 new BigDecimal(6), // minHours
971                 new BigDecimal("90.00"), // maxGap
972                 dayArray);
973 
974         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
975         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
976         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
977 
978         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
979 
980         //reg timeblock 3pm - 5pm
981         DateTime tbStart1 = new DateTime(2010, 3, 30, 15, 0, 0, 0, zone);
982         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
983 
984         // 6pm - 9pm
985         DateTime tbStart2 = new DateTime(2010, 3, 30, 18, 0, 0, 0, zone);
986         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("3"), "REG", jobNumber, workArea));
987 
988         // 10pm - 11pm
989         DateTime tbStart3 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
990         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3,   1, new BigDecimal("1"), "REG", jobNumber, workArea));
991 
992         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
993 
994         // Verify pre-Rule Run
995         TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
996                 .put("PRM", BigDecimal.ZERO)
997                 .put("REG", BigDecimal.valueOf(6)).build(), aggregate, 2);
998 
999         // Run Rule
1000         TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
1001         tdoc.setTimeBlocks(blocks);
1002         TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
1003 
1004         // Verify post-Rule Run
1005         TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1006                         .put("PRM", BigDecimal.valueOf(6))
1007                         .put("REG", BigDecimal.valueOf(6)).build(),
1008                 aggregate,
1009                 2);
1010     }
1011 
1012     /**
1013      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
1014      *
1015      * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
1016      */
1017     @Test
1018     public void fourBlocksWithinSameShiftSpanningTwoDays() {
1019         // Create the Rule
1020         boolean[] dayArray = {true, true, true, true, true, true, true};
1021         // Matches HR Job ID #1 (job # 30)
1022         Long jobNumber = 30L;
1023         Long workArea = 0L;
1024         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1025 
1026         //3pm to 8am, 6 hour minimum, 90 minute max gap
1027         this.createShiftDifferentialRule(
1028                 "BWS-CAL",
1029                 "REG",
1030                 "PRM",
1031                 "IN",
1032                 "SD1",
1033                 "SD1",
1034                 (new LocalTime(15, 0)),
1035                 (new LocalTime(8, 0)),
1036                 new BigDecimal(6), // minHours
1037                 new BigDecimal("90.00"), // maxGap
1038                 dayArray);
1039 
1040         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1041         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1042         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1043 
1044         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1045 
1046         //reg timeblock 5pm - 6pm
1047         DateTime tbStart1 = new DateTime(2010, 3, 30, 17, 0, 0, 0, zone);
1048         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("1"), "REG", jobNumber, workArea));
1049 
1050         // 7:30pm - 8:30pm
1051         DateTime tbStart2 = new DateTime(2010, 3, 30, 19, 30, 0, 0, zone);
1052         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("1"), "REG", jobNumber, workArea));
1053 
1054         // 10pm - midnight
1055         DateTime tbStart3 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
1056         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
1057 
1058         // 1:30am - 4:30am
1059         DateTime tbStart4 = new DateTime(2010, 3, 31, 1, 30, 0, 0, zone);
1060         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart4,   1, new BigDecimal("3"), "REG", jobNumber, workArea));
1061 
1062         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1063 
1064         // Verify pre-Rule Run
1065         TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1066                 .put("PRM", BigDecimal.ZERO)
1067                 .put("REG", BigDecimal.valueOf(7)).build(), aggregate, 2);
1068 
1069         // Run Rule
1070         TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
1071         tdoc.setTimeBlocks(blocks);
1072         TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
1073 
1074         // Verify post-Rule Run
1075         TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1076                         .put("PRM", BigDecimal.valueOf(7))
1077                         .put("REG", BigDecimal.valueOf(7)).build(),
1078                 aggregate,
1079                 2);
1080     }
1081 
1082 
1083     /**
1084      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
1085      *
1086      * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
1087      */
1088     @Test
1089     public void EarnGroupTest() {
1090         // Create the Rule
1091         // Matches HR Job ID #1 (job # 30)
1092         String principalId = "10112";
1093 
1094 
1095         Long jobNumber = 0L;
1096         String groupKey = "IU-IN";
1097         Long workArea = 1010L;
1098         Long task = 0L;
1099         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1100 
1101 
1102         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1103         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1104         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1105 
1106         TimesheetDocument td = TkTestUtils.populateBlankTimesheetDocument(start, principalId);
1107 
1108         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1109 
1110         //rgh - monday 8a-11a no-shift
1111         DateTime tbStart1 = new DateTime(2010, 3, 30, 8, 0, 0, 0, zone);
1112         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("3"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1113 
1114         //echr - monday 2p-5p - should have 3 hrs SHEG
1115         DateTime tbStart2 = new DateTime(2010, 3, 30, 14, 0, 0, 0, zone);
1116         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("3"), "ECHR", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1117 
1118         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1119 
1120         // Verify pre-Rule Run
1121         TkTestUtils.verifyAggregateHourSums(principalId, "Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1122                 .put("RGH", BigDecimal.valueOf(3))
1123                 .put("ECHR", BigDecimal.valueOf(3)).build(), aggregate, 2);
1124 
1125         // Run Rule
1126 
1127         //td.setTimeBlocks(blocks);
1128         List<LeaveBlock> leaveBlocks = TimesheetUtils.getLeaveBlocksForTimesheet(td);
1129         List<TimeBlock> initialBlocks = TimesheetUtils.getTimesheetTimeblocksForProcessing(td, true);
1130         List<TimeBlock> referenceTimeBlocks = TimesheetUtils.getReferenceTimeBlocks(initialBlocks);
1131 
1132         //reset time block
1133         //List<TimeBlock> tbs = TkServiceLocator.getTimesheetService().resetTimeBlock(initialBlocks, td.getAsOfDate());
1134         TimesheetUtils.processTimeBlocksWithRuleChange(blocks, referenceTimeBlocks, leaveBlocks, td.getCalendarEntry(), td, HrContext.getPrincipalId());
1135 
1136         //refresh Timesheet
1137         td = TkServiceLocator.getTimesheetService().getTimesheetDocument(td.getDocumentId());
1138         aggregate = new TkTimeBlockAggregate(td.getTimeBlocks(), payCalendarEntry);
1139         // Verify post-Rule Run
1140         TkTestUtils.verifyAggregateHourSums(principalId, "Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1141                         .put("RGH", BigDecimal.valueOf(3))
1142                         .put("ECHR", BigDecimal.valueOf(3))
1143                         .put("SHEG", BigDecimal.valueOf(3)).build(),
1144                 aggregate,
1145                 2);
1146     }
1147 
1148     @Test
1149     public void calendarGroupTest() {
1150         // Create the Rule
1151         // Matches HR Job ID #1 (job # 30)
1152         String principalId = "10113";
1153 
1154 
1155         Long jobNumber = 0L;
1156         String groupKey = "IU-IN";
1157         Long workArea = 1010L;
1158         Long task = 0L;
1159         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1160 
1161 
1162         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1163         DateTime start = new DateTime(2010, 3, 16, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1164         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates(principalId, start);
1165 
1166         TimesheetDocument td = TkTestUtils.populateBlankTimesheetDocument(start, principalId);
1167 
1168         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1169 
1170         //rgh - monday 8a-11a no-shift
1171         DateTime tbStart1 = new DateTime(2010, 3, 21, 8, 0, 0, 0, zone);
1172         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("3"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1173 
1174         //echr - monday 2p-5p - should have 3 hrs SHEG
1175         //DateTime tbStart2 = new DateTime(2010, 3, 30, 14, 0, 0, 0, zone);
1176         //blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("3"), "ECHR", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1177 
1178         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1179 
1180         // Verify pre-Rule Run
1181         TkTestUtils.verifyAggregateHourSums(principalId, "Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1182                 .put("RGH", BigDecimal.valueOf(3)).build(), aggregate, 1);
1183 
1184         // Run Rule
1185 
1186         //td.setTimeBlocks(blocks);
1187         List<LeaveBlock> leaveBlocks = TimesheetUtils.getLeaveBlocksForTimesheet(td);
1188         List<TimeBlock> initialBlocks = TimesheetUtils.getTimesheetTimeblocksForProcessing(td, true);
1189         List<TimeBlock> referenceTimeBlocks = TimesheetUtils.getReferenceTimeBlocks(initialBlocks);
1190 
1191         //reset time block
1192         //List<TimeBlock> tbs = TkServiceLocator.getTimesheetService().resetTimeBlock(initialBlocks, td.getAsOfDate());
1193         TimesheetUtils.processTimeBlocksWithRuleChange(blocks, referenceTimeBlocks, leaveBlocks, td.getCalendarEntry(), td, HrContext.getPrincipalId());
1194 
1195         //refresh Timesheet
1196         td = TkServiceLocator.getTimesheetService().getTimesheetDocument(td.getDocumentId());
1197         aggregate = new TkTimeBlockAggregate(td.getTimeBlocks(), payCalendarEntry);
1198         // Verify post-Rule Run
1199         TkTestUtils.verifyAggregateHourSums(principalId, "Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1200                         .put("RGH", BigDecimal.valueOf(3))
1201                         .put("SHCG", BigDecimal.valueOf(3))
1202                         .put("SHDY", BigDecimal.valueOf(0)).build(),
1203                 aggregate,
1204                 1);
1205     }
1206 
1207     @Test
1208     public void daysTest() {
1209         // Create the Rule
1210         // Matches HR Job ID #1 (job # 30)
1211         String principalId = "10114";
1212 
1213 
1214         Long jobNumber = 0L;
1215         String groupKey = "IU-IN";
1216         Long workArea = 1010L;
1217         Long task = 0L;
1218         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1219 
1220 
1221         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1222         DateTime start = new DateTime(2010, 3, 21, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1223         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1224 
1225         TimesheetDocument td = TkTestUtils.populateBlankTimesheetDocument(start, principalId);
1226 
1227         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1228 
1229         //rgh - sunday 3p-5p shift: 2 hr SHDY
1230         DateTime tbStart1 = new DateTime(2010, 3, 28, 15, 0, 0, 0, zone);
1231         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1232 
1233         //rgh - monday 3p-5p - shift: none
1234         DateTime tbStart2 = new DateTime(2010, 3, 29, 15, 0, 0, 0, zone);
1235         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1236 
1237         //rgh - sun 10p- mon 3a - shift: 2hr SHDY (on sunday timeblock)
1238         DateTime tbStart3 = new DateTime(2010, 3, 28, 22, 0, 0, 0, zone);
1239         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1240         DateTime tbStart4 = new DateTime(2010, 3, 29, 0, 0, 0, 0, zone);
1241         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart4,   1, new BigDecimal("3"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1242 
1243 
1244         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1245 
1246         // Verify pre-Rule Run
1247         TkTestUtils.verifyAggregateHourSums(principalId, "Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1248                 .put("RGH", BigDecimal.valueOf(9)).build(), aggregate, 2);
1249 
1250         // Run Rule
1251 
1252         //td.setTimeBlocks(blocks);
1253         List<LeaveBlock> leaveBlocks = TimesheetUtils.getLeaveBlocksForTimesheet(td);
1254         List<TimeBlock> initialBlocks = TimesheetUtils.getTimesheetTimeblocksForProcessing(td, true);
1255         List<TimeBlock> referenceTimeBlocks = TimesheetUtils.getReferenceTimeBlocks(initialBlocks);
1256 
1257         //reset time block
1258         //List<TimeBlock> tbs = TkServiceLocator.getTimesheetService().resetTimeBlock(initialBlocks, td.getAsOfDate());
1259         TimesheetUtils.processTimeBlocksWithRuleChange(blocks, referenceTimeBlocks, leaveBlocks, td.getCalendarEntry(), td, HrContext.getPrincipalId());
1260 
1261         //refresh Timesheet
1262         td = TkServiceLocator.getTimesheetService().getTimesheetDocument(td.getDocumentId());
1263         aggregate = new TkTimeBlockAggregate(td.getTimeBlocks(), payCalendarEntry);
1264         // Verify post-Rule Run
1265         TkTestUtils.verifyAggregateHourSums(principalId, "Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1266                         .put("RGH", BigDecimal.valueOf(9))
1267                         .put("SHDY", BigDecimal.valueOf(4)).build(),
1268                 aggregate,
1269                 2);
1270     }
1271 
1272 
1273     @Test
1274     public void daysTestTimeZone() {
1275         // Create the Rule
1276         // Matches HR Job ID #1 (job # 30)
1277         String principalId = "10115";
1278 
1279 
1280         Long jobNumber = 0L;
1281         String groupKey = "IU-IN";
1282         Long workArea = 1010L;
1283         Long task = 0L;
1284         HrContext.setTargetPrincipalId(principalId);
1285         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1286 
1287 
1288         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1289         DateTime start = new DateTime(2010, 3, 21, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1290         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1291 
1292         TimesheetDocument td = TkTestUtils.populateBlankTimesheetDocument(start, principalId);
1293 
1294         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1295 
1296         //rgh - sunday 3p-5p shift: 2 hr SHDY
1297         DateTime tbStart1 = new DateTime(2010, 3, 28, 15, 0, 0, 0, zone);
1298         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1299 
1300         //rgh - monday 3p-5p - shift: none
1301         DateTime tbStart2 = new DateTime(2010, 3, 29, 15, 0, 0, 0, zone);
1302         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1303 
1304         //rgh - sun 10p- mon 3a - shift: 2hr SHDY2 (on sunday timeblock)
1305         DateTime tbStart3 = new DateTime(2010, 3, 28, 22, 0, 0, 0, zone);
1306         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1307         DateTime tbStart4 = new DateTime(2010, 3, 29, 0, 0, 0, 0, zone);
1308         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart4,   1, new BigDecimal("3"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1309 
1310 
1311         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1312 
1313         // Verify pre-Rule Run
1314         TkTestUtils.verifyAggregateHourSums(principalId, "Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1315                 .put("RGH", BigDecimal.valueOf(9)).build(), aggregate, 2);
1316 
1317         // Run Rule
1318 
1319         //td.setTimeBlocks(blocks);
1320         List<LeaveBlock> leaveBlocks = TimesheetUtils.getLeaveBlocksForTimesheet(td);
1321         List<TimeBlock> initialBlocks = TimesheetUtils.getTimesheetTimeblocksForProcessing(td, true);
1322         List<TimeBlock> referenceTimeBlocks = TimesheetUtils.getReferenceTimeBlocks(initialBlocks);
1323 
1324         //reset time block
1325         //List<TimeBlock> tbs = TkServiceLocator.getTimesheetService().resetTimeBlock(initialBlocks, td.getAsOfDate());
1326         TimesheetUtils.processTimeBlocksWithRuleChange(blocks, referenceTimeBlocks, leaveBlocks, td.getCalendarEntry(), td, HrContext.getPrincipalId());
1327 
1328         //refresh Timesheet
1329         td = TkServiceLocator.getTimesheetService().getTimesheetDocument(td.getDocumentId());
1330         aggregate = new TkTimeBlockAggregate(td.getTimeBlocks(), payCalendarEntry);
1331         // Verify post-Rule Run
1332         TkTestUtils.verifyAggregateHourSums(principalId, "Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1333                         .put("RGH", BigDecimal.valueOf(9))
1334                         .put("SHDY2", BigDecimal.valueOf(4)).build(),
1335                 aggregate,
1336                 2);
1337         HrContext.clearTargetUser();
1338     }
1339 
1340     @Test
1341     public void daysTimeTest() {
1342         // Create the Rule
1343         // Matches HR Job ID #1 (job # 30)
1344         String principalId = "10116";
1345 
1346 
1347         Long jobNumber = 0L;
1348         String groupKey = "IU-IN";
1349         Long workArea = 1010L;
1350         Long task = 0L;
1351         HrContext.setTargetPrincipalId(principalId);
1352         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1353 
1354 
1355         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1356         DateTime start = new DateTime(2010, 3, 21, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1357         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1358 
1359         TimesheetDocument td = TkTestUtils.populateBlankTimesheetDocument(start, principalId);
1360 
1361         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1362 
1363         //rgh - monday 2p-4p - shift: 1 hr SHDT
1364         DateTime tbStart1 = new DateTime(2010, 3, 22, 14, 0, 0, 0, zone);
1365         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1366 
1367         //rgh - tuesday 4:30p-8p - shift: 3.5 hr SHDT
1368         DateTime tbStart2 = new DateTime(2010, 3, 23, 16, 30, 0, 0, zone);
1369         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("3.5"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1370 
1371         //rgh - sun 3p - 8p - no shift
1372         DateTime tbStart3 = new DateTime(2010, 3, 21, 15, 0, 0, 0, zone);
1373         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3,   1, new BigDecimal("5"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1374 
1375         //rgh - wed, thurs, fri, sat 2p - 7p  - shift: SHDT 4hr on wed-fri (none saturday)
1376         DateTime tbStart4 = new DateTime(2010, 3, 24, 14, 0, 0, 0, zone);
1377         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart4,   4, new BigDecimal("5"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1378 
1379 
1380         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1381 
1382         // Verify pre-Rule Run
1383         TkTestUtils.verifyAggregateHourSums(principalId, "Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1384                 .put("RGH", BigDecimal.valueOf(30.5)).build(), aggregate, 1);
1385 
1386         // Run Rule
1387 
1388         //td.setTimeBlocks(blocks);
1389         List<LeaveBlock> leaveBlocks = TimesheetUtils.getLeaveBlocksForTimesheet(td);
1390         List<TimeBlock> initialBlocks = TimesheetUtils.getTimesheetTimeblocksForProcessing(td, true);
1391         List<TimeBlock> referenceTimeBlocks = TimesheetUtils.getReferenceTimeBlocks(initialBlocks);
1392 
1393         //reset time block
1394         //List<TimeBlock> tbs = TkServiceLocator.getTimesheetService().resetTimeBlock(initialBlocks, td.getAsOfDate());
1395         TimesheetUtils.processTimeBlocksWithRuleChange(blocks, referenceTimeBlocks, leaveBlocks, td.getCalendarEntry(), td, HrContext.getPrincipalId());
1396 
1397         //refresh Timesheet
1398         td = TkServiceLocator.getTimesheetService().getTimesheetDocument(td.getDocumentId());
1399         aggregate = new TkTimeBlockAggregate(td.getTimeBlocks(), payCalendarEntry);
1400         // Verify post-Rule Run
1401         TkTestUtils.verifyAggregateHourSums(principalId, "Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1402                         .put("RGH", BigDecimal.valueOf(30.5))
1403                         .put("SHDT", BigDecimal.valueOf(16.5)).build(),
1404                 aggregate,
1405                 1);
1406         HrContext.clearTargetUser();
1407     }
1408 
1409     @Test
1410     public void timeTest() {
1411         // Create the Rule
1412         // Matches HR Job ID #1 (job # 30)
1413         String principalId = "10117";
1414 
1415 
1416         Long jobNumber = 0L;
1417         String groupKey = "IU-IN";
1418         Long workArea = 1010L;
1419         Long task = 0L;
1420         HrContext.setTargetPrincipalId(principalId);
1421         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1422 
1423 
1424         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1425         DateTime start = new DateTime(2010, 3, 21, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1426         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1427 
1428         TimesheetDocument td = TkTestUtils.populateBlankTimesheetDocument(start, principalId);
1429 
1430         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1431 
1432         //rgh - sunday 3p - monday 3a - shift: 9 hr SHTT
1433         DateTime tbStart1a = new DateTime(2010, 3, 21, 15, 0, 0, 0, zone);
1434         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1a,   1, new BigDecimal("9"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1435         DateTime tbStart1b = new DateTime(2010, 3, 22, 0, 0, 0, 0, zone);
1436         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1b,   1, new BigDecimal("3"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1437 
1438 
1439         //rgh - monday 3a-9a - shift: 3 hr SHTT
1440         DateTime tbStart2 = new DateTime(2010, 3, 22, 3, 0, 0, 0, zone);
1441         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("6"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1442 
1443         //rgh - monday 10p - 11:30p - shift: 1.5 hr SHTT
1444         DateTime tbStart3 = new DateTime(2010, 3, 22, 22, 0, 0, 0, zone);
1445         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3,   1, new BigDecimal("1.5"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1446 
1447         //rgh - tues 12a - 7a  - shift: SHTT 6hr
1448         DateTime tbStart4 = new DateTime(2010, 3, 23, 0, 0, 0, 0, zone);
1449         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart4,   1, new BigDecimal("7"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1450 
1451         //rgh - wed 6a - 11a  - no shift
1452         DateTime tbStart5 = new DateTime(2010, 3, 24, 6, 0, 0, 0, zone);
1453         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart5,   1, new BigDecimal("5"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1454 
1455 
1456         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1457 
1458         // Verify pre-Rule Run
1459         TkTestUtils.verifyAggregateHourSums(principalId, "Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1460                 .put("RGH", BigDecimal.valueOf(31.5)).build(), aggregate, 1);
1461 
1462         // Run Rule
1463 
1464         //td.setTimeBlocks(blocks);
1465         List<LeaveBlock> leaveBlocks = TimesheetUtils.getLeaveBlocksForTimesheet(td);
1466         List<TimeBlock> initialBlocks = TimesheetUtils.getTimesheetTimeblocksForProcessing(td, true);
1467         List<TimeBlock> referenceTimeBlocks = TimesheetUtils.getReferenceTimeBlocks(initialBlocks);
1468 
1469         //reset time block
1470         //List<TimeBlock> tbs = TkServiceLocator.getTimesheetService().resetTimeBlock(initialBlocks, td.getAsOfDate());
1471         TimesheetUtils.processTimeBlocksWithRuleChange(blocks, referenceTimeBlocks, leaveBlocks, td.getCalendarEntry(), td, HrContext.getPrincipalId());
1472 
1473         //refresh Timesheet
1474         td = TkServiceLocator.getTimesheetService().getTimesheetDocument(td.getDocumentId());
1475         aggregate = new TkTimeBlockAggregate(td.getTimeBlocks(), payCalendarEntry);
1476         // Verify post-Rule Run
1477         TkTestUtils.verifyAggregateHourSums(principalId, "Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1478                         .put("RGH", BigDecimal.valueOf(31.5))
1479                         .put("SHTT", BigDecimal.valueOf(19.5)).build(),
1480                 aggregate,
1481                 1);
1482         HrContext.clearTargetUser();
1483     }
1484 
1485 
1486     @Ignore
1487     @Test
1488     public void minHoursTest() {
1489         // Create the Rule
1490         // Matches HR Job ID #1 (job # 30)
1491         String principalId = "10118";
1492 
1493 
1494         Long jobNumber = 0L;
1495         String groupKey = "IU-IN";
1496         Long workArea = 1010L;
1497         Long task = 0L;
1498         HrContext.setTargetPrincipalId(principalId);
1499         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1500 
1501 
1502         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1503         DateTime start = new DateTime(2010, 3, 21, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1504         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1505 
1506         TimesheetDocument td = TkTestUtils.populateBlankTimesheetDocument(start, principalId);
1507 
1508         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1509 
1510         //rgh - sunday 3p - monday 3a - shift: 9 hr SHMH
1511         DateTime tbStart1a = new DateTime(2010, 3, 21, 15, 0, 0, 0, zone);
1512         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1a,   1, new BigDecimal("9"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1513         DateTime tbStart1b = new DateTime(2010, 3, 22, 0, 0, 0, 0, zone);
1514         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1b,   1, new BigDecimal("3"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1515 
1516 
1517         //rgh - monday 3a-9a - shift: 3 hr SHMH
1518         DateTime tbStart2 = new DateTime(2010, 3, 22, 3, 0, 0, 0, zone);
1519         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("6"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1520 
1521         //rgh - monday 10p - 11:30p - shift: 1.5 hr SHMH (after timeblock added below)
1522         DateTime tbStart3 = new DateTime(2010, 3, 22, 22, 0, 0, 0, zone);
1523         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3,   1, new BigDecimal("1.5"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1524 
1525         //rgh - tues 12a - 7a  - shift: SHTT 6hr
1526         DateTime tbStart4 = new DateTime(2010, 3, 23, 0, 0, 0, 0, zone);
1527         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart4,   1, new BigDecimal("7"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1528 
1529         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1530 
1531         // Verify pre-Rule Run
1532         TkTestUtils.verifyAggregateHourSums(principalId, "Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1533                 .put("RGH", BigDecimal.valueOf(26.5)).build(), aggregate, 1);
1534 
1535         // Run Rule
1536 
1537         //td.setTimeBlocks(blocks);
1538         List<LeaveBlock> leaveBlocks = TimesheetUtils.getLeaveBlocksForTimesheet(td);
1539         List<TimeBlock> initialBlocks = TimesheetUtils.getTimesheetTimeblocksForProcessing(td, true);
1540         List<TimeBlock> referenceTimeBlocks = TimesheetUtils.getReferenceTimeBlocks(initialBlocks);
1541 
1542         //reset time block
1543         //List<TimeBlock> tbs = TkServiceLocator.getTimesheetService().resetTimeBlock(initialBlocks, td.getAsOfDate());
1544         TimesheetUtils.processTimeBlocksWithRuleChange(blocks, referenceTimeBlocks, leaveBlocks, td.getCalendarEntry(), td, HrContext.getPrincipalId());
1545 
1546         //refresh Timesheet
1547         td = TkServiceLocator.getTimesheetService().getTimesheetDocument(td.getDocumentId());
1548         aggregate = new TkTimeBlockAggregate(td.getTimeBlocks(), payCalendarEntry);
1549         // Verify post-Rule Run
1550         TkTestUtils.verifyAggregateHourSums(principalId, "Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1551                         .put("RGH", BigDecimal.valueOf(26.5))
1552                         .put("SHTT", BigDecimal.valueOf(7.5))
1553                         .put("SHMH", BigDecimal.valueOf(12)).build(),
1554                 aggregate,
1555                 1);
1556         HrContext.clearTargetUser();
1557     }
1558 }