001    /**
002     * Copyright 2004-2012 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.hr.time.approval.service;
017    
018    import java.math.BigDecimal;
019    import java.sql.Types;
020    import java.text.SimpleDateFormat;
021    import java.util.ArrayList;
022    import java.util.Date;
023    import java.util.HashMap;
024    import java.util.HashSet;
025    import java.util.LinkedHashMap;
026    import java.util.LinkedList;
027    import java.util.List;
028    import java.util.Map;
029    import java.util.Set;
030    import java.util.SortedSet;
031    import java.util.TreeSet;
032    
033    import org.apache.commons.lang.StringUtils;
034    import org.apache.log4j.Logger;
035    import org.joda.time.DateMidnight;
036    import org.joda.time.DateTime;
037    import org.joda.time.DateTimeZone;
038    import org.joda.time.Hours;
039    import org.joda.time.Interval;
040    import org.joda.time.format.DateTimeFormat;
041    import org.joda.time.format.DateTimeFormatter;
042    import org.kuali.hr.job.Job;
043    import org.kuali.hr.time.approval.web.ApprovalTimeSummaryRow;
044    import org.kuali.hr.time.assignment.Assignment;
045    import org.kuali.hr.time.assignment.AssignmentDescriptionKey;
046    import org.kuali.hr.time.calendar.Calendar;
047    import org.kuali.hr.time.calendar.CalendarEntries;
048    import org.kuali.hr.time.clocklog.ClockLog;
049    import org.kuali.hr.time.flsa.FlsaDay;
050    import org.kuali.hr.time.flsa.FlsaWeek;
051    import org.kuali.hr.time.person.TKPerson;
052    import org.kuali.hr.time.principal.PrincipalHRAttributes;
053    import org.kuali.hr.time.roles.TkUserRoles;
054    import org.kuali.hr.time.service.base.TkServiceLocator;
055    import org.kuali.hr.time.timeblock.TimeBlock;
056    import org.kuali.hr.time.timesheet.TimesheetDocument;
057    import org.kuali.hr.time.util.TKContext;
058    import org.kuali.hr.time.util.TKUser;
059    import org.kuali.hr.time.util.TKUtils;
060    import org.kuali.hr.time.util.TkConstants;
061    import org.kuali.hr.time.util.TkTimeBlockAggregate;
062    import org.kuali.hr.time.workarea.WorkArea;
063    import org.kuali.hr.time.workflow.TimesheetDocumentHeader;
064    import org.kuali.rice.kew.api.KewApiServiceLocator;
065    import org.kuali.rice.kew.notes.Note;
066    import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
067    import org.kuali.rice.kew.service.KEWServiceLocator;
068    import org.kuali.rice.kim.api.services.KimApiServiceLocator;
069    import org.kuali.rice.krad.util.GlobalVariables;
070    import org.springframework.jdbc.support.rowset.SqlRowSet;
071    
072    import com.google.common.collect.HashMultimap;
073    import com.google.common.collect.Multimap;
074    
075    public class TimeApproveServiceImpl implements TimeApproveService {
076    
077            private static final Logger LOG = Logger
078                            .getLogger(TimeApproveServiceImpl.class);
079    
080            public static final int DAYS_WINDOW_DELTA = 31;
081    
082            public Map<String, CalendarEntries> getPayCalendarEntriesForDept(
083                            String dept, Date currentDate) {
084                    DateTime minDt = new DateTime(currentDate,
085                                    TKUtils.getSystemDateTimeZone());
086                    minDt = minDt.minusDays(DAYS_WINDOW_DELTA);
087                    java.sql.Date windowDate = TKUtils.getTimelessDate(minDt.toDate());
088    
089                    Map<String, CalendarEntries> pceMap = new HashMap<String, CalendarEntries>();
090                    Set<String> principals = new HashSet<String>();
091                    List<WorkArea> workAreasForDept = TkServiceLocator.getWorkAreaService()
092                                    .getWorkAreas(dept, new java.sql.Date(currentDate.getTime()));
093                    // Get all of the principals within our window of time.
094                    for (WorkArea workArea : workAreasForDept) {
095                            Long waNum = workArea.getWorkArea();
096                            List<Assignment> assignments = TkServiceLocator
097                                            .getAssignmentService().getActiveAssignmentsForWorkArea(
098                                                            waNum, TKUtils.getTimelessDate(currentDate));
099    
100                            if (assignments != null) {
101                                    for (Assignment assignment : assignments) {
102                                            principals.add(assignment.getPrincipalId());
103                                    }
104                            } else {
105                                    assignments = TkServiceLocator.getAssignmentService()
106                                                    .getActiveAssignmentsForWorkArea(waNum, windowDate);
107                                    if (assignments != null) {
108                                            for (Assignment assignment : assignments) {
109                                                    principals.add(assignment.getPrincipalId());
110                                            }
111                                    }
112                            }
113                    }
114    
115                    // Get the pay calendars
116                    Set<Calendar> payCals = new HashSet<Calendar>();
117                    for (String pid : principals) {
118                            PrincipalHRAttributes pc = TkServiceLocator
119                                            .getPrincipalHRAttributeService().getPrincipalCalendar(pid,
120                                                            currentDate);
121                            if (pc == null)
122                                    pc = TkServiceLocator.getPrincipalHRAttributeService()
123                                                    .getPrincipalCalendar(pid, windowDate);
124    
125                            if (pc != null) {
126                                    payCals.add(pc.getCalendar());
127                            } else {
128                                    LOG.warn("PrincipalCalendar null for principal: '" + pid + "'");
129                            }
130                    }
131    
132                    // Grab the pay calendar entries + groups
133                    for (Calendar pc : payCals) {
134                            CalendarEntries pce = TkServiceLocator
135                                            .getCalendarEntriesService()
136                                            .getCurrentCalendarEntriesByCalendarId(
137                                pc.getHrCalendarId(), currentDate);
138                            pceMap.put(pc.getCalendarName(), pce);
139                    }
140    
141                    return pceMap;
142            }
143    
144            @Override
145            public Map<String, CalendarEntries> getPayCalendarEntriesForApprover(
146                            String principalId, Date currentDate, String dept) {
147                    TKUser tkUser = TKContext.getUser();
148    
149                    Map<String, CalendarEntries> pceMap = new HashMap<String, CalendarEntries>();
150                    Set<String> principals = new HashSet<String>();
151                    DateTime minDt = new DateTime(currentDate,
152                                    TKUtils.getSystemDateTimeZone());
153                    minDt = minDt.minusDays(DAYS_WINDOW_DELTA);
154                    java.sql.Date windowDate = TKUtils.getTimelessDate(minDt.toDate());
155                    Set<Long> approverWorkAreas = TkUserRoles.getUserRoles(GlobalVariables.getUserSession().getPrincipalId()).getApproverWorkAreas();
156    
157                    // Get all of the principals within our window of time.
158                    for (Long waNum : approverWorkAreas) {
159                            List<Assignment> assignments = TkServiceLocator
160                                            .getAssignmentService().getActiveAssignmentsForWorkArea(
161                                                            waNum, TKUtils.getTimelessDate(currentDate));
162    
163                            if (assignments != null) {
164                                    for (Assignment assignment : assignments) {
165                                            principals.add(assignment.getPrincipalId());
166                                    }
167                            }
168                    }
169    
170                    // Get the pay calendars
171                    Set<Calendar> payCals = new HashSet<Calendar>();
172                    for (String pid : principals) {
173                            PrincipalHRAttributes pc = TkServiceLocator
174                                            .getPrincipalHRAttributeService().getPrincipalCalendar(pid,
175                                currentDate);
176                            if (pc == null)
177                                    pc = TkServiceLocator.getPrincipalHRAttributeService()
178                                                    .getPrincipalCalendar(pid, windowDate);
179    
180                            if (pc != null) {
181                                    payCals.add(pc.getCalendar());
182                            } else {
183                                    LOG.warn("PrincipalCalendar null for principal: '" + pid + "'");
184                            }
185                    }
186    
187                    // Grab the pay calendar entries + groups
188                    for (Calendar pc : payCals) {
189                            CalendarEntries pce = TkServiceLocator
190                                            .getCalendarEntriesService()
191                                            .getCurrentCalendarEntriesByCalendarId(
192                                pc.getHrCalendarId(), currentDate);
193                            pceMap.put(pc.getCalendarName(), pce);
194                    }
195    
196                    return pceMap;
197            }
198    
199            public SortedSet<String> getApproverPayCalendarGroups(Date payBeginDate,
200                            Date payEndDate) {
201                    SortedSet<String> pcg = new TreeSet<String>();
202    
203                    TKUser tkUser = TKContext.getUser();
204                    Set<Long> approverWorkAreas = TkUserRoles.getUserRoles(GlobalVariables.getUserSession().getPrincipalId()).getApproverWorkAreas();
205                    List<Assignment> assignments = new ArrayList<Assignment>();
206    
207                    for (Long workArea : approverWorkAreas) {
208                            if (workArea != null) {
209                                    assignments.addAll(TkServiceLocator.getAssignmentService()
210                                                    .getActiveAssignmentsForWorkArea(workArea,
211                                                                    new java.sql.Date(payBeginDate.getTime())));
212                            }
213                    }
214                    if (!assignments.isEmpty()) {
215                            for (Assignment assign : assignments) {
216                                    String principalId = assign.getPrincipalId();
217                                    TimesheetDocumentHeader tdh = TkServiceLocator
218                                                    .getTimesheetDocumentHeaderService().getDocumentHeader(
219                                                                    principalId, payBeginDate, payEndDate);
220                                    if (tdh != null) {
221                                            String pyCalendarGroup = TkServiceLocator
222                                                            .getPrincipalHRAttributeService()
223                                                            .getPrincipalCalendar(principalId, tdh.getPayBeginDate())
224                                                            .getCalendar().getCalendarName();
225                                            pcg.add(pyCalendarGroup);
226                                    }
227                            }
228                    }
229                    return pcg;
230            }
231    
232            @SuppressWarnings("rawtypes")
233            @Override
234            public List<ApprovalTimeSummaryRow> getApprovalSummaryRows(
235                            Date payBeginDate, Date payEndDate, String calGroup,
236                            List<TKPerson> persons, List<String> payCalendarLabels,
237                            CalendarEntries payCalendarEntries) {
238                    List<ApprovalTimeSummaryRow> rows = new LinkedList<ApprovalTimeSummaryRow>();
239                    Map<String, TimesheetDocumentHeader> principalDocumentHeader = getPrincipalDocumehtHeader(
240                                    persons, payBeginDate, payEndDate);
241    
242                    Calendar payCalendar = TkServiceLocator.getCalendarService()
243                                    .getCalendar(payCalendarEntries.getHrCalendarId());
244                    DateTimeZone dateTimeZone = TkServiceLocator.getTimezoneService()
245                                    .getUserTimezoneWithFallback();
246                    List<Interval> dayIntervals = TKUtils
247                                    .getDaySpanForCalendarEntry(payCalendarEntries);
248    
249                    for (TKPerson person : persons) {
250                            TimesheetDocumentHeader tdh = new TimesheetDocumentHeader();
251                            String documentId = "";
252                            if (principalDocumentHeader.containsKey(person.getPrincipalId())) {
253                                    tdh = principalDocumentHeader.get(person.getPrincipalId());
254                                    documentId = principalDocumentHeader.get(
255                                                    person.getPrincipalId()).getDocumentId();
256                            }
257                            List<TimeBlock> timeBlocks = new ArrayList<TimeBlock>();
258                            List notes = new ArrayList();
259                            List<String> warnings = new ArrayList<String>();
260    
261                            ApprovalTimeSummaryRow approvalSummaryRow = new ApprovalTimeSummaryRow();
262    
263                            if (principalDocumentHeader.containsKey(person.getPrincipalId())) {
264                                    approvalSummaryRow
265                                                    .setApprovalStatus(TkConstants.DOC_ROUTE_STATUS.get(tdh
266                                                                    .getDocumentStatus()));
267                            }
268    
269                            if (StringUtils.isNotBlank(documentId)) {
270                                    timeBlocks = TkServiceLocator.getTimeBlockService()
271                                                    .getTimeBlocks(documentId);
272                                    notes = this.getNotesForDocument(documentId);
273                    TimesheetDocument td = TkServiceLocator.getTimesheetService().getTimesheetDocument(documentId);
274                                    warnings = TkServiceLocator.getWarningService().getWarnings(td);
275                            }
276    
277                            Map<String, BigDecimal> hoursToPayLabelMap = getHoursToPayDayMap(
278                                            person.getPrincipalId(), payEndDate, payCalendarLabels,
279                                            timeBlocks, null, payCalendarEntries, payCalendar,
280                                            dateTimeZone, dayIntervals);
281    
282                            approvalSummaryRow.setName(person.getPrincipalName());
283                            approvalSummaryRow.setPrincipalId(person.getPrincipalId());
284                            approvalSummaryRow.setPayCalendarGroup(calGroup);
285                            approvalSummaryRow.setDocumentId(documentId);
286                            approvalSummaryRow.setHoursToPayLabelMap(hoursToPayLabelMap);
287                            approvalSummaryRow.setPeriodTotal(hoursToPayLabelMap
288                                            .get("Period Total"));
289                            approvalSummaryRow.setLstTimeBlocks(timeBlocks);
290                            approvalSummaryRow.setNotes(notes);
291                            approvalSummaryRow.setWarnings(warnings);
292    
293                            // Compare last clock log versus now and if > threshold
294                            // highlight entry
295                            ClockLog lastClockLog = TkServiceLocator.getClockLogService()
296                                            .getLastClockLog(person.getPrincipalId());
297                            approvalSummaryRow
298                                            .setClockStatusMessage(createLabelForLastClockLog(lastClockLog));
299                            if (lastClockLog != null
300                                            && (StringUtils.equals(lastClockLog.getClockAction(),
301                                                            TkConstants.CLOCK_IN) || StringUtils
302                                                            .equals(lastClockLog.getClockAction(),
303                                                                            TkConstants.LUNCH_IN))) {
304                                    DateTime startTime = new DateTime(lastClockLog
305                                                    .getClockTimestamp().getTime());
306                                    DateTime endTime = new DateTime(System.currentTimeMillis());
307    
308                                    Hours hour = Hours.hoursBetween(startTime, endTime);
309                                    if (hour != null) {
310                                            int elapsedHours = hour.getHours();
311                                            if (elapsedHours >= TkConstants.NUMBER_OF_HOURS_CLOCKED_IN_APPROVE_TAB_HIGHLIGHT) {
312                                                    approvalSummaryRow.setClockedInOverThreshold(true);
313                                            }
314                                    }
315    
316                            }
317                            rows.add(approvalSummaryRow);
318                    }
319                    return rows;
320            }
321    
322            public List<TimesheetDocumentHeader> getDocumentHeadersByPrincipalIds(
323                            Date payBeginDate, Date payEndDate, List<String> principalIds) {
324                    List<TimesheetDocumentHeader> headers = new LinkedList<TimesheetDocumentHeader>();
325                    for (String principalId : principalIds) {
326                            TimesheetDocumentHeader tdh = TkServiceLocator
327                                            .getTimesheetDocumentHeaderService().getDocumentHeader(
328                                                            principalId, payBeginDate, payEndDate);
329                            if (tdh != null) {
330                                    headers.add(tdh);
331                            }
332                    }
333    
334                    return headers;
335            }
336    
337            /**
338             * Get pay calendar labels for approval tab
339             */
340            public List<String> getPayCalendarLabelsForApprovalTab(Date payBeginDate,
341                            Date payEndDate) {
342                    // :)
343                    // http://stackoverflow.com/questions/111933/why-shouldnt-i-use-hungarian-notation
344                    List<String> lstPayCalendarLabels = new ArrayList<String>();
345                    DateTime payBegin = new DateTime(payBeginDate.getTime());
346                    DateTime payEnd = new DateTime(payEndDate.getTime());
347                    DateTime currTime = payBegin;
348                    int dayCounter = 1;
349                    int weekCounter = 1;
350    
351                    while (currTime.isBefore(payEnd)) {
352                            String labelForDay = createLabelForDay(currTime);
353                            lstPayCalendarLabels.add(labelForDay);
354                            currTime = currTime.plusDays(1);
355                            if ((dayCounter % 7) == 0) {
356                                    lstPayCalendarLabels.add("Week " + weekCounter);
357                                    weekCounter++;
358                            }
359                            dayCounter++;
360                    }
361                    lstPayCalendarLabels.add("Total Hours");
362                    return lstPayCalendarLabels;
363            }
364    
365            /**
366             * Create label for a given pay calendar day
367             * 
368             * @param fromDate
369             * @return
370             */
371            private String createLabelForDay(DateTime fromDate) {
372                    DateMidnight dateMidnight = new DateMidnight(fromDate);
373                    if (dateMidnight.compareTo(fromDate) == 0) {
374                            DateTimeFormatter fmt = DateTimeFormat.forPattern("MMM/dd");
375                            return fmt.print(fromDate);
376                    }
377                    DateTime toDate = fromDate.plusDays(1);
378                    DateTimeFormatter fmt = DateTimeFormat.forPattern("MMM/dd k:m:s");
379                    return fmt.print(fromDate) + "-" + fmt.print(toDate);
380            }
381    
382            /**
383             * Create label for the last clock log
384             * 
385             * @param cl
386             * @return
387             */
388            private String createLabelForLastClockLog(ClockLog cl) {
389                    // return sdf.format(dt);
390                    if (cl == null) {
391                            return "No previous clock information";
392                    }
393                    SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy hh:mm a");
394                    String dateTime = sdf.format(new java.sql.Date(cl.getClockTimestamp()
395                                    .getTime()));
396                    if (StringUtils.equals(cl.getClockAction(), TkConstants.CLOCK_IN)) {
397                            return "Clocked in since: " + dateTime;
398                    } else if (StringUtils.equals(cl.getClockAction(),
399                                    TkConstants.LUNCH_OUT)) {
400                            return "At Lunch since: " + dateTime;
401                    } else if (StringUtils
402                                    .equals(cl.getClockAction(), TkConstants.LUNCH_IN)) {
403                            return "Returned from Lunch : " + dateTime;
404                    } else if (StringUtils.equals(cl.getClockAction(),
405                                    TkConstants.CLOCK_OUT)) {
406                            return "Clocked out since: " + dateTime;
407                    } else {
408                            return "No previous clock information";
409                    }
410    
411            }
412    
413            public List<Map<String, Map<String, BigDecimal>>> getHoursByDayAssignmentBuckets(
414                            TkTimeBlockAggregate aggregate,
415                            List<Assignment> approverAssignments, List<String> payCalendarLabels) {
416                    Map<String, Assignment> mappedAssignments = mapAssignmentsByAssignmentKey(approverAssignments);
417                    List<List<TimeBlock>> blocksByDay = aggregate.getDayTimeBlockList();
418    
419                    // (assignment_key, <List of Hours Summed by Day>)
420                    Map<String, List<BigDecimal>> approverHours = new HashMap<String, List<BigDecimal>>();
421                    Map<String, List<BigDecimal>> otherHours = new HashMap<String, List<BigDecimal>>();
422                    for (int day = 0; day < blocksByDay.size(); day++) {
423                            List<TimeBlock> dayBlocks = blocksByDay.get(day);
424                            for (TimeBlock block : dayBlocks) {
425                                    List<BigDecimal> hours;
426                                    // Approver vs. Other :: Set our day-hour-list object
427                                    if (mappedAssignments.containsKey(block.getAssignmentKey())) {
428                                            hours = approverHours.get(block.getAssignmentKey());
429                                            if (hours == null) {
430                                                    hours = new ArrayList<BigDecimal>();
431                                                    approverHours.put(block.getAssignmentKey(), hours);
432                                            }
433                                    } else {
434                                            hours = otherHours.get(block.getAssignmentKey());
435                                            if (hours == null) {
436                                                    hours = new ArrayList<BigDecimal>();
437                                                    otherHours.put(block.getAssignmentKey(), hours);
438                                            }
439                                    }
440    
441                                    // Fill in zeroes for days with 0 hours / no time blocks
442                                    for (int fill = hours.size(); fill <= day; fill++) {
443                                            hours.add(TkConstants.BIG_DECIMAL_SCALED_ZERO);
444                                    }
445    
446                                    // Add time from time block to existing time.
447                                    BigDecimal timeToAdd = hours.get(day);
448                                    timeToAdd = timeToAdd.add(block.getHours(),
449                                                    TkConstants.MATH_CONTEXT);
450                                    hours.set(day, timeToAdd);
451                            }
452                    }
453    
454                    // Compute Weekly / Period Summary Totals for each Assignment.
455                    // assignment row, each row has a map of pay calendar label -> big
456                    // decimal totals.
457                    Map<String, Map<String, BigDecimal>> approverAssignToPayHourTotals = new HashMap<String, Map<String, BigDecimal>>();
458                    Map<String, Map<String, BigDecimal>> otherAssignToPayHourTotals = new HashMap<String, Map<String, BigDecimal>>();
459    
460                    // Pass by Reference
461                    generateSummaries(approverAssignToPayHourTotals, approverHours,
462                                    payCalendarLabels);
463                    generateSummaries(otherAssignToPayHourTotals, otherHours,
464                                    payCalendarLabels);
465    
466                    // Add to our return list, "virtual" tuple.
467                    List<Map<String, Map<String, BigDecimal>>> returnTuple = new ArrayList<Map<String, Map<String, BigDecimal>>>(
468                                    2);
469                    returnTuple.add(approverAssignToPayHourTotals);
470                    returnTuple.add(otherAssignToPayHourTotals);
471    
472                    return returnTuple;
473            }
474    
475            // Helper method for above method.
476            private void generateSummaries(
477                            Map<String, Map<String, BigDecimal>> payHourTotals,
478                            Map<String, List<BigDecimal>> assignmentToHours,
479                            List<String> payCalendarLabels) {
480                    for (String key : assignmentToHours.keySet()) {
481                            // for every Assignment
482                            Map<String, BigDecimal> hoursToPayLabelMap = new LinkedHashMap<String, BigDecimal>();
483                            List<BigDecimal> dayTotals = assignmentToHours.get(key);
484                            int dayCount = 0;
485                            BigDecimal weekTotal = new BigDecimal(0.00);
486                            BigDecimal periodTotal = new BigDecimal(0.00);
487                            for (String payCalendarLabel : payCalendarLabels) {
488                                    if (StringUtils.contains(payCalendarLabel, "Week")) {
489                                            hoursToPayLabelMap.put(payCalendarLabel, weekTotal);
490                                            weekTotal = new BigDecimal(0.00);
491                                    } else if (StringUtils
492                                                    .contains(payCalendarLabel, "Total Hours")) {
493                                            hoursToPayLabelMap.put(payCalendarLabel, periodTotal);
494                                    } else {
495                                            BigDecimal dayTotal = TkConstants.BIG_DECIMAL_SCALED_ZERO;
496                                            if (dayCount < dayTotals.size())
497                                                    dayTotal = dayTotals.get(dayCount);
498    
499                                            hoursToPayLabelMap.put(payCalendarLabel, dayTotal);
500                                            weekTotal = weekTotal.add(dayTotal,
501                                                            TkConstants.MATH_CONTEXT);
502                                            periodTotal = periodTotal.add(dayTotal);
503                                            dayCount++;
504                                    }
505                            }
506                            payHourTotals.put(key, hoursToPayLabelMap);
507                    }
508            }
509    
510            private Map<String, Assignment> mapAssignmentsByAssignmentKey(
511                            List<Assignment> assignments) {
512                    Map<String, Assignment> assignmentMap = new HashMap<String, Assignment>();
513                    for (Assignment assignment : assignments) {
514                            assignmentMap
515                                            .put(AssignmentDescriptionKey
516                                                            .getAssignmentKeyString(assignment), assignment);
517                    }
518                    return assignmentMap;
519            }
520    
521            /**
522             * Aggregate TimeBlocks to hours per day and sum for week
523             * 
524             * @param principalId
525             * @param payEndDate
526             * @param payCalendarLabels
527             * @param lstTimeBlocks
528             * @param workArea
529             * @return
530             */
531            @Override
532            public Map<String, BigDecimal> getHoursToPayDayMap(String principalId,
533                            Date payEndDate, List<String> payCalendarLabels,
534                            List<TimeBlock> lstTimeBlocks, Long workArea,
535                            CalendarEntries payCalendarEntries, Calendar payCalendar,
536                            DateTimeZone dateTimeZone, List<Interval> dayIntervals) {
537                    Map<String, BigDecimal> hoursToPayLabelMap = new LinkedHashMap<String, BigDecimal>();
538                    List<BigDecimal> dayTotals = new ArrayList<BigDecimal>();
539    
540                    TkTimeBlockAggregate tkTimeBlockAggregate = new TkTimeBlockAggregate(
541                                    lstTimeBlocks, payCalendarEntries, payCalendar, true,
542                                    dayIntervals);
543                    List<FlsaWeek> flsaWeeks = tkTimeBlockAggregate
544                                    .getFlsaWeeks(dateTimeZone);
545                    for (FlsaWeek week : flsaWeeks) {
546                            for (FlsaDay day : week.getFlsaDays()) {
547                                    BigDecimal total = new BigDecimal(0.00);
548                                    for (TimeBlock tb : day.getAppliedTimeBlocks()) {
549                                            if (workArea != null) {
550                                                    if (tb.getWorkArea().compareTo(workArea) == 0) {
551                                                            total = total.add(tb.getHours(),
552                                                                            TkConstants.MATH_CONTEXT);
553                                                    } else {
554                                                            total = total.add(new BigDecimal("0"),
555                                                                            TkConstants.MATH_CONTEXT);
556                                                    }
557                                            } else {
558                                                    total = total.add(tb.getHours(),
559                                                                    TkConstants.MATH_CONTEXT);
560                                            }
561                                    }
562                                    dayTotals.add(total);
563                            }
564                    }
565    
566                    int dayCount = 0;
567                    BigDecimal weekTotal = new BigDecimal(0.00);
568                    BigDecimal periodTotal = new BigDecimal(0.00);
569                    for (String payCalendarLabel : payCalendarLabels) {
570                            if (StringUtils.contains(payCalendarLabel, "Week")) {
571                                    hoursToPayLabelMap.put(payCalendarLabel, weekTotal);
572                                    weekTotal = new BigDecimal(0.00);
573                            } else if (StringUtils.contains(payCalendarLabel, "Period Total")) {
574                                    hoursToPayLabelMap.put(payCalendarLabel, periodTotal);
575                            } else {
576                                    if(dayCount < dayTotals.size()) {
577                                            hoursToPayLabelMap.put(payCalendarLabel,
578                                                            dayTotals.get(dayCount));
579                                            weekTotal = weekTotal.add(dayTotals.get(dayCount),
580                                                            TkConstants.MATH_CONTEXT);
581                                            periodTotal = periodTotal.add(dayTotals.get(dayCount));
582                                            dayCount++;
583                                    }
584    
585                            }
586    
587                    }
588                    return hoursToPayLabelMap;
589            }
590    
591            public boolean doesApproverHavePrincipalsForCalendarGroup(Date asOfDate,
592                            String calGroup) {
593                    TKUser tkUser = TKContext.getUser();
594                    Set<Long> approverWorkAreas = TkUserRoles.getUserRoles(GlobalVariables.getUserSession().getPrincipalId()).getApproverWorkAreas();
595                    for (Long workArea : approverWorkAreas) {
596                            List<Assignment> assignments = TkServiceLocator
597                                            .getAssignmentService().getActiveAssignmentsForWorkArea(
598                                                            workArea, new java.sql.Date(asOfDate.getTime()));
599                            List<String> principalIds = new ArrayList<String>();
600                            for (Assignment assign : assignments) {
601                                    if (principalIds.contains(assign.getPrincipalId())) {
602                                            continue;
603                                    }
604                                    principalIds.add(assign.getPrincipalId());
605                            }
606    
607                            for (String principalId : principalIds) {
608                                    PrincipalHRAttributes principalCal = TkServiceLocator
609                                                    .getPrincipalHRAttributeService().getPrincipalCalendar(
610                                                                    principalId, asOfDate);
611                                    if (StringUtils.equals(principalCal.getPayCalendar(),
612                                                    calGroup)) {
613                                            return true;
614                                    }
615                            }
616                    }
617                    return false;
618            }
619    
620            @SuppressWarnings("rawtypes")
621            public List getNotesForDocument(String documentNumber) {
622            List notes = KewApiServiceLocator.getNoteService().getNotes(documentNumber);
623                    // add the user name in the note object
624    //              for (Object obj : notes) {
625    //                      Note note = (Note) obj;
626    //                      note.setNoteAuthorFullName(KimApiServiceLocator.getPersonService()
627    //                                      .getPerson(note.getNoteAuthorWorkflowId()).getName());
628    //              }
629                    return notes;
630            }
631    
632            @Override
633            public List<String> getUniquePayGroups() {
634                    String sql = "SELECT DISTINCT P.pay_calendar FROM hr_principal_attributes_t P WHERE P.active = 'Y'";
635                    SqlRowSet rs = TkServiceLocator.getTkJdbcTemplate().queryForRowSet(sql);
636                    List<String> pyGroups = new LinkedList<String>();
637                    while (rs.next()) {
638                            pyGroups.add(rs.getString("pay_calendar"));
639                    }
640                    return pyGroups;
641            }
642    
643            @Override
644            public List<String> getPrincipalIdsByDeptWorkAreaRolename(String roleName,
645                            String department, String workArea, java.sql.Date payBeginDate,
646                            java.sql.Date payEndDate, String calGroup) {
647                    List<String> principalIds = getPrincipalIdsWithActiveAssignmentsForCalendarGroupByDeptAndWorkArea(
648                                    roleName, department, workArea, calGroup, payEndDate,
649                                    payBeginDate, payEndDate);
650                    return principalIds;
651            }
652            
653            protected List<String> getPrincipalIdsWithActiveAssignmentsForCalendarGroupByDeptAndWorkArea(
654                          String roleName, String department, String workArea,
655                          String payCalendarGroup, java.sql.Date effdt,
656                          java.sql.Date beginDate, java.sql.Date endDate) {
657                String sql = null;
658    
659            List<Job> jobs = TkServiceLocator.getJobService().getJobs(TKUser.getCurrentTargetPerson().getPrincipalId(), effdt);
660            String jobPositionNumbersList = "'";
661            for (Job job : jobs) {
662                            jobPositionNumbersList += job.getPositionNumber() + "','";
663            }
664            /* the sql statement will enclose this string in single quotes, so we do not want the leading quote, or the trailing quote, comma, and quote. */
665            if (jobPositionNumbersList.length() > 3) {
666                jobPositionNumbersList = jobPositionNumbersList.substring(1, jobPositionNumbersList.length()-3) ;
667            } else {
668                jobPositionNumbersList = jobPositionNumbersList.substring(1);
669            }
670    
671                if (department == null || department.isEmpty()) {
672                  return new ArrayList<String>();
673                } else {
674                  List<String> principalIds = new ArrayList<String>();
675                  SqlRowSet rs = null;
676              sql = "SELECT DISTINCT A0.PRINCIPAL_ID FROM TK_ASSIGNMENT_T A0, HR_ROLES_T R0, TK_WORK_AREA_T W0, HR_PRINCIPAL_ATTRIBUTES_T P0  WHERE "
677                              + "((A0.EFFDT =  (SELECT MAX(EFFDT)  FROM TK_ASSIGNMENT_T  WHERE PRINCIPAL_ID = A0.PRINCIPAL_ID  AND EFFDT <= ? AND WORK_AREA = A0.WORK_AREA  AND TASK = A0.TASK AND JOB_NUMBER = A0.JOB_NUMBER) AND "
678                      + "A0.TIMESTAMP =  (SELECT MAX(TIMESTAMP)  FROM TK_ASSIGNMENT_T  WHERE PRINCIPAL_ID = A0.PRINCIPAL_ID  AND EFFDT = A0.EFFDT AND WORK_AREA = A0.WORK_AREA AND TASK = A0.TASK AND JOB_NUMBER = A0.JOB_NUMBER) AND "
679                      + "A0.ACTIVE = 'Y') OR (A0.ACTIVE = 'N'  AND A0.EFFDT >= ? AND A0.EFFDT <= ?)) AND "
680                      + "R0.WORK_AREA = A0.WORK_AREA AND "
681                      + "R0.ROLE_NAME IN ('TK_APPROVER', 'TK_APPROVER_DELEGATE', 'TK_REVIEWER') AND "
682                      + "R0.ACTIVE = 'Y' AND "
683                      + "( (R0.PRINCIPAL_ID = ? AND "
684                      + "R0.EFFDT = (SELECT MAX(EFFDT)  FROM HR_ROLES_T  WHERE ROLE_NAME = R0.ROLE_NAME AND PRINCIPAL_ID = R0.PRINCIPAL_ID AND EFFDT <= ? AND WORK_AREA = R0.WORK_AREA) AND "
685                      + "R0.TIMESTAMP = (SELECT MAX(TIMESTAMP)  FROM HR_ROLES_T  WHERE ROLE_NAME = R0.ROLE_NAME AND PRINCIPAL_ID = R0.PRINCIPAL_ID AND EFFDT = R0.EFFDT AND WORK_AREA = R0.WORK_AREA) "
686                      + ") or ("
687                      + "R0.POSITION_NBR in (?) AND "
688                      + "R0.EFFDT = (SELECT MAX(EFFDT)  FROM HR_ROLES_T  WHERE ROLE_NAME = R0.ROLE_NAME AND POSITION_NBR = R0.POSITION_NBR AND EFFDT <= ? AND WORK_AREA = R0.WORK_AREA) AND "
689                      + "R0.TIMESTAMP = (SELECT MAX(TIMESTAMP)  FROM HR_ROLES_T  WHERE ROLE_NAME = R0.ROLE_NAME AND POSITION_NBR = R0.POSITION_NBR AND EFFDT = R0.EFFDT AND WORK_AREA = R0.WORK_AREA) "
690                      + ") ) AND "
691                      + "W0.WORK_AREA = A0.WORK_AREA AND "
692                      + "W0.DEPT = ? AND "
693                      + "W0.EFFDT = (SELECT MAX(EFFDT) FROM TK_WORK_AREA_T WHERE EFFDT <= ? AND WORK_AREA = W0.WORK_AREA) AND "
694                      + "W0.TIMESTAMP =  (SELECT MAX(TIMESTAMP)  FROM TK_WORK_AREA_T  WHERE WORK_AREA = W0.WORK_AREA  AND EFFDT = W0.EFFDT) AND "
695                      + "W0.ACTIVE = 'Y' AND "
696                      + "P0.PRINCIPAL_ID = A0.PRINCIPAL_ID AND "
697                      + "P0.PAY_CALENDAR = ?";
698    
699    
700                   int[] params = null;
701                   Object[] values = null;
702                   if (workArea != null) {
703                      sql += " AND A0.WORK_AREA = ? ";
704                      params = new int[] {java.sql.Types.DATE,
705                          java.sql.Types.DATE,
706                          java.sql.Types.DATE,
707                          java.sql.Types.VARCHAR, 
708                          java.sql.Types.DATE,
709                      java.sql.Types.VARCHAR,
710                      java.sql.Types.DATE,
711                          java.sql.Types.VARCHAR,
712                          java.sql.Types.DATE,
713                          java.sql.Types.VARCHAR,
714                          java.sql.Types.INTEGER };
715                      values = new Object[] {effdt, beginDate, endDate, TKUser.getCurrentTargetPerson().getPrincipalId(), effdt, jobPositionNumbersList, effdt, department, effdt, payCalendarGroup, workArea };
716                    }else {
717                      params = new int[] {java.sql.Types.DATE,
718                          java.sql.Types.DATE,
719                          java.sql.Types.DATE,
720                          java.sql.Types.VARCHAR, 
721                          java.sql.Types.DATE,
722                      java.sql.Types.VARCHAR,
723                      java.sql.Types.DATE,
724                          java.sql.Types.VARCHAR,
725                          java.sql.Types.DATE,
726                          java.sql.Types.VARCHAR};
727                      values = new Object[] {effdt, beginDate, endDate, TKUser.getCurrentTargetPerson().getPrincipalId(), effdt, jobPositionNumbersList, effdt, department, effdt, payCalendarGroup};
728                    }
729                    rs = TkServiceLocator.getTkJdbcTemplate().queryForRowSet(
730                        sql, values, params);
731                  while (rs.next()) {
732                    principalIds.add(rs.getString("principal_id"));
733                  }
734                  return principalIds;
735                }
736            }
737    
738            @Override
739            public Map<String, TimesheetDocumentHeader> getPrincipalDocumehtHeader(
740                            List<TKPerson> persons, Date payBeginDate, Date payEndDate) {
741                    Map<String, TimesheetDocumentHeader> principalDocumentHeader = new LinkedHashMap<String, TimesheetDocumentHeader>();
742                    for (TKPerson person : persons) {
743                            String principalId = person.getPrincipalId();
744                            
745                            TimesheetDocumentHeader tdh = TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeader(principalId, payBeginDate, payEndDate);
746                            if(tdh != null) {
747                                    principalDocumentHeader.put(principalId, tdh);  
748                            }
749                    }
750                    return principalDocumentHeader;
751            }
752    
753            @Override
754            public Multimap<String, Long> getDeptWorkAreasByWorkAreas(
755                            Set<Long> approverWorkAreas) {
756                    Multimap<String, Long> deptWorkAreas = HashMultimap.create();
757    
758                    if (approverWorkAreas.size() > 0) {
759                            // prepare the OR statement for query
760                            StringBuilder workAreas = new StringBuilder();
761                            for (Long workarea : approverWorkAreas) {
762                                    if(workarea != null) {
763                                            workAreas.append("work_area = " + workarea + " or ");
764                                    }
765                            }
766                            String workAreasForQuery = workAreas.substring(0,
767                                            workAreas.length() - 3);
768                            String sql = "SELECT DISTINCT work_area, dept FROM tk_work_area_t "
769                                            + "WHERE " + workAreasForQuery + " AND effdt <= ?";
770    
771                            /**
772                             * Multimap is an interface from Google's java common library -
773                             * Guava. HashMultimap allows us to create a map with duplicate keys
774                             * which will then generate a data structure, i.e. [key] => [value1,
775                             * value2, value3...]
776                             * 
777                             * It save a good lines of code to do the same thing through the
778                             * java map, e.g. Map<String, List<String>> map = new
779                             * Hashmap<String, List<String>>();
780                             * 
781                             * See the java doc for more information:
782                             * http://google-collections.googlecode
783                             * .com/svn/trunk/javadoc/com/google/common/collect/Multimap.html
784                             */
785                            SqlRowSet rs = TkServiceLocator.getTkJdbcTemplate().queryForRowSet(
786                                            sql, new Object[] { TKUtils.getCurrentDate() },
787                                            new int[] { Types.DATE });
788                            while (rs.next()) {
789                                    deptWorkAreas
790                                                    .put(rs.getString("dept"), rs.getLong("work_area"));
791                            }
792                    }
793                    return deptWorkAreas;
794            }
795    
796            @Override
797            public Multimap<String, Long> getDeptWorkAreasByDepts(Set<String> userDepts) {
798                    Multimap<String, Long> deptWorkAreas = HashMultimap.create();
799    
800                    if (userDepts.size() > 0) {
801                            // prepare the OR statement for query
802                            StringBuilder depts = new StringBuilder();
803                            for (String dept : userDepts) {
804                                    depts.append("dept = '" + dept + "' or ");
805                            }
806                            String deptsForQuery = depts.substring(0, depts.length() - 4);
807                            String sql = "SELECT DISTINCT work_area, dept FROM tk_work_area_t "
808                                            + "WHERE " + deptsForQuery + " AND effdt <= ?";
809    
810                            SqlRowSet rs = TkServiceLocator.getTkJdbcTemplate().queryForRowSet(
811                                            sql, new Object[] { TKUtils.getCurrentDate() },
812                                            new int[] { Types.DATE });
813                            while (rs.next()) {
814                                    deptWorkAreas
815                                                    .put(rs.getString("dept"), rs.getLong("work_area"));
816                            }
817                    }
818                    return deptWorkAreas;
819            }
820    
821            public DocumentRouteHeaderValue getRouteHeader(String documentId) {
822                    return KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId);
823            }
824            
825            @Override
826            public List<CalendarEntries> getAllPayCalendarEntriesForApprover(String principalId, Date currentDate) {
827                    TKUser tkUser = TKContext.getUser();
828                    Set<String> principals = new HashSet<String>();
829                    DateTime minDt = new DateTime(currentDate,
830                                    TKUtils.getSystemDateTimeZone());
831                    minDt = minDt.minusDays(DAYS_WINDOW_DELTA);
832                    Set<Long> approverWorkAreas = TkUserRoles.getUserRoles(GlobalVariables.getUserSession().getPrincipalId()).getApproverWorkAreas();
833    
834                    // Get all of the principals within our window of time.
835                    for (Long waNum : approverWorkAreas) {
836                            List<Assignment> assignments = TkServiceLocator
837                                            .getAssignmentService().getActiveAssignmentsForWorkArea(waNum, TKUtils.getTimelessDate(currentDate));
838    
839                            if (assignments != null) {
840                                    for (Assignment assignment : assignments) {
841                                            principals.add(assignment.getPrincipalId());
842                                    }
843                            }
844                    }
845                    List<TimesheetDocumentHeader> documentHeaders = new ArrayList<TimesheetDocumentHeader>();
846                    for(String pid : principals) {
847                            documentHeaders.addAll(TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeadersForPrincipalId(pid));
848                    }
849                    Set<CalendarEntries> payPeriodSet = new HashSet<CalendarEntries>();
850                    for(TimesheetDocumentHeader tdh : documentHeaders) {
851                    CalendarEntries pe = TkServiceLocator.getCalendarEntriesService().getCalendarEntriesByBeginAndEndDate(tdh.getPayBeginDate(), tdh.getPayEndDate());
852                    if(pe != null) {
853                            payPeriodSet.add(pe);
854                    }
855            }
856                    List<CalendarEntries> ppList = new ArrayList<CalendarEntries>(payPeriodSet);
857            
858                    return ppList;
859            }
860            
861    }