1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.kpme.tklm.leave.accrual.service;
17
18 import java.math.BigDecimal;
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26
27 import org.apache.commons.collections.CollectionUtils;
28 import org.apache.commons.lang.StringUtils;
29 import org.apache.log4j.Logger;
30 import org.joda.time.DateTime;
31 import org.joda.time.DateTimeConstants;
32 import org.joda.time.Interval;
33 import org.joda.time.LocalDate;
34 import org.kuali.kpme.core.accrualcategory.AccrualCategory;
35 import org.kuali.kpme.core.accrualcategory.rule.AccrualCategoryRule;
36 import org.kuali.kpme.core.api.accrualcategory.AccrualEarnInterval;
37 import org.kuali.kpme.core.assignment.Assignment;
38 import org.kuali.kpme.core.calendar.entry.CalendarEntry;
39 import org.kuali.kpme.core.earncode.EarnCode;
40 import org.kuali.kpme.core.job.Job;
41 import org.kuali.kpme.core.leaveplan.LeavePlan;
42 import org.kuali.kpme.core.principal.PrincipalHRAttributes;
43 import org.kuali.kpme.core.service.HrServiceLocator;
44 import org.kuali.kpme.core.util.HrConstants;
45 import org.kuali.kpme.core.util.HrContext;
46 import org.kuali.kpme.core.util.TKUtils;
47 import org.kuali.kpme.tklm.common.LMConstants;
48 import org.kuali.kpme.tklm.leave.accrual.PrincipalAccrualRan;
49 import org.kuali.kpme.tklm.leave.accrual.RateRange;
50 import org.kuali.kpme.tklm.leave.accrual.RateRangeAggregate;
51 import org.kuali.kpme.tklm.leave.block.LeaveBlock;
52 import org.kuali.kpme.tklm.leave.service.LmServiceLocator;
53 import org.kuali.kpme.tklm.leave.timeoff.SystemScheduledTimeOff;
54 import org.kuali.kpme.tklm.leave.workflow.LeaveCalendarDocumentHeader;
55
56 public class AccrualServiceImpl implements AccrualService {
57 private static final Logger LOG = Logger.getLogger(AccrualServiceImpl.class);
58
59 @Override
60 public void runAccrual(String principalId) {
61 DateTime startDate = getStartAccrualDate(principalId);
62 DateTime endDate = getEndAccrualDate(principalId);
63
64 LOG.info("AccrualServiceImpl.runAccrual() STARTED with Principal: "+principalId);
65 runAccrual(principalId,startDate,endDate, true);
66
67 }
68
69 @Override
70 public void runAccrual(String principalId, DateTime startDate, DateTime endDate, boolean recordRanData) {
71 runAccrual(principalId, startDate, endDate, recordRanData, HrContext.getPrincipalId());
72 }
73
74 @SuppressWarnings("unchecked")
75 @Override
76 public void runAccrual(String principalId, DateTime startDate, DateTime endDate, boolean recordRanData, String runAsPrincipalId) {
77 List<LeaveBlock> accrualLeaveBlocks = new ArrayList<LeaveBlock>();
78 Map<String, BigDecimal> accumulatedAccrualCatToAccrualAmounts = new HashMap<String,BigDecimal>();
79 Map<String, BigDecimal> accumulatedAccrualCatToNegativeAccrualAmounts = new HashMap<String,BigDecimal>();
80
81 if (startDate != null && endDate != null) {
82 LOG.info("AccrualServiceImpl.runAccrual() STARTED with Principal: "+principalId+" Start: "+startDate.toString()+" End: "+endDate.toString());
83 }
84 if(startDate.isAfter(endDate)) {
85 LOG.error("Start Date " + startDate.toString() + " should not be later than End Date " + endDate.toString());
86 return;
87
88 }
89
90 deactivateOldAccruals(principalId, startDate, endDate, runAsPrincipalId);
91
92
93
94 RateRangeAggregate rrAggregate = this.buildRateRangeAggregate(principalId, startDate, endDate);
95 PrincipalHRAttributes phra = null;
96 PrincipalHRAttributes endPhra = null;
97 LeavePlan lp = null;
98 List<AccrualCategory> accrCatList = null;
99
100
101 DateTime currentDate = startDate;
102 while (!currentDate.isAfter(endDate)) {
103 RateRange currentRange = rrAggregate.getRateOnDate(currentDate);
104 if(currentRange == null) {
105 currentDate = currentDate.plusDays(1);
106 continue;
107 }
108
109 phra = currentRange.getPrincipalHRAttributes();
110 if(phra == null || currentDate.toLocalDate().isBefore(phra.getServiceLocalDate())) {
111 currentDate = currentDate.plusDays(1);
112 continue;
113 }
114
115
116
117
118
119 endPhra = currentRange.getEndPrincipalHRAttributes();
120 if(endPhra != null && currentDate.toLocalDate().isAfter(endPhra.getEffectiveLocalDate())) {
121 currentDate = currentDate.plusDays(1);
122 continue;
123 }
124
125
126 if(endDate.toLocalDate().isBefore(phra.getServiceLocalDate())) {
127 return;
128 }
129 lp = currentRange.getLeavePlan();
130 accrCatList = currentRange.getAcList();
131
132 if(currentRange.isStatusChanged()) {
133 this.createEmptyLeaveBlockForStatusChange(principalId, accrualLeaveBlocks, currentDate.toLocalDate());
134 }
135
136 if(CollectionUtils.isEmpty(currentRange.getJobs())) {
137 currentDate = currentDate.plusDays(1);
138 continue;
139 }
140
141 BigDecimal ftePercentage = currentRange.getAccrualRatePercentageModifier();
142 BigDecimal totalOfStandardHours = currentRange.getStandardHours();
143 boolean fullFteGranted = false;
144 for(AccrualCategory anAC : accrCatList) {
145 if(anAC == null)
146 continue;
147
148 fullFteGranted = false;
149 if(!currentDate.toLocalDate().isBefore(phra.getEffectiveLocalDate()) && !anAC.getAccrualEarnInterval().equals("N")) {
150 boolean prorationFlag = this.isProrationFlag(anAC.getProration());
151
152 AccrualCategoryRule currentAcRule = this.getRuleForAccrualCategory(currentRange.getAcRuleList(), anAC);
153
154
155 if(currentAcRule != null) {
156 DateTime ruleStartDate = getRuleStartDate(currentAcRule.getServiceUnitOfTime(), phra.getServiceLocalDate(), currentAcRule.getStart());
157 DateTime previousIntervalDay = this.getPrevIntervalDate(ruleStartDate, anAC.getAccrualEarnInterval(), phra.getPayCalendar(), rrAggregate.getCalEntryMap());
158 DateTime nextIntervalDay = this.getNextIntervalDate(ruleStartDate, anAC.getAccrualEarnInterval(), phra.getPayCalendar(), rrAggregate.getCalEntryMap());
159
160 RateRange previousRange = rrAggregate.getRateOnDate(previousIntervalDay);
161 AccrualCategoryRule previousAcRule = null;
162 if(previousRange != null) {
163 previousAcRule = this.getRuleForAccrualCategory(previousRange.getAcRuleList(), anAC);
164 }
165
166 if(previousAcRule != null && !previousAcRule.getLmAccrualCategoryRuleId().equals(currentAcRule.getLmAccrualCategoryRuleId())) {
167 if(currentDate.toLocalDate().compareTo(previousIntervalDay.toLocalDate()) >= 0
168 && currentDate.toLocalDate().compareTo(nextIntervalDay.toLocalDate()) <= 0) {
169 int workDaysInBetween = TKUtils.getWorkDays(ruleStartDate, nextIntervalDay);
170 boolean minReachedFlag = minimumPercentageReachedForPayPeriod(anAC.getMinPercentWorked(),
171 anAC.getAccrualEarnInterval(), workDaysInBetween, nextIntervalDay,
172 phra.getPayCalendar(), rrAggregate.getCalEntryMap());
173 if(prorationFlag) {
174 if(minReachedFlag) {
175
176
177 } else {
178
179 currentAcRule = previousAcRule;
180 }
181 } else {
182 if(minReachedFlag) {
183
184 accumulatedAccrualCatToAccrualAmounts.put(anAC.getLmAccrualCategoryId(), currentAcRule.getAccrualRate());
185 fullFteGranted = true;
186 } else {
187
188 accumulatedAccrualCatToAccrualAmounts.put(anAC.getLmAccrualCategoryId(), previousAcRule.getAccrualRate());
189 fullFteGranted = true;
190 }
191 }
192 }
193 }
194 }
195
196
197 DateTime firstIntervalDate = this.getNextIntervalDate(phra.getEffectiveLocalDate().toDateTimeAtStartOfDay(), anAC.getAccrualEarnInterval(), phra.getPayCalendar(), rrAggregate.getCalEntryMap());
198 if(!currentDate.toLocalDate().isBefore(phra.getEffectiveLocalDate())
199 && !currentDate.toLocalDate().isAfter(firstIntervalDate.toLocalDate())) {
200 int workDaysInBetween = TKUtils.getWorkDays(phra.getEffectiveLocalDate().toDateTimeAtStartOfDay(), firstIntervalDate);
201 boolean minReachedFlag = minimumPercentageReachedForPayPeriod(anAC.getMinPercentWorked(), anAC.getAccrualEarnInterval(),
202 workDaysInBetween, firstIntervalDate,
203 phra.getPayCalendar(), rrAggregate.getCalEntryMap());
204
205 if(prorationFlag) {
206 if(minReachedFlag) {
207
208
209 } else {
210
211 accumulatedAccrualCatToAccrualAmounts.remove(anAC.getLmAccrualCategoryId());
212 accumulatedAccrualCatToNegativeAccrualAmounts.remove(anAC.getLmAccrualCategoryId());
213 continue;
214 }
215 } else {
216 if(minReachedFlag && currentAcRule != null) {
217
218 accumulatedAccrualCatToAccrualAmounts.put(anAC.getLmAccrualCategoryId(), currentAcRule.getAccrualRate());
219 fullFteGranted = true;
220 } else {
221
222 accumulatedAccrualCatToAccrualAmounts.remove(anAC.getLmAccrualCategoryId());
223 accumulatedAccrualCatToNegativeAccrualAmounts.remove(anAC.getLmAccrualCategoryId());
224 continue;
225 }
226 }
227 }
228
229 if(endPhra != null) {
230 DateTime previousIntervalDate = this.getPrevIntervalDate(endPhra.getEffectiveLocalDate().toDateTimeAtStartOfDay(), anAC.getAccrualEarnInterval(), phra.getPayCalendar(), rrAggregate.getCalEntryMap());
231
232 if(!currentDate.toLocalDate().isAfter(endPhra.getEffectiveLocalDate())
233 && currentDate.toLocalDate().isAfter(previousIntervalDate.toLocalDate())) {
234 DateTime lastIntervalDate = this.getNextIntervalDate(endPhra.getEffectiveLocalDate().toDateTimeAtStartOfDay(), anAC.getAccrualEarnInterval(), phra.getPayCalendar(), rrAggregate.getCalEntryMap());
235 int workDaysInBetween = TKUtils.getWorkDays(previousIntervalDate, endPhra.getEffectiveLocalDate().toDateTimeAtStartOfDay());
236 boolean minReachedFlag = minimumPercentageReachedForPayPeriod(anAC.getMinPercentWorked(), anAC.getAccrualEarnInterval(),
237 workDaysInBetween, lastIntervalDate,
238 phra.getPayCalendar(), rrAggregate.getCalEntryMap());
239 if(prorationFlag) {
240 if(minReachedFlag) {
241
242
243 } else {
244
245 accumulatedAccrualCatToAccrualAmounts.remove(anAC.getLmAccrualCategoryId());
246 accumulatedAccrualCatToNegativeAccrualAmounts.remove(anAC.getLmAccrualCategoryId());
247 continue;
248 }
249 } else {
250 if(minReachedFlag) {
251
252 accumulatedAccrualCatToAccrualAmounts.put(anAC.getLmAccrualCategoryId(), currentAcRule.getAccrualRate());
253 fullFteGranted = true;
254 } else {
255
256 accumulatedAccrualCatToAccrualAmounts.remove(anAC.getLmAccrualCategoryId());
257 accumulatedAccrualCatToNegativeAccrualAmounts.remove(anAC.getLmAccrualCategoryId());
258 continue;
259 }
260 }
261 }
262 }
263
264 if(currentAcRule == null) {
265 accumulatedAccrualCatToAccrualAmounts.remove(anAC.getLmAccrualCategoryId());
266 accumulatedAccrualCatToNegativeAccrualAmounts.remove(anAC.getLmAccrualCategoryId());
267 continue;
268 }
269
270
271 BigDecimal accrualRate = currentAcRule.getAccrualRate();
272 int numberOfWorkDays = this.getWorkDaysInInterval(currentDate, anAC.getAccrualEarnInterval(), phra.getPayCalendar(), rrAggregate.getCalEntryMap());
273 BigDecimal dayRate = numberOfWorkDays > 0 ? accrualRate.divide(new BigDecimal(numberOfWorkDays), 6, BigDecimal.ROUND_HALF_UP) : new BigDecimal(0);
274
275
276 BigDecimal noAccrualHours = getNotEligibleForAccrualHours(principalId, currentDate.toLocalDate());
277 if(noAccrualHours != null && noAccrualHours.compareTo(BigDecimal.ZERO) != 0 && totalOfStandardHours.compareTo(BigDecimal.ZERO) != 0) {
278 BigDecimal dayHours = totalOfStandardHours.divide(new BigDecimal(5), 6, BigDecimal.ROUND_HALF_UP);
279
280 if(noAccrualHours.abs().compareTo(dayHours.abs()) == 1) {
281 noAccrualHours = dayHours.abs().negate();
282 }
283 BigDecimal noAccrualRate = dayRate.multiply(noAccrualHours.divide(dayHours));
284 this.calculateHours(anAC.getLmAccrualCategoryId(), ftePercentage, noAccrualRate, accumulatedAccrualCatToNegativeAccrualAmounts);
285 }
286
287
288 if(!TKUtils.isWeekend(currentDate) && !fullFteGranted) {
289
290
291 this.calculateHours(anAC.getLmAccrualCategoryId(), ftePercentage, dayRate, accumulatedAccrualCatToAccrualAmounts);
292 }
293
294
295 if(this.isDateAnIntervalDate(currentDate.toLocalDate(), anAC.getAccrualEarnInterval(), phra.getPayCalendar(), rrAggregate.getCalEntryMap())) {
296 BigDecimal acHours = accumulatedAccrualCatToAccrualAmounts.get(anAC.getLmAccrualCategoryId());
297
298 if(acHours != null) {
299 createLeaveBlock(principalId, accrualLeaveBlocks, currentDate.toLocalDate(), acHours, anAC, null, true, currentRange.getLeaveCalendarDocumentId());
300 accumulatedAccrualCatToAccrualAmounts.remove(anAC.getLmAccrualCategoryId());
301 fullFteGranted = false;
302 }
303
304 BigDecimal adjustmentHours = accumulatedAccrualCatToNegativeAccrualAmounts.get(anAC.getLmAccrualCategoryId());
305 if(adjustmentHours != null && adjustmentHours.compareTo(BigDecimal.ZERO) != 0) {
306
307 createLeaveBlock(principalId, accrualLeaveBlocks, currentDate.toLocalDate(), adjustmentHours, anAC, null, false, currentRange.getLeaveCalendarDocumentId());
308 accumulatedAccrualCatToNegativeAccrualAmounts.remove(anAC.getLmAccrualCategoryId());
309 }
310 }
311 }
312 }
313
314 SystemScheduledTimeOff ssto = currentRange.getSysScheTimeOff();
315 if(ssto != null) {
316 AccrualCategory anAC = HrServiceLocator.getAccrualCategoryService().getAccrualCategory(ssto.getAccrualCategory(), ssto.getEffectiveLocalDate());
317 if(anAC == null) {
318 LOG.error("Cannot find Accrual Category for system scheduled time off " + ssto.getLmSystemScheduledTimeOffId());
319 return;
320
321 }
322 BigDecimal hrs = ssto.getAmountofTime().multiply(ftePercentage);
323
324 createLeaveBlock(principalId, accrualLeaveBlocks, currentDate.toLocalDate(), hrs, anAC, ssto.getLmSystemScheduledTimeOffId(), true, currentRange.getLeaveCalendarDocumentId());
325
326 if(ssto.getScheduledTimeOffDate() != null) {
327
328 createLeaveBlock(principalId, accrualLeaveBlocks, ssto.getScheduledTimeOffLocalDate(), hrs.negate(), anAC, ssto.getLmSystemScheduledTimeOffId(), true, currentRange.getLeaveCalendarDocumentId());
329 }
330 }
331
332 if(endPhra != null && currentDate.toLocalDate().equals(endPhra.getEffectiveLocalDate())){
333
334 if(!accumulatedAccrualCatToAccrualAmounts.isEmpty()) {
335 for(Map.Entry<String, BigDecimal> entry : accumulatedAccrualCatToAccrualAmounts.entrySet()) {
336 if(entry.getValue() != null && entry.getValue().compareTo(BigDecimal.ZERO) != 0) {
337 AccrualCategory anAC = HrServiceLocator.getAccrualCategoryService().getAccrualCategory(entry.getKey());
338 createLeaveBlock(principalId, accrualLeaveBlocks, currentDate.toLocalDate(), entry.getValue(), anAC, null, true, currentRange.getLeaveCalendarDocumentId());
339 }
340 }
341 accumulatedAccrualCatToAccrualAmounts = new HashMap<String,BigDecimal>();
342 }
343
344 if(!accumulatedAccrualCatToNegativeAccrualAmounts.isEmpty()) {
345 for(Map.Entry<String, BigDecimal> entry : accumulatedAccrualCatToNegativeAccrualAmounts.entrySet()) {
346 if(entry.getValue() != null && entry.getValue().compareTo(BigDecimal.ZERO) != 0) {
347 AccrualCategory anAC = HrServiceLocator.getAccrualCategoryService().getAccrualCategory(entry.getKey());
348 createLeaveBlock(principalId, accrualLeaveBlocks, currentDate.toLocalDate(), entry.getValue(), anAC, null, true, currentRange.getLeaveCalendarDocumentId());
349 }
350 }
351 accumulatedAccrualCatToNegativeAccrualAmounts = new HashMap<String,BigDecimal>();
352 }
353 phra = null;
354 endPhra = null;
355 }
356
357 currentDate = currentDate.plusDays(1);
358 }
359
360
361 LmServiceLocator.getLeaveBlockService().saveLeaveBlocks(accrualLeaveBlocks);
362
363
364 if(recordRanData) {
365 LmServiceLocator.getPrincipalAccrualRanService().updatePrincipalAccrualRanInfo(principalId);
366 }
367
368 }
369
370 private void deactivateOldAccruals(String principalId, DateTime startDate, DateTime endDate, String runAsPrincipalId) {
371 List<LeaveBlock> previousLB = LmServiceLocator.getLeaveBlockService().getAccrualGeneratedLeaveBlocks(principalId, startDate.toLocalDate(), endDate.toLocalDate());
372 List<LeaveBlock> sstoAccrualList = new ArrayList<LeaveBlock>();
373 List<LeaveBlock> sstoUsageList = new ArrayList<LeaveBlock>();
374
375 for(LeaveBlock lb : previousLB) {
376 if(StringUtils.isNotEmpty(lb.getScheduleTimeOffId())) {
377 if(lb.getLeaveAmount().compareTo(BigDecimal.ZERO) > 0) {
378 sstoAccrualList.add(lb);
379 } else if(lb.getLeaveAmount().compareTo(BigDecimal.ZERO) < 0) {
380 sstoUsageList.add(lb);
381 }
382 } else {
383 if(!(StringUtils.equals(lb.getLeaveBlockType(),LMConstants.LEAVE_BLOCK_TYPE.BALANCE_TRANSFER) ||
384 StringUtils.equals(lb.getLeaveBlockType(),LMConstants.LEAVE_BLOCK_TYPE.LEAVE_PAYOUT))) {
385 LmServiceLocator.getLeaveBlockService().deleteLeaveBlock(lb.getLmLeaveBlockId(), runAsPrincipalId);
386 }
387 }
388 }
389
390 for(LeaveBlock accrualLb : sstoAccrualList) {
391 for(LeaveBlock usageLb : sstoUsageList) {
392
393
394 if(accrualLb.getScheduleTimeOffId().equals(usageLb.getScheduleTimeOffId())) {
395 LmServiceLocator.getLeaveBlockService().deleteLeaveBlock(accrualLb.getLmLeaveBlockId(), runAsPrincipalId);
396 LmServiceLocator.getLeaveBlockService().deleteLeaveBlock(usageLb.getLmLeaveBlockId(), runAsPrincipalId);
397 }
398 }
399 }
400
401 }
402
403 private BigDecimal getNotEligibleForAccrualHours(String principalId, LocalDate currentDate) {
404 BigDecimal hours = BigDecimal.ZERO;
405
406 List<LeaveBlock> lbs = LmServiceLocator.getLeaveBlockService().getNotAccrualGeneratedLeaveBlocksForDate(principalId, currentDate);
407 for(LeaveBlock lb : lbs) {
408 EarnCode ec = HrServiceLocator.getEarnCodeService().getEarnCode(lb.getEarnCode(), currentDate);
409 if(ec == null) {
410 LOG.error("Cannot find Earn Code for Leave block " + lb.getLmLeaveBlockId());
411 return null;
412
413 }
414 if(ec.getEligibleForAccrual().equals("N")
415 && ec.getAccrualBalanceAction().equals(HrConstants.ACCRUAL_BALANCE_ACTION.USAGE)
416 && lb.getLeaveAmount().compareTo(BigDecimal.ZERO) != 0) {
417 hours = hours.add(lb.getLeaveAmount());
418 }
419 }
420 return hours;
421 }
422
423 private void createLeaveBlock(String principalId, List<LeaveBlock> accrualLeaveBlocks,
424 LocalDate leaveDate, BigDecimal hrs, AccrualCategory anAC, String sysSchTimeOffId,
425 boolean createZeroLeaveBlock, String leaveDocId) {
426
427 EarnCode ec = HrServiceLocator.getEarnCodeService().getEarnCode(anAC.getEarnCode(), anAC.getEffectiveLocalDate());
428 if(ec == null) {
429
430 LOG.error("Cannot find Earn Code for Accrual category " + anAC.getAccrualCategory());
431 return;
432 }
433
434 BigDecimal roundedHours = HrServiceLocator.getEarnCodeService().roundHrsWithEarnCode(hrs, ec);
435 if(!createZeroLeaveBlock && roundedHours.compareTo(BigDecimal.ZERO) == 0) {
436 return;
437 }
438 LeaveBlock aLeaveBlock = new LeaveBlock();
439 aLeaveBlock.setAccrualCategory(anAC.getAccrualCategory());
440 aLeaveBlock.setLeaveLocalDate(leaveDate);
441 aLeaveBlock.setPrincipalId(principalId);
442
443 aLeaveBlock.setEarnCode(anAC.getEarnCode());
444 aLeaveBlock.setAccrualGenerated(true);
445 aLeaveBlock.setBlockId(0L);
446 aLeaveBlock.setScheduleTimeOffId(sysSchTimeOffId);
447 aLeaveBlock.setLeaveAmount(roundedHours);
448 aLeaveBlock.setLeaveBlockType(LMConstants.LEAVE_BLOCK_TYPE.ACCRUAL_SERVICE);
449 aLeaveBlock.setRequestStatus(HrConstants.REQUEST_STATUS.APPROVED);
450 aLeaveBlock.setDocumentId(leaveDocId);
451
452 accrualLeaveBlocks.add(aLeaveBlock);
453
454 }
455
456 private void createEmptyLeaveBlockForStatusChange(String principalId, List<LeaveBlock> accrualLeaveBlocks, LocalDate leaveDate) {
457 LeaveBlock aLeaveBlock = new LeaveBlock();
458 aLeaveBlock.setAccrualCategory(null);
459 aLeaveBlock.setLeaveLocalDate(leaveDate);
460 aLeaveBlock.setPrincipalId(principalId);
461 aLeaveBlock.setEarnCode(LMConstants.STATUS_CHANGE_EARN_CODE);
462 aLeaveBlock.setAccrualGenerated(true);
463 aLeaveBlock.setBlockId(0L);
464 aLeaveBlock.setScheduleTimeOffId(null);
465 aLeaveBlock.setLeaveAmount(BigDecimal.ZERO);
466 aLeaveBlock.setLeaveBlockType(LMConstants.LEAVE_BLOCK_TYPE.ACCRUAL_SERVICE);
467 aLeaveBlock.setRequestStatus(HrConstants.REQUEST_STATUS.APPROVED);
468
469 accrualLeaveBlocks.add(aLeaveBlock);
470
471 }
472
473 private void calculateHours(String accrualCategoryId, BigDecimal fte, BigDecimal rate, Map<String, BigDecimal> accumulatedAmounts ) {
474 BigDecimal hours = rate.multiply(fte);
475 BigDecimal oldHours = accumulatedAmounts.get(accrualCategoryId);
476 BigDecimal newHours = oldHours == null ? hours : hours.add(oldHours);
477 accumulatedAmounts.put(accrualCategoryId, newHours);
478 }
479
480 public DateTime getStartAccrualDate(String principalId){
481 return null;
482 }
483
484 public DateTime getEndAccrualDate(String principalId){
485 return null;
486 }
487
488 @Override
489 public void runAccrual(List<String> principalIds) {
490 for(String principalId : principalIds){
491 runAccrual(principalId);
492 }
493 }
494
495 private boolean isDateAnIntervalDate(LocalDate aDate, String earnInterval, String payCalName, Map<String, List<CalendarEntry>> aMap) {
496 if(earnInterval.equals(AccrualEarnInterval.PAY_CAL.getCode())) {
497 return isDateAtPayCalInterval(aDate, earnInterval, payCalName, aMap);
498 } else {
499 return this.isDateAtEarnInterval(aDate, earnInterval);
500 }
501 }
502
503 private boolean isDateAtPayCalInterval(LocalDate aDate, String earnInterval, String payCalName, Map<String, List<CalendarEntry>> aMap) {
504 if(StringUtils.isNotEmpty(payCalName)
505 && !aMap.isEmpty()
506 && earnInterval.equals(AccrualEarnInterval.PAY_CAL.getCode())) {
507 List<CalendarEntry> entryList = aMap.get(payCalName);
508 if(CollectionUtils.isNotEmpty(entryList)) {
509 for(CalendarEntry anEntry : entryList) {
510
511 LocalDate endDate = anEntry.getEndPeriodFullDateTime().toLocalDate().minusDays(1);
512 if(aDate.compareTo(endDate) == 0) {
513 return true;
514 }
515 }
516 }
517 }
518 return false;
519 }
520
521 @Override
522 public boolean isDateAtEarnInterval(LocalDate aDate, String earnInterval) {
523 boolean atEarnInterval = false;
524 AccrualEarnInterval accrualEarnInterval = AccrualEarnInterval.fromCode(earnInterval);
525 if (accrualEarnInterval != null) {
526 if (AccrualEarnInterval.DAILY.equals(accrualEarnInterval)) {
527 atEarnInterval = true;
528 } else if(AccrualEarnInterval.WEEKLY.equals(accrualEarnInterval)) {
529
530 if (aDate.getDayOfWeek() == DateTimeConstants.SATURDAY) {
531 atEarnInterval = true;
532 }
533 } else if (AccrualEarnInterval.SEMI_MONTHLY.equals(accrualEarnInterval)) {
534
535 if (aDate.getDayOfMonth() == 15 || aDate.getDayOfMonth() == aDate.dayOfMonth().getMaximumValue()) {
536 atEarnInterval = true;
537 }
538 } else if (AccrualEarnInterval.MONTHLY.equals(accrualEarnInterval)) {
539
540 if (aDate.getDayOfMonth() == aDate.dayOfMonth().getMaximumValue()) {
541 atEarnInterval = true;
542 }
543 } else if (AccrualEarnInterval.YEARLY.equals(accrualEarnInterval)) {
544
545 if (aDate.getDayOfYear() == aDate.dayOfYear().getMaximumValue()) {
546 atEarnInterval = true;
547 }
548 } else if (AccrualEarnInterval.NO_ACCRUAL.equals(accrualEarnInterval)) {
549
550 }
551 }
552 return atEarnInterval;
553 }
554
555
556 @Override
557 public RateRangeAggregate buildRateRangeAggregate(String principalId, DateTime startDate, DateTime endDate) {
558 RateRangeAggregate rrAggregate = new RateRangeAggregate();
559 List<RateRange> rateRangeList = new ArrayList<RateRange>();
560
561 List<Job> activeJobs = HrServiceLocator.getJobService().getAllActiveLeaveJobs(principalId, endDate.toLocalDate());
562 List<Job> inactiveJobs = HrServiceLocator.getJobService().getAllInActiveLeaveJobsInRange(principalId, endDate.toLocalDate());
563
564 List<PrincipalHRAttributes> phaList = HrServiceLocator.getPrincipalHRAttributeService().getAllActivePrincipalHrAttributesForPrincipalId(principalId, endDate.toLocalDate());
565 List<PrincipalHRAttributes> inactivePhaList = HrServiceLocator.getPrincipalHRAttributeService().getAllInActivePrincipalHrAttributesForPrincipalId(principalId, endDate.toLocalDate());
566
567 if(activeJobs.isEmpty() || phaList.isEmpty()) {
568 return rrAggregate;
569 }
570
571 Set<String> phaLpSet = new HashSet<String>();
572 Set<String> calNameSet = new HashSet<String>();
573 if(CollectionUtils.isNotEmpty(phaList)) {
574 for(PrincipalHRAttributes pha : phaList) {
575 phaLpSet.add(pha.getLeavePlan());
576 calNameSet.add(pha.getPayCalendar());
577 }
578 }
579
580 List<LeavePlan> activeLpList = new ArrayList<LeavePlan> ();
581 List<LeavePlan> inactiveLpList = new ArrayList<LeavePlan> ();
582 for(String lpString : phaLpSet) {
583 List<LeavePlan> aList = HrServiceLocator.getLeavePlanService().getAllActiveLeavePlan(lpString, endDate.toLocalDate());
584 activeLpList.addAll(aList);
585
586 aList = HrServiceLocator.getLeavePlanService().getAllInActiveLeavePlan(lpString, endDate.toLocalDate());
587 inactiveLpList.addAll(aList);
588 }
589
590
591 Map<String, List<CalendarEntry>> calEntryMap = new HashMap<String, List<CalendarEntry>>();
592 for(String calName : calNameSet) {
593 org.kuali.kpme.core.calendar.Calendar aCal = HrServiceLocator.getCalendarService().getCalendarByGroup(calName);
594 if(aCal != null) {
595 List<CalendarEntry> aList = HrServiceLocator.getCalendarEntryService().getAllCalendarEntriesForCalendarId(aCal.getHrCalendarId());
596 Collections.sort(aList);
597 calEntryMap.put(calName, aList);
598 }
599 }
600 rrAggregate.setCalEntryMap(calEntryMap);
601
602 Set<String> lpStringSet = new HashSet<String>();
603 if(CollectionUtils.isNotEmpty(activeLpList)) {
604 for(LeavePlan lp : activeLpList) {
605 lpStringSet.add(lp.getLeavePlan());
606 }
607 }
608 List<SystemScheduledTimeOff> sstoList = new ArrayList<SystemScheduledTimeOff>();
609 for(String lpString : lpStringSet) {
610 List<SystemScheduledTimeOff> aList =LmServiceLocator.getSysSchTimeOffService().getSystemScheduledTimeOffsForLeavePlan(startDate.toLocalDate(), endDate.toLocalDate(), lpString);
611 if(CollectionUtils.isNotEmpty(aList)) {
612 sstoList.addAll(aList);
613 }
614 }
615
616 List<AccrualCategory> activeAccrCatList = new ArrayList<AccrualCategory>();
617 List<AccrualCategory> inactiveAccrCatList = new ArrayList<AccrualCategory>();
618 for(String lpString : lpStringSet) {
619 List<AccrualCategory> aList = HrServiceLocator.getAccrualCategoryService().getActiveLeaveAccrualCategoriesForLeavePlan(lpString, endDate.toLocalDate());
620 if(CollectionUtils.isNotEmpty(aList)) {
621 activeAccrCatList.addAll(aList);
622 }
623
624 aList = HrServiceLocator.getAccrualCategoryService().getInActiveLeaveAccrualCategoriesForLeavePlan(lpString, endDate.toLocalDate());
625 if(CollectionUtils.isNotEmpty(aList)) {
626 inactiveAccrCatList.addAll(aList);
627 }
628 }
629
630 List<AccrualCategoryRule> activeRuleList = new ArrayList<AccrualCategoryRule>();
631 List<AccrualCategoryRule> inactiveRuleList = new ArrayList<AccrualCategoryRule>();
632 for(AccrualCategory ac : activeAccrCatList) {
633 List<AccrualCategoryRule> aRuleList = HrServiceLocator.getAccrualCategoryRuleService().getActiveRulesForAccrualCategoryId(ac.getLmAccrualCategoryId());
634 activeRuleList.addAll(aRuleList);
635
636 aRuleList = HrServiceLocator.getAccrualCategoryRuleService().getInActiveRulesForAccrualCategoryId(ac.getLmAccrualCategoryId());
637 inactiveRuleList.addAll(aRuleList);
638 }
639
640 List<LeaveCalendarDocumentHeader> lcDocList = LmServiceLocator.getLeaveCalendarDocumentHeaderService().getAllDocumentHeadersInRangeForPricipalId(principalId, startDate, endDate);
641
642 BigDecimal previousFte = null;
643 List<Job> jobs = new ArrayList<Job>();
644
645 DateTime currentDate = startDate;
646 while (!currentDate.isAfter(endDate)) {
647 RateRange rateRange = new RateRange();
648
649 jobs = this.getJobsForDate(activeJobs, inactiveJobs, currentDate.toLocalDate());
650 if(jobs.isEmpty()) {
651 currentDate = currentDate.plusDays(1);
652 continue;
653 }
654 rateRange.setJobs(jobs);
655
656
657 BigDecimal fteSum = HrServiceLocator.getJobService().getFteSumForJobs(jobs);
658 rateRange.setAccrualRatePercentageModifier(fteSum);
659 BigDecimal standardHours = HrServiceLocator.getJobService().getStandardHoursSumForJobs(jobs);
660 rateRange.setStandardHours(standardHours);
661
662 if(previousFte != null && !previousFte.equals(fteSum)) {
663 rateRange.setStatusChanged(true);
664 rrAggregate.setRateRangeChanged(true);
665 }
666 previousFte = fteSum;
667
668
669 PrincipalHRAttributes phra = this.getPrincipalHrAttributesForDate(phaList, currentDate.toLocalDate());
670 rateRange.setPrincipalHRAttributes(phra);
671
672 if(rateRange.getPrincipalHRAttributes() != null) {
673
674 PrincipalHRAttributes endPhra = this.getInactivePrincipalHrAttributesForDate(inactivePhaList, rateRange.getPrincipalHRAttributes().getEffectiveLocalDate(), currentDate.toLocalDate());
675 rateRange.setEndPrincipalHRAttributes(endPhra);
676 }
677
678
679 if(rateRange.getPrincipalHRAttributes()!= null) {
680 rateRange.setLeavePlan(this.getLeavePlanForDate(activeLpList, inactiveLpList, rateRange.getPrincipalHRAttributes().getLeavePlan(), currentDate.toLocalDate()));
681 }
682
683 if(rateRange.getLeavePlan() != null) {
684
685 List<AccrualCategory> acsForDay = this.getAccrualCategoriesForDate(activeAccrCatList, inactiveAccrCatList, rateRange.getLeavePlan().getLeavePlan(), currentDate.toLocalDate());
686 rateRange.setAcList(acsForDay);
687
688
689 for(SystemScheduledTimeOff ssto : sstoList) {
690 if(ssto.getAccruedLocalDate().equals(currentDate.toLocalDate())
691 && ssto.getLeavePlan().equals(rateRange.getLeavePlan().getLeavePlan())) {
692
693
694
695 List<LeaveBlock> sstoLbList = LmServiceLocator.getLeaveBlockService().getSSTOLeaveBlocks(principalId, ssto.getLmSystemScheduledTimeOffId(), ssto.getAccruedLocalDate());
696 if(CollectionUtils.isEmpty(sstoLbList)) {
697 rateRange.setSysScheTimeOff(ssto);
698 }
699 }
700 }
701 }
702
703 if(CollectionUtils.isNotEmpty(rateRange.getAcList())) {
704 List<AccrualCategoryRule> rulesForDay = new ArrayList<AccrualCategoryRule>();
705 for(AccrualCategory ac : rateRange.getAcList()) {
706 rulesForDay.addAll(this.getAccrualCategoryRulesForDate
707 (activeRuleList, ac.getLmAccrualCategoryId(), currentDate.toLocalDate(), rateRange.getPrincipalHRAttributes().getServiceLocalDate()));
708 }
709 rateRange.setAcRuleList(rulesForDay);
710
711 }
712
713 Interval range = new Interval(currentDate, currentDate.plusDays(1));
714 rateRange.setRange(range);
715
716
717 rateRange.setLeaveCalendarDocumentId(this.getLeaveDocumentForDate(lcDocList, currentDate));
718 rateRangeList.add(rateRange);
719
720 currentDate = currentDate.plusDays(1);
721 }
722 rrAggregate.setRateRanges(rateRangeList);
723 rrAggregate.setCurrentRate(null);
724 return rrAggregate;
725 }
726
727 private String getLeaveDocumentForDate(List<LeaveCalendarDocumentHeader> lcDocList, DateTime currentDate) {
728 for(LeaveCalendarDocumentHeader lcdh : lcDocList) {
729 if(!lcdh.getBeginDateTime().isAfter(currentDate) && lcdh.getEndDateTime().isAfter(currentDate)) {
730 return lcdh.getDocumentId();
731 }
732 }
733 return "";
734 }
735
736 public List<Job> getJobsForDate(List<Job> activeJobs, List<Job> inactiveJobs, LocalDate currentDate) {
737 List<Job> jobs = new ArrayList<Job>();
738 for(Job aJob : activeJobs) {
739 if(!aJob.getEffectiveLocalDate().isAfter(currentDate)) {
740 jobs.add(aJob);
741 }
742 }
743 if(CollectionUtils.isNotEmpty(jobs)) {
744 List<Job> tempList = new ArrayList<Job>();
745 tempList.addAll(jobs);
746 for(Job aJob : tempList) {
747 for(Job inactiveJob : inactiveJobs) {
748 if(inactiveJob.getJobNumber().equals(aJob.getJobNumber())
749 && inactiveJob.getEffectiveLocalDate().isAfter(aJob.getEffectiveLocalDate())
750 && !inactiveJob.getEffectiveLocalDate().isAfter(currentDate)) {
751
752 jobs.remove(aJob);
753 }
754 }
755 }
756 }
757 return jobs;
758 }
759
760 public PrincipalHRAttributes getPrincipalHrAttributesForDate(List<PrincipalHRAttributes> activeList, LocalDate currentDate) {
761 List<PrincipalHRAttributes> phasForDay = new ArrayList<PrincipalHRAttributes>();
762 for(PrincipalHRAttributes pha : activeList) {
763 if(pha != null && pha.getEffectiveLocalDate() != null && pha.getServiceLocalDate() != null
764 && !pha.getEffectiveLocalDate().isAfter(currentDate) && !pha.getServiceLocalDate().isAfter(currentDate)) {
765 phasForDay.add(pha);
766 }
767 }
768 if(CollectionUtils.isNotEmpty(phasForDay)) {
769 PrincipalHRAttributes pha = phasForDay.get(0);
770 int indexOfMaxEffDt = 0;
771 if(phasForDay.size() > 1) {
772 for(int i = 1; i < phasForDay.size(); i++) {
773 if( (phasForDay.get(i).getEffectiveLocalDate().isAfter(phasForDay.get(indexOfMaxEffDt).getEffectiveLocalDate()))
774 ||(phasForDay.get(i).getEffectiveLocalDate().equals(phasForDay.get(indexOfMaxEffDt).getEffectiveLocalDate())
775 && phasForDay.get(i).getTimestamp().after(phasForDay.get(indexOfMaxEffDt).getTimestamp()))) {
776 indexOfMaxEffDt = i;
777 }
778 }
779 pha = phasForDay.get(indexOfMaxEffDt);
780 }
781 return pha;
782 }
783 return null;
784 }
785
786 public PrincipalHRAttributes getInactivePrincipalHrAttributesForDate(List<PrincipalHRAttributes> inactiveList, LocalDate activeDate, LocalDate currentDate) {
787 List<PrincipalHRAttributes> inactivePhasForDay = new ArrayList<PrincipalHRAttributes>();
788 for(PrincipalHRAttributes pha : inactiveList) {
789 if( pha.getEffectiveLocalDate().isAfter(activeDate) && !pha.getServiceLocalDate().isAfter(currentDate)) {
790 inactivePhasForDay.add(pha);
791 }
792 }
793 if(CollectionUtils.isNotEmpty(inactivePhasForDay)) {
794 PrincipalHRAttributes pha = inactivePhasForDay.get(0);
795 int indexOfMaxEffDt = 0;
796 if(inactivePhasForDay.size() > 1) {
797 for(int i = 1; i < inactivePhasForDay.size(); i++) {
798 if( (inactivePhasForDay.get(i).getEffectiveLocalDate().isAfter(inactivePhasForDay.get(indexOfMaxEffDt).getEffectiveLocalDate()))
799 ||(inactivePhasForDay.get(i).getEffectiveLocalDate().equals(inactivePhasForDay.get(indexOfMaxEffDt).getEffectiveLocalDate())
800 && inactivePhasForDay.get(i).getTimestamp().after(inactivePhasForDay.get(indexOfMaxEffDt).getTimestamp()))) {
801 indexOfMaxEffDt = i;
802 }
803 }
804 pha = inactivePhasForDay.get(indexOfMaxEffDt);
805 }
806 return pha;
807 }
808 return null;
809 }
810
811 public LeavePlan getLeavePlanForDate(List<LeavePlan> activeLpList, List<LeavePlan> inactiveLpList, String leavePlan, LocalDate currentDate) {
812 List<LeavePlan> lpsForDay = new ArrayList<LeavePlan>();
813 for(LeavePlan lp : activeLpList) {
814 if(lp.getLeavePlan().equals(leavePlan) && !lp.getEffectiveLocalDate().isAfter(currentDate)) {
815 lpsForDay.add(lp);
816 }
817 }
818 List<LeavePlan> aList = new ArrayList<LeavePlan>();
819 aList.addAll(lpsForDay);
820 for(LeavePlan lp : aList) {
821 for(LeavePlan inactiveLp : inactiveLpList) {
822 if(inactiveLp.getLeavePlan().equals(lp.getLeavePlan())
823 && inactiveLp.getEffectiveLocalDate().isAfter(lp.getEffectiveLocalDate())
824 && !inactiveLp.getEffectiveLocalDate().isAfter(currentDate)) {
825
826 lpsForDay.remove(lp);
827 }
828 }
829 }
830 if(CollectionUtils.isNotEmpty(lpsForDay)) {
831 LeavePlan aLp = lpsForDay.get(0);
832 int indexOfMaxEffDt = 0;
833 if(lpsForDay.size() > 1) {
834 for(int i = 1; i < lpsForDay.size(); i++) {
835 if( (lpsForDay.get(i).getEffectiveDate().after(lpsForDay.get(indexOfMaxEffDt).getEffectiveDate()))
836 ||(lpsForDay.get(i).getEffectiveDate().equals(lpsForDay.get(indexOfMaxEffDt).getEffectiveDate())
837 && lpsForDay.get(i).getTimestamp().after(lpsForDay.get(indexOfMaxEffDt).getTimestamp()))) {
838 indexOfMaxEffDt = i;
839 }
840 }
841 aLp = lpsForDay.get(indexOfMaxEffDt);
842 }
843 return aLp;
844 }
845 return null;
846 }
847
848 public List<AccrualCategory> getAccrualCategoriesForDate(List<AccrualCategory> activeAccrCatList, List<AccrualCategory> inactiveAccrCatList, String leavePlan, LocalDate currentDate) {
849 Set<AccrualCategory> aSet = new HashSet<AccrualCategory>();
850 for(AccrualCategory ac : activeAccrCatList) {
851 if(ac.getLeavePlan().equals(leavePlan) && !ac.getEffectiveLocalDate().isAfter(currentDate)) {
852 aSet.add(ac);
853 }
854 }
855 List<AccrualCategory> list1 = new ArrayList<AccrualCategory>();
856 list1.addAll(aSet);
857 for(AccrualCategory ac : list1) {
858 for(AccrualCategory inactiveAc : inactiveAccrCatList) {
859 if(inactiveAc.getAccrualCategory().equals(ac.getAccrualCategory())
860 && inactiveAc.getEffectiveLocalDate().isAfter(ac.getEffectiveLocalDate())
861 && !inactiveAc.getEffectiveLocalDate().isAfter(currentDate)) {
862
863 aSet.remove(ac);
864 }
865 }
866 }
867 List<AccrualCategory> acsForDay = new ArrayList<AccrualCategory>();
868 acsForDay.addAll(aSet);
869 return acsForDay;
870 }
871
872 @Override
873 public boolean isEmpoyeementFutureStatusChanged(String principalId, DateTime startDate, DateTime endDate) {
874 if(endDate.isAfter(LocalDate.now().toDateTimeAtStartOfDay())) {
875 RateRangeAggregate rrAggregate = this.buildRateRangeAggregate(principalId, startDate, endDate);
876 if(rrAggregate.isRateRangeChanged()) {
877 return true;
878 }
879 }
880 return false;
881 }
882
883 @Override
884 public void calculateFutureAccrualUsingPlanningMonth(String principalId, LocalDate asOfDate, String runAsPrincipalId) {
885 PrincipalHRAttributes phra = HrServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, asOfDate);
886 if(phra != null) {
887
888 LeavePlan lp = HrServiceLocator.getLeavePlanService().getLeavePlan(phra.getLeavePlan(), asOfDate);
889 if(lp != null && StringUtils.isNotEmpty(lp.getPlanningMonths())) {
890
891 LocalDate startDate = asOfDate.minusYears(1);
892 if(startDate.getDayOfMonth() > startDate.dayOfMonth().getMaximumValue()) {
893 startDate = startDate.withDayOfMonth(startDate.dayOfMonth().getMaximumValue());
894 }
895
896 LocalDate endDate = asOfDate.plusMonths(Integer.parseInt(lp.getPlanningMonths()));
897
898 if(endDate.getDayOfMonth() > endDate.dayOfMonth().getMaximumValue()) {
899 endDate = endDate.withDayOfMonth(endDate.dayOfMonth().getMaximumValue());
900 }
901 runAccrual(principalId, startDate.toDateTimeAtStartOfDay(), endDate.toDateTimeAtStartOfDay(), true, runAsPrincipalId);
902
903 }
904 }
905 }
906
907 private boolean minimumPercentageReachedForPayPeriod(BigDecimal min, String earnInterval, int workDays, DateTime intervalDate, String payCalName, Map<String, List<CalendarEntry>> aMap) {
908 if(min == null || min.compareTo(BigDecimal.ZERO) == 0) {
909 return true;
910 }
911 int daysInInterval = this.getWorkDaysInInterval(intervalDate, earnInterval, payCalName, aMap);
912 if(daysInInterval == 0) {
913 return true;
914 }
915 BigDecimal actualPercentage = new BigDecimal(workDays).divide(new BigDecimal(daysInInterval), 2, BigDecimal.ROUND_HALF_EVEN);
916 if(actualPercentage.compareTo(min) >= 0) {
917 return true;
918 }
919
920 return false;
921 }
922
923 private DateTime getPrevIntervalDate(DateTime aDate, String earnInterval, String payCalName, Map<String, List<CalendarEntry>> aMap) {
924 if(earnInterval.equals(AccrualEarnInterval.PAY_CAL.getCode())) {
925 return this.getPrevPayCalIntervalDate(aDate, earnInterval, payCalName, aMap);
926 } else {
927 return this.getPreviousAccrualIntervalDate(earnInterval, aDate);
928 }
929 }
930
931 @Override
932 public DateTime getPreviousAccrualIntervalDate(String earnInterval, DateTime aDate) {
933 DateTime previousAccrualIntervalDate = null;
934 AccrualEarnInterval accrualEarnInterval = AccrualEarnInterval.fromCode(earnInterval);
935 if (AccrualEarnInterval.DAILY.equals(accrualEarnInterval)) {
936 previousAccrualIntervalDate = aDate.minusDays(1);
937 } else if(AccrualEarnInterval.WEEKLY.equals(accrualEarnInterval)) {
938 previousAccrualIntervalDate = aDate.minusWeeks(1).withDayOfWeek(DateTimeConstants.SATURDAY);
939 } else if (AccrualEarnInterval.SEMI_MONTHLY.equals(accrualEarnInterval)) {
940 previousAccrualIntervalDate = aDate.minusDays(15);
941 if (previousAccrualIntervalDate.getDayOfMonth() <= 15) {
942 previousAccrualIntervalDate = previousAccrualIntervalDate.withDayOfMonth(15);
943 } else {
944 previousAccrualIntervalDate = previousAccrualIntervalDate.withDayOfMonth(previousAccrualIntervalDate.dayOfMonth().getMaximumValue());
945 }
946 } else if (AccrualEarnInterval.MONTHLY.equals(accrualEarnInterval)) {
947 previousAccrualIntervalDate = aDate.minusMonths(1);
948 previousAccrualIntervalDate = previousAccrualIntervalDate.withDayOfMonth(previousAccrualIntervalDate.dayOfMonth().getMaximumValue());
949 } else if (AccrualEarnInterval.YEARLY.equals(accrualEarnInterval)) {
950 previousAccrualIntervalDate = aDate.minusYears(1);
951 previousAccrualIntervalDate = previousAccrualIntervalDate.withDayOfYear(previousAccrualIntervalDate.dayOfYear().getMaximumValue());
952 } else if (AccrualEarnInterval.NO_ACCRUAL.equals(accrualEarnInterval)) {
953 previousAccrualIntervalDate = aDate;
954 }
955
956 return previousAccrualIntervalDate;
957 }
958
959 private DateTime getPrevPayCalIntervalDate(DateTime aDate, String earnInterval, String payCalName, Map<String, List<CalendarEntry>> aMap) {
960 if(StringUtils.isNotEmpty(payCalName)
961 && !aMap.isEmpty()
962 && earnInterval.equals(AccrualEarnInterval.PAY_CAL.getCode())) {
963 List<CalendarEntry> entryList = aMap.get(payCalName);
964 if(CollectionUtils.isNotEmpty(entryList)) {
965 for(CalendarEntry anEntry : entryList) {
966
967 DateTime endDate = anEntry.getEndPeriodFullDateTime().minusDays(1);
968 if(anEntry.getBeginPeriodFullDateTime().compareTo(aDate) <= 0 && endDate.compareTo(aDate) >= 0) {
969
970 DateTime prevIntvDate = anEntry.getBeginPeriodFullDateTime().minusDays(1);
971 return prevIntvDate;
972 }
973 }
974 }
975 }
976 return aDate;
977 }
978
979 @Override
980 public DateTime getNextIntervalDate(DateTime aDate, String earnInterval, String payCalName, Map<String, List<CalendarEntry>> aMap) {
981 if(earnInterval.equals(AccrualEarnInterval.PAY_CAL.getCode())) {
982 return this.getNextPayCalIntervalDate(aDate, earnInterval, payCalName, aMap);
983 } else {
984 return this.getNextAccrualIntervalDate(earnInterval, aDate);
985 }
986 }
987
988 private DateTime getNextPayCalIntervalDate(DateTime aDate, String earnInterval, String payCalName, Map<String, List<CalendarEntry>> aMap) {
989 if(StringUtils.isNotEmpty(payCalName)
990 && !aMap.isEmpty()
991 && earnInterval.equals(AccrualEarnInterval.PAY_CAL.getCode())) {
992 List<CalendarEntry> entryList = aMap.get(payCalName);
993 if(CollectionUtils.isNotEmpty(entryList)) {
994 for(CalendarEntry anEntry : entryList) {
995
996 DateTime endDate = anEntry.getEndPeriodFullDateTime().minusDays(1);
997 if(anEntry.getBeginPeriodFullDateTime().compareTo(aDate) <= 0 && endDate.compareTo(aDate) >= 0) {
998
999 return endDate;
1000 }
1001 }
1002 }
1003 }
1004 return aDate;
1005 }
1006
1007 @Override
1008 public DateTime getNextAccrualIntervalDate(String earnInterval, DateTime aDate) {
1009 DateTime nextAccrualIntervalDate = null;
1010 AccrualEarnInterval accrualEarnInterval = AccrualEarnInterval.fromCode(earnInterval);
1011 if (AccrualEarnInterval.DAILY.equals(accrualEarnInterval)) {
1012 nextAccrualIntervalDate = aDate;
1013 } else if(AccrualEarnInterval.WEEKLY.equals(accrualEarnInterval)) {
1014 if (aDate.getDayOfWeek() != DateTimeConstants.SATURDAY) {
1015 nextAccrualIntervalDate = aDate.withDayOfWeek(DateTimeConstants.SATURDAY);
1016 } else {
1017 nextAccrualIntervalDate = aDate.withWeekOfWeekyear(1);
1018 }
1019 } else if (AccrualEarnInterval.SEMI_MONTHLY.equals(accrualEarnInterval)) {
1020 if(aDate.getDayOfMonth() <= 15) {
1021 nextAccrualIntervalDate = aDate.withDayOfMonth(15);
1022 } else {
1023 nextAccrualIntervalDate = aDate.withDayOfMonth(aDate.dayOfMonth().getMaximumValue());
1024 }
1025 } else if (AccrualEarnInterval.MONTHLY.equals(accrualEarnInterval)) {
1026 nextAccrualIntervalDate = aDate.withDayOfMonth(aDate.dayOfMonth().getMaximumValue());
1027 } else if (AccrualEarnInterval.YEARLY.equals(accrualEarnInterval)) {
1028 nextAccrualIntervalDate = aDate.withDayOfYear(aDate.dayOfYear().getMaximumValue());
1029 } else if (AccrualEarnInterval.NO_ACCRUAL.equals(accrualEarnInterval)) {
1030 nextAccrualIntervalDate = aDate;
1031 } else if (AccrualEarnInterval.PAY_CAL.equals(accrualEarnInterval)) {
1032 LOG.error("Accrual Earn Interval of Pay CAL is not valid for AccrualServiceImpl:getNextAccrualIntervalDate");
1033 nextAccrualIntervalDate = null;
1034 }
1035
1036 return nextAccrualIntervalDate;
1037 }
1038
1039 private int getWorkDaysInInterval(DateTime aDate, String earnInterval, String payCalName, Map<String, List<CalendarEntry>> aMap) {
1040 if(earnInterval.equals(AccrualEarnInterval.PAY_CAL.getCode())) {
1041 return this.getWorkDaysInPayCalInterval(aDate, earnInterval, payCalName, aMap);
1042 } else {
1043 return this.getWorkDaysInAccrualInterval(earnInterval, aDate);
1044 }
1045 }
1046
1047 private int getWorkDaysInPayCalInterval(DateTime aDate, String earnInterval, String payCalName, Map<String, List<CalendarEntry>> aMap) {
1048 if(StringUtils.isNotEmpty(payCalName)
1049 && !aMap.isEmpty()
1050 && earnInterval.equals(AccrualEarnInterval.PAY_CAL.getCode())) {
1051 List<CalendarEntry> entryList = aMap.get(payCalName);
1052 if(CollectionUtils.isNotEmpty(entryList)) {
1053 for(CalendarEntry anEntry : entryList) {
1054
1055 DateTime endDate = anEntry.getEndPeriodFullDateTime().minusDays(1);
1056 if(anEntry.getBeginPeriodFullDateTime().compareTo(aDate) <= 0 && endDate.compareTo(aDate) >= 0) {
1057 return TKUtils.getWorkDays(anEntry.getBeginPeriodFullDateTime(), endDate);
1058 }
1059 }
1060 }
1061 }
1062 return 0;
1063 }
1064
1065 @Override
1066 public int getWorkDaysInAccrualInterval(String earnInterval, DateTime aDate) {
1067 AccrualEarnInterval accrualEarnInterval = AccrualEarnInterval.fromCode(earnInterval);
1068 if (accrualEarnInterval != null) {
1069 if(AccrualEarnInterval.DAILY.equals(accrualEarnInterval)) {
1070 return 1;
1071 } else if(AccrualEarnInterval.WEEKLY.equals(accrualEarnInterval)) {
1072 return 5;
1073 } else if (AccrualEarnInterval.SEMI_MONTHLY.equals(accrualEarnInterval)) {
1074 if(aDate.getDayOfMonth() <= 15) {
1075 return TKUtils.getWorkDays(aDate.withDayOfMonth(1), aDate.withDayOfMonth(15));
1076 } else {
1077 return TKUtils.getWorkDays(aDate.withDayOfMonth(16), aDate.withDayOfMonth(aDate.dayOfMonth().getMaximumValue()));
1078 }
1079 } else if (AccrualEarnInterval.MONTHLY.equals(accrualEarnInterval)) {
1080 return TKUtils.getWorkDays(aDate.withDayOfMonth(1), aDate.withDayOfMonth(aDate.dayOfMonth().getMaximumValue()));
1081 } else if (AccrualEarnInterval.YEARLY.equals(accrualEarnInterval)) {
1082 return TKUtils.getWorkDays(aDate.withDayOfYear(1), aDate.withDayOfYear(aDate.dayOfYear().getMaximumValue()));
1083 } else if (AccrualEarnInterval.NO_ACCRUAL.equals(accrualEarnInterval)) {
1084 return 0;
1085 }
1086 }
1087 return 0;
1088 }
1089
1090 public DateTime getRuleStartDate(String earnInterval, LocalDate serviceDate, Long startAcc) {
1091 DateTime ruleStartDate = null;
1092
1093 String intervalValue = HrConstants.SERVICE_UNIT_OF_TIME.get(earnInterval);
1094
1095 if (intervalValue.equals("Months")) {
1096 ruleStartDate = serviceDate.toDateTimeAtStartOfDay().plusMonths(startAcc.intValue());
1097 if (ruleStartDate.getDayOfMonth() > ruleStartDate.dayOfMonth().getMaximumValue()) {
1098 ruleStartDate = ruleStartDate.withDayOfMonth(ruleStartDate.dayOfMonth().getMaximumValue());
1099 }
1100 } else if (intervalValue.equals("Years")) {
1101 ruleStartDate = serviceDate.toDateTimeAtStartOfDay().withYear(startAcc.intValue());
1102 } else {
1103 ruleStartDate = serviceDate.toDateTimeAtStartOfDay();
1104 }
1105
1106 return ruleStartDate;
1107 }
1108
1109 public boolean isProrationFlag(String proration) {
1110 if(proration == null) {
1111 return true;
1112 }
1113 return proration.equals("Y") ? true : false;
1114 }
1115
1116 @Override
1117 public boolean statusChangedSinceLastRun(String principalId) {
1118 PrincipalAccrualRan par = LmServiceLocator.getPrincipalAccrualRanService().getLastPrincipalAccrualRan(principalId);
1119 if(par == null) {
1120 return true;
1121 }
1122 Job aJob = HrServiceLocator.getJobService().getMaxTimestampJob(principalId);
1123
1124 if(aJob != null && aJob.getTimestamp().after(par.getLastRanTs())) {
1125 return true;
1126 }
1127
1128 Assignment anAssign = HrServiceLocator.getAssignmentService().getMaxTimestampAssignment(principalId);
1129 if(anAssign != null && anAssign.getTimestamp().after(par.getLastRanTs())) {
1130 return true;
1131 }
1132
1133 PrincipalHRAttributes pha = HrServiceLocator.getPrincipalHRAttributeService().getMaxTimeStampPrincipalHRAttributes(principalId);
1134 if(pha != null && pha.getTimestamp().after(par.getLastRanTs())) {
1135 return true;
1136 }
1137
1138 List<LeaveBlock> lbList = LmServiceLocator.getLeaveBlockService().getABELeaveBlocksSinceTime(principalId, par.getLastRanDateTime());
1139 if(CollectionUtils.isNotEmpty(lbList)) {
1140 return true;
1141 }
1142 return false;
1143 }
1144
1145 public List<AccrualCategoryRule> getAccrualCategoryRulesForDate(List<AccrualCategoryRule> acrList, String accrualCategoryId, LocalDate currentDate, LocalDate serviceDate) {
1146 List<AccrualCategoryRule> aList = new ArrayList<AccrualCategoryRule>();
1147 if(CollectionUtils.isNotEmpty(acrList)) {
1148 for(AccrualCategoryRule acr : acrList) {
1149 if(acr.getLmAccrualCategoryId().equals(accrualCategoryId)) {
1150 String uot = acr.getServiceUnitOfTime();
1151 int startTime = acr.getStart().intValue();
1152 int endTime = acr.getEnd().intValue();
1153
1154 LocalDate startDate = serviceDate;
1155 LocalDate endDate = serviceDate;
1156 if(uot.equals("M")) {
1157 startDate = startDate.plusMonths(startTime);
1158 endDate = endDate.plusMonths(endTime).minusDays(1);
1159 } else if(uot.endsWith("Y")) {
1160 startDate = startDate.plusYears(startTime);
1161 endDate = endDate.plusYears(endTime).minusDays(1);
1162 }
1163
1164
1165 if(startDate.getDayOfMonth() > startDate.dayOfMonth().getMaximumValue()) {
1166 startDate = startDate.withDayOfMonth(startDate.dayOfMonth().getMaximumValue());
1167 }
1168 if(endDate.getDayOfMonth() > endDate.dayOfMonth().getMaximumValue()) {
1169 endDate = endDate.withDayOfMonth(endDate.dayOfMonth().getMaximumValue());
1170 }
1171
1172 if(currentDate.compareTo(startDate) >= 0 && currentDate.compareTo(endDate) <=0 ) {
1173 aList.add(acr);
1174 }
1175 }
1176 }
1177 }
1178 return aList;
1179 }
1180
1181 public AccrualCategoryRule getRuleForAccrualCategory(List<AccrualCategoryRule> acrList, AccrualCategory ac) {
1182 if(CollectionUtils.isNotEmpty(acrList)) {
1183 for(AccrualCategoryRule acr : acrList) {
1184 if(acr.getLmAccrualCategoryId().equals(ac.getLmAccrualCategoryId())) {
1185 return acr;
1186 }
1187 }
1188 }
1189 return null;
1190 }
1191
1192
1193 @Override
1194 public BigDecimal getAccruedBalanceForPrincipal(String principalId,
1195 AccrualCategory accrualCategory, LocalDate asOfDate) {
1196 BigDecimal balance = new BigDecimal(0);
1197 PrincipalHRAttributes pha = HrServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, asOfDate);
1198 if(pha == null)
1199 return BigDecimal.ZERO;
1200
1201 List<LeaveBlock> leaveBlocks = LmServiceLocator.getLeaveBlockService().getLeaveBlocksWithAccrualCategory(principalId, pha.getServiceLocalDate(), asOfDate, accrualCategory.getAccrualCategory());
1202 for(LeaveBlock block : leaveBlocks) {
1203 if(!(StringUtils.equals(block.getRequestStatus(),HrConstants.REQUEST_STATUS.DEFERRED)
1204 || StringUtils.equals(block.getRequestStatus(),HrConstants.REQUEST_STATUS.DISAPPROVED))) {
1205 balance = balance.add(block.getLeaveAmount());
1206
1207
1208
1209
1210
1211 }
1212 }
1213 return balance;
1214 }
1215
1216 @Override
1217 public BigDecimal getApprovedBalanceForPrincipal(String principalId,
1218 AccrualCategory accrualCategory, LocalDate asOfDate) {
1219 BigDecimal balance = new BigDecimal(0);
1220 PrincipalHRAttributes pha = HrServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, asOfDate);
1221 List<LeaveBlock> leaveBlocks = LmServiceLocator.getLeaveBlockService().getLeaveBlocksWithAccrualCategory(principalId, pha.getServiceLocalDate(), asOfDate, accrualCategory.getAccrualCategory());
1222 for(LeaveBlock block : leaveBlocks) {
1223 if(StringUtils.equals(block.getRequestStatus(),HrConstants.REQUEST_STATUS.APPROVED)) {
1224 balance = balance.add(block.getLeaveAmount());
1225
1226
1227
1228
1229
1230 }
1231 }
1232 return balance;
1233 }
1234 }