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 }