org.kuali.kpme.tklm.time.timesummary.service.TimeSummaryServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.kpme.tklm.time.timesummary.service.TimeSummaryServiceImpl.java

Source

/**
 * Copyright 2004-2013 The Kuali Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.opensource.org/licenses/ecl2.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.kuali.kpme.tklm.time.timesummary.service;

import java.math.BigDecimal;
import java.util.*;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.joda.time.DateTime;
import org.joda.time.DateTimeFieldType;
import org.joda.time.DateTimeZone;
import org.joda.time.Interval;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.kuali.kpme.core.accrualcategory.rule.AccrualCategoryRule;
import org.kuali.kpme.core.assignment.Assignment;
import org.kuali.kpme.core.assignment.AssignmentDescriptionKey;
import org.kuali.kpme.core.calendar.Calendar;
import org.kuali.kpme.core.calendar.entry.CalendarEntry;
import org.kuali.kpme.core.earncode.EarnCode;
import org.kuali.kpme.core.earncode.group.EarnCodeGroup;
import org.kuali.kpme.core.job.Job;
import org.kuali.kpme.core.service.HrServiceLocator;
import org.kuali.kpme.core.util.HrConstants;
import org.kuali.kpme.core.workarea.WorkArea;
import org.kuali.kpme.tklm.common.TkConstants;
import org.kuali.kpme.tklm.leave.block.LeaveBlock;
import org.kuali.kpme.tklm.leave.block.LeaveBlockAggregate;
import org.kuali.kpme.tklm.leave.service.LmServiceLocator;
import org.kuali.kpme.tklm.leave.summary.LeaveSummary;
import org.kuali.kpme.tklm.leave.summary.LeaveSummaryRow;
import org.kuali.kpme.tklm.time.flsa.FlsaDay;
import org.kuali.kpme.tklm.time.flsa.FlsaWeek;
import org.kuali.kpme.tklm.time.timeblock.TimeBlock;
import org.kuali.kpme.tklm.time.timehourdetail.TimeHourDetail;
import org.kuali.kpme.tklm.time.timesheet.TimesheetDocument;
import org.kuali.kpme.tklm.time.timesummary.AssignmentColumn;
import org.kuali.kpme.tklm.time.timesummary.AssignmentRow;
import org.kuali.kpme.tklm.time.timesummary.EarnCodeSection;
import org.kuali.kpme.tklm.time.timesummary.EarnGroupSection;
import org.kuali.kpme.tklm.time.timesummary.TimeSummary;
import org.kuali.kpme.tklm.time.util.TkTimeBlockAggregate;

public class TimeSummaryServiceImpl implements TimeSummaryService {
    private static final String OTHER_EARN_GROUP = "Other";
    private static final Logger LOG = Logger.getLogger(TimeSummaryServiceImpl.class);

    @Override
    public TimeSummary getTimeSummary(TimesheetDocument timesheetDocument) {
        TimeSummary timeSummary = new TimeSummary();

        if (timesheetDocument == null || timesheetDocument.getTimeBlocks() == null) {
            return timeSummary;
        }

        List<Boolean> dayArrangements = new ArrayList<Boolean>();

        timeSummary.setSummaryHeader(getHeaderForSummary(timesheetDocument.getCalendarEntry(), dayArrangements));
        TkTimeBlockAggregate tkTimeBlockAggregate = new TkTimeBlockAggregate(
                timesheetDocument.getTimeBlocks(), timesheetDocument.getCalendarEntry(), HrServiceLocator
                        .getCalendarService().getCalendar(timesheetDocument.getCalendarEntry().getHrCalendarId()),
                true);

        List<Assignment> timeAssignments = timesheetDocument.getAssignments();
        List<String> tAssignmentKeys = new ArrayList<String>();
        Set<String> regularEarnCodes = new HashSet<String>();
        for (Assignment assign : timeAssignments) {
            tAssignmentKeys.add(assign.getAssignmentKey());
            regularEarnCodes.add(assign.getJob().getPayTypeObj().getRegEarnCode());
        }
        List<LeaveBlock> leaveBlocks = LmServiceLocator.getLeaveBlockService().getLeaveBlocksForTimeCalendar(
                timesheetDocument.getPrincipalId(),
                timesheetDocument.getCalendarEntry().getBeginPeriodFullDateTime().toLocalDate(),
                timesheetDocument.getCalendarEntry().getEndPeriodFullDateTime().toLocalDate(), tAssignmentKeys);
        LeaveBlockAggregate leaveBlockAggregate = new LeaveBlockAggregate(leaveBlocks,
                timesheetDocument.getCalendarEntry());
        tkTimeBlockAggregate = TkTimeBlockAggregate.combineTimeAndLeaveAggregates(tkTimeBlockAggregate,
                leaveBlockAggregate);

        timeSummary.setWorkedHours(getWorkedHours(tkTimeBlockAggregate, regularEarnCodes, timeSummary));

        // Set Flsa week total map
        Map<String, BigDecimal> flsaWeekTotal = getHoursToFlsaWeekMap(tkTimeBlockAggregate,
                timesheetDocument.getPrincipalId(), null);
        timeSummary.setFlsaWeekTotalMap(flsaWeekTotal);

        List<EarnGroupSection> earnGroupSections = getEarnGroupSections(tkTimeBlockAggregate,
                timeSummary.getSummaryHeader().size() + 1, dayArrangements, timesheetDocument.getAsOfDate(),
                timesheetDocument.getDocEndDate());
        timeSummary.setSections(sortEarnGroupSections(earnGroupSections, regularEarnCodes));

        try {
            List<LeaveSummaryRow> maxedLeaveRows = getMaxedLeaveRows(timesheetDocument.getCalendarEntry(),
                    timesheetDocument.getPrincipalId());
            timeSummary.setMaxedLeaveRows(maxedLeaveRows);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            LOG.error("error retreiving maxed leave rows", e);
        }

        return timeSummary;
    }

    private List<EarnGroupSection> sortEarnGroupSections(List<EarnGroupSection> sections,
            Set<String> regularEarnCodes) {
        List<EarnGroupSection> sortedList = new ArrayList<EarnGroupSection>();
        //first sort by alpha
        Collections.sort(sections, new Comparator<EarnGroupSection>() {
            @Override
            public int compare(EarnGroupSection egs1, EarnGroupSection egs2) {
                if (egs1 == null ^ egs2 == null) {
                    return egs1 == null ? -1 : 1;
                }
                if (egs1 == null && egs2 == null) {
                    return 0;
                }
                //'other' earn group needs to be last.
                boolean isOther1 = StringUtils.equals(egs1.getEarnGroup(), OTHER_EARN_GROUP);
                boolean isOther2 = StringUtils.equals(egs2.getEarnGroup(), OTHER_EARN_GROUP);
                if (isOther1 ^ isOther2) {
                    return isOther1 ? 1 : -1;
                }
                if (isOther1 && isOther2) {
                    return 0;
                }
                return egs1.getEarnGroup().compareTo(egs2.getEarnGroup());
            }
        });

        List<EarnGroupSection> copy = new ArrayList<EarnGroupSection>(sections);
        //loop through in reverse
        for (EarnGroupSection egs : copy) {
            if (!CollectionUtils.intersection(regularEarnCodes, egs.getEarnCodeToEarnCodeSectionMap().keySet())
                    .isEmpty()) {
                sortedList.add(egs);
                sections.remove(egs);
            }
        }
        sortedList.addAll(sections);
        return sortedList;
    }

    private List<LeaveSummaryRow> getMaxedLeaveRows(CalendarEntry calendarEntry, String principalId)
            throws Exception {
        List<LeaveSummaryRow> maxedLeaveRows = new ArrayList<LeaveSummaryRow>();

        if (LmServiceLocator.getLeaveApprovalService().isActiveAssignmentFoundOnJobFlsaStatus(principalId,
                HrConstants.FLSA_STATUS_NON_EXEMPT, true)) {

            Map<String, Set<LeaveBlock>> eligibilities = LmServiceLocator.getAccrualCategoryMaxBalanceService()
                    .getMaxBalanceViolations(calendarEntry, principalId);
            Set<LeaveBlock> onDemandTransfers = eligibilities.get(HrConstants.MAX_BAL_ACTION_FREQ.ON_DEMAND);

            Interval calendarEntryInterval = new Interval(calendarEntry.getBeginPeriodDate().getTime(),
                    calendarEntry.getEndPeriodDate().getTime());

            //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
            //change, no warning given to the employee of balance limits being exceeded except on or after that day.

            if (!onDemandTransfers.isEmpty()) {
                for (LeaveBlock lb : onDemandTransfers) {
                    LocalDate leaveDate = lb.getLeaveLocalDate();
                    LeaveSummary summary = LmServiceLocator.getLeaveSummaryService()
                            .getLeaveSummaryAsOfDate(principalId, leaveDate.plusDays(1));
                    LeaveSummaryRow row = summary.getLeaveSummaryRowForAccrualCtgy(lb.getAccrualCategory());
                    if (row != null) {
                        //AccrualCategory accrualCategory = HrServiceLocator.getAccrualCategoryService().getAccrualCategory(row.getAccrualCategoryId());
                        //AccrualCategoryRule currentRule = HrServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRuleForDate(accrualCategory, asOfDate, pha.getServiceDate());
                        if (calendarEntryInterval.contains(leaveDate.toDate().getTime())) {
                            //do not allow the on-demand max balance action if the rule the action occurs under is no longer in effect,
                            //or if the infraction did not occur within this interval. ( if it occurred during the previous interval, 
                            //the employee will have the option to take action in that interval up to & including the end date of that interval. )
                            row.setInfractingLeaveBlockId(lb.getAccrualCategoryRuleId());
                            AccrualCategoryRule aRule = HrServiceLocator.getAccrualCategoryRuleService()
                                    .getAccrualCategoryRule(lb.getAccrualCategoryRuleId());

                            if (StringUtils.equals(aRule.getActionAtMaxBalance(),
                                    HrConstants.ACTION_AT_MAX_BALANCE.TRANSFER))
                                row.setTransferable(true);
                            else if (StringUtils.equals(aRule.getActionAtMaxBalance(),
                                    HrConstants.ACTION_AT_MAX_BALANCE.PAYOUT))
                                row.setPayoutable(true);

                            boolean exists = false;
                            for (LeaveSummaryRow maxedRow : maxedLeaveRows) {
                                if (StringUtils.equals(maxedRow.getAccrualCategoryId(), row.getAccrualCategoryId()))
                                    exists = true;
                            }
                            if (!exists)
                                maxedLeaveRows.add(row);
                        }
                    }
                }
            }
        }
        return maxedLeaveRows;
    }

    /**
      * Aggregates timeblocks into the appropriate earngroup-> earncode -> assignment rows
      * @param tkTimeBlockAggregate
      * @param numEntries
      * @param dayArrangements
      * @param asOfDate
      * @return
      */
    public List<EarnGroupSection> getEarnGroupSections(TkTimeBlockAggregate tkTimeBlockAggregate, int numEntries,
            List<Boolean> dayArrangements, LocalDate asOfDate, LocalDate docEndDate) {
        List<EarnGroupSection> earnGroupSections = new ArrayList<EarnGroupSection>();
        List<FlsaWeek> flsaWeeks = tkTimeBlockAggregate
                .getFlsaWeeks(HrServiceLocator.getTimezoneService().getUserTimezoneWithFallback());
        Map<String, EarnCodeSection> earnCodeToEarnCodeSection = new TreeMap<String, EarnCodeSection>();
        Map<String, EarnGroupSection> earnGroupToEarnGroupSection = new HashMap<String, EarnGroupSection>();

        int dayCount = 0;

        //TODO remove this and correct the aggregate .. not sure what the down stream changes are
        //so leaving this for initial release
        List<FlsaWeek> trimmedFlsaWeeks = new ArrayList<FlsaWeek>();
        for (FlsaWeek flsaWeek : flsaWeeks) {
            if (flsaWeek.getFlsaDays().size() > 0) {
                trimmedFlsaWeeks.add(flsaWeek);
            }
        }

        //For every flsa week and day aggegate each time hour detail 
        // buckets it by earn code section first
        for (FlsaWeek flsaWeek : trimmedFlsaWeeks) {
            int weekSize = 0;
            List<FlsaDay> flsaDays = flsaWeek.getFlsaDays();
            for (FlsaDay flsaDay : flsaDays) {
                Map<String, List<TimeBlock>> earnCodeToTimeBlocks = flsaDay.getEarnCodeToTimeBlocks();

                for (List<TimeBlock> timeBlocks : earnCodeToTimeBlocks.values()) {
                    for (TimeBlock timeBlock : timeBlocks) {
                        for (TimeHourDetail thd : timeBlock.getTimeHourDetails()) {
                            if (StringUtils.equals(HrConstants.LUNCH_EARN_CODE, thd.getEarnCode())) {
                                continue;
                            }
                            EarnCodeSection earnCodeSection = earnCodeToEarnCodeSection.get(thd.getEarnCode());
                            if (earnCodeSection == null) {
                                earnCodeSection = new EarnCodeSection();
                                earnCodeSection.setEarnCode(thd.getEarnCode());
                                EarnCode earnCodeObj = HrServiceLocator.getEarnCodeService()
                                        .getEarnCode(thd.getEarnCode(), asOfDate);
                                earnCodeSection
                                        .setDescription(earnCodeObj != null ? earnCodeObj.getDescription() : null);
                                earnCodeSection.setIsAmountEarnCode(
                                        earnCodeObj != null
                                                ? HrConstants.EARN_CODE_AMOUNT
                                                        .equalsIgnoreCase(earnCodeObj.getRecordMethod())
                                                : false);
                                for (int i = 0; i < (numEntries - 1); i++) {
                                    earnCodeSection.getTotals().add(BigDecimal.ZERO);
                                }

                                earnCodeToEarnCodeSection.put(thd.getEarnCode(), earnCodeSection);
                            }
                            String assignKey = timeBlock.getAssignmentKey();
                            AssignmentRow assignRow = earnCodeSection.getAssignKeyToAssignmentRowMap()
                                    .get(assignKey);
                            if (assignRow == null) {
                                assignRow = new AssignmentRow();
                                assignRow.setAssignmentKey(assignKey);
                                AssignmentDescriptionKey assignmentKey = HrServiceLocator.getAssignmentService()
                                        .getAssignmentDescriptionKey(assignKey);
                                Assignment assignment = HrServiceLocator.getAssignmentService()
                                        .getAssignment(timeBlock.getPrincipalId(), assignmentKey, asOfDate);
                                // some assignment may not be effective at the beginning of the pay period, use the end date of the period to find it
                                if (assignment == null) {
                                    assignment = HrServiceLocator.getAssignmentService()
                                            .getAssignment(timeBlock.getPrincipalId(), assignmentKey, asOfDate);
                                }
                                //TODO push this up to the assignment fetch/fully populated instead of like this
                                if (assignment != null) {
                                    if (assignment.getJob() == null) {
                                        Job aJob = HrServiceLocator.getJobService().getJob(
                                                assignment.getPrincipalId(), assignment.getJobNumber(),
                                                assignment.getEffectiveLocalDate());
                                        assignment.setJob(aJob);
                                    }
                                    if (assignment.getWorkAreaObj() == null) {
                                        WorkArea aWorkArea = HrServiceLocator.getWorkAreaService().getWorkArea(
                                                assignment.getWorkArea(), assignment.getEffectiveLocalDate());
                                        assignment.setWorkAreaObj(aWorkArea);
                                    }
                                    assignRow.setDescr(assignment.getAssignmentDescription());
                                }
                                assignRow.setEarnCodeSection(earnCodeSection);
                                for (int i = 0; i < numEntries - 1; i++) {
                                    assignRow.addAssignmentColumn(new AssignmentColumn());
                                }
                                earnCodeSection.addAssignmentRow(assignRow);
                            }
                            assignRow.addToTotal(dayCount, thd.getHours());
                            assignRow.addToAmount(dayCount, thd.getAmount());
                        }
                    }
                }
                dayCount++;
                weekSize++;
            }
            //end of flsa week accumulate weekly totals
            for (EarnCodeSection earnCodeSection : earnCodeToEarnCodeSection.values()) {
                earnCodeSection.addWeeklyTotal(dayCount, weekSize);
            }
            weekSize = 0;

            dayCount++;
        }

        dayCount = 0;
        //now create all teh earn group sections and aggregate accordingly
        for (EarnCodeSection earnCodeSection : earnCodeToEarnCodeSection.values()) {
            String earnCode = earnCodeSection.getEarnCode();
            EarnCodeGroup earnGroupObj = HrServiceLocator.getEarnCodeGroupService()
                    .getEarnCodeGroupSummaryForEarnCode(earnCode, asOfDate);
            String earnGroup = null;
            if (earnGroupObj == null) {
                earnGroup = OTHER_EARN_GROUP;
            } else {
                earnGroup = earnGroupObj.getDescr();
            }

            EarnGroupSection earnGroupSection = earnGroupToEarnGroupSection.get(earnGroup);
            if (earnGroupSection == null) {
                earnGroupSection = new EarnGroupSection();
                earnGroupSection.setEarnGroup(earnGroup);
                for (int i = 0; i < (numEntries - 1); i++) {
                    earnGroupSection.getTotals().add(BigDecimal.ZERO);
                }
                earnGroupToEarnGroupSection.put(earnGroup, earnGroupSection);
            }
            earnGroupSection.addEarnCodeSection(earnCodeSection, dayArrangements);

        }
        for (EarnGroupSection earnGroupSection : earnGroupToEarnGroupSection.values()) {
            earnGroupSections.add(earnGroupSection);
        }
        return earnGroupSections;
    }

    /**
     * Generate a list of string describing this pay calendar entry for the summary
     * @param payCalEntry
     * @return
     */
    protected List<String> getSummaryHeader(CalendarEntry payCalEntry) {
        List<String> summaryHeader = new ArrayList<String>();
        int dayCount = 0;
        DateTime beginDateTime = payCalEntry.getBeginPeriodFullDateTime();
        DateTime endDateTime = payCalEntry.getEndPeriodFullDateTime();
        boolean virtualDays = false;
        LocalDateTime endDate = payCalEntry.getEndPeriodLocalDateTime();

        if (endDate.get(DateTimeFieldType.hourOfDay()) != 0 || endDate.get(DateTimeFieldType.minuteOfHour()) != 0
                || endDate.get(DateTimeFieldType.secondOfMinute()) != 0) {
            virtualDays = true;
        }

        DateTime currentDateTime = beginDateTime;

        while (currentDateTime.isBefore(endDateTime)) {
            LocalDateTime currDate = currentDateTime.toLocalDateTime();
            summaryHeader.add(makeHeaderDiplayString(currDate, virtualDays));

            dayCount++;
            if ((dayCount % 7) == 0) {
                summaryHeader.add("Week " + ((dayCount / 7)));
            }
            currentDateTime = currentDateTime.plusDays(1);
        }

        summaryHeader.add("Period Total");
        return summaryHeader;
    }

    /**
     * Provides the number of hours worked for the pay period indicated in the
     * aggregate.
     *
     * @param aggregate The aggregate we are summing
     *
     * @return A list of BigDecimals containing the number of hours worked.
     * This list will line up with the header.
     */
    private List<BigDecimal> getWorkedHours(TkTimeBlockAggregate aggregate, Set<String> regularEarnCodes,
            TimeSummary timeSummary) {
        List<BigDecimal> hours = new ArrayList<BigDecimal>();

        Map<String, BigDecimal> weekTotalMap = new LinkedHashMap<String, BigDecimal>();

        BigDecimal periodTotal = HrConstants.BIG_DECIMAL_SCALED_ZERO;
        int i = 0;
        for (FlsaWeek week : aggregate
                .getFlsaWeeks(HrServiceLocator.getTimezoneService().getUserTimezoneWithFallback())) {
            BigDecimal weeklyTotal = HrConstants.BIG_DECIMAL_SCALED_ZERO;
            for (FlsaDay day : week.getFlsaDays()) {
                BigDecimal totalForDay = HrConstants.BIG_DECIMAL_SCALED_ZERO;
                for (TimeBlock block : day.getAppliedTimeBlocks()) {
                    EarnCode ec = HrServiceLocator.getEarnCodeService().getEarnCode(block.getEarnCode(),
                            block.getEndDateTime().toLocalDate());
                    if (ec != null && (ec.getOvtEarnCode() || regularEarnCodes.contains(ec.getEarnCode())
                            || ec.getCountsAsRegularPay().equals("Y"))) {
                        totalForDay = totalForDay.add(block.getHours(), HrConstants.MATH_CONTEXT);
                        weeklyTotal = weeklyTotal.add(block.getHours(), HrConstants.MATH_CONTEXT);
                        periodTotal = periodTotal.add(block.getHours(), HrConstants.MATH_CONTEXT);
                    }
                }
                hours.add(totalForDay);
            }
            i++;
            weekTotalMap.put("Week " + i, weeklyTotal);
            hours.add(weeklyTotal);
        }
        hours.add(periodTotal);
        timeSummary.setWeekTotalMap(weekTotalMap);
        return hours;
    }

    /**
     * Handles the generation of the display header for the time summary.
     *
     * @param cal The PayCalendarEntries object we are using to derive information.
     * @param dayArrangements Container passed in to store the position of week / period aggregate sums
     *
     * @return An in-order string of days for this period that properly accounts
     * for FLSA week boundaries in the pay period.
     */
    @Override
    public List<String> getHeaderForSummary(CalendarEntry cal, List<Boolean> dayArrangements) {
        List<String> header = new ArrayList<String>();
        if (cal == null) {
            return Collections.emptyList();
        }
        // Maps directly to joda time day of week constants.
        int flsaBeginDay = this.getPayCalendarForEntry(cal).getFlsaBeginDayConstant();
        boolean virtualDays = false;
        LocalDateTime startDate = cal.getBeginPeriodLocalDateTime();
        LocalDateTime endDate = cal.getEndPeriodLocalDateTime();

        // Increment end date if we are on a virtual day calendar, so that the
        // for loop can account for having the proper amount of days on the
        // summary calendar.
        if (endDate.get(DateTimeFieldType.hourOfDay()) != 0 || endDate.get(DateTimeFieldType.minuteOfHour()) != 0
                || endDate.get(DateTimeFieldType.secondOfMinute()) != 0) {
            endDate = endDate.plusDays(1);
            virtualDays = true;
        }

        boolean afterFirstDay = false;
        int week = 1;
        for (LocalDateTime currentDate = startDate; currentDate.compareTo(endDate) < 0; currentDate = currentDate
                .plusDays(1)) {

            if (currentDate.getDayOfWeek() == flsaBeginDay && afterFirstDay) {
                header.add("Week " + week);
                dayArrangements.add(false);
                week++;
            }

            header.add(makeHeaderDiplayString(currentDate, virtualDays));
            dayArrangements.add(true);

            afterFirstDay = true;
        }

        // We may have a very small final "week" on this pay period. For now
        // we will mark it as a week, and if someone doesn't like it, it can
        // be removed.
        if (!header.isEmpty() && !header.get(header.size() - 1).startsWith("Week")) {
            dayArrangements.add(false);
            header.add("Week " + week);
        }

        header.add("Period Total");
        dayArrangements.add(false);
        return header;
    }

    /**
     * Helper function to generate display text for the summary header.
     * @param currentDate The date we are generating for.
     * @param virtualDays Whether or not virtual days apply.
     * @return A string appropriate for UI display.
     */
    private String makeHeaderDiplayString(LocalDateTime currentDate, boolean virtualDays) {
        StringBuilder display = new StringBuilder();

        display.append(currentDate.toString("E"));
        if (virtualDays) {
            LocalDateTime nextDate = currentDate.plusDays(1);
            display.append(" - ");
            display.append(nextDate.toString("E"));
        }

        display.append("<br />");

        display.append(currentDate.toString(TkConstants.DT_ABBREV_DATE_FORMAT));
        if (virtualDays) {
            LocalDateTime nextDate = currentDate.plusDays(1);
            display.append(" - ");
            display.append(nextDate.toString(TkConstants.DT_ABBREV_DATE_FORMAT));
        }

        return display.toString();
    }

    /**
     * @param calEntry Calendar entry we are using for lookup.
     * @return The PayCalendar that owns the provided entry.
     */
    private Calendar getPayCalendarForEntry(CalendarEntry calEntry) {
        Calendar cal = null;

        if (calEntry != null) {
            cal = HrServiceLocator.getCalendarService().getCalendar(calEntry.getHrCalendarId());
        }

        return cal;
    }

    private Map<String, BigDecimal> getHoursToFlsaWeekMap(TkTimeBlockAggregate tkTimeBlockAggregate,
            String principalId, Long workArea) {

        Map<String, BigDecimal> hoursToFlsaWeekMap = new LinkedHashMap<String, BigDecimal>();
        DateTimeZone dateTimeZone = HrServiceLocator.getTimezoneService().getUserTimezoneWithFallback();
        List<List<FlsaWeek>> flsaWeeks = tkTimeBlockAggregate.getFlsaWeeks(dateTimeZone, principalId);

        int weekCount = 1;
        for (List<FlsaWeek> flsaWeekParts : flsaWeeks) {
            BigDecimal weekTotal = new BigDecimal(0.00);
            for (FlsaWeek flsaWeekPart : flsaWeekParts) {
                for (FlsaDay flsaDay : flsaWeekPart.getFlsaDays()) {
                    for (TimeBlock timeBlock : flsaDay.getAppliedTimeBlocks()) {
                        if (workArea != null) {
                            if (timeBlock.getWorkArea().compareTo(workArea) == 0) {
                                weekTotal = weekTotal.add(timeBlock.getHours(), HrConstants.MATH_CONTEXT);
                            } else {
                                weekTotal = weekTotal.add(new BigDecimal("0"), HrConstants.MATH_CONTEXT);
                            }
                        } else {
                            weekTotal = weekTotal.add(timeBlock.getHours(), HrConstants.MATH_CONTEXT);
                        }
                    }
                }
            }
            hoursToFlsaWeekMap.put("Week " + weekCount++, weekTotal);
        }

        return hoursToFlsaWeekMap;
    }
}