001    /**
002     * Copyright 2004-2013 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.hr.time.shiftdiff.rule.service;
017    
018    import java.math.BigDecimal;
019    import java.sql.Date;
020    import java.sql.Time;
021    import java.util.ArrayList;
022    import java.util.HashMap;
023    import java.util.List;
024    
025    import org.joda.time.DateTime;
026    import org.joda.time.DateTimeZone;
027    import org.joda.time.LocalDate;
028    import org.joda.time.LocalTime;
029    import org.junit.Assert;
030    import org.junit.Ignore;
031    import org.junit.Test;
032    import org.kuali.hr.test.KPMETestCase;
033    import org.kuali.hr.time.assignment.Assignment;
034    import org.kuali.hr.time.assignment.AssignmentDescriptionKey;
035    import org.kuali.hr.time.calendar.CalendarEntries;
036    import org.kuali.hr.time.service.base.TkServiceLocator;
037    import org.kuali.hr.time.shiftdiff.rule.ShiftDifferentialRule;
038    import org.kuali.hr.time.test.TkTestUtils;
039    import org.kuali.hr.time.timeblock.TimeBlock;
040    import org.kuali.hr.time.timesheet.TimesheetDocument;
041    import org.kuali.hr.time.util.TKContext;
042    import org.kuali.hr.time.util.TKUtils;
043    import org.kuali.hr.time.util.TkConstants;
044    import org.kuali.hr.time.util.TkTimeBlockAggregate;
045    import org.kuali.hr.time.workschedule.WorkSchedule;
046    import org.kuali.hr.time.workschedule.WorkScheduleAssignment;
047    import org.kuali.hr.time.workschedule.WorkScheduleEntry;
048    
049    /**
050     *
051     * @author djunk
052     *
053     */
054    public class ShiftDifferentialRuleServiceProcessTest extends KPMETestCase {
055    
056    
057            public static final String USER_PRINCIPAL_ID = "admin";
058            private Date JAN_AS_OF_DATE = new Date((new DateTime(2010, 1, 1, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone())).getMillis());
059    
060    
061            /**
062             * Test with boundary carryover and overlapping rules.
063             *
064             * Rule 1:
065             *
066             * Runs on Tu, Wed, Th on the interval: [22:00, 4:00)
067             * Max Gap: 15 minutes
068             * Min Hours: 3
069             *
070             * Rule 2:
071             *
072             * Runs on Tu, Th on the interval: [23:00, 2:00)
073             * Max Gap: 2 hours
074             * Min Hours: 3
075             *
076             * Rule 3:
077             *
078             * Runs on W, Th on the interval: [5:00, 12:00)
079             * Max Gap: 15 minutes
080             * Min Hours: 7 hours
081             *
082             * Rule 4:
083             *
084             * Runs on W on the interval: [5:00, 12:00)
085             * Max Gap: 15 minutes
086             * Min Hours: 5
087             *
088             *
089             * |--------------+----+------------+------------|
090             * | Tu : 8/31/10 | XX | W : 9/1/10 | Th: 9/2/10 |
091             * |--------------+----+------------+------------|
092             * | 9:45p - 11:45| XX | Mid - 5a   | 5p - 11p   |
093             * |              | XX | 6a - Noon  |            |
094             * |--------------+----+------------+------------|
095         *
096         *
097         * Aug 31: 2h  : 21:45 - 23:45 (Tue) **
098         *                           [1: 5h 45m]  // [2: 2h 45m] - Not qualifying, min hours must be 3.
099         * Sep  1: 5h  : 00:00 - 05:00 (Wed) **
100         * Sep  1: 6h  : 06:00 - 12:00 (Wed) [4: 6h]
101         *
102         * Sep  1: 2h  : 22:00 - 24:00 (Wed)
103         * Sep  2: 1h  : 00:00 - 01:00 (Thu) [1: 3h]
104         *
105         * Sep  2: 6h  : 17:00 - 22:00 (Thu)
106         *
107         * 1: [22:00,  4:00) (Tue/Wed/Thu) minimum: 3h gap: 15m
108         * 2: [23:00,  2:00) (Tue/Thu)     minimum: 3h gap: 2h
109         * 3: [05:00, 12:00) (Wed/Thu)     minimum: 7h gap: 15m
110         * 4: [05:00, 12:00) (Wed)         minimum: 5h gap: 15m
111         *
112             */
113            @SuppressWarnings("serial")
114            @Test
115            public void testProcessTimesheetBoundaryCarryoverOverlapCase() throws Exception {
116            DateTimeZone tz = TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback();
117                    // Create the Rule    Sun,   Mon,   Tue,  Wed,   Thu,  Fri,  Sat
118                    boolean[] dayArray = {false, false, true, true, true, true, true};
119                    // Matches HR Job ID #1 (job # 30)
120                    Long jobNumber = 30L;
121                    Long workArea = 0L;
122                    this.createShiftDifferentialRule(
123                                    "BWS-CAL", "REG", "PRM", "SD1", "SD1", "SD1",
124                                    (new DateTime(2010, 8, 31, 22, 0, 0, 0, tz)),
125                                    (new DateTime(2010, 8, 31,  4, 0, 0, 0, tz)),
126                                    new BigDecimal(3), // minHours
127                                    new BigDecimal("0.25"), // maxGap
128                                    dayArray);
129    
130            dayArray = new boolean [] {false, false, true, false, true, true, true};
131                    this.createShiftDifferentialRule(
132                                    "BWS-CAL", "REG", "PRM", "SD1", "SD1", "SD1",
133                                    (new DateTime(2010, 8, 31, 23, 0, 0, 0, tz)),
134                                    (new DateTime(2010, 8, 31,  2, 0, 0, 0, tz)),
135                                    new BigDecimal(3), // minHours
136                                    new BigDecimal("2.0"), // maxGap
137                                    dayArray);
138    
139                    dayArray = new boolean[] {false, false, false, true, true, false, false};
140                    this.createShiftDifferentialRule(
141                                    "BWS-CAL", "REG", "PRM", "SD1", "SD1", "SD1",
142                                    (new DateTime(2010, 8, 31, 5, 0, 0, 0, tz)),
143                                    (new DateTime(2010, 8, 31,  12, 0, 0, 0, tz)),
144                                    new BigDecimal("7.0"), // minHours
145                                    new BigDecimal(".25"), // maxGap
146                                    dayArray);
147                    dayArray = new boolean[] {false, false, false, true, false, false, false};
148                    this.createShiftDifferentialRule(
149                                    "BWS-CAL", "REG", "PRM", "SD1", "SD1", "SD1",
150                                    (new DateTime(2010, 8, 31, 5, 0, 0, 0, tz)),
151                                    (new DateTime(2010, 8, 31,  12, 0, 0, 0, tz)),
152                                    new BigDecimal("5"), // minHours
153                                    new BigDecimal("0.25"), // maxGap
154                                    dayArray);
155    
156                    // Timeblocks
157    
158                    // August
159                    DateTime beginPeriodDate = new DateTime(2010, 8, 15, 0, 0, 0, 0, tz);
160                    DateTime endPeriodDate = new DateTime(2010, 9, 1, 0, 0, 0, 0, tz);
161                    CalendarEntries endOfAugust = TkServiceLocator.getCalendarEntriesService().getCalendarEntriesByBeginAndEndDate(beginPeriodDate.toLocalDate().toDateMidnight().toDate(), endPeriodDate.toLocalDate().toDateMidnight().toDate());
162                    DateTime start = new DateTime(2010, 8, 31, 21, 45, 0, 0, tz);
163                    List<TimeBlock> blocks = new ArrayList<TimeBlock>();
164                    TimesheetDocument tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", endOfAugust);
165                    Assignment assignment = TkServiceLocator.getAssignmentService().getAssignment("admin", new AssignmentDescriptionKey("30_30_30"), new Date(beginPeriodDate.getMillis()));
166                    blocks.addAll(TkTestUtils.createUniformActualTimeBlocks(tdoc, assignment, "RGN", start, 1, new BigDecimal(2), BigDecimal.ZERO));
167                    TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, endOfAugust, TkServiceLocator.getCalendarService().getCalendar(endOfAugust.getHrCalendarId()), true);
168                    tdoc.setTimeBlocks(blocks);
169                    TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
170                    TkTestUtils.verifyAggregateHourSumsFlatList("August Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(2));}},aggregate);
171                    TkServiceLocator.getTimeBlockService().saveTimeBlocks(new ArrayList<TimeBlock>(), aggregate.getFlattenedTimeBlockList(), TKContext.getPrincipalId());
172    
173    
174                    // September
175    
176                    start = new DateTime(2010, 9, 1, 0, 0, 0, 0, tz);
177                    CalendarEntries payCalendarEntry = TkServiceLocator.getCalendarService().getCurrentCalendarDates("admin", start.toLocalDate().toDateMidnight().toDate());
178                    tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", payCalendarEntry);
179                    blocks = new ArrayList<TimeBlock>();
180                    blocks.addAll(TkTestUtils.createUniformTimeBlocks(start, 1, new BigDecimal("5"), "RGN", jobNumber, workArea));
181                    blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusHours(6), 1, new BigDecimal("6"), "RGN", jobNumber, workArea));
182            blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusHours(22), 1, new BigDecimal("2"), "RGN", jobNumber, workArea));
183            blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusDays(1), 1, new BigDecimal("1"), "RGN", jobNumber, workArea));
184                    blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusDays(1).plusHours(17), 1, new BigDecimal("6"), "RGN", jobNumber, workArea));
185                    setDocumentIdOnBlocks(blocks, tdoc.getDocumentId());
186            
187                    aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry, TkServiceLocator.getCalendarService().getCalendar(payCalendarEntry.getHrCalendarId()), true);
188                    
189                    TkTestUtils.verifyAggregateHourSumsFlatList("September Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(20));}},aggregate);
190    
191                    // Verify carry over and applied PRM bucket
192                    TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
193                    TkTestUtils.verifyAggregateHourSumsFlatList("September Post-Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal("14.75"));put("RGN", new BigDecimal(20));}},aggregate);
194            }
195    
196        private void setDocumentIdOnBlocks(List<TimeBlock> blocks, String id) {
197            for (TimeBlock b : blocks) {
198                b.setDocumentId(id);
199            }
200        }
201    
202    
203            /**
204             * Test where previous time sheet contains hours that should be added to
205             * the next pay periods first day shift.
206             *
207             * Runs on Tu, Th on the interval: [22:00, 4:00)
208             * Max Gap: 15 minutes
209             * Min Hours: 3
210             *
211             * |--------------+----+------------+-------------|
212             * | Tu : 8/31/10 | XX | W : 9/1/10 | Th : 9/2/10 |
213             * |--------------+----+------------+-------------|
214             * | 10pm - Mid   | XX | Mid - 5am  | 5pm - 11pm  |
215             * |--------------+----+------------+-------------|
216             *
217             * @throws Exception
218             */
219            @SuppressWarnings("serial")
220            @Test
221            public void testProcessShiftTimesheeetBoundaryCarryoverCase() throws Exception {
222                    // Create the Rule    Sun,   Mon,   Tue,  Wed,   Thu,  Fri,  Sat
223                    boolean[] dayArray = {false, false, true, false, true, true, true};
224                    // Matches HR Job ID #1 (job # 30)
225                    Long jobNumber = 30L;
226                    Long workArea = 0L;
227    
228            DateTimeZone tz = TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback();
229                    this.createShiftDifferentialRule(
230                                    "BWS-CAL",
231                                    "REG",
232                                    "PRM",
233                                    "SD1",
234                                    "SD1",
235                                    "SD1",
236                                    (new DateTime(2010, 8, 31, 22, 0, 0, 0, tz)),
237                                    (new DateTime(2010, 8, 31,  5, 0, 0, 0, tz)),
238                                    new BigDecimal(3), // minHours
239                                    new BigDecimal("0.25"), // maxGap
240                                    dayArray);
241    
242                    // August
243                    Date beginPeriodDate = new Date(new DateTime(2010, 8, 15, 0, 0, 0, 0).getMillis());
244                    Date endPeriodDate = new Date(new DateTime(2010, 9, 1, 0, 0, 0, 0).getMillis());
245                    CalendarEntries endOfAugust = TkServiceLocator.getCalendarEntriesService().getCalendarEntriesByBeginAndEndDate(beginPeriodDate, endPeriodDate);
246                    DateTime start = new DateTime(2010, 8, 31, 22, 0, 0, 0, tz);
247                    List<TimeBlock> blocks = new ArrayList<TimeBlock>();
248                    TimesheetDocument tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", endOfAugust);
249                    Assignment assignment = TkServiceLocator.getAssignmentService().getAssignment("admin", new AssignmentDescriptionKey("30_30_30"), endOfAugust.getBeginPeriodDate());
250                    blocks.addAll(TkTestUtils.createUniformActualTimeBlocks(tdoc, assignment, "RGN", start, 1, new BigDecimal(2), BigDecimal.ZERO));
251                    TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, endOfAugust, TkServiceLocator.getCalendarService().getCalendar(endOfAugust.getHrCalendarId()), true);
252    
253    
254    
255                    tdoc.setTimeBlocks(blocks);
256                    TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
257                    TkTestUtils.verifyAggregateHourSumsFlatList("August Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(2));}},aggregate);
258                    TkServiceLocator.getTimeBlockService().saveTimeBlocks(new ArrayList<TimeBlock>(), aggregate.getFlattenedTimeBlockList(), TKContext.getPrincipalId());
259    
260    
261                    // September
262                    start = new DateTime(2010, 9, 1, 0, 0, 0, 0, tz);
263            java.util.Date septStartDate = new LocalDate(new DateTime(2010,9,1,0,0,0,0)).toDateMidnight().toDate();
264                    CalendarEntries payCalendarEntry = TkServiceLocator.getCalendarService().getCurrentCalendarDates("admin", septStartDate);
265                    tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", payCalendarEntry);
266                    blocks = new ArrayList<TimeBlock>();
267                    blocks.addAll(TkTestUtils.createUniformTimeBlocks(start, 1, new BigDecimal("5"), "RGN", jobNumber, workArea));
268                    aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry, TkServiceLocator.getCalendarService().getCalendar(payCalendarEntry.getHrCalendarId()), true);
269                    TkTestUtils.verifyAggregateHourSumsFlatList("September Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(5));}},aggregate);
270    
271                    // Verify carry over and applied PRM bucket
272                    TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
273                    TkTestUtils.verifyAggregateHourSumsFlatList("September Post-Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(7));put("RGN", new BigDecimal(5));}},aggregate);
274            }
275    
276            @SuppressWarnings("serial")
277            @Test
278            /**
279             * Runs on every day on the interval: [16:00, 24:00)
280             * Max Gap: 15 minutes
281             * Min Hours: 4
282             *
283             * Added some extra time blocks that are not in the shift interval, but
284             * close to the time blocks that are.
285             *
286             * @throws Exception
287             */
288            public void testProcessShiftSimpleNoisyCase() throws Exception {
289                    // Create the Rule
290                    boolean[] dayArray = {true, true, true, true, true, true, true};
291                    // Matches HR Job ID #1 (job # 30)
292                    Long jobNumber = 30L;
293                    Long workArea = 0L;
294                    this.createShiftDifferentialRule(
295                                    "BWS-CAL",
296                                    "REG",
297                                    "PRM",
298                                    "SD1",
299                                    "SD1",
300                                    "SD1",
301                                    (new DateTime(2010, 3, 29, 16, 0, 0, 0, TKUtils.getSystemDateTimeZone())),
302                                    (new DateTime(2010, 3, 30, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone())),
303                                    new BigDecimal(4), // minHours
304                                    new BigDecimal("15"), // maxGap
305                                    dayArray);
306    
307                    // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
308                    DateTime start = new DateTime(2010, 3, 29, 14, 0, 0, 0, TKUtils.getSystemDateTimeZone());
309                    List<TimeBlock> blocks = new ArrayList<TimeBlock>();
310                    CalendarEntries payCalendarEntry = TkServiceLocator.getCalendarService().getCurrentCalendarDates("admin", new Date(start.getMillis()));
311                    blocks.addAll(TkTestUtils.createUniformTimeBlocks(start, 2, new BigDecimal("4"), "RGN", jobNumber, workArea));
312                    blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusHours(4).plusMinutes(15), 2, new BigDecimal("2"), "RGN", jobNumber, workArea));
313                    blocks.addAll(TkTestUtils.createUniformTimeBlocks(new DateTime(2010, 3, 29, 12, 58, 0, 0, TKUtils.getSystemDateTimeZone()), 2, new BigDecimal(1), "RGN", jobNumber, workArea));
314                    TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
315    
316                    // Verify pre-Rule Run
317                    TkTestUtils.verifyAggregateHourSums("Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("RGN", new BigDecimal(14));}},aggregate,2);
318    
319                    // Run Rule
320                    TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(new Date(start.getMillis()));
321                    tdoc.setTimeBlocks(blocks);
322                    TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
323    
324                    // Verify post-Rule Run
325                    TkTestUtils.verifyAggregateHourSums("Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(8));put("RGN", new BigDecimal(14));}},aggregate,2);
326            }
327    
328            @SuppressWarnings("serial")
329            @Test
330            /**
331             * Runs on every day on the interval: [16:00, 24:00)
332             * Max Gap: 15 minutes
333             * Min Hours: 4
334             *
335             * @throws Exception
336             */
337            public void testProcessShiftSimpleCase() throws Exception {
338                    // Create the Rule
339                    boolean[] dayArray = {true, true, true, true, true, true, true};
340                    // Matches HR Job ID #1 (job # 30)
341                    Long jobNumber = 30L;
342                    Long workArea = 0L;
343                    this.createShiftDifferentialRule(
344                                    "BWS-CAL",
345                                    "REG",
346                                    "PRM",
347                                    "SD1",
348                                    "SD1",
349                                    "SD1",
350                                    (new DateTime(2010, 3, 29, 16, 0, 0, 0, TKUtils.getSystemDateTimeZone())),
351                                    (new DateTime(2010, 3, 30, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone())),
352                                    new BigDecimal(4), // minHours
353                                    new BigDecimal("15"), // maxGap
354                                    dayArray);
355    
356                    // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
357                    DateTime start = new DateTime(2010, 3, 29, 14, 0, 0, 0, TKUtils.getSystemDateTimeZone());
358                    List<TimeBlock> blocks = new ArrayList<TimeBlock>();
359                    CalendarEntries payCalendarEntry = TkServiceLocator.getCalendarService().getCurrentCalendarDates("admin", new Date(start.getMillis()));
360                    blocks.addAll(TkTestUtils.createUniformTimeBlocks(start, 2, new BigDecimal("4"), "REG", jobNumber, workArea));
361                    blocks.addAll(TkTestUtils.createUniformTimeBlocks(start.plusHours(4).plusMinutes(15), 2, new BigDecimal("2"), "REG", jobNumber, workArea));
362                    TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
363    
364                    // Verify pre-Rule Run
365                    TkTestUtils.verifyAggregateHourSums("Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("REG", new BigDecimal(12));}},aggregate,2);
366    
367                    // Run Rule
368                    TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(new Date(start.getMillis()));
369                    tdoc.setTimeBlocks(blocks);
370                    TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
371    
372                    // Verify post-Rule Run
373                    TkTestUtils.verifyAggregateHourSums("Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(8));put("REG", new BigDecimal(12));}},aggregate,2);
374            }
375    
376            /**
377             * Stores the Shift Differential Rule in the database for testing.
378             *
379             * dayBooleans[] is a 7 element array of booleans, [0, 6] is [sun, sat]
380             */
381            private void createShiftDifferentialRule(String pyCalendarGroup, String fromEarnGroup, String premiumEarnCode, String location, String payGrade, String hrSalGroup, DateTime startTime, DateTime endTime, BigDecimal minHours, BigDecimal maxGap, boolean dayBooleans[]) {
382                    Assert.assertTrue("Wrong number of day booleans", dayBooleans.length == 7);
383    
384                    ShiftDifferentialRuleService service = TkServiceLocator.getShiftDifferentialRuleService();
385                    ShiftDifferentialRule sdr = new ShiftDifferentialRule();
386    
387                    sdr.setBeginTime(new Time(startTime.getMillis()));
388                    sdr.setEndTime(new Time(endTime.getMillis()));
389                    sdr.setMinHours(minHours);
390                    sdr.setMaxGap(maxGap);
391                    sdr.setActive(true);
392                    sdr.setUserPrincipalId(USER_PRINCIPAL_ID);
393                    sdr.setEffectiveDate(JAN_AS_OF_DATE);
394                    sdr.setLocation(location);
395                    sdr.setPayGrade(payGrade);
396                    sdr.setHrSalGroup(hrSalGroup);
397                    sdr.setFromEarnGroup(fromEarnGroup);
398                    sdr.setPyCalendarGroup(pyCalendarGroup);
399                    sdr.setEarnCode(premiumEarnCode);
400    
401                    for (int i=0; i<dayBooleans.length; i++) {
402                            switch(i) {
403                            case 0:
404                                    sdr.setSunday(dayBooleans[i]);
405                                    break;
406                            case 1:
407                                    sdr.setMonday(dayBooleans[i]);
408                                    break;
409                            case 2:
410                                    sdr.setTuesday(dayBooleans[i]);
411                                    break;
412                            case 3:
413                                    sdr.setWednesday(dayBooleans[i]);
414                                    break;
415                            case 4:
416                                    sdr.setThursday(dayBooleans[i]);
417                                    break;
418                            case 5:
419                                    sdr.setFriday(dayBooleans[i]);
420                                    break;
421                            case 6:
422                                    sdr.setSaturday(dayBooleans[i]);
423                                    break;
424                            }
425                    }
426    
427                    service.saveOrUpdate(sdr);
428    
429                    ShiftDifferentialRule sdrBack = service.getShiftDifferentialRule(sdr.getTkShiftDiffRuleId());
430    
431            DateTimeZone tz = TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback();
432            LocalTime orig_start = new LocalTime(sdr.getBeginTime(), tz);
433                    LocalTime orig_end = new LocalTime(sdr.getEndTime(), tz);
434    
435                    LocalTime stored_start = new LocalTime(sdrBack.getBeginTime(), tz);
436                    LocalTime stored_end = new LocalTime(sdrBack.getEndTime(), tz);
437    
438                    Assert.assertTrue("Start times not equal.", orig_start.equals(stored_start));
439                    Assert.assertTrue("End times not equal.", orig_end.equals(stored_end));
440            }
441    
442    
443        @Ignore
444        @Test
445        /**
446         * Tests WorkSchedules impact on Shift Differential Rule: Simple Case
447         *
448         * Create a timeblock on two days, one day has normal REG shift eligible
449         * hours, one day has HOL time.
450         *
451         * Modified version of the simple case, SDR from 12:00 to 17:00, every day,
452         * must have at least 4 hours with a maximum 15 minute gap.
453         *
454         */
455        public void simpleCaseWithWorkSchedule() throws Exception {
456                    // Create the Rule
457                    boolean[] dayArray = {true, true, true, true, true, true, true};
458                    // Matches HR Job ID #1 (job # 30)
459                    Long jobNumber = 30L;
460                    Long workArea = 0L;
461                    this.createShiftDifferentialRule(
462                                    "BWS-CAL",
463                                    "REG",
464                                    "PRM",
465                                    "SD1",
466                                    "SD1",
467                                    "SD1",
468                                    (new DateTime(2010, 3, 29, 12, 0, 0, 0, TKUtils.getSystemDateTimeZone())),
469                                    (new DateTime(2010, 3, 29, 17, 0, 0, 0, TKUtils.getSystemDateTimeZone())),
470                                    new BigDecimal(4), // minHours
471                                    new BigDecimal("0.25"), // maxGap
472                                    dayArray);
473    
474                    // Create Time Blocks (2 days, 2 blocks on each day, 15 minute gap between blocks, 4 hours total each.
475                    DateTime start = new DateTime(2010, 3, 29, 12, 0, 0, 0, TKUtils.getSystemDateTimeZone());
476            DateTime holtime = new DateTime(2010, 3, 30, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone());
477                    List<TimeBlock> blocks = new ArrayList<TimeBlock>();
478                    CalendarEntries payCalendarEntry = TkServiceLocator.getCalendarService().getCurrentCalendarDates("admin", new Date(start.getMillis()));
479                    blocks.addAll(TkTestUtils.createUniformTimeBlocks(start,   1, new BigDecimal("4"), "REG", jobNumber, workArea));
480            blocks.addAll(TkTestUtils.createUniformTimeBlocks(holtime, 1, new BigDecimal("4"), "HOL", jobNumber, workArea));
481    
482                    TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(blocks, payCalendarEntry);
483    
484                    // Verify pre-Rule Run
485                    TkTestUtils.verifyAggregateHourSums("Pre-Check", new HashMap<String,BigDecimal>() {{put("PRM", BigDecimal.ZERO);put("REG", new BigDecimal(4));put("HOL", new BigDecimal(4));}},aggregate,2);
486    
487                    // Run Rule
488                    TimesheetDocument tdoc = TkTestUtils.populateBlankTimesheetDocument(new Date(start.getMillis()));
489                    tdoc.setTimeBlocks(blocks);
490                    TkServiceLocator.getShiftDifferentialRuleService().processShiftDifferentialRules(tdoc, aggregate);
491    
492                    // Verify post-Rule Run
493                    TkTestUtils.verifyAggregateHourSums("Post Rules Check", new HashMap<String,BigDecimal>() {{put("PRM", new BigDecimal(8));put("REG", new BigDecimal(4));}},aggregate,2);
494    
495        }
496    
497        /**
498         * Creates a new Work Schedule and Assignment work schedule setup for the
499         * 'admin' user.
500         *
501         *  8a - 5p work schedule
502         *
503         * @param workSch
504         */
505        public void createWorkSchedule(Long workSch) {
506            // Create a Work Schedule Assignment
507            //
508            DateTimeZone tz = TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback();
509            WorkScheduleAssignment workScheduleAssignment = new WorkScheduleAssignment();
510            workScheduleAssignment.setHrWorkSchedule(workSch);
511            workScheduleAssignment.setDept("%");
512            workScheduleAssignment.setWorkArea(-1L);
513            workScheduleAssignment.setPrincipalId("admin");
514            workScheduleAssignment.setEffectiveDate(JAN_AS_OF_DATE);
515            workScheduleAssignment.setActive(true);
516            workScheduleAssignment.setUserPrincipalId("admin");
517    
518            // Create a Work Schedule
519            //
520            WorkSchedule workSchedule = new WorkSchedule();
521            workSchedule.setHrWorkSchedule(workSch); // we can set this to whatever, it's not a row ID.
522            workSchedule.setActive(true);
523            workSchedule.setEarnGroup("WS1"); // Test data should have an earn group for WS1
524            workSchedule.setWorkScheduleDesc("desc");
525            workSchedule.setEffectiveDate(JAN_AS_OF_DATE);
526            workSchedule.setUserPrincipalId("admin");
527    
528            // Create the actual schedule entries.
529            //
530            List<WorkScheduleEntry> workScheduleEntries = new ArrayList<WorkScheduleEntry>();
531    
532            WorkScheduleEntry workScheduleEntry = new WorkScheduleEntry();
533            workScheduleEntry.setBeginTime(new Time((new DateTime(2010, 3, 1, 8, 0, 0, 0, tz)).getMillis()));
534            workScheduleEntry.setEndTime(new Time((new DateTime(2010, 3, 1, 17, 0, 0, 0, tz)).getMillis()));
535            workScheduleEntry.setIndexOfDay(0L);
536            workScheduleEntries.add(workScheduleEntry);
537            workSchedule.setWorkScheduleEntries(workScheduleEntries);
538    
539            // Save Work Schedule, Work Schedule Assignment
540            TkServiceLocator.getWorkScheduleService().saveOrUpdate(workSchedule);
541            TkServiceLocator.getWorkScheduleAssignmentService().saveOrUpdate(workScheduleAssignment);
542        }
543    
544    }