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.HrContext;
033import org.kuali.kpme.core.util.TKUtils;
034import org.kuali.kpme.tklm.api.leave.block.LeaveBlock;
035import org.kuali.kpme.tklm.api.time.timeblock.TimeBlock;
036import org.kuali.kpme.tklm.time.rules.shiftdifferential.ShiftDifferentialRule;
037import org.kuali.kpme.tklm.time.rules.shiftdifferential.service.ShiftDifferentialRuleService;
038import org.kuali.kpme.tklm.time.service.TkServiceLocator;
039import org.kuali.kpme.tklm.time.timesheet.TimesheetDocument;
040import org.kuali.kpme.tklm.time.timesheet.TimesheetUtils;
041import org.kuali.kpme.tklm.time.util.TkTimeBlockAggregate;
042import org.kuali.kpme.tklm.utils.TkTestUtils;
043
044import java.math.BigDecimal;
045import java.sql.Time;
046import java.util.ArrayList;
047import java.util.HashMap;
048import java.util.List;
049
050/**
051 *
052 * @author djunk
053 *
054 */
055@FunctionalTest
056public class ShiftDifferentialRuleServiceProcessTest extends KPMEWebTestCase {
057
058
059        public static final String USER_PRINCIPAL_ID = "admin";
060        private DateTime JAN_AS_OF_DATE = new DateTime(2010, 1, 1, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
061
062
063        /**
064         * Test with boundary carryover and overlapping rules.
065         *
066         * Rule 1:
067         *
068         * Runs on Tu, Wed, Th on the interval: [22:00, 4:00)
069         * Max Gap: 15 minutes
070         * Min Hours: 3
071         *
072         * Rule 2:
073         *
074         * Runs on Tu, Th on the interval: [23:00, 2:00)
075         * Max Gap: 2 hours
076         * Min Hours: 3
077         *
078         * Rule 3:
079         *
080         * Runs on W, Th on the interval: [5:00, 12:00)
081         * Max Gap: 15 minutes
082         * Min Hours: 7 hours
083         *
084         * Rule 4:
085         *
086         * Runs on W on the interval: [5:00, 12:00)
087         * Max Gap: 15 minutes
088         * Min Hours: 5
089         *
090         *
091         * |--------------+----+------------+------------|
092         * | Tu : 8/31/10 | XX | W : 9/1/10 | Th: 9/2/10 |
093         * |--------------+----+------------+------------|
094         * | 9:45p - 11:45| XX | Mid - 5a   | 5p - 11p   |
095         * |              | XX | 6a - Noon  |            |
096         * |--------------+----+------------+------------|
097     *
098     *
099     * Aug 31: 2h  : 21:45 - 23:45 (Tue) **
100     *                           [1: 5h 45m]  // [2: 2h 45m] - Not qualifying, min hours must be 3.
101     * Sep  1: 5h  : 00:00 - 05:00 (Wed) **
102     * Sep  1: 6h  : 06:00 - 12:00 (Wed) [4: 6h]
103     *
104     * Sep  1: 2h  : 22:00 - 24:00 (Wed)
105     * Sep  2: 1h  : 00:00 - 01:00 (Thu) [1: 3h]
106     *
107     * Sep  2: 6h  : 17:00 - 22:00 (Thu)
108     *
109     * 1: [22:00,  4:00) (Tue/Wed/Thu) minimum: 3h gap: 15m
110     * 2: [23:00,  2:00) (Tue/Thu)     minimum: 3h gap: 2h
111     * 3: [05:00, 12:00) (Wed/Thu)     minimum: 7h gap: 15m
112     * 4: [05:00, 12:00) (Wed)         minimum: 5h gap: 15m
113     *
114         */
115        @SuppressWarnings("serial")
116        @Test
117        public void testProcessTimesheetBoundaryCarryoverOverlapCase() throws Exception {
118        DateTimeZone tz = HrServiceLocator.getTimezoneService().getUserTimezoneWithFallback();
119                // Create the Rule    Sun,   Mon,   Tue,  Wed,   Thu,  Fri,  Sat
120                boolean[] dayArray = {false, false, true, true, true, true, true};
121                // Matches HR Job ID #1 (job # 30)
122                Long jobNumber = 30L;
123                Long workArea = 0L;
124                this.createShiftDifferentialRule(
125                                "BWS-CAL", "REG", "PRM", "IN", "SD1", "SD1",// // changed from "SD1" to "IN" for changes of adding groupKeyCode to Job
126                                (new LocalTime(22, 0)),
127                                (new LocalTime(4, 0)),
128                                new BigDecimal(3), // minHours
129                                new BigDecimal("15.00"), // maxGap
130                                dayArray);
131
132
133                // Timeblocks
134
135                // August
136                DateTime beginPeriodDate = new DateTime(2010, 8, 15, 0, 0, 0, 0, tz);
137                CalendarEntry endOfAugust =  HrServiceLocator.getCalendarEntryService().getCalendarEntryByIdAndPeriodEndDate("2", new DateTime(2010, 9, 1, 0, 0, 0, 0));
138                DateTime start = new DateTime(2010, 8, 31, 21, 45, 0, 0, tz);
139                List<TimeBlock> blocks = new ArrayList<TimeBlock>();
140                TimesheetDocument tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", endOfAugust);
141        Assignment assignment = HrServiceLocator.getAssignmentService().getAssignment("admin", AssignmentDescriptionKey.get("IU-IN_30_30_30"), beginPeriodDate.toLocalDate());
142                blocks.addAll(TkTestUtils.createUniformActualTimeBlocks(tdoc, assignment, "RGN", start, 1, new BigDecimal(2), BigDecimal.ZERO, "admin"));
143                TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, endOfAugust, HrServiceLocator.getCalendarService().getCalendar(endOfAugust.getHrCalendarId()), true);
144                tdoc.setTimeBlocks(blocks);
145                TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
146                TkTestUtils.verifyAggregateHourSumsFlatList("August Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(2));}},aggregate);
147                TkServiceLocator.getTimeBlockService().saveOrUpdateTimeBlocks(new ArrayList<TimeBlock>(), aggregate.getFlattenedTimeBlockList(), "admin");
148
149
150                // September
151
152                start = new DateTime(2010, 9, 1, 0, 0, 0, 0, tz);
153                CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start.toLocalDate().toDateTimeAtStartOfDay());
154                tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", payCalendarEntry);
155                blocks = new ArrayList<TimeBlock>();
156                blocks.addAll(TkTestUtils.createUniformTimeBlocks(start, 1, new BigDecimal("5"), "RGN", jobNumber, workArea));
157                blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusHours(6), 1, new BigDecimal("6"), "RGN", jobNumber, workArea));
158        blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusHours(22), 1, new BigDecimal("2"), "RGN", jobNumber, workArea));
159        blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusDays(1), 1, new BigDecimal("1"), "RGN", jobNumber, workArea));
160                blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusDays(1).plusHours(17), 1, new BigDecimal("6"), "RGN", jobNumber, workArea));
161                blocks = setDocumentIdOnBlocks(blocks, tdoc.getDocumentId());
162        
163                aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry, HrServiceLocator.getCalendarService().getCalendar(payCalendarEntry.getHrCalendarId()), true);
164                
165                TkTestUtils.verifyAggregateHourSumsFlatList("September Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(20));}},aggregate);
166
167                // Verify carry over and applied PRM bucket
168                TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
169                TkTestUtils.verifyAggregateHourSumsFlatList("September Post-Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal("8.75"));put("RGN", new BigDecimal(22));}},aggregate);
170        }
171
172    private List<TimeBlock> setDocumentIdOnBlocks(List<TimeBlock> blocks, String id) {
173        List<TimeBlock> updatedTimeBlocks = new ArrayList<TimeBlock>();
174        for (TimeBlock b : blocks) {
175            TimeBlock.Builder builder = TimeBlock.Builder.create(b);
176            builder.setDocumentId(id);
177            updatedTimeBlocks.add(builder.build());
178        }
179        return updatedTimeBlocks;
180    }
181
182
183        /**
184         * Test where previous time sheet contains hours that should be added to
185         * the next pay periods first day shift.
186         *
187         * Runs on Tu, Th on the interval: [22:00, 4:00)
188         * Max Gap: 15 minutes
189         * Min Hours: 3
190         *
191         * |--------------+----+------------+-------------|
192         * | Tu : 8/31/10 | XX | W : 9/1/10 | Th : 9/2/10 |
193         * |--------------+----+------------+-------------|
194         * | 10pm - Mid   | XX | Mid - 5am  | 5pm - 11pm  |
195         * |--------------+----+------------+-------------|
196         *
197         * @throws Exception
198         */
199        @SuppressWarnings("serial")
200        @Test
201        public void testProcessShiftTimesheeetBoundaryCarryoverCase() throws Exception {
202                // Create the Rule    Sun,   Mon,   Tue,  Wed,   Thu,  Fri,  Sat
203                boolean[] dayArray = {false, false, true, false, true, true, true};
204                // Matches HR Job ID #1 (job # 30)
205                Long jobNumber = 30L;
206                Long workArea = 0L;
207
208        DateTimeZone tz = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
209                this.createShiftDifferentialRule(
210                                "BWS-CAL",
211                                "REG",
212                                "PRM",
213                                "IN",  // changed from "SD1" to "IN" for changes of adding groupKeyCode to Job
214                                "SD1",
215                                "SD1",
216                                (new LocalTime(22, 0)),
217                                (new LocalTime(5, 0)),
218                                new BigDecimal(3), // minHours
219                                new BigDecimal("0.25"), // maxGap
220                                dayArray);
221
222                // August
223                DateTime endPeriodDate = new DateTime(2010, 9, 1, 0, 0, 0, 0);
224        CalendarEntry endOfAugust =  HrServiceLocator.getCalendarEntryService().getCalendarEntryByIdAndPeriodEndDate("2", endPeriodDate);
225                DateTime start = new DateTime(2010, 8, 31, 22, 0, 0, 0, tz);
226                List<TimeBlock> blocks = new ArrayList<TimeBlock>();
227                TimesheetDocument tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", endOfAugust);
228        Assignment assignment = HrServiceLocator.getAssignmentService().getAssignment("admin", AssignmentDescriptionKey.get("IU-IN_30_30_30"), endOfAugust.getBeginPeriodFullDateTime().toLocalDate());
229                blocks.addAll(TkTestUtils.createUniformActualTimeBlocks(tdoc, assignment, "RGN", start, 1, new BigDecimal(2), BigDecimal.ZERO, "admin"));
230                TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, endOfAugust, HrServiceLocator.getCalendarService().getCalendar(endOfAugust.getHrCalendarId()), true);
231
232
233
234                tdoc.setTimeBlocks(blocks);
235                TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
236                TkTestUtils.verifyAggregateHourSumsFlatList("August Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(2));}},aggregate);
237                TkServiceLocator.getTimeBlockService().saveOrUpdateTimeBlocks(new ArrayList<TimeBlock>(), aggregate.getFlattenedTimeBlockList(), "admin");
238
239
240                // September
241                start = new DateTime(2010, 9, 1, 0, 0, 0, 0, tz);
242                CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start.toLocalDate().toDateTimeAtStartOfDay());
243                tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", payCalendarEntry);
244                blocks = new ArrayList<TimeBlock>();
245                blocks.addAll(TkTestUtils.createUniformTimeBlocks(start, 1, new BigDecimal("5"), "RGN", jobNumber, workArea));
246                aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry, HrServiceLocator.getCalendarService().getCalendar(payCalendarEntry.getHrCalendarId()), true);
247                TkTestUtils.verifyAggregateHourSumsFlatList("September Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(5));}},aggregate);
248
249                // Verify carry over and applied PRM bucket
250                TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
251                TkTestUtils.verifyAggregateHourSumsFlatList("September Post-Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(7));put("RGN", new BigDecimal(7));}},aggregate);
252        }
253
254        @SuppressWarnings("serial")
255        @Test
256        /**
257         * Runs on every day on the interval: [16:00, 24:00)
258         * Max Gap: 15 minutes
259         * Min Hours: 4
260         *
261         * Added some extra time blocks that are not in the shift interval, but
262         * close to the time blocks that are.
263         *
264         * @throws Exception
265         */
266        public void testProcessShiftSimpleNoisyCase() throws Exception {
267                // Create the Rule
268                boolean[] dayArray = {true, true, true, true, true, true, true};
269                // Matches HR Job ID #1 (job # 30)
270                Long jobNumber = 30L;
271                Long workArea = 0L;
272        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
273                this.createShiftDifferentialRule(
274                                "BWS-CAL",
275                                "REG",
276                                "PRM",
277                                "IN", // changed from "SD1" to "IN" for changes of adding groupKeyCode to Job
278                                "SD1",
279                                "SD1",
280                                (new LocalTime(16, 0)),
281                                (new LocalTime(0, 0)),
282                                new BigDecimal(4), // minHours
283                                new BigDecimal("15"), // maxGap
284                                dayArray);
285
286                // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
287                DateTime start = new DateTime(2010, 3, 29, 14, 0, 0, 0, zone);
288                List<TimeBlock> blocks = new ArrayList<TimeBlock>();
289                CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
290                blocks.addAll(TkTestUtils.createUniformTimeBlocks(start, 2, new BigDecimal("4"), "RGN", jobNumber, workArea));
291                blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusHours(4).plusMinutes(15), 2, new BigDecimal("2"), "RGN", jobNumber, workArea));
292                blocks.addAll(TkTestUtils.createUniformTimeBlocks(new DateTime(2010, 3, 29, 12, 58, 0, 0, zone), 2, new BigDecimal(1), "RGN", jobNumber, workArea));
293                TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
294
295                // Verify pre-Rule Run
296                TkTestUtils.verifyAggregateHourSums("admin", "Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(14));}},aggregate,2);
297
298                // Run Rule
299                TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
300                tdoc.setTimeBlocks(blocks);
301                TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
302
303                // Verify post-Rule Run
304                TkTestUtils.verifyAggregateHourSums("admin", "Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(8));put("RGN", new BigDecimal(14));}},aggregate,2);
305        }
306
307        @SuppressWarnings("serial")
308        @Test
309        /**
310         * Runs on every day on the interval: [16:00, 24:00)
311         * Max Gap: 15 minutes
312         * Min Hours: 4
313         *
314         * @throws Exception
315         */
316        public void testProcessShiftSimpleCase() throws Exception {
317                // Create the Rule
318                boolean[] dayArray = {true, true, true, true, true, true, true};
319                // Matches HR Job ID #1 (job # 30)
320                Long jobNumber = 30L;
321                Long workArea = 0L;
322        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
323                this.createShiftDifferentialRule(
324                                "BWS-CAL",
325                                "REG",
326                                "PRM",
327                                "IN", // changed from "SD1" to "IN" for changes of adding groupKeyCode to Job
328                                "SD1",
329                                "SD1",
330                                (new LocalTime(16, 0)), //4pm
331                                (new LocalTime(0, 0)),  //midnight
332                                new BigDecimal(4), // minHours
333                                new BigDecimal("15.00"), // maxGap minutes
334                                dayArray);
335
336                // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
337                DateTime start = new DateTime(2010, 3, 29, 14, 0, 0, 0, zone);
338                List<TimeBlock> blocks = new ArrayList<TimeBlock>();
339                CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
340                blocks.addAll(TkTestUtils.createUniformTimeBlocks(start, 2, new BigDecimal("4"), "REG", jobNumber, workArea));
341                blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusHours(4).plusMinutes(15), 2, new BigDecimal("2"), "REG", jobNumber, workArea));
342                TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
343
344                // Verify pre-Rule Run
345                TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("REG", new BigDecimal(12));}},aggregate,2);
346
347                // Run Rule
348                TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
349                tdoc.setTimeBlocks(blocks);
350                TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
351
352                // Verify post-Rule Run
353                TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(8));put("REG", new BigDecimal(12));}},aggregate,2);
354        }
355
356        /**
357         * Stores the Shift Differential Rule in the database for testing.
358         *
359         * dayBooleans[] is a 7 element array of booleans, [0, 6] is [sun, sat]
360         */
361        private void createShiftDifferentialRule(String pyCalendarGroup, String fromEarnGroup, String premiumEarnCode, String location, String payGrade, String hrSalGroup, LocalTime startTime, LocalTime endTime, BigDecimal minHours, BigDecimal maxGap, boolean dayBooleans[]) {
362                Assert.assertTrue("Wrong number of day booleans", dayBooleans.length == 7);
363
364                ShiftDifferentialRuleService service = TkServiceLocator.getShiftDifferentialRuleService();
365                ShiftDifferentialRule sdr = new ShiftDifferentialRule();
366
367                sdr.setBeginTime(new Time(startTime.toDateTimeToday().getMillis()));
368                sdr.setEndTime(new Time(endTime.toDateTimeToday().getMillis()));
369                sdr.setMinHours(minHours);
370                sdr.setMaxGap(maxGap);
371                sdr.setActive(true);
372                sdr.setUserPrincipalId(USER_PRINCIPAL_ID);
373                sdr.setEffectiveLocalDate(JAN_AS_OF_DATE.toLocalDate());
374                sdr.setLocation(location);
375                sdr.setPayGrade(payGrade);
376                sdr.setHrSalGroup(hrSalGroup);
377                sdr.setFromEarnGroup(fromEarnGroup);
378                sdr.setPyCalendarGroup(pyCalendarGroup);
379                sdr.setEarnCode(premiumEarnCode);
380        sdr.setRuleType("default");
381
382                for (int i=0; i<dayBooleans.length; i++) {
383                        switch(i) {
384                        case 0:
385                                sdr.setSunday(dayBooleans[i]);
386                                break;
387                        case 1:
388                                sdr.setMonday(dayBooleans[i]);
389                                break;
390                        case 2:
391                                sdr.setTuesday(dayBooleans[i]);
392                                break;
393                        case 3:
394                                sdr.setWednesday(dayBooleans[i]);
395                                break;
396                        case 4:
397                                sdr.setThursday(dayBooleans[i]);
398                                break;
399                        case 5:
400                                sdr.setFriday(dayBooleans[i]);
401                                break;
402                        case 6:
403                                sdr.setSaturday(dayBooleans[i]);
404                                break;
405                        }
406                }
407
408                service.saveOrUpdate(sdr);
409
410                ShiftDifferentialRule sdrBack = service.getShiftDifferentialRule(sdr.getTkShiftDiffRuleId());
411
412        DateTimeZone tz = HrServiceLocator.getTimezoneService().getUserTimezoneWithFallback();
413        LocalTime orig_start = new LocalTime(sdr.getBeginTime());
414                LocalTime orig_end = new LocalTime(sdr.getEndTime());
415
416                LocalTime stored_start = new LocalTime(sdrBack.getBeginTime());
417                LocalTime stored_end = new LocalTime(sdrBack.getEndTime());
418
419                Assert.assertTrue("Start times not equal.", orig_start.equals(stored_start));
420                Assert.assertTrue("End times not equal.", orig_end.equals(stored_end));
421        }
422
423
424    /**
425     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
426     *
427     * Create a single 24 hour timeblock that spans two different shift, both exceeding the min hours
428     */
429    @Test
430    public void overlapMultipleShiftsWithSameTimeBlock() {
431        // Create the Rule
432        boolean[] dayArray = {true, true, true, true, true, true, true};
433        // Matches HR Job ID #1 (job # 30)
434        Long jobNumber = 30L;
435        Long workArea = 0L;
436        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
437
438        //3pm to 8am, 6 hour minimum, 90 minute max gap
439        this.createShiftDifferentialRule(
440                "BWS-CAL",
441                "REG",
442                "PRM",
443                "IN",
444                "SD1",
445                "SD1",
446                (new LocalTime(15, 0)),
447                (new LocalTime(8, 0)),
448                new BigDecimal(6), // minHours
449                new BigDecimal("90.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, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
454        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
455
456        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
457        DateTime tbStart = new DateTime(2010, 3, 29, 0, 0, 0, 0, zone);
458
459        //24 time block (midnight to midnight)
460        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart, 1, new BigDecimal("24"), "REG", jobNumber, workArea));
461
462        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
463
464        // Verify pre-Rule Run
465        TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("REG", new BigDecimal(24));}},aggregate,2);
466
467        // Run Rule
468        TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
469        tdoc.setTimeBlocks(blocks);
470        TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
471
472        // Verify post-Rule Run
473        TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(17));put("REG", new BigDecimal(24));}},aggregate,2);
474
475    }
476
477    /**
478     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
479     *
480     * Create a two 24 hour timeblocks that span three different shifts, all exceeding the min hours
481     */
482    @Test
483    public void overlapMultipleShiftsWithMultipleTimeBlocks() {
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        //24 time block (midnight to midnight)
516        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart.plusDays(1), 1, new BigDecimal("24"), "REG", jobNumber, workArea));
517
518        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
519
520        // Verify pre-Rule Run
521        TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("REG", new BigDecimal(48));}},aggregate,2);
522
523        // Run Rule
524        TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
525        tdoc.setTimeBlocks(blocks);
526        TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
527
528        // Verify post-Rule Run
529        //overlaps from 12a-8a (8 hours), 3p-8a (17 hours), and 3p - 12a (9 hours) == 34 hours
530        TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(34));put("REG", new BigDecimal(48));}},aggregate,2);
531
532    }
533
534    /**
535     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
536     *
537     * Create a single 24 hour timeblock that spans two different shift, both exceeding the min hours
538     */
539    @Test
540    public void overlapMultipleShiftsWithSameTimeBlockExceedingMinOnOneShift() {
541        // Create the Rule
542        boolean[] dayArray = {true, true, true, true, true, true, true};
543        // Matches HR Job ID #1 (job # 30)
544        Long jobNumber = 30L;
545        Long workArea = 0L;
546        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
547
548        //3pm to 8am, 6 hour minimum, 90 minute max gap
549        this.createShiftDifferentialRule(
550                "BWS-CAL",
551                "REG",
552                "PRM",
553                "IN",
554                "SD1",
555                "SD1",
556                (new LocalTime(15, 0)),
557                (new LocalTime(8, 0)),
558                new BigDecimal(6), // minHours
559                new BigDecimal("90.00"), // maxGap
560                dayArray);
561
562        DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
563        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
564        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
565
566        //reg timeblock 3am - midnight
567        DateTime tbStart = new DateTime(2010, 3, 30, 3, 0, 0, 0, zone);
568        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart,   1, new BigDecimal("21"), "REG", jobNumber, workArea));
569
570        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
571
572        // Verify pre-Rule Run
573        TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
574                .put("PRM", BigDecimal.ZERO)
575                .put("REG", new BigDecimal(21)).build(), aggregate, 2);
576
577        // Run Rule
578        TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
579        tdoc.setTimeBlocks(blocks);
580        TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
581
582        // Verify post-Rule Run
583        TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
584                .put("PRM", new BigDecimal(9))
585                .put("REG", new BigDecimal(21)).build(),
586                aggregate,
587                2);
588
589    }
590
591    /**
592     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
593     *
594     * Create a single 24 hour timeblock that spans two different shift, both exceeding the min hours
595     */
596    @Test
597    public void overlapMultipleShiftsWithSameTimeBlockExceedingMinOnFirstShift() {
598        // Create the Rule
599        boolean[] dayArray = {true, true, true, true, true, true, true};
600        // Matches HR Job ID #1 (job # 30)
601        Long jobNumber = 30L;
602        Long workArea = 0L;
603        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
604
605        //3pm to 8am, 6 hour minimum, 90 minute max gap
606        this.createShiftDifferentialRule(
607                "BWS-CAL",
608                "REG",
609                "PRM",
610                "IN",
611                "SD1",
612                "SD1",
613                (new LocalTime(15, 0)),
614                (new LocalTime(8, 0)),
615                new BigDecimal(6), // minHours
616                new BigDecimal("90.00"), // maxGap
617                dayArray);
618
619        DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
620        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
621        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
622
623        //reg timeblock 1am - 9pm
624        DateTime tbStart = new DateTime(2010, 3, 30, 1, 0, 0, 0, zone);
625        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart,   1, new BigDecimal("19"), "REG", jobNumber, workArea));
626
627        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
628
629        // Verify pre-Rule Run
630        TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
631                .put("PRM", BigDecimal.ZERO)
632                .put("REG", new BigDecimal(19)).build(), aggregate, 2);
633
634        // Run Rule
635        TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
636        tdoc.setTimeBlocks(blocks);
637        TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
638
639        // Verify post-Rule Run
640        TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
641                        .put("PRM", new BigDecimal(7))
642                        .put("REG", new BigDecimal(19)).build(),
643                aggregate,
644                2);
645
646    }
647
648    /**
649     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
650     *
651     * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
652     */
653    @Test
654    public void overlapMultipleShiftsWithSameTimeBlocNeitherExceedingMin() {
655        // Create the Rule
656        boolean[] dayArray = {true, true, true, true, true, true, true};
657        // Matches HR Job ID #1 (job # 30)
658        Long jobNumber = 30L;
659        Long workArea = 0L;
660        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
661
662        //3pm to 8am, 6 hour minimum, 90 minute max gap
663        this.createShiftDifferentialRule(
664                "BWS-CAL",
665                "REG",
666                "PRM",
667                "IN",
668                "SD1",
669                "SD1",
670                (new LocalTime(15, 0)),
671                (new LocalTime(8, 0)),
672                new BigDecimal(6), // minHours
673                new BigDecimal("90.00"), // maxGap
674                dayArray);
675
676        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
677        DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
678        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
679        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
680
681        //reg timeblock 3am - 8pm
682        DateTime tbStart = new DateTime(2010, 3, 30, 3, 0, 0, 0, zone);
683        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart,   1, new BigDecimal("17"), "REG", jobNumber, workArea));
684
685        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
686
687        // Verify pre-Rule Run
688        TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
689                .put("PRM", BigDecimal.ZERO)
690                .put("REG", new BigDecimal(17)).build(), aggregate, 2);
691
692        // Run Rule
693        TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
694        tdoc.setTimeBlocks(blocks);
695        TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
696
697        // Verify post-Rule Run
698        TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
699                        .put("PRM", BigDecimal.ZERO)
700                        .put("REG", new BigDecimal(17)).build(),
701                aggregate,
702                2);
703    }
704
705    /**
706     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
707     *
708     * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
709     */
710    @Test
711    public void multipleTimeBlocksOvernightExceedingMin() {
712        // Create the Rule
713        boolean[] dayArray = {true, true, true, true, true, true, true};
714        // Matches HR Job ID #1 (job # 30)
715        Long jobNumber = 30L;
716        Long workArea = 0L;
717        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
718
719        //3pm to 8am, 6 hour minimum, 90 minute max gap
720        this.createShiftDifferentialRule(
721                "BWS-CAL",
722                "REG",
723                "PRM",
724                "IN",
725                "SD1",
726                "SD1",
727                (new LocalTime(15, 0)),
728                (new LocalTime(8, 0)),
729                new BigDecimal(6), // minHours
730                new BigDecimal("90.00"), // maxGap
731                dayArray);
732
733        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
734        DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
735        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
736        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
737
738        //reg timeblock 10pm - midnight
739        DateTime tbStart1 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
740        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
741        //reg timeblock midnight - 5am
742        DateTime tbStart2 = new DateTime(2010, 3, 31, 0, 0, 0, 0, zone);
743        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("5"), "REG", jobNumber, workArea));
744
745        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
746
747        // Verify pre-Rule Run
748        TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
749                .put("PRM", BigDecimal.ZERO)
750                .put("REG", BigDecimal.valueOf(7)).build(), aggregate, 2);
751
752        // Run Rule
753        TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
754        tdoc.setTimeBlocks(blocks);
755        TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
756
757        // Verify post-Rule Run
758        TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
759                        .put("PRM", BigDecimal.valueOf(7))
760                        .put("REG", BigDecimal.valueOf(7)).build(),
761                aggregate,
762                2);
763    }
764
765    /**
766     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
767     *
768     * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
769     */
770    @Test
771    public void multipleTimeBlocksOvernightExceedingMinWithSixtyMinuteGap() {
772        // Create the Rule
773        boolean[] dayArray = {true, true, true, true, true, true, true};
774        // Matches HR Job ID #1 (job # 30)
775        Long jobNumber = 30L;
776        Long workArea = 0L;
777        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
778
779        //3pm to 8am, 6 hour minimum, 90 minute max gap
780        this.createShiftDifferentialRule(
781                "BWS-CAL",
782                "REG",
783                "PRM",
784                "IN",
785                "SD1",
786                "SD1",
787                (new LocalTime(15, 0)),
788                (new LocalTime(8, 0)),
789                new BigDecimal(6), // minHours
790                new BigDecimal("90.00"), // maxGap
791                dayArray);
792
793        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
794        DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
795        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
796        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
797
798        //reg timeblock 10pm - midnight
799        DateTime tbStart1 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
800        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
801        //reg timeblock 1am - 5am
802        DateTime tbStart2 = new DateTime(2010, 3, 31, 1, 0, 0, 0, zone);
803        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("4"), "REG", jobNumber, workArea));
804
805        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
806
807        // Verify pre-Rule Run
808        TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
809                .put("PRM", BigDecimal.ZERO)
810                .put("REG", BigDecimal.valueOf(6)).build(), aggregate, 2);
811
812        // Run Rule
813        TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
814        tdoc.setTimeBlocks(blocks);
815        TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
816
817        // Verify post-Rule Run
818        TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
819                        .put("PRM", BigDecimal.valueOf(6))
820                        .put("REG", BigDecimal.valueOf(6)).build(),
821                aggregate,
822                2);
823    }
824
825    /**
826     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
827     *
828     * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
829     */
830    @Test
831    public void multipleTimeBlocksOvernightExceedingMinWithNinetyMinuteGap() {
832        // Create the Rule
833        boolean[] dayArray = {true, true, true, true, true, true, true};
834        // Matches HR Job ID #1 (job # 30)
835        Long jobNumber = 30L;
836        Long workArea = 0L;
837        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
838
839        //3pm to 8am, 6 hour minimum, 90 minute max gap
840        this.createShiftDifferentialRule(
841                "BWS-CAL",
842                "REG",
843                "PRM",
844                "IN",
845                "SD1",
846                "SD1",
847                (new LocalTime(15, 0)),
848                (new LocalTime(8, 0)),
849                new BigDecimal(6), // minHours
850                new BigDecimal("90.00"), // maxGap
851                dayArray);
852
853        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
854        DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
855        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
856        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
857
858        //reg timeblock 10pm - midnight
859        DateTime tbStart1 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
860        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
861        //reg timeblock 1:30am - 5:30pm
862        DateTime tbStart2 = new DateTime(2010, 3, 31, 1, 30, 0, 0, zone);
863        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("4"), "REG", jobNumber, workArea));
864
865        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
866
867        // Verify pre-Rule Run
868        TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
869                .put("PRM", BigDecimal.ZERO)
870                .put("REG", BigDecimal.valueOf(6)).build(), aggregate, 2);
871
872        // Run Rule
873        TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
874        tdoc.setTimeBlocks(blocks);
875        TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
876
877        // Verify post-Rule Run
878        TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
879                        .put("PRM", BigDecimal.valueOf(6))
880                        .put("REG", BigDecimal.valueOf(6)).build(),
881                aggregate,
882                2);
883    }
884
885    /**
886     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
887     *
888     * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
889     */
890    @Test
891    public void multipleTimeBlocksOvernightExceedingMinButExceedingGap() {
892        // Create the Rule
893        boolean[] dayArray = {true, true, true, true, true, true, true};
894        // Matches HR Job ID #1 (job # 30)
895        Long jobNumber = 30L;
896        Long workArea = 0L;
897        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
898
899        //3pm to 8am, 6 hour minimum, 90 minute max gap
900        this.createShiftDifferentialRule(
901                "BWS-CAL",
902                "REG",
903                "PRM",
904                "IN",
905                "SD1",
906                "SD1",
907                (new LocalTime(15, 0)),
908                (new LocalTime(8, 0)),
909                new BigDecimal(6), // minHours
910                new BigDecimal("90.00"), // maxGap
911                dayArray);
912
913        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
914        DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
915        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
916        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
917
918        //reg timeblock 10pm - midnight
919        DateTime tbStart1 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
920        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
921        //reg timeblock 1:36am - 5:36am
922        DateTime tbStart2 = new DateTime(2010, 3, 31, 1, 36, 0, 0, zone);
923        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("4"), "REG", jobNumber, workArea));
924
925        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
926
927        // Verify pre-Rule Run
928        TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
929                .put("PRM", BigDecimal.ZERO)
930                .put("REG", BigDecimal.valueOf(6)).build(), aggregate, 2);
931
932        // Run Rule
933        TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
934        tdoc.setTimeBlocks(blocks);
935        TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
936
937        // Verify post-Rule Run
938        TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
939                        .put("PRM", BigDecimal.valueOf(0))
940                        .put("REG", BigDecimal.valueOf(6)).build(),
941                aggregate,
942                2);
943    }
944
945
946    /**
947     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
948     *
949     * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
950     */
951    @Test
952    public void threeBlocksWithinSameShift() {
953        // Create the Rule
954        boolean[] dayArray = {true, true, true, true, true, true, true};
955        // Matches HR Job ID #1 (job # 30)
956        Long jobNumber = 30L;
957        Long workArea = 0L;
958        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
959
960        //3pm to 8am, 6 hour minimum, 90 minute max gap
961        this.createShiftDifferentialRule(
962                "BWS-CAL",
963                "REG",
964                "PRM",
965                "IN",
966                "SD1",
967                "SD1",
968                (new LocalTime(15, 0)),
969                (new LocalTime(8, 0)),
970                new BigDecimal(6), // minHours
971                new BigDecimal("90.00"), // maxGap
972                dayArray);
973
974        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
975        DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
976        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
977
978        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
979
980        //reg timeblock 3pm - 5pm
981        DateTime tbStart1 = new DateTime(2010, 3, 30, 15, 0, 0, 0, zone);
982        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
983
984        // 6pm - 9pm
985        DateTime tbStart2 = new DateTime(2010, 3, 30, 18, 0, 0, 0, zone);
986        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("3"), "REG", jobNumber, workArea));
987
988        // 10pm - 11pm
989        DateTime tbStart3 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
990        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3,   1, new BigDecimal("1"), "REG", jobNumber, workArea));
991
992        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
993
994        // Verify pre-Rule Run
995        TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
996                .put("PRM", BigDecimal.ZERO)
997                .put("REG", BigDecimal.valueOf(6)).build(), aggregate, 2);
998
999        // Run Rule
1000        TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
1001        tdoc.setTimeBlocks(blocks);
1002        TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
1003
1004        // Verify post-Rule Run
1005        TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1006                        .put("PRM", BigDecimal.valueOf(6))
1007                        .put("REG", BigDecimal.valueOf(6)).build(),
1008                aggregate,
1009                2);
1010    }
1011
1012    /**
1013     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
1014     *
1015     * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
1016     */
1017    @Test
1018    public void fourBlocksWithinSameShiftSpanningTwoDays() {
1019        // Create the Rule
1020        boolean[] dayArray = {true, true, true, true, true, true, true};
1021        // Matches HR Job ID #1 (job # 30)
1022        Long jobNumber = 30L;
1023        Long workArea = 0L;
1024        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1025
1026        //3pm to 8am, 6 hour minimum, 90 minute max gap
1027        this.createShiftDifferentialRule(
1028                "BWS-CAL",
1029                "REG",
1030                "PRM",
1031                "IN",
1032                "SD1",
1033                "SD1",
1034                (new LocalTime(15, 0)),
1035                (new LocalTime(8, 0)),
1036                new BigDecimal(6), // minHours
1037                new BigDecimal("90.00"), // maxGap
1038                dayArray);
1039
1040        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1041        DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1042        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1043
1044        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1045
1046        //reg timeblock 5pm - 6pm
1047        DateTime tbStart1 = new DateTime(2010, 3, 30, 17, 0, 0, 0, zone);
1048        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("1"), "REG", jobNumber, workArea));
1049
1050        // 7:30pm - 8:30pm
1051        DateTime tbStart2 = new DateTime(2010, 3, 30, 19, 30, 0, 0, zone);
1052        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("1"), "REG", jobNumber, workArea));
1053
1054        // 10pm - midnight
1055        DateTime tbStart3 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
1056        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
1057
1058        // 1:30am - 4:30am
1059        DateTime tbStart4 = new DateTime(2010, 3, 31, 1, 30, 0, 0, zone);
1060        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart4,   1, new BigDecimal("3"), "REG", jobNumber, workArea));
1061
1062        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1063
1064        // Verify pre-Rule Run
1065        TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1066                .put("PRM", BigDecimal.ZERO)
1067                .put("REG", BigDecimal.valueOf(7)).build(), aggregate, 2);
1068
1069        // Run Rule
1070        TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
1071        tdoc.setTimeBlocks(blocks);
1072        TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
1073
1074        // Verify post-Rule Run
1075        TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1076                        .put("PRM", BigDecimal.valueOf(7))
1077                        .put("REG", BigDecimal.valueOf(7)).build(),
1078                aggregate,
1079                2);
1080    }
1081
1082
1083    /**
1084     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
1085     *
1086     * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
1087     */
1088    @Test
1089    public void EarnGroupTest() {
1090        // Create the Rule
1091        // Matches HR Job ID #1 (job # 30)
1092        String principalId = "10112";
1093
1094
1095        Long jobNumber = 0L;
1096        String groupKey = "IU-IN";
1097        Long workArea = 1010L;
1098        Long task = 0L;
1099        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1100
1101
1102        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1103        DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1104        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1105
1106        TimesheetDocument td = TkTestUtils.populateBlankTimesheetDocument(start, principalId);
1107
1108        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1109
1110        //rgh - monday 8a-11a no-shift
1111        DateTime tbStart1 = new DateTime(2010, 3, 30, 8, 0, 0, 0, zone);
1112        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("3"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1113
1114        //echr - monday 2p-5p - should have 3 hrs SHEG
1115        DateTime tbStart2 = new DateTime(2010, 3, 30, 14, 0, 0, 0, zone);
1116        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("3"), "ECHR", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1117
1118        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1119
1120        // Verify pre-Rule Run
1121        TkTestUtils.verifyAggregateHourSums(principalId, "Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1122                .put("RGH", BigDecimal.valueOf(3))
1123                .put("ECHR", BigDecimal.valueOf(3)).build(), aggregate, 2);
1124
1125        // Run Rule
1126
1127        //td.setTimeBlocks(blocks);
1128        List<LeaveBlock> leaveBlocks = TimesheetUtils.getLeaveBlocksForTimesheet(td);
1129        List<TimeBlock> initialBlocks = TimesheetUtils.getTimesheetTimeblocksForProcessing(td, true);
1130        List<TimeBlock> referenceTimeBlocks = TimesheetUtils.getReferenceTimeBlocks(initialBlocks);
1131
1132        //reset time block
1133        //List<TimeBlock> tbs = TkServiceLocator.getTimesheetService().resetTimeBlock(initialBlocks, td.getAsOfDate());
1134        TimesheetUtils.processTimeBlocksWithRuleChange(blocks, referenceTimeBlocks, leaveBlocks, td.getCalendarEntry(), td, HrContext.getPrincipalId());
1135
1136        //refresh Timesheet
1137        td = TkServiceLocator.getTimesheetService().getTimesheetDocument(td.getDocumentId());
1138        aggregate = new TkTimeBlockAggregate(td.getTimeBlocks(), payCalendarEntry);
1139        // Verify post-Rule Run
1140        TkTestUtils.verifyAggregateHourSums(principalId, "Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1141                        .put("RGH", BigDecimal.valueOf(3))
1142                        .put("ECHR", BigDecimal.valueOf(3))
1143                        .put("SHEG", BigDecimal.valueOf(3)).build(),
1144                aggregate,
1145                2);
1146    }
1147
1148    @Test
1149    public void calendarGroupTest() {
1150        // Create the Rule
1151        // Matches HR Job ID #1 (job # 30)
1152        String principalId = "10113";
1153
1154
1155        Long jobNumber = 0L;
1156        String groupKey = "IU-IN";
1157        Long workArea = 1010L;
1158        Long task = 0L;
1159        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1160
1161
1162        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1163        DateTime start = new DateTime(2010, 3, 16, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1164        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates(principalId, start);
1165
1166        TimesheetDocument td = TkTestUtils.populateBlankTimesheetDocument(start, principalId);
1167
1168        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1169
1170        //rgh - monday 8a-11a no-shift
1171        DateTime tbStart1 = new DateTime(2010, 3, 21, 8, 0, 0, 0, zone);
1172        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("3"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1173
1174        //echr - monday 2p-5p - should have 3 hrs SHEG
1175        //DateTime tbStart2 = new DateTime(2010, 3, 30, 14, 0, 0, 0, zone);
1176        //blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("3"), "ECHR", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1177
1178        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1179
1180        // Verify pre-Rule Run
1181        TkTestUtils.verifyAggregateHourSums(principalId, "Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1182                .put("RGH", BigDecimal.valueOf(3)).build(), aggregate, 1);
1183
1184        // Run Rule
1185
1186        //td.setTimeBlocks(blocks);
1187        List<LeaveBlock> leaveBlocks = TimesheetUtils.getLeaveBlocksForTimesheet(td);
1188        List<TimeBlock> initialBlocks = TimesheetUtils.getTimesheetTimeblocksForProcessing(td, true);
1189        List<TimeBlock> referenceTimeBlocks = TimesheetUtils.getReferenceTimeBlocks(initialBlocks);
1190
1191        //reset time block
1192        //List<TimeBlock> tbs = TkServiceLocator.getTimesheetService().resetTimeBlock(initialBlocks, td.getAsOfDate());
1193        TimesheetUtils.processTimeBlocksWithRuleChange(blocks, referenceTimeBlocks, leaveBlocks, td.getCalendarEntry(), td, HrContext.getPrincipalId());
1194
1195        //refresh Timesheet
1196        td = TkServiceLocator.getTimesheetService().getTimesheetDocument(td.getDocumentId());
1197        aggregate = new TkTimeBlockAggregate(td.getTimeBlocks(), payCalendarEntry);
1198        // Verify post-Rule Run
1199        TkTestUtils.verifyAggregateHourSums(principalId, "Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1200                        .put("RGH", BigDecimal.valueOf(3))
1201                        .put("SHCG", BigDecimal.valueOf(3))
1202                        .put("SHDY", BigDecimal.valueOf(0)).build(),
1203                aggregate,
1204                1);
1205    }
1206
1207    @Test
1208    public void daysTest() {
1209        // Create the Rule
1210        // Matches HR Job ID #1 (job # 30)
1211        String principalId = "10114";
1212
1213
1214        Long jobNumber = 0L;
1215        String groupKey = "IU-IN";
1216        Long workArea = 1010L;
1217        Long task = 0L;
1218        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1219
1220
1221        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1222        DateTime start = new DateTime(2010, 3, 21, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1223        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1224
1225        TimesheetDocument td = TkTestUtils.populateBlankTimesheetDocument(start, principalId);
1226
1227        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1228
1229        //rgh - sunday 3p-5p shift: 2 hr SHDY
1230        DateTime tbStart1 = new DateTime(2010, 3, 28, 15, 0, 0, 0, zone);
1231        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1232
1233        //rgh - monday 3p-5p - shift: none
1234        DateTime tbStart2 = new DateTime(2010, 3, 29, 15, 0, 0, 0, zone);
1235        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1236
1237        //rgh - sun 10p- mon 3a - shift: 2hr SHDY (on sunday timeblock)
1238        DateTime tbStart3 = new DateTime(2010, 3, 28, 22, 0, 0, 0, zone);
1239        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1240        DateTime tbStart4 = new DateTime(2010, 3, 29, 0, 0, 0, 0, zone);
1241        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart4,   1, new BigDecimal("3"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1242
1243
1244        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1245
1246        // Verify pre-Rule Run
1247        TkTestUtils.verifyAggregateHourSums(principalId, "Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1248                .put("RGH", BigDecimal.valueOf(9)).build(), aggregate, 2);
1249
1250        // Run Rule
1251
1252        //td.setTimeBlocks(blocks);
1253        List<LeaveBlock> leaveBlocks = TimesheetUtils.getLeaveBlocksForTimesheet(td);
1254        List<TimeBlock> initialBlocks = TimesheetUtils.getTimesheetTimeblocksForProcessing(td, true);
1255        List<TimeBlock> referenceTimeBlocks = TimesheetUtils.getReferenceTimeBlocks(initialBlocks);
1256
1257        //reset time block
1258        //List<TimeBlock> tbs = TkServiceLocator.getTimesheetService().resetTimeBlock(initialBlocks, td.getAsOfDate());
1259        TimesheetUtils.processTimeBlocksWithRuleChange(blocks, referenceTimeBlocks, leaveBlocks, td.getCalendarEntry(), td, HrContext.getPrincipalId());
1260
1261        //refresh Timesheet
1262        td = TkServiceLocator.getTimesheetService().getTimesheetDocument(td.getDocumentId());
1263        aggregate = new TkTimeBlockAggregate(td.getTimeBlocks(), payCalendarEntry);
1264        // Verify post-Rule Run
1265        TkTestUtils.verifyAggregateHourSums(principalId, "Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1266                        .put("RGH", BigDecimal.valueOf(9))
1267                        .put("SHDY", BigDecimal.valueOf(4)).build(),
1268                aggregate,
1269                2);
1270    }
1271
1272
1273    @Test
1274    public void daysTestTimeZone() {
1275        // Create the Rule
1276        // Matches HR Job ID #1 (job # 30)
1277        String principalId = "10115";
1278
1279
1280        Long jobNumber = 0L;
1281        String groupKey = "IU-IN";
1282        Long workArea = 1010L;
1283        Long task = 0L;
1284        HrContext.setTargetPrincipalId(principalId);
1285        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1286
1287
1288        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1289        DateTime start = new DateTime(2010, 3, 21, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1290        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1291
1292        TimesheetDocument td = TkTestUtils.populateBlankTimesheetDocument(start, principalId);
1293
1294        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1295
1296        //rgh - sunday 3p-5p shift: 2 hr SHDY
1297        DateTime tbStart1 = new DateTime(2010, 3, 28, 15, 0, 0, 0, zone);
1298        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1299
1300        //rgh - monday 3p-5p - shift: none
1301        DateTime tbStart2 = new DateTime(2010, 3, 29, 15, 0, 0, 0, zone);
1302        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1303
1304        //rgh - sun 10p- mon 3a - shift: 2hr SHDY2 (on sunday timeblock)
1305        DateTime tbStart3 = new DateTime(2010, 3, 28, 22, 0, 0, 0, zone);
1306        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1307        DateTime tbStart4 = new DateTime(2010, 3, 29, 0, 0, 0, 0, zone);
1308        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart4,   1, new BigDecimal("3"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1309
1310
1311        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1312
1313        // Verify pre-Rule Run
1314        TkTestUtils.verifyAggregateHourSums(principalId, "Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1315                .put("RGH", BigDecimal.valueOf(9)).build(), aggregate, 2);
1316
1317        // Run Rule
1318
1319        //td.setTimeBlocks(blocks);
1320        List<LeaveBlock> leaveBlocks = TimesheetUtils.getLeaveBlocksForTimesheet(td);
1321        List<TimeBlock> initialBlocks = TimesheetUtils.getTimesheetTimeblocksForProcessing(td, true);
1322        List<TimeBlock> referenceTimeBlocks = TimesheetUtils.getReferenceTimeBlocks(initialBlocks);
1323
1324        //reset time block
1325        //List<TimeBlock> tbs = TkServiceLocator.getTimesheetService().resetTimeBlock(initialBlocks, td.getAsOfDate());
1326        TimesheetUtils.processTimeBlocksWithRuleChange(blocks, referenceTimeBlocks, leaveBlocks, td.getCalendarEntry(), td, HrContext.getPrincipalId());
1327
1328        //refresh Timesheet
1329        td = TkServiceLocator.getTimesheetService().getTimesheetDocument(td.getDocumentId());
1330        aggregate = new TkTimeBlockAggregate(td.getTimeBlocks(), payCalendarEntry);
1331        // Verify post-Rule Run
1332        TkTestUtils.verifyAggregateHourSums(principalId, "Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1333                        .put("RGH", BigDecimal.valueOf(9))
1334                        .put("SHDY2", BigDecimal.valueOf(4)).build(),
1335                aggregate,
1336                2);
1337        HrContext.clearTargetUser();
1338    }
1339
1340    @Test
1341    public void daysTimeTest() {
1342        // Create the Rule
1343        // Matches HR Job ID #1 (job # 30)
1344        String principalId = "10116";
1345
1346
1347        Long jobNumber = 0L;
1348        String groupKey = "IU-IN";
1349        Long workArea = 1010L;
1350        Long task = 0L;
1351        HrContext.setTargetPrincipalId(principalId);
1352        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1353
1354
1355        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1356        DateTime start = new DateTime(2010, 3, 21, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1357        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1358
1359        TimesheetDocument td = TkTestUtils.populateBlankTimesheetDocument(start, principalId);
1360
1361        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1362
1363        //rgh - monday 2p-4p - shift: 1 hr SHDT
1364        DateTime tbStart1 = new DateTime(2010, 3, 22, 14, 0, 0, 0, zone);
1365        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1366
1367        //rgh - tuesday 4:30p-8p - shift: 3.5 hr SHDT
1368        DateTime tbStart2 = new DateTime(2010, 3, 23, 16, 30, 0, 0, zone);
1369        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("3.5"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1370
1371        //rgh - sun 3p - 8p - no shift
1372        DateTime tbStart3 = new DateTime(2010, 3, 21, 15, 0, 0, 0, zone);
1373        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3,   1, new BigDecimal("5"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1374
1375        //rgh - wed, thurs, fri, sat 2p - 7p  - shift: SHDT 4hr on wed-fri (none saturday)
1376        DateTime tbStart4 = new DateTime(2010, 3, 24, 14, 0, 0, 0, zone);
1377        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart4,   4, new BigDecimal("5"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1378
1379
1380        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1381
1382        // Verify pre-Rule Run
1383        TkTestUtils.verifyAggregateHourSums(principalId, "Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1384                .put("RGH", BigDecimal.valueOf(30.5)).build(), aggregate, 1);
1385
1386        // Run Rule
1387
1388        //td.setTimeBlocks(blocks);
1389        List<LeaveBlock> leaveBlocks = TimesheetUtils.getLeaveBlocksForTimesheet(td);
1390        List<TimeBlock> initialBlocks = TimesheetUtils.getTimesheetTimeblocksForProcessing(td, true);
1391        List<TimeBlock> referenceTimeBlocks = TimesheetUtils.getReferenceTimeBlocks(initialBlocks);
1392
1393        //reset time block
1394        //List<TimeBlock> tbs = TkServiceLocator.getTimesheetService().resetTimeBlock(initialBlocks, td.getAsOfDate());
1395        TimesheetUtils.processTimeBlocksWithRuleChange(blocks, referenceTimeBlocks, leaveBlocks, td.getCalendarEntry(), td, HrContext.getPrincipalId());
1396
1397        //refresh Timesheet
1398        td = TkServiceLocator.getTimesheetService().getTimesheetDocument(td.getDocumentId());
1399        aggregate = new TkTimeBlockAggregate(td.getTimeBlocks(), payCalendarEntry);
1400        // Verify post-Rule Run
1401        TkTestUtils.verifyAggregateHourSums(principalId, "Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1402                        .put("RGH", BigDecimal.valueOf(30.5))
1403                        .put("SHDT", BigDecimal.valueOf(16.5)).build(),
1404                aggregate,
1405                1);
1406        HrContext.clearTargetUser();
1407    }
1408
1409    @Test
1410    public void timeTest() {
1411        // Create the Rule
1412        // Matches HR Job ID #1 (job # 30)
1413        String principalId = "10117";
1414
1415
1416        Long jobNumber = 0L;
1417        String groupKey = "IU-IN";
1418        Long workArea = 1010L;
1419        Long task = 0L;
1420        HrContext.setTargetPrincipalId(principalId);
1421        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1422
1423
1424        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1425        DateTime start = new DateTime(2010, 3, 21, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1426        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1427
1428        TimesheetDocument td = TkTestUtils.populateBlankTimesheetDocument(start, principalId);
1429
1430        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1431
1432        //rgh - sunday 3p - monday 3a - shift: 9 hr SHTT
1433        DateTime tbStart1a = new DateTime(2010, 3, 21, 15, 0, 0, 0, zone);
1434        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1a,   1, new BigDecimal("9"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1435        DateTime tbStart1b = new DateTime(2010, 3, 22, 0, 0, 0, 0, zone);
1436        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1b,   1, new BigDecimal("3"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1437
1438
1439        //rgh - monday 3a-9a - shift: 3 hr SHTT
1440        DateTime tbStart2 = new DateTime(2010, 3, 22, 3, 0, 0, 0, zone);
1441        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("6"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1442
1443        //rgh - monday 10p - 11:30p - shift: 1.5 hr SHTT
1444        DateTime tbStart3 = new DateTime(2010, 3, 22, 22, 0, 0, 0, zone);
1445        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3,   1, new BigDecimal("1.5"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1446
1447        //rgh - tues 12a - 7a  - shift: SHTT 6hr
1448        DateTime tbStart4 = new DateTime(2010, 3, 23, 0, 0, 0, 0, zone);
1449        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart4,   1, new BigDecimal("7"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1450
1451        //rgh - wed 6a - 11a  - no shift
1452        DateTime tbStart5 = new DateTime(2010, 3, 24, 6, 0, 0, 0, zone);
1453        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart5,   1, new BigDecimal("5"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1454
1455
1456        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1457
1458        // Verify pre-Rule Run
1459        TkTestUtils.verifyAggregateHourSums(principalId, "Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1460                .put("RGH", BigDecimal.valueOf(31.5)).build(), aggregate, 1);
1461
1462        // Run Rule
1463
1464        //td.setTimeBlocks(blocks);
1465        List<LeaveBlock> leaveBlocks = TimesheetUtils.getLeaveBlocksForTimesheet(td);
1466        List<TimeBlock> initialBlocks = TimesheetUtils.getTimesheetTimeblocksForProcessing(td, true);
1467        List<TimeBlock> referenceTimeBlocks = TimesheetUtils.getReferenceTimeBlocks(initialBlocks);
1468
1469        //reset time block
1470        //List<TimeBlock> tbs = TkServiceLocator.getTimesheetService().resetTimeBlock(initialBlocks, td.getAsOfDate());
1471        TimesheetUtils.processTimeBlocksWithRuleChange(blocks, referenceTimeBlocks, leaveBlocks, td.getCalendarEntry(), td, HrContext.getPrincipalId());
1472
1473        //refresh Timesheet
1474        td = TkServiceLocator.getTimesheetService().getTimesheetDocument(td.getDocumentId());
1475        aggregate = new TkTimeBlockAggregate(td.getTimeBlocks(), payCalendarEntry);
1476        // Verify post-Rule Run
1477        TkTestUtils.verifyAggregateHourSums(principalId, "Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1478                        .put("RGH", BigDecimal.valueOf(31.5))
1479                        .put("SHTT", BigDecimal.valueOf(19.5)).build(),
1480                aggregate,
1481                1);
1482        HrContext.clearTargetUser();
1483    }
1484
1485
1486    @Ignore
1487    @Test
1488    public void minHoursTest() {
1489        // Create the Rule
1490        // Matches HR Job ID #1 (job # 30)
1491        String principalId = "10118";
1492
1493
1494        Long jobNumber = 0L;
1495        String groupKey = "IU-IN";
1496        Long workArea = 1010L;
1497        Long task = 0L;
1498        HrContext.setTargetPrincipalId(principalId);
1499        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1500
1501
1502        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1503        DateTime start = new DateTime(2010, 3, 21, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1504        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1505
1506        TimesheetDocument td = TkTestUtils.populateBlankTimesheetDocument(start, principalId);
1507
1508        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1509
1510        //rgh - sunday 3p - monday 3a - shift: 9 hr SHMH
1511        DateTime tbStart1a = new DateTime(2010, 3, 21, 15, 0, 0, 0, zone);
1512        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1a,   1, new BigDecimal("9"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1513        DateTime tbStart1b = new DateTime(2010, 3, 22, 0, 0, 0, 0, zone);
1514        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1b,   1, new BigDecimal("3"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1515
1516
1517        //rgh - monday 3a-9a - shift: 3 hr SHMH
1518        DateTime tbStart2 = new DateTime(2010, 3, 22, 3, 0, 0, 0, zone);
1519        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("6"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1520
1521        //rgh - monday 10p - 11:30p - shift: 1.5 hr SHMH (after timeblock added below)
1522        DateTime tbStart3 = new DateTime(2010, 3, 22, 22, 0, 0, 0, zone);
1523        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3,   1, new BigDecimal("1.5"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1524
1525        //rgh - tues 12a - 7a  - shift: SHTT 6hr
1526        DateTime tbStart4 = new DateTime(2010, 3, 23, 0, 0, 0, 0, zone);
1527        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart4,   1, new BigDecimal("7"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1528
1529        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1530
1531        // Verify pre-Rule Run
1532        TkTestUtils.verifyAggregateHourSums(principalId, "Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1533                .put("RGH", BigDecimal.valueOf(26.5)).build(), aggregate, 1);
1534
1535        // Run Rule
1536
1537        //td.setTimeBlocks(blocks);
1538        List<LeaveBlock> leaveBlocks = TimesheetUtils.getLeaveBlocksForTimesheet(td);
1539        List<TimeBlock> initialBlocks = TimesheetUtils.getTimesheetTimeblocksForProcessing(td, true);
1540        List<TimeBlock> referenceTimeBlocks = TimesheetUtils.getReferenceTimeBlocks(initialBlocks);
1541
1542        //reset time block
1543        //List<TimeBlock> tbs = TkServiceLocator.getTimesheetService().resetTimeBlock(initialBlocks, td.getAsOfDate());
1544        TimesheetUtils.processTimeBlocksWithRuleChange(blocks, referenceTimeBlocks, leaveBlocks, td.getCalendarEntry(), td, HrContext.getPrincipalId());
1545
1546        //refresh Timesheet
1547        td = TkServiceLocator.getTimesheetService().getTimesheetDocument(td.getDocumentId());
1548        aggregate = new TkTimeBlockAggregate(td.getTimeBlocks(), payCalendarEntry);
1549        // Verify post-Rule Run
1550        TkTestUtils.verifyAggregateHourSums(principalId, "Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1551                        .put("RGH", BigDecimal.valueOf(26.5))
1552                        .put("SHTT", BigDecimal.valueOf(7.5))
1553                        .put("SHMH", BigDecimal.valueOf(12)).build(),
1554                aggregate,
1555                1);
1556        HrContext.clearTargetUser();
1557    }
1558}