001 /**
002 * Copyright 2004-2013 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.kuali.hr.time.shiftdiff.rule.service;
017
018 import java.math.BigDecimal;
019 import java.sql.Date;
020 import java.sql.Time;
021 import java.util.ArrayList;
022 import java.util.HashMap;
023 import java.util.List;
024
025 import org.joda.time.DateTime;
026 import org.joda.time.DateTimeZone;
027 import org.joda.time.LocalDate;
028 import org.joda.time.LocalTime;
029 import org.junit.Assert;
030 import org.junit.Ignore;
031 import org.junit.Test;
032 import org.kuali.hr.test.KPMETestCase;
033 import org.kuali.hr.time.assignment.Assignment;
034 import org.kuali.hr.time.assignment.AssignmentDescriptionKey;
035 import org.kuali.hr.time.calendar.CalendarEntries;
036 import org.kuali.hr.time.service.base.TkServiceLocator;
037 import org.kuali.hr.time.shiftdiff.rule.ShiftDifferentialRule;
038 import org.kuali.hr.time.test.TkTestUtils;
039 import org.kuali.hr.time.timeblock.TimeBlock;
040 import org.kuali.hr.time.timesheet.TimesheetDocument;
041 import org.kuali.hr.time.util.TKContext;
042 import org.kuali.hr.time.util.TKUtils;
043 import org.kuali.hr.time.util.TkConstants;
044 import org.kuali.hr.time.util.TkTimeBlockAggregate;
045 import org.kuali.hr.time.workschedule.WorkSchedule;
046 import org.kuali.hr.time.workschedule.WorkScheduleAssignment;
047 import org.kuali.hr.time.workschedule.WorkScheduleEntry;
048
049 /**
050 *
051 * @author djunk
052 *
053 */
054 public class ShiftDifferentialRuleServiceProcessTest extends KPMETestCase {
055
056
057 public static final String USER_PRINCIPAL_ID = "admin";
058 private Date JAN_AS_OF_DATE = new Date((new DateTime(2010, 1, 1, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone())).getMillis());
059
060
061 /**
062 * Test with boundary carryover and overlapping rules.
063 *
064 * Rule 1:
065 *
066 * Runs on Tu, Wed, Th on the interval: [22:00, 4:00)
067 * Max Gap: 15 minutes
068 * Min Hours: 3
069 *
070 * Rule 2:
071 *
072 * Runs on Tu, Th on the interval: [23:00, 2:00)
073 * Max Gap: 2 hours
074 * Min Hours: 3
075 *
076 * Rule 3:
077 *
078 * Runs on W, Th on the interval: [5:00, 12:00)
079 * Max Gap: 15 minutes
080 * Min Hours: 7 hours
081 *
082 * Rule 4:
083 *
084 * Runs on W on the interval: [5:00, 12:00)
085 * Max Gap: 15 minutes
086 * Min Hours: 5
087 *
088 *
089 * |--------------+----+------------+------------|
090 * | Tu : 8/31/10 | XX | W : 9/1/10 | Th: 9/2/10 |
091 * |--------------+----+------------+------------|
092 * | 9:45p - 11:45| XX | Mid - 5a | 5p - 11p |
093 * | | XX | 6a - Noon | |
094 * |--------------+----+------------+------------|
095 *
096 *
097 * Aug 31: 2h : 21:45 - 23:45 (Tue) **
098 * [1: 5h 45m] // [2: 2h 45m] - Not qualifying, min hours must be 3.
099 * 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 }