View Javadoc

1   /**
2    * Copyright 2004-2012 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.hr.time.overtime.weekly.rule.service;
17  
18  import java.math.BigDecimal;
19  import java.sql.Date;
20  import java.util.ArrayList;
21  import java.util.HashMap;
22  import java.util.List;
23  
24  import org.joda.time.DateTime;
25  import org.junit.Assert;
26  import org.junit.Test;
27  import org.kuali.hr.test.KPMETestCase;
28  import org.kuali.hr.time.calendar.CalendarEntries;
29  import org.kuali.hr.time.overtime.weekly.rule.WeeklyOvertimeRule;
30  import org.kuali.hr.time.service.base.TkServiceLocator;
31  import org.kuali.hr.time.test.TkTestUtils;
32  import org.kuali.hr.time.timeblock.TimeBlock;
33  import org.kuali.hr.time.timesheet.TimesheetDocument;
34  import org.kuali.hr.time.util.TKUtils;
35  import org.kuali.hr.time.util.TkTimeBlockAggregate;
36  import org.kuali.rice.krad.service.KRADServiceLocator;
37  
38  /**
39   * 
40   * @author djunk
41   *
42   */
43  public class WeeklyOvertimeRuleServiceTest extends KPMETestCase {
44  	
45  	private static Date DEFAULT_EFFDT = new Date((new DateTime(2010, 1, 1, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone())).getMillis());
46  	private static Long DEFAULT_JOB_NUMBER = 30L;
47  	private static Long DEFAULT_WORK_AREA = 30L;
48  	
49  	@SuppressWarnings("serial")
50  	@Test
51  	/**
52  	 * This test should create 10 hours of OVT and leave 40 hours of REG remaining.
53  	 * It operates WITHIN a standard week.
54  	 * 
55  	 */
56  	public void testProcessSimpleStandardWeek() throws Exception {
57  		List<TimeBlock> timeBlocks = new ArrayList<TimeBlock>();
58  		DateTime start = new DateTime(2010, 1, 4, 5, 0, 0, 0, TKUtils.getSystemDateTimeZone());
59  		timeBlocks = TkTestUtils.createUniformTimeBlocks(start, 5, BigDecimal.TEN, "REG", DEFAULT_JOB_NUMBER, DEFAULT_WORK_AREA);
60  		CalendarEntries payCalendarEntry = TkServiceLocator.getCalendarService().getCurrentCalendarDates("admin", DEFAULT_EFFDT);
61  
62  		// Check our initial data.
63  		TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(timeBlocks, payCalendarEntry);
64  		TkTestUtils.verifyAggregateHourSums(new HashMap<String,BigDecimal>() {{put("OVT", BigDecimal.ZERO);put("REG", new BigDecimal(50));}},aggregate,1);
65  		
66  		// Create the rule.
67  		this.setupWeeklyOvertimeRule("REG", "OVT", "REG", 1, new BigDecimal(40), DEFAULT_EFFDT);		
68  		TimesheetDocument timesheetDocument = TkTestUtils.populateBlankTimesheetDocument(DEFAULT_EFFDT);
69  		timesheetDocument.setTimeBlocks(timeBlocks);
70  		TkServiceLocator.getWeeklyOvertimeRuleService().processWeeklyOvertimeRule(timesheetDocument, aggregate);
71  		
72  		// Check the rule for OVT applied data.
73  		TkTestUtils.verifyAggregateHourSums(new HashMap<String,BigDecimal>() {{put("OVT", BigDecimal.TEN);put("REG", new BigDecimal(40));}},aggregate,1);
74  	}
75  	
76  	@SuppressWarnings("serial")
77  	@Test
78  	/**
79  	 * OVT Hit on Current Month
80  	 * 
81  	 * This test should create 10 hours of OVT on the first FLSA week of the
82  	 * current pay period.  The first FLSA week has 20 REG hours, the previous
83  	 * pay period was a partial week and contained 30 REG hours.
84  	 * 
85  	 * march 29-31; april 1-4
86  	 *  
87  	 *  |--------+--------+--------+----+-------+-------|
88  	 *  |   29th |   30th |   31st | xx |   1st |   2nd |
89  	 *  |--------+--------+--------+----+-------+-------|
90  	 *  |     10 |     10 |     10 | xx |    10 |    10 |
91  	 *  |--------+--------+--------+----+-------+-------|
92  	 * 
93  	 *  4/1/2010 starts on a Thursday
94  	 */
95  	public void testProcessPreviousMonthFlsaBoundary() throws Exception {
96  		// March end time blocks: 3/29-3-31 [m, w]
97  		List<TimeBlock> timeBlocks = new ArrayList<TimeBlock>();
98  		DateTime start = new DateTime(2010, 3, 29, 5, 0, 0, 0, TKUtils.getSystemDateTimeZone());
99  		Date beginPeriodDate = new Date(new DateTime(2010, 3, 15, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone()).getMillis());
100 		Date endPeriodDate = new Date(new DateTime(2010, 4, 1, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone()).getMillis());
101 		CalendarEntries endOfMarch = TkServiceLocator.getCalendarEntriesService().getCalendarEntriesByBeginAndEndDate(beginPeriodDate, endPeriodDate);
102 		TimesheetDocument tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", endOfMarch);
103 		Assert.assertTrue("No Assignments Found.", tdoc.getAssignments().size() > 0);
104 		timeBlocks = TkTestUtils.createUniformActualTimeBlocks(tdoc, tdoc.getAssignments().get(0), "RGN", start, 3, BigDecimal.TEN, BigDecimal.ZERO);
105 		TkServiceLocator.getTimeBlockService().saveTimeBlocks(new ArrayList<TimeBlock>(), timeBlocks);
106 		tdoc.setTimeBlocks(timeBlocks);
107 		
108 		// Verify previous calendar times
109 		CalendarEntries payCalendarEntry = TkServiceLocator.getCalendarService().getCurrentCalendarDates("admin", new Date(start.getMillis()));
110 		TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(timeBlocks, payCalendarEntry);
111 		TkTestUtils.verifyAggregateHourSums("Prior month", new HashMap<String,BigDecimal>() {{put("OVT", BigDecimal.ZERO);put("RGN", new BigDecimal(30));}},aggregate,2);
112 				
113 		
114 		// April time blocks & document
115 		start = new DateTime(2010, 4, 1, 5, 0, 0, 0, TKUtils.getSystemDateTimeZone());
116 		timeBlocks = TkTestUtils.createUniformTimeBlocks(start, 2, BigDecimal.TEN, "REG", DEFAULT_JOB_NUMBER, DEFAULT_WORK_AREA);
117 		payCalendarEntry = TkServiceLocator.getCalendarService().getCurrentCalendarDates("admin", new Date(start.getMillis()));
118 		aggregate = new TkTimeBlockAggregate(timeBlocks, payCalendarEntry);
119 		TkTestUtils.verifyAggregateHourSums("Pre-Rules verification", new HashMap<String,BigDecimal>() {{put("OVT", BigDecimal.ZERO);put("REG", new BigDecimal(20));}},aggregate,0);
120 		TimesheetDocument timesheetDocument = TkTestUtils.populateBlankTimesheetDocument(new Date(start.getMillis()));
121 		timesheetDocument.setTimeBlocks(timeBlocks);
122 		
123 		// Create Rule
124 		this.setupWeeklyOvertimeRule("REG", "OVT", "REG", 1, new BigDecimal(40), DEFAULT_EFFDT);		
125 
126 		// Apply
127 		TkServiceLocator.getWeeklyOvertimeRuleService().processWeeklyOvertimeRule(timesheetDocument, aggregate);		
128 		
129 		// Verify
130 		TkTestUtils.verifyAggregateHourSums("Overtime processed", new HashMap<String,BigDecimal>() {{put("OVT", BigDecimal.TEN);put("REG", new BigDecimal(10));}},aggregate,0);
131 	}
132 	
133 	
134 	@SuppressWarnings("serial")
135 	@Test
136 	/**
137 	 * OVT hit on previous month.
138 	 * 
139 	 * |------+------+------+------+----+-----+-----|
140 	 * | 27th | 28th | 29th | 30th | xx | 1st | 2nd |
141 	 * |------+------+------+------+----+-----+-----|
142 	 * |   11 |   11 |   11 |   11 | xx |  11 |  11 |
143 	 * |------+------+------+------+----+-----+-----|
144 	 */
145 	public void testProcessPreviousMonthFlsaOT() throws Exception {
146 		List<TimeBlock> timeBlocks = new ArrayList<TimeBlock>();
147 		DateTime start = new DateTime(2010, 6, 27, 5, 0, 0, 0, TKUtils.getSystemDateTimeZone());
148 		Date beginPeriodDate = new Date(new DateTime(2010, 6, 15, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone()).getMillis());
149 		Date endPeriodDate = new Date(new DateTime(2010, 7, 1, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone()).getMillis());
150 		CalendarEntries endOfJune = TkServiceLocator.getCalendarEntriesService().getCalendarEntriesByBeginAndEndDate(beginPeriodDate, endPeriodDate);
151 		TimesheetDocument tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", endOfJune);
152 		Assert.assertTrue("No Assignments Found.", tdoc.getAssignments().size() > 0);
153 		timeBlocks = TkTestUtils.createUniformActualTimeBlocks(tdoc, tdoc.getAssignments().get(0), "RGN", start, 4, new BigDecimal(11), BigDecimal.ZERO);
154 		
155 		tdoc.setTimeBlocks(timeBlocks);
156 		
157 		// Create Rule
158 		this.setupWeeklyOvertimeRule("REG", "OVT", "REG", 1, new BigDecimal(40), DEFAULT_EFFDT);
159 
160 
161 		// Verify previous calendar times
162 		CalendarEntries payCalendarEntry = TkServiceLocator.getCalendarService().getCurrentCalendarDates("admin", new Date(start.getMillis()));
163 		
164 		TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(timeBlocks, payCalendarEntry);
165 		// Create and Process Previous month to have totals set up correctly
166 		TkServiceLocator.getWeeklyOvertimeRuleService().processWeeklyOvertimeRule(tdoc, aggregate);
167 		TkTestUtils.verifyAggregateHourSums("Prior month", new HashMap<String,BigDecimal>() {{put("OVT", new BigDecimal(4));put("RGN", new BigDecimal(40));}},aggregate,2);
168 		TkServiceLocator.getTimeBlockService().saveTimeBlocks(new ArrayList<TimeBlock>(), aggregate.getFlattenedTimeBlockList());
169 		
170 		// April time blocks & document
171 		start = new DateTime(2010, 7, 1, 5, 0, 0, 0, TKUtils.getSystemDateTimeZone());
172 		timeBlocks = TkTestUtils.createUniformTimeBlocks(start, 2, new BigDecimal(11), "RGN", DEFAULT_JOB_NUMBER, DEFAULT_WORK_AREA);
173 		payCalendarEntry = TkServiceLocator.getCalendarService().getCurrentCalendarDates("admin", new Date(start.getMillis()));
174 		aggregate = new TkTimeBlockAggregate(timeBlocks, payCalendarEntry);
175 		TkTestUtils.verifyAggregateHourSums("Pre-Rules verification", new HashMap<String,BigDecimal>() {{put("OVT", BigDecimal.ZERO);put("RGN", new BigDecimal(22));}},aggregate,0);
176 		TimesheetDocument timesheetDocument = TkTestUtils.populateBlankTimesheetDocument(new Date(start.getMillis()));
177 		timesheetDocument.setTimeBlocks(timeBlocks);		
178 
179 		// Apply
180 		TkServiceLocator.getWeeklyOvertimeRuleService().processWeeklyOvertimeRule(timesheetDocument, aggregate);		
181 		
182 		// Verify
183 		TkTestUtils.verifyAggregateHourSums("Overtime processed", new HashMap<String,BigDecimal>() {{put("OVT", new BigDecimal(22));put("RGN", BigDecimal.ZERO);}},aggregate,0);
184 	}
185 	
186 	@SuppressWarnings("serial")
187 	@Test
188 	/**
189 	 * Three step OVT Test
190 	 * 
191 	 * |------+------+------+------+----+-----+-----|
192 	 * | 27th | 28th | 29th | 30th | xx | 1st | 2nd |
193 	 * |------+------+------+------+----+-----+-----|
194 	 * |  ABC | XYZ  | ABC  |  XYZ | xx | ABC | REG | 
195 	 * |------+------+------+------+----+-----+-----|
196 	 * |   11 |   11 |   11 |   11 | xx |  11 |  11 |
197 	 * |------+------+------+------+----+-----+-----|
198 	 * 
199 	 * Contrived example using 3 steps that convert strangely coded hours to 
200 	 * REG via a multi-step process.  Eventually REG is converted to OVT using
201 	 * normal rules.
202 	 * 
203 	 * Step 1: g:SD3:[XYZ] to [ABC]
204 	 * Step 2: g:SD2:[ABC] to [REG]
205 	 * Step 3: g:REG:[REG] to [OVT]
206 	 * 
207 	 * XYZ -> ABC -> REG -> OVT
208 	 * 
209 	 * 27-30th:
210 	 * ABC: 1    
211 	 * XYZ: 1    
212 	 * REG: 40  
213 	 * OVT: 2    
214 	 * 
215 	 * 1st-2nd:
216 	 * ABC: 0    
217 	 * REG: 0    
218 	 * OVT: 22 
219 	 */
220 	public void testProcessThreeStepOvtRule() throws Exception {
221 		this.setupWeeklyOvertimeRule("REG", "OVT", "REG", 3, new BigDecimal(40), DEFAULT_EFFDT);
222 		this.setupWeeklyOvertimeRule("SD2", "RGN", "SD2", 2, new BigDecimal(1), DEFAULT_EFFDT);
223 		this.setupWeeklyOvertimeRule("SD3", "ABC", "SD3", 1, new BigDecimal(1), DEFAULT_EFFDT);
224 
225 		
226 		List<TimeBlock> timeBlocks = new ArrayList<TimeBlock>();
227 		DateTime start = new DateTime(2010, 6, 27, 5, 0, 0, 0, TKUtils.getSystemDateTimeZone());
228 		Date beginPeriodDate = new Date(new DateTime(2010, 6, 15, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone()).getMillis());
229 		Date endPeriodDate = new Date(new DateTime(2010, 7, 1, 0, 0, 0, 0, TKUtils.getSystemDateTimeZone()).getMillis());
230 		CalendarEntries endOfJune = TkServiceLocator.getCalendarEntriesService().getCalendarEntriesByBeginAndEndDate(beginPeriodDate, endPeriodDate);
231 		TimesheetDocument tdoc = TkServiceLocator.getTimesheetService().openTimesheetDocument("admin", endOfJune);
232 		Assert.assertTrue("No Assignments Found.", tdoc.getAssignments().size() > 0);
233 		
234 		
235 		timeBlocks.addAll(TkTestUtils.createUniformActualTimeBlocks(tdoc, tdoc.getAssignments().get(0), "ABC", start, 1, new BigDecimal(11), BigDecimal.ZERO));
236 		timeBlocks.addAll(TkTestUtils.createUniformActualTimeBlocks(tdoc, tdoc.getAssignments().get(0), "XYZ", start.plusDays(1), 1, new BigDecimal(11), BigDecimal.ZERO));
237 		timeBlocks.addAll(TkTestUtils.createUniformActualTimeBlocks(tdoc, tdoc.getAssignments().get(0), "ABC", start.plusDays(2), 1, new BigDecimal(11), BigDecimal.ZERO));
238 		timeBlocks.addAll(TkTestUtils.createUniformActualTimeBlocks(tdoc, tdoc.getAssignments().get(0), "XYZ", start.plusDays(3), 1, new BigDecimal(11), BigDecimal.ZERO));		
239 		tdoc.setTimeBlocks(timeBlocks);
240 		
241 
242 
243 		// Verify previous calendar times
244 		CalendarEntries payCalendarEntry = TkServiceLocator.getCalendarService().getCurrentCalendarDates("admin", new Date(start.getMillis()));
245 		TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(timeBlocks, payCalendarEntry);
246 		// Create and Process Previous month to have totals set up correctly
247 		TkServiceLocator.getWeeklyOvertimeRuleService().processWeeklyOvertimeRule(tdoc, aggregate);
248 		TkTestUtils.verifyAggregateHourSums("Prior month", new HashMap<String,BigDecimal>() {{put("OVT", new BigDecimal(2));put("RGN", new BigDecimal(40));put("ABC", new BigDecimal(1));put("XYZ", new BigDecimal(1));}},aggregate,2);
249 		TkServiceLocator.getTimeBlockService().saveTimeBlocks(new ArrayList<TimeBlock>(), aggregate.getFlattenedTimeBlockList());
250 		
251 		// April time blocks & document
252 		start = new DateTime(2010, 7, 1, 5, 0, 0, 0, TKUtils.getSystemDateTimeZone());
253 		timeBlocks = TkTestUtils.createUniformTimeBlocks(start, 2, new BigDecimal(11), "REG", DEFAULT_JOB_NUMBER, DEFAULT_WORK_AREA);
254 		payCalendarEntry = TkServiceLocator.getCalendarService().getCurrentCalendarDates("admin", new Date(start.getMillis()));
255 		aggregate = new TkTimeBlockAggregate(timeBlocks, payCalendarEntry);
256 		TkTestUtils.verifyAggregateHourSums("Pre-Rules verification", new HashMap<String,BigDecimal>() {{put("OVT", BigDecimal.ZERO);put("REG", new BigDecimal(22));}},aggregate,0);
257 		TimesheetDocument timesheetDocument = TkTestUtils.populateBlankTimesheetDocument(new Date(start.getMillis()));
258 		timesheetDocument.setTimeBlocks(timeBlocks);		
259 
260 		// Apply
261 		TkServiceLocator.getWeeklyOvertimeRuleService().processWeeklyOvertimeRule(timesheetDocument, aggregate);		
262 		
263 		// Verify
264 		TkTestUtils.verifyAggregateHourSums("Overtime processed", new HashMap<String,BigDecimal>() {{put("ABC", BigDecimal.ZERO);put("OVT", new BigDecimal(22));put("REG", BigDecimal.ZERO);}},aggregate,0);		
265 	}
266 	
267 	/**
268 	 * Helper method that creates a weekly overtime rule.
269 	 */
270 	private WeeklyOvertimeRule setupWeeklyOvertimeRule(String fromEarnGroup, String toEarnCode, String maxHoursEarnGroup, int step, BigDecimal maxHours, Date effectiveDate){
271 		WeeklyOvertimeRule weeklyOvertimeRule = new WeeklyOvertimeRule();
272 		weeklyOvertimeRule.setActive(true);
273 		weeklyOvertimeRule.setConvertFromEarnGroup(fromEarnGroup);
274 		weeklyOvertimeRule.setConvertToEarnCode(toEarnCode);
275 		weeklyOvertimeRule.setMaxHoursEarnGroup(maxHoursEarnGroup);
276 		weeklyOvertimeRule.setStep(new BigDecimal(step));
277 		weeklyOvertimeRule.setMaxHours(maxHours);
278 		weeklyOvertimeRule.setEffectiveDate(effectiveDate);
279 		
280 		weeklyOvertimeRule.setTkWeeklyOvertimeRuleGroupId(1L);
281 		
282 		TkServiceLocator.getWeeklyOvertimeRuleService().saveOrUpdate(weeklyOvertimeRule);
283 		return weeklyOvertimeRule;
284 	}
285 
286 	@Override
287 	public void tearDown() throws Exception {
288 		KRADServiceLocator.getBusinessObjectService().deleteMatching(WeeklyOvertimeRule.class, new HashMap());
289 		super.tearDown();
290 	}
291 		
292 }