View Javadoc
1   /**
2    * Copyright 2004-2015 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.kpme.tklm.leave.approval.service;
17  
18  import java.math.BigDecimal;
19  import java.text.SimpleDateFormat;
20  import java.util.ArrayList;
21  import java.util.Date;
22  import java.util.HashMap;
23  import java.util.HashSet;
24  import java.util.LinkedHashMap;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Map.Entry;
28  import java.util.Set;
29  import java.util.TreeSet;
30  
31  import org.apache.commons.collections.CollectionUtils;
32  import org.apache.commons.lang.StringUtils;
33  import org.joda.time.DateTime;
34  import org.joda.time.DateTimeConstants;
35  import org.joda.time.DateTimeFieldType;
36  import org.joda.time.LocalDate;
37  import org.joda.time.LocalDateTime;
38  import org.kuali.kpme.core.accrualcategory.AccrualCategory;
39  import org.kuali.kpme.core.accrualcategory.rule.AccrualCategoryRule;
40  import org.kuali.kpme.core.assignment.Assignment;
41  import org.kuali.kpme.core.calendar.Calendar;
42  import org.kuali.kpme.core.calendar.entry.CalendarEntry;
43  import org.kuali.kpme.core.principal.PrincipalHRAttributes;
44  import org.kuali.kpme.core.service.HrServiceLocator;
45  import org.kuali.kpme.core.util.HrConstants;
46  import org.kuali.kpme.tklm.common.LMConstants;
47  import org.kuali.kpme.tklm.common.TkConstants;
48  import org.kuali.kpme.tklm.leave.approval.web.ApprovalLeaveSummaryRow;
49  import org.kuali.kpme.tklm.leave.block.LeaveBlock;
50  import org.kuali.kpme.tklm.leave.calendar.LeaveCalendarDocument;
51  import org.kuali.kpme.tklm.leave.calendar.validation.LeaveCalendarValidationUtil;
52  import org.kuali.kpme.tklm.leave.service.LmServiceLocator;
53  import org.kuali.kpme.tklm.leave.summary.LeaveSummary;
54  import org.kuali.kpme.tklm.leave.summary.LeaveSummaryRow;
55  import org.kuali.kpme.tklm.leave.workflow.LeaveCalendarDocumentHeader;
56  import org.kuali.kpme.tklm.time.timesummary.TimeSummary;
57  import org.kuali.rice.kew.api.KewApiServiceLocator;
58  import org.kuali.rice.kew.api.action.ActionRequest;
59  import org.kuali.rice.kew.api.exception.WorkflowException;
60  import org.kuali.rice.kew.api.note.Note;
61  import org.kuali.rice.kim.api.identity.principal.EntityNamePrincipalName;
62  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
63  
64  public class LeaveApprovalServiceImpl implements LeaveApprovalService {
65  	
66  	public static final int DAYS_WINDOW_DELTA = 31;
67  
68  	@Override
69  	public List<ApprovalLeaveSummaryRow> getLeaveApprovalSummaryRows(List<String> principalIds, CalendarEntry payCalendarEntry, List<Date> leaveSummaryDates, String docIdSearchTerm) {
70  		DateTime payBeginDate = payCalendarEntry.getBeginPeriodFullDateTime();
71  		DateTime payEndDate = payCalendarEntry.getEndPeriodFullDateTime();
72  		List<ApprovalLeaveSummaryRow> rowList = new ArrayList<ApprovalLeaveSummaryRow>();		
73  		for(String principalId : principalIds) {
74  			
75  			ApprovalLeaveSummaryRow aRow = new ApprovalLeaveSummaryRow();
76              List<Note> notes = new ArrayList<Note>();
77  //            List<String> warnings = new ArrayList<String>();
78              EntityNamePrincipalName name = KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(principalId);
79              aRow.setName(name != null
80                             && name.getDefaultName() != null
81                             && name.getDefaultName().getCompositeName() != null ? name.getDefaultName().getCompositeName() : principalId);
82  			aRow.setPrincipalId(principalId);
83  			
84  			try {
85  				aRow.setLeaveSummary(LmServiceLocator.getLeaveSummaryService().getLeaveSummary(principalId, payCalendarEntry));
86  			} catch (Exception e1) {
87  				// TODO Auto-generated catch block
88  				e1.printStackTrace();
89  			}
90  			
91  			String lastApprovedString = "No previous approved leave calendar information";
92  			LeaveCalendarDocumentHeader lastApprovedDoc = LmServiceLocator.getLeaveCalendarDocumentHeaderService().getMaxEndDateApprovedLeaveCalendar(principalId);
93  			if(lastApprovedDoc != null) {
94  				lastApprovedString = "Last Approved: " + (new SimpleDateFormat("MMM yyyy")).format(lastApprovedDoc.getBeginDate());
95              }
96  			aRow.setLastApproveMessage(lastApprovedString);
97  			
98  			LeaveCalendarDocumentHeader aDoc = LmServiceLocator.getLeaveCalendarDocumentHeaderService().getDocumentHeader(principalId, payBeginDate, payEndDate);
99  			if(aDoc != null) {
100 				if(StringUtils.isNotBlank(docIdSearchTerm) && !aDoc.getDocumentId().contains(docIdSearchTerm)) {
101 					continue;	// if the leave document of this pricipalId does not match the docIdSearchTerm, move on to the next principalId
102 				}
103 				aRow.setDocumentId(aDoc.getDocumentId());
104 				aRow.setApprovalStatus(HrConstants.DOC_ROUTE_STATUS.get(aDoc.getDocumentStatus()));
105                 notes = getNotesForDocument(aDoc.getDocumentId());
106                 if (StringUtils.isNotBlank(aRow.getDocumentId())) {
107                     List<ActionRequest> actionRequests = KewApiServiceLocator.getWorkflowDocumentService().getPendingActionRequests(aRow.getDocumentId());
108                     Map<String, String> roleNames = new HashMap<String, String>();
109                     for (ActionRequest ar : actionRequests) {
110                         roleNames.put(ar.getPrincipalId(), ar.getQualifiedRoleNameLabel());
111                     }
112                     aRow.setRoleNames(roleNames);
113                 }
114 			}
115 			List<LeaveCalendarDocumentHeader> docList = LmServiceLocator.getLeaveCalendarDocumentHeaderService().getApprovalDelinquentDocumentHeaders(principalId);
116 			if(docList.size() > LMConstants.DELINQUENT_LEAVE_CALENDARS_LIMIT ) {
117 				aRow.setMoreThanOneCalendar(true);
118 			}
119 			
120 	        // if the time of the last day of the pay period is 00:00 or 12:00 am, we should subtract a day from the calendar's end date
121 	        // since the leave blocks from that day should go into next pay period
122 			LocalDate endDate = payEndDate.toLocalDate();
123 	        if(payEndDate.getHourOfDay() == 0) {
124 	        	endDate = endDate.plusDays(-1);
125 	        }
126 			List<LeaveBlock> leaveBlocks = LmServiceLocator.getLeaveBlockService().getLeaveBlocks(principalId, payBeginDate.toLocalDate(), endDate);
127 			if(aDoc != null ){
128 				Map<Integer, String> weeklyDistribution = getWeekHeadersForSummary(principalId,aDoc,payCalendarEntry,aRow.getWeekDates(), aRow.getWeekDateList(), aRow.getDetailMap(), aRow.getEnableWeekDetails());
129 				aRow.setWeeklyDistribution(weeklyDistribution);
130 			}else {
131 											
132 				if (payCalendarEntry != null) {
133 					try{
134 						LeaveCalendarDocument leaveCalendarDocument = null;	
135 					if (LmServiceLocator.getLeaveCalendarService().shouldCreateLeaveDocument(principalId, payCalendarEntry)) {
136 						leaveCalendarDocument = LmServiceLocator.getLeaveCalendarService().openLeaveCalendarDocument(principalId, payCalendarEntry);
137 					} else {
138 						 aDoc = LmServiceLocator.getLeaveCalendarDocumentHeaderService().getDocumentHeader(principalId, payCalendarEntry.getBeginPeriodFullDateTime(), payCalendarEntry.getEndPeriodFullDateTime());
139 						if (aDoc != null) {
140 							leaveCalendarDocument = LmServiceLocator.getLeaveCalendarService().getLeaveCalendarDocument(aDoc.getDocumentId());
141 						}
142 					}
143 					
144 					} catch (WorkflowException e) {
145 			    		e.printStackTrace();
146 			    	}
147 		    	}	
148 				
149 				 Map<Integer, String> weeklyDistribution = getWeekHeadersForSummary(principalId,aDoc,payCalendarEntry,aRow.getWeekDates(), aRow.getWeekDateList(), aRow.getDetailMap(), aRow.getEnableWeekDetails());
150 				 aRow.setWeeklyDistribution(weeklyDistribution);
151 				
152 			}
153 			
154             aRow.setLeaveBlockList(leaveBlocks);
155             
156 			Map<Date, Map<String, BigDecimal>> earnCodeLeaveHours = getEarnCodeLeaveHours(leaveBlocks, leaveSummaryDates);
157 			aRow.setEarnCodeLeaveHours(earnCodeLeaveHours);
158             aRow.setNotes(notes);
159 
160             Map<String, Set<String>> allMessages = findWarnings(principalId, payCalendarEntry, leaveBlocks);
161 			
162 			Map<String,Set<String>> transactionalMessages = findTransactionsWithinPeriod(aDoc, payCalendarEntry);
163 			
164 			allMessages.get("infoMessages").addAll(transactionalMessages.get("infoMessages"));
165 			allMessages.get("warningMessages").addAll(transactionalMessages.get("warningMessages"));
166 			allMessages.get("actionMessages").addAll(transactionalMessages.get("actionMessages"));
167 			
168 			if(aRow.getLeaveSummary() != null && aRow.getLeaveSummary().getLeaveSummaryRows().size() > 0) {
169 				for(LeaveSummaryRow summaryRow : aRow.getLeaveSummary().getLeaveSummaryRows()){
170 					// check for negative available balance for accrual category.
171 		    		if(summaryRow.getLeaveBalance() != null && summaryRow.getLeaveBalance().compareTo(BigDecimal.ZERO) < 0) {
172 		    			String message = "Negative available balance found for the accrual category '"+summaryRow.getAccrualCategory()+ "'.";
173 		    			allMessages.get("warningMessages").add(message);
174 		    		}
175 				}
176 			}
177 
178             List<String> warningMessages = new ArrayList<String>();
179             warningMessages.addAll(allMessages.get("warningMessages"));
180             warningMessages.addAll(allMessages.get("infoMessages"));
181             warningMessages.addAll(allMessages.get("actionMessages"));
182             
183             
184 
185             aRow.setWarnings(warningMessages); //these are only warning messages.
186 
187 			rowList.add(aRow);
188 		
189 		}
190 		
191 		return rowList;
192 	}
193 	private Calendar getPayCalendarForEntry(CalendarEntry calEntry) {
194         Calendar cal = null;
195 
196         if (calEntry != null) {
197             cal = HrServiceLocator.getCalendarService().getCalendar(calEntry.getHrCalendarId());
198         }
199 
200         return cal;
201     }
202 
203 	private Map<Integer, String> getWeekHeadersForSummary(String principalId,LeaveCalendarDocumentHeader lcdh,CalendarEntry cal, Map<String, String> weekDates, Map<String, Set<Date>> weekDateList, Map<String,List<Map<String, Object>>> weekDetailMap, Map<String,Boolean> enableWeekDetails) {
204         
205 		Map<Integer, String> header = new LinkedHashMap<Integer,String>();                
206         header.put(DateTimeConstants.SUNDAY, "Sun");
207         header.put(DateTimeConstants.MONDAY, "Mon");
208         header.put(DateTimeConstants.TUESDAY, "Tue");
209         header.put(DateTimeConstants.WEDNESDAY, "Wed");
210         header.put(DateTimeConstants.THURSDAY, "Thu");
211         header.put(DateTimeConstants.FRIDAY, "Fri");
212         header.put(DateTimeConstants.SATURDAY, "Sat");
213         
214         int flsaBeginDay = this.getPayCalendarForEntry(cal).getFlsaBeginDayConstant();
215         if(flsaBeginDay <= 0) {
216         	flsaBeginDay = DateTimeConstants.SUNDAY;
217         }
218         LocalDateTime startDate = cal.getBeginPeriodLocalDateTime();
219         LocalDateTime endDate = cal.getEndPeriodLocalDateTime();
220 
221         LocalDateTime actualStartDate = cal.getBeginPeriodLocalDateTime();
222         LocalDateTime actualEndDate = cal.getEndPeriodLocalDateTime();
223         // if the time of the last day of the pay period is 00:00 or 12:00 am, we should subtract a day from the calendar's end date
224         // since the leave blocks from that day should go into next pay periodd
225         if(cal.getEndPeriodFullDateTime().getHourOfDay() == 0) {
226         	endDate = endDate.plusDays(-1);
227         }
228         
229         int daysToMinus = 0;
230         if(DateTimeConstants.SUNDAY != startDate.getDayOfWeek()) {
231         	daysToMinus = startDate.getDayOfWeek();
232         }
233         
234         actualStartDate = startDate.minusDays(daysToMinus);
235         int daysToAdd = 0;
236         if(endDate.getDayOfWeek() != DateTimeConstants.SUNDAY) {
237         	daysToAdd = DateTimeConstants.SATURDAY - endDate.getDayOfWeek();
238         } else {
239         	daysToAdd = DateTimeConstants.SATURDAY;
240         }
241         
242         actualEndDate = endDate.plusDays(daysToAdd);
243         
244         // Increment end date if we are on a virtual day calendar, so that the
245         // for loop can account for having the proper amount of days on the
246         // summary calendar.
247         if (endDate.get(DateTimeFieldType.hourOfDay()) != 0 || endDate.get(DateTimeFieldType.minuteOfHour()) != 0 ||
248                 endDate.get(DateTimeFieldType.secondOfMinute()) != 0)
249         {
250         	actualEndDate = endDate.plusDays(1);
251         }
252 
253         boolean afterFirstDay = false;
254         int week = 1;
255         
256         LocalDateTime weekStart = actualStartDate;
257         LocalDateTime weekEnd = actualStartDate;
258         Set<Date> dates = new TreeSet<Date>();
259         for (LocalDateTime currentDate = actualStartDate; currentDate.compareTo(actualEndDate) <= 0; currentDate = currentDate.plusDays(1)) {
260         	
261             if ( (currentDate.getDayOfWeek() == flsaBeginDay && afterFirstDay)
262             		|| currentDate.compareTo(actualEndDate) == 0) {
263             	String weekString = "Week " + week;
264                 StringBuilder display = new StringBuilder();
265                 // show the week's range within the calendar period
266                 String startDateString = weekStart.isBefore(startDate) ? 
267                 		startDate.toString(TkConstants.DT_ABBREV_DATE_FORMAT) : weekStart.toString(TkConstants.DT_ABBREV_DATE_FORMAT);
268                 display.append(startDateString);
269                 display.append(" - ");
270                 String endDateString = weekEnd.minusDays(1).isAfter(endDate) ? 
271                 		endDate.toString(TkConstants.DT_ABBREV_DATE_FORMAT) : weekEnd.minusDays(1).toString(TkConstants.DT_ABBREV_DATE_FORMAT);
272                 display.append(endDateString);
273                 weekDates.put(weekString, display.toString());
274                 if(currentDate.compareTo(actualEndDate) == 0) {
275                 	dates.add(currentDate.toDate());
276                 }
277                 weekDateList.put(weekString, dates);
278                 dates = new TreeSet<Date>();
279                 dates.add(currentDate.toDate());
280                 weekStart = currentDate;
281                 week++;
282             } else {
283             	dates.add(currentDate.toDate());
284             }
285             weekEnd = weekEnd.plusDays(1);
286             afterFirstDay = true;
287         }
288 
289         // We may have a very small final "week" on this pay period. For now
290         // we will mark it as a week, and if someone doesn't like it, it can
291         // be removed.
292         if (!header.isEmpty() && !header.get(header.size() - 1).startsWith("Week")) {
293         	if(weekStart.compareTo(endDate) < 0) {
294 	        	StringBuilder display = new StringBuilder();
295 	        	// show the week's range within the calendar period
296 	        	String startDateString = weekStart.isBefore(startDate) ? 
297                 		startDate.toString(TkConstants.DT_ABBREV_DATE_FORMAT) : weekStart.toString(TkConstants.DT_ABBREV_DATE_FORMAT);
298                 display.append(startDateString);
299 	            display.append(" - ");
300 	            String endDateString = actualEndDate.isAfter(endDate) ? 
301                 		endDate.toString(TkConstants.DT_ABBREV_DATE_FORMAT) : actualEndDate.toString(TkConstants.DT_ABBREV_DATE_FORMAT);
302                 display.append(endDateString);
303 	            weekDates.put("Week "+week, display.toString());
304 	            dates.add(actualEndDate.toDate());
305 	            weekDateList.put("Week "+week, dates);
306         	}
307             
308         }       
309         
310         // get Accrual category map for week
311         int cnt = 1;
312         for(String key : weekDateList.keySet()) {
313         	
314         	Set<Date> dateList = weekDateList.get(key);
315         	Date[] datesArray = new Date[dateList.size()];
316         	datesArray = dateList.toArray(datesArray);
317         	LocalDateTime sd = new LocalDateTime(datesArray[0].getTime());
318         	LocalDateTime ed = new LocalDateTime(datesArray[datesArray.length -1].getTime());
319         	if(cnt == 1) {
320         		sd = startDate;
321         	}
322         	if (cnt == weekDateList.size()) {
323         		ed = endDate;
324         	}
325 	        List<Map<String, Object>> detailMap = this.getLeaveApprovalDetailSections(principalId, lcdh,sd.toDateTime(), ed.toDateTime(), new ArrayList<Date>(dateList), key, enableWeekDetails);
326 	        weekDetailMap.put(key, detailMap);
327 	        cnt++;
328         }
329                
330         return header;
331     }
332 	
333     private Map<String,Set<String>> findTransactionsWithinPeriod(LeaveCalendarDocumentHeader aDoc,
334 			CalendarEntry payCalendarEntry) {
335 		Map<String,Set<String>> allMessages = new HashMap<String,Set<String>>();
336 		
337 		allMessages.put("actionMessages", new HashSet<String>());
338 		allMessages.put("infoMessages", new HashSet<String>());
339 		allMessages.put("warningMessages", new HashSet<String>());
340 		if(aDoc != null) {
341 			allMessages = LeaveCalendarValidationUtil.validatePendingTransactions(aDoc.getPrincipalId(), payCalendarEntry.getBeginPeriodFullDateTime().toLocalDate(), payCalendarEntry.getEndPeriodFullDateTime().toLocalDate());
342 		}
343 		return allMessages;
344 	}
345 
346 	private Map<String, Set<String>> findWarnings(String principalId, CalendarEntry calendarEntry, List<LeaveBlock> leaveBlocks) {
347 //        List<String> warnings = LeaveCalendarValidationUtil.getWarningMessagesForLeaveBlocks(leaveBlocks);
348         Map<String, Set<String>> allMessages= LeaveCalendarValidationUtil.getWarningMessagesForLeaveBlocks(leaveBlocks, calendarEntry.getBeginPeriodDate(), calendarEntry.getEndPeriodDate());
349         //get LeaveSummary and check for warnings
350     	Map<String, Set<LeaveBlock>> eligibilities;
351     	try {
352     		eligibilities = LmServiceLocator.getAccrualCategoryMaxBalanceService().getMaxBalanceViolations(calendarEntry, principalId);
353     	} catch (Exception e) {
354     		eligibilities = null;
355     	}
356     	if (eligibilities != null) {
357     		for (Entry<String,Set<LeaveBlock>> entry : eligibilities.entrySet()) {
358     			for(LeaveBlock block : entry.getValue()) {
359                     AccrualCategoryRule rule = block.getAccrualCategoryRule();
360     				if (rule != null) {
361     					AccrualCategory accrualCategory = HrServiceLocator.getAccrualCategoryService().getAccrualCategory(rule.getLmAccrualCategoryId());
362     					if (rule.getActionAtMaxBalance().equals(HrConstants.ACTION_AT_MAX_BALANCE.TRANSFER)) {
363     						//Todo: add link to balance transfer
364     						allMessages.get("warningMessages").add("Accrual Category '" + accrualCategory.getAccrualCategory() + "' is over max balance.");   //warningMessages
365     					} else if (rule.getActionAtMaxBalance().equals(HrConstants.ACTION_AT_MAX_BALANCE.LOSE)) {
366     						//Todo: compute and display amount of time lost.
367     						allMessages.get("warningMessages").add("Accrual Category '" + accrualCategory.getAccrualCategory() + "' is over max balance.");      //warningMessages
368     					} else if (rule.getActionAtMaxBalance().equals(HrConstants.ACTION_AT_MAX_BALANCE.PAYOUT)){
369     						//Todo: display information about the payout
370     						allMessages.get("warningMessages").add("Accrual Category '" + accrualCategory.getAccrualCategory() + "' is over max balance.");      //warningMessages
371 
372     					}
373 
374     				}
375     			}
376     		}
377     	}   
378         return allMessages;
379     }
380 	
381 	@Override
382 	public Map<Date, Map<String, BigDecimal>> getEarnCodeLeaveHours(List<LeaveBlock> leaveBlocks, List<Date> leaveSummaryDates) {
383 		Map<Date, Map<String, BigDecimal>> earnCodeLeaveHours = new LinkedHashMap<Date, Map<String, BigDecimal>>();
384 		
385 		for (Date leaveSummaryDate : leaveSummaryDates) {
386 			earnCodeLeaveHours.put(leaveSummaryDate, new LinkedHashMap<String, BigDecimal>());
387 		}
388 		
389 		for (LeaveBlock lb : leaveBlocks) {
390 			DateTime leaveDate = lb.getLeaveLocalDate().toDateTimeAtStartOfDay();
391 			
392 			if (earnCodeLeaveHours.get(leaveDate.toDate()) != null) {
393 				
394 				Map<String, BigDecimal> leaveHours = earnCodeLeaveHours.get(leaveDate.toDate());
395 
396 				BigDecimal amount = lb.getLeaveAmount();
397                 String key = lb.getEarnCode() + "|" + lb.getRequestStatus() + "|" + lb.getLeaveBlockType();
398 				if (leaveHours.get(key) != null) {
399 					amount = leaveHours.get(key).add(lb.getLeaveAmount());
400 				}
401 				
402 				leaveHours.put(key, amount);
403 			}
404 		}
405 		
406 		return earnCodeLeaveHours;
407 	}
408 	
409 	@Override
410 	public List<Map<String, Object>> getLeaveApprovalDetailSections(LeaveCalendarDocumentHeader lcdh)  {
411 		
412 		List<Map<String, Object>> acRows = new ArrayList<Map<String, Object>>();
413 		
414 		if (lcdh == null) {
415 			return acRows;
416 		}
417 		
418 		String principalId = lcdh.getPrincipalId();
419         CalendarEntry calendarEntry = LmServiceLocator.getLeaveCalendarService().getLeaveCalendarDocument(lcdh.getDocumentId()).getCalendarEntry();
420 		//CalendarEntries calendarEntry = TkServiceLocator.getCalendarEntriesService().getCalendarEntriesByBeginAndEndDate(lcdh.getBeginDate(), lcdh.getEndDate());
421 		if(calendarEntry != null) {
422 			DateTime beginDate = calendarEntry.getBeginPeriodFullDateTime();
423 			DateTime endDate = calendarEntry.getEndPeriodFullDateTime();
424 			LeaveSummary leaveSummary;
425 			List<Date> leaveSummaryDates = LmServiceLocator.getLeaveSummaryService().getLeaveSummaryDates(calendarEntry);
426             try {
427                 leaveSummary = LmServiceLocator.getLeaveSummaryService().getLeaveSummary(principalId, calendarEntry);
428             } catch (Exception e) {
429                 leaveSummary = null;
430             }
431             List<LeaveBlock> leaveBlocks = LmServiceLocator.getLeaveBlockService().getLeaveBlocks(principalId, beginDate.toLocalDate(), endDate.toLocalDate());
432 			Map<Date, Map<String, BigDecimal>> accrualCategoryLeaveHours = getAccrualCategoryLeaveHours(leaveBlocks, leaveSummaryDates);
433 
434 			//get all accrual categories of this employee
435 			PrincipalHRAttributes pha = HrServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, endDate.toLocalDate());
436 			if(pha != null) {
437 				List<AccrualCategory> acList = HrServiceLocator.getAccrualCategoryService().getActiveAccrualCategoriesForLeavePlan(pha.getLeavePlan(), endDate.toLocalDate());
438 				for(AccrualCategory ac : acList) {
439 					List<BigDecimal> acDayDetails = new ArrayList<BigDecimal>();
440 					Map<String, Object> displayMap = new HashMap<String, Object>();
441 					BigDecimal totalAmount = BigDecimal.ZERO;
442 					displayMap.put("accrualCategory", ac.getAccrualCategory());
443 					int index = 0;
444 					for (Date leaveSummaryDate : leaveSummaryDates) {
445 						acDayDetails.add(index, null);
446 						if (accrualCategoryLeaveHours.get(leaveSummaryDate) != null) {
447 							Map<String, BigDecimal> leaveHours = accrualCategoryLeaveHours.get(leaveSummaryDate);
448 							if (leaveHours.containsKey(ac.getAccrualCategory())) {
449 								BigDecimal amount =  leaveHours.get(ac.getAccrualCategory());
450 								totalAmount = totalAmount.add(amount);
451 								acDayDetails.set(index, amount);
452 							}
453 						}
454 						index++;
455 					}
456                     LeaveSummaryRow lsr = leaveSummary == null ? null : leaveSummary.getLeaveSummaryRowForAccrualCtgy(ac.getAccrualCategory());
457 					displayMap.put("periodUsage", totalAmount);
458 					displayMap.put("availableBalance", BigDecimal.ZERO);
459                     displayMap.put("availableBalance", lsr == null ? BigDecimal.ZERO : lsr.getLeaveBalance());
460 					displayMap.put("daysDetail", acDayDetails);
461 					displayMap.put("daysSize", acDayDetails.size());
462 					acRows.add(displayMap);
463 				}
464 			}
465 			
466 		}
467 		return acRows;
468 	}
469 
470 	public List<Map<String, Object>> getLeaveApprovalDetailSections(String principalId, LeaveCalendarDocumentHeader lcdh,DateTime beginDate, DateTime endDate, List<Date> leaveSummaryDates, String week, Map<String,Boolean> enableWeekDetails)  {
471 		List<Map<String, Object>> acRows = new ArrayList<Map<String, Object>>();
472 		
473 		 
474         //CalendarEntry calendarEntry = LmServiceLocator.getLeaveCalendarService().getLeaveCalendarDocument(lcdh.getDocumentId()).getCalendarEntry();
475 		//CalendarEntries calendarEntry = TkServiceLocator.getCalendarEntriesService().getCalendarEntriesByBeginAndEndDate(lcdh.getBeginDate(), lcdh.getEndDate());
476 			LeaveSummary leaveSummary;
477 			PrincipalHRAttributes pha; 
478 //			List<Date> leaveSummaryDates = LmServiceLocator.getLeaveSummaryService().getLeaveSummaryDates(calendarEntry);
479             try {
480                 leaveSummary = LmServiceLocator.getLeaveSummaryService().getLeaveSummaryAsOfDate(principalId, endDate.toLocalDate());
481                 pha = HrServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, endDate.toLocalDate());
482             } catch (Exception e) {
483                 leaveSummary = null;
484                 pha=null;
485             }
486             List<LeaveBlock> leaveBlocks = LmServiceLocator.getLeaveBlockService().getLeaveBlocks(principalId, beginDate.toLocalDate(), endDate.toLocalDate());
487 			Map<Date, Map<String, BigDecimal>> accrualCategoryLeaveHours = getAccrualCategoryLeaveHours(leaveBlocks, leaveSummaryDates);
488 			//get all accrual categories of this employee
489 			
490 			if(pha != null) {
491 				List<AccrualCategory> acList = HrServiceLocator.getAccrualCategoryService().getActiveAccrualCategoriesForLeavePlan(pha.getLeavePlan(), endDate.toLocalDate());
492 				for(AccrualCategory ac : acList) {
493 					List<BigDecimal> acDayDetails = new ArrayList<BigDecimal>();
494 					Map<String, Object> displayMap = new HashMap<String, Object>();
495 					BigDecimal totalAmount = BigDecimal.ZERO;
496 					displayMap.put("accrualCategory", ac.getAccrualCategory());
497 					int index = 0;
498 					for (Date leaveSummaryDate : leaveSummaryDates) {
499 						if(leaveSummaryDate.compareTo(beginDate.toDate()) >= 0 && leaveSummaryDate.compareTo(endDate.toDate())<=0) {
500 							acDayDetails.add(index, null);
501 							if (accrualCategoryLeaveHours.get(leaveSummaryDate) != null) {
502 								Map<String, BigDecimal> leaveHours = accrualCategoryLeaveHours.get(leaveSummaryDate);
503 								if (leaveHours.containsKey(ac.getAccrualCategory())) {
504 									BigDecimal amount =  leaveHours.get(ac.getAccrualCategory());
505 									totalAmount = totalAmount.add(amount);
506 									acDayDetails.set(index, amount);
507 								}
508 							}
509 							index++;
510 						}
511 					}
512                     LeaveSummaryRow lsr = leaveSummary == null ? null : leaveSummary.getLeaveSummaryRowForAccrualCtgy(ac.getAccrualCategory());
513                     if(!totalAmount.equals(BigDecimal.ZERO)) {                    	
514                     	enableWeekDetails.put(week, Boolean.TRUE);
515                     	
516                     }
517 					displayMap.put("periodUsage", totalAmount);
518 					displayMap.put("availableBalance", BigDecimal.ZERO);
519                     displayMap.put("availableBalance", lsr == null ? BigDecimal.ZERO : lsr.getLeaveBalance());
520 					displayMap.put("daysDetail", acDayDetails);
521 					displayMap.put("daysSize", acDayDetails.size());
522 					acRows.add(displayMap);
523 				}
524 			}
525 		return acRows;
526 	}
527 
528     @Override
529     public List<Note> getNotesForDocument(String documentNumber) {
530         return KewApiServiceLocator.getNoteService().getNotes(documentNumber);
531     }
532 
533 	@Override
534 	public Map<Date, Map<String, BigDecimal>> getAccrualCategoryLeaveHours(List<LeaveBlock> leaveBlocks, List<Date> leaveSummaryDates) {
535 		Map<Date, Map<String, BigDecimal>> accrualCategoryLeaveHours = new LinkedHashMap<Date, Map<String, BigDecimal>>();
536 		
537 		for (Date leaveSummaryDate : leaveSummaryDates) {
538 			accrualCategoryLeaveHours.put(leaveSummaryDate, new LinkedHashMap<String, BigDecimal>());
539 		}
540 		
541 		for (LeaveBlock lb : leaveBlocks) {
542 			DateTime leaveDate = lb.getLeaveLocalDate().toDateTimeAtStartOfDay();
543 			
544 			AccrualCategory ac = lb.getAccrualCategoryObj();
545 			if (ac != null && ac.getShowOnGrid().equals("Y")) {
546 				if (accrualCategoryLeaveHours.get(leaveDate.toDate()) != null) {
547 					Map<String, BigDecimal> leaveHours = accrualCategoryLeaveHours.get(leaveDate.toDate());
548 					
549 					BigDecimal amount = lb.getLeaveAmount();
550 					if (leaveHours.get(ac.getAccrualCategory()) != null) {
551 						amount = leaveHours.get(ac.getAccrualCategory()).add(lb.getLeaveAmount());
552 					}
553 					
554 					leaveHours.put(ac.getAccrualCategory(), amount);
555 				}
556 			}
557 		}
558 		
559 		return accrualCategoryLeaveHours;
560 	}
561 
562 	@Override
563 	public void removeNonLeaveEmployees(List<String> principalIds) {
564 		if(CollectionUtils.isNotEmpty(principalIds)) {
565 			LocalDate asOfDate = LocalDate.now();
566 			List<String> idList = new ArrayList<String>();
567 			idList.addAll(principalIds);
568 	     	for(String principalId: idList) {
569 	     		boolean leaveFlag = false;
570 	     		List<Assignment> activeAssignments = HrServiceLocator.getAssignmentService().getAssignments(principalId, asOfDate);
571 	     		if(CollectionUtils.isNotEmpty(activeAssignments)) {
572 	         		for(Assignment assignment : activeAssignments) {
573 	         			if(assignment != null && assignment.getJob() != null && assignment.getJob().isEligibleForLeave()) {
574 	         				leaveFlag = true;
575 	         				break;
576 	         			}
577 	         		}
578 	         		if(!leaveFlag) {  // employee is not eligible for leave, remove the id from principalIds
579 	         			principalIds.remove(principalId);
580 	         		}
581 	         	}
582 	     	}
583 		}
584 	}
585 	
586 	@Override
587 	public List<String> getLeavePrincipalIdsWithSearchCriteria(List<String> workAreaList, String calendarGroup, LocalDate effdt, LocalDate beginDate, LocalDate endDate) {
588 		if (CollectionUtils.isEmpty(workAreaList)) {
589 	      return new ArrayList<String>();
590 	    }
591 		
592 		List<String> principalIds = HrServiceLocator.getAssignmentService().getPrincipalIds(workAreaList, effdt, beginDate, endDate);
593 		LmServiceLocator.getLeaveApprovalService().removeNonLeaveEmployees(principalIds);
594 
595 		if(CollectionUtils.isEmpty(principalIds)) {
596 		return new ArrayList<String>();
597 		}
598 		// use unique principalIds and selected calendarGroup to get unique ids from principalHRAttributes table
599 		List<String> idList = CollectionUtils.isEmpty(principalIds) ? 
600 			new ArrayList<String> () 
601 			: HrServiceLocator.getPrincipalHRAttributeService()
602 				.getActiveEmployeesIdForLeaveCalendarAndIdList(calendarGroup, principalIds, endDate); 
603 		
604 		return idList;
605 	}	
606 
607 	@Override
608 	public Map<String, LeaveCalendarDocumentHeader> getPrincipalDocumentHeader(List<String> principalIds, DateTime payBeginDate, DateTime payEndDate) {
609 		Map<String, LeaveCalendarDocumentHeader> principalDocumentHeader = new LinkedHashMap<String, LeaveCalendarDocumentHeader>();
610 		for (String principalId : principalIds) {
611 			LeaveCalendarDocumentHeader lcdh = LmServiceLocator.getLeaveCalendarDocumentHeaderService().getDocumentHeader(principalId, payBeginDate, payEndDate);
612 			if(lcdh != null) {
613 				principalDocumentHeader.put(principalId, lcdh);	
614 			}
615 		}
616 
617 		return principalDocumentHeader;
618 	}
619 
620 	@Override
621 	public boolean isActiveAssignmentFoundOnJobFlsaStatus(String principalId,
622 			String flsaStatus, boolean chkForLeaveEligible) {
623 		boolean isActiveAssFound = false;
624 		LocalDate asOfDate = LocalDate.now();
625 		List<Assignment> activeAssignments = HrServiceLocator
626 				.getAssignmentService().getAssignments(principalId, asOfDate);
627 		if (activeAssignments != null && !activeAssignments.isEmpty()) {
628 			for (Assignment assignment : activeAssignments) {
629 				if (assignment != null
630 						&& assignment.getJob() != null
631 						&& assignment.getJob().getFlsaStatus() != null
632 						&& assignment.getJob().getFlsaStatus()
633 								.equalsIgnoreCase(flsaStatus)) {
634 					if (chkForLeaveEligible) {
635 						isActiveAssFound = assignment.getJob()
636 								.isEligibleForLeave();
637 						if (!isActiveAssFound) {
638 							continue;
639 						}
640 					}
641 					isActiveAssFound = true;
642 					break;
643 				}
644 			}
645 		}
646 		return isActiveAssFound;
647     }
648 }