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    @Ignore
425    @Test
426    /**
427     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
428     *
429     * Create a timeblock on two days, one day has normal REG shift eligible
430     * hours, one day has HOL time.
431     *
432     * Modified version of the simple case, SDR from 12:00 to 17:00, every day,
433     * must have at least 4 hours with a maximum 15 minute gap.
434     *
435     */
436    public void simpleCaseWithWorkSchedule() throws Exception {
437                // Create the Rule
438                boolean[] dayArray = {true, true, true, true, true, true, true};
439                // Matches HR Job ID #1 (job # 30)
440                Long jobNumber = 30L;
441                Long workArea = 0L;
442                this.createShiftDifferentialRule(
443                                "BWS-CAL",
444                                "REG",
445                                "PRM",
446                                "IN",  // changed from "SD1" to "IN" for changes of adding groupKeyCode to Job
447                                "SD1",
448                                "SD1",
449                                (new LocalTime(12, 0)),
450                                (new LocalTime(17, 0)),
451                                new BigDecimal(4), // minHours
452                                new BigDecimal("15.00"), // maxGap
453                                dayArray);
454
455                // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
456                DateTime start = new DateTime(2010, 3, 29, 12, 0, 0, 0, TKUtils.getSystemDateTimeZone());
457        DateTime holtime = new DateTime(2010, 3, 30, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
458                List<TimeBlock> blocks = new ArrayList<TimeBlock>();
459                CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
460                blocks.addAll(TkTestUtils.createUniformTimeBlocks(start,   1, new BigDecimal("4"), "REG", jobNumber, workArea));
461        blocks.addAll(TkTestUtils.createUniformTimeBlocks(holtime, 1, new BigDecimal("4"), "HOL", jobNumber, workArea));
462
463                TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
464
465                // Verify pre-Rule Run
466                TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("REG", new BigDecimal(4));put("HOL", new BigDecimal(4));}},aggregate,2);
467
468                // Run Rule
469                TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
470                tdoc.setTimeBlocks(blocks);
471                TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
472
473                // Verify post-Rule Run
474                TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(8));put("REG", new BigDecimal(4));}},aggregate,2);
475
476    }
477
478
479
480    /**
481     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
482     *
483     * Create a single 24 hour timeblock that spans two different shift, both exceeding the min hours
484     */
485    @Test
486    public void overlapMultipleShiftsWithSameTimeBlock() {
487        // Create the Rule
488        boolean[] dayArray = {true, true, true, true, true, true, true};
489        // Matches HR Job ID #1 (job # 30)
490        Long jobNumber = 30L;
491        Long workArea = 0L;
492        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
493
494        //3pm to 8am, 6 hour minimum, 90 minute max gap
495        this.createShiftDifferentialRule(
496                "BWS-CAL",
497                "REG",
498                "PRM",
499                "IN",
500                "SD1",
501                "SD1",
502                (new LocalTime(15, 0)),
503                (new LocalTime(8, 0)),
504                new BigDecimal(6), // minHours
505                new BigDecimal("90.00"), // maxGap
506                dayArray);
507
508        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
509        DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
510        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
511
512        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
513        DateTime tbStart = new DateTime(2010, 3, 29, 0, 0, 0, 0, zone);
514
515        //24 time block (midnight to midnight)
516        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart, 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(24));}},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        TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(17));put("REG", new BigDecimal(24));}},aggregate,2);
530
531    }
532
533    /**
534     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
535     *
536     * Create a two 24 hour timeblocks that span three different shifts, all exceeding the min hours
537     */
538    @Test
539    public void overlapMultipleShiftsWithMultipleTimeBlocks() {
540        // Create the Rule
541        boolean[] dayArray = {true, true, true, true, true, true, true};
542        // Matches HR Job ID #1 (job # 30)
543        Long jobNumber = 30L;
544        Long workArea = 0L;
545        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
546
547        //3pm to 8am, 6 hour minimum, 90 minute max gap
548        this.createShiftDifferentialRule(
549                "BWS-CAL",
550                "REG",
551                "PRM",
552                "IN",
553                "SD1",
554                "SD1",
555                (new LocalTime(15, 0)),
556                (new LocalTime(8, 0)),
557                new BigDecimal(6), // minHours
558                new BigDecimal("90.00"), // maxGap
559                dayArray);
560
561        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
562        DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
563        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
564
565        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
566        DateTime tbStart = new DateTime(2010, 3, 29, 0, 0, 0, 0, zone);
567
568        //24 time block (midnight to midnight)
569        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart, 1, new BigDecimal("24"), "REG", jobNumber, workArea));
570
571        //24 time block (midnight to midnight)
572        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart.plusDays(1), 1, new BigDecimal("24"), "REG", jobNumber, workArea));
573
574        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
575
576        // Verify pre-Rule Run
577        TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("REG", new BigDecimal(48));}},aggregate,2);
578
579        // Run Rule
580        TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
581        tdoc.setTimeBlocks(blocks);
582        TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
583
584        // Verify post-Rule Run
585        //overlaps from 12a-8a (8 hours), 3p-8a (17 hours), and 3p - 12a (9 hours) == 34 hours
586        TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(34));put("REG", new BigDecimal(48));}},aggregate,2);
587
588    }
589
590    /**
591     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
592     *
593     * Create a single 24 hour timeblock that spans two different shift, both exceeding the min hours
594     */
595    @Test
596    public void overlapMultipleShiftsWithSameTimeBlockExceedingMinOnOneShift() {
597        // Create the Rule
598        boolean[] dayArray = {true, true, true, true, true, true, true};
599        // Matches HR Job ID #1 (job # 30)
600        Long jobNumber = 30L;
601        Long workArea = 0L;
602        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
603
604        //3pm to 8am, 6 hour minimum, 90 minute max gap
605        this.createShiftDifferentialRule(
606                "BWS-CAL",
607                "REG",
608                "PRM",
609                "IN",
610                "SD1",
611                "SD1",
612                (new LocalTime(15, 0)),
613                (new LocalTime(8, 0)),
614                new BigDecimal(6), // minHours
615                new BigDecimal("90.00"), // maxGap
616                dayArray);
617
618        DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
619        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
620        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
621
622        //reg timeblock 3am - midnight
623        DateTime tbStart = new DateTime(2010, 3, 30, 3, 0, 0, 0, zone);
624        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart,   1, new BigDecimal("21"), "REG", jobNumber, workArea));
625
626        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
627
628        // Verify pre-Rule Run
629        TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
630                .put("PRM", BigDecimal.ZERO)
631                .put("REG", new BigDecimal(21)).build(), aggregate, 2);
632
633        // Run Rule
634        TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
635        tdoc.setTimeBlocks(blocks);
636        TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
637
638        // Verify post-Rule Run
639        TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
640                .put("PRM", new BigDecimal(9))
641                .put("REG", new BigDecimal(21)).build(),
642                aggregate,
643                2);
644
645    }
646
647    /**
648     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
649     *
650     * Create a single 24 hour timeblock that spans two different shift, both exceeding the min hours
651     */
652    @Test
653    public void overlapMultipleShiftsWithSameTimeBlockExceedingMinOnFirstShift() {
654        // Create the Rule
655        boolean[] dayArray = {true, true, true, true, true, true, true};
656        // Matches HR Job ID #1 (job # 30)
657        Long jobNumber = 30L;
658        Long workArea = 0L;
659        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
660
661        //3pm to 8am, 6 hour minimum, 90 minute max gap
662        this.createShiftDifferentialRule(
663                "BWS-CAL",
664                "REG",
665                "PRM",
666                "IN",
667                "SD1",
668                "SD1",
669                (new LocalTime(15, 0)),
670                (new LocalTime(8, 0)),
671                new BigDecimal(6), // minHours
672                new BigDecimal("90.00"), // maxGap
673                dayArray);
674
675        DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
676        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
677        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
678
679        //reg timeblock 1am - 9pm
680        DateTime tbStart = new DateTime(2010, 3, 30, 1, 0, 0, 0, zone);
681        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart,   1, new BigDecimal("19"), "REG", jobNumber, workArea));
682
683        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
684
685        // Verify pre-Rule Run
686        TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
687                .put("PRM", BigDecimal.ZERO)
688                .put("REG", new BigDecimal(19)).build(), aggregate, 2);
689
690        // Run Rule
691        TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
692        tdoc.setTimeBlocks(blocks);
693        TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
694
695        // Verify post-Rule Run
696        TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
697                        .put("PRM", new BigDecimal(7))
698                        .put("REG", new BigDecimal(19)).build(),
699                aggregate,
700                2);
701
702    }
703
704    /**
705     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
706     *
707     * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
708     */
709    @Test
710    public void overlapMultipleShiftsWithSameTimeBlocNeitherExceedingMin() {
711        // Create the Rule
712        boolean[] dayArray = {true, true, true, true, true, true, true};
713        // Matches HR Job ID #1 (job # 30)
714        Long jobNumber = 30L;
715        Long workArea = 0L;
716        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
717
718        //3pm to 8am, 6 hour minimum, 90 minute max gap
719        this.createShiftDifferentialRule(
720                "BWS-CAL",
721                "REG",
722                "PRM",
723                "IN",
724                "SD1",
725                "SD1",
726                (new LocalTime(15, 0)),
727                (new LocalTime(8, 0)),
728                new BigDecimal(6), // minHours
729                new BigDecimal("90.00"), // maxGap
730                dayArray);
731
732        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
733        DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
734        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
735        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
736
737        //reg timeblock 3am - 8pm
738        DateTime tbStart = new DateTime(2010, 3, 30, 3, 0, 0, 0, zone);
739        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart,   1, new BigDecimal("17"), "REG", jobNumber, workArea));
740
741        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
742
743        // Verify pre-Rule Run
744        TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
745                .put("PRM", BigDecimal.ZERO)
746                .put("REG", new BigDecimal(17)).build(), aggregate, 2);
747
748        // Run Rule
749        TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
750        tdoc.setTimeBlocks(blocks);
751        TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
752
753        // Verify post-Rule Run
754        TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
755                        .put("PRM", BigDecimal.ZERO)
756                        .put("REG", new BigDecimal(17)).build(),
757                aggregate,
758                2);
759    }
760
761    /**
762     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
763     *
764     * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
765     */
766    @Test
767    public void multipleTimeBlocksOvernightExceedingMin() {
768        // Create the Rule
769        boolean[] dayArray = {true, true, true, true, true, true, true};
770        // Matches HR Job ID #1 (job # 30)
771        Long jobNumber = 30L;
772        Long workArea = 0L;
773        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
774
775        //3pm to 8am, 6 hour minimum, 90 minute max gap
776        this.createShiftDifferentialRule(
777                "BWS-CAL",
778                "REG",
779                "PRM",
780                "IN",
781                "SD1",
782                "SD1",
783                (new LocalTime(15, 0)),
784                (new LocalTime(8, 0)),
785                new BigDecimal(6), // minHours
786                new BigDecimal("90.00"), // maxGap
787                dayArray);
788
789        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
790        DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
791        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
792        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
793
794        //reg timeblock 10pm - midnight
795        DateTime tbStart1 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
796        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
797        //reg timeblock midnight - 5am
798        DateTime tbStart2 = new DateTime(2010, 3, 31, 0, 0, 0, 0, zone);
799        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("5"), "REG", jobNumber, workArea));
800
801        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
802
803        // Verify pre-Rule Run
804        TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
805                .put("PRM", BigDecimal.ZERO)
806                .put("REG", BigDecimal.valueOf(7)).build(), aggregate, 2);
807
808        // Run Rule
809        TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
810        tdoc.setTimeBlocks(blocks);
811        TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
812
813        // Verify post-Rule Run
814        TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
815                        .put("PRM", BigDecimal.valueOf(7))
816                        .put("REG", BigDecimal.valueOf(7)).build(),
817                aggregate,
818                2);
819    }
820
821    /**
822     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
823     *
824     * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
825     */
826    @Test
827    public void multipleTimeBlocksOvernightExceedingMinWithSixtyMinuteGap() {
828        // Create the Rule
829        boolean[] dayArray = {true, true, true, true, true, true, true};
830        // Matches HR Job ID #1 (job # 30)
831        Long jobNumber = 30L;
832        Long workArea = 0L;
833        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
834
835        //3pm to 8am, 6 hour minimum, 90 minute max gap
836        this.createShiftDifferentialRule(
837                "BWS-CAL",
838                "REG",
839                "PRM",
840                "IN",
841                "SD1",
842                "SD1",
843                (new LocalTime(15, 0)),
844                (new LocalTime(8, 0)),
845                new BigDecimal(6), // minHours
846                new BigDecimal("90.00"), // maxGap
847                dayArray);
848
849        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
850        DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
851        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
852        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
853
854        //reg timeblock 10pm - midnight
855        DateTime tbStart1 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
856        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
857        //reg timeblock 1am - 5am
858        DateTime tbStart2 = new DateTime(2010, 3, 31, 1, 0, 0, 0, zone);
859        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("4"), "REG", jobNumber, workArea));
860
861        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
862
863        // Verify pre-Rule Run
864        TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
865                .put("PRM", BigDecimal.ZERO)
866                .put("REG", BigDecimal.valueOf(6)).build(), aggregate, 2);
867
868        // Run Rule
869        TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
870        tdoc.setTimeBlocks(blocks);
871        TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
872
873        // Verify post-Rule Run
874        TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
875                        .put("PRM", BigDecimal.valueOf(6))
876                        .put("REG", BigDecimal.valueOf(6)).build(),
877                aggregate,
878                2);
879    }
880
881    /**
882     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
883     *
884     * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
885     */
886    @Test
887    public void multipleTimeBlocksOvernightExceedingMinWithNinetyMinuteGap() {
888        // Create the Rule
889        boolean[] dayArray = {true, true, true, true, true, true, true};
890        // Matches HR Job ID #1 (job # 30)
891        Long jobNumber = 30L;
892        Long workArea = 0L;
893        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
894
895        //3pm to 8am, 6 hour minimum, 90 minute max gap
896        this.createShiftDifferentialRule(
897                "BWS-CAL",
898                "REG",
899                "PRM",
900                "IN",
901                "SD1",
902                "SD1",
903                (new LocalTime(15, 0)),
904                (new LocalTime(8, 0)),
905                new BigDecimal(6), // minHours
906                new BigDecimal("90.00"), // maxGap
907                dayArray);
908
909        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
910        DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
911        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
912        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
913
914        //reg timeblock 10pm - midnight
915        DateTime tbStart1 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
916        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
917        //reg timeblock 1:30am - 5:30pm
918        DateTime tbStart2 = new DateTime(2010, 3, 31, 1, 30, 0, 0, zone);
919        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("4"), "REG", jobNumber, workArea));
920
921        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
922
923        // Verify pre-Rule Run
924        TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
925                .put("PRM", BigDecimal.ZERO)
926                .put("REG", BigDecimal.valueOf(6)).build(), aggregate, 2);
927
928        // Run Rule
929        TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
930        tdoc.setTimeBlocks(blocks);
931        TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
932
933        // Verify post-Rule Run
934        TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
935                        .put("PRM", BigDecimal.valueOf(6))
936                        .put("REG", BigDecimal.valueOf(6)).build(),
937                aggregate,
938                2);
939    }
940
941    /**
942     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
943     *
944     * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
945     */
946    @Test
947    public void multipleTimeBlocksOvernightExceedingMinButExceedingGap() {
948        // Create the Rule
949        boolean[] dayArray = {true, true, true, true, true, true, true};
950        // Matches HR Job ID #1 (job # 30)
951        Long jobNumber = 30L;
952        Long workArea = 0L;
953        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
954
955        //3pm to 8am, 6 hour minimum, 90 minute max gap
956        this.createShiftDifferentialRule(
957                "BWS-CAL",
958                "REG",
959                "PRM",
960                "IN",
961                "SD1",
962                "SD1",
963                (new LocalTime(15, 0)),
964                (new LocalTime(8, 0)),
965                new BigDecimal(6), // minHours
966                new BigDecimal("90.00"), // maxGap
967                dayArray);
968
969        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
970        DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
971        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
972        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
973
974        //reg timeblock 10pm - midnight
975        DateTime tbStart1 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
976        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
977        //reg timeblock 1:36am - 5:36am
978        DateTime tbStart2 = new DateTime(2010, 3, 31, 1, 36, 0, 0, zone);
979        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("4"), "REG", jobNumber, workArea));
980
981        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
982
983        // Verify pre-Rule Run
984        TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
985                .put("PRM", BigDecimal.ZERO)
986                .put("REG", BigDecimal.valueOf(6)).build(), aggregate, 2);
987
988        // Run Rule
989        TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
990        tdoc.setTimeBlocks(blocks);
991        TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
992
993        // Verify post-Rule Run
994        TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
995                        .put("PRM", BigDecimal.valueOf(0))
996                        .put("REG", BigDecimal.valueOf(6)).build(),
997                aggregate,
998                2);
999    }
1000
1001
1002    /**
1003     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
1004     *
1005     * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
1006     */
1007    @Test
1008    public void threeBlocksWithinSameShift() {
1009        // Create the Rule
1010        boolean[] dayArray = {true, true, true, true, true, true, true};
1011        // Matches HR Job ID #1 (job # 30)
1012        Long jobNumber = 30L;
1013        Long workArea = 0L;
1014        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1015
1016        //3pm to 8am, 6 hour minimum, 90 minute max gap
1017        this.createShiftDifferentialRule(
1018                "BWS-CAL",
1019                "REG",
1020                "PRM",
1021                "IN",
1022                "SD1",
1023                "SD1",
1024                (new LocalTime(15, 0)),
1025                (new LocalTime(8, 0)),
1026                new BigDecimal(6), // minHours
1027                new BigDecimal("90.00"), // maxGap
1028                dayArray);
1029
1030        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1031        DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1032        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1033
1034        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1035
1036        //reg timeblock 3pm - 5pm
1037        DateTime tbStart1 = new DateTime(2010, 3, 30, 15, 0, 0, 0, zone);
1038        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
1039
1040        // 6pm - 9pm
1041        DateTime tbStart2 = new DateTime(2010, 3, 30, 18, 0, 0, 0, zone);
1042        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("3"), "REG", jobNumber, workArea));
1043
1044        // 10pm - 11pm
1045        DateTime tbStart3 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
1046        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3,   1, new BigDecimal("1"), "REG", jobNumber, workArea));
1047
1048        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1049
1050        // Verify pre-Rule Run
1051        TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1052                .put("PRM", BigDecimal.ZERO)
1053                .put("REG", BigDecimal.valueOf(6)).build(), aggregate, 2);
1054
1055        // Run Rule
1056        TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
1057        tdoc.setTimeBlocks(blocks);
1058        TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
1059
1060        // Verify post-Rule Run
1061        TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1062                        .put("PRM", BigDecimal.valueOf(6))
1063                        .put("REG", BigDecimal.valueOf(6)).build(),
1064                aggregate,
1065                2);
1066    }
1067
1068    /**
1069     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
1070     *
1071     * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
1072     */
1073    @Test
1074    public void fourBlocksWithinSameShiftSpanningTwoDays() {
1075        // Create the Rule
1076        boolean[] dayArray = {true, true, true, true, true, true, true};
1077        // Matches HR Job ID #1 (job # 30)
1078        Long jobNumber = 30L;
1079        Long workArea = 0L;
1080        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1081
1082        //3pm to 8am, 6 hour minimum, 90 minute max gap
1083        this.createShiftDifferentialRule(
1084                "BWS-CAL",
1085                "REG",
1086                "PRM",
1087                "IN",
1088                "SD1",
1089                "SD1",
1090                (new LocalTime(15, 0)),
1091                (new LocalTime(8, 0)),
1092                new BigDecimal(6), // minHours
1093                new BigDecimal("90.00"), // maxGap
1094                dayArray);
1095
1096        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1097        DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1098        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1099
1100        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1101
1102        //reg timeblock 5pm - 6pm
1103        DateTime tbStart1 = new DateTime(2010, 3, 30, 17, 0, 0, 0, zone);
1104        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("1"), "REG", jobNumber, workArea));
1105
1106        // 7:30pm - 8:30pm
1107        DateTime tbStart2 = new DateTime(2010, 3, 30, 19, 30, 0, 0, zone);
1108        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("1"), "REG", jobNumber, workArea));
1109
1110        // 10pm - midnight
1111        DateTime tbStart3 = new DateTime(2010, 3, 30, 22, 0, 0, 0, zone);
1112        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3,   1, new BigDecimal("2"), "REG", jobNumber, workArea));
1113
1114        // 1:30am - 4:30am
1115        DateTime tbStart4 = new DateTime(2010, 3, 31, 1, 30, 0, 0, zone);
1116        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart4,   1, new BigDecimal("3"), "REG", jobNumber, workArea));
1117
1118        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1119
1120        // Verify pre-Rule Run
1121        TkTestUtils.verifyAggregateHourSums("admin","Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1122                .put("PRM", BigDecimal.ZERO)
1123                .put("REG", BigDecimal.valueOf(7)).build(), aggregate, 2);
1124
1125        // Run Rule
1126        TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(start, "admin");
1127        tdoc.setTimeBlocks(blocks);
1128        TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
1129
1130        // Verify post-Rule Run
1131        TkTestUtils.verifyAggregateHourSums("admin","Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1132                        .put("PRM", BigDecimal.valueOf(7))
1133                        .put("REG", BigDecimal.valueOf(7)).build(),
1134                aggregate,
1135                2);
1136    }
1137
1138
1139    /**
1140     * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
1141     *
1142     * Create a single 17 hour timeblock that spans two different shift, neither exceeding the min hours
1143     */
1144    @Test
1145    public void EarnGroupTest() {
1146        // Create the Rule
1147        // Matches HR Job ID #1 (job # 30)
1148        String principalId = "10112";
1149
1150
1151        Long jobNumber = 0L;
1152        String groupKey = "IU-IN";
1153        Long workArea = 1010L;
1154        Long task = 0L;
1155        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1156
1157
1158        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1159        DateTime start = new DateTime(2010, 3, 29, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1160        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1161
1162        TimesheetDocument td = TkTestUtils.populateBlankTimesheetDocument(start, principalId);
1163
1164        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1165
1166        //rgh - monday 8a-11a no-shift
1167        DateTime tbStart1 = new DateTime(2010, 3, 30, 8, 0, 0, 0, zone);
1168        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("3"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1169
1170        //echr - monday 2p-5p - should have 3 hrs SHEG
1171        DateTime tbStart2 = new DateTime(2010, 3, 30, 14, 0, 0, 0, zone);
1172        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("3"), "ECHR", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1173
1174        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1175
1176        // Verify pre-Rule Run
1177        TkTestUtils.verifyAggregateHourSums(principalId, "Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1178                .put("RGH", BigDecimal.valueOf(3))
1179                .put("ECHR", BigDecimal.valueOf(3)).build(), aggregate, 2);
1180
1181        // Run Rule
1182
1183        //td.setTimeBlocks(blocks);
1184        List<LeaveBlock> leaveBlocks = TimesheetUtils.getLeaveBlocksForTimesheet(td);
1185        List<TimeBlock> initialBlocks = TimesheetUtils.getTimesheetTimeblocksForProcessing(td, true);
1186        List<TimeBlock> referenceTimeBlocks = TimesheetUtils.getReferenceTimeBlocks(initialBlocks);
1187
1188        //reset time block
1189        //List<TimeBlock> tbs = TkServiceLocator.getTimesheetService().resetTimeBlock(initialBlocks, td.getAsOfDate());
1190        TimesheetUtils.processTimeBlocksWithRuleChange(blocks, referenceTimeBlocks, leaveBlocks, td.getCalendarEntry(), td, HrContext.getPrincipalId());
1191
1192        //refresh Timesheet
1193        td = TkServiceLocator.getTimesheetService().getTimesheetDocument(td.getDocumentId());
1194        aggregate = new TkTimeBlockAggregate(td.getTimeBlocks(), payCalendarEntry);
1195        // Verify post-Rule Run
1196        TkTestUtils.verifyAggregateHourSums(principalId, "Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1197                        .put("RGH", BigDecimal.valueOf(3))
1198                        .put("ECHR", BigDecimal.valueOf(3))
1199                        .put("SHEG", BigDecimal.valueOf(3)).build(),
1200                aggregate,
1201                2);
1202    }
1203
1204    @Test
1205    public void calendarGroupTest() {
1206        // Create the Rule
1207        // Matches HR Job ID #1 (job # 30)
1208        String principalId = "10113";
1209
1210
1211        Long jobNumber = 0L;
1212        String groupKey = "IU-IN";
1213        Long workArea = 1010L;
1214        Long task = 0L;
1215        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1216
1217
1218        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1219        DateTime start = new DateTime(2010, 3, 16, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1220        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates(principalId, start);
1221
1222        TimesheetDocument td = TkTestUtils.populateBlankTimesheetDocument(start, principalId);
1223
1224        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1225
1226        //rgh - monday 8a-11a no-shift
1227        DateTime tbStart1 = new DateTime(2010, 3, 21, 8, 0, 0, 0, zone);
1228        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("3"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1229
1230        //echr - monday 2p-5p - should have 3 hrs SHEG
1231        //DateTime tbStart2 = new DateTime(2010, 3, 30, 14, 0, 0, 0, zone);
1232        //blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("3"), "ECHR", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1233
1234        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1235
1236        // Verify pre-Rule Run
1237        TkTestUtils.verifyAggregateHourSums(principalId, "Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1238                .put("RGH", BigDecimal.valueOf(3)).build(), aggregate, 1);
1239
1240        // Run Rule
1241
1242        //td.setTimeBlocks(blocks);
1243        List<LeaveBlock> leaveBlocks = TimesheetUtils.getLeaveBlocksForTimesheet(td);
1244        List<TimeBlock> initialBlocks = TimesheetUtils.getTimesheetTimeblocksForProcessing(td, true);
1245        List<TimeBlock> referenceTimeBlocks = TimesheetUtils.getReferenceTimeBlocks(initialBlocks);
1246
1247        //reset time block
1248        //List<TimeBlock> tbs = TkServiceLocator.getTimesheetService().resetTimeBlock(initialBlocks, td.getAsOfDate());
1249        TimesheetUtils.processTimeBlocksWithRuleChange(blocks, referenceTimeBlocks, leaveBlocks, td.getCalendarEntry(), td, HrContext.getPrincipalId());
1250
1251        //refresh Timesheet
1252        td = TkServiceLocator.getTimesheetService().getTimesheetDocument(td.getDocumentId());
1253        aggregate = new TkTimeBlockAggregate(td.getTimeBlocks(), payCalendarEntry);
1254        // Verify post-Rule Run
1255        TkTestUtils.verifyAggregateHourSums(principalId, "Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1256                        .put("RGH", BigDecimal.valueOf(3))
1257                        .put("SHCG", BigDecimal.valueOf(3))
1258                        .put("SHDY", BigDecimal.valueOf(0)).build(),
1259                aggregate,
1260                1);
1261    }
1262
1263    @Test
1264    public void daysTest() {
1265        // Create the Rule
1266        // Matches HR Job ID #1 (job # 30)
1267        String principalId = "10114";
1268
1269
1270        Long jobNumber = 0L;
1271        String groupKey = "IU-IN";
1272        Long workArea = 1010L;
1273        Long task = 0L;
1274        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1275
1276
1277        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1278        DateTime start = new DateTime(2010, 3, 21, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1279        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1280
1281        TimesheetDocument td = TkTestUtils.populateBlankTimesheetDocument(start, principalId);
1282
1283        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1284
1285        //rgh - sunday 3p-5p shift: 2 hr SHDY
1286        DateTime tbStart1 = new DateTime(2010, 3, 28, 15, 0, 0, 0, zone);
1287        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1288
1289        //rgh - monday 3p-5p - shift: none
1290        DateTime tbStart2 = new DateTime(2010, 3, 29, 15, 0, 0, 0, zone);
1291        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1292
1293        //rgh - sun 10p- mon 3a - shift: 2hr SHDY (on sunday timeblock)
1294        DateTime tbStart3 = new DateTime(2010, 3, 28, 22, 0, 0, 0, zone);
1295        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1296        DateTime tbStart4 = new DateTime(2010, 3, 29, 0, 0, 0, 0, zone);
1297        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart4,   1, new BigDecimal("3"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1298
1299
1300        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1301
1302        // Verify pre-Rule Run
1303        TkTestUtils.verifyAggregateHourSums(principalId, "Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1304                .put("RGH", BigDecimal.valueOf(9)).build(), aggregate, 2);
1305
1306        // Run Rule
1307
1308        //td.setTimeBlocks(blocks);
1309        List<LeaveBlock> leaveBlocks = TimesheetUtils.getLeaveBlocksForTimesheet(td);
1310        List<TimeBlock> initialBlocks = TimesheetUtils.getTimesheetTimeblocksForProcessing(td, true);
1311        List<TimeBlock> referenceTimeBlocks = TimesheetUtils.getReferenceTimeBlocks(initialBlocks);
1312
1313        //reset time block
1314        //List<TimeBlock> tbs = TkServiceLocator.getTimesheetService().resetTimeBlock(initialBlocks, td.getAsOfDate());
1315        TimesheetUtils.processTimeBlocksWithRuleChange(blocks, referenceTimeBlocks, leaveBlocks, td.getCalendarEntry(), td, HrContext.getPrincipalId());
1316
1317        //refresh Timesheet
1318        td = TkServiceLocator.getTimesheetService().getTimesheetDocument(td.getDocumentId());
1319        aggregate = new TkTimeBlockAggregate(td.getTimeBlocks(), payCalendarEntry);
1320        // Verify post-Rule Run
1321        TkTestUtils.verifyAggregateHourSums(principalId, "Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1322                        .put("RGH", BigDecimal.valueOf(9))
1323                        .put("SHDY", BigDecimal.valueOf(4)).build(),
1324                aggregate,
1325                2);
1326    }
1327
1328
1329    @Test
1330    public void daysTestTimeZone() {
1331        // Create the Rule
1332        // Matches HR Job ID #1 (job # 30)
1333        String principalId = "10115";
1334
1335
1336        Long jobNumber = 0L;
1337        String groupKey = "IU-IN";
1338        Long workArea = 1010L;
1339        Long task = 0L;
1340        HrContext.setTargetPrincipalId(principalId);
1341        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
1342
1343
1344        // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
1345        DateTime start = new DateTime(2010, 3, 21, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
1346        CalendarEntry payCalendarEntry =  HrServiceLocator.getCalendarEntryService().getCurrentCalendarDates("admin", start);
1347
1348        TimesheetDocument td = TkTestUtils.populateBlankTimesheetDocument(start, principalId);
1349
1350        List<TimeBlock> blocks = new ArrayList<TimeBlock>();
1351
1352        //rgh - sunday 3p-5p shift: 2 hr SHDY
1353        DateTime tbStart1 = new DateTime(2010, 3, 28, 15, 0, 0, 0, zone);
1354        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart1,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1355
1356        //rgh - monday 3p-5p - shift: none
1357        DateTime tbStart2 = new DateTime(2010, 3, 29, 15, 0, 0, 0, zone);
1358        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart2,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1359
1360        //rgh - sun 10p- mon 3a - shift: 2hr SHDY2 (on sunday timeblock)
1361        DateTime tbStart3 = new DateTime(2010, 3, 28, 22, 0, 0, 0, zone);
1362        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart3,   1, new BigDecimal("2"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1363        DateTime tbStart4 = new DateTime(2010, 3, 29, 0, 0, 0, 0, zone);
1364        blocks.addAll(TkTestUtils.createUniformTimeBlocks(tbStart4,   1, new BigDecimal("3"), "RGH", jobNumber, workArea, task, groupKey, td.getDocumentId()));
1365
1366
1367        TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
1368
1369        // Verify pre-Rule Run
1370        TkTestUtils.verifyAggregateHourSums(principalId, "Pre-Check", new ImmutableMap.Builder<String, BigDecimal>()
1371                .put("RGH", BigDecimal.valueOf(9)).build(), aggregate, 2);
1372
1373        // Run Rule
1374
1375        //td.setTimeBlocks(blocks);
1376        List<LeaveBlock> leaveBlocks = TimesheetUtils.getLeaveBlocksForTimesheet(td);
1377        List<TimeBlock> initialBlocks = TimesheetUtils.getTimesheetTimeblocksForProcessing(td, true);
1378        List<TimeBlock> referenceTimeBlocks = TimesheetUtils.getReferenceTimeBlocks(initialBlocks);
1379
1380        //reset time block
1381        //List<TimeBlock> tbs = TkServiceLocator.getTimesheetService().resetTimeBlock(initialBlocks, td.getAsOfDate());
1382        TimesheetUtils.processTimeBlocksWithRuleChange(blocks, referenceTimeBlocks, leaveBlocks, td.getCalendarEntry(), td, HrContext.getPrincipalId());
1383
1384        //refresh Timesheet
1385        td = TkServiceLocator.getTimesheetService().getTimesheetDocument(td.getDocumentId());
1386        aggregate = new TkTimeBlockAggregate(td.getTimeBlocks(), payCalendarEntry);
1387        // Verify post-Rule Run
1388        TkTestUtils.verifyAggregateHourSums(principalId, "Post Rules Check", new ImmutableMap.Builder<String, BigDecimal>()
1389                        .put("RGH", BigDecimal.valueOf(9))
1390                        .put("SHDY2", BigDecimal.valueOf(4)).build(),
1391                aggregate,
1392                2);
1393        HrContext.clearTargetUser();
1394    }
1395}