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.approval.service;
17  
18  import java.math.BigDecimal;
19  import java.sql.Types;
20  import java.text.SimpleDateFormat;
21  import java.util.ArrayList;
22  import java.util.Date;
23  import java.util.HashMap;
24  import java.util.HashSet;
25  import java.util.LinkedHashMap;
26  import java.util.LinkedList;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  import java.util.SortedSet;
31  import java.util.TreeSet;
32  
33  import org.apache.commons.lang.StringUtils;
34  import org.apache.log4j.Logger;
35  import org.joda.time.DateMidnight;
36  import org.joda.time.DateTime;
37  import org.joda.time.DateTimeZone;
38  import org.joda.time.Hours;
39  import org.joda.time.Interval;
40  import org.joda.time.format.DateTimeFormat;
41  import org.joda.time.format.DateTimeFormatter;
42  import org.kuali.hr.job.Job;
43  import org.kuali.hr.time.approval.web.ApprovalTimeSummaryRow;
44  import org.kuali.hr.time.assignment.Assignment;
45  import org.kuali.hr.time.assignment.AssignmentDescriptionKey;
46  import org.kuali.hr.time.calendar.Calendar;
47  import org.kuali.hr.time.calendar.CalendarEntries;
48  import org.kuali.hr.time.clocklog.ClockLog;
49  import org.kuali.hr.time.flsa.FlsaDay;
50  import org.kuali.hr.time.flsa.FlsaWeek;
51  import org.kuali.hr.time.person.TKPerson;
52  import org.kuali.hr.time.principal.PrincipalHRAttributes;
53  import org.kuali.hr.time.roles.TkUserRoles;
54  import org.kuali.hr.time.service.base.TkServiceLocator;
55  import org.kuali.hr.time.timeblock.TimeBlock;
56  import org.kuali.hr.time.timesheet.TimesheetDocument;
57  import org.kuali.hr.time.util.TKContext;
58  import org.kuali.hr.time.util.TKUser;
59  import org.kuali.hr.time.util.TKUtils;
60  import org.kuali.hr.time.util.TkConstants;
61  import org.kuali.hr.time.util.TkTimeBlockAggregate;
62  import org.kuali.hr.time.workarea.WorkArea;
63  import org.kuali.hr.time.workflow.TimesheetDocumentHeader;
64  import org.kuali.rice.kew.api.KewApiServiceLocator;
65  import org.kuali.rice.kew.notes.Note;
66  import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
67  import org.kuali.rice.kew.service.KEWServiceLocator;
68  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
69  import org.kuali.rice.krad.util.GlobalVariables;
70  import org.springframework.jdbc.support.rowset.SqlRowSet;
71  
72  import com.google.common.collect.HashMultimap;
73  import com.google.common.collect.Multimap;
74  
75  public class TimeApproveServiceImpl implements TimeApproveService {
76  
77  	private static final Logger LOG = Logger
78  			.getLogger(TimeApproveServiceImpl.class);
79  
80  	public static final int DAYS_WINDOW_DELTA = 31;
81  
82  	public Map<String, CalendarEntries> getPayCalendarEntriesForDept(
83  			String dept, Date currentDate) {
84  		DateTime minDt = new DateTime(currentDate,
85  				TKUtils.getSystemDateTimeZone());
86  		minDt = minDt.minusDays(DAYS_WINDOW_DELTA);
87  		java.sql.Date windowDate = TKUtils.getTimelessDate(minDt.toDate());
88  
89  		Map<String, CalendarEntries> pceMap = new HashMap<String, CalendarEntries>();
90  		Set<String> principals = new HashSet<String>();
91  		List<WorkArea> workAreasForDept = TkServiceLocator.getWorkAreaService()
92  				.getWorkAreas(dept, new java.sql.Date(currentDate.getTime()));
93  		// Get all of the principals within our window of time.
94  		for (WorkArea workArea : workAreasForDept) {
95  			Long waNum = workArea.getWorkArea();
96  			List<Assignment> assignments = TkServiceLocator
97  					.getAssignmentService().getActiveAssignmentsForWorkArea(
98  							waNum, TKUtils.getTimelessDate(currentDate));
99  
100 			if (assignments != null) {
101 				for (Assignment assignment : assignments) {
102 					principals.add(assignment.getPrincipalId());
103 				}
104 			} else {
105 				assignments = TkServiceLocator.getAssignmentService()
106 						.getActiveAssignmentsForWorkArea(waNum, windowDate);
107 				if (assignments != null) {
108 					for (Assignment assignment : assignments) {
109 						principals.add(assignment.getPrincipalId());
110 					}
111 				}
112 			}
113 		}
114 
115 		// Get the pay calendars
116 		Set<Calendar> payCals = new HashSet<Calendar>();
117 		for (String pid : principals) {
118 			PrincipalHRAttributes pc = TkServiceLocator
119 					.getPrincipalHRAttributeService().getPrincipalCalendar(pid,
120 							currentDate);
121 			if (pc == null)
122 				pc = TkServiceLocator.getPrincipalHRAttributeService()
123 						.getPrincipalCalendar(pid, windowDate);
124 
125 			if (pc != null) {
126 				payCals.add(pc.getCalendar());
127 			} else {
128 				LOG.warn("PrincipalCalendar null for principal: '" + pid + "'");
129 			}
130 		}
131 
132 		// Grab the pay calendar entries + groups
133 		for (Calendar pc : payCals) {
134 			CalendarEntries pce = TkServiceLocator
135 					.getCalendarEntriesService()
136 					.getCurrentCalendarEntriesByCalendarId(
137                             pc.getHrCalendarId(), currentDate);
138 			pceMap.put(pc.getCalendarName(), pce);
139 		}
140 
141 		return pceMap;
142 	}
143 
144 	@Override
145 	public Map<String, CalendarEntries> getPayCalendarEntriesForApprover(
146 			String principalId, Date currentDate, String dept) {
147 		TKUser tkUser = TKContext.getUser();
148 
149 		Map<String, CalendarEntries> pceMap = new HashMap<String, CalendarEntries>();
150 		Set<String> principals = new HashSet<String>();
151 		DateTime minDt = new DateTime(currentDate,
152 				TKUtils.getSystemDateTimeZone());
153 		minDt = minDt.minusDays(DAYS_WINDOW_DELTA);
154 		java.sql.Date windowDate = TKUtils.getTimelessDate(minDt.toDate());
155 		Set<Long> approverWorkAreas = TkUserRoles.getUserRoles(GlobalVariables.getUserSession().getPrincipalId()).getApproverWorkAreas();
156 
157 		// Get all of the principals within our window of time.
158 		for (Long waNum : approverWorkAreas) {
159 			List<Assignment> assignments = TkServiceLocator
160 					.getAssignmentService().getActiveAssignmentsForWorkArea(
161 							waNum, TKUtils.getTimelessDate(currentDate));
162 
163 			if (assignments != null) {
164 				for (Assignment assignment : assignments) {
165 					principals.add(assignment.getPrincipalId());
166 				}
167 			}
168 		}
169 
170 		// Get the pay calendars
171 		Set<Calendar> payCals = new HashSet<Calendar>();
172 		for (String pid : principals) {
173 			PrincipalHRAttributes pc = TkServiceLocator
174 					.getPrincipalHRAttributeService().getPrincipalCalendar(pid,
175                             currentDate);
176 			if (pc == null)
177 				pc = TkServiceLocator.getPrincipalHRAttributeService()
178 						.getPrincipalCalendar(pid, windowDate);
179 
180 			if (pc != null) {
181 				payCals.add(pc.getCalendar());
182 			} else {
183 				LOG.warn("PrincipalCalendar null for principal: '" + pid + "'");
184 			}
185 		}
186 
187 		// Grab the pay calendar entries + groups
188 		for (Calendar pc : payCals) {
189 			CalendarEntries pce = TkServiceLocator
190 					.getCalendarEntriesService()
191 					.getCurrentCalendarEntriesByCalendarId(
192                             pc.getHrCalendarId(), currentDate);
193 			pceMap.put(pc.getCalendarName(), pce);
194 		}
195 
196 		return pceMap;
197 	}
198 
199 	public SortedSet<String> getApproverPayCalendarGroups(Date payBeginDate,
200 			Date payEndDate) {
201 		SortedSet<String> pcg = new TreeSet<String>();
202 
203 		TKUser tkUser = TKContext.getUser();
204 		Set<Long> approverWorkAreas = TkUserRoles.getUserRoles(GlobalVariables.getUserSession().getPrincipalId()).getApproverWorkAreas();
205 		List<Assignment> assignments = new ArrayList<Assignment>();
206 
207 		for (Long workArea : approverWorkAreas) {
208 			if (workArea != null) {
209 				assignments.addAll(TkServiceLocator.getAssignmentService()
210 						.getActiveAssignmentsForWorkArea(workArea,
211 								new java.sql.Date(payBeginDate.getTime())));
212 			}
213 		}
214 		if (!assignments.isEmpty()) {
215 			for (Assignment assign : assignments) {
216 				String principalId = assign.getPrincipalId();
217 				TimesheetDocumentHeader tdh = TkServiceLocator
218 						.getTimesheetDocumentHeaderService().getDocumentHeader(
219 								principalId, payBeginDate, payEndDate);
220 				if (tdh != null) {
221 					String pyCalendarGroup = TkServiceLocator
222 							.getPrincipalHRAttributeService()
223 							.getPrincipalCalendar(principalId, tdh.getPayBeginDate())
224 							.getCalendar().getCalendarName();
225 					pcg.add(pyCalendarGroup);
226 				}
227 			}
228 		}
229 		return pcg;
230 	}
231 
232 	@SuppressWarnings("rawtypes")
233 	@Override
234 	public List<ApprovalTimeSummaryRow> getApprovalSummaryRows(
235 			Date payBeginDate, Date payEndDate, String calGroup,
236 			List<TKPerson> persons, List<String> payCalendarLabels,
237 			CalendarEntries payCalendarEntries) {
238 		List<ApprovalTimeSummaryRow> rows = new LinkedList<ApprovalTimeSummaryRow>();
239 		Map<String, TimesheetDocumentHeader> principalDocumentHeader = getPrincipalDocumehtHeader(
240 				persons, payBeginDate, payEndDate);
241 
242 		Calendar payCalendar = TkServiceLocator.getCalendarService()
243 				.getCalendar(payCalendarEntries.getHrCalendarId());
244 		DateTimeZone dateTimeZone = TkServiceLocator.getTimezoneService()
245 				.getUserTimezoneWithFallback();
246 		List<Interval> dayIntervals = TKUtils
247 				.getDaySpanForCalendarEntry(payCalendarEntries);
248 
249 		for (TKPerson person : persons) {
250 			TimesheetDocumentHeader tdh = new TimesheetDocumentHeader();
251 			String documentId = "";
252 			if (principalDocumentHeader.containsKey(person.getPrincipalId())) {
253 				tdh = principalDocumentHeader.get(person.getPrincipalId());
254 				documentId = principalDocumentHeader.get(
255 						person.getPrincipalId()).getDocumentId();
256 			}
257 			List<TimeBlock> timeBlocks = new ArrayList<TimeBlock>();
258 			List notes = new ArrayList();
259 			List<String> warnings = new ArrayList<String>();
260 
261 			ApprovalTimeSummaryRow approvalSummaryRow = new ApprovalTimeSummaryRow();
262 
263 			if (principalDocumentHeader.containsKey(person.getPrincipalId())) {
264 				approvalSummaryRow
265 						.setApprovalStatus(TkConstants.DOC_ROUTE_STATUS.get(tdh
266 								.getDocumentStatus()));
267 			}
268 
269 			if (StringUtils.isNotBlank(documentId)) {
270 				timeBlocks = TkServiceLocator.getTimeBlockService()
271 						.getTimeBlocks(documentId);
272 				notes = this.getNotesForDocument(documentId);
273                 TimesheetDocument td = TkServiceLocator.getTimesheetService().getTimesheetDocument(documentId);
274 				warnings = TkServiceLocator.getWarningService().getWarnings(td);
275 			}
276 
277 			Map<String, BigDecimal> hoursToPayLabelMap = getHoursToPayDayMap(
278 					person.getPrincipalId(), payEndDate, payCalendarLabels,
279 					timeBlocks, null, payCalendarEntries, payCalendar,
280 					dateTimeZone, dayIntervals);
281 
282 			approvalSummaryRow.setName(person.getPrincipalName());
283 			approvalSummaryRow.setPrincipalId(person.getPrincipalId());
284 			approvalSummaryRow.setPayCalendarGroup(calGroup);
285 			approvalSummaryRow.setDocumentId(documentId);
286 			approvalSummaryRow.setHoursToPayLabelMap(hoursToPayLabelMap);
287 			approvalSummaryRow.setPeriodTotal(hoursToPayLabelMap
288 					.get("Period Total"));
289 			approvalSummaryRow.setLstTimeBlocks(timeBlocks);
290 			approvalSummaryRow.setNotes(notes);
291 			approvalSummaryRow.setWarnings(warnings);
292 
293 			// Compare last clock log versus now and if > threshold
294 			// highlight entry
295 			ClockLog lastClockLog = TkServiceLocator.getClockLogService()
296 					.getLastClockLog(person.getPrincipalId());
297 			approvalSummaryRow
298 					.setClockStatusMessage(createLabelForLastClockLog(lastClockLog));
299 			if (lastClockLog != null
300 					&& (StringUtils.equals(lastClockLog.getClockAction(),
301 							TkConstants.CLOCK_IN) || StringUtils
302 							.equals(lastClockLog.getClockAction(),
303 									TkConstants.LUNCH_IN))) {
304 				DateTime startTime = new DateTime(lastClockLog
305 						.getClockTimestamp().getTime());
306 				DateTime endTime = new DateTime(System.currentTimeMillis());
307 
308 				Hours hour = Hours.hoursBetween(startTime, endTime);
309 				if (hour != null) {
310 					int elapsedHours = hour.getHours();
311 					if (elapsedHours >= TkConstants.NUMBER_OF_HOURS_CLOCKED_IN_APPROVE_TAB_HIGHLIGHT) {
312 						approvalSummaryRow.setClockedInOverThreshold(true);
313 					}
314 				}
315 
316 			}
317 			rows.add(approvalSummaryRow);
318 		}
319 		return rows;
320 	}
321 
322 	public List<TimesheetDocumentHeader> getDocumentHeadersByPrincipalIds(
323 			Date payBeginDate, Date payEndDate, List<String> principalIds) {
324 		List<TimesheetDocumentHeader> headers = new LinkedList<TimesheetDocumentHeader>();
325 		for (String principalId : principalIds) {
326 			TimesheetDocumentHeader tdh = TkServiceLocator
327 					.getTimesheetDocumentHeaderService().getDocumentHeader(
328 							principalId, payBeginDate, payEndDate);
329 			if (tdh != null) {
330 				headers.add(tdh);
331 			}
332 		}
333 
334 		return headers;
335 	}
336 
337 	/**
338 	 * Get pay calendar labels for approval tab
339 	 */
340 	public List<String> getPayCalendarLabelsForApprovalTab(Date payBeginDate,
341 			Date payEndDate) {
342 		// :)
343 		// http://stackoverflow.com/questions/111933/why-shouldnt-i-use-hungarian-notation
344 		List<String> lstPayCalendarLabels = new ArrayList<String>();
345 		DateTime payBegin = new DateTime(payBeginDate.getTime());
346 		DateTime payEnd = new DateTime(payEndDate.getTime());
347 		DateTime currTime = payBegin;
348 		int dayCounter = 1;
349 		int weekCounter = 1;
350 
351 		while (currTime.isBefore(payEnd)) {
352 			String labelForDay = createLabelForDay(currTime);
353 			lstPayCalendarLabels.add(labelForDay);
354 			currTime = currTime.plusDays(1);
355 			if ((dayCounter % 7) == 0) {
356 				lstPayCalendarLabels.add("Week " + weekCounter);
357 				weekCounter++;
358 			}
359 			dayCounter++;
360 		}
361 		lstPayCalendarLabels.add("Total Hours");
362 		return lstPayCalendarLabels;
363 	}
364 
365 	/**
366 	 * Create label for a given pay calendar day
367 	 * 
368 	 * @param fromDate
369 	 * @return
370 	 */
371 	private String createLabelForDay(DateTime fromDate) {
372 		DateMidnight dateMidnight = new DateMidnight(fromDate);
373 		if (dateMidnight.compareTo(fromDate) == 0) {
374 			DateTimeFormatter fmt = DateTimeFormat.forPattern("MMM/dd");
375 			return fmt.print(fromDate);
376 		}
377 		DateTime toDate = fromDate.plusDays(1);
378 		DateTimeFormatter fmt = DateTimeFormat.forPattern("MMM/dd k:m:s");
379 		return fmt.print(fromDate) + "-" + fmt.print(toDate);
380 	}
381 
382 	/**
383 	 * Create label for the last clock log
384 	 * 
385 	 * @param cl
386 	 * @return
387 	 */
388 	private String createLabelForLastClockLog(ClockLog cl) {
389 		// return sdf.format(dt);
390 		if (cl == null) {
391 			return "No previous clock information";
392 		}
393 		SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy hh:mm a");
394 		String dateTime = sdf.format(new java.sql.Date(cl.getClockTimestamp()
395 				.getTime()));
396 		if (StringUtils.equals(cl.getClockAction(), TkConstants.CLOCK_IN)) {
397 			return "Clocked in since: " + dateTime;
398 		} else if (StringUtils.equals(cl.getClockAction(),
399 				TkConstants.LUNCH_OUT)) {
400 			return "At Lunch since: " + dateTime;
401 		} else if (StringUtils
402 				.equals(cl.getClockAction(), TkConstants.LUNCH_IN)) {
403 			return "Returned from Lunch : " + dateTime;
404 		} else if (StringUtils.equals(cl.getClockAction(),
405 				TkConstants.CLOCK_OUT)) {
406 			return "Clocked out since: " + dateTime;
407 		} else {
408 			return "No previous clock information";
409 		}
410 
411 	}
412 
413 	public List<Map<String, Map<String, BigDecimal>>> getHoursByDayAssignmentBuckets(
414 			TkTimeBlockAggregate aggregate,
415 			List<Assignment> approverAssignments, List<String> payCalendarLabels) {
416 		Map<String, Assignment> mappedAssignments = mapAssignmentsByAssignmentKey(approverAssignments);
417 		List<List<TimeBlock>> blocksByDay = aggregate.getDayTimeBlockList();
418 
419 		// (assignment_key, <List of Hours Summed by Day>)
420 		Map<String, List<BigDecimal>> approverHours = new HashMap<String, List<BigDecimal>>();
421 		Map<String, List<BigDecimal>> otherHours = new HashMap<String, List<BigDecimal>>();
422 		for (int day = 0; day < blocksByDay.size(); day++) {
423 			List<TimeBlock> dayBlocks = blocksByDay.get(day);
424 			for (TimeBlock block : dayBlocks) {
425 				List<BigDecimal> hours;
426 				// Approver vs. Other :: Set our day-hour-list object
427 				if (mappedAssignments.containsKey(block.getAssignmentKey())) {
428 					hours = approverHours.get(block.getAssignmentKey());
429 					if (hours == null) {
430 						hours = new ArrayList<BigDecimal>();
431 						approverHours.put(block.getAssignmentKey(), hours);
432 					}
433 				} else {
434 					hours = otherHours.get(block.getAssignmentKey());
435 					if (hours == null) {
436 						hours = new ArrayList<BigDecimal>();
437 						otherHours.put(block.getAssignmentKey(), hours);
438 					}
439 				}
440 
441 				// Fill in zeroes for days with 0 hours / no time blocks
442 				for (int fill = hours.size(); fill <= day; fill++) {
443 					hours.add(TkConstants.BIG_DECIMAL_SCALED_ZERO);
444 				}
445 
446 				// Add time from time block to existing time.
447 				BigDecimal timeToAdd = hours.get(day);
448 				timeToAdd = timeToAdd.add(block.getHours(),
449 						TkConstants.MATH_CONTEXT);
450 				hours.set(day, timeToAdd);
451 			}
452 		}
453 
454 		// Compute Weekly / Period Summary Totals for each Assignment.
455 		// assignment row, each row has a map of pay calendar label -> big
456 		// decimal totals.
457 		Map<String, Map<String, BigDecimal>> approverAssignToPayHourTotals = new HashMap<String, Map<String, BigDecimal>>();
458 		Map<String, Map<String, BigDecimal>> otherAssignToPayHourTotals = new HashMap<String, Map<String, BigDecimal>>();
459 
460 		// Pass by Reference
461 		generateSummaries(approverAssignToPayHourTotals, approverHours,
462 				payCalendarLabels);
463 		generateSummaries(otherAssignToPayHourTotals, otherHours,
464 				payCalendarLabels);
465 
466 		// Add to our return list, "virtual" tuple.
467 		List<Map<String, Map<String, BigDecimal>>> returnTuple = new ArrayList<Map<String, Map<String, BigDecimal>>>(
468 				2);
469 		returnTuple.add(approverAssignToPayHourTotals);
470 		returnTuple.add(otherAssignToPayHourTotals);
471 
472 		return returnTuple;
473 	}
474 
475 	// Helper method for above method.
476 	private void generateSummaries(
477 			Map<String, Map<String, BigDecimal>> payHourTotals,
478 			Map<String, List<BigDecimal>> assignmentToHours,
479 			List<String> payCalendarLabels) {
480 		for (String key : assignmentToHours.keySet()) {
481 			// for every Assignment
482 			Map<String, BigDecimal> hoursToPayLabelMap = new LinkedHashMap<String, BigDecimal>();
483 			List<BigDecimal> dayTotals = assignmentToHours.get(key);
484 			int dayCount = 0;
485 			BigDecimal weekTotal = new BigDecimal(0.00);
486 			BigDecimal periodTotal = new BigDecimal(0.00);
487 			for (String payCalendarLabel : payCalendarLabels) {
488 				if (StringUtils.contains(payCalendarLabel, "Week")) {
489 					hoursToPayLabelMap.put(payCalendarLabel, weekTotal);
490 					weekTotal = new BigDecimal(0.00);
491 				} else if (StringUtils
492 						.contains(payCalendarLabel, "Total Hours")) {
493 					hoursToPayLabelMap.put(payCalendarLabel, periodTotal);
494 				} else {
495 					BigDecimal dayTotal = TkConstants.BIG_DECIMAL_SCALED_ZERO;
496 					if (dayCount < dayTotals.size())
497 						dayTotal = dayTotals.get(dayCount);
498 
499 					hoursToPayLabelMap.put(payCalendarLabel, dayTotal);
500 					weekTotal = weekTotal.add(dayTotal,
501 							TkConstants.MATH_CONTEXT);
502 					periodTotal = periodTotal.add(dayTotal);
503 					dayCount++;
504 				}
505 			}
506 			payHourTotals.put(key, hoursToPayLabelMap);
507 		}
508 	}
509 
510 	private Map<String, Assignment> mapAssignmentsByAssignmentKey(
511 			List<Assignment> assignments) {
512 		Map<String, Assignment> assignmentMap = new HashMap<String, Assignment>();
513 		for (Assignment assignment : assignments) {
514 			assignmentMap
515 					.put(AssignmentDescriptionKey
516 							.getAssignmentKeyString(assignment), assignment);
517 		}
518 		return assignmentMap;
519 	}
520 
521 	/**
522 	 * Aggregate TimeBlocks to hours per day and sum for week
523 	 * 
524 	 * @param principalId
525 	 * @param payEndDate
526 	 * @param payCalendarLabels
527 	 * @param lstTimeBlocks
528 	 * @param workArea
529 	 * @return
530 	 */
531 	@Override
532 	public Map<String, BigDecimal> getHoursToPayDayMap(String principalId,
533 			Date payEndDate, List<String> payCalendarLabels,
534 			List<TimeBlock> lstTimeBlocks, Long workArea,
535 			CalendarEntries payCalendarEntries, Calendar payCalendar,
536 			DateTimeZone dateTimeZone, List<Interval> dayIntervals) {
537 		Map<String, BigDecimal> hoursToPayLabelMap = new LinkedHashMap<String, BigDecimal>();
538 		List<BigDecimal> dayTotals = new ArrayList<BigDecimal>();
539 
540 		TkTimeBlockAggregate tkTimeBlockAggregate = new TkTimeBlockAggregate(
541 				lstTimeBlocks, payCalendarEntries, payCalendar, true,
542 				dayIntervals);
543 		List<FlsaWeek> flsaWeeks = tkTimeBlockAggregate
544 				.getFlsaWeeks(dateTimeZone);
545 		for (FlsaWeek week : flsaWeeks) {
546 			for (FlsaDay day : week.getFlsaDays()) {
547 				BigDecimal total = new BigDecimal(0.00);
548 				for (TimeBlock tb : day.getAppliedTimeBlocks()) {
549 					if (workArea != null) {
550 						if (tb.getWorkArea().compareTo(workArea) == 0) {
551 							total = total.add(tb.getHours(),
552 									TkConstants.MATH_CONTEXT);
553 						} else {
554 							total = total.add(new BigDecimal("0"),
555 									TkConstants.MATH_CONTEXT);
556 						}
557 					} else {
558 						total = total.add(tb.getHours(),
559 								TkConstants.MATH_CONTEXT);
560 					}
561 				}
562 				dayTotals.add(total);
563 			}
564 		}
565 
566 		int dayCount = 0;
567 		BigDecimal weekTotal = new BigDecimal(0.00);
568 		BigDecimal periodTotal = new BigDecimal(0.00);
569 		for (String payCalendarLabel : payCalendarLabels) {
570 			if (StringUtils.contains(payCalendarLabel, "Week")) {
571 				hoursToPayLabelMap.put(payCalendarLabel, weekTotal);
572 				weekTotal = new BigDecimal(0.00);
573 			} else if (StringUtils.contains(payCalendarLabel, "Period Total")) {
574 				hoursToPayLabelMap.put(payCalendarLabel, periodTotal);
575 			} else {
576 				if(dayCount < dayTotals.size()) {
577 					hoursToPayLabelMap.put(payCalendarLabel,
578 							dayTotals.get(dayCount));
579 					weekTotal = weekTotal.add(dayTotals.get(dayCount),
580 							TkConstants.MATH_CONTEXT);
581 					periodTotal = periodTotal.add(dayTotals.get(dayCount));
582 					dayCount++;
583 				}
584 
585 			}
586 
587 		}
588 		return hoursToPayLabelMap;
589 	}
590 
591 	public boolean doesApproverHavePrincipalsForCalendarGroup(Date asOfDate,
592 			String calGroup) {
593 		TKUser tkUser = TKContext.getUser();
594 		Set<Long> approverWorkAreas = TkUserRoles.getUserRoles(GlobalVariables.getUserSession().getPrincipalId()).getApproverWorkAreas();
595 		for (Long workArea : approverWorkAreas) {
596 			List<Assignment> assignments = TkServiceLocator
597 					.getAssignmentService().getActiveAssignmentsForWorkArea(
598 							workArea, new java.sql.Date(asOfDate.getTime()));
599 			List<String> principalIds = new ArrayList<String>();
600 			for (Assignment assign : assignments) {
601 				if (principalIds.contains(assign.getPrincipalId())) {
602 					continue;
603 				}
604 				principalIds.add(assign.getPrincipalId());
605 			}
606 
607 			for (String principalId : principalIds) {
608 				PrincipalHRAttributes principalCal = TkServiceLocator
609 						.getPrincipalHRAttributeService().getPrincipalCalendar(
610 								principalId, asOfDate);
611 				if (StringUtils.equals(principalCal.getPayCalendar(),
612 						calGroup)) {
613 					return true;
614 				}
615 			}
616 		}
617 		return false;
618 	}
619 
620 	@SuppressWarnings("rawtypes")
621 	public List getNotesForDocument(String documentNumber) {
622         List notes = KewApiServiceLocator.getNoteService().getNotes(documentNumber);
623 		// add the user name in the note object
624 //		for (Object obj : notes) {
625 //			Note note = (Note) obj;
626 //			note.setNoteAuthorFullName(KimApiServiceLocator.getPersonService()
627 //					.getPerson(note.getNoteAuthorWorkflowId()).getName());
628 //		}
629 		return notes;
630 	}
631 
632 	@Override
633 	public List<String> getUniquePayGroups() {
634 		String sql = "SELECT DISTINCT P.pay_calendar FROM hr_principal_attributes_t P WHERE P.active = 'Y'";
635 		SqlRowSet rs = TkServiceLocator.getTkJdbcTemplate().queryForRowSet(sql);
636 		List<String> pyGroups = new LinkedList<String>();
637 		while (rs.next()) {
638 			pyGroups.add(rs.getString("pay_calendar"));
639 		}
640 		return pyGroups;
641 	}
642 
643 	@Override
644 	public List<String> getPrincipalIdsByDeptWorkAreaRolename(String roleName,
645 			String department, String workArea, java.sql.Date payBeginDate,
646 			java.sql.Date payEndDate, String calGroup) {
647 		List<String> principalIds = getPrincipalIdsWithActiveAssignmentsForCalendarGroupByDeptAndWorkArea(
648 				roleName, department, workArea, calGroup, payEndDate,
649 				payBeginDate, payEndDate);
650 		return principalIds;
651 	}
652 	
653 	protected List<String> getPrincipalIdsWithActiveAssignmentsForCalendarGroupByDeptAndWorkArea(
654 		      String roleName, String department, String workArea,
655 		      String payCalendarGroup, java.sql.Date effdt,
656 		      java.sql.Date beginDate, java.sql.Date endDate) {
657 	    String sql = null;
658 
659         List<Job> jobs = TkServiceLocator.getJobService().getJobs(TKUser.getCurrentTargetPerson().getPrincipalId(), effdt);
660         String jobPositionNumbersList = "'";
661         for (Job job : jobs) {
662                         jobPositionNumbersList += job.getPositionNumber() + "','";
663         }
664         /* the sql statement will enclose this string in single quotes, so we do not want the leading quote, or the trailing quote, comma, and quote. */
665         if (jobPositionNumbersList.length() > 3) {
666             jobPositionNumbersList = jobPositionNumbersList.substring(1, jobPositionNumbersList.length()-3) ;
667         } else {
668             jobPositionNumbersList = jobPositionNumbersList.substring(1);
669         }
670 
671 	    if (department == null || department.isEmpty()) {
672 	      return new ArrayList<String>();
673 	    } else {
674 	      List<String> principalIds = new ArrayList<String>();
675 	      SqlRowSet rs = null;
676           sql = "SELECT DISTINCT A0.PRINCIPAL_ID FROM TK_ASSIGNMENT_T A0, HR_ROLES_T R0, TK_WORK_AREA_T W0, HR_PRINCIPAL_ATTRIBUTES_T P0  WHERE "
677         		  + "((A0.EFFDT =  (SELECT MAX(EFFDT)  FROM TK_ASSIGNMENT_T  WHERE PRINCIPAL_ID = A0.PRINCIPAL_ID  AND EFFDT <= ? AND WORK_AREA = A0.WORK_AREA  AND TASK = A0.TASK AND JOB_NUMBER = A0.JOB_NUMBER) AND "
678                   + "A0.TIMESTAMP =  (SELECT MAX(TIMESTAMP)  FROM TK_ASSIGNMENT_T  WHERE PRINCIPAL_ID = A0.PRINCIPAL_ID  AND EFFDT = A0.EFFDT AND WORK_AREA = A0.WORK_AREA AND TASK = A0.TASK AND JOB_NUMBER = A0.JOB_NUMBER) AND "
679                   + "A0.ACTIVE = 'Y') OR (A0.ACTIVE = 'N'  AND A0.EFFDT >= ? AND A0.EFFDT <= ?)) AND "
680                   + "R0.WORK_AREA = A0.WORK_AREA AND "
681                   + "R0.ROLE_NAME IN ('TK_APPROVER', 'TK_APPROVER_DELEGATE', 'TK_REVIEWER') AND "
682                   + "R0.ACTIVE = 'Y' AND "
683                   + "( (R0.PRINCIPAL_ID = ? AND "
684                   + "R0.EFFDT = (SELECT MAX(EFFDT)  FROM HR_ROLES_T  WHERE ROLE_NAME = R0.ROLE_NAME AND PRINCIPAL_ID = R0.PRINCIPAL_ID AND EFFDT <= ? AND WORK_AREA = R0.WORK_AREA) AND "
685                   + "R0.TIMESTAMP = (SELECT MAX(TIMESTAMP)  FROM HR_ROLES_T  WHERE ROLE_NAME = R0.ROLE_NAME AND PRINCIPAL_ID = R0.PRINCIPAL_ID AND EFFDT = R0.EFFDT AND WORK_AREA = R0.WORK_AREA) "
686                   + ") or ("
687                   + "R0.POSITION_NBR in (?) AND "
688                   + "R0.EFFDT = (SELECT MAX(EFFDT)  FROM HR_ROLES_T  WHERE ROLE_NAME = R0.ROLE_NAME AND POSITION_NBR = R0.POSITION_NBR AND EFFDT <= ? AND WORK_AREA = R0.WORK_AREA) AND "
689                   + "R0.TIMESTAMP = (SELECT MAX(TIMESTAMP)  FROM HR_ROLES_T  WHERE ROLE_NAME = R0.ROLE_NAME AND POSITION_NBR = R0.POSITION_NBR AND EFFDT = R0.EFFDT AND WORK_AREA = R0.WORK_AREA) "
690                   + ") ) AND "
691                   + "W0.WORK_AREA = A0.WORK_AREA AND "
692                   + "W0.DEPT = ? AND "
693                   + "W0.EFFDT = (SELECT MAX(EFFDT) FROM TK_WORK_AREA_T WHERE EFFDT <= ? AND WORK_AREA = W0.WORK_AREA) AND "
694                   + "W0.TIMESTAMP =  (SELECT MAX(TIMESTAMP)  FROM TK_WORK_AREA_T  WHERE WORK_AREA = W0.WORK_AREA  AND EFFDT = W0.EFFDT) AND "
695                   + "W0.ACTIVE = 'Y' AND "
696                   + "P0.PRINCIPAL_ID = A0.PRINCIPAL_ID AND "
697                   + "P0.PAY_CALENDAR = ?";
698 
699 
700 	       int[] params = null;
701 	       Object[] values = null;
702 	       if (workArea != null) {
703 	          sql += " AND A0.WORK_AREA = ? ";
704 	          params = new int[] {java.sql.Types.DATE,
705 	              java.sql.Types.DATE,
706 	              java.sql.Types.DATE,
707 	              java.sql.Types.VARCHAR, 
708 	              java.sql.Types.DATE,
709                   java.sql.Types.VARCHAR,
710                   java.sql.Types.DATE,
711 	              java.sql.Types.VARCHAR,
712 	              java.sql.Types.DATE,
713 	              java.sql.Types.VARCHAR,
714 	              java.sql.Types.INTEGER };
715 	          values = new Object[] {effdt, beginDate, endDate, TKUser.getCurrentTargetPerson().getPrincipalId(), effdt, jobPositionNumbersList, effdt, department, effdt, payCalendarGroup, workArea };
716 	        }else {
717 	          params = new int[] {java.sql.Types.DATE,
718 	              java.sql.Types.DATE,
719 	              java.sql.Types.DATE,
720 	              java.sql.Types.VARCHAR, 
721 	              java.sql.Types.DATE,
722                   java.sql.Types.VARCHAR,
723                   java.sql.Types.DATE,
724 	              java.sql.Types.VARCHAR,
725 	              java.sql.Types.DATE,
726 	              java.sql.Types.VARCHAR};
727 	          values = new Object[] {effdt, beginDate, endDate, TKUser.getCurrentTargetPerson().getPrincipalId(), effdt, jobPositionNumbersList, effdt, department, effdt, payCalendarGroup};
728 	        }
729 	        rs = TkServiceLocator.getTkJdbcTemplate().queryForRowSet(
730 	            sql, values, params);
731 	      while (rs.next()) {
732 	        principalIds.add(rs.getString("principal_id"));
733 	      }
734 	      return principalIds;
735 	    }
736 	}
737 
738 	@Override
739 	public Map<String, TimesheetDocumentHeader> getPrincipalDocumehtHeader(
740 			List<TKPerson> persons, Date payBeginDate, Date payEndDate) {
741 		Map<String, TimesheetDocumentHeader> principalDocumentHeader = new LinkedHashMap<String, TimesheetDocumentHeader>();
742 		for (TKPerson person : persons) {
743 			String principalId = person.getPrincipalId();
744 			
745 			TimesheetDocumentHeader tdh = TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeader(principalId, payBeginDate, payEndDate);
746 			if(tdh != null) {
747 				principalDocumentHeader.put(principalId, tdh);	
748 			}
749 		}
750 		return principalDocumentHeader;
751 	}
752 
753 	@Override
754 	public Multimap<String, Long> getDeptWorkAreasByWorkAreas(
755 			Set<Long> approverWorkAreas) {
756 		Multimap<String, Long> deptWorkAreas = HashMultimap.create();
757 
758 		if (approverWorkAreas.size() > 0) {
759 			// prepare the OR statement for query
760 			StringBuilder workAreas = new StringBuilder();
761 			for (Long workarea : approverWorkAreas) {
762 				if(workarea != null) {
763 					workAreas.append("work_area = " + workarea + " or ");
764 				}
765 			}
766 			String workAreasForQuery = workAreas.substring(0,
767 					workAreas.length() - 3);
768 			String sql = "SELECT DISTINCT work_area, dept FROM tk_work_area_t "
769 					+ "WHERE " + workAreasForQuery + " AND effdt <= ?";
770 
771 			/**
772 			 * Multimap is an interface from Google's java common library -
773 			 * Guava. HashMultimap allows us to create a map with duplicate keys
774 			 * which will then generate a data structure, i.e. [key] => [value1,
775 			 * value2, value3...]
776 			 * 
777 			 * It save a good lines of code to do the same thing through the
778 			 * java map, e.g. Map<String, List<String>> map = new
779 			 * Hashmap<String, List<String>>();
780 			 * 
781 			 * See the java doc for more information:
782 			 * http://google-collections.googlecode
783 			 * .com/svn/trunk/javadoc/com/google/common/collect/Multimap.html
784 			 */
785 			SqlRowSet rs = TkServiceLocator.getTkJdbcTemplate().queryForRowSet(
786 					sql, new Object[] { TKUtils.getCurrentDate() },
787 					new int[] { Types.DATE });
788 			while (rs.next()) {
789 				deptWorkAreas
790 						.put(rs.getString("dept"), rs.getLong("work_area"));
791 			}
792 		}
793 		return deptWorkAreas;
794 	}
795 
796 	@Override
797 	public Multimap<String, Long> getDeptWorkAreasByDepts(Set<String> userDepts) {
798 		Multimap<String, Long> deptWorkAreas = HashMultimap.create();
799 
800 		if (userDepts.size() > 0) {
801 			// prepare the OR statement for query
802 			StringBuilder depts = new StringBuilder();
803 			for (String dept : userDepts) {
804 				depts.append("dept = '" + dept + "' or ");
805 			}
806 			String deptsForQuery = depts.substring(0, depts.length() - 4);
807 			String sql = "SELECT DISTINCT work_area, dept FROM tk_work_area_t "
808 					+ "WHERE " + deptsForQuery + " AND effdt <= ?";
809 
810 			SqlRowSet rs = TkServiceLocator.getTkJdbcTemplate().queryForRowSet(
811 					sql, new Object[] { TKUtils.getCurrentDate() },
812 					new int[] { Types.DATE });
813 			while (rs.next()) {
814 				deptWorkAreas
815 						.put(rs.getString("dept"), rs.getLong("work_area"));
816 			}
817 		}
818 		return deptWorkAreas;
819 	}
820 
821 	public DocumentRouteHeaderValue getRouteHeader(String documentId) {
822 		return KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId);
823 	}
824 	
825 	@Override
826 	public List<CalendarEntries> getAllPayCalendarEntriesForApprover(String principalId, Date currentDate) {
827 		TKUser tkUser = TKContext.getUser();
828 		Set<String> principals = new HashSet<String>();
829 		DateTime minDt = new DateTime(currentDate,
830 				TKUtils.getSystemDateTimeZone());
831 		minDt = minDt.minusDays(DAYS_WINDOW_DELTA);
832 		Set<Long> approverWorkAreas = TkUserRoles.getUserRoles(GlobalVariables.getUserSession().getPrincipalId()).getApproverWorkAreas();
833 
834 		// Get all of the principals within our window of time.
835 		for (Long waNum : approverWorkAreas) {
836 			List<Assignment> assignments = TkServiceLocator
837 					.getAssignmentService().getActiveAssignmentsForWorkArea(waNum, TKUtils.getTimelessDate(currentDate));
838 
839 			if (assignments != null) {
840 				for (Assignment assignment : assignments) {
841 					principals.add(assignment.getPrincipalId());
842 				}
843 			}
844 		}
845 		List<TimesheetDocumentHeader> documentHeaders = new ArrayList<TimesheetDocumentHeader>();
846 		for(String pid : principals) {
847 			documentHeaders.addAll(TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeadersForPrincipalId(pid));
848 		}
849 		Set<CalendarEntries> payPeriodSet = new HashSet<CalendarEntries>();
850 		for(TimesheetDocumentHeader tdh : documentHeaders) {
851     		CalendarEntries pe = TkServiceLocator.getCalendarEntriesService().getCalendarEntriesByBeginAndEndDate(tdh.getPayBeginDate(), tdh.getPayEndDate());
852     		if(pe != null) {
853     			payPeriodSet.add(pe);
854     		}
855         }
856 		List<CalendarEntries> ppList = new ArrayList<CalendarEntries>(payPeriodSet);
857         
858 		return ppList;
859 	}
860 	
861 }