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.timesummary.service; 017 018 import org.apache.commons.collections.CollectionUtils; 019 import org.apache.commons.lang.StringUtils; 020 import org.apache.commons.lang.time.DateUtils; 021 import org.apache.log4j.Logger; 022 import org.joda.time.DateTime; 023 import org.joda.time.DateTimeFieldType; 024 import org.joda.time.Interval; 025 import org.joda.time.LocalDateTime; 026 import org.kuali.hr.job.Job; 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.accrual.service.AccrualServiceImpl; 031 import org.kuali.hr.lm.leaveSummary.LeaveSummary; 032 import org.kuali.hr.lm.leaveSummary.LeaveSummaryRow; 033 import org.kuali.hr.lm.leaveblock.LeaveBlock; 034 import org.kuali.hr.lm.util.LeaveBlockAggregate; 035 import org.kuali.hr.time.assignment.Assignment; 036 import org.kuali.hr.time.assignment.AssignmentDescriptionKey; 037 import org.kuali.hr.time.calendar.Calendar; 038 import org.kuali.hr.time.calendar.CalendarEntries; 039 import org.kuali.hr.time.earncode.EarnCode; 040 import org.kuali.hr.time.earncodegroup.EarnCodeGroup; 041 import org.kuali.hr.time.flsa.FlsaDay; 042 import org.kuali.hr.time.flsa.FlsaWeek; 043 import org.kuali.hr.time.principal.PrincipalHRAttributes; 044 import org.kuali.hr.time.service.base.TkServiceLocator; 045 import org.kuali.hr.time.timeblock.TimeBlock; 046 import org.kuali.hr.time.timeblock.TimeHourDetail; 047 import org.kuali.hr.time.timesheet.TimesheetDocument; 048 import org.kuali.hr.time.timesummary.AssignmentRow; 049 import org.kuali.hr.time.timesummary.EarnCodeSection; 050 import org.kuali.hr.time.timesummary.EarnGroupSection; 051 import org.kuali.hr.time.timesummary.TimeSummary; 052 import org.kuali.hr.time.util.TKUtils; 053 import org.kuali.hr.time.util.TkConstants; 054 import org.kuali.hr.time.util.TkTimeBlockAggregate; 055 import org.kuali.hr.time.workarea.WorkArea; 056 057 import java.math.BigDecimal; 058 import java.sql.Timestamp; 059 import java.util.*; 060 061 public class TimeSummaryServiceImpl implements TimeSummaryService { 062 private static final String OTHER_EARN_GROUP = "Other"; 063 private static final Logger LOG = Logger.getLogger(TimeSummaryServiceImpl.class); 064 065 @Override 066 public TimeSummary getTimeSummary(TimesheetDocument timesheetDocument) { 067 TimeSummary timeSummary = new TimeSummary(); 068 069 if(timesheetDocument.getTimeBlocks() == null) { 070 return timeSummary; 071 } 072 073 List<Boolean> dayArrangements = new ArrayList<Boolean>(); 074 075 timeSummary.setSummaryHeader(getHeaderForSummary(timesheetDocument.getCalendarEntry(), dayArrangements)); 076 TkTimeBlockAggregate tkTimeBlockAggregate = new TkTimeBlockAggregate(timesheetDocument.getTimeBlocks(), timesheetDocument.getCalendarEntry(), TkServiceLocator.getCalendarService().getCalendar(timesheetDocument.getCalendarEntry().getHrCalendarId()), true); 077 078 List<Assignment> timeAssignments = timesheetDocument.getAssignments(); 079 List<String> tAssignmentKeys = new ArrayList<String>(); 080 for(Assignment assign : timeAssignments) { 081 tAssignmentKeys.add(assign.getAssignmentKey()); 082 } 083 List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocksForTimeCalendar(timesheetDocument.getPrincipalId(), 084 timesheetDocument.getCalendarEntry().getBeginPeriodDate(), timesheetDocument.getCalendarEntry().getEndPeriodDate(), tAssignmentKeys); 085 LeaveBlockAggregate leaveBlockAggregate = new LeaveBlockAggregate(leaveBlocks, timesheetDocument.getCalendarEntry()); 086 tkTimeBlockAggregate = combineTimeAndLeaveAggregates(tkTimeBlockAggregate, leaveBlockAggregate); 087 088 timeSummary.setWorkedHours(getWorkedHours(tkTimeBlockAggregate)); 089 090 List<EarnGroupSection> earnGroupSections = getEarnGroupSections(tkTimeBlockAggregate, timeSummary.getSummaryHeader().size()+1, 091 dayArrangements, timesheetDocument.getAsOfDate(), timesheetDocument.getDocEndDate()); 092 timeSummary.setSections(earnGroupSections); 093 094 try { 095 List<LeaveSummaryRow> maxedLeaveRows = getMaxedLeaveRows(timesheetDocument.getCalendarEntry(),timesheetDocument.getPrincipalId()); 096 timeSummary.setMaxedLeaveRows(maxedLeaveRows); 097 } catch (Exception e) { 098 // TODO Auto-generated catch block 099 LOG.error("error retreiving maxed leave rows", e); 100 } 101 102 return timeSummary; 103 } 104 105 private List<LeaveSummaryRow> getMaxedLeaveRows( 106 CalendarEntries calendarEntry, String principalId) throws Exception { 107 List<LeaveSummaryRow> maxedLeaveRows = new ArrayList<LeaveSummaryRow>(); 108 109 if (TkServiceLocator.getLeaveApprovalService().isActiveAssignmentFoundOnJobFlsaStatus(principalId, TkConstants.FLSA_STATUS_NON_EXEMPT, true)) { 110 111 Map<String,Set<LeaveBlock>> eligibilities = TkServiceLocator.getAccrualCategoryMaxBalanceService().getMaxBalanceViolations(calendarEntry,principalId); 112 Set<LeaveBlock> onDemandTransfers = eligibilities.get(LMConstants.MAX_BAL_ACTION_FREQ.ON_DEMAND); 113 114 Interval calendarEntryInterval = new Interval(calendarEntry.getBeginPeriodDate().getTime(),calendarEntry.getEndPeriodDate().getTime()); 115 116 //use the current date if on the current calendar? yes -> no warning given until accrual is reached. If accrual occurs on last day of period or last day of service interval 117 //change, no warning given to the employee of balance limits being exceeded except on or after that day. 118 119 if(!onDemandTransfers.isEmpty()) { 120 for(LeaveBlock lb : onDemandTransfers) { 121 Date leaveDate = lb.getLeaveDate(); 122 LeaveSummary summary = TkServiceLocator.getLeaveSummaryService().getLeaveSummaryAsOfDate(principalId, new java.sql.Date(DateUtils.addDays(leaveDate, 1).getTime())); 123 LeaveSummaryRow row = summary.getLeaveSummaryRowForAccrualCtgy(lb.getAccrualCategory()); 124 if(row != null) { 125 //AccrualCategory accrualCategory = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(row.getAccrualCategoryId()); 126 //AccrualCategoryRule currentRule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRuleForDate(accrualCategory, asOfDate, pha.getServiceDate()); 127 if(calendarEntryInterval.contains(leaveDate.getTime())) { 128 //do not allow the on-demand max balance action if the rule the action occurs under is no longer in effect, 129 //or if the infraction did not occur within this interval. ( if it occurred during the previous interval, 130 //the employee will have the option to take action in that interval up to & including the end date of that interval. ) 131 row.setInfractingLeaveBlockId(lb.getAccrualCategoryRuleId()); 132 AccrualCategoryRule aRule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRule(lb.getAccrualCategoryRuleId()); 133 134 if(StringUtils.equals(aRule.getActionAtMaxBalance(),LMConstants.ACTION_AT_MAX_BAL.TRANSFER)) 135 row.setTransferable(true); 136 else if(StringUtils.equals(aRule.getActionAtMaxBalance(),LMConstants.ACTION_AT_MAX_BAL.PAYOUT)) 137 row.setPayoutable(true); 138 139 boolean exists = false; 140 for(LeaveSummaryRow maxedRow : maxedLeaveRows) { 141 if(StringUtils.equals(maxedRow.getAccrualCategoryId(),row.getAccrualCategoryId())) 142 exists = true; 143 } 144 if(!exists) 145 maxedLeaveRows.add(row); 146 } 147 } 148 } 149 } 150 } 151 return maxedLeaveRows; 152 } 153 154 /** 155 * Aggregates timeblocks into the appropriate earngroup-> earncode -> assignment rows 156 * @param tkTimeBlockAggregate 157 * @param numEntries 158 * @param dayArrangements 159 * @param asOfDate 160 * @return 161 */ 162 public List<EarnGroupSection> getEarnGroupSections(TkTimeBlockAggregate tkTimeBlockAggregate, int numEntries, List<Boolean> dayArrangements, Date asOfDate , Date docEndDate){ 163 List<EarnGroupSection> earnGroupSections = new ArrayList<EarnGroupSection>(); 164 List<FlsaWeek> flsaWeeks = tkTimeBlockAggregate.getFlsaWeeks(TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback()); 165 Map<String, EarnCodeSection> earnCodeToEarnCodeSection = new HashMap<String, EarnCodeSection>(); 166 Map<String, EarnGroupSection> earnGroupToEarnGroupSection = new HashMap<String, EarnGroupSection>(); 167 168 int dayCount = 0; 169 170 //TODO remove this and correct the aggregate .. not sure what the down stream changes are 171 //so leaving this for initial release 172 List<FlsaWeek> trimmedFlsaWeeks = new ArrayList<FlsaWeek>(); 173 for(FlsaWeek flsaWeek : flsaWeeks){ 174 if(flsaWeek.getFlsaDays().size() > 0){ 175 trimmedFlsaWeeks.add(flsaWeek); 176 } 177 } 178 179 //For every flsa week and day aggegate each time hour detail 180 // buckets it by earn code section first 181 for(FlsaWeek flsaWeek : trimmedFlsaWeeks){ 182 int weekSize = 0; 183 List<FlsaDay> flsaDays = flsaWeek.getFlsaDays(); 184 for(FlsaDay flsaDay : flsaDays){ 185 Map<String, List<TimeBlock>> earnCodeToTimeBlocks = flsaDay.getEarnCodeToTimeBlocks(); 186 187 for(List<TimeBlock> timeBlocks : earnCodeToTimeBlocks.values()){ 188 for(TimeBlock timeBlock : timeBlocks){ 189 for(TimeHourDetail thd : timeBlock.getTimeHourDetails()){ 190 if(StringUtils.equals(TkConstants.LUNCH_EARN_CODE, thd.getEarnCode())){ 191 continue; 192 } 193 EarnCodeSection earnCodeSection = earnCodeToEarnCodeSection.get(thd.getEarnCode()); 194 if(earnCodeSection == null){ 195 earnCodeSection = new EarnCodeSection(); 196 earnCodeSection.setEarnCode(thd.getEarnCode()); 197 EarnCode earnCodeObj = TkServiceLocator.getEarnCodeService().getEarnCode(thd.getEarnCode(), TKUtils.getTimelessDate(asOfDate)); 198 earnCodeSection.setDescription(earnCodeObj.getDescription()); 199 earnCodeSection.setIsAmountEarnCode((earnCodeObj.getRecordMethod()!= null && earnCodeObj.getRecordMethod().equalsIgnoreCase(TkConstants.EARN_CODE_AMOUNT)) ? true : false); 200 for(int i = 0;i<(numEntries-1);i++){ 201 earnCodeSection.getTotals().add(BigDecimal.ZERO); 202 } 203 204 earnCodeToEarnCodeSection.put(thd.getEarnCode(), earnCodeSection); 205 } 206 String assignKey = timeBlock.getAssignmentKey(); 207 AssignmentRow assignRow = earnCodeSection.getAssignKeyToAssignmentRowMap().get(assignKey); 208 if(assignRow == null){ 209 assignRow = new AssignmentRow(); 210 assignRow.setAssignmentKey(assignKey); 211 AssignmentDescriptionKey assignmentKey = TkServiceLocator.getAssignmentService().getAssignmentDescriptionKey(assignKey); 212 Assignment assignment = TkServiceLocator.getAssignmentService().getAssignment(timeBlock.getPrincipalId(), assignmentKey, TKUtils.getTimelessDate(asOfDate)); 213 // some assignment may not be effective at the beginning of the pay period, use the end date of the period to find it 214 if(assignment == null) { 215 assignment = TkServiceLocator.getAssignmentService().getAssignment(timeBlock.getPrincipalId(), assignmentKey, TKUtils.getTimelessDate(docEndDate)); 216 } 217 //TODO push this up to the assignment fetch/fully populated instead of like this 218 if(assignment != null){ 219 if(assignment.getJob() == null){ 220 Job aJob = TkServiceLocator.getJobService().getJob(assignment.getPrincipalId(),assignment.getJobNumber(),TKUtils.getTimelessDate(assignment.getEffectiveDate())); 221 assignment.setJob(aJob); 222 } 223 if(assignment.getWorkAreaObj() == null){ 224 WorkArea aWorkArea = TkServiceLocator.getWorkAreaService().getWorkArea(assignment.getWorkArea(), TKUtils.getTimelessDate(assignment.getEffectiveDate())); 225 assignment.setWorkAreaObj(aWorkArea); 226 } 227 assignRow.setDescr(assignment.getAssignmentDescription()); 228 } 229 for(int i = 0;i<(numEntries-1);i++){ 230 assignRow.getTotal().add(BigDecimal.ZERO); 231 assignRow.getAmount().add(BigDecimal.ZERO); 232 } 233 assignRow.setEarnCodeSection(earnCodeSection); 234 earnCodeSection.addAssignmentRow(assignRow); 235 } 236 assignRow.addToTotal(dayCount, thd.getHours()); 237 assignRow.addToAmount(dayCount, thd.getAmount()); 238 } 239 } 240 } 241 dayCount++; 242 weekSize++; 243 } 244 //end of flsa week accumulate weekly totals 245 for(EarnCodeSection earnCodeSection : earnCodeToEarnCodeSection.values()){ 246 earnCodeSection.addWeeklyTotal(dayCount, weekSize); 247 } 248 weekSize = 0; 249 250 dayCount++; 251 } 252 253 dayCount = 0; 254 //now create all teh earn group sections and aggregate accordingly 255 for(EarnCodeSection earnCodeSection : earnCodeToEarnCodeSection.values()){ 256 String earnCode = earnCodeSection.getEarnCode(); 257 EarnCodeGroup earnGroupObj = TkServiceLocator.getEarnCodeGroupService().getEarnCodeGroupSummaryForEarnCode(earnCode, TKUtils.getTimelessDate(asOfDate)); 258 String earnGroup = null; 259 if(earnGroupObj == null){ 260 earnGroup = OTHER_EARN_GROUP; 261 } else{ 262 earnGroup = earnGroupObj.getDescr(); 263 } 264 265 EarnGroupSection earnGroupSection = earnGroupToEarnGroupSection.get(earnGroup); 266 if(earnGroupSection == null){ 267 earnGroupSection = new EarnGroupSection(); 268 earnGroupSection.setEarnGroup(earnGroup); 269 for(int i =0;i<(numEntries-1);i++){ 270 earnGroupSection.getTotals().add(BigDecimal.ZERO); 271 } 272 earnGroupToEarnGroupSection.put(earnGroup, earnGroupSection); 273 } 274 earnGroupSection.addEarnCodeSection(earnCodeSection, dayArrangements); 275 276 } 277 for(EarnGroupSection earnGroupSection : earnGroupToEarnGroupSection.values()){ 278 earnGroupSections.add(earnGroupSection); 279 } 280 return earnGroupSections; 281 } 282 283 /** 284 * Generate a list of string describing this pay calendar entry for the summary 285 * @param payCalEntry 286 * @return 287 */ 288 protected List<String> getSummaryHeader(CalendarEntries payCalEntry){ 289 List<String> summaryHeader = new ArrayList<String>(); 290 int dayCount = 0; 291 Date beginDateTime = payCalEntry.getBeginPeriodDateTime(); 292 Date endDateTime = payCalEntry.getEndPeriodDateTime(); 293 boolean virtualDays = false; 294 LocalDateTime endDate = payCalEntry.getEndLocalDateTime(); 295 296 if (endDate.get(DateTimeFieldType.hourOfDay()) != 0 || endDate.get(DateTimeFieldType.minuteOfHour()) != 0 || 297 endDate.get(DateTimeFieldType.secondOfMinute()) != 0){ 298 virtualDays = true; 299 } 300 301 Date currDateTime = beginDateTime; 302 java.util.Calendar cal = GregorianCalendar.getInstance(); 303 304 while(currDateTime.before(endDateTime)){ 305 LocalDateTime currDate = new LocalDateTime(currDateTime); 306 summaryHeader.add(makeHeaderDiplayString(currDate, virtualDays)); 307 308 dayCount++; 309 if((dayCount % 7) == 0){ 310 summaryHeader.add("Week "+ ((dayCount / 7))); 311 } 312 cal.setTime(currDateTime); 313 cal.add(java.util.Calendar.HOUR, 24); 314 currDateTime = cal.getTime(); 315 } 316 317 summaryHeader.add("Period Total"); 318 return summaryHeader; 319 } 320 321 //kind of a hack 322 private TkTimeBlockAggregate combineTimeAndLeaveAggregates(TkTimeBlockAggregate tbAggregate, LeaveBlockAggregate lbAggregate) { 323 if (tbAggregate != null 324 && lbAggregate != null 325 && tbAggregate.getDayTimeBlockList().size() == lbAggregate.getDayLeaveBlockList().size()) { 326 for (int i = 0; i < tbAggregate.getDayTimeBlockList().size(); i++) { 327 List<LeaveBlock> leaveBlocks = lbAggregate.getDayLeaveBlockList().get(i); 328 if (CollectionUtils.isNotEmpty(leaveBlocks)) { 329 for (LeaveBlock lb : leaveBlocks) { 330 //convert leave block to generic time block and add to list 331 //conveniently, we only really need the hours amount 332 TimeBlock timeBlock = new TimeBlock(); 333 timeBlock.setHours(lb.getLeaveAmount().negate()); 334 timeBlock.setBeginTimestamp(new Timestamp(lb.getLeaveDate().getTime())); 335 timeBlock.setEndTimestamp(new Timestamp(new DateTime(lb.getLeaveDate()).plusMinutes(timeBlock.getHours().intValue()).getMillis())); 336 timeBlock.setAssignmentKey(lb.getAssignmentKey()); 337 timeBlock.setEarnCode(lb.getEarnCode()); 338 timeBlock.setPrincipalId(lb.getPrincipalId()); 339 timeBlock.setWorkArea(lb.getWorkArea()); 340 TimeHourDetail timeHourDetail = new TimeHourDetail(); 341 timeHourDetail.setEarnCode(timeBlock.getEarnCode()); 342 timeHourDetail.setHours(timeBlock.getHours()); 343 timeHourDetail.setAmount(BigDecimal.ZERO); 344 timeBlock.addTimeHourDetail(timeHourDetail); 345 tbAggregate.getDayTimeBlockList().get(i).add(timeBlock); 346 } 347 } 348 349 } 350 } 351 return tbAggregate; 352 } 353 354 355 356 /** 357 * Provides the number of hours worked for the pay period indicated in the 358 * aggregate. 359 * 360 * @param aggregate The aggregate we are summing 361 * 362 * @return A list of BigDecimals containing the number of hours worked. 363 * This list will line up with the header. 364 */ 365 private List<BigDecimal> getWorkedHours(TkTimeBlockAggregate aggregate) { 366 List<BigDecimal> hours = new ArrayList<BigDecimal>(); 367 BigDecimal periodTotal = TkConstants.BIG_DECIMAL_SCALED_ZERO; 368 for (FlsaWeek week : aggregate.getFlsaWeeks(TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback())) { 369 BigDecimal weeklyTotal = TkConstants.BIG_DECIMAL_SCALED_ZERO; 370 for (FlsaDay day : week.getFlsaDays()) { 371 BigDecimal totalForDay = TkConstants.BIG_DECIMAL_SCALED_ZERO; 372 for (TimeBlock block : day.getAppliedTimeBlocks()) { 373 totalForDay = totalForDay.add(block.getHours(), TkConstants.MATH_CONTEXT); 374 weeklyTotal = weeklyTotal.add(block.getHours(), TkConstants.MATH_CONTEXT); 375 periodTotal = periodTotal.add(block.getHours(), TkConstants.MATH_CONTEXT); 376 } 377 hours.add(totalForDay); 378 } 379 hours.add(weeklyTotal); 380 } 381 hours.add(periodTotal); 382 383 return hours; 384 } 385 386 387 /** 388 * Handles the generation of the display header for the time summary. 389 * 390 * @param cal The PayCalendarEntries object we are using to derive information. 391 * @param dayArrangements Container passed in to store the position of week / period aggregate sums 392 * 393 * @return An in-order string of days for this period that properly accounts 394 * for FLSA week boundaries in the pay period. 395 */ 396 @Override 397 public List<String> getHeaderForSummary(CalendarEntries cal, List<Boolean> dayArrangements) { 398 List<String> header = new ArrayList<String>(); 399 if (cal == null) { 400 return Collections.emptyList(); 401 } 402 // Maps directly to joda time day of week constants. 403 int flsaBeginDay = this.getPayCalendarForEntry(cal).getFlsaBeginDayConstant(); 404 boolean virtualDays = false; 405 LocalDateTime startDate = cal.getBeginLocalDateTime(); 406 LocalDateTime endDate = cal.getEndLocalDateTime(); 407 408 // Increment end date if we are on a virtual day calendar, so that the 409 // for loop can account for having the proper amount of days on the 410 // summary calendar. 411 if (endDate.get(DateTimeFieldType.hourOfDay()) != 0 || endDate.get(DateTimeFieldType.minuteOfHour()) != 0 || 412 endDate.get(DateTimeFieldType.secondOfMinute()) != 0) 413 { 414 endDate = endDate.plusDays(1); 415 virtualDays = true; 416 } 417 418 boolean afterFirstDay = false; 419 int week = 1; 420 for (LocalDateTime currentDate = startDate; currentDate.compareTo(endDate) < 0; currentDate = currentDate.plusDays(1)) { 421 422 if (currentDate.getDayOfWeek() == flsaBeginDay && afterFirstDay) { 423 header.add("Week " + week); 424 dayArrangements.add(false); 425 week++; 426 } 427 428 header.add(makeHeaderDiplayString(currentDate, virtualDays)); 429 dayArrangements.add(true); 430 431 432 afterFirstDay = true; 433 } 434 435 // We may have a very small final "week" on this pay period. For now 436 // we will mark it as a week, and if someone doesn't like it, it can 437 // be removed. 438 if (!header.get(header.size()-1).startsWith("Week")) { 439 dayArrangements.add(false); 440 header.add("Week " + week); 441 } 442 443 444 header.add("Period Total"); 445 dayArrangements.add(false); 446 return header; 447 } 448 449 /** 450 * Helper function to generate display text for the summary header. 451 * @param currentDate The date we are generating for. 452 * @param virtualDays Whether or not virtual days apply. 453 * @return A string appropriate for UI display. 454 */ 455 private String makeHeaderDiplayString(LocalDateTime currentDate, boolean virtualDays) { 456 StringBuilder display = new StringBuilder(); 457 458 display.append(currentDate.toString("E")); 459 if (virtualDays) { 460 LocalDateTime nextDate = currentDate.plusDays(1); 461 display.append(" - "); 462 display.append(nextDate.toString("E")); 463 } 464 465 display.append("<br />"); 466 467 display.append(currentDate.toString(TkConstants.DT_ABBREV_DATE_FORMAT)); 468 if (virtualDays) { 469 LocalDateTime nextDate = currentDate.plusDays(1); 470 display.append(" - "); 471 display.append(nextDate.toString(TkConstants.DT_ABBREV_DATE_FORMAT)); 472 } 473 474 return display.toString(); 475 } 476 477 /** 478 * @param calEntry Calendar entry we are using for lookup. 479 * @return The PayCalendar that owns the provided entry. 480 */ 481 private Calendar getPayCalendarForEntry(CalendarEntries calEntry) { 482 Calendar cal = null; 483 484 if (calEntry != null) { 485 cal = TkServiceLocator.getCalendarService().getCalendar(calEntry.getHrCalendarId()); 486 } 487 488 return cal; 489 } 490 491 }