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 }