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