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