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