001/**
002 * Copyright 2004-2014 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 */
016package org.kuali.kpme.tklm.time.approval.service;
017
018import org.apache.commons.collections.CollectionUtils;
019import org.apache.commons.lang.StringUtils;
020import org.joda.time.DateTime;
021import org.joda.time.DateTimeZone;
022import org.joda.time.Hours;
023import org.joda.time.Interval;
024import org.joda.time.LocalDate;
025import org.json.simple.JSONValue;
026import org.kuali.kpme.core.api.accrualcategory.AccrualCategory;
027import org.kuali.kpme.core.api.accrualcategory.rule.AccrualCategoryRuleContract;
028import org.kuali.kpme.core.api.assignment.Assignment;
029import org.kuali.kpme.core.api.calendar.Calendar;
030import org.kuali.kpme.core.api.calendar.entry.CalendarEntry;
031import org.kuali.kpme.core.calendar.web.CalendarDay;
032import org.kuali.kpme.core.calendar.web.CalendarWeek;
033import org.kuali.kpme.core.service.HrServiceLocator;
034import org.kuali.kpme.core.util.HrConstants;
035import org.kuali.kpme.core.util.HrContext;
036import org.kuali.kpme.core.util.TKUtils;
037import org.kuali.kpme.tklm.api.common.TkConstants;
038import org.kuali.kpme.tklm.api.leave.block.LeaveBlock;
039import org.kuali.kpme.tklm.api.leave.block.LeaveBlockContract;
040import org.kuali.kpme.tklm.api.time.clocklog.ClockLog;
041import org.kuali.kpme.tklm.api.time.timeblock.TimeBlock;
042import org.kuali.kpme.tklm.api.time.timehourdetail.TimeHourDetail;
043import org.kuali.kpme.tklm.api.time.timesummary.TimeSummaryContract;
044import org.kuali.kpme.tklm.leave.block.LeaveBlockAggregate;
045import org.kuali.kpme.tklm.leave.calendar.validation.LeaveCalendarValidationUtil;
046import org.kuali.kpme.tklm.leave.service.LmServiceLocator;
047import org.kuali.kpme.tklm.time.approval.summaryrow.ApprovalTimeSummaryRow;
048import org.kuali.kpme.tklm.time.calendar.TkCalendar;
049import org.kuali.kpme.tklm.time.calendar.TkCalendarDay;
050import org.kuali.kpme.tklm.time.flsa.FlsaDay;
051import org.kuali.kpme.tklm.time.flsa.FlsaWeek;
052import org.kuali.kpme.tklm.time.rules.timecollection.TimeCollectionRule;
053import org.kuali.kpme.tklm.time.service.TkServiceLocator;
054import org.kuali.kpme.tklm.time.timeblock.web.TimeBlockRenderer;
055import org.kuali.kpme.tklm.time.timehourdetail.TimeHourDetailRenderer;
056import org.kuali.kpme.tklm.time.timesheet.TimesheetDocument;
057import org.kuali.kpme.tklm.time.util.TkTimeBlockAggregate;
058import org.kuali.kpme.tklm.time.workflow.TimesheetDocumentHeader;
059import org.kuali.rice.kew.api.KewApiServiceLocator;
060import org.kuali.rice.kew.api.note.Note;
061import org.kuali.rice.kim.api.identity.principal.EntityNamePrincipalName;
062import org.kuali.rice.kim.api.services.KimApiServiceLocator;
063
064import java.math.BigDecimal;
065import java.util.ArrayList;
066import java.util.HashMap;
067import java.util.HashSet;
068import java.util.LinkedHashMap;
069import java.util.LinkedList;
070import java.util.List;
071import java.util.Map;
072import java.util.Map.Entry;
073import java.util.Set;
074
075public class TimeApproveServiceImpl implements TimeApproveService {
076
077        @Override
078        public List<ApprovalTimeSummaryRow> getApprovalSummaryRows(String calGroup, List<String> principalIds, List<String> payCalendarLabels, CalendarEntry payCalendarEntry, String docIdSearchTerm) {
079                DateTime payBeginDate = payCalendarEntry.getBeginPeriodFullDateTime();
080                DateTime payEndDate = payCalendarEntry.getEndPeriodFullDateTime();
081                
082                List<Map<String, Object>> timeBlockJsonMap = new ArrayList<Map<String,Object>>();
083                List<ApprovalTimeSummaryRow> rows = new LinkedList<ApprovalTimeSummaryRow>();
084                Map<String, TimesheetDocumentHeader> principalDocumentHeader = getPrincipalDocumentHeader(
085                principalIds, payBeginDate, payEndDate, docIdSearchTerm);
086
087                Calendar payCalendar = HrServiceLocator.getCalendarService()
088                                .getCalendar(payCalendarEntry.getHrCalendarId());
089                DateTimeZone dateTimeZone = HrServiceLocator.getTimezoneService()
090                                .getUserTimezoneWithFallback();
091                List<Interval> dayIntervals = TKUtils
092                                .getDaySpanForCalendarEntry(payCalendarEntry);
093
094                
095                String color = null;
096                
097                Map<String, String> userColorMap = new HashMap<String, String>();
098                Set<String> randomColors = new HashSet<String>();
099                
100                String  approverId = HrContext.getPrincipalId();
101                String timeZoneString = HrServiceLocator.getTimezoneService().getApproverTimezone(approverId);
102                DateTimeZone approverTimeZone = StringUtils.isNotBlank(timeZoneString) ? DateTimeZone.forID(timeZoneString) : null;
103                
104                for (String principalId : principalIds) {
105                        TimesheetDocumentHeader tdh = new TimesheetDocumentHeader();
106                        String documentId = "";
107                        if (principalDocumentHeader.containsKey(principalId)) {
108                                tdh = principalDocumentHeader.get(principalId);
109                                documentId = principalDocumentHeader.get(principalId).getDocumentId();
110                        } else if(StringUtils.isNotBlank(docIdSearchTerm)){
111                                continue;       // if there's a search term for document id, only build the rows for principalIds from principalDocumentHeader
112                        }
113                        List<TimeBlock> timeBlocks = new ArrayList<TimeBlock>();
114                        List<LeaveBlock> leaveBlocks = new ArrayList<LeaveBlock>();
115                        List<Note> notes = new ArrayList<Note>();
116                        List<String> warnings = new ArrayList<String>();
117                        List<String> clockLogWarnings = new ArrayList<String>();
118
119                        ApprovalTimeSummaryRow approvalSummaryRow = new ApprovalTimeSummaryRow();
120
121                        if (principalDocumentHeader.containsKey(principalId)) {
122                                approvalSummaryRow
123                                                .setApprovalStatus(HrConstants.DOC_ROUTE_STATUS.get(tdh
124                                                                .getDocumentStatus()));
125                        }
126                        TimesheetDocument td = null;
127                        if (StringUtils.isNotBlank(documentId)) {
128                 td = TkServiceLocator.getTimesheetService().getTimesheetDocument(documentId);
129                timeBlocks = td.getTimeBlocks();
130                clockLogWarnings =  TkServiceLocator.getClockLogService().getUnapprovedIPWarning(timeBlocks);
131                //timeBlocks = TkServiceLocator.getTimeBlockService()
132                //              .getTimeBlocks(documentId);
133                List<String> assignKeys = new ArrayList<String>();
134                for(Assignment a : td.getAllAssignments()) {
135                        assignKeys.add(a.getAssignmentKey());
136                }
137                leaveBlocks.addAll(LmServiceLocator.getLeaveBlockService().getLeaveBlocksForTimeCalendar(principalId,
138                        payBeginDate.toLocalDate(), payEndDate.toLocalDate(), assignKeys));
139                notes = getNotesForDocument(documentId);
140                Map<String, List<LocalDate>> earnCodeMap = new HashMap<String, List<LocalDate>>();
141                for(TimeBlock tb : td.getTimeBlocks()) {
142                        if(!earnCodeMap.containsKey(tb.getEarnCode())) {
143                                List<LocalDate> lst = new ArrayList<LocalDate>();
144                                lst.add(tb.getBeginDateTime().toLocalDate());
145                                earnCodeMap.put(tb.getEarnCode(), lst);
146                        }
147                        else
148                                earnCodeMap.get(tb.getEarnCode()).add(tb.getBeginDateTime().toLocalDate());
149                }
150                warnings = HrServiceLocator.getEarnCodeGroupService().getWarningTextFromEarnCodeGroups(earnCodeMap);
151                
152                // Get Timesheet blocks
153                
154               
155                List<Interval> intervals = TKUtils.getFullWeekDaySpanForCalendarEntry(payCalendarEntry);
156                TkTimeBlockAggregate tbAggregate = buildAndMergeAggregates(timeBlocks, leaveBlocks, payCalendarEntry, payCalendar, dayIntervals);
157//                TkTimeBlockAggregate tbAggregate = new TkTimeBlockAggregate(timeBlocks, payCalendarEntry, payCalendar, true,intervals);
158                // use both time aggregate to populate the calendar
159                TkCalendar cal = TkCalendar.getCalendar(tbAggregate);
160                
161                
162                for (CalendarWeek week : cal.getWeeks()) {
163                        for(CalendarDay day : week.getDays()) {
164                                TkCalendarDay tkDay = (TkCalendarDay) day;
165                                for (TimeBlockRenderer renderer : tkDay.getBlockRenderers()) {
166                                        Map<String, Object> timeBlockMap = new HashMap<String, Object>();
167                                        
168                                        // set title..
169                                        StringBuffer title = new StringBuffer();
170                                        if(!renderer.getEarnCodeType().equalsIgnoreCase(HrConstants.EARN_CODE_AMOUNT)) {
171                                                if(renderer.getDetailRenderers() != null && !renderer.getDetailRenderers().isEmpty()) {
172                                                        for(TimeHourDetailRenderer thdr : renderer.getDetailRenderers()) {
173                                                                title.append("\n");
174                                                                title = new StringBuffer(thdr.getTitle());
175                                                                title.append(" - "+thdr.getHours());
176                                                        }
177                                                }
178                                        }
179                                        
180                                        timeBlockMap.put("start", tkDay.getDateString());
181                                        StringBuffer titleString = new StringBuffer();
182                                        titleString.append(renderer.getTitle());
183                                        if(renderer.getTimeRange() != null && !renderer.getTimeRange().isEmpty()) {
184                                                titleString.append("\n" +renderer.getTimeRange());
185                                        }
186                                        titleString.append("\n"+title.toString());
187                                        timeBlockMap.put("title",  titleString.toString());
188                                        timeBlockMap.put("id", tkDay.getDayNumberString());
189                                        if(!userColorMap.containsKey(principalId)) {
190                                                color = TKUtils.getRandomColor(randomColors);
191                                                randomColors.add(color);
192                                                userColorMap.put(principalId, color);
193                                        }
194                                        color = userColorMap.get(principalId);
195                                        timeBlockMap.put("color", userColorMap.get(principalId));
196                                        timeBlockMap.put("className", "event-approval");
197                                        timeBlockJsonMap.add(timeBlockMap);
198                                }
199                        }
200                }
201                
202                                warnings = HrServiceLocator.getEarnCodeGroupService().getWarningTextFromEarnCodeGroups(td.getEarnCodeMap());
203                        }
204                        
205                        Map<String, Set<String>> transactionalWarnings = LeaveCalendarValidationUtil.validatePendingTransactions(principalId, payCalendarEntry.getBeginPeriodFullDateTime().toLocalDate(), payCalendarEntry.getEndPeriodFullDateTime().toLocalDate());
206                        
207                        warnings.addAll(transactionalWarnings.get("infoMessages"));
208                        warnings.addAll(transactionalWarnings.get("warningMessages"));
209                        warnings.addAll(transactionalWarnings.get("actionMessages"));
210                        
211                        Map<String, Set<String>> eligibleTransfers = findWarnings(principalId, payCalendarEntry);
212                        warnings.addAll(eligibleTransfers.get("warningMessages"));
213                        
214                        warnings.addAll(clockLogWarnings);                      
215                        Map<String, BigDecimal> hoursToPayLabelMap = getHoursToPayDayMap(
216                                        principalId, payEndDate, payCalendarLabels,
217                                        timeBlocks, leaveBlocks, null, payCalendarEntry, payCalendar,
218                                        dateTimeZone, dayIntervals);
219                        
220                        Map<String, BigDecimal> hoursToFlsaPayLabelMap = getHoursToFlsaWeekMap(
221                                        principalId, payEndDate, payCalendarLabels,
222                                        timeBlocks, leaveBlocks, null, payCalendarEntry, payCalendar,
223                                        dateTimeZone, dayIntervals);
224
225            EntityNamePrincipalName name = KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(principalId);
226            approvalSummaryRow.setName(name != null
227                                         && name.getDefaultName() != null
228                                         && name.getDefaultName().getCompositeName() != null ? name.getDefaultName().getCompositeName() : principalId);
229                        approvalSummaryRow.setPrincipalId(principalId);
230                        approvalSummaryRow.setColor(userColorMap.get(principalId));
231                        approvalSummaryRow.setPayCalendarGroup(calGroup);
232                        approvalSummaryRow.setDocumentId(documentId);
233
234            
235                        approvalSummaryRow.setHoursToPayLabelMap(hoursToPayLabelMap);
236                        approvalSummaryRow.setHoursToFlsaPayLabelMap(hoursToFlsaPayLabelMap);
237                        approvalSummaryRow.setPeriodTotal(hoursToPayLabelMap
238                                        .get("Period Total"));
239                        approvalSummaryRow.setLstTimeBlocks(timeBlocks);
240                        approvalSummaryRow.setNotes(notes);
241                        approvalSummaryRow.setWarnings(warnings);
242
243                        // Compare last clock log versus now and if > threshold
244                        // highlight entry
245                        ClockLog lastClockLog = TkServiceLocator.getClockLogService()
246                                        .getLastClockLog(principalId);
247                        if (isSynchronousUser(principalId)) {
248                approvalSummaryRow.setClockStatusMessage(createLabelForLastClockLog(lastClockLog, approverTimeZone));
249            }
250                        if (lastClockLog != null
251                                        && (StringUtils.equals(lastClockLog.getClockAction(),
252                                                        TkConstants.CLOCK_IN) || StringUtils
253                                                        .equals(lastClockLog.getClockAction(),
254                                                                        TkConstants.LUNCH_IN))) {
255                                DateTime startTime = lastClockLog.getClockDateTime();
256                                DateTime endTime = new DateTime();
257
258                                Hours hour = Hours.hoursBetween(startTime, endTime);
259                                if (hour != null) {
260                                        int elapsedHours = hour.getHours();
261                                        if (elapsedHours >= TkConstants.NUMBER_OF_HOURS_CLOCKED_IN_APPROVE_TAB_HIGHLIGHT && isSynchronousUser(principalId)) {
262                                                approvalSummaryRow.setClockedInOverThreshold(true);
263                                        }
264                                }
265
266                        }
267                        //KPME-2563
268                        try{
269                                if(td != null) {
270                                        TimeSummaryContract ts = TkServiceLocator.getTimeSummaryService()
271                            .getTimeSummary(td.getPrincipalId(), td.getTimeBlocks(), td.getCalendarEntry(), td.getAssignmentMap());
272                                        approvalSummaryRow.setTimeSummary(ts);                                  
273                                }                               
274                        } catch (Exception ex){
275                                ex.printStackTrace();
276                        }                                               
277                        rows.add(approvalSummaryRow);
278                }
279                
280                String outputString = JSONValue.toJSONString(timeBlockJsonMap);
281                if(rows != null && !rows.isEmpty()) {
282                        rows.get(0).setOutputString(outputString);
283                }
284                return rows;
285        }
286
287    private boolean isSynchronousUser(String principalId) {
288        List<Assignment> assignments = HrServiceLocator.getAssignmentService().getAssignments(principalId, LocalDate.now());
289        boolean isSynchronousUser = false;
290        if (CollectionUtils.isNotEmpty(assignments)) {
291            for (Assignment assignment : assignments) {
292                if(assignment.getJob() != null) {
293                            TimeCollectionRule tcr = TkServiceLocator.getTimeCollectionRuleService().getTimeCollectionRule(assignment.getDept(), assignment.getWorkArea(), assignment.getJob().getHrPayType(), assignment.getGroupKeyCode(), LocalDate.now());
294                            isSynchronousUser |= (tcr == null || tcr.isClockUserFl());
295                }
296            }
297        }
298        return isSynchronousUser;
299    }
300
301        private Map<String, Set<String>> findWarnings(String principalId, CalendarEntry calendarEntry) {
302                Map<String, Set<String>> allMessages = new HashMap<String,Set<String>>();
303                allMessages.put("warningMessages", new HashSet<String>());
304
305        Map<String, Set<LeaveBlockContract>> eligibilities = LmServiceLocator.getAccrualCategoryMaxBalanceService().getMaxBalanceViolations(calendarEntry, principalId);
306        
307                if (eligibilities != null) {
308                        for (Entry<String,Set<LeaveBlockContract>> entry : eligibilities.entrySet()) {
309                                for(LeaveBlockContract lb : entry.getValue()) {
310                                        AccrualCategoryRuleContract rule = lb.getAccrualCategoryRule();
311                                        if (rule != null) {
312                                                AccrualCategory accrualCategory = HrServiceLocator.getAccrualCategoryService().getAccrualCategory(rule.getLmAccrualCategoryId());
313                                                if (rule.getActionAtMaxBalance().equals(HrConstants.ACTION_AT_MAX_BALANCE.TRANSFER)) {
314                                                        //Todo: add link to balance transfer
315                                                        allMessages.get("warningMessages").add("Accrual Category '" + accrualCategory.getAccrualCategory() + "' is over max balance.");   //warningMessages
316                                                } else if (rule.getActionAtMaxBalance().equals(HrConstants.ACTION_AT_MAX_BALANCE.LOSE)) {
317                                                        //Todo: compute and display amount of time lost.
318                                                        allMessages.get("warningMessages").add("Accrual Category '" + accrualCategory.getAccrualCategory() + "' is over max balance.");      //warningMessages
319                                                } else if (rule.getActionAtMaxBalance().equals(HrConstants.ACTION_AT_MAX_BALANCE.PAYOUT)) {
320                                                        //Todo: display payout details.
321                                                        allMessages.get("warningMessages").add("Accrual Category '" + accrualCategory.getAccrualCategory() + "' is over max balance.");      //warningMessages                                            
322                                                }
323                                        }
324                                }
325                        }
326                }
327              
328                return allMessages;
329        }
330
331        /**
332         * Create label for the last clock log
333         * 
334         * @param cl
335         * @return
336         */
337        private String createLabelForLastClockLog(ClockLog cl, DateTimeZone approverTimeZone) {
338                if (cl == null) {
339                        return "No previous clock information";
340                }
341                
342                String zoneString = "";
343                DateTime clockTimeWithZone = cl.getClockDateTime();
344                if(approverTimeZone != null) {
345                        clockTimeWithZone = clockTimeWithZone.withZone(approverTimeZone);
346                        zoneString = DateTime.now(approverTimeZone).toString("z");;
347                }
348                String dateTime = clockTimeWithZone.toString(TkConstants.DT_FULL_DATE_TIME_FORMAT);
349                dateTime += " " + zoneString;
350                                
351                if (StringUtils.equals(cl.getClockAction(), TkConstants.CLOCK_IN)) {
352                        return "Clocked in since: " + dateTime;
353                } else if (StringUtils.equals(cl.getClockAction(),
354                                TkConstants.LUNCH_OUT)) {
355                        return "At Lunch since: " + dateTime;
356                } else if (StringUtils
357                                .equals(cl.getClockAction(), TkConstants.LUNCH_IN)) {
358                        return "Returned from Lunch : " + dateTime;
359                } else if (StringUtils.equals(cl.getClockAction(),
360                                TkConstants.CLOCK_OUT)) {
361                        return "Clocked out since: " + dateTime;
362                } else {
363                        return "No previous clock information";
364                }
365        }
366
367        /**
368         * Aggregate TimeBlocks to hours per day and sum for week
369         */
370        @Override
371        public Map<String, BigDecimal> getHoursToPayDayMap(String principalId,
372                        DateTime payEndDate, List<String> payCalendarLabels,
373                        List<TimeBlock> lstTimeBlocks, List<LeaveBlock> leaveBlocks, Long workArea,
374                        CalendarEntry payCalendarEntry, Calendar payCalendar,
375                        DateTimeZone dateTimeZone, List<Interval> dayIntervals) {
376                Map<String, BigDecimal> hoursToPayLabelMap = new LinkedHashMap<String, BigDecimal>();
377                List<BigDecimal> dayTotals = new ArrayList<BigDecimal>();
378
379        TkTimeBlockAggregate tkTimeBlockAggregate = buildAndMergeAggregates(lstTimeBlocks, leaveBlocks, payCalendarEntry, payCalendar, dayIntervals);
380
381                List<FlsaWeek> flsaWeeks = tkTimeBlockAggregate.getFlsaWeeks(dateTimeZone,0, false);
382                for (FlsaWeek week : flsaWeeks) {
383                        for (FlsaDay day : week.getFlsaDays()) {
384                                BigDecimal total = new BigDecimal(0.00);
385                                for (TimeBlock tb : day.getAppliedTimeBlocks()) {
386                    for (TimeHourDetail thd : tb.getTimeHourDetails()) {
387                        if (workArea != null) {
388                            if (tb.getWorkArea().compareTo(workArea) == 0) {
389                                    total = total.add(thd.getHours(),
390                                            HrConstants.MATH_CONTEXT);
391                            } else {
392                                total = total.add(new BigDecimal("0"),
393                                        HrConstants.MATH_CONTEXT);
394                            }
395                        } else {
396                                total = total.add(thd.getHours(),
397                                    HrConstants.MATH_CONTEXT);
398                        }
399                                    }
400                                }
401                                dayTotals.add(total);
402                        }
403                }
404
405                int dayCount = 0;
406                BigDecimal weekTotal = new BigDecimal(0.00);
407                BigDecimal periodTotal = new BigDecimal(0.00);
408                for (String payCalendarLabel : payCalendarLabels) {
409                        if (StringUtils.contains(payCalendarLabel, "Week")) {
410                                hoursToPayLabelMap.put(payCalendarLabel, weekTotal);
411                                weekTotal = new BigDecimal(0.00);
412                        } else if (StringUtils.contains(payCalendarLabel, "Period Total")) {
413                                hoursToPayLabelMap.put(payCalendarLabel, periodTotal);
414                        } else {
415                                if(dayCount < dayTotals.size()) {
416                                        hoursToPayLabelMap.put(payCalendarLabel,
417                                                        dayTotals.get(dayCount));
418                                        weekTotal = weekTotal.add(dayTotals.get(dayCount),
419                                                        HrConstants.MATH_CONTEXT);
420                                        periodTotal = periodTotal.add(dayTotals.get(dayCount));
421                                        dayCount++;
422                                }
423
424                        }
425
426                }
427                return hoursToPayLabelMap;
428        }
429        
430    private TkTimeBlockAggregate buildAndMergeAggregates(List<TimeBlock> timeBlocks, List<LeaveBlock> leaveBlocks,
431                                                         CalendarEntry calendarEntries, Calendar calendar, List<Interval> dayIntervals) {
432        TkTimeBlockAggregate tkTimeBlockAggregate = new TkTimeBlockAggregate(timeBlocks, calendarEntries, calendar, true, dayIntervals);
433        LeaveBlockAggregate leaveBlockAggregate = new LeaveBlockAggregate(leaveBlocks, calendarEntries);
434        return TkTimeBlockAggregate.combineTimeAndLeaveAggregates(tkTimeBlockAggregate, leaveBlockAggregate);
435    }
436
437        /**
438         * Aggregate TimeBlocks to hours per day and sum for flsa week (including previous/next weeks)
439         */
440        @Override
441        public Map<String, BigDecimal> getHoursToFlsaWeekMap(String principalId, 
442                        DateTime payEndDate, List<String> payCalendarLabels, 
443                        List<TimeBlock> lstTimeBlocks, List<LeaveBlock> leaveBlocks,
444            Long workArea, CalendarEntry payCalendarEntry, Calendar payCalendar,
445                        DateTimeZone dateTimeZone, List<Interval> dayIntervals) {
446                
447                Map<String, BigDecimal> hoursToFlsaWeekMap = new LinkedHashMap<String, BigDecimal>();
448
449        TkTimeBlockAggregate tkTimeBlockAggregate = buildAndMergeAggregates(lstTimeBlocks, leaveBlocks, payCalendarEntry, payCalendar, dayIntervals);
450                List<List<FlsaWeek>> flsaWeeks = tkTimeBlockAggregate.getFlsaWeeks(dateTimeZone, principalId);
451                
452                int weekCount = 1;
453                for (List<FlsaWeek> flsaWeekParts : flsaWeeks) {
454                        BigDecimal weekTotal = new BigDecimal(0.00);
455                        for (FlsaWeek flsaWeekPart : flsaWeekParts) {
456                                for (FlsaDay flsaDay : flsaWeekPart.getFlsaDays()) {
457                                        for (TimeBlock timeBlock : flsaDay.getAppliedTimeBlocks()) {
458                        for (TimeHourDetail thd : timeBlock.getTimeHourDetails()) {
459                            if (workArea != null) {
460                                if (timeBlock.getWorkArea().compareTo(workArea) == 0) {
461                                        weekTotal = weekTotal.add(thd.getHours(), HrConstants.MATH_CONTEXT);
462                                } else {
463                                    weekTotal = weekTotal.add(new BigDecimal("0"), HrConstants.MATH_CONTEXT);
464                                }
465                            } else {
466                                    weekTotal = weekTotal.add(thd.getHours(),HrConstants.MATH_CONTEXT);
467                            }
468                                            }
469                    }
470                            }
471                        }
472                        hoursToFlsaWeekMap.put("Week " + weekCount++, weekTotal);
473                }
474                
475                return hoursToFlsaWeekMap;
476        }
477
478    @Override
479        public List<Note> getNotesForDocument(String documentNumber) {
480        return KewApiServiceLocator.getNoteService().getNotes(documentNumber);
481        }
482
483    @Override
484    public List<String> getTimePrincipalIdsWithSearchCriteria(List<String> workAreaList, String calendarGroup, LocalDate effdt, LocalDate beginDate, LocalDate endDate) {
485        if (CollectionUtils.isEmpty(workAreaList)) {
486                return new ArrayList<String>();
487            }
488                List<Assignment> assignmentList = HrServiceLocator.getAssignmentService().getAssignments(workAreaList, effdt, beginDate, endDate);
489                List<Assignment> tempList = this.removeNoTimeAssignment(assignmentList);
490                Set<String> pids = new HashSet<String>();
491        for(Assignment anAssignment : tempList) {
492                if(anAssignment != null) {
493                        pids.add(anAssignment.getPrincipalId());
494                }
495        }
496        List<String> ids = new ArrayList<String>();
497        ids.addAll(pids);
498                
499                if(CollectionUtils.isEmpty(ids)) {
500                        return new ArrayList<String>();
501                }
502                // use unique principalIds and selected calendarGroup to get unique ids from principalHRAttributes table
503                List<String> idList = HrServiceLocator.getPrincipalHRAttributeService()
504                                .getActiveEmployeesIdForTimeCalendarAndIdList(calendarGroup, ids, endDate); 
505                if(CollectionUtils.isEmpty(idList)) {
506                        return new ArrayList<String>();
507                }
508                return idList;
509    }
510    
511    private List<Assignment> removeNoTimeAssignment(List<Assignment> assignmentList) {
512        List<Assignment> results = new ArrayList<Assignment>();
513                if(CollectionUtils.isNotEmpty(assignmentList)) {
514                for(Assignment anAssignment: assignmentList) {
515                        if(anAssignment != null 
516                                        && anAssignment.getJob() != null 
517                                        && anAssignment.getJob().getFlsaStatus() != null 
518                                        && anAssignment.getJob().getFlsaStatus().equalsIgnoreCase(HrConstants.FLSA_STATUS_NON_EXEMPT)) {
519                                results.add(anAssignment);      
520                        }
521                }
522            }
523                return results;
524        }
525    
526        @Override
527        public Map<String, TimesheetDocumentHeader> getPrincipalDocumentHeader(
528                        List<String> principalIds, DateTime payBeginDate, DateTime payEndDate, String docIdSearchTerm) {
529                Map<String, TimesheetDocumentHeader> principalDocumentHeader = new LinkedHashMap<String, TimesheetDocumentHeader>();
530                for (String principalId : principalIds) {
531                        
532                        TimesheetDocumentHeader tdh = TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeader(principalId, payBeginDate, payEndDate.plusMillis(1));
533                        if(tdh != null) {
534                                if(StringUtils.isNotBlank(docIdSearchTerm)) {
535                                        if(tdh.getDocumentId().contains(docIdSearchTerm)) {
536                                                principalDocumentHeader.put(principalId, tdh);  
537                                        }
538                                } else {
539                                        principalDocumentHeader.put(principalId, tdh);
540                                }
541                        }
542                }
543                return principalDocumentHeader;
544        }
545        
546}