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 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);
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
145 Map<String, Set<String>> allMessages= LeaveCalendarValidationUtil.getWarningMessagesForLeaveBlocks(leaveBlocks);
146
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
162 allMessages.get("warningMessages").add("Accrual Category '" + accrualCategory.getAccrualCategory() + "' is over max balance.");
163 } else if (rule.getActionAtMaxBalance().equals(LMConstants.ACTION_AT_MAX_BAL.LOSE)) {
164
165 allMessages.get("warningMessages").add("Accrual Category '" + accrualCategory.getAccrualCategory() + "' is over max balance.");
166 } else if (rule.getActionAtMaxBalance().equals(LMConstants.ACTION_AT_MAX_BAL.PAYOUT)){
167
168 allMessages.get("warningMessages").add("Accrual Category '" + accrualCategory.getAccrualCategory() + "' is over max balance.");
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
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
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
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) {
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
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 }