org.kuali.kpme.tklm.time.timeblock.service.TimeBlockServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.kpme.tklm.time.timeblock.service.TimeBlockServiceImpl.java

Source

/**
 * Copyright 2004-2014 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.timeblock.service;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Interval;
import org.joda.time.LocalDate;
import org.kuali.kpme.core.api.assignment.Assignment;
import org.kuali.kpme.core.api.assignment.AssignmentDescriptionKey;
import org.kuali.kpme.core.api.block.CalendarBlockPermissions;
import org.kuali.kpme.core.api.calendar.entry.CalendarEntry;
import org.kuali.kpme.core.api.earncode.EarnCode;
import org.kuali.kpme.core.api.earncode.security.EarnCodeSecurityContract;
import org.kuali.kpme.core.api.job.JobContract;
import org.kuali.kpme.core.api.namespace.KPMENamespace;
import org.kuali.kpme.core.api.paytype.PayType;
import org.kuali.kpme.core.api.principal.PrincipalHRAttributes;
import org.kuali.kpme.core.role.KPMERole;
import org.kuali.kpme.core.service.HrServiceLocator;
import org.kuali.kpme.core.util.HrConstants;
import org.kuali.kpme.core.util.HrContext;
import org.kuali.kpme.core.util.TKUtils;
import org.kuali.kpme.tklm.api.common.TkConstants;
import org.kuali.kpme.tklm.api.leave.timeoff.SystemScheduledTimeOffContract;
import org.kuali.kpme.tklm.api.time.timeblock.TimeBlock;
import org.kuali.kpme.tklm.api.time.timeblock.TimeBlockService;
import org.kuali.kpme.tklm.api.time.timehourdetail.TimeHourDetail;
import org.kuali.kpme.tklm.leave.service.LmServiceLocator;
import org.kuali.kpme.tklm.time.service.TkServiceLocator;
import org.kuali.kpme.tklm.time.timeblock.TimeBlockBo;
import org.kuali.kpme.tklm.time.timeblock.TimeBlockHistory;
import org.kuali.kpme.tklm.time.timeblock.dao.TimeBlockDao;
import org.kuali.kpme.tklm.time.timehourdetail.TimeHourDetailBo;
import org.kuali.kpme.tklm.time.workflow.TimesheetDocumentHeader;
import org.kuali.rice.core.api.mo.ModelObjectUtils;
import org.kuali.rice.kns.service.KNSServiceLocator;
import org.kuali.rice.kew.api.KewApiServiceLocator;
import org.kuali.rice.kew.api.note.Note;
import org.kuali.rice.kim.api.services.KimApiServiceLocator;
import org.kuali.rice.krad.service.KRADServiceLocator;
import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
import org.kuali.rice.krad.util.GlobalVariables;

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

public class TimeBlockServiceImpl implements TimeBlockService {

    private static final Logger LOG = Logger.getLogger(TimeBlockServiceImpl.class);
    private static final ModelObjectUtils.Transformer<TimeBlockBo, TimeBlock> toTimeBlock = new ModelObjectUtils.Transformer<TimeBlockBo, TimeBlock>() {
        public TimeBlock transform(TimeBlockBo input) {
            return TimeBlockBo.to(input);
        };
    };
    private static final ModelObjectUtils.Transformer<TimeBlock, TimeBlockBo> toTimeBlockBo = new ModelObjectUtils.Transformer<TimeBlock, TimeBlockBo>() {
        public TimeBlockBo transform(TimeBlock input) {
            return TimeBlockBo.from(input);
        };
    };
    private TimeBlockDao timeBlockDao;

    public void setTimeBlockDao(TimeBlockDao timeBlockDao) {
        this.timeBlockDao = timeBlockDao;
    }

    //This function is used to build timeblocks that span days
    @Override
    public List<TimeBlock> buildTimeBlocksSpanDates(String principalId, CalendarEntry calendarEntry,
            Assignment assignment, String earnCode, String documentId, DateTime beginDateTime, DateTime endDateTime,
            BigDecimal hours, BigDecimal amount, Boolean getClockLogCreated, Boolean getLunchDeleted,
            String userPrincipalId, String clockLogBeginId, String clockLogEndId) {
        DateTimeZone zone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
        DateTime beginDt = beginDateTime.withZone(zone);
        DateTime endDt = beginDt.toLocalDate().toDateTime(endDateTime.withZone(zone).toLocalTime(), zone);
        if (endDt.isBefore(beginDt) || endDt.isEqual(beginDt.toDateMidnight()))
            endDt = endDt.plusDays(1);

        List<Interval> dayInt = TKUtils.getDaySpanForCalendarEntry(calendarEntry);
        TimeBlockBo firstTimeBlock = new TimeBlockBo();
        List<TimeBlockBo> lstTimeBlocks = new ArrayList<TimeBlockBo>();

        DateTime endOfFirstDay = null; // KPME-2568
        long diffInMillis = 0; // KPME-2568

        for (Interval dayIn : dayInt) {
            if (dayIn.contains(beginDt)) {
                if (dayIn.contains(endDt) || dayIn.getEnd().equals(endDt)) {
                    firstTimeBlock = TimeBlockBo.from(createTimeBlock(principalId, documentId, beginDateTime, endDt,
                            assignment, earnCode, hours, amount, getClockLogCreated, getLunchDeleted,
                            userPrincipalId, clockLogBeginId, clockLogEndId));
                    lstTimeBlocks.add(firstTimeBlock);
                } else {
                    //TODO move this to prerule validation
                    //throw validation error if this case met error
                }
            }
        }

        DateTime endTime = endDateTime.withZone(zone);
        if (firstTimeBlock.getEndDateTime() != null) { // KPME-2568
            endOfFirstDay = firstTimeBlock.getEndDateTime().withZone(zone);
            diffInMillis = endOfFirstDay.minus(beginDt.getMillis()).getMillis();
        }
        DateTime currTime = beginDt.plusDays(1);
        while (currTime.isBefore(endTime) || currTime.isEqual(endTime)) {
            TimeBlockBo tb = TimeBlockBo.from(createTimeBlock(principalId, documentId, currTime,
                    currTime.plus(diffInMillis), assignment, earnCode, hours, amount, getClockLogCreated,
                    getLunchDeleted, userPrincipalId, clockLogBeginId, clockLogEndId));
            lstTimeBlocks.add(tb);
            currTime = currTime.plusDays(1);
        }
        return ModelObjectUtils.transform(lstTimeBlocks, toTimeBlock);
    }

    public List<TimeBlock> buildTimeBlocks(String principalId, CalendarEntry calendarEntry, Assignment assignment,
            String earnCode, String documentId, DateTime beginDateTime, DateTime endDateTime, BigDecimal hours,
            BigDecimal amount, Boolean getClockLogCreated, Boolean getLunchDeleted, String userPrincipalId,
            String clockLogBeginId, String clockLogEndId) {

        //Create 1 or many timeblocks if the span of timeblocks exceed more than one
        //day that is determined by pay period day(24 hrs + period begin date)
        Interval firstDay = null;
        DateTimeZone userTimeZone = DateTimeZone
                .forID(HrServiceLocator.getTimezoneService().getUserTimezone(principalId));
        if (userTimeZone == null)
            userTimeZone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();

        List<Interval> dayIntervals = TKUtils.getDaySpanForCalendarEntry(calendarEntry, userTimeZone);
        //        List<Interval> dayIntervals = TKUtils.getDaySpanForCalendarEntry(timesheetDocument.getCalendarEntry());
        List<TimeBlock> lstTimeBlocks = new ArrayList<TimeBlock>();
        DateTime currentDateTime = beginDateTime;

        for (Interval dayInt : dayIntervals) {
            // the time period spans more than one day
            if (firstDay != null) {
                if (!dayInt.contains(endDateTime)) {
                    currentDateTime = dayInt.getStart();
                } else if ((dayInt.getStartMillis() - endDateTime.getMillis()) != 0) {
                    TimeBlock tb = createTimeBlock(principalId, documentId, dayInt.getStart(), endDateTime,
                            assignment, earnCode, hours, amount, getClockLogCreated, getLunchDeleted,
                            userPrincipalId, clockLogBeginId, clockLogEndId);
                    lstTimeBlocks.add(tb);
                    break;
                }
            }
            if (dayInt.contains(currentDateTime)) {
                firstDay = dayInt;
                // KPME-361
                // added a condition to handle the time block which ends at 12a, e.g. a 10p-12a timeblock
                // this is the same fix as TkTimeBlockAggregate
                if (dayInt.contains(endDateTime) || (endDateTime.getMillis() == dayInt.getEnd().getMillis())) {
                    //create one timeblock if contained in one day interval
                    TimeBlock.Builder tb = TimeBlock.Builder.create(createTimeBlock(principalId, documentId,
                            currentDateTime, endDateTime, assignment, earnCode, hours, amount, getClockLogCreated,
                            getLunchDeleted, userPrincipalId, clockLogBeginId, clockLogEndId));
                    tb.setBeginDateTime(currentDateTime);
                    tb.setEndDateTime(endDateTime);
                    lstTimeBlocks.add(tb.build());
                    break;
                } else {
                    // create a timeblock that wraps the 24 hr day
                    TimeBlock.Builder tb = TimeBlock.Builder.create(createTimeBlock(principalId, documentId,
                            currentDateTime, dayInt.getEnd(), assignment, earnCode, hours, amount,
                            getClockLogCreated, getLunchDeleted, userPrincipalId, clockLogBeginId, clockLogEndId));
                    tb.setBeginDateTime(currentDateTime);
                    tb.setEndDateTime(firstDay.getEnd());
                    lstTimeBlocks.add(tb.build());
                }
            }
        }
        return lstTimeBlocks;
    }

    @Override
    public List<TimeBlock> saveOrUpdateTimeBlocks(List<TimeBlock> oldTimeBlocks, List<TimeBlock> newTimeBlocks,
            String userPrincipalId) {
        List<TimeBlockBo> alteredTimeBlocks = new ArrayList<TimeBlockBo>();

        List<TimeBlockBo> oldTimeBlockBos = ModelObjectUtils.transform(oldTimeBlocks, toTimeBlockBo);
        List<TimeBlockBo> newTimeBlockBos = ModelObjectUtils.transform(newTimeBlocks, toTimeBlockBo);
        for (TimeBlockBo tb : newTimeBlockBos) {
            boolean persist = true;
            for (TimeBlockBo tbOld : oldTimeBlockBos) {
                HrServiceLocator.getHRPermissionService()
                        .updateTimeBlockPermissions(CalendarBlockPermissions.newInstance(tbOld.getTkTimeBlockId()));
                if (tb.equals(tbOld)) {
                    persist = false;
                    break;
                }
            }
            if (persist) {
                alteredTimeBlocks.add(tb);
            }
        }

        Set<String> timeBlockIds = new HashSet<String>();

        for (TimeBlockBo timeBlock : alteredTimeBlocks) {
            if (timeBlock.getTkTimeBlockId() != null) {
                timeBlockIds.add(timeBlock.getTkTimeBlockId());
            }
            TkServiceLocator.getTimeHourDetailService().removeTimeHourDetails(timeBlock.getTkTimeBlockId());
            timeBlock.setUserPrincipalId(userPrincipalId);
        }

        //List<TimeBlockBo> savedTimeBlocks = timeBlockDao.saveOrUpdate(alteredTimeBlocks);
        List<TimeBlockBo> savedTimeBlocks = (List<TimeBlockBo>) KNSServiceLocator.getBusinessObjectService()
                .save(alteredTimeBlocks);

        for (TimeBlockBo timeBlock : savedTimeBlocks) {
            if (!timeBlockIds.contains(timeBlock.getTkTimeBlockId())) {
                timeBlock.setTimeBlockHistories(
                        createTimeBlockHistories(timeBlock, TkConstants.ACTIONS.ADD_TIME_BLOCK));
                //Add a note to timesheet if approver has added the timeblock
                addNote(timeBlock, "added");
                KNSServiceLocator.getBusinessObjectService().save(timeBlock.getTimeBlockHistories());
            } else {
                timeBlock.setTimeBlockHistories(
                        createTimeBlockHistories(timeBlock, TkConstants.ACTIONS.UPDATE_TIME_BLOCK));
                //Add a note to timesheet if approver has updated the timeblock
                addNote(timeBlock, "updated");
                KNSServiceLocator.getBusinessObjectService().save(timeBlock.getTimeBlockHistories());
            }
        }
        return ModelObjectUtils.transform(savedTimeBlocks, toTimeBlock);
    }

    @Override
    public List<TimeBlock> saveTimeBlocks(List<TimeBlock> tbList) {
        List<TimeBlockBo> savedTimeBlocks = new ArrayList<TimeBlockBo>();
        for (TimeBlock tb : tbList) {
            TimeBlockBo tbBo = TimeBlockBo.from(tb);
            if (StringUtils.isNotEmpty(tb.getTkTimeBlockId())) {
                HrServiceLocator.getHRPermissionService()
                        .updateTimeBlockPermissions(CalendarBlockPermissions.newInstance(tb.getTkTimeBlockId()));
            }
            TkServiceLocator.getTimeHourDetailService().removeTimeHourDetails(tb.getTkTimeBlockId());
            savedTimeBlocks.add(KNSServiceLocator.getBusinessObjectService().save(tbBo));
            for (TimeBlockHistory tbh : tbBo.getTimeBlockHistories()) {
                TkServiceLocator.getTimeBlockHistoryService().saveTimeBlockHistory(tbh);
            }
        }

        return ModelObjectUtils.transform(savedTimeBlocks, toTimeBlock);
    }

    @Override
    public TimeBlock updateTimeBlock(TimeBlock tb) {
        if (tb == null) {
            return null;
        }
        return TimeBlockBo.to(KNSServiceLocator.getBusinessObjectService().save(TimeBlockBo.from(tb)));
    }

    @Override
    public TimeBlock createTimeBlock(String principalId, String documentId, DateTime beginDateTime,
            DateTime endDateTime, Assignment assignment, String earnCode, BigDecimal hours, BigDecimal amount,
            Boolean clockLogCreated, Boolean lunchDeleted, String userPrincipalId) {
        DateTimeZone timezone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
        EarnCode earnCodeObj = HrServiceLocator.getEarnCodeService().getEarnCode(earnCode,
                beginDateTime.toLocalDate());

        TimeBlockBo tb = new TimeBlockBo();
        tb.setDocumentId(documentId);
        tb.setPrincipalId(principalId);
        tb.setJobNumber(assignment.getJobNumber());
        tb.setWorkArea(assignment.getWorkArea());
        tb.setTask(assignment.getTask());
        tb.setGroupKeyCode(assignment.getGroupKeyCode());
        tb.setEarnCode(earnCode);
        tb.setBeginDateTime(beginDateTime);
        tb.setEndDateTime(endDateTime);
        tb.setBeginTimeDisplay(tb.getBeginDateTime().withZone(timezone));
        tb.setEndTimeDisplay(tb.getEndDateTime().withZone(timezone));
        // only calculate the hours from the time fields if the passed in hour is zero
        if (hours == null || hours.compareTo(BigDecimal.ZERO) == 0) {
            hours = TKUtils.getHoursBetween(beginDateTime.getMillis(), endDateTime.getMillis());
        }
        tb.setAmount(amount);
        //If earn code has an inflate min hours check if it is greater than zero
        //and compare if the hours specified is less than min hours awarded for this
        //earn code

        tb.setEarnCodeType(earnCodeObj.getEarnCodeType());
        tb.setHours(hours);
        tb.setClockLogCreated(clockLogCreated);
        tb.setUserPrincipalId(userPrincipalId);
        tb.setTimestamp(TKUtils.getCurrentTimestamp());
        tb.setLunchDeleted(lunchDeleted);

        tb.setTimeHourDetails(this.createTimeHourDetails(earnCodeObj, hours, amount, tb.getTkTimeBlockId(), true));

        return TimeBlockBo.to(tb);
    }

    protected TimeBlock createTimeBlock(String principalId, String documentId, DateTime beginDateTime,
            DateTime endDateTime, Assignment assignment, String earnCode, BigDecimal hours, BigDecimal amount,
            Boolean clockLogCreated, Boolean lunchDeleted, String userPrincipalId, String clockLogBeginId,
            String clockLogEndId) {
        TimeBlock.Builder tb = TimeBlock.Builder.create(createTimeBlock(principalId, documentId, beginDateTime,
                endDateTime, assignment, earnCode, hours, amount, clockLogCreated, lunchDeleted, userPrincipalId));
        TimeBlockBo bo = TimeBlockBo.from(tb.build());
        bo.setClockLogBeginId(clockLogBeginId);
        bo.setClockLogEndId(clockLogEndId);

        return TimeBlockBo.to(bo);
    }

    @Override
    public TimeBlock getTimeBlock(String tkTimeBlockId) {
        return TimeBlockBo.to(getTimeBlockBo(tkTimeBlockId));
    }

    protected TimeBlockBo getTimeBlockBo(String tkTimeBlockId) {
        return timeBlockDao.getTimeBlock(tkTimeBlockId);
    }

    @Override
    public void deleteTimeBlock(TimeBlock timeBlock) {
        TimeBlockBo timeBlockBo = TimeBlockBo.from(timeBlock);
        //Add note to timesheet if approver deleted the timeblock.
        addNote(timeBlockBo, "deleted");
        KNSServiceLocator.getBusinessObjectService().delete(TimeBlockBo.from(timeBlock));
    }

    //Add a note to timesheet for approver's actions
    public void addNote(TimeBlockBo timeBlock, String actionMessage) {
        if (!HrContext.getPrincipalId().equals(timeBlock.getPrincipalId())) {
            Note.Builder builder = Note.Builder.create(timeBlock.getDocumentId(), timeBlock.getUserPrincipalId());
            builder.setCreateDate(new DateTime());
            String principalName = KimApiServiceLocator.getIdentityService()
                    .getDefaultNamesForPrincipalId(HrContext.getPrincipalId()).getDefaultName().getCompositeName();
            builder.setText("Timeblock on " + timeBlock.getBeginDateTime() + " was " + actionMessage
                    + " on this timesheet by " + principalName + " on your behalf");
            KewApiServiceLocator.getNoteService().createNote(builder.build());
        }
    }

    @Override
    public List<TimeBlock> resetTimeHourDetail(List<TimeBlock> origTimeBlocks) {
        List<TimeBlockBo> originalBos = ModelObjectUtils.transform(origTimeBlocks, toTimeBlockBo);
        Collections.sort(originalBos, new Comparator<TimeBlockBo>() {

            @Override
            public int compare(TimeBlockBo o1, TimeBlockBo o2) {
                return o1.getEndDateTime().compareTo(o2.getEndDateTime().toInstant());
            }

        });
        TimeBlockBo previousTimeBlock = null;
        for (TimeBlockBo tb : originalBos) {
            EarnCode earnCodeObj = HrServiceLocator.getEarnCodeService().getEarnCode(tb.getEarnCode(),
                    tb.getBeginDateTime().toLocalDate());
            if (tb.getBeginTime() != null && tb.getEndTime() != null
                    && StringUtils.equals(tb.getEarnCodeType(), HrConstants.EARN_CODE_TIME)) {
                BigDecimal hours = TKUtils.getHoursBetween(tb.getBeginDateTime().getMillis(),
                        tb.getEndDateTime().getMillis());

                //If earn code has an inflate min hours check if it is greater than zero
                //and compare if the hours specified is less than min hours awarded for this
                //earn code
                if (previousTimeBlock != null
                        && StringUtils.equals(earnCodeObj.getEarnCode(), previousTimeBlock.getEarnCode())
                        && (tb.getBeginTime().getMillisOfDay()
                                - previousTimeBlock.getEndTime().getMillisOfDay() == 0L)) {
                    List<TimeHourDetailBo> newDetails = new ArrayList<TimeHourDetailBo>();
                    BigDecimal prevTimeBlockHours = TKUtils.getHoursBetween(
                            previousTimeBlock.getBeginDateTime().getMillis(),
                            previousTimeBlock.getEndDateTime().getMillis());
                    previousTimeBlock.setHours(prevTimeBlockHours);
                    BigDecimal cummulativeHours = prevTimeBlockHours.add(hours, HrConstants.MATH_CONTEXT);
                    //remove any inflation done when resetting the previous time block's hours.
                    previousTimeBlock.setTimeHourDetails(this.createTimeHourDetails(earnCodeObj, prevTimeBlockHours,
                            previousTimeBlock.getAmount(), previousTimeBlock.getTkTimeBlockId(), false));

                    if (earnCodeObj.getInflateMinHours() != null) {
                        if ((earnCodeObj.getInflateMinHours().compareTo(BigDecimal.ZERO) != 0)
                                && earnCodeObj.getInflateMinHours().compareTo(cummulativeHours) > 0) {
                            //if previous timeblock has no gap then assume its one block if the same earn code and divide inflated hours accordingly

                            //add previous time block's hours to this time blocks hours. If the cummulative hours is less than the value for inflate min,
                            //create time hour detail with hours equal to the hours needed to reach inflate min plus the hours for this time block.
                            if (earnCodeObj.getInflateMinHours().compareTo(cummulativeHours) > 0) {
                                //apply inflations to the cummulative hours
                                newDetails = this.createTimeHourDetails(earnCodeObj, cummulativeHours,
                                        tb.getAmount(), tb.getTkTimeBlockId(), false);
                                TimeHourDetailBo detail = newDetails.get(0);
                                //this detail's hours will be the cummulative inflated hours less the hours in previous time block detail's hours.
                                detail.setHours(earnCodeObj.getInflateMinHours().subtract(prevTimeBlockHours));
                                newDetails.clear();
                                newDetails.add(detail);
                                tb.setTimeHourDetails(newDetails);
                            }
                        }
                    }
                    //the hours for this time block may be under the inflate factor, but when combined with the hours from first "part"
                    //of their shift, it is over, thus no inflation should be requested of the time hour detail's hours.
                    if (earnCodeObj.getInflateFactor() != null
                            && earnCodeObj.getInflateFactor().compareTo(BigDecimal.ZERO) != 0) {
                        //apply inflate factor separately so as to not inflate "hours" if it is under the minimum
                        TimeHourDetailBo detail = new TimeHourDetailBo();
                        if (newDetails.isEmpty()) {
                            //populate some default values...
                            newDetails = this.createTimeHourDetails(earnCodeObj, hours, tb.getAmount(),
                                    tb.getTkTimeBlockId(), false);
                        }
                        detail = newDetails.get(0);
                        BigDecimal newHours = detail.getHours().multiply(earnCodeObj.getInflateFactor(),
                                HrConstants.MATH_CONTEXT);
                        detail.setHours(newHours);
                        newDetails.clear();
                        newDetails.add(detail);
                        tb.setTimeHourDetails(newDetails);
                        newDetails = previousTimeBlock.getTimeHourDetails();
                        detail = newDetails.get(0);
                        detail.setHours(prevTimeBlockHours.multiply(earnCodeObj.getInflateFactor(),
                                HrConstants.MATH_CONTEXT));
                        newDetails.clear();
                        newDetails.add(detail);
                        previousTimeBlock.setTimeHourDetails(newDetails);
                    }
                } else {
                    tb.setTimeHourDetails(this.createTimeHourDetails(earnCodeObj, tb.getHours(), tb.getAmount(),
                            tb.getTkTimeBlockId(), true));
                }

                tb.setHours(hours);
            } else {
                // create new time hour details for earn codes of other types
                tb.setTimeHourDetails(this.createTimeHourDetails(earnCodeObj, tb.getHours(), tb.getAmount(),
                        tb.getTkTimeBlockId(), true));
            }
            //reset time block history details
            for (TimeBlockHistory tbh : tb.getTimeBlockHistories()) {
                TkServiceLocator.getTimeBlockHistoryService().addTimeBlockHistoryDetails(tbh, tb);
            }
            previousTimeBlock = tb;
        }
        return ModelObjectUtils.transform(originalBos, toTimeBlock);
    }

    private List<TimeHourDetailBo> createTimeHourDetails(EarnCode earnCode, BigDecimal hours, BigDecimal amount,
            String timeBlockId, boolean inflate) {
        List<TimeHourDetailBo> timeHourDetails = new ArrayList<TimeHourDetailBo>();

        TimeHourDetailBo timeHourDetail = new TimeHourDetailBo();
        timeHourDetail.setEarnCode(earnCode.getEarnCode());
        if (inflate) {
            timeHourDetail.setHours(this.applyInflateMinHoursAndFactor(earnCode, hours));
        } else {
            timeHourDetail.setHours(hours);
        }
        timeHourDetail.setAmount(amount);
        timeHourDetail.setTkTimeBlockId(timeBlockId);
        timeHourDetails.add(timeHourDetail);

        return timeHourDetails;
    }

    protected List<TimeBlockHistory> createTimeBlockHistories(TimeBlockBo tb, String actionHistory) {
        List<TimeBlockHistory> tbhs = new ArrayList<TimeBlockHistory>();

        TimeBlockHistory tbh = new TimeBlockHistory(tb);
        tbh.setActionHistory(actionHistory);
        // add time block history details to this time block history
        TkServiceLocator.getTimeBlockHistoryService().addTimeBlockHistoryDetails(tbh, tb);

        tbhs.add(tbh);

        return tbhs;
    }

    // This method now translates time based on timezone settings.
    //
    public List<TimeBlockBo> getInitialTimeBlocks(String documentId) {
        List<TimeBlockBo> timeBlocks = timeBlockDao.getTimeBlocks(documentId);
        TimesheetDocumentHeader tdh = TkServiceLocator.getTimesheetDocumentHeaderService()
                .getDocumentHeader(documentId);
        DateTimeZone timezone = DateTimeZone
                .forID(HrServiceLocator.getTimezoneService().getUserTimezone(tdh.getPrincipalId()));
        if (timezone == null) {
            timezone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
        }
        for (TimeBlockBo tb : timeBlocks) {
            String earnCodeType = HrServiceLocator.getEarnCodeService().getEarnCodeType(tb.getEarnCode(),
                    tb.getBeginDateTime().toLocalDate());
            tb.setEarnCodeType(earnCodeType);
            //            tb.assignClockedByMissedPunch();
            if (ObjectUtils.equals(timezone, TKUtils.getSystemDateTimeZone())) {
                tb.setBeginTimeDisplay(tb.getBeginDateTime());
                tb.setEndTimeDisplay(tb.getEndDateTime());
            } else {
                tb.setBeginTimeDisplay(tb.getBeginDateTime().withZone(timezone));
                tb.setEndTimeDisplay(tb.getEndDateTime().withZone(timezone));
            }
        }
        return timeBlocks;
    }

    public List<TimeBlock> getTimeBlocks(String documentId) {
        return ModelObjectUtils.transform(getInitialTimeBlocks(documentId), toTimeBlock);
    }

    public List<TimeBlock> getTimeBlocksWithMissedPunchInfo(String documentId) {
        List<TimeBlockBo> tbs = getInitialTimeBlocks(documentId);
        for (TimeBlockBo tb : tbs) {
            tb.assignClockedByMissedPunch();
        }
        return ModelObjectUtils.transform(tbs, toTimeBlock);
    }

    /*    @Override
        public List<TimeBlock> getTimeBlocksForAssignment(AssignmentContract assign) {
           List<TimeBlockBo> timeBlocks = new ArrayList<TimeBlockBo>();
           if(assign != null) {
       timeBlocks = timeBlockDao.getTimeBlocksForAssignment(assign);
           }
    DateTimeZone timezone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
    for(TimeBlockBo tb : timeBlocks) {
         String earnCodeType = HrServiceLocator.getEarnCodeService().getEarnCodeType(tb.getEarnCode(), tb.getBeginDateTime().toLocalDate());
         tb.setEarnCodeType(earnCodeType);
      if(ObjectUtils.equals(timezone, TKUtils.getSystemDateTimeZone())){
        tb.setBeginTimeDisplay(tb.getBeginDateTime());
        tb.setEndTimeDisplay(tb.getEndDateTime());
     }
     else {
        tb.setBeginTimeDisplay(tb.getBeginDateTime().withZone(timezone));
        tb.setEndTimeDisplay(tb.getEndDateTime().withZone(timezone));
     }
     }
           return ModelObjectUtils.transform(timeBlocks, toTimeBlock);
        }*/

    @Override
    public void deleteTimeBlocksAssociatedWithDocumentId(String documentId) {
        timeBlockDao.deleteTimeBlocksAssociatedWithDocumentId(documentId);
    }

    @Override
    // figure out if the user has permission to edit/delete the time block
    public Boolean getTimeBlockEditable(TimeBlock timeBlock) {
        String userId = GlobalVariables.getUserSession().getPrincipalId();

        if (userId != null) {

            if (HrContext.isSystemAdmin()) {
                return true;
            }
            DateTime date = LocalDate.now().toDateTimeAtStartOfDay();
            if (HrServiceLocator.getKPMERoleService().principalHasRoleInWorkArea(userId,
                    KPMENamespace.KPME_HR.getNamespaceCode(), KPMERole.REVIEWER.getRoleName(),
                    timeBlock.getWorkArea(), date)
                    || HrServiceLocator.getKPMERoleService().principalHasRoleInWorkArea(userId,
                            KPMENamespace.KPME_HR.getNamespaceCode(), KPMERole.APPROVER_DELEGATE.getRoleName(),
                            timeBlock.getWorkArea(), date)
                    || HrServiceLocator.getKPMERoleService().principalHasRoleInWorkArea(userId,
                            KPMENamespace.KPME_HR.getNamespaceCode(), KPMERole.APPROVER.getRoleName(),
                            timeBlock.getWorkArea(), date)) {

                JobContract job = HrServiceLocator.getJobService().getJob(HrContext.getTargetPrincipalId(),
                        timeBlock.getJobNumber(), timeBlock.getEndDateTime().toLocalDate());
                PayType payType = HrServiceLocator.getPayTypeService().getPayType(job.getHrPayType(),
                        timeBlock.getEndDateTime().toLocalDate());
                if (StringUtils.equals(payType.getRegEarnCode(), timeBlock.getEarnCode())) {
                    return true;
                }

                List<? extends EarnCodeSecurityContract> deptEarnCodes = HrServiceLocator
                        .getEarnCodeSecurityService().getEarnCodeSecurities(job.getDept(), job.getHrSalGroup(),
                                timeBlock.getEndDateTime().toLocalDate(), job.getGroupKey().getGroupKeyCode());
                for (EarnCodeSecurityContract dec : deptEarnCodes) {
                    if (dec.isApprover() && StringUtils.equals(dec.getEarnCode(), timeBlock.getEarnCode())) {
                        return true;
                    }
                }
            }

            if (userId.equals(HrContext.getTargetPrincipalId())) {
                JobContract job = HrServiceLocator.getJobService().getJob(HrContext.getTargetPrincipalId(),
                        timeBlock.getJobNumber(), timeBlock.getEndDateTime().toLocalDate());
                PayType payType = HrServiceLocator.getPayTypeService().getPayType(job.getHrPayType(),
                        timeBlock.getEndDateTime().toLocalDate());
                if (StringUtils.equals(payType.getRegEarnCode(), timeBlock.getEarnCode())) {
                    return true;
                }

                List<? extends EarnCodeSecurityContract> deptEarnCodes = HrServiceLocator
                        .getEarnCodeSecurityService().getEarnCodeSecurities(job.getDept(), job.getHrSalGroup(),
                                timeBlock.getEndDateTime().toLocalDate(), job.getGroupKey().getGroupKeyCode());
                for (EarnCodeSecurityContract dec : deptEarnCodes) {
                    if (dec.isEmployee() && StringUtils.equals(dec.getEarnCode(), timeBlock.getEarnCode())) {
                        return true;
                    }
                }
                // if the user is the creator of this time block
            }

        }
        return false;
    }

    @Override
    public List<TimeBlock> getTimeBlocksForClockLogEndId(String tkClockLogId) {
        return ModelObjectUtils.transform(timeBlockDao.getTimeBlocksForClockLogEndId(tkClockLogId), toTimeBlock);
    }

    @Override
    public List<TimeBlock> getTimeBlocksForClockLogBeginId(String tkClockLogId) {
        return ModelObjectUtils.transform(timeBlockDao.getTimeBlocksForClockLogBeginId(tkClockLogId), toTimeBlock);
    }

    @Override
    public List<TimeBlock> getLatestEndTimestampForEarnCode(String earnCode) {
        return ModelObjectUtils.transform(timeBlockDao.getLatestEndTimestampForEarnCode(earnCode), toTimeBlock);
    }

    @Override
    public List<TimeBlock> getOvernightTimeBlocks(String clockLogEndId) {
        return ModelObjectUtils.transform(timeBlockDao.getOvernightTimeBlocks(clockLogEndId), toTimeBlock);
    }

    @Override
    public boolean isOvernightTimeBlock(String clockLogEndId) {
        List<TimeBlockBo> timeBlockBos = timeBlockDao.getOvernightTimeBlocks(clockLogEndId);
        return CollectionUtils.isNotEmpty(timeBlockBos) && timeBlockBos.size() >= 2;
    }

    @Override
    public void deleteLunchDeduction(String tkTimeHourDetailId) {
        TimeHourDetail thd = TkServiceLocator.getTimeHourDetailService().getTimeHourDetail(tkTimeHourDetailId);
        TimeBlockBo tb = getTimeBlockBo(thd.getTkTimeBlockId());
        //clear any timeblock permissions
        HrServiceLocator.getHRPermissionService()
                .updateTimeBlockPermissions(CalendarBlockPermissions.newInstance(tb.getTkTimeBlockId()));
        // mark the lunch deleted as Y
        tb.setLunchDeleted(true);
        // save the change
        KNSServiceLocator.getBusinessObjectService().save(tb);
        // remove the related time hour detail row with the lunch deduction
        TkServiceLocator.getTimeHourDetailService().removeTimeHourDetail(thd.getTkTimeHourDetailId());
    }
    /*@Override
    public List<TimeBlock> getTimeBlocksWithEarnCode(String earnCode, DateTime effDate) {
       return ModelObjectUtils.transform(timeBlockDao.getTimeBlocksWithEarnCode(earnCode, effDate), toTimeBlock);
    }*/

    private BigDecimal applyInflateMinHoursAndFactor(EarnCode earnCodeObj, BigDecimal blockHours) {
        if (earnCodeObj != null) {
            //If earn code has an inflate min hours check if it is greater than zero
            //and compare if the hours specified is less than min hours awarded for this
            //earn code
            if (earnCodeObj.getInflateMinHours() != null) {
                if ((earnCodeObj.getInflateMinHours().compareTo(BigDecimal.ZERO) != 0)
                        && earnCodeObj.getInflateMinHours().compareTo(blockHours) > 0) {
                    blockHours = earnCodeObj.getInflateMinHours();
                }
            }
            //If earn code has an inflate factor multiple hours specified by the factor
            if (earnCodeObj.getInflateFactor() != null) {
                if ((earnCodeObj.getInflateFactor().compareTo(new BigDecimal(1.0)) != 0)
                        && (earnCodeObj.getInflateFactor().compareTo(BigDecimal.ZERO) != 0)) {
                    blockHours = earnCodeObj.getInflateFactor().multiply(blockHours, HrConstants.MATH_CONTEXT)
                            .setScale(HrConstants.BIG_DECIMAL_SCALE);
                }
            }
        }
        return blockHours;
    }

    /*@Override
    public List<TimeBlock> getTimeBlocksForLookup(String documentId,
     String principalId, String userPrincipalId, LocalDate fromDate,
     LocalDate toDate) {
       return ModelObjectUtils.transform(timeBlockDao.getTimeBlocksForLookup(documentId, principalId, userPrincipalId, fromDate, toDate), toTimeBlock);
    }*/

    @Override
    public List<TimeBlock> applyHolidayPremiumEarnCode(String principalId, List<Assignment> timeAssignments,
            List<TimeBlock> timeBlockList) {

        if (CollectionUtils.isNotEmpty(timeBlockList)) {
            Set<String> regularEarnCodes = new HashSet<String>();
            for (Assignment assign : timeAssignments) {
                regularEarnCodes.add(assign.getJob().getPayTypeObj().getRegEarnCode());
            }
            List<TimeBlockBo> bos = ModelObjectUtils.transform(timeBlockList, toTimeBlockBo);
            for (TimeBlockBo tb : bos) {
                EarnCode earnCodeObj = HrServiceLocator.getEarnCodeService().getEarnCode(tb.getEarnCode(),
                        tb.getBeginDateTime().toLocalDate());
                Assignment assignment = TKUtils.getAssignmentWithKey(timeAssignments,
                        AssignmentDescriptionKey.get(tb.getAssignmentKey()));
                List<TimeHourDetailBo> timeHourDetails = new ArrayList<TimeHourDetailBo>();
                if (earnCodeObj.getCountsAsRegularPay().equals("Y")
                        || regularEarnCodes.contains(earnCodeObj.getEarnCode())) {
                    if (assignment != null && assignment.getJob() != null
                            && assignment.getJob().isEligibleForLeave()) {
                        PrincipalHRAttributes principalCalendar = HrServiceLocator.getPrincipalHRAttributeService()
                                .getPrincipalCalendar(principalId, tb.getBeginDateTime().toLocalDate());
                        if (principalCalendar != null && StringUtils.isNotEmpty(principalCalendar.getLeavePlan())) {
                            SystemScheduledTimeOffContract sstoHoliday = LmServiceLocator.getSysSchTimeOffService()
                                    .getSystemScheduledTimeOffByDate(principalCalendar.getLeavePlan(),
                                            tb.getBeginDateTime().toLocalDate());
                            if (sstoHoliday != null && sstoHoliday.getPremiumHoliday().equalsIgnoreCase("Y")
                                    && StringUtils.isNotEmpty(sstoHoliday.getPremiumEarnCode())) {
                                EarnCode premiumEarnCodeObj = HrServiceLocator.getEarnCodeService().getEarnCode(
                                        sstoHoliday.getPremiumEarnCode(), tb.getBeginDateTime().toLocalDate());
                                if (premiumEarnCodeObj != null) {
                                    timeHourDetails.addAll(this.createTimeHourDetails(earnCodeObj, BigDecimal.ZERO,
                                            tb.getAmount(), tb.getTkTimeBlockId(), true));
                                    timeHourDetails.addAll(this.createTimeHourDetails(premiumEarnCodeObj,
                                            tb.getHours(), tb.getAmount(), tb.getTkTimeBlockId(), true));
                                    tb.setTimeHourDetails(timeHourDetails);
                                }
                            }
                        }
                    }
                }
            } // end of for loop
            return ModelObjectUtils.transform(bos, toTimeBlock);
        }
        return timeBlockList;
    }

    @Override
    public List<TimeBlock> getIntersectingTimeBlocks(String principalId, DateTime startTime, DateTime endTime) {
        Interval interval = new Interval(startTime, endTime);
        return ModelObjectUtils.transform(timeBlockDao.getIntersectingTimeBlocks(principalId, interval),
                toTimeBlock);
    }
}