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