001/** 002 * Copyright 2004-2014 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 */ 016package org.kuali.hr.time.shiftdiff.rule; 017 018import com.google.common.collect.ImmutableMap; 019import org.joda.time.DateTime; 020import org.joda.time.DateTimeZone; 021import org.joda.time.LocalTime; 022import org.junit.Assert; 023import org.junit.Ignore; 024import org.junit.Test; 025import org.kuali.hr.KPMEWebTestCase; 026import org.kuali.kpme.core.FunctionalTest; 027import org.kuali.kpme.core.api.assignment.Assignment; 028import org.kuali.kpme.core.api.assignment.AssignmentDescriptionKey; 029import org.kuali.kpme.core.api.calendar.entry.CalendarEntry; 030import org.kuali.kpme.core.calendar.CalendarBo; 031import org.kuali.kpme.core.service.HrServiceLocator; 032import org.kuali.kpme.core.util.TKUtils; 033import org.kuali.kpme.tklm.api.time.timeblock.TimeBlock; 034import org.kuali.kpme.tklm.time.rules.shiftdifferential.ShiftDifferentialRule; 035import org.kuali.kpme.tklm.time.rules.shiftdifferential.service.ShiftDifferentialRuleService; 036import org.kuali.kpme.tklm.time.service.TkServiceLocator; 037import org.kuali.kpme.tklm.time.timesheet.TimesheetDocument; 038import org.kuali.kpme.tklm.time.util.TkTimeBlockAggregate; 039import org.kuali.kpme.tklm.utils.TkTestUtils; 040 041import java.math.BigDecimal; 042import java.sql.Time; 043import java.util.ArrayList; 044import java.util.HashMap; 045import java.util.List; 046 047/** 048 * 049 * @author djunk 050 * 051 */ 052@FunctionalTest 053public class ShiftDifferentialRuleServiceProcessTest extends KPMEWebTestCase { 054 055 056 public static final String USER_PRINCIPAL_ID = "admin"; 057 private DateTime JAN_AS_OF_DATE = new DateTime(2010, 1, 1, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone()); 058 059 060 /** 061 * Test with boundary carryover and overlapping rules. 062 * 063 * Rule 1: 064 * 065 * Runs on Tu, Wed, Th on the interval: [22:00, 4:00) 066 * Max Gap: 15 minutes 067 * Min Hours: 3 068 * 069 * Rule 2: 070 * 071 * Runs on Tu, Th on the interval: [23:00, 2:00) 072 * Max Gap: 2 hours 073 * Min Hours: 3 074 * 075 * Rule 3: 076 * 077 * Runs on W, Th on the interval: [5:00, 12:00) 078 * Max Gap: 15 minutes 079 * Min Hours: 7 hours 080 * 081 * Rule 4: 082 * 083 * Runs on W on the interval: [5:00, 12:00) 084 * Max Gap: 15 minutes 085 * Min Hours: 5 086 * 087 * 088 * |--------------+----+------------+------------| 089 * | Tu : 8/31/10 | XX | W : 9/1/10 | Th: 9/2/10 | 090 * |--------------+----+------------+------------| 091 * | 9:45p - 11:45| XX | Mid - 5a | 5p - 11p | 092 * | | XX | 6a - Noon | | 093 * |--------------+----+------------+------------| 094 * 095 * 096 * Aug 31: 2h : 21:45 - 23:45 (Tue) ** 097 * [1: 5h 45m] // [2: 2h 45m] - Not qualifying, min hours must be 3. 098 * Sep 1: 5h : 00:00 - 05:00 (Wed) ** 099 * Sep 1: 6h : 06:00 - 12:00 (Wed) [4: 6h] 100 * 101 * Sep 1: 2h : 22:00 - 24:00 (Wed) 102 * Sep 2: 1h : 00:00 - 01:00 (Thu) [1: 3h] 103 * 104 * Sep 2: 6h : 17:00 - 22:00 (Thu) 105 * 106 * 1: [22:00, 4:00) (Tue/Wed/Thu) minimum: 3h gap: 15m 107 * 2: [23:00, 2:00) (Tue/Thu) minimum: 3h gap: 2h 108 * 3: [05:00, 12:00) (Wed/Thu) minimum: 7h gap: 15m 109 * 4: [05:00, 12:00) (Wed) minimum: 5h gap: 15m 110 * 111 */ 112 @SuppressWarnings("serial") 113 @Test 114 public void testProcessTimesheetBoundaryCarryoverOverlapCase() throws Exception { 115 DateTimeZone tz = HrServiceLocator.getTimezoneService().getUserTimezoneWithFallback(); 116 // Create the Rule Sun, Mon, Tue, Wed, Thu, Fri, Sat 117 boolean[] dayArray = {false, false, true, true, true, true, true}; 118 // Matches HR Job ID #1 (job # 30) 119 Long jobNumber = 30L; 120 Long workArea = 0L; 121 this.createShiftDifferentialRule( 122 "BWS-CAL", "REG", "PRM", "IN", "SD1", "SD1",// // changed from "SD1" to "IN" for changes of adding groupKeyCode to Job 123 (new LocalTime(22, 0)), 124 (new LocalTime(4, 0)), 125 new BigDecimal(3), // minHours 126 new BigDecimal("15.00"), // maxGap 127 dayArray); 128 129 130 // Timeblocks 131 132 // August 133 DateTime beginPeriodDate = new DateTime(2010, 8, 15, 0, 0, 0, 0, tz); 134 CalendarEntry endOfAugust = HrServiceLocator.getCalendarEntryService().getCalendarEntryByIdAndPeriodEndDate("2", new DateTime(2010, 9, 1, 0, 0, 0, 0)); 135 DateTime start = new DateTime(2010, 8, 31, 21, 45, 0, 0, tz); 136 List<TimeBlock> blocks = new ArrayList<TimeBlock>(); 137 TimesheetDocument tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", endOfAugust); 138 Assignment assignment = HrServiceLocator.getAssignmentService().getAssignment("admin", AssignmentDescriptionKey.get("IU-IN_30_30_30"), beginPeriodDate.toLocalDate()); 139 blocks.addAll(TkTestUtils.createUniformActualTimeBlocks(tdoc, assignment, "RGN", start, 1, new BigDecimal(2), BigDecimal.ZERO, "admin")); 140 TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, endOfAugust, HrServiceLocator.getCalendarService().getCalendar(endOfAugust.getHrCalendarId()), true); 141 tdoc.setTimeBlocks(blocks); 142 TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate); 143 TkTestUtils.verifyAggregateHourSumsFlatList("August Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(2));}},aggregate); 144 TkServiceLocator.getTimeBlockService().saveOrUpdateTimeBlocks(new ArrayList<TimeBlock>(), aggregate.getFlattenedTimeBlockList(), "admin"); 145 146 147 // September 148 149 start = new DateTime(2010, 9, 1, 0, 0, 0, 0, tz); 150 CalendarEntry payCalendarEntry = HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start.toLocalDate().toDateTimeAtStartOfDay()); 151 tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", payCalendarEntry); 152 blocks = new ArrayList<TimeBlock>(); 153 blocks.addAll(TkTestUtils.createUniformTimeBlocks(start, 1, new BigDecimal("5"), "RGN", jobNumber, workArea)); 154 blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusHours(6), 1, new BigDecimal("6"), "RGN", jobNumber, workArea)); 155 blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusHours(22), 1, new BigDecimal("2"), "RGN", jobNumber, workArea)); 156 blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusDays(1), 1, new BigDecimal("1"), "RGN", jobNumber, workArea)); 157 blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusDays(1).plusHours(17), 1, new BigDecimal("6"), "RGN", jobNumber, workArea)); 158 blocks = setDocumentIdOnBlocks(blocks, tdoc.getDocumentId()); 159 160 aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry, HrServiceLocator.getCalendarService().getCalendar(payCalendarEntry.getHrCalendarId()), true); 161 162 TkTestUtils.verifyAggregateHourSumsFlatList("September Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(20));}},aggregate); 163 164 // Verify carry over and applied PRM bucket 165 TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate); 166 TkTestUtils.verifyAggregateHourSumsFlatList("September Post-Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal("8.75"));put("RGN", new BigDecimal(22));}},aggregate); 167 } 168 169 private List<TimeBlock> setDocumentIdOnBlocks(List<TimeBlock> blocks, String id) { 170 List<TimeBlock> updatedTimeBlocks = new ArrayList<TimeBlock>(); 171 for (TimeBlock b : blocks) { 172 TimeBlock.Builder builder = TimeBlock.Builder.create(b); 173 builder.setDocumentId(id); 174 updatedTimeBlocks.add(builder.build()); 175 } 176 return updatedTimeBlocks; 177 } 178 179 180 /** 181 * Test where previous time sheet contains hours that should be added to 182 * the next pay periods first day shift. 183 * 184 * Runs on Tu, Th on the interval: [22:00, 4:00) 185 * Max Gap: 15 minutes 186 * Min Hours: 3 187 * 188 * |--------------+----+------------+-------------| 189 * | Tu : 8/31/10 | XX | W : 9/1/10 | Th : 9/2/10 | 190 * |--------------+----+------------+-------------| 191 * | 10pm - Mid | XX | Mid - 5am | 5pm - 11pm | 192 * |--------------+----+------------+-------------| 193 * 194 * @throws Exception 195 */ 196 @SuppressWarnings("serial") 197 @Test 198 public void testProcessShiftTimesheeetBoundaryCarryoverCase() throws Exception { 199 // Create the Rule Sun, Mon, Tue, Wed, Thu, Fri, Sat 200 boolean[] dayArray = {false, false, true, false, true, true, true}; 201 // Matches HR Job ID #1 (job # 30) 202 Long jobNumber = 30L; 203 Long workArea = 0L; 204 205 DateTimeZone tz = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback(); 206 this.createShiftDifferentialRule( 207 "BWS-CAL", 208 "REG", 209 "PRM", 210 "IN", // changed from "SD1" to "IN" for changes of adding groupKeyCode to Job 211 "SD1", 212 "SD1", 213 (new LocalTime(22, 0)), 214 (new LocalTime(5, 0)), 215 new BigDecimal(3), // minHours 216 new BigDecimal("0.25"), // maxGap 217 dayArray); 218 219 // August 220 DateTime endPeriodDate = new DateTime(2010, 9, 1, 0, 0, 0, 0); 221 CalendarEntry endOfAugust = HrServiceLocator.getCalendarEntryService().getCalendarEntryByIdAndPeriodEndDate("2", endPeriodDate); 222 DateTime start = new DateTime(2010, 8, 31, 22, 0, 0, 0, tz); 223 List<TimeBlock> blocks = new ArrayList<TimeBlock>(); 224 TimesheetDocument tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", endOfAugust); 225 Assignment assignment = HrServiceLocator.getAssignmentService().getAssignment("admin", AssignmentDescriptionKey.get("IU-IN_30_30_30"), endOfAugust.getBeginPeriodFullDateTime().toLocalDate()); 226 blocks.addAll(TkTestUtils.createUniformActualTimeBlocks(tdoc, assignment, "RGN", start, 1, new BigDecimal(2), BigDecimal.ZERO, "admin")); 227 TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, endOfAugust, HrServiceLocator.getCalendarService().getCalendar(endOfAugust.getHrCalendarId()), true); 228 229 230 231 tdoc.setTimeBlocks(blocks); 232 TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate); 233 TkTestUtils.verifyAggregateHourSumsFlatList("August Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(2));}},aggregate); 234 TkServiceLocator.getTimeBlockService().saveOrUpdateTimeBlocks(new ArrayList<TimeBlock>(), aggregate.getFlattenedTimeBlockList(), "admin"); 235 236 237 // September 238 start = new DateTime(2010, 9, 1, 0, 0, 0, 0, tz); 239 CalendarEntry payCalendarEntry = HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start.toLocalDate().toDateTimeAtStartOfDay()); 240 tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", payCalendarEntry); 241 blocks = new ArrayList<TimeBlock>(); 242 blocks.addAll(TkTestUtils.createUniformTimeBlocks(start, 1, new BigDecimal("5"), "RGN", jobNumber, workArea)); 243 aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry, HrServiceLocator.getCalendarService().getCalendar(payCalendarEntry.getHrCalendarId()), true); 244 TkTestUtils.verifyAggregateHourSumsFlatList("September Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(5));}},aggregate); 245 246 // Verify carry over and applied PRM bucket 247 TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate); 248 TkTestUtils.verifyAggregateHourSumsFlatList("September Post-Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(7));put("RGN", new BigDecimal(7));}},aggregate); 249 } 250 251 @SuppressWarnings("serial") 252 @Test 253 /** 254 * Runs on every day on the interval: [16:00, 24:00) 255 * Max Gap: 15 minutes 256 * Min Hours: 4 257 * 258 * Added some extra time blocks that are not in the shift interval, but 259 * close to the time blocks that are. 260 * 261 * @throws Exception 262 */ 263 public void testProcessShiftSimpleNoisyCase() throws Exception { 264 // Create the Rule 265 boolean[] dayArray = {true, true, true, true, true, true, true}; 266 // Matches HR Job ID #1 (job # 30) 267 Long jobNumber = 30L; 268 Long workArea = 0L; 269 DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback(); 270 this.createShiftDifferentialRule( 271 "BWS-CAL", 272 "REG", 273 "PRM", 274 "IN", // changed from "SD1" to "IN" for changes of adding groupKeyCode to Job 275 "SD1", 276 "SD1", 277 (new LocalTime(16, 0)), 278 (new LocalTime(0, 0)), 279 new BigDecimal(4), // minHours 280 new BigDecimal("15"), // maxGap 281 dayArray); 282 283 // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each. 284 DateTime start = new DateTime(2010, 3, 29, 14, 0, 0, 0, zone); 285 List<TimeBlock> blocks = new ArrayList<TimeBlock>(); 286 CalendarEntry payCalendarEntry = HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start); 287 blocks.addAll(TkTestUtils.createUniformTimeBlocks(start, 2, new BigDecimal("4"), "RGN", jobNumber, workArea)); 288 blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusHours(4).plusMinutes(15), 2, new BigDecimal("2"), "RGN", jobNumber, workArea)); 289 blocks.addAll(TkTestUtils.createUniformTimeBlocks(new DateTime(2010, 3, 29, 12, 58, 0, 0, zone), 2, new BigDecimal(1), "RGN", jobNumber, workArea)); 290 TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry); 291 292 // Verify pre-Rule Run 293 TkTestUtils.verifyAggregateHourSums("Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(14));}},aggregate,2); 294 295 // Run Rule 296 TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin"); 297 tdoc.setTimeBlocks(blocks); 298 TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate); 299 300 // Verify post-Rule Run 301 TkTestUtils.verifyAggregateHourSums("Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(8));put("RGN", new BigDecimal(14));}},aggregate,2); 302 } 303 304 @SuppressWarnings("serial") 305 @Test 306 /** 307 * Runs on every day on the interval: [16:00, 24:00) 308 * Max Gap: 15 minutes 309 * Min Hours: 4 310 * 311 * @throws Exception 312 */ 313 public void testProcessShiftSimpleCase() throws Exception { 314 // Create the Rule 315 boolean[] dayArray = {true, true, true, true, true, true, true}; 316 // Matches HR Job ID #1 (job # 30) 317 Long jobNumber = 30L; 318 Long workArea = 0L; 319 DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback(); 320 this.createShiftDifferentialRule( 321 "BWS-CAL", 322 "REG", 323 "PRM", 324 "IN", // changed from "SD1" to "IN" for changes of adding groupKeyCode to Job 325 "SD1", 326 "SD1", 327 (new LocalTime(16, 0)), //4pm 328 (new LocalTime(0, 0)), //midnight 329 new BigDecimal(4), // minHours 330 new BigDecimal("15.00"), // maxGap minutes 331 dayArray); 332 333 // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each. 334 DateTime start = new DateTime(2010, 3, 29, 14, 0, 0, 0, zone); 335 List<TimeBlock> blocks = new ArrayList<TimeBlock>(); 336 CalendarEntry payCalendarEntry = HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start); 337 blocks.addAll(TkTestUtils.createUniformTimeBlocks(start, 2, new BigDecimal("4"), "REG", jobNumber, workArea)); 338 blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusHours(4).plusMinutes(15), 2, new BigDecimal("2"), "REG", jobNumber, workArea)); 339 TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry); 340 341 // Verify pre-Rule Run 342 TkTestUtils.verifyAggregateHourSums("Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("REG", new BigDecimal(12));}},aggregate,2); 343 344 // Run Rule 345 TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin"); 346 tdoc.setTimeBlocks(blocks); 347 TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate); 348 349 // Verify post-Rule Run 350 TkTestUtils.verifyAggregateHourSums("Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(8));put("REG", new BigDecimal(12));}},aggregate,2); 351 } 352 353 /** 354 * Stores the Shift Differential Rule in the database for testing. 355 * 356 * dayBooleans[] is a 7 element array of booleans, [0, 6] is [sun, sat] 357 */ 358 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[]) { 359 Assert.assertTrue("Wrong number of day booleans", dayBooleans.length == 7); 360 361 ShiftDifferentialRuleService service = TkServiceLocator.getShiftDifferentialRuleService(); 362 ShiftDifferentialRule sdr = new ShiftDifferentialRule(); 363 364 sdr.setBeginTime(new Time(startTime.toDateTimeToday().getMillis())); 365 sdr.setEndTime(new Time(endTime.toDateTimeToday().getMillis())); 366 sdr.setMinHours(minHours); 367 sdr.setMaxGap(maxGap); 368 sdr.setActive(true); 369 sdr.setUserPrincipalId(USER_PRINCIPAL_ID); 370 sdr.setEffectiveLocalDate(JAN_AS_OF_DATE.toLocalDate()); 371 sdr.setLocation(location); 372 sdr.setPayGrade(payGrade); 373 sdr.setHrSalGroup(hrSalGroup); 374 sdr.setFromEarnGroup(fromEarnGroup); 375 sdr.setPyCalendarGroup(pyCalendarGroup); 376 sdr.setEarnCode(premiumEarnCode); 377 sdr.setRuleType("default"); 378 379 for (int i=0; i<dayBooleans.length; i++) { 380 switch(i) { 381 case 0: 382 sdr.setSunday(dayBooleans[i]); 383 break; 384 case 1: 385 sdr.setMonday(dayBooleans[i]); 386 break; 387 case 2: 388 sdr.setTuesday(dayBooleans[i]); 389 break; 390 case 3: 391 sdr.setWednesday(dayBooleans[i]); 392 break; 393 case 4: 394 sdr.setThursday(dayBooleans[i]); 395 break; 396 case 5: 397 sdr.setFriday(dayBooleans[i]); 398 break; 399 case 6: 400 sdr.setSaturday(dayBooleans[i]); 401 break; 402 } 403 } 404 405 service.saveOrUpdate(sdr); 406 407 ShiftDifferentialRule sdrBack = service.getShiftDifferentialRule(sdr.getTkShiftDiffRuleId()); 408 409 DateTimeZone tz = HrServiceLocator.getTimezoneService().getUserTimezoneWithFallback(); 410 LocalTime orig_start = new LocalTime(sdr.getBeginTime()); 411 LocalTime orig_end = new LocalTime(sdr.getEndTime()); 412 413 LocalTime stored_start = new LocalTime(sdrBack.getBeginTime()); 414 LocalTime stored_end = new LocalTime(sdrBack.getEndTime()); 415 416 Assert.assertTrue("Start times not equal.", orig_start.equals(stored_start)); 417 Assert.assertTrue("End times not equal.", orig_end.equals(stored_end)); 418 } 419 420 421 @Ignore 422 @Test 423 /** 424 * Tests WorkSchedules impact on Shift Differential Rule: Simple Case 425 * 426 * Create a timeblock on two days, one day has normal REG shift eligible 427 * hours, one day has HOL time. 428 * 429 * Modified version of the simple case, SDR from 12:00 to 17:00, every day, 430 * must have at least 4 hours with a maximum 15 minute gap. 431 * 432 */ 433 public void simpleCaseWithWorkSchedule() throws Exception { 434 // Create the Rule 435 boolean[] dayArray = {true, true, true, true, true, true, true}; 436 // Matches HR Job ID #1 (job # 30) 437 Long jobNumber = 30L; 438 Long workArea = 0L; 439 this.createShiftDifferentialRule( 440 "BWS-CAL", 441 "REG", 442 "PRM", 443 "IN", // changed from "SD1" to "IN" for changes of adding groupKeyCode to Job 444 "SD1", 445 "SD1", 446 (new LocalTime(12, 0)), 447 (new LocalTime(17, 0)), 448 new BigDecimal(4), // minHours 449 new BigDecimal("15.00"), // maxGap 450 dayArray); 451 452 // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each. 453 DateTime start = new DateTime(2010, 3, 29, 12, 0, 0, 0, TKUtils.getSystemDateTimeZone()); 454 DateTime holtime = new DateTime(2010, 3, 30, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone()); 455 List<TimeBlock> blocks = new ArrayList<TimeBlock>(); 456 CalendarEntry payCalendarEntry = HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start); 457 blocks.addAll(TkTestUtils.createUniformTimeBlocks(start, 1, new BigDecimal("4"), "REG", jobNumber, workArea)); 458 blocks.addAll(TkTestUtils.createUniformTimeBlocks(holtime, 1, new BigDecimal("4"), "HOL", jobNumber, workArea)); 459 460 TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry); 461 462 // Verify pre-Rule Run 463 TkTestUtils.verifyAggregateHourSums("Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("REG", new BigDecimal(4));put("HOL", new BigDecimal(4));}},aggregate,2); 464 465 // Run Rule 466 TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin"); 467 tdoc.setTimeBlocks(blocks); 468 TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate); 469 470 // Verify post-Rule Run 471 TkTestUtils.verifyAggregateHourSums("Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(8));put("REG", new BigDecimal(4));}},aggregate,2); 472 473 } 474 475 476 477 /** 478 * Tests WorkSchedules impact on Shift Differential Rule: Simple Case 479 * 480 * Create a single 24 hour timeblock that spans two different shift, both exceeding the min hours 481 */ 482 @Test 483 public void overlapMultipleShiftsWithSameTimeBlock() { 484 // Create the Rule 485 boolean[] dayArray = {true, true, true, true, true, true, true}; 486 // Matches HR Job ID #1 (job # 30) 487 Long jobNumber = 30L; 488 Long workArea = 0L; 489 DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback(); 490 491 //3pm to 8am, 6 hour minimum, 90 minute max gap 492 this.createShiftDifferentialRule( 493 "BWS-CAL", 494 "REG", 495 "PRM", 496 "IN", 497 "SD1", 498 "SD1", 499 (new LocalTime(15, 0)), 500 (new LocalTime(8, 0)), 501 new BigDecimal(6), // minHours 502 new BigDecimal("90.00"), // maxGap 503 dayArray); 504 505 // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each. 506 DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone()); 507 CalendarEntry payCalendarEntry = HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start); 508 509 List<TimeBlock> blocks = new ArrayList<TimeBlock>(); 510 DateTime tbStart = new DateTime(2010, 3, 29, 0, 0, 0, 0, zone); 511 512 //24 time block (midnight to midnight) 513 blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart, 1, new BigDecimal("24"), "REG", jobNumber, workArea)); 514 515 TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry); 516 517 // Verify pre-Rule Run 518 TkTestUtils.verifyAggregateHourSums("Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("REG", new BigDecimal(24));}},aggregate,2); 519 520 // Run Rule 521 TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin"); 522 tdoc.setTimeBlocks(blocks); 523 TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate); 524 525 // Verify post-Rule Run 526 TkTestUtils.verifyAggregateHourSums("Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(17));put("REG", new BigDecimal(24));}},aggregate,2); 527 528 } 529 530 /** 531 * Tests WorkSchedules impact on Shift Differential Rule: Simple Case 532 * 533 * Create a two 24 hour timeblocks that span three different shifts, all exceeding the min hours 534 */ 535 @Test 536 public void overlapMultipleShiftsWithMultipleTimeBlocks() { 537 // Create the Rule 538 boolean[] dayArray = {true, true, true, true, true, true, true}; 539 // Matches HR Job ID #1 (job # 30) 540 Long jobNumber = 30L; 541 Long workArea = 0L; 542 DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback(); 543 544 //3pm to 8am, 6 hour minimum, 90 minute max gap 545 this.createShiftDifferentialRule( 546 "BWS-CAL", 547 "REG", 548 "PRM", 549 "IN", 550 "SD1", 551 "SD1", 552 (new LocalTime(15, 0)), 553 (new LocalTime(8, 0)), 554 new BigDecimal(6), // minHours 555 new BigDecimal("90.00"), // maxGap 556 dayArray); 557 558 // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each. 559 DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone()); 560 CalendarEntry payCalendarEntry = HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start); 561 562 List<TimeBlock> blocks = new ArrayList<TimeBlock>(); 563 DateTime tbStart = new DateTime(2010, 3, 29, 0, 0, 0, 0, zone); 564 565 //24 time block (midnight to midnight) 566 blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart, 1, new BigDecimal("24"), "REG", jobNumber, workArea)); 567 568 //24 time block (midnight to midnight) 569 blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart.plusDays(1), 1, new BigDecimal("24"), "REG", jobNumber, workArea)); 570 571 TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry); 572 573 // Verify pre-Rule Run 574 TkTestUtils.verifyAggregateHourSums("Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("REG", new BigDecimal(48));}},aggregate,2); 575 576 // Run Rule 577 TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin"); 578 tdoc.setTimeBlocks(blocks); 579 TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate); 580 581 // Verify post-Rule Run 582 //overlaps from 12a-8a (8 hours), 3p-8a (17 hours), and 3p - 12a (9 hours) == 34 hours 583 TkTestUtils.verifyAggregateHourSums("Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(34));put("REG", new BigDecimal(48));}},aggregate,2); 584 585 } 586 587 /** 588 * Tests WorkSchedules impact on Shift Differential Rule: Simple Case 589 * 590 * Create a single 24 hour timeblock that spans two different shift, both exceeding the min hours 591 */ 592 @Test 593 public void overlapMultipleShiftsWithSameTimeBlockExceedingMinOnOneShift() { 594 // Create the Rule 595 boolean[] dayArray = {true, true, true, true, true, true, true}; 596 // Matches HR Job ID #1 (job # 30) 597 Long jobNumber = 30L; 598 Long workArea = 0L; 599 DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback(); 600 601 //3pm to 8am, 6 hour minimum, 90 minute max gap 602 this.createShiftDifferentialRule( 603 "BWS-CAL", 604 "REG", 605 "PRM", 606 "IN", 607 "SD1", 608 "SD1", 609 (new LocalTime(15, 0)), 610 (new LocalTime(8, 0)), 611 new BigDecimal(6), // minHours 612 new BigDecimal("90.00"), // maxGap 613 dayArray); 614 615 DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone()); 616 List<TimeBlock> blocks = new ArrayList<TimeBlock>(); 617 CalendarEntry payCalendarEntry = HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start); 618 619 //reg timeblock 3am - midnight 620 DateTime tbStart = new DateTime(2010, 3, 30, 3, 0, 0, 0, zone); 621 blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart, 1, new BigDecimal("21"), "REG", jobNumber, workArea)); 622 623 TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry); 624 625 // Verify pre-Rule Run 626 TkTestUtils.verifyAggregateHourSums("Pre-Check", new ImmutableMap.Builder<String, BigDecimal>() 627 .put("PRM", BigDecimal.ZERO) 628 .put("REG", new BigDecimal(21)).build(), aggregate, 2); 629 630 // Run Rule 631 TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin"); 632 tdoc.setTimeBlocks(blocks); 633 TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate); 634 635 // Verify post-Rule Run 636 TkTestUtils.verifyAggregateHourSums("Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>() 637 .put("PRM", new BigDecimal(9)) 638 .put("REG", new BigDecimal(21)).build(), 639 aggregate, 640 2); 641 642 } 643 644 /** 645 * Tests WorkSchedules impact on Shift Differential Rule: Simple Case 646 * 647 * Create a single 24 hour timeblock that spans two different shift, both exceeding the min hours 648 */ 649 @Test 650 public void overlapMultipleShiftsWithSameTimeBlockExceedingMinOnFirstShift() { 651 // Create the Rule 652 boolean[] dayArray = {true, true, true, true, true, true, true}; 653 // Matches HR Job ID #1 (job # 30) 654 Long jobNumber = 30L; 655 Long workArea = 0L; 656 DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback(); 657 658 //3pm to 8am, 6 hour minimum, 90 minute max gap 659 this.createShiftDifferentialRule( 660 "BWS-CAL", 661 "REG", 662 "PRM", 663 "IN", 664 "SD1", 665 "SD1", 666 (new LocalTime(15, 0)), 667 (new LocalTime(8, 0)), 668 new BigDecimal(6), // minHours 669 new BigDecimal("90.00"), // maxGap 670 dayArray); 671 672 DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone()); 673 List<TimeBlock> blocks = new ArrayList<TimeBlock>(); 674 CalendarEntry payCalendarEntry = HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start); 675 676 //reg timeblock 1am - 9pm 677 DateTime tbStart = new DateTime(2010, 3, 30, 1, 0, 0, 0, zone); 678 blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart, 1, new BigDecimal("19"), "REG", jobNumber, workArea)); 679 680 TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry); 681 682 // Verify pre-Rule Run 683 TkTestUtils.verifyAggregateHourSums("Pre-Check", new ImmutableMap.Builder<String, BigDecimal>() 684 .put("PRM", BigDecimal.ZERO) 685 .put("REG", new BigDecimal(19)).build(), aggregate, 2); 686 687 // Run Rule 688 TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin"); 689 tdoc.setTimeBlocks(blocks); 690 TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate); 691 692 // Verify post-Rule Run 693 TkTestUtils.verifyAggregateHourSums("Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>() 694 .put("PRM", new BigDecimal(7)) 695 .put("REG", new BigDecimal(19)).build(), 696 aggregate, 697 2); 698 699 } 700 701 /** 702 * Tests WorkSchedules impact on Shift Differential Rule: Simple Case 703 * 704 * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours 705 */ 706 @Test 707 public void overlapMultipleShiftsWithSameTimeBlocNeitherExceedingMin() { 708 // Create the Rule 709 boolean[] dayArray = {true, true, true, true, true, true, true}; 710 // Matches HR Job ID #1 (job # 30) 711 Long jobNumber = 30L; 712 Long workArea = 0L; 713 DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback(); 714 715 //3pm to 8am, 6 hour minimum, 90 minute max gap 716 this.createShiftDifferentialRule( 717 "BWS-CAL", 718 "REG", 719 "PRM", 720 "IN", 721 "SD1", 722 "SD1", 723 (new LocalTime(15, 0)), 724 (new LocalTime(8, 0)), 725 new BigDecimal(6), // minHours 726 new BigDecimal("90.00"), // maxGap 727 dayArray); 728 729 // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each. 730 DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone()); 731 List<TimeBlock> blocks = new ArrayList<TimeBlock>(); 732 CalendarEntry payCalendarEntry = HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start); 733 734 //reg timeblock 3am - 8pm 735 DateTime tbStart = new DateTime(2010, 3, 30, 3, 0, 0, 0, zone); 736 blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart, 1, new BigDecimal("17"), "REG", jobNumber, workArea)); 737 738 TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry); 739 740 // Verify pre-Rule Run 741 TkTestUtils.verifyAggregateHourSums("Pre-Check", new ImmutableMap.Builder<String, BigDecimal>() 742 .put("PRM", BigDecimal.ZERO) 743 .put("REG", new BigDecimal(17)).build(), aggregate, 2); 744 745 // Run Rule 746 TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin"); 747 tdoc.setTimeBlocks(blocks); 748 TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate); 749 750 // Verify post-Rule Run 751 TkTestUtils.verifyAggregateHourSums("Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>() 752 .put("PRM", BigDecimal.ZERO) 753 .put("REG", new BigDecimal(17)).build(), 754 aggregate, 755 2); 756 } 757 758 /** 759 * Tests WorkSchedules impact on Shift Differential Rule: Simple Case 760 * 761 * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours 762 */ 763 @Test 764 public void multipleTimeBlocksOvernightExceedingMin() { 765 // Create the Rule 766 boolean[] dayArray = {true, true, true, true, true, true, true}; 767 // Matches HR Job ID #1 (job # 30) 768 Long jobNumber = 30L; 769 Long workArea = 0L; 770 DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback(); 771 772 //3pm to 8am, 6 hour minimum, 90 minute max gap 773 this.createShiftDifferentialRule( 774 "BWS-CAL", 775 "REG", 776 "PRM", 777 "IN", 778 "SD1", 779 "SD1", 780 (new LocalTime(15, 0)), 781 (new LocalTime(8, 0)), 782 new BigDecimal(6), // minHours 783 new BigDecimal("90.00"), // maxGap 784 dayArray); 785 786 // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each. 787 DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone()); 788 List<TimeBlock> blocks = new ArrayList<TimeBlock>(); 789 CalendarEntry payCalendarEntry = HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start); 790 791 //reg timeblock 10pm - midnight 792 DateTime tbStart1 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone); 793 blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1, 1, new BigDecimal("2"), "REG", jobNumber, workArea)); 794 //reg timeblock midnight - 5am 795 DateTime tbStart2 = new DateTime(2010, 3, 31, 0, 0, 0, 0, zone); 796 blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2, 1, new BigDecimal("5"), "REG", jobNumber, workArea)); 797 798 TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry); 799 800 // Verify pre-Rule Run 801 TkTestUtils.verifyAggregateHourSums("Pre-Check", new ImmutableMap.Builder<String, BigDecimal>() 802 .put("PRM", BigDecimal.ZERO) 803 .put("REG", BigDecimal.valueOf(7)).build(), aggregate, 2); 804 805 // Run Rule 806 TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin"); 807 tdoc.setTimeBlocks(blocks); 808 TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate); 809 810 // Verify post-Rule Run 811 TkTestUtils.verifyAggregateHourSums("Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>() 812 .put("PRM", BigDecimal.valueOf(7)) 813 .put("REG", BigDecimal.valueOf(7)).build(), 814 aggregate, 815 2); 816 } 817 818 /** 819 * Tests WorkSchedules impact on Shift Differential Rule: Simple Case 820 * 821 * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours 822 */ 823 @Test 824 public void multipleTimeBlocksOvernightExceedingMinWithSixtyMinuteGap() { 825 // Create the Rule 826 boolean[] dayArray = {true, true, true, true, true, true, true}; 827 // Matches HR Job ID #1 (job # 30) 828 Long jobNumber = 30L; 829 Long workArea = 0L; 830 DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback(); 831 832 //3pm to 8am, 6 hour minimum, 90 minute max gap 833 this.createShiftDifferentialRule( 834 "BWS-CAL", 835 "REG", 836 "PRM", 837 "IN", 838 "SD1", 839 "SD1", 840 (new LocalTime(15, 0)), 841 (new LocalTime(8, 0)), 842 new BigDecimal(6), // minHours 843 new BigDecimal("90.00"), // maxGap 844 dayArray); 845 846 // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each. 847 DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone()); 848 List<TimeBlock> blocks = new ArrayList<TimeBlock>(); 849 CalendarEntry payCalendarEntry = HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start); 850 851 //reg timeblock 10pm - midnight 852 DateTime tbStart1 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone); 853 blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1, 1, new BigDecimal("2"), "REG", jobNumber, workArea)); 854 //reg timeblock 1am - 5am 855 DateTime tbStart2 = new DateTime(2010, 3, 31, 1, 0, 0, 0, zone); 856 blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2, 1, new BigDecimal("4"), "REG", jobNumber, workArea)); 857 858 TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry); 859 860 // Verify pre-Rule Run 861 TkTestUtils.verifyAggregateHourSums("Pre-Check", new ImmutableMap.Builder<String, BigDecimal>() 862 .put("PRM", BigDecimal.ZERO) 863 .put("REG", BigDecimal.valueOf(6)).build(), aggregate, 2); 864 865 // Run Rule 866 TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin"); 867 tdoc.setTimeBlocks(blocks); 868 TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate); 869 870 // Verify post-Rule Run 871 TkTestUtils.verifyAggregateHourSums("Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>() 872 .put("PRM", BigDecimal.valueOf(6)) 873 .put("REG", BigDecimal.valueOf(6)).build(), 874 aggregate, 875 2); 876 } 877 878 /** 879 * Tests WorkSchedules impact on Shift Differential Rule: Simple Case 880 * 881 * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours 882 */ 883 @Test 884 public void multipleTimeBlocksOvernightExceedingMinWithNinetyMinuteGap() { 885 // Create the Rule 886 boolean[] dayArray = {true, true, true, true, true, true, true}; 887 // Matches HR Job ID #1 (job # 30) 888 Long jobNumber = 30L; 889 Long workArea = 0L; 890 DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback(); 891 892 //3pm to 8am, 6 hour minimum, 90 minute max gap 893 this.createShiftDifferentialRule( 894 "BWS-CAL", 895 "REG", 896 "PRM", 897 "IN", 898 "SD1", 899 "SD1", 900 (new LocalTime(15, 0)), 901 (new LocalTime(8, 0)), 902 new BigDecimal(6), // minHours 903 new BigDecimal("90.00"), // maxGap 904 dayArray); 905 906 // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each. 907 DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone()); 908 List<TimeBlock> blocks = new ArrayList<TimeBlock>(); 909 CalendarEntry payCalendarEntry = HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start); 910 911 //reg timeblock 10pm - midnight 912 DateTime tbStart1 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone); 913 blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1, 1, new BigDecimal("2"), "REG", jobNumber, workArea)); 914 //reg timeblock 1:30am - 5:30pm 915 DateTime tbStart2 = new DateTime(2010, 3, 31, 1, 30, 0, 0, zone); 916 blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2, 1, new BigDecimal("4"), "REG", jobNumber, workArea)); 917 918 TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry); 919 920 // Verify pre-Rule Run 921 TkTestUtils.verifyAggregateHourSums("Pre-Check", new ImmutableMap.Builder<String, BigDecimal>() 922 .put("PRM", BigDecimal.ZERO) 923 .put("REG", BigDecimal.valueOf(6)).build(), aggregate, 2); 924 925 // Run Rule 926 TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin"); 927 tdoc.setTimeBlocks(blocks); 928 TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate); 929 930 // Verify post-Rule Run 931 TkTestUtils.verifyAggregateHourSums("Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>() 932 .put("PRM", BigDecimal.valueOf(6)) 933 .put("REG", BigDecimal.valueOf(6)).build(), 934 aggregate, 935 2); 936 } 937 938 /** 939 * Tests WorkSchedules impact on Shift Differential Rule: Simple Case 940 * 941 * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours 942 */ 943 @Test 944 public void multipleTimeBlocksOvernightExceedingMinButExceedingGap() { 945 // Create the Rule 946 boolean[] dayArray = {true, true, true, true, true, true, true}; 947 // Matches HR Job ID #1 (job # 30) 948 Long jobNumber = 30L; 949 Long workArea = 0L; 950 DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback(); 951 952 //3pm to 8am, 6 hour minimum, 90 minute max gap 953 this.createShiftDifferentialRule( 954 "BWS-CAL", 955 "REG", 956 "PRM", 957 "IN", 958 "SD1", 959 "SD1", 960 (new LocalTime(15, 0)), 961 (new LocalTime(8, 0)), 962 new BigDecimal(6), // minHours 963 new BigDecimal("90.00"), // maxGap 964 dayArray); 965 966 // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each. 967 DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone()); 968 List<TimeBlock> blocks = new ArrayList<TimeBlock>(); 969 CalendarEntry payCalendarEntry = HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start); 970 971 //reg timeblock 10pm - midnight 972 DateTime tbStart1 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone); 973 blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1, 1, new BigDecimal("2"), "REG", jobNumber, workArea)); 974 //reg timeblock 1:36am - 5:36am 975 DateTime tbStart2 = new DateTime(2010, 3, 31, 1, 36, 0, 0, zone); 976 blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2, 1, new BigDecimal("4"), "REG", jobNumber, workArea)); 977 978 TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry); 979 980 // Verify pre-Rule Run 981 TkTestUtils.verifyAggregateHourSums("Pre-Check", new ImmutableMap.Builder<String, BigDecimal>() 982 .put("PRM", BigDecimal.ZERO) 983 .put("REG", BigDecimal.valueOf(6)).build(), aggregate, 2); 984 985 // Run Rule 986 TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin"); 987 tdoc.setTimeBlocks(blocks); 988 TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate); 989 990 // Verify post-Rule Run 991 TkTestUtils.verifyAggregateHourSums("Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>() 992 .put("PRM", BigDecimal.valueOf(0)) 993 .put("REG", BigDecimal.valueOf(6)).build(), 994 aggregate, 995 2); 996 } 997 998 999 /** 1000 * Tests WorkSchedules impact on Shift Differential Rule: Simple Case 1001 * 1002 * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours 1003 */ 1004 @Test 1005 public void threeBlocksWithinSameShift() { 1006 // Create the Rule 1007 boolean[] dayArray = {true, true, true, true, true, true, true}; 1008 // Matches HR Job ID #1 (job # 30) 1009 Long jobNumber = 30L; 1010 Long workArea = 0L; 1011 DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback(); 1012 1013 //3pm to 8am, 6 hour minimum, 90 minute max gap 1014 this.createShiftDifferentialRule( 1015 "BWS-CAL", 1016 "REG", 1017 "PRM", 1018 "IN", 1019 "SD1", 1020 "SD1", 1021 (new LocalTime(15, 0)), 1022 (new LocalTime(8, 0)), 1023 new BigDecimal(6), // minHours 1024 new BigDecimal("90.00"), // maxGap 1025 dayArray); 1026 1027 // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each. 1028 DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone()); 1029 CalendarEntry payCalendarEntry = HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start); 1030 1031 List<TimeBlock> blocks = new ArrayList<TimeBlock>(); 1032 1033 //reg timeblock 3pm - 5pm 1034 DateTime tbStart1 = new DateTime(2010, 3, 30, 15, 0, 0, 0, zone); 1035 blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1, 1, new BigDecimal("2"), "REG", jobNumber, workArea)); 1036 1037 // 6pm - 9pm 1038 DateTime tbStart2 = new DateTime(2010, 3, 30, 18, 0, 0, 0, zone); 1039 blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2, 1, new BigDecimal("3"), "REG", jobNumber, workArea)); 1040 1041 // 10pm - 11pm 1042 DateTime tbStart3 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone); 1043 blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3, 1, new BigDecimal("1"), "REG", jobNumber, workArea)); 1044 1045 TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry); 1046 1047 // Verify pre-Rule Run 1048 TkTestUtils.verifyAggregateHourSums("Pre-Check", new ImmutableMap.Builder<String, BigDecimal>() 1049 .put("PRM", BigDecimal.ZERO) 1050 .put("REG", BigDecimal.valueOf(6)).build(), aggregate, 2); 1051 1052 // Run Rule 1053 TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin"); 1054 tdoc.setTimeBlocks(blocks); 1055 TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate); 1056 1057 // Verify post-Rule Run 1058 TkTestUtils.verifyAggregateHourSums("Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>() 1059 .put("PRM", BigDecimal.valueOf(6)) 1060 .put("REG", BigDecimal.valueOf(6)).build(), 1061 aggregate, 1062 2); 1063 } 1064 1065 /** 1066 * Tests WorkSchedules impact on Shift Differential Rule: Simple Case 1067 * 1068 * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours 1069 */ 1070 @Test 1071 public void fourBlocksWithinSameShiftSpanningTwoDays() { 1072 // Create the Rule 1073 boolean[] dayArray = {true, true, true, true, true, true, true}; 1074 // Matches HR Job ID #1 (job # 30) 1075 Long jobNumber = 30L; 1076 Long workArea = 0L; 1077 DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback(); 1078 1079 //3pm to 8am, 6 hour minimum, 90 minute max gap 1080 this.createShiftDifferentialRule( 1081 "BWS-CAL", 1082 "REG", 1083 "PRM", 1084 "IN", 1085 "SD1", 1086 "SD1", 1087 (new LocalTime(15, 0)), 1088 (new LocalTime(8, 0)), 1089 new BigDecimal(6), // minHours 1090 new BigDecimal("90.00"), // maxGap 1091 dayArray); 1092 1093 // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each. 1094 DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone()); 1095 CalendarEntry payCalendarEntry = HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start); 1096 1097 List<TimeBlock> blocks = new ArrayList<TimeBlock>(); 1098 1099 //reg timeblock 5pm - 6pm 1100 DateTime tbStart1 = new DateTime(2010, 3, 30, 17, 0, 0, 0, zone); 1101 blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1, 1, new BigDecimal("1"), "REG", jobNumber, workArea)); 1102 1103 // 7:30pm - 8:30pm 1104 DateTime tbStart2 = new DateTime(2010, 3, 30, 19, 30, 0, 0, zone); 1105 blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2, 1, new BigDecimal("1"), "REG", jobNumber, workArea)); 1106 1107 // 10pm - midnight 1108 DateTime tbStart3 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone); 1109 blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3, 1, new BigDecimal("2"), "REG", jobNumber, workArea)); 1110 1111 // 1:30am - 4:30am 1112 DateTime tbStart4 = new DateTime(2010, 3, 31, 1, 30, 0, 0, zone); 1113 blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart4, 1, new BigDecimal("3"), "REG", jobNumber, workArea)); 1114 1115 TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry); 1116 1117 // Verify pre-Rule Run 1118 TkTestUtils.verifyAggregateHourSums("Pre-Check", new ImmutableMap.Builder<String, BigDecimal>() 1119 .put("PRM", BigDecimal.ZERO) 1120 .put("REG", BigDecimal.valueOf(7)).build(), aggregate, 2); 1121 1122 // Run Rule 1123 TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin"); 1124 tdoc.setTimeBlocks(blocks); 1125 TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate); 1126 1127 // Verify post-Rule Run 1128 TkTestUtils.verifyAggregateHourSums("Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>() 1129 .put("PRM", BigDecimal.valueOf(7)) 1130 .put("REG", BigDecimal.valueOf(7)).build(), 1131 aggregate, 1132 2); 1133 } 1134}