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     @Ignore
425     @Test
426     /**
427      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
428      *
429      * Create a timeblock on two days, one day has normal REG shift eligible
430      * hours, one day has HOL time.
431      *
432      * Modified version of the simple case, SDR from 12:00 to 17:00, every day,
433      * must have at least 4 hours with a maximum 15 minute gap.
434      *
435      */
436     public void simpleCaseWithWorkSchedule() throws Exception {
437 		// Create the Rule
438 		boolean[] dayArray = {true, true, true, true, true, true, true};
439 		// Matches HR Job ID #1 (job # 30)
440 		Long jobNumber = 30L;
441 		Long workArea = 0L;
442 		this.createShiftDifferentialRule(
443 				"BWS-CAL",
444 				"REG",
445 				"PRM",
446 				"IN",  // changed from "SD1" to "IN" for changes of adding groupKeyCode to Job
447 				"SD1",
448 				"SD1",
449 				(new LocalTime(12, 0)),
450 				(new LocalTime(17, 0)),
451 				new BigDecimal(4), // minHours
452 				new BigDecimal("15.00"), // maxGap
453 				dayArray);
454 
455 		// Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
456 		DateTime start = new DateTime(2010, 3, 29, 12, 0, 0, 0, TKUtils.getSystemDateTimeZone());
457         DateTime holtime = new DateTime(2010, 3, 30, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
458 		List<TimeBlock> blocks = new ArrayList<TimeBlock>();
459 		CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
460 		blocks.addAll(TkTestUtils.createUniformTimeBlocks(start,   1, new BigDecimal("4"), "REG", jobNumber, workArea));
461         blocks.addAll(TkTestUtils.createUniformTimeBlocks(holtime, 1, new BigDecimal("4"), "HOL", jobNumber, workArea));
462 
463 		TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
464 
465 		// Verify pre-Rule Run
466 		TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("REG", new BigDecimal(4));put("HOL", new BigDecimal(4));}},aggregate,2);
467 
468 		// Run Rule
469 		TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
470 		tdoc.setTimeBlocks(blocks);
471 		TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
472 
473 		// Verify post-Rule Run
474 		TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(8));put("REG", new BigDecimal(4));}},aggregate,2);
475 
476     }
477 
478 
479 
480     /**
481      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
482      *
483      * Create a single 24 hour timeblock that spans two different shift, both exceeding the min hours
484      */
485     @Test
486     public void overlapMultipleShiftsWithSameTimeBlock() {
487         // Create the Rule
488         boolean[] dayArray = {true, true, true, true, true, true, true};
489         // Matches HR Job ID #1 (job # 30)
490         Long jobNumber = 30L;
491         Long workArea = 0L;
492         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
493 
494         //3pm to 8am, 6 hour minimum, 90 minute max gap
495         this.createShiftDifferentialRule(
496                 "BWS-CAL",
497                 "REG",
498                 "PRM",
499                 "IN",
500                 "SD1",
501                 "SD1",
502                 (new LocalTime(15, 0)),
503                 (new LocalTime(8, 0)),
504                 new BigDecimal(6), // minHours
505                 new BigDecimal("90.00"), // maxGap
506                 dayArray);
507 
508         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
509         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
510         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
511 
512         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
513         DateTime tbStart = new DateTime(2010, 3, 29, 0, 0, 0, 0, zone);
514 
515         //24 time block (midnight to midnight)
516         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart, 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(24));}},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         TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(17));put("REG", new BigDecimal(24));}},aggregate,2);
530 
531     }
532 
533     /**
534      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
535      *
536      * Create a two 24 hour timeblocks that span three different shifts, all exceeding the min hours
537      */
538     @Test
539     public void overlapMultipleShiftsWithMultipleTimeBlocks() {
540         // Create the Rule
541         boolean[] dayArray = {true, true, true, true, true, true, true};
542         // Matches HR Job ID #1 (job # 30)
543         Long jobNumber = 30L;
544         Long workArea = 0L;
545         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
546 
547         //3pm to 8am, 6 hour minimum, 90 minute max gap
548         this.createShiftDifferentialRule(
549                 "BWS-CAL",
550                 "REG",
551                 "PRM",
552                 "IN",
553                 "SD1",
554                 "SD1",
555                 (new LocalTime(15, 0)),
556                 (new LocalTime(8, 0)),
557                 new BigDecimal(6), // minHours
558                 new BigDecimal("90.00"), // maxGap
559                 dayArray);
560 
561         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
562         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
563         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
564 
565         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
566         DateTime tbStart = new DateTime(2010, 3, 29, 0, 0, 0, 0, zone);
567 
568         //24 time block (midnight to midnight)
569         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart, 1, new BigDecimal("24"), "REG", jobNumber, workArea));
570 
571         //24 time block (midnight to midnight)
572         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart.plusDays(1), 1, new BigDecimal("24"), "REG", jobNumber, workArea));
573 
574         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
575 
576         // Verify pre-Rule Run
577         TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("REG", new BigDecimal(48));}},aggregate,2);
578 
579         // Run Rule
580         TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
581         tdoc.setTimeBlocks(blocks);
582         TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
583 
584         // Verify post-Rule Run
585         //overlaps from 12a-8a (8 hours), 3p-8a (17 hours), and 3p - 12a (9 hours) == 34 hours
586         TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(34));put("REG", new BigDecimal(48));}},aggregate,2);
587 
588     }
589 
590     /**
591      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
592      *
593      * Create a single 24 hour timeblock that spans two different shift, both exceeding the min hours
594      */
595     @Test
596     public void overlapMultipleShiftsWithSameTimeBlockExceedingMinOnOneShift() {
597         // Create the Rule
598         boolean[] dayArray = {true, true, true, true, true, true, true};
599         // Matches HR Job ID #1 (job # 30)
600         Long jobNumber = 30L;
601         Long workArea = 0L;
602         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
603 
604         //3pm to 8am, 6 hour minimum, 90 minute max gap
605         this.createShiftDifferentialRule(
606                 "BWS-CAL",
607                 "REG",
608                 "PRM",
609                 "IN",
610                 "SD1",
611                 "SD1",
612                 (new LocalTime(15, 0)),
613                 (new LocalTime(8, 0)),
614                 new BigDecimal(6), // minHours
615                 new BigDecimal("90.00"), // maxGap
616                 dayArray);
617 
618         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
619         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
620         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
621 
622         //reg timeblock 3am - midnight
623         DateTime tbStart = new DateTime(2010, 3, 30, 3, 0, 0, 0, zone);
624         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart,   1, new BigDecimal("21"), "REG", jobNumber, workArea));
625 
626         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
627 
628         // Verify pre-Rule Run
629         TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
630                 .put("PRM", BigDecimal.ZERO)
631                 .put("REG", new BigDecimal(21)).build(), aggregate, 2);
632 
633         // Run Rule
634         TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
635         tdoc.setTimeBlocks(blocks);
636         TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
637 
638         // Verify post-Rule Run
639         TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
640                 .put("PRM", new BigDecimal(9))
641                 .put("REG", new BigDecimal(21)).build(),
642                 aggregate,
643                 2);
644 
645     }
646 
647     /**
648      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
649      *
650      * Create a single 24 hour timeblock that spans two different shift, both exceeding the min hours
651      */
652     @Test
653     public void overlapMultipleShiftsWithSameTimeBlockExceedingMinOnFirstShift() {
654         // Create the Rule
655         boolean[] dayArray = {true, true, true, true, true, true, true};
656         // Matches HR Job ID #1 (job # 30)
657         Long jobNumber = 30L;
658         Long workArea = 0L;
659         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
660 
661         //3pm to 8am, 6 hour minimum, 90 minute max gap
662         this.createShiftDifferentialRule(
663                 "BWS-CAL",
664                 "REG",
665                 "PRM",
666                 "IN",
667                 "SD1",
668                 "SD1",
669                 (new LocalTime(15, 0)),
670                 (new LocalTime(8, 0)),
671                 new BigDecimal(6), // minHours
672                 new BigDecimal("90.00"), // maxGap
673                 dayArray);
674 
675         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
676         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
677         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
678 
679         //reg timeblock 1am - 9pm
680         DateTime tbStart = new DateTime(2010, 3, 30, 1, 0, 0, 0, zone);
681         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart,   1, new BigDecimal("19"), "REG", jobNumber, workArea));
682 
683         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
684 
685         // Verify pre-Rule Run
686         TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
687                 .put("PRM", BigDecimal.ZERO)
688                 .put("REG", new BigDecimal(19)).build(), aggregate, 2);
689 
690         // Run Rule
691         TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
692         tdoc.setTimeBlocks(blocks);
693         TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
694 
695         // Verify post-Rule Run
696         TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
697                         .put("PRM", new BigDecimal(7))
698                         .put("REG", new BigDecimal(19)).build(),
699                 aggregate,
700                 2);
701 
702     }
703 
704     /**
705      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
706      *
707      * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
708      */
709     @Test
710     public void overlapMultipleShiftsWithSameTimeBlocNeitherExceedingMin() {
711         // Create the Rule
712         boolean[] dayArray = {true, true, true, true, true, true, true};
713         // Matches HR Job ID #1 (job # 30)
714         Long jobNumber = 30L;
715         Long workArea = 0L;
716         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
717 
718         //3pm to 8am, 6 hour minimum, 90 minute max gap
719         this.createShiftDifferentialRule(
720                 "BWS-CAL",
721                 "REG",
722                 "PRM",
723                 "IN",
724                 "SD1",
725                 "SD1",
726                 (new LocalTime(15, 0)),
727                 (new LocalTime(8, 0)),
728                 new BigDecimal(6), // minHours
729                 new BigDecimal("90.00"), // maxGap
730                 dayArray);
731 
732         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
733         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
734         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
735         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
736 
737         //reg timeblock 3am - 8pm
738         DateTime tbStart = new DateTime(2010, 3, 30, 3, 0, 0, 0, zone);
739         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart,   1, new BigDecimal("17"), "REG", jobNumber, workArea));
740 
741         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
742 
743         // Verify pre-Rule Run
744         TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
745                 .put("PRM", BigDecimal.ZERO)
746                 .put("REG", new BigDecimal(17)).build(), aggregate, 2);
747 
748         // Run Rule
749         TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
750         tdoc.setTimeBlocks(blocks);
751         TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
752 
753         // Verify post-Rule Run
754         TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
755                         .put("PRM", BigDecimal.ZERO)
756                         .put("REG", new BigDecimal(17)).build(),
757                 aggregate,
758                 2);
759     }
760 
761     /**
762      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
763      *
764      * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
765      */
766     @Test
767     public void multipleTimeBlocksOvernightExceedingMin() {
768         // Create the Rule
769         boolean[] dayArray = {true, true, true, true, true, true, true};
770         // Matches HR Job ID #1 (job # 30)
771         Long jobNumber = 30L;
772         Long workArea = 0L;
773         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
774 
775         //3pm to 8am, 6 hour minimum, 90 minute max gap
776         this.createShiftDifferentialRule(
777                 "BWS-CAL",
778                 "REG",
779                 "PRM",
780                 "IN",
781                 "SD1",
782                 "SD1",
783                 (new LocalTime(15, 0)),
784                 (new LocalTime(8, 0)),
785                 new BigDecimal(6), // minHours
786                 new BigDecimal("90.00"), // maxGap
787                 dayArray);
788 
789         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
790         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
791         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
792         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
793 
794         //reg timeblock 10pm - midnight
795         DateTime tbStart1 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
796         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
797         //reg timeblock midnight - 5am
798         DateTime tbStart2 = new DateTime(2010, 3, 31, 0, 0, 0, 0, zone);
799         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("5"), "REG", jobNumber, workArea));
800 
801         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
802 
803         // Verify pre-Rule Run
804         TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
805                 .put("PRM", BigDecimal.ZERO)
806                 .put("REG", BigDecimal.valueOf(7)).build(), aggregate, 2);
807 
808         // Run Rule
809         TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
810         tdoc.setTimeBlocks(blocks);
811         TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
812 
813         // Verify post-Rule Run
814         TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
815                         .put("PRM", BigDecimal.valueOf(7))
816                         .put("REG", BigDecimal.valueOf(7)).build(),
817                 aggregate,
818                 2);
819     }
820 
821     /**
822      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
823      *
824      * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
825      */
826     @Test
827     public void multipleTimeBlocksOvernightExceedingMinWithSixtyMinuteGap() {
828         // Create the Rule
829         boolean[] dayArray = {true, true, true, true, true, true, true};
830         // Matches HR Job ID #1 (job # 30)
831         Long jobNumber = 30L;
832         Long workArea = 0L;
833         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
834 
835         //3pm to 8am, 6 hour minimum, 90 minute max gap
836         this.createShiftDifferentialRule(
837                 "BWS-CAL",
838                 "REG",
839                 "PRM",
840                 "IN",
841                 "SD1",
842                 "SD1",
843                 (new LocalTime(15, 0)),
844                 (new LocalTime(8, 0)),
845                 new BigDecimal(6), // minHours
846                 new BigDecimal("90.00"), // maxGap
847                 dayArray);
848 
849         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
850         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
851         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
852         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
853 
854         //reg timeblock 10pm - midnight
855         DateTime tbStart1 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
856         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
857         //reg timeblock 1am - 5am
858         DateTime tbStart2 = new DateTime(2010, 3, 31, 1, 0, 0, 0, zone);
859         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("4"), "REG", jobNumber, workArea));
860 
861         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
862 
863         // Verify pre-Rule Run
864         TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
865                 .put("PRM", BigDecimal.ZERO)
866                 .put("REG", BigDecimal.valueOf(6)).build(), aggregate, 2);
867 
868         // Run Rule
869         TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
870         tdoc.setTimeBlocks(blocks);
871         TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
872 
873         // Verify post-Rule Run
874         TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
875                         .put("PRM", BigDecimal.valueOf(6))
876                         .put("REG", BigDecimal.valueOf(6)).build(),
877                 aggregate,
878                 2);
879     }
880 
881     /**
882      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
883      *
884      * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
885      */
886     @Test
887     public void multipleTimeBlocksOvernightExceedingMinWithNinetyMinuteGap() {
888         // Create the Rule
889         boolean[] dayArray = {true, true, true, true, true, true, true};
890         // Matches HR Job ID #1 (job # 30)
891         Long jobNumber = 30L;
892         Long workArea = 0L;
893         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
894 
895         //3pm to 8am, 6 hour minimum, 90 minute max gap
896         this.createShiftDifferentialRule(
897                 "BWS-CAL",
898                 "REG",
899                 "PRM",
900                 "IN",
901                 "SD1",
902                 "SD1",
903                 (new LocalTime(15, 0)),
904                 (new LocalTime(8, 0)),
905                 new BigDecimal(6), // minHours
906                 new BigDecimal("90.00"), // maxGap
907                 dayArray);
908 
909         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
910         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
911         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
912         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
913 
914         //reg timeblock 10pm - midnight
915         DateTime tbStart1 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
916         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
917         //reg timeblock 1:30am - 5:30pm
918         DateTime tbStart2 = new DateTime(2010, 3, 31, 1, 30, 0, 0, zone);
919         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("4"), "REG", jobNumber, workArea));
920 
921         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
922 
923         // Verify pre-Rule Run
924         TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
925                 .put("PRM", BigDecimal.ZERO)
926                 .put("REG", BigDecimal.valueOf(6)).build(), aggregate, 2);
927 
928         // Run Rule
929         TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
930         tdoc.setTimeBlocks(blocks);
931         TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
932 
933         // Verify post-Rule Run
934         TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
935                         .put("PRM", BigDecimal.valueOf(6))
936                         .put("REG", BigDecimal.valueOf(6)).build(),
937                 aggregate,
938                 2);
939     }
940 
941     /**
942      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
943      *
944      * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
945      */
946     @Test
947     public void multipleTimeBlocksOvernightExceedingMinButExceedingGap() {
948         // Create the Rule
949         boolean[] dayArray = {true, true, true, true, true, true, true};
950         // Matches HR Job ID #1 (job # 30)
951         Long jobNumber = 30L;
952         Long workArea = 0L;
953         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
954 
955         //3pm to 8am, 6 hour minimum, 90 minute max gap
956         this.createShiftDifferentialRule(
957                 "BWS-CAL",
958                 "REG",
959                 "PRM",
960                 "IN",
961                 "SD1",
962                 "SD1",
963                 (new LocalTime(15, 0)),
964                 (new LocalTime(8, 0)),
965                 new BigDecimal(6), // minHours
966                 new BigDecimal("90.00"), // maxGap
967                 dayArray);
968 
969         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
970         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
971         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
972         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
973 
974         //reg timeblock 10pm - midnight
975         DateTime tbStart1 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
976         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
977         //reg timeblock 1:36am - 5:36am
978         DateTime tbStart2 = new DateTime(2010, 3, 31, 1, 36, 0, 0, zone);
979         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("4"), "REG", jobNumber, workArea));
980 
981         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
982 
983         // Verify pre-Rule Run
984         TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
985                 .put("PRM", BigDecimal.ZERO)
986                 .put("REG", BigDecimal.valueOf(6)).build(), aggregate, 2);
987 
988         // Run Rule
989         TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
990         tdoc.setTimeBlocks(blocks);
991         TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
992 
993         // Verify post-Rule Run
994         TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
995                         .put("PRM", BigDecimal.valueOf(0))
996                         .put("REG", BigDecimal.valueOf(6)).build(),
997                 aggregate,
998                 2);
999     }
1000 
1001 
1002     /**
1003      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
1004      *
1005      * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
1006      */
1007     @Test
1008     public void threeBlocksWithinSameShift() {
1009         // Create the Rule
1010         boolean[] dayArray = {true, true, true, true, true, true, true};
1011         // Matches HR Job ID #1 (job # 30)
1012         Long jobNumber = 30L;
1013         Long workArea = 0L;
1014         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1015 
1016         //3pm to 8am, 6 hour minimum, 90 minute max gap
1017         this.createShiftDifferentialRule(
1018                 "BWS-CAL",
1019                 "REG",
1020                 "PRM",
1021                 "IN",
1022                 "SD1",
1023                 "SD1",
1024                 (new LocalTime(15, 0)),
1025                 (new LocalTime(8, 0)),
1026                 new BigDecimal(6), // minHours
1027                 new BigDecimal("90.00"), // maxGap
1028                 dayArray);
1029 
1030         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1031         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1032         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1033 
1034         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1035 
1036         //reg timeblock 3pm - 5pm
1037         DateTime tbStart1 = new DateTime(2010, 3, 30, 15, 0, 0, 0, zone);
1038         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
1039 
1040         // 6pm - 9pm
1041         DateTime tbStart2 = new DateTime(2010, 3, 30, 18, 0, 0, 0, zone);
1042         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("3"), "REG", jobNumber, workArea));
1043 
1044         // 10pm - 11pm
1045         DateTime tbStart3 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
1046         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3,   1, new BigDecimal("1"), "REG", jobNumber, workArea));
1047 
1048         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1049 
1050         // Verify pre-Rule Run
1051         TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1052                 .put("PRM", BigDecimal.ZERO)
1053                 .put("REG", BigDecimal.valueOf(6)).build(), aggregate, 2);
1054 
1055         // Run Rule
1056         TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
1057         tdoc.setTimeBlocks(blocks);
1058         TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
1059 
1060         // Verify post-Rule Run
1061         TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1062                         .put("PRM", BigDecimal.valueOf(6))
1063                         .put("REG", BigDecimal.valueOf(6)).build(),
1064                 aggregate,
1065                 2);
1066     }
1067 
1068     /**
1069      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
1070      *
1071      * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
1072      */
1073     @Test
1074     public void fourBlocksWithinSameShiftSpanningTwoDays() {
1075         // Create the Rule
1076         boolean[] dayArray = {true, true, true, true, true, true, true};
1077         // Matches HR Job ID #1 (job # 30)
1078         Long jobNumber = 30L;
1079         Long workArea = 0L;
1080         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1081 
1082         //3pm to 8am, 6 hour minimum, 90 minute max gap
1083         this.createShiftDifferentialRule(
1084                 "BWS-CAL",
1085                 "REG",
1086                 "PRM",
1087                 "IN",
1088                 "SD1",
1089                 "SD1",
1090                 (new LocalTime(15, 0)),
1091                 (new LocalTime(8, 0)),
1092                 new BigDecimal(6), // minHours
1093                 new BigDecimal("90.00"), // maxGap
1094                 dayArray);
1095 
1096         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1097         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1098         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1099 
1100         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1101 
1102         //reg timeblock 5pm - 6pm
1103         DateTime tbStart1 = new DateTime(2010, 3, 30, 17, 0, 0, 0, zone);
1104         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("1"), "REG", jobNumber, workArea));
1105 
1106         // 7:30pm - 8:30pm
1107         DateTime tbStart2 = new DateTime(2010, 3, 30, 19, 30, 0, 0, zone);
1108         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("1"), "REG", jobNumber, workArea));
1109 
1110         // 10pm - midnight
1111         DateTime tbStart3 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
1112         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
1113 
1114         // 1:30am - 4:30am
1115         DateTime tbStart4 = new DateTime(2010, 3, 31, 1, 30, 0, 0, zone);
1116         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart4,   1, new BigDecimal("3"), "REG", jobNumber, workArea));
1117 
1118         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1119 
1120         // Verify pre-Rule Run
1121         TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1122                 .put("PRM", BigDecimal.ZERO)
1123                 .put("REG", BigDecimal.valueOf(7)).build(), aggregate, 2);
1124 
1125         // Run Rule
1126         TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
1127         tdoc.setTimeBlocks(blocks);
1128         TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
1129 
1130         // Verify post-Rule Run
1131         TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1132                         .put("PRM", BigDecimal.valueOf(7))
1133                         .put("REG", BigDecimal.valueOf(7)).build(),
1134                 aggregate,
1135                 2);
1136     }
1137 
1138 
1139     /**
1140      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
1141      *
1142      * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
1143      */
1144     @Test
1145     public void EarnGroupTest() {
1146         // Create the Rule
1147         // Matches HR Job ID #1 (job # 30)
1148         String principalId = "10112";
1149 
1150 
1151         Long jobNumber = 0L;
1152         String groupKey = "IU-IN";
1153         Long workArea = 1010L;
1154         Long task = 0L;
1155         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1156 
1157 
1158         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1159         DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1160         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1161 
1162         TimesheetDocument td = TkTestUtils.populateBlankTimesheetDocument(start, principalId);
1163 
1164         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1165 
1166         //rgh - monday 8a-11a no-shift
1167         DateTime tbStart1 = new DateTime(2010, 3, 30, 8, 0, 0, 0, zone);
1168         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("3"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1169 
1170         //echr - monday 2p-5p - should have 3 hrs SHEG
1171         DateTime tbStart2 = new DateTime(2010, 3, 30, 14, 0, 0, 0, zone);
1172         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("3"), "ECHR", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1173 
1174         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1175 
1176         // Verify pre-Rule Run
1177         TkTestUtils.verifyAggregateHourSums(principalId, "Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1178                 .put("RGH", BigDecimal.valueOf(3))
1179                 .put("ECHR", BigDecimal.valueOf(3)).build(), aggregate, 2);
1180 
1181         // Run Rule
1182 
1183         //td.setTimeBlocks(blocks);
1184         List<LeaveBlock> leaveBlocks = TimesheetUtils.getLeaveBlocksForTimesheet(td);
1185         List<TimeBlock> initialBlocks = TimesheetUtils.getTimesheetTimeblocksForProcessing(td, true);
1186         List<TimeBlock> referenceTimeBlocks = TimesheetUtils.getReferenceTimeBlocks(initialBlocks);
1187 
1188         //reset time block
1189         //List<TimeBlock> tbs = TkServiceLocator.getTimesheetService().resetTimeBlock(initialBlocks, td.getAsOfDate());
1190         TimesheetUtils.processTimeBlocksWithRuleChange(blocks, referenceTimeBlocks, leaveBlocks, td.getCalendarEntry(), td, HrContext.getPrincipalId());
1191 
1192         //refresh Timesheet
1193         td = TkServiceLocator.getTimesheetService().getTimesheetDocument(td.getDocumentId());
1194         aggregate = new TkTimeBlockAggregate(td.getTimeBlocks(), payCalendarEntry);
1195         // Verify post-Rule Run
1196         TkTestUtils.verifyAggregateHourSums(principalId, "Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1197                         .put("RGH", BigDecimal.valueOf(3))
1198                         .put("ECHR", BigDecimal.valueOf(3))
1199                         .put("SHEG", BigDecimal.valueOf(3)).build(),
1200                 aggregate,
1201                 2);
1202     }
1203 
1204     @Test
1205     public void calendarGroupTest() {
1206         // Create the Rule
1207         // Matches HR Job ID #1 (job # 30)
1208         String principalId = "10113";
1209 
1210 
1211         Long jobNumber = 0L;
1212         String groupKey = "IU-IN";
1213         Long workArea = 1010L;
1214         Long task = 0L;
1215         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1216 
1217 
1218         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1219         DateTime start = new DateTime(2010, 3, 16, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1220         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates(principalId, start);
1221 
1222         TimesheetDocument td = TkTestUtils.populateBlankTimesheetDocument(start, principalId);
1223 
1224         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1225 
1226         //rgh - monday 8a-11a no-shift
1227         DateTime tbStart1 = new DateTime(2010, 3, 21, 8, 0, 0, 0, zone);
1228         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("3"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1229 
1230         //echr - monday 2p-5p - should have 3 hrs SHEG
1231         //DateTime tbStart2 = new DateTime(2010, 3, 30, 14, 0, 0, 0, zone);
1232         //blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("3"), "ECHR", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1233 
1234         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1235 
1236         // Verify pre-Rule Run
1237         TkTestUtils.verifyAggregateHourSums(principalId, "Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1238                 .put("RGH", BigDecimal.valueOf(3)).build(), aggregate, 1);
1239 
1240         // Run Rule
1241 
1242         //td.setTimeBlocks(blocks);
1243         List<LeaveBlock> leaveBlocks = TimesheetUtils.getLeaveBlocksForTimesheet(td);
1244         List<TimeBlock> initialBlocks = TimesheetUtils.getTimesheetTimeblocksForProcessing(td, true);
1245         List<TimeBlock> referenceTimeBlocks = TimesheetUtils.getReferenceTimeBlocks(initialBlocks);
1246 
1247         //reset time block
1248         //List<TimeBlock> tbs = TkServiceLocator.getTimesheetService().resetTimeBlock(initialBlocks, td.getAsOfDate());
1249         TimesheetUtils.processTimeBlocksWithRuleChange(blocks, referenceTimeBlocks, leaveBlocks, td.getCalendarEntry(), td, HrContext.getPrincipalId());
1250 
1251         //refresh Timesheet
1252         td = TkServiceLocator.getTimesheetService().getTimesheetDocument(td.getDocumentId());
1253         aggregate = new TkTimeBlockAggregate(td.getTimeBlocks(), payCalendarEntry);
1254         // Verify post-Rule Run
1255         TkTestUtils.verifyAggregateHourSums(principalId, "Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1256                         .put("RGH", BigDecimal.valueOf(3))
1257                         .put("SHCG", BigDecimal.valueOf(3))
1258                         .put("SHDY", BigDecimal.valueOf(0)).build(),
1259                 aggregate,
1260                 1);
1261     }
1262 
1263     @Test
1264     public void daysTest() {
1265         // Create the Rule
1266         // Matches HR Job ID #1 (job # 30)
1267         String principalId = "10114";
1268 
1269 
1270         Long jobNumber = 0L;
1271         String groupKey = "IU-IN";
1272         Long workArea = 1010L;
1273         Long task = 0L;
1274         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1275 
1276 
1277         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1278         DateTime start = new DateTime(2010, 3, 21, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1279         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1280 
1281         TimesheetDocument td = TkTestUtils.populateBlankTimesheetDocument(start, principalId);
1282 
1283         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1284 
1285         //rgh - sunday 3p-5p shift: 2 hr SHDY
1286         DateTime tbStart1 = new DateTime(2010, 3, 28, 15, 0, 0, 0, zone);
1287         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1288 
1289         //rgh - monday 3p-5p - shift: none
1290         DateTime tbStart2 = new DateTime(2010, 3, 29, 15, 0, 0, 0, zone);
1291         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1292 
1293         //rgh - sun 10p- mon 3a - shift: 2hr SHDY (on sunday timeblock)
1294         DateTime tbStart3 = new DateTime(2010, 3, 28, 22, 0, 0, 0, zone);
1295         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1296         DateTime tbStart4 = new DateTime(2010, 3, 29, 0, 0, 0, 0, zone);
1297         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart4,   1, new BigDecimal("3"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1298 
1299 
1300         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1301 
1302         // Verify pre-Rule Run
1303         TkTestUtils.verifyAggregateHourSums(principalId, "Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1304                 .put("RGH", BigDecimal.valueOf(9)).build(), aggregate, 2);
1305 
1306         // Run Rule
1307 
1308         //td.setTimeBlocks(blocks);
1309         List<LeaveBlock> leaveBlocks = TimesheetUtils.getLeaveBlocksForTimesheet(td);
1310         List<TimeBlock> initialBlocks = TimesheetUtils.getTimesheetTimeblocksForProcessing(td, true);
1311         List<TimeBlock> referenceTimeBlocks = TimesheetUtils.getReferenceTimeBlocks(initialBlocks);
1312 
1313         //reset time block
1314         //List<TimeBlock> tbs = TkServiceLocator.getTimesheetService().resetTimeBlock(initialBlocks, td.getAsOfDate());
1315         TimesheetUtils.processTimeBlocksWithRuleChange(blocks, referenceTimeBlocks, leaveBlocks, td.getCalendarEntry(), td, HrContext.getPrincipalId());
1316 
1317         //refresh Timesheet
1318         td = TkServiceLocator.getTimesheetService().getTimesheetDocument(td.getDocumentId());
1319         aggregate = new TkTimeBlockAggregate(td.getTimeBlocks(), payCalendarEntry);
1320         // Verify post-Rule Run
1321         TkTestUtils.verifyAggregateHourSums(principalId, "Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1322                         .put("RGH", BigDecimal.valueOf(9))
1323                         .put("SHDY", BigDecimal.valueOf(4)).build(),
1324                 aggregate,
1325                 2);
1326     }
1327 
1328 
1329     @Test
1330     public void daysTestTimeZone() {
1331         // Create the Rule
1332         // Matches HR Job ID #1 (job # 30)
1333         String principalId = "10115";
1334 
1335 
1336         Long jobNumber = 0L;
1337         String groupKey = "IU-IN";
1338         Long workArea = 1010L;
1339         Long task = 0L;
1340         HrContext.setTargetPrincipalId(principalId);
1341         DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1342 
1343 
1344         // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1345         DateTime start = new DateTime(2010, 3, 21, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1346         CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1347 
1348         TimesheetDocument td = TkTestUtils.populateBlankTimesheetDocument(start, principalId);
1349 
1350         List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1351 
1352         //rgh - sunday 3p-5p shift: 2 hr SHDY
1353         DateTime tbStart1 = new DateTime(2010, 3, 28, 15, 0, 0, 0, zone);
1354         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1355 
1356         //rgh - monday 3p-5p - shift: none
1357         DateTime tbStart2 = new DateTime(2010, 3, 29, 15, 0, 0, 0, zone);
1358         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1359 
1360         //rgh - sun 10p- mon 3a - shift: 2hr SHDY2 (on sunday timeblock)
1361         DateTime tbStart3 = new DateTime(2010, 3, 28, 22, 0, 0, 0, zone);
1362         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1363         DateTime tbStart4 = new DateTime(2010, 3, 29, 0, 0, 0, 0, zone);
1364         blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart4,   1, new BigDecimal("3"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1365 
1366 
1367         TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1368 
1369         // Verify pre-Rule Run
1370         TkTestUtils.verifyAggregateHourSums(principalId, "Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1371                 .put("RGH", BigDecimal.valueOf(9)).build(), aggregate, 2);
1372 
1373         // Run Rule
1374 
1375         //td.setTimeBlocks(blocks);
1376         List<LeaveBlock> leaveBlocks = TimesheetUtils.getLeaveBlocksForTimesheet(td);
1377         List<TimeBlock> initialBlocks = TimesheetUtils.getTimesheetTimeblocksForProcessing(td, true);
1378         List<TimeBlock> referenceTimeBlocks = TimesheetUtils.getReferenceTimeBlocks(initialBlocks);
1379 
1380         //reset time block
1381         //List<TimeBlock> tbs = TkServiceLocator.getTimesheetService().resetTimeBlock(initialBlocks, td.getAsOfDate());
1382         TimesheetUtils.processTimeBlocksWithRuleChange(blocks, referenceTimeBlocks, leaveBlocks, td.getCalendarEntry(), td, HrContext.getPrincipalId());
1383 
1384         //refresh Timesheet
1385         td = TkServiceLocator.getTimesheetService().getTimesheetDocument(td.getDocumentId());
1386         aggregate = new TkTimeBlockAggregate(td.getTimeBlocks(), payCalendarEntry);
1387         // Verify post-Rule Run
1388         TkTestUtils.verifyAggregateHourSums(principalId, "Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1389                         .put("RGH", BigDecimal.valueOf(9))
1390                         .put("SHDY2", BigDecimal.valueOf(4)).build(),
1391                 aggregate,
1392                 2);
1393         HrContext.clearTargetUser();
1394     }
1395 }