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.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
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);
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
151 Map<String, Set<String>> allMessages= LeaveCalendarValidationUtil.getWarningMessagesForLeaveBlocks(leaveBlocks);
152
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
168 allMessages.get("warningMessages").add("Accrual Category '" + accrualCategory.getAccrualCategory() + "' is over max balance.");
169 } else if (rule.getActionAtMaxBalance().equals(LMConstants.ACTION_AT_MAX_BAL.LOSE)) {
170
171 allMessages.get("warningMessages").add("Accrual Category '" + accrualCategory.getAccrualCategory() + "' is over max balance.");
172 } else if (rule.getActionAtMaxBalance().equals(LMConstants.ACTION_AT_MAX_BAL.PAYOUT)){
173
174 allMessages.get("warningMessages").add("Accrual Category '" + accrualCategory.getAccrualCategory() + "' is over max balance.");
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
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
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
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) {
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
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 }