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