View Javadoc

1   /**
2    * Copyright 2004-2013 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.hr.lm.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.LinkedList;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Map.Entry;
29  import java.util.Set;
30  
31  import org.apache.commons.collections.CollectionUtils;
32  import org.apache.commons.lang3.StringUtils;
33  import org.apache.commons.lang3.time.DateUtils;
34  import org.joda.time.DateTime;
35  import org.kuali.hr.lm.LMConstants;
36  import org.kuali.hr.lm.accrual.AccrualCategory;
37  import org.kuali.hr.lm.accrual.AccrualCategoryRule;
38  import org.kuali.hr.lm.balancetransfer.BalanceTransfer;
39  import org.kuali.hr.lm.leaveSummary.LeaveSummary;
40  import org.kuali.hr.lm.leaveSummary.LeaveSummaryRow;
41  import org.kuali.hr.lm.leaveblock.LeaveBlock;
42  import org.kuali.hr.lm.leavecalendar.validation.LeaveCalendarValidationUtil;
43  import org.kuali.hr.lm.leavepayout.LeavePayout;
44  import org.kuali.hr.lm.workflow.LeaveCalendarDocumentHeader;
45  import org.kuali.hr.time.approval.web.ApprovalLeaveSummaryRow;
46  import org.kuali.hr.time.assignment.Assignment;
47  import org.kuali.hr.time.calendar.CalendarEntries;
48  import org.kuali.hr.time.principal.PrincipalHRAttributes;
49  import org.kuali.hr.time.principal.dao.PrincipalHRAttributesDao;
50  import org.kuali.hr.time.roles.TkUserRoles;
51  import org.kuali.hr.time.roles.UserRoles;
52  import org.kuali.hr.time.service.base.TkServiceLocator;
53  import org.kuali.hr.time.util.TKContext;
54  import org.kuali.hr.time.util.TKUser;
55  import org.kuali.hr.time.util.TKUtils;
56  import org.kuali.hr.time.util.TkConstants;
57  import org.kuali.rice.kew.api.KewApiServiceLocator;
58  import org.kuali.rice.kew.api.note.Note;
59  import org.kuali.rice.kim.api.identity.principal.EntityNamePrincipalName;
60  import org.kuali.rice.kim.api.identity.principal.Principal;
61  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
62  import org.kuali.rice.krad.util.GlobalVariables;
63  import org.springframework.jdbc.support.rowset.SqlRowSet;
64  
65  public class LeaveApprovalServiceImpl implements LeaveApprovalService{
66  	public static final int DAYS_WINDOW_DELTA = 31;
67      private PrincipalHRAttributesDao principalHRAttributesDao;
68  
69      public void setPrincipalHRAttributesDao(PrincipalHRAttributesDao principalHRAttributesDao) {
70          this.principalHRAttributesDao = principalHRAttributesDao;
71      }
72  
73  	@Override
74  	public List<ApprovalLeaveSummaryRow> getLeaveApprovalSummaryRows(List<String> principalIds, CalendarEntries payCalendarEntries, List<Date> leaveSummaryDates) {
75  		Date payBeginDate = payCalendarEntries.getBeginPeriodDate();
76  		Date payEndDate = payCalendarEntries.getEndPeriodDate();
77  		List<ApprovalLeaveSummaryRow> rowList = new ArrayList<ApprovalLeaveSummaryRow>();		
78  
79  		for(String principalId : principalIds) {
80  			
81  			ApprovalLeaveSummaryRow aRow = new ApprovalLeaveSummaryRow();
82              List<Note> notes = new ArrayList<Note>();
83  //            List<String> warnings = new ArrayList<String>();
84              EntityNamePrincipalName name = KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(principalId);
85              aRow.setName(name != null
86                      && name.getDefaultName() != null
87                      && name.getDefaultName().getCompositeName() != null ? name.getDefaultName().getCompositeName() : principalId);
88  			aRow.setPrincipalId(principalId);
89  			
90  			String lastApprovedString = "No previous approved leave calendar information";
91  			LeaveCalendarDocumentHeader lastApprovedDoc = TkServiceLocator.getLeaveCalendarDocumentHeaderService().getMaxEndDateApprovedLeaveCalendar(principalId);
92  			if(lastApprovedDoc != null) {
93  				lastApprovedString = "Last Approved: " + (new SimpleDateFormat("MMM yyyy")).format(lastApprovedDoc.getBeginDate());
94              }
95  			aRow.setLastApproveMessage(lastApprovedString);
96  			
97  			LeaveCalendarDocumentHeader aDoc = TkServiceLocator.getLeaveCalendarDocumentHeaderService().getDocumentHeader(principalId, payBeginDate, payEndDate);
98  			if(aDoc != null) {
99  				aRow.setDocumentId(aDoc.getDocumentId());
100 				aRow.setApprovalStatus(TkConstants.DOC_ROUTE_STATUS.get(aDoc.getDocumentStatus()));
101                 notes = getNotesForDocument(aDoc.getDocumentId());
102 			}
103 			List<LeaveCalendarDocumentHeader> docList = TkServiceLocator.getLeaveCalendarDocumentHeaderService().getApprovalDelinquentDocumentHeaders(principalId);
104 			if(docList.size() > LMConstants.DELINQUENT_LEAVE_CALENDARS_LIMIT ) {
105 				aRow.setMoreThanOneCalendar(true);
106 			}
107 			
108 			List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocks(principalId, payBeginDate, payEndDate);
109 
110             aRow.setLeaveBlockList(leaveBlocks);
111 			Map<Date, Map<String, BigDecimal>> earnCodeLeaveHours = getEarnCodeLeaveHours(leaveBlocks, leaveSummaryDates);
112 			aRow.setEarnCodeLeaveHours(earnCodeLeaveHours);
113             aRow.setNotes(notes);
114 
115             Map<String, Set<String>> allMessages = findWarnings(principalId, payCalendarEntries, leaveBlocks);
116 			
117 			Map<String,Set<String>> transactionalMessages = findTransactionsWithinPeriod(aDoc, payCalendarEntries);
118 			
119 			allMessages.get("infoMessages").addAll(transactionalMessages.get("infoMessages"));
120 			allMessages.get("warningMessages").addAll(transactionalMessages.get("warningMessages"));
121 			allMessages.get("actionMessages").addAll(transactionalMessages.get("actionMessages"));
122 
123             List<String> warningMessages = new ArrayList<String>();
124             warningMessages.addAll(allMessages.get("warningMessages"));
125             warningMessages.addAll(allMessages.get("infoMessages"));
126             warningMessages.addAll(allMessages.get("actionMessages"));
127 
128             aRow.setWarnings(warningMessages); //these are only warning messages.
129 
130 			rowList.add(aRow);
131 		}
132 		
133 		return rowList;
134 	}
135 
136     private Map<String,Set<String>> findTransactionsWithinPeriod(LeaveCalendarDocumentHeader aDoc,
137 			CalendarEntries payCalendarEntries) {
138 		Map<String,Set<String>> allMessages = new HashMap<String,Set<String>>();
139 		
140 		allMessages.put("actionMessages", new HashSet<String>());
141 		allMessages.put("infoMessages", new HashSet<String>());
142 		allMessages.put("warningMessages", new HashSet<String>());
143 		if(aDoc != null) {
144 			allMessages = LeaveCalendarValidationUtil.validatePendingTransactions(aDoc.getPrincipalId(), payCalendarEntries.getBeginPeriodDate(), payCalendarEntries.getEndPeriodDate());
145 		}
146 		return allMessages;
147 	}
148 
149 	private Map<String, Set<String>> findWarnings(String principalId, CalendarEntries calendarEntry, List<LeaveBlock> leaveBlocks) {
150 //        List<String> warnings = LeaveCalendarValidationUtil.getWarningMessagesForLeaveBlocks(leaveBlocks);
151         Map<String, Set<String>> allMessages= LeaveCalendarValidationUtil.getWarningMessagesForLeaveBlocks(leaveBlocks);
152         //get LeaveSummary and check for warnings
153     	Map<String, Set<LeaveBlock>> eligibilities;
154     	try {
155     		eligibilities = TkServiceLocator.getAccrualCategoryMaxBalanceService().getMaxBalanceViolations(calendarEntry, principalId);
156     	} catch (Exception e) {
157     		eligibilities = null;
158     	}
159     	if (eligibilities != null) {
160     		for (Entry<String,Set<LeaveBlock>> entry : eligibilities.entrySet()) {
161     			for(LeaveBlock block : entry.getValue()) {
162 
163     				AccrualCategoryRule rule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRule(block.getAccrualCategoryRuleId());
164     				if (rule != null) {
165     					AccrualCategory accrualCategory = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(rule.getLmAccrualCategoryId());
166     					if (rule.getActionAtMaxBalance().equals(LMConstants.ACTION_AT_MAX_BAL.TRANSFER)) {
167     						//Todo: add link to balance transfer
168     						allMessages.get("warningMessages").add("Accrual Category '" + accrualCategory.getAccrualCategory() + "' is over max balance.");   //warningMessages
169     					} else if (rule.getActionAtMaxBalance().equals(LMConstants.ACTION_AT_MAX_BAL.LOSE)) {
170     						//Todo: compute and display amount of time lost.
171     						allMessages.get("warningMessages").add("Accrual Category '" + accrualCategory.getAccrualCategory() + "' is over max balance.");      //warningMessages
172     					} else if (rule.getActionAtMaxBalance().equals(LMConstants.ACTION_AT_MAX_BAL.PAYOUT)){
173     						//Todo: display information about the payout
174     						allMessages.get("warningMessages").add("Accrual Category '" + accrualCategory.getAccrualCategory() + "' is over max balance.");      //warningMessages
175 
176     					}
177 
178     				}
179     			}
180     		}
181     	}    
182         return allMessages;
183     }
184 	
185 	@Override
186 	public Map<Date, Map<String, BigDecimal>> getEarnCodeLeaveHours(List<LeaveBlock> leaveBlocks, List<Date> leaveSummaryDates) {
187 		Map<Date, Map<String, BigDecimal>> earnCodeLeaveHours = new LinkedHashMap<Date, Map<String, BigDecimal>>();
188 		
189 		for (Date leaveSummaryDate : leaveSummaryDates) {
190 			earnCodeLeaveHours.put(leaveSummaryDate, new LinkedHashMap<String, BigDecimal>());
191 		}
192 
193         for (LeaveBlock lb : leaveBlocks) {
194 			DateTime leaveDate = new DateTime(lb.getLeaveDate()).toLocalDate().toDateTimeAtStartOfDay();
195 
196             if (earnCodeLeaveHours.get(leaveDate.toDate()) != null) {
197                 Map<String, BigDecimal> leaveHours = earnCodeLeaveHours.get(leaveDate.toDate());
198 
199                 BigDecimal amount = lb.getLeaveAmount();
200                 String key = lb.getEarnCode() + "|" + lb.getRequestStatus() + "|" + lb.getLeaveBlockType();
201                 if (leaveHours.get(key) != null) {
202                     amount = leaveHours.get(key).add(lb.getLeaveAmount());
203                 }
204 
205                 leaveHours.put(key, amount);
206             }
207         }
208 
209         return earnCodeLeaveHours;
210     }
211 	
212 	@Override
213 	public List<Map<String, Object>> getLeaveApprovalDetailSections(LeaveCalendarDocumentHeader lcdh)  {
214 		
215 		List<Map<String, Object>> acRows = new ArrayList<Map<String, Object>>();
216 		if (lcdh == null) {
217             return acRows;
218         }
219 		String principalId = lcdh.getPrincipalId();
220         CalendarEntries calendarEntry = TkServiceLocator.getLeaveCalendarService().getLeaveCalendarDocument(lcdh.getDocumentId()).getCalendarEntry();
221 		//CalendarEntries calendarEntry = TkServiceLocator.getCalendarEntriesService().getCalendarEntriesByBeginAndEndDate(lcdh.getBeginDate(), lcdh.getEndDate());
222 		if(calendarEntry != null) {
223 			Date beginDate = calendarEntry.getBeginPeriodDate();
224 			Date endDate = calendarEntry.getEndPeriodDate();
225 			LeaveSummary leaveSummary;
226 			List<Date> leaveSummaryDates = TkServiceLocator.getLeaveSummaryService().getLeaveSummaryDates(calendarEntry);
227             try {
228                 leaveSummary = TkServiceLocator.getLeaveSummaryService().getLeaveSummary(principalId, calendarEntry);
229             } catch (Exception e) {
230                 leaveSummary = null;
231             }
232             List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocks(principalId, beginDate, endDate);
233 			Map<Date, Map<String, BigDecimal>> accrualCategoryLeaveHours = getAccrualCategoryLeaveHours(leaveBlocks, leaveSummaryDates);
234 
235 			//get all accrual categories of this employee
236 			PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, endDate);
237 			if(pha != null) {
238 				List<AccrualCategory> acList = TkServiceLocator.getAccrualCategoryService().getActiveAccrualCategoriesForLeavePlan(pha.getLeavePlan(), new java.sql.Date(endDate.getTime()));
239 				for(AccrualCategory ac : acList) {
240 					List<BigDecimal> acDayDetails = new ArrayList<BigDecimal>();
241 					Map<String, Object> displayMap = new HashMap<String, Object>();
242 					BigDecimal totalAmount = BigDecimal.ZERO;
243 					displayMap.put("accrualCategory", ac.getAccrualCategory());
244 					int index = 0;
245 					for (Date leaveSummaryDate : leaveSummaryDates) {
246 						acDayDetails.add(index, null);
247 						if (accrualCategoryLeaveHours.get(leaveSummaryDate) != null) {
248 							Map<String, BigDecimal> leaveHours = accrualCategoryLeaveHours.get(leaveSummaryDate);
249 							if (leaveHours.containsKey(ac.getAccrualCategory())) {
250 								BigDecimal amount =  leaveHours.get(ac.getAccrualCategory());
251 								totalAmount = totalAmount.add(amount);
252 								acDayDetails.set(index, amount);
253 							}
254 						}
255 						index++;
256 					}
257                     LeaveSummaryRow lsr = leaveSummary == null ? null : leaveSummary.getLeaveSummaryRowForAccrualCtgy(ac.getAccrualCategory());
258 					displayMap.put("periodUsage", totalAmount);
259 					displayMap.put("availableBalance", BigDecimal.ZERO);
260                     displayMap.put("availableBalance", lsr == null ? BigDecimal.ZERO : lsr.getLeaveBalance());
261 					displayMap.put("daysDetail", acDayDetails);
262 					displayMap.put("daysSize", acDayDetails.size());
263 					acRows.add(displayMap);
264 				}
265 			}
266 			
267 		}
268 		return acRows;
269 	}
270 
271     @Override
272     public List<Note> getNotesForDocument(String documentNumber) {
273         return KewApiServiceLocator.getNoteService().getNotes(documentNumber);
274     }
275 
276 	@Override
277 	public Map<Date, Map<String, BigDecimal>> getAccrualCategoryLeaveHours(List<LeaveBlock> leaveBlocks, List<Date> leaveSummaryDates) {
278 		Map<Date, Map<String, BigDecimal>> accrualCategoryLeaveHours = new LinkedHashMap<Date, Map<String, BigDecimal>>();
279 		
280 		for (Date leaveSummaryDate : leaveSummaryDates) {
281 			accrualCategoryLeaveHours.put(leaveSummaryDate, new LinkedHashMap<String, BigDecimal>());
282 		}
283 		
284 		for (LeaveBlock lb : leaveBlocks) {
285 			DateTime leaveDate = new DateTime(lb.getLeaveDate()).toLocalDate().toDateTimeAtStartOfDay();
286 			
287 			AccrualCategory ac = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(lb.getAccrualCategory(), lb.getLeaveDate());
288 			if (ac != null && ac.getShowOnGrid().equals("Y")) {
289 				if (accrualCategoryLeaveHours.get(leaveDate.toDate()) != null) {
290 					Map<String, BigDecimal> leaveHours = accrualCategoryLeaveHours.get(leaveDate.toDate());
291 					
292 					BigDecimal amount = lb.getLeaveAmount();
293 					if (leaveHours.get(ac.getAccrualCategory()) != null) {
294 						amount = leaveHours.get(ac.getAccrualCategory()).add(lb.getLeaveAmount());
295 					}
296 					
297 					leaveHours.put(ac.getAccrualCategory(), amount);
298 				}
299 			}
300 		}
301 		
302 		return accrualCategoryLeaveHours;
303 	}
304 
305     @Override
306     public List<String> getUniqueLeavePayGroupsForPrincipalIds(List<String> principalIds) {
307         return principalHRAttributesDao.getUniqueLeavePayGroupsForPrincipalIds(principalIds);
308     }
309 	
310 	@Override
311 	public List<CalendarEntries> getAllLeavePayCalendarEntriesForApprover(String principalId, Date currentDate) {
312 		Set<String> principals = new HashSet<String>();
313         UserRoles userRoles = TkUserRoles.getUserRoles(principalId);
314 		Set<Long> approverWorkAreas = userRoles.getApproverWorkAreas();
315         approverWorkAreas.addAll(userRoles.getReviewerWorkAreas());
316 		// Get all of the principals within our window of time.
317 		for (Long waNum : approverWorkAreas) {
318 			List<Assignment> assignments = TkServiceLocator
319 					.getAssignmentService().getActiveAssignmentsForWorkArea(waNum, TKUtils.getTimelessDate(currentDate));
320 
321 			if (assignments != null) {
322 				for (Assignment assignment : assignments) {
323 					principals.add(assignment.getPrincipalId());
324 				}
325 			}
326 		}
327 		List<LeaveCalendarDocumentHeader> documentHeaders = new ArrayList<LeaveCalendarDocumentHeader>();
328 		for(String pid : principals) {
329 			documentHeaders.addAll(TkServiceLocator.getLeaveCalendarDocumentHeaderService().getAllDocumentHeadersForPricipalId(pid));
330 		}
331 		Set<CalendarEntries> payPeriodSet = new HashSet<CalendarEntries>();
332 		for(LeaveCalendarDocumentHeader lcdh : documentHeaders) {
333             CalendarEntries pe = TkServiceLocator.getLeaveCalendarService().getLeaveCalendarDocument(lcdh.getDocumentId()).getCalendarEntry();
334     		if(pe != null) {
335     			payPeriodSet.add(pe);
336     		}
337         }
338 		List<CalendarEntries> ppList = new ArrayList<CalendarEntries>(payPeriodSet);
339         
340 		return ppList;
341 	}
342 	@Override
343 	public void removeNonLeaveEmployees(List<String> principalIds) {
344 		if(CollectionUtils.isNotEmpty(principalIds)) {
345 			java.sql.Date asOfDate = TKUtils.getTimelessDate(null);
346 			List<String> idList = new ArrayList<String>();
347 			idList.addAll(principalIds);
348 	     	for(String principalId: idList) {
349 	     		boolean leaveFlag = false;
350 	     		List<Assignment> activeAssignments = TkServiceLocator.getAssignmentService().getAssignments(principalId, asOfDate);
351 	     		if(CollectionUtils.isNotEmpty(activeAssignments)) {
352 	         		for(Assignment assignment : activeAssignments) {
353 	         			if(assignment != null && assignment.getJob() != null && assignment.getJob().isEligibleForLeave()) {
354 	         				leaveFlag = true;
355 	         				break;
356 	         			}
357 	         		}
358 	         		if(!leaveFlag) {  // employee is not eligible for leave, remove the id from principalIds
359 	         			principalIds.remove(principalId);
360 	         		}
361 	         	}
362 	     	}
363 		}
364 	}
365 	
366 	@Override
367 	public List<String> getLeavePrincipalIdsWithSearchCriteria(List<String> workAreaList, String calendarGroup, java.sql.Date effdt, java.sql.Date beginDate, java.sql.Date endDate) {
368 		if (CollectionUtils.isEmpty(workAreaList)) {
369 	      return new ArrayList<String>();
370 	    }
371 		
372 		List<String> principalIds = TkServiceLocator.getAssignmentService().getPrincipalIds(workAreaList, effdt, beginDate, endDate);
373 		TkServiceLocator.getLeaveApprovalService().removeNonLeaveEmployees(principalIds);
374 
375 		if(CollectionUtils.isEmpty(principalIds)) {
376 		return new ArrayList<String>();
377 		}
378 		// use unique principalIds and selected calendarGroup to get unique ids from principalHRAttributes table
379 		List<String> idList = CollectionUtils.isEmpty(principalIds) ? 
380 			new ArrayList<String> () 
381 			: TkServiceLocator.getPrincipalHRAttributeService()
382 				.getActiveEmployeesIdForLeaveCalendarAndIdList(calendarGroup, principalIds, endDate); 
383 		
384 		return idList;
385 	}	
386 
387 	@Override
388 	public Map<String, LeaveCalendarDocumentHeader> getPrincipalDocumentHeader(List<String> principalIds, Date payBeginDate, Date payEndDate) {
389 		Map<String, LeaveCalendarDocumentHeader> principalDocumentHeader = new LinkedHashMap<String, LeaveCalendarDocumentHeader>();
390 		for (String principalId : principalIds) {
391 			LeaveCalendarDocumentHeader lcdh = TkServiceLocator.getLeaveCalendarDocumentHeaderService().getDocumentHeader(principalId, payBeginDate, payEndDate);
392 			if(lcdh != null) {
393 				principalDocumentHeader.put(principalId, lcdh);	
394 			}
395 		}
396 		return principalDocumentHeader;
397 	}
398 
399 	@Override
400 	public boolean isActiveAssignmentFoundOnJobFlsaStatus(String principalId,
401 			String flsaStatus, boolean chkForLeaveEligible) {
402 		boolean isActiveAssFound = false;
403 		java.sql.Date asOfDate = TKUtils.getTimelessDate(null);
404 		List<Assignment> activeAssignments = TkServiceLocator
405 				.getAssignmentService().getAssignments(principalId, asOfDate);
406 		if (activeAssignments != null && !activeAssignments.isEmpty()) {
407 			for (Assignment assignment : activeAssignments) {
408 				if (assignment != null
409 						&& assignment.getJob() != null
410 						&& assignment.getJob().getFlsaStatus() != null
411 						&& assignment.getJob().getFlsaStatus()
412 								.equalsIgnoreCase(flsaStatus)) {
413 					if (chkForLeaveEligible) {
414 						isActiveAssFound = assignment.getJob()
415 								.isEligibleForLeave();
416 						if (!isActiveAssFound) {
417 							continue;
418 						}
419 					}
420 					isActiveAssFound = true;
421 					break;
422 				}
423 			}
424 		}
425 		return isActiveAssFound;
426     }
427 }