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 }