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