View Javadoc

1   /**
2    * Copyright 2004-2013 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.kpme.tklm.time.util;
17  
18  import java.math.BigDecimal;
19  import java.sql.Time;
20  import java.sql.Timestamp;
21  import java.util.ArrayList;
22  import java.util.Collections;
23  import java.util.Comparator;
24  import java.util.List;
25  import java.util.ListIterator;
26  
27  import org.apache.commons.collections.CollectionUtils;
28  import org.joda.time.DateTime;
29  import org.joda.time.DateTimeConstants;
30  import org.joda.time.DateTimeZone;
31  import org.joda.time.Interval;
32  import org.joda.time.LocalDateTime;
33  import org.joda.time.LocalTime;
34  import org.kuali.kpme.core.assignment.Assignment;
35  import org.kuali.kpme.core.calendar.Calendar;
36  import org.kuali.kpme.core.calendar.entry.CalendarEntry;
37  import org.kuali.kpme.core.earncode.EarnCode;
38  import org.kuali.kpme.core.service.HrServiceLocator;
39  import org.kuali.kpme.core.util.TKUtils;
40  import org.kuali.kpme.tklm.api.time.util.TkTimeBlockAggregateContract;
41  import org.kuali.kpme.tklm.leave.block.LeaveBlock;
42  import org.kuali.kpme.tklm.leave.block.LeaveBlockAggregate;
43  import org.kuali.kpme.tklm.leave.service.LmServiceLocator;
44  import org.kuali.kpme.tklm.time.flsa.FlsaDay;
45  import org.kuali.kpme.tklm.time.flsa.FlsaWeek;
46  import org.kuali.kpme.tklm.time.service.TkServiceLocator;
47  import org.kuali.kpme.tklm.time.timeblock.TimeBlock;
48  import org.kuali.kpme.tklm.time.timehourdetail.TimeHourDetail;
49  import org.kuali.kpme.tklm.time.timesheet.TimesheetDocument;
50  import org.kuali.kpme.tklm.time.workflow.TimesheetDocumentHeader;
51  
52  public class TkTimeBlockAggregate implements TkTimeBlockAggregateContract {
53  	private List<List<TimeBlock>> dayTimeBlockList = new ArrayList<List<TimeBlock>>();
54  	private List<List<LeaveBlock>> dayLeaveBlockList = new ArrayList<List<LeaveBlock>>();
55  	private CalendarEntry payCalendarEntry;
56  	private Calendar payCalendar;
57  
58      /**
59       * Defaults to using SYSTEM time zone.
60       *
61       * @param timeBlocks
62       * @param payCalendarEntry
63       */
64  	public TkTimeBlockAggregate(List<TimeBlock> timeBlocks, CalendarEntry payCalendarEntry){
65  		this(timeBlocks, payCalendarEntry, HrServiceLocator.getCalendarService().getCalendar(payCalendarEntry.getHrCalendarId()));
66  	}
67  
68      /**
69       * Defaults to using SYSTEM time zone.
70       *
71       * @param timeBlocks
72       * @param payCalendarEntry
73       * @param payCalendar
74       */
75  	public TkTimeBlockAggregate(List<TimeBlock> timeBlocks, CalendarEntry payCalendarEntry, Calendar payCalendar) {
76          this(timeBlocks, payCalendarEntry, payCalendar, false);
77      }
78  
79      /**
80       * Provides the option to refer to the time zone adjusted time for the current
81       * user.
82       * @param timeBlocks
83       * @param payCalendarEntry
84       * @param payCalendar
85       * @param useUserTimeZone
86       */
87      public TkTimeBlockAggregate(List<TimeBlock> timeBlocks, CalendarEntry payCalendarEntry, Calendar payCalendar, boolean useUserTimeZone) {
88          this(timeBlocks, payCalendarEntry, payCalendar, useUserTimeZone, 
89          		useUserTimeZone ? TKUtils.getDaySpanForCalendarEntry(payCalendarEntry) : TKUtils.getDaySpanForCalendarEntry(payCalendarEntry, TKUtils.getSystemDateTimeZone()));
90  	}
91  
92      public TkTimeBlockAggregate(List<TimeBlock> timeBlocks, CalendarEntry payCalendarEntry, Calendar payCalendar, boolean useUserTimeZone, List<Interval> dayIntervals) {
93      	this.payCalendarEntry = payCalendarEntry;
94  		this.payCalendar = payCalendar;
95  		
96  		for(Interval dayInt : dayIntervals){
97  			List<TimeBlock> dayTimeBlocks = new ArrayList<TimeBlock>();
98  			for(TimeBlock timeBlock : timeBlocks){
99  
100                 // Assumption: Timezones can only be switched at pay period end boundaries.
101                 // If the above assumption becomes false, the logic below will need to
102                 // accommodate virtual chopping of time blocks to have them fit nicely
103                 // in the "days" that are displayed to users.
104 
105 				DateTime beginTime = useUserTimeZone ? timeBlock.getBeginTimeDisplay() : timeBlock.getBeginDateTime().withZone(TKUtils.getSystemDateTimeZone());
106 				DateTime endTime = useUserTimeZone ? timeBlock.getEndTimeDisplay() :  timeBlock.getEndDateTime().withZone(TKUtils.getSystemDateTimeZone());
107 				if(dayInt.contains(beginTime)){
108 					if(dayInt.contains(endTime) || endTime.compareTo(dayInt.getEnd()) == 0){
109 						// determine if the time block needs to be pushed forward / backward
110 						if(beginTime.getHourOfDay() < dayInt.getStart().getHourOfDay()) {
111 							timeBlock.setPushBackward(true);
112 						}
113 
114 						dayTimeBlocks.add(timeBlock);
115 					}
116 				}
117 			}
118 			dayTimeBlockList.add(dayTimeBlocks);
119 		}
120 	}
121     
122     /**
123      * Defaults to using SYSTEM time zone.
124      *
125      * @param timeBlocks
126      * @param leaveBlocks
127      * @param payCalendarEntry
128      */
129 	public TkTimeBlockAggregate(List<TimeBlock> timeBlocks, List<LeaveBlock> leaveBlocks, CalendarEntry payCalendarEntry){
130 		this(timeBlocks, leaveBlocks, payCalendarEntry, HrServiceLocator.getCalendarService().getCalendar(payCalendarEntry.getHrCalendarId()));
131 	}
132 
133     /**
134      * Defaults to using SYSTEM time zone.
135      *
136      * @param timeBlocks
137      * @param leaveBlocks
138      * @param payCalendarEntry
139      * @param payCalendar
140      */
141 	public TkTimeBlockAggregate(List<TimeBlock> timeBlocks, List<LeaveBlock> leaveBlocks, CalendarEntry payCalendarEntry, Calendar payCalendar) {
142         this(timeBlocks, leaveBlocks, payCalendarEntry, payCalendar, false);
143     }
144 
145     /**
146      * Provides the option to refer to the time zone adjusted time for the current
147      * user.
148      * @param timeBlocks
149      * @param payCalendarEntry
150      * @param payCalendar
151      * @param useUserTimeZone
152      */
153     public TkTimeBlockAggregate(List<TimeBlock> timeBlocks, List<LeaveBlock> leaveBlocks, CalendarEntry payCalendarEntry, Calendar payCalendar, boolean useUserTimeZone) {
154         this(timeBlocks, leaveBlocks, payCalendarEntry, payCalendar, useUserTimeZone, 
155         		useUserTimeZone ? TKUtils.getDaySpanForCalendarEntry(payCalendarEntry) : TKUtils.getDaySpanForCalendarEntry(payCalendarEntry, TKUtils.getSystemDateTimeZone()));
156 	}
157 
158     public TkTimeBlockAggregate(List<TimeBlock> timeBlocks, List<LeaveBlock> leaveBlocks, CalendarEntry payCalendarEntry, Calendar payCalendar, boolean useUserTimeZone, List<Interval> dayIntervals) {
159     	this.payCalendarEntry = payCalendarEntry;
160 		this.payCalendar = payCalendar;
161 
162 		for(Interval dayInt : dayIntervals){
163 			List<TimeBlock> dayTimeBlocks = new ArrayList<TimeBlock>();
164 			for(TimeBlock timeBlock : timeBlocks){
165 
166                 // Assumption: Timezones can only be switched at pay period end boundaries.
167                 // If the above assumption becomes false, the logic below will need to
168                 // accommodate virtual chopping of time blocks to have them fit nicely
169                 // in the "days" that are displayed to users.
170 
171 				DateTime beginTime = useUserTimeZone ? timeBlock.getBeginTimeDisplay() : new DateTime(timeBlock.getBeginTimestamp(), TKUtils.getSystemDateTimeZone());
172 				DateTime endTime = useUserTimeZone ? timeBlock.getEndTimeDisplay() :  new DateTime(timeBlock.getEndTimestamp(), TKUtils.getSystemDateTimeZone());
173 				if(dayInt.contains(beginTime)){
174 					if(dayInt.contains(endTime) || endTime.compareTo(dayInt.getEnd()) == 0){
175 						// determine if the time block needs to be pushed forward / backward
176 						if(beginTime.getHourOfDay() < dayInt.getStart().getHourOfDay()) {
177 							timeBlock.setPushBackward(true);
178 						}
179 
180 						dayTimeBlocks.add(timeBlock);
181 					}
182 				}
183 			}
184 			dayTimeBlockList.add(dayTimeBlocks);
185 
186             List<LeaveBlock> dayLeaveBlocks = new ArrayList<LeaveBlock>();
187 			for(LeaveBlock leaveBlock : leaveBlocks){
188 
189                 // Assumption: Timezones can only be switched at pay period end boundaries.
190                 // If the above assumption becomes false, the logic below will need to
191                 // accommodate virtual chopping of leave blocks to have them fit nicely
192                 // in the "days" that are displayed to users.
193 				
194 				DateTimeZone dateTimeZone = HrServiceLocator.getTimezoneService().getUserTimezoneWithFallback();
195 				DateTime beginTime = new DateTime(leaveBlock.getLeaveDate(), useUserTimeZone ? dateTimeZone : TKUtils.getSystemDateTimeZone());
196 				DateTime endTime = new DateTime(leaveBlock.getLeaveDate(), useUserTimeZone ? dateTimeZone : TKUtils.getSystemDateTimeZone());
197 				if(dayInt.contains(beginTime)){
198 					if(dayInt.contains(endTime) || endTime.compareTo(dayInt.getEnd()) == 0){
199 						dayLeaveBlocks.add(leaveBlock);
200 					}
201 				}
202 			}
203 			dayLeaveBlockList.add(dayLeaveBlocks);
204 		}
205     }
206     
207 	public List<TimeBlock> getFlattenedTimeBlockList(){
208 		List<TimeBlock> lstTimeBlocks = new ArrayList<TimeBlock>();
209 		for(List<TimeBlock> timeBlocks : dayTimeBlockList){
210 			lstTimeBlocks.addAll(timeBlocks);
211 		}
212 
213 		Collections.sort(lstTimeBlocks, new Comparator<TimeBlock>() { // Sort the Time Blocks
214 			public int compare(TimeBlock tb1, TimeBlock tb2) {
215 				if (tb1 != null && tb2 != null)
216 					return tb1.getBeginTimestamp().compareTo(tb2.getBeginTimestamp());
217 				return 0;
218 			}
219 		});
220 
221 		return lstTimeBlocks;
222 	}
223 	
224 	public List<LeaveBlock> getFlattenedLeaveBlockList(){
225 		List<LeaveBlock> lstLeaveBlocks = new ArrayList<LeaveBlock>();
226 		for(List<LeaveBlock> leaveBlocks : dayLeaveBlockList){
227 			lstLeaveBlocks.addAll(leaveBlocks);
228 		}
229 
230 		Collections.sort(lstLeaveBlocks, new Comparator<LeaveBlock>() { // Sort the Leave Blocks
231 			public int compare(LeaveBlock lb1, LeaveBlock lb2) {
232 				if (lb1 != null && lb2 != null)
233 					return lb1.getLeaveDate().compareTo(lb2.getLeaveDate());
234 				return 0;
235 			}
236 		});
237 
238 		return lstLeaveBlocks;
239 	}
240 
241 	/**
242 	 * Provides a way to access all of the time blocks for a given week.
243 	 *
244 	 * Outer list is 0 indexed list representing days in a week.
245 	 * Inner List are all of the time blocks for that day.
246 	 *
247 	 * Ex.
248 	 *
249 	 * List<List<TimeBlock>> week0 = getWeekTimeBlocks(0);
250 	 * List<TimeBlock> day0 = week0.get(0);
251 	 *
252 	 * @param week
253 	 * @return
254 	 */
255 	public List<List<TimeBlock>> getWeekTimeBlocks(int week){
256 		int startIndex = week*7;
257 		int endIndex = (week*7)+7;
258 		endIndex = endIndex > dayTimeBlockList.size() ? dayTimeBlockList.size() : endIndex;
259 
260         // Need to sort each day by clock time.
261         List<List<TimeBlock>> wList = dayTimeBlockList.subList(startIndex, endIndex);
262         for (List<TimeBlock> dList : wList) {
263             Collections.sort(dList, new Comparator<TimeBlock>() { // Sort the Time Blocks
264                 public int compare(TimeBlock tb1, TimeBlock tb2) {
265                     if (tb1 != null && tb2 != null)
266                         return tb1.getBeginTimestamp().compareTo(tb2.getBeginTimestamp());
267                     return 0;
268                 }
269             });
270         }
271 
272 		return wList;
273 	}
274 	
275 	/**
276 	 * Provides a way to access all of the leave blocks for a given week.
277 	 *
278 	 * Outer list is 0 indexed list representing days in a week.
279 	 * Inner List are all of the leave blocks for that day.
280 	 *
281 	 * Ex.
282 	 *
283 	 * List<List<LeaveBlock>> week0 = getWeekLeaveBlocks(0);
284 	 * List<LeaveBlock> day0 = week0.get(0);
285 	 *
286 	 * @param week
287 	 * @return
288 	 */
289 	public List<List<LeaveBlock>> getWeekLeaveBlocks(int week){
290 		int startIndex = week*7;
291 		int endIndex = (week*7)+7;
292 		endIndex = endIndex > dayLeaveBlockList.size() ? dayLeaveBlockList.size() : endIndex;
293 
294         // Need to sort each day by clock time.
295         List<List<LeaveBlock>> wList = dayLeaveBlockList.subList(startIndex, endIndex);
296         for (List<LeaveBlock> dList : wList) {
297             Collections.sort(dList, new Comparator<LeaveBlock>() { // Sort the Leave Blocks
298                 public int compare(LeaveBlock lb1, LeaveBlock lb2) {
299                     if (lb1 != null && lb2 != null)
300                         return lb1.getLeaveDate().compareTo(lb2.getLeaveDate());
301                     return 0;
302                 }
303             });
304         }
305 
306 		return wList;
307 	}
308 
309 	/**
310 	 * When consuming these weeks, you must be aware that you could be on a
311 	 * virtual day, ie noon to noon schedule and have your FLSA time start
312 	 * before the virtual day start,
313 	 * but still have a full 7 days for your week.
314      *
315      * @param zone The TimeZone to use when constructing this relative sorting.
316 	 */
317 	public List<FlsaWeek> getFlsaWeeks(DateTimeZone zone, int flsaDayConstant, boolean isFlsaDayPassed){
318 		if(!isFlsaDayPassed) {
319 			flsaDayConstant = payCalendar.getFlsaBeginDayConstant(); 
320 		}
321 				
322 		Time flsaBeginTime  = payCalendar.getFlsaBeginTime();
323 
324 		// We can use these to build our interval, we have to make sure we
325 		// place them on the proper day when we construct it.
326 		LocalTime flsaBeginLocalTime = LocalTime.fromDateFields(flsaBeginTime);
327 
328 		// Defines both the start date and the start virtual time.
329 		// We will add 1 day to this to move over all days.
330 		//
331 		// FLSA time is set.  This is an FLSA start date.
332         LocalDateTime startLDT = payCalendarEntry.getBeginPeriodLocalDateTime();
333 
334 		List<FlsaWeek> flsaWeeks = new ArrayList<FlsaWeek>();
335 		List<TimeBlock> flattenedTimeBlocks = getFlattenedTimeBlockList();
336 		List<LeaveBlock> flattenedLeaveBlocks = getFlattenedLeaveBlockList();
337 		FlsaWeek currentWeek = new FlsaWeek(flsaDayConstant, flsaBeginLocalTime, payCalendarEntry.getBeginPeriodFullDateTime().toLocalTime());
338 		FlsaDay flsaDay = new FlsaDay(startLDT, flattenedTimeBlocks, flattenedLeaveBlocks, zone);
339 		currentWeek.addFlsaDay(flsaDay);
340 		flsaWeeks.add(currentWeek);
341 
342 		for (int i = 1; i < dayTimeBlockList.size(); i++) {
343 			LocalDateTime currentDate = startLDT.plusDays(i);
344 			flsaDay = new FlsaDay(currentDate, flattenedTimeBlocks, flattenedLeaveBlocks, zone);
345 
346 			if (currentDate.getDayOfWeek() == flsaDayConstant) {
347 				currentWeek = new FlsaWeek(flsaDayConstant, flsaBeginLocalTime, flsaBeginLocalTime);
348 				flsaWeeks.add(currentWeek);	
349 			}
350 			
351 			currentWeek.addFlsaDay(flsaDay);
352 		}
353 
354 		return flsaWeeks;
355 	}
356 	
357 	public List<List<FlsaWeek>> getFlsaWeeks(DateTimeZone zone, String principalId) {
358 		List<List<FlsaWeek>> flsaWeeks = new ArrayList<List<FlsaWeek>>();
359 		
360 		List<FlsaWeek> currentWeeks = getFlsaWeeks(zone, 0, false);
361 		
362 		for (ListIterator<FlsaWeek> weekIterator = currentWeeks.listIterator(); weekIterator.hasNext(); ) {
363 			List<FlsaWeek> flsaWeek = new ArrayList<FlsaWeek>();
364 			
365 			int index = weekIterator.nextIndex();
366 			FlsaWeek currentWeek = weekIterator.next();
367 			
368 			if (index == 0 && !currentWeek.isFirstWeekFull()) {
369 				CalendarEntry previousCalendarEntry = HrServiceLocator.getCalendarEntryService().getPreviousCalendarEntryByCalendarId(payCalendar.getHrCalendarId(), payCalendarEntry);
370 				if (previousCalendarEntry != null) {
371 					TimesheetDocumentHeader timesheetDocumentHeader = TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeader(principalId, previousCalendarEntry.getBeginPeriodFullDateTime(), previousCalendarEntry.getEndPeriodFullDateTime());
372 					if (timesheetDocumentHeader != null) { 
373 		                TimesheetDocument timesheetDocument = TkServiceLocator.getTimesheetService().getTimesheetDocument(timesheetDocumentHeader.getDocumentId());
374 		                List<String> assignmentKeys = new ArrayList<String>();
375 		                for(Assignment assignment : timesheetDocument.getAssignments()) {
376 		                	assignmentKeys.add(assignment.getAssignmentKey());
377 		                }
378 		                
379 						List<TimeBlock> timeBlocks = TkServiceLocator.getTimeBlockService().getTimeBlocks(timesheetDocumentHeader.getDocumentId());
380 						List<LeaveBlock> leaveBlocks = LmServiceLocator.getLeaveBlockService().getLeaveBlocksForTimeCalendar(principalId, previousCalendarEntry.getBeginPeriodFullDateTime().toLocalDate(), previousCalendarEntry.getEndPeriodFullDateTime().toLocalDate(), assignmentKeys);
381 						if (CollectionUtils.isNotEmpty(timeBlocks)) {
382 							TkTimeBlockAggregate previousAggregate = new TkTimeBlockAggregate(timeBlocks, leaveBlocks, previousCalendarEntry, payCalendar, true);
383 							List<FlsaWeek> previousWeek = previousAggregate.getFlsaWeeks(zone, 0, false);
384 							if (CollectionUtils.isNotEmpty(previousWeek)) {
385 								flsaWeek.add(previousWeek.get(previousWeek.size() - 1));
386 							}
387 						}
388 					 }
389 				}
390 			}
391 			
392 			flsaWeek.add(currentWeek);
393 			
394 			if (index == currentWeeks.size() - 1 && !currentWeek.isLastWeekFull()) {
395 				CalendarEntry nextCalendarEntry = HrServiceLocator.getCalendarEntryService().getNextCalendarEntryByCalendarId(payCalendar.getHrCalendarId(), payCalendarEntry);
396 				if (nextCalendarEntry != null) {
397 					TimesheetDocumentHeader timesheetDocumentHeader = TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeader(principalId, nextCalendarEntry.getBeginPeriodFullDateTime(), nextCalendarEntry.getEndPeriodFullDateTime());
398 					if (timesheetDocumentHeader != null) {
399 		                TimesheetDocument timesheetDocument = TkServiceLocator.getTimesheetService().getTimesheetDocument(timesheetDocumentHeader.getDocumentId());
400 		                List<String> assignmentKeys = new ArrayList<String>();
401 		                for(Assignment assignment : timesheetDocument.getAssignments()) {
402 		                	assignmentKeys.add(assignment.getAssignmentKey());
403 		                }
404 		                
405 						List<TimeBlock> timeBlocks = TkServiceLocator.getTimeBlockService().getTimeBlocks(timesheetDocumentHeader.getDocumentId());
406 						List<LeaveBlock> leaveBlocks = LmServiceLocator.getLeaveBlockService().getLeaveBlocksForTimeCalendar(principalId, nextCalendarEntry.getBeginPeriodFullDateTime().toLocalDate(), nextCalendarEntry.getEndPeriodFullDateTime().toLocalDate(), assignmentKeys);
407 						if (CollectionUtils.isNotEmpty(timeBlocks)) {
408 							TkTimeBlockAggregate nextAggregate = new TkTimeBlockAggregate(timeBlocks, leaveBlocks, nextCalendarEntry, payCalendar, true);
409 							List<FlsaWeek> nextWeek = nextAggregate.getFlsaWeeks(zone, 0, false);
410 							if (CollectionUtils.isNotEmpty(nextWeek)) {
411 								flsaWeek.add(nextWeek.get(0));
412 							}
413 						}
414 					 }
415 				}
416 			}
417 			
418 			flsaWeeks.add(flsaWeek);
419 		}
420 
421 		return flsaWeeks;
422 	}
423 
424 	/**
425 	 * @return the total number of weeks that this object represents.
426 	 */
427 	public int numberOfAggregatedWeeks() {
428 		int weeks = 0;
429 
430 		if (this.dayTimeBlockList.size() > 0) {
431 			weeks = this.dayTimeBlockList.size() / 7;
432 			if (this.dayTimeBlockList.size() % 7 > 0)
433 				weeks++;
434 		}
435 
436 		return weeks;
437 	}
438 
439 	public List<List<TimeBlock>> getDayTimeBlockList() {
440 		return dayTimeBlockList;
441 	}
442 	
443 	public List<List<LeaveBlock>> getDayLeaveBlockList() {
444 		return dayLeaveBlockList;
445 	}
446 
447 	public CalendarEntry getPayCalendarEntry() {
448 		return payCalendarEntry;
449 	}
450 
451 	public void setPayCalendarEntry(CalendarEntry payCalendarEntry) {
452 		this.payCalendarEntry = payCalendarEntry;
453 	}
454 
455 	public Calendar getPayCalendar() {
456 		return payCalendar;
457 	}
458 
459 	public void setPayCalendar(Calendar payCalendar) {
460 		this.payCalendar = payCalendar;
461 	}
462 	
463 	//Very much a hack to add valid leave blocks to a time block aggregate...
464 	public static TkTimeBlockAggregate combineTimeAndLeaveAggregates(TkTimeBlockAggregate tbAggregate, LeaveBlockAggregate lbAggregate) {
465 		if (tbAggregate != null
466 				&& lbAggregate != null
467 				&& tbAggregate.getDayTimeBlockList().size() == lbAggregate.getDayLeaveBlockList().size()) {
468 			for (int i = 0; i < tbAggregate.getDayTimeBlockList().size(); i++) {
469 				List<LeaveBlock> leaveBlocks = lbAggregate.getDayLeaveBlockList().get(i);
470 				if (CollectionUtils.isNotEmpty(leaveBlocks)) {
471 					for (LeaveBlock lb : leaveBlocks) {
472 						//convert leave block to generic time block and add to list
473 						//conveniently, we only really need the hours amount
474 						TimeBlock timeBlock = new TimeBlock();
475 						timeBlock.setHours(lb.getLeaveAmount().negate());
476 						timeBlock.setBeginTimestamp(new Timestamp(lb.getLeaveDate().getTime()));
477 						timeBlock.setEndTimestamp(new Timestamp(new DateTime(lb.getLeaveDate()).plusMinutes(timeBlock.getHours().intValue()).getMillis()));
478 						timeBlock.setAssignmentKey(lb.getAssignmentKey());
479 						timeBlock.setJobNumber(lb.getJobNumber());
480 						timeBlock.setWorkArea(lb.getWorkArea());
481 						timeBlock.setTask(lb.getTask());
482 						timeBlock.setEarnCode(lb.getEarnCode());
483 						
484 						EarnCode earnCodeObj = HrServiceLocator.getEarnCodeService().getEarnCode(lb.getEarnCode(), lb.getLeaveLocalDate());
485 						if(earnCodeObj != null) {
486 							timeBlock.setEarnCodeType(earnCodeObj.getEarnCodeType());
487 						}
488 						timeBlock.setPrincipalId(lb.getPrincipalId());
489 						timeBlock.setWorkArea(lb.getWorkArea());
490 						TimeHourDetail timeHourDetail = new TimeHourDetail();
491 						timeHourDetail.setEarnCode(timeBlock.getEarnCode());
492 						timeHourDetail.setHours(timeBlock.getHours());
493 						timeHourDetail.setAmount(BigDecimal.ZERO);
494 						timeBlock.addTimeHourDetail(timeHourDetail);
495 						tbAggregate.getDayTimeBlockList().get(i).add(timeBlock);
496 					}
497 				}
498 
499 			}
500 		}
501 		return tbAggregate;
502 	}
503 
504 }