View Javadoc

1   /**
2    * Copyright 2004-2013 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.service;
17  
18  import java.math.BigDecimal;
19  import java.sql.Date;
20  import java.sql.Time;
21  import java.util.ArrayList;
22  import java.util.HashMap;
23  import java.util.List;
24  
25  import org.joda.time.DateTime;
26  import org.joda.time.DateTimeZone;
27  import org.joda.time.LocalDate;
28  import org.joda.time.LocalTime;
29  import org.junit.Assert;
30  import org.junit.Ignore;
31  import org.junit.Test;
32  import org.kuali.hr.test.KPMETestCase;
33  import org.kuali.hr.time.assignment.Assignment;
34  import org.kuali.hr.time.assignment.AssignmentDescriptionKey;
35  import org.kuali.hr.time.calendar.CalendarEntries;
36  import org.kuali.hr.time.service.base.TkServiceLocator;
37  import org.kuali.hr.time.shiftdiff.rule.ShiftDifferentialRule;
38  import org.kuali.hr.time.test.TkTestUtils;
39  import org.kuali.hr.time.timeblock.TimeBlock;
40  import org.kuali.hr.time.timesheet.TimesheetDocument;
41  import org.kuali.hr.time.util.TKContext;
42  import org.kuali.hr.time.util.TKUtils;
43  import org.kuali.hr.time.util.TkConstants;
44  import org.kuali.hr.time.util.TkTimeBlockAggregate;
45  import org.kuali.hr.time.workschedule.WorkSchedule;
46  import org.kuali.hr.time.workschedule.WorkScheduleAssignment;
47  import org.kuali.hr.time.workschedule.WorkScheduleEntry;
48  
49  /**
50   *
51   * @author djunk
52   *
53   */
54  public class ShiftDifferentialRuleServiceProcessTest extends KPMETestCase {
55  
56  
57  	public static final String USER_PRINCIPAL_ID = "admin";
58  	private Date JAN_AS_OF_DATE = new Date((new DateTime(2010, 1, 1, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone())).getMillis());
59  
60  
61  	/**
62  	 * Test with boundary carryover and overlapping rules.
63  	 *
64  	 * Rule 1:
65  	 *
66  	 * Runs on Tu, Wed, Th on the interval: [22:00, 4:00)
67  	 * Max Gap: 15 minutes
68  	 * Min Hours: 3
69  	 *
70  	 * Rule 2:
71  	 *
72  	 * Runs on Tu, Th on the interval: [23:00, 2:00)
73  	 * Max Gap: 2 hours
74  	 * Min Hours: 3
75  	 *
76  	 * Rule 3:
77  	 *
78  	 * Runs on W, Th on the interval: [5:00, 12:00)
79  	 * Max Gap: 15 minutes
80  	 * Min Hours: 7 hours
81  	 *
82  	 * Rule 4:
83  	 *
84  	 * Runs on W on the interval: [5:00, 12:00)
85  	 * Max Gap: 15 minutes
86  	 * Min Hours: 5
87  	 *
88  	 *
89  	 * |--------------+----+------------+------------|
90  	 * | Tu : 8/31/10 | XX | W : 9/1/10 | Th: 9/2/10 |
91  	 * |--------------+----+------------+------------|
92  	 * | 9:45p - 11:45| XX | Mid - 5a   | 5p - 11p   |
93  	 * |              | XX | 6a - Noon  |            |
94  	 * |--------------+----+------------+------------|
95       *
96       *
97       * Aug 31: 2h  : 21:45 - 23:45 (Tue) **
98       *                           [1: 5h 45m]  // [2: 2h 45m] - Not qualifying, min hours must be 3.
99       * Sep  1: 5h  : 00:00 - 05:00 (Wed) **
100      * Sep  1: 6h  : 06:00 - 12:00 (Wed) [4: 6h]
101      *
102      * Sep  1: 2h  : 22:00 - 24:00 (Wed)
103      * Sep  2: 1h  : 00:00 - 01:00 (Thu) [1: 3h]
104      *
105      * Sep  2: 6h  : 17:00 - 22:00 (Thu)
106      *
107      * 1: [22:00,  4:00) (Tue/Wed/Thu) minimum: 3h gap: 15m
108      * 2: [23:00,  2:00) (Tue/Thu)     minimum: 3h gap: 2h
109      * 3: [05:00, 12:00) (Wed/Thu)     minimum: 7h gap: 15m
110      * 4: [05:00, 12:00) (Wed)         minimum: 5h gap: 15m
111      *
112 	 */
113 	@SuppressWarnings("serial")
114 	@Test
115 	public void testProcessTimesheetBoundaryCarryoverOverlapCase() throws Exception {
116         DateTimeZone tz = TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback();
117 		// Create the Rule    Sun,   Mon,   Tue,  Wed,   Thu,  Fri,  Sat
118 		boolean[] dayArray = {false, false, true, true, true, true, true};
119 		// Matches HR Job ID #1 (job # 30)
120 		Long jobNumber = 30L;
121 		Long workArea = 0L;
122 		this.createShiftDifferentialRule(
123 				"BWS-CAL", "REG", "PRM", "SD1", "SD1", "SD1",
124 				(new DateTime(2010, 8, 31, 22, 0, 0, 0, tz)),
125 				(new DateTime(2010, 8, 31,  4, 0, 0, 0, tz)),
126 				new BigDecimal(3), // minHours
127 				new BigDecimal("0.25"), // maxGap
128 				dayArray);
129 
130         dayArray = new boolean [] {false, false, true, false, true, true, true};
131 		this.createShiftDifferentialRule(
132 				"BWS-CAL", "REG", "PRM", "SD1", "SD1", "SD1",
133 				(new DateTime(2010, 8, 31, 23, 0, 0, 0, tz)),
134 				(new DateTime(2010, 8, 31,  2, 0, 0, 0, tz)),
135 				new BigDecimal(3), // minHours
136 				new BigDecimal("2.0"), // maxGap
137 				dayArray);
138 
139 		dayArray = new boolean[] {false, false, false, true, true, false, false};
140 		this.createShiftDifferentialRule(
141 				"BWS-CAL", "REG", "PRM", "SD1", "SD1", "SD1",
142 				(new DateTime(2010, 8, 31, 5, 0, 0, 0, tz)),
143 				(new DateTime(2010, 8, 31,  12, 0, 0, 0, tz)),
144 				new BigDecimal("7.0"), // minHours
145 				new BigDecimal(".25"), // maxGap
146 				dayArray);
147 		dayArray = new boolean[] {false, false, false, true, false, false, false};
148 		this.createShiftDifferentialRule(
149 				"BWS-CAL", "REG", "PRM", "SD1", "SD1", "SD1",
150 				(new DateTime(2010, 8, 31, 5, 0, 0, 0, tz)),
151 				(new DateTime(2010, 8, 31,  12, 0, 0, 0, tz)),
152 				new BigDecimal("5"), // minHours
153 				new BigDecimal("0.25"), // maxGap
154 				dayArray);
155 
156 		// Timeblocks
157 
158 		// August
159 		DateTime beginPeriodDate = new DateTime(2010, 8, 15, 0, 0, 0, 0, tz);
160 		DateTime endPeriodDate = new DateTime(2010, 9, 1, 0, 0, 0, 0, tz);
161 		CalendarEntries endOfAugust = TkServiceLocator.getCalendarEntriesService().getCalendarEntriesByBeginAndEndDate(beginPeriodDate.toLocalDate().toDateMidnight().toDate(), endPeriodDate.toLocalDate().toDateMidnight().toDate());
162 		DateTime start = new DateTime(2010, 8, 31, 21, 45, 0, 0, tz);
163 		List<TimeBlock> blocks = new ArrayList<TimeBlock>();
164 		TimesheetDocument tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", endOfAugust);
165 		Assignment assignment = TkServiceLocator.getAssignmentService().getAssignment("admin", new AssignmentDescriptionKey("30_30_30"), new Date(beginPeriodDate.getMillis()));
166 		blocks.addAll(TkTestUtils.createUniformActualTimeBlocks(tdoc, assignment, "RGN", start, 1, new BigDecimal(2), BigDecimal.ZERO));
167 		TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, endOfAugust, TkServiceLocator.getCalendarService().getCalendar(endOfAugust.getHrCalendarId()), true);
168 		tdoc.setTimeBlocks(blocks);
169 		TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
170 		TkTestUtils.verifyAggregateHourSumsFlatList("August Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(2));}},aggregate);
171 		TkServiceLocator.getTimeBlockService().saveTimeBlocks(new ArrayList<TimeBlock>(), aggregate.getFlattenedTimeBlockList(), TKContext.getPrincipalId());
172 
173 
174 		// September
175 
176 		start = new DateTime(2010, 9, 1, 0, 0, 0, 0, tz);
177 		CalendarEntries payCalendarEntry = TkServiceLocator.getCalendarService().getCurrentCalendarDates("admin", start.toLocalDate().toDateMidnight().toDate());
178 		tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", payCalendarEntry);
179 		blocks = new ArrayList<TimeBlock>();
180 		blocks.addAll(TkTestUtils.createUniformTimeBlocks(start, 1, new BigDecimal("5"), "RGN", jobNumber, workArea));
181 		blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusHours(6), 1, new BigDecimal("6"), "RGN", jobNumber, workArea));
182         blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusHours(22), 1, new BigDecimal("2"), "RGN", jobNumber, workArea));
183         blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusDays(1), 1, new BigDecimal("1"), "RGN", jobNumber, workArea));
184 		blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusDays(1).plusHours(17), 1, new BigDecimal("6"), "RGN", jobNumber, workArea));
185 		setDocumentIdOnBlocks(blocks, tdoc.getDocumentId());
186         
187 		aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry, TkServiceLocator.getCalendarService().getCalendar(payCalendarEntry.getHrCalendarId()), true);
188 		
189 		TkTestUtils.verifyAggregateHourSumsFlatList("September Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(20));}},aggregate);
190 
191 		// Verify carry over and applied PRM bucket
192 		TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
193 		TkTestUtils.verifyAggregateHourSumsFlatList("September Post-Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal("14.75"));put("RGN", new BigDecimal(20));}},aggregate);
194 	}
195 
196     private void setDocumentIdOnBlocks(List<TimeBlock> blocks, String id) {
197         for (TimeBlock b : blocks) {
198             b.setDocumentId(id);
199         }
200     }
201 
202 
203 	/**
204 	 * Test where previous time sheet contains hours that should be added to
205 	 * the next pay periods first day shift.
206 	 *
207 	 * Runs on Tu, Th on the interval: [22:00, 4:00)
208 	 * Max Gap: 15 minutes
209 	 * Min Hours: 3
210 	 *
211 	 * |--------------+----+------------+-------------|
212 	 * | Tu : 8/31/10 | XX | W : 9/1/10 | Th : 9/2/10 |
213 	 * |--------------+----+------------+-------------|
214 	 * | 10pm - Mid   | XX | Mid - 5am  | 5pm - 11pm  |
215 	 * |--------------+----+------------+-------------|
216 	 *
217 	 * @throws Exception
218 	 */
219 	@SuppressWarnings("serial")
220 	@Test
221 	public void testProcessShiftTimesheeetBoundaryCarryoverCase() throws Exception {
222 		// Create the Rule    Sun,   Mon,   Tue,  Wed,   Thu,  Fri,  Sat
223 		boolean[] dayArray = {false, false, true, false, true, true, true};
224 		// Matches HR Job ID #1 (job # 30)
225 		Long jobNumber = 30L;
226 		Long workArea = 0L;
227 
228         DateTimeZone tz = TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback();
229 		this.createShiftDifferentialRule(
230 				"BWS-CAL",
231 				"REG",
232 				"PRM",
233 				"SD1",
234 				"SD1",
235 				"SD1",
236 				(new DateTime(2010, 8, 31, 22, 0, 0, 0, tz)),
237 				(new DateTime(2010, 8, 31,  5, 0, 0, 0, tz)),
238 				new BigDecimal(3), // minHours
239 				new BigDecimal("0.25"), // maxGap
240 				dayArray);
241 
242 		// August
243 		Date beginPeriodDate = new Date(new DateTime(2010, 8, 15, 0, 0, 0, 0).getMillis());
244 		Date endPeriodDate = new Date(new DateTime(2010, 9, 1, 0, 0, 0, 0).getMillis());
245 		CalendarEntries endOfAugust = TkServiceLocator.getCalendarEntriesService().getCalendarEntriesByBeginAndEndDate(beginPeriodDate, endPeriodDate);
246 		DateTime start = new DateTime(2010, 8, 31, 22, 0, 0, 0, tz);
247 		List<TimeBlock> blocks = new ArrayList<TimeBlock>();
248 		TimesheetDocument tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", endOfAugust);
249 		Assignment assignment = TkServiceLocator.getAssignmentService().getAssignment("admin", new AssignmentDescriptionKey("30_30_30"), endOfAugust.getBeginPeriodDate());
250 		blocks.addAll(TkTestUtils.createUniformActualTimeBlocks(tdoc, assignment, "RGN", start, 1, new BigDecimal(2), BigDecimal.ZERO));
251 		TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, endOfAugust, TkServiceLocator.getCalendarService().getCalendar(endOfAugust.getHrCalendarId()), true);
252 
253 
254 
255 		tdoc.setTimeBlocks(blocks);
256 		TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
257 		TkTestUtils.verifyAggregateHourSumsFlatList("August Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(2));}},aggregate);
258 		TkServiceLocator.getTimeBlockService().saveTimeBlocks(new ArrayList<TimeBlock>(), aggregate.getFlattenedTimeBlockList(), TKContext.getPrincipalId());
259 
260 
261 		// September
262 		start = new DateTime(2010, 9, 1, 0, 0, 0, 0, tz);
263         java.util.Date septStartDate = new LocalDate(new DateTime(2010,9,1,0,0,0,0)).toDateMidnight().toDate();
264 		CalendarEntries payCalendarEntry = TkServiceLocator.getCalendarService().getCurrentCalendarDates("admin", septStartDate);
265 		tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", payCalendarEntry);
266 		blocks = new ArrayList<TimeBlock>();
267 		blocks.addAll(TkTestUtils.createUniformTimeBlocks(start, 1, new BigDecimal("5"), "RGN", jobNumber, workArea));
268 		aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry, TkServiceLocator.getCalendarService().getCalendar(payCalendarEntry.getHrCalendarId()), true);
269 		TkTestUtils.verifyAggregateHourSumsFlatList("September Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(5));}},aggregate);
270 
271 		// Verify carry over and applied PRM bucket
272 		TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
273 		TkTestUtils.verifyAggregateHourSumsFlatList("September Post-Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(7));put("RGN", new BigDecimal(5));}},aggregate);
274 	}
275 
276 	@SuppressWarnings("serial")
277 	@Test
278 	/**
279 	 * Runs on every day on the interval: [16:00, 24:00)
280 	 * Max Gap: 15 minutes
281 	 * Min Hours: 4
282 	 *
283 	 * Added some extra time blocks that are not in the shift interval, but
284 	 * close to the time blocks that are.
285 	 *
286 	 * @throws Exception
287 	 */
288 	public void testProcessShiftSimpleNoisyCase() throws Exception {
289 		// Create the Rule
290 		boolean[] dayArray = {true, true, true, true, true, true, true};
291 		// Matches HR Job ID #1 (job # 30)
292 		Long jobNumber = 30L;
293 		Long workArea = 0L;
294 		this.createShiftDifferentialRule(
295 				"BWS-CAL",
296 				"REG",
297 				"PRM",
298 				"SD1",
299 				"SD1",
300 				"SD1",
301 				(new DateTime(2010, 3, 29, 16, 0, 0, 0, TKUtils.getSystemDateTimeZone())),
302 				(new DateTime(2010, 3, 30, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone())),
303 				new BigDecimal(4), // minHours
304 				new BigDecimal("15"), // maxGap
305 				dayArray);
306 
307 		// Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
308 		DateTime start = new DateTime(2010, 3, 29, 14, 0, 0, 0, TKUtils.getSystemDateTimeZone());
309 		List<TimeBlock> blocks = new ArrayList<TimeBlock>();
310 		CalendarEntries payCalendarEntry = TkServiceLocator.getCalendarService().getCurrentCalendarDates("admin", new Date(start.getMillis()));
311 		blocks.addAll(TkTestUtils.createUniformTimeBlocks(start, 2, new BigDecimal("4"), "RGN", jobNumber, workArea));
312 		blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusHours(4).plusMinutes(15), 2, new BigDecimal("2"), "RGN", jobNumber, workArea));
313 		blocks.addAll(TkTestUtils.createUniformTimeBlocks(new DateTime(2010, 3, 29, 12, 58, 0, 0, TKUtils.getSystemDateTimeZone()), 2, new BigDecimal(1), "RGN", jobNumber, workArea));
314 		TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
315 
316 		// Verify pre-Rule Run
317 		TkTestUtils.verifyAggregateHourSums("Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(14));}},aggregate,2);
318 
319 		// Run Rule
320 		TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(new Date(start.getMillis()));
321 		tdoc.setTimeBlocks(blocks);
322 		TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
323 
324 		// Verify post-Rule Run
325 		TkTestUtils.verifyAggregateHourSums("Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(8));put("RGN", new BigDecimal(14));}},aggregate,2);
326 	}
327 
328 	@SuppressWarnings("serial")
329 	@Test
330 	/**
331 	 * Runs on every day on the interval: [16:00, 24:00)
332 	 * Max Gap: 15 minutes
333 	 * Min Hours: 4
334 	 *
335 	 * @throws Exception
336 	 */
337 	public void testProcessShiftSimpleCase() throws Exception {
338 		// Create the Rule
339 		boolean[] dayArray = {true, true, true, true, true, true, true};
340 		// Matches HR Job ID #1 (job # 30)
341 		Long jobNumber = 30L;
342 		Long workArea = 0L;
343 		this.createShiftDifferentialRule(
344 				"BWS-CAL",
345 				"REG",
346 				"PRM",
347 				"SD1",
348 				"SD1",
349 				"SD1",
350 				(new DateTime(2010, 3, 29, 16, 0, 0, 0, TKUtils.getSystemDateTimeZone())),
351 				(new DateTime(2010, 3, 30, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone())),
352 				new BigDecimal(4), // minHours
353 				new BigDecimal("15"), // maxGap
354 				dayArray);
355 
356 		// Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
357 		DateTime start = new DateTime(2010, 3, 29, 14, 0, 0, 0, TKUtils.getSystemDateTimeZone());
358 		List<TimeBlock> blocks = new ArrayList<TimeBlock>();
359 		CalendarEntries payCalendarEntry = TkServiceLocator.getCalendarService().getCurrentCalendarDates("admin", new Date(start.getMillis()));
360 		blocks.addAll(TkTestUtils.createUniformTimeBlocks(start, 2, new BigDecimal("4"), "REG", jobNumber, workArea));
361 		blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusHours(4).plusMinutes(15), 2, new BigDecimal("2"), "REG", jobNumber, workArea));
362 		TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
363 
364 		// Verify pre-Rule Run
365 		TkTestUtils.verifyAggregateHourSums("Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("REG", new BigDecimal(12));}},aggregate,2);
366 
367 		// Run Rule
368 		TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(new Date(start.getMillis()));
369 		tdoc.setTimeBlocks(blocks);
370 		TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
371 
372 		// Verify post-Rule Run
373 		TkTestUtils.verifyAggregateHourSums("Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(8));put("REG", new BigDecimal(12));}},aggregate,2);
374 	}
375 
376 	/**
377 	 * Stores the Shift Differential Rule in the database for testing.
378 	 *
379 	 * dayBooleans[] is a 7 element array of booleans, [0, 6] is [sun, sat]
380 	 */
381 	private void createShiftDifferentialRule(String pyCalendarGroup, String fromEarnGroup, String premiumEarnCode, String location, String payGrade, String hrSalGroup, DateTime startTime, DateTime endTime, BigDecimal minHours, BigDecimal maxGap, boolean dayBooleans[]) {
382 		Assert.assertTrue("Wrong number of day booleans", dayBooleans.length == 7);
383 
384 		ShiftDifferentialRuleService service = TkServiceLocator.getShiftDifferentialRuleService();
385 		ShiftDifferentialRule sdr = new ShiftDifferentialRule();
386 
387 		sdr.setBeginTime(new Time(startTime.getMillis()));
388 		sdr.setEndTime(new Time(endTime.getMillis()));
389 		sdr.setMinHours(minHours);
390 		sdr.setMaxGap(maxGap);
391 		sdr.setActive(true);
392 		sdr.setUserPrincipalId(USER_PRINCIPAL_ID);
393 		sdr.setEffectiveDate(JAN_AS_OF_DATE);
394 		sdr.setLocation(location);
395 		sdr.setPayGrade(payGrade);
396 		sdr.setHrSalGroup(hrSalGroup);
397 		sdr.setFromEarnGroup(fromEarnGroup);
398 		sdr.setPyCalendarGroup(pyCalendarGroup);
399 		sdr.setEarnCode(premiumEarnCode);
400 
401 		for (int i=0; i<dayBooleans.length; i++) {
402 			switch(i) {
403 			case 0:
404 				sdr.setSunday(dayBooleans[i]);
405 				break;
406 			case 1:
407 				sdr.setMonday(dayBooleans[i]);
408 				break;
409 			case 2:
410 				sdr.setTuesday(dayBooleans[i]);
411 				break;
412 			case 3:
413 				sdr.setWednesday(dayBooleans[i]);
414 				break;
415 			case 4:
416 				sdr.setThursday(dayBooleans[i]);
417 				break;
418 			case 5:
419 				sdr.setFriday(dayBooleans[i]);
420 				break;
421 			case 6:
422 				sdr.setSaturday(dayBooleans[i]);
423 				break;
424 			}
425 		}
426 
427 		service.saveOrUpdate(sdr);
428 
429 		ShiftDifferentialRule sdrBack = service.getShiftDifferentialRule(sdr.getTkShiftDiffRuleId());
430 
431         DateTimeZone tz = TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback();
432         LocalTime orig_start = new LocalTime(sdr.getBeginTime(), tz);
433 		LocalTime orig_end = new LocalTime(sdr.getEndTime(), tz);
434 
435 		LocalTime stored_start = new LocalTime(sdrBack.getBeginTime(), tz);
436 		LocalTime stored_end = new LocalTime(sdrBack.getEndTime(), tz);
437 
438 		Assert.assertTrue("Start times not equal.", orig_start.equals(stored_start));
439 		Assert.assertTrue("End times not equal.", orig_end.equals(stored_end));
440 	}
441 
442 
443     @Ignore
444     @Test
445     /**
446      * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
447      *
448      * Create a timeblock on two days, one day has normal REG shift eligible
449      * hours, one day has HOL time.
450      *
451      * Modified version of the simple case, SDR from 12:00 to 17:00, every day,
452      * must have at least 4 hours with a maximum 15 minute gap.
453      *
454      */
455     public void simpleCaseWithWorkSchedule() throws Exception {
456 		// Create the Rule
457 		boolean[] dayArray = {true, true, true, true, true, true, true};
458 		// Matches HR Job ID #1 (job # 30)
459 		Long jobNumber = 30L;
460 		Long workArea = 0L;
461 		this.createShiftDifferentialRule(
462 				"BWS-CAL",
463 				"REG",
464 				"PRM",
465 				"SD1",
466 				"SD1",
467 				"SD1",
468 				(new DateTime(2010, 3, 29, 12, 0, 0, 0, TKUtils.getSystemDateTimeZone())),
469 				(new DateTime(2010, 3, 29, 17, 0, 0, 0, TKUtils.getSystemDateTimeZone())),
470 				new BigDecimal(4), // minHours
471 				new BigDecimal("0.25"), // maxGap
472 				dayArray);
473 
474 		// Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
475 		DateTime start = new DateTime(2010, 3, 29, 12, 0, 0, 0, TKUtils.getSystemDateTimeZone());
476         DateTime holtime = new DateTime(2010, 3, 30, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
477 		List<TimeBlock> blocks = new ArrayList<TimeBlock>();
478 		CalendarEntries payCalendarEntry = TkServiceLocator.getCalendarService().getCurrentCalendarDates("admin", new Date(start.getMillis()));
479 		blocks.addAll(TkTestUtils.createUniformTimeBlocks(start,   1, new BigDecimal("4"), "REG", jobNumber, workArea));
480         blocks.addAll(TkTestUtils.createUniformTimeBlocks(holtime, 1, new BigDecimal("4"), "HOL", jobNumber, workArea));
481 
482 		TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
483 
484 		// Verify pre-Rule Run
485 		TkTestUtils.verifyAggregateHourSums("Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("REG", new BigDecimal(4));put("HOL", new BigDecimal(4));}},aggregate,2);
486 
487 		// Run Rule
488 		TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(new Date(start.getMillis()));
489 		tdoc.setTimeBlocks(blocks);
490 		TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
491 
492 		// Verify post-Rule Run
493 		TkTestUtils.verifyAggregateHourSums("Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(8));put("REG", new BigDecimal(4));}},aggregate,2);
494 
495     }
496 
497     /**
498      * Creates a new Work Schedule and Assignment work schedule setup for the
499      * 'admin' user.
500      *
501      *  8a - 5p work schedule
502      *
503      * @param workSch
504      */
505     public void createWorkSchedule(Long workSch) {
506         // Create a Work Schedule Assignment
507         //
508         DateTimeZone tz = TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback();
509         WorkScheduleAssignment workScheduleAssignment = new WorkScheduleAssignment();
510         workScheduleAssignment.setHrWorkSchedule(workSch);
511         workScheduleAssignment.setDept("%");
512         workScheduleAssignment.setWorkArea(-1L);
513         workScheduleAssignment.setPrincipalId("admin");
514         workScheduleAssignment.setEffectiveDate(JAN_AS_OF_DATE);
515         workScheduleAssignment.setActive(true);
516         workScheduleAssignment.setUserPrincipalId("admin");
517 
518         // Create a Work Schedule
519         //
520         WorkSchedule workSchedule = new WorkSchedule();
521         workSchedule.setHrWorkSchedule(workSch); // we can set this to whatever, it's not a row ID.
522         workSchedule.setActive(true);
523         workSchedule.setEarnGroup("WS1"); // Test data should have an earn group for WS1
524         workSchedule.setWorkScheduleDesc("desc");
525         workSchedule.setEffectiveDate(JAN_AS_OF_DATE);
526         workSchedule.setUserPrincipalId("admin");
527 
528         // Create the actual schedule entries.
529         //
530         List<WorkScheduleEntry> workScheduleEntries = new ArrayList<WorkScheduleEntry>();
531 
532         WorkScheduleEntry workScheduleEntry = new WorkScheduleEntry();
533         workScheduleEntry.setBeginTime(new Time((new DateTime(2010, 3, 1, 8, 0, 0, 0, tz)).getMillis()));
534         workScheduleEntry.setEndTime(new Time((new DateTime(2010, 3, 1, 17, 0, 0, 0, tz)).getMillis()));
535         workScheduleEntry.setIndexOfDay(0L);
536         workScheduleEntries.add(workScheduleEntry);
537         workSchedule.setWorkScheduleEntries(workScheduleEntries);
538 
539         // Save Work Schedule, Work Schedule Assignment
540         TkServiceLocator.getWorkScheduleService().saveOrUpdate(workSchedule);
541         TkServiceLocator.getWorkScheduleAssignmentService().saveOrUpdate(workScheduleAssignment);
542     }
543 
544 }