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