001 /**
002 * Copyright 2004-2013 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.kuali.hr.lm.leaveSummary.service;
017
018 import org.apache.commons.collections.CollectionUtils;
019 import org.apache.commons.lang.StringUtils;
020 import org.apache.commons.lang.time.DateUtils;
021 import org.joda.time.DateMidnight;
022 import org.joda.time.DateTime;
023 import org.joda.time.Interval;
024 import org.joda.time.LocalDateTime;
025 import org.kuali.hr.lm.LMConstants;
026 import org.kuali.hr.lm.accrual.AccrualCategory;
027 import org.kuali.hr.lm.accrual.AccrualCategoryRule;
028 import org.kuali.hr.lm.employeeoverride.EmployeeOverride;
029 import org.kuali.hr.lm.leaveSummary.LeaveSummary;
030 import org.kuali.hr.lm.leaveSummary.LeaveSummaryRow;
031 import org.kuali.hr.lm.leaveblock.LeaveBlock;
032 import org.kuali.hr.lm.leaveblock.service.LeaveBlockService;
033 import org.kuali.hr.lm.leaveplan.LeavePlan;
034 import org.kuali.hr.lm.workflow.LeaveCalendarDocumentHeader;
035 import org.kuali.hr.time.calendar.CalendarEntries;
036 import org.kuali.hr.time.earncode.EarnCode;
037 import org.kuali.hr.time.principal.PrincipalHRAttributes;
038 import org.kuali.hr.time.service.base.TkServiceLocator;
039 import org.kuali.hr.time.util.TKUtils;
040 import org.kuali.hr.time.util.TkConstants;
041 import org.kuali.rice.krad.util.ObjectUtils;
042
043 import java.math.BigDecimal;
044 import java.sql.Timestamp;
045 import java.text.DateFormat;
046 import java.text.ParseException;
047 import java.text.SimpleDateFormat;
048 import java.util.*;
049
050 public class LeaveSummaryServiceImpl implements LeaveSummaryService {
051 private LeaveBlockService leaveBlockService;
052
053 @Override
054 public LeaveSummary getLeaveSummaryAsOfDate(String principalId, java.sql.Date asOfDate) {
055 return getLeaveSummary(principalId, asOfDate, asOfDate, null, true);
056 }
057
058 public LeaveSummary getLeaveSummaryAsOfDateWithoutFuture(String principalId, java.sql.Date asOfDate) {
059 return getLeaveSummary(principalId, asOfDate, asOfDate, null, false);
060 }
061
062 @Override
063 public LeaveSummary getLeaveSummary(String principalId, CalendarEntries calendarEntry) {
064 return getLeaveSummary(principalId, calendarEntry.getBeginPeriodDate(), calendarEntry.getEndPeriodDate(), null, true);
065 }
066
067 @Override
068 public LeaveSummary getLeaveSummaryAsOfDateForAccrualCategory(String principalId, java.sql.Date asOfDate, String accrualCategory) {
069 return getLeaveSummary(principalId, asOfDate, asOfDate, accrualCategory, true);
070 }
071
072 @Override
073 // startDate is the leave request start date, endDat is leave request end date, usageEndDate is the date before next accrual interval date for leave requst end date
074 // will get leave balance up to the next earn interval for a certain date, including usage up to that next earn interval
075 public BigDecimal getLeaveBalanceForAccrCatUpToDate(String principalId,
076 java.sql.Date startDate,
077 java.sql.Date endDate,
078 String accrualCategory,
079 Date usageEndDate) {
080 BigDecimal leaveBalance = BigDecimal.ZERO;
081 if(StringUtils.isEmpty(principalId) || startDate == null || endDate == null || StringUtils.isEmpty(accrualCategory) || usageEndDate == null) {
082 return leaveBalance;
083 }
084
085 LeaveSummaryRow lsr = new LeaveSummaryRow();
086 AccrualCategory ac = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(accrualCategory, endDate);
087
088 if(ac != null) {
089 LeavePlan lp = TkServiceLocator.getLeavePlanService().getLeavePlan(ac.getLeavePlan(), ac.getEffectiveDate());
090 if(lp == null) {
091 return leaveBalance;
092 }
093 PrincipalHRAttributes pha = getPrincipalHrAttributes(principalId, startDate, endDate);
094 //until we have something that creates carry over, we need to grab everything.
095 // Calculating leave bLocks from Calendar Year start instead of Service Date
096 Map<String, LeaveBlock> carryOverBlocks = getLeaveBlockService().getLastCarryOverBlocks(principalId, startDate);
097 //remove unwanted carry over blocks from map
098 LeaveBlock carryOverBlock = carryOverBlocks.get(accrualCategory);
099 carryOverBlocks = new HashMap<String, LeaveBlock>(1);
100 if(ObjectUtils.isNotNull(carryOverBlock))
101 carryOverBlocks.put(carryOverBlock.getAccrualCategory(), carryOverBlock);
102
103 List<LeaveBlock> leaveBlocks = getLeaveBlockService().getLeaveBlocksSinceCarryOver(principalId, carryOverBlocks, (new LocalDateTime(endDate)).toDateTime(), true);
104 List<LeaveBlock> acLeaveBlocks = new ArrayList<LeaveBlock>();
105 for(LeaveBlock lb : leaveBlocks) {
106 if(StringUtils.equals(lb.getAccrualCategory(), accrualCategory)) {
107 acLeaveBlocks.add(lb);
108 }
109 }
110 // get all leave blocks from the requested date to the usageEndDate
111 List<LeaveBlock> futureLeaveBlocks = getLeaveBlockService().getLeaveBlocksWithAccrualCategory(principalId, endDate, usageEndDate, accrualCategory);
112 EmployeeOverride maxUsageOverride = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverride(principalId, lp.getLeavePlan(), accrualCategory, "MU", new java.sql.Date(usageEndDate.getTime()));
113
114 //get max balances
115 AccrualCategoryRule acRule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRuleForDate(ac, TKUtils.getCurrentDate(), pha.getServiceDate());
116 //accrual category rule id set on a leave summary row will be useful in generating a relevant balance transfer
117 //document from the leave calendar display. Could put this id in the request for balance transfer document.
118 lsr.setAccrualCategoryRuleId(acRule == null ? null : acRule.getLmAccrualCategoryRuleId());
119 if(acRule != null &&
120 (acRule.getMaxBalance()!= null
121 || acRule.getMaxUsage() != null)) {
122 if (acRule.getMaxUsage() != null) {
123 lsr.setUsageLimit(new BigDecimal(acRule.getMaxUsage()).setScale(2));
124 } else {
125 lsr.setUsageLimit(null);
126 }
127 } else {
128 lsr.setUsageLimit(null);
129 }
130
131 if(maxUsageOverride !=null)
132 lsr.setUsageLimit(new BigDecimal(maxUsageOverride.getOverrideValue()));
133
134 //Fetching leaveblocks for accCat with type CarryOver -- This is logic according to the CO blocks creatLed from scheduler job.
135 BigDecimal carryOver = BigDecimal.ZERO.setScale(2);
136 lsr.setCarryOver(carryOver);
137
138 assignApprovedValuesToRow(lsr, ac.getAccrualCategory(), acLeaveBlocks, lp, startDate, endDate);
139
140 //merge key sets
141 if (carryOverBlocks.containsKey(lsr.getAccrualCategory())) {
142 carryOver = carryOverBlocks.get(lsr.getAccrualCategory()).getLeaveAmount();
143 }
144 Set<String> keyset = new HashSet<String>();
145 keyset.addAll(lsr.getPriorYearsUsage().keySet());
146 keyset.addAll(lsr.getPriorYearsTotalAccrued().keySet());
147 for (String key : keyset) {
148 BigDecimal value = lsr.getPriorYearsTotalAccrued().get(key);
149 if (value == null) {
150 value = BigDecimal.ZERO;
151 }
152 carryOver = carryOver.add(value);
153 BigDecimal use = lsr.getPriorYearsUsage().containsKey(key) ? lsr.getPriorYearsUsage().get(key) : BigDecimal.ZERO;
154 carryOver = carryOver.add(use);
155 if (acRule != null && acRule.getMaxCarryOver() != null && acRule.getMaxCarryOver() < carryOver.longValue()) {
156 carryOver = new BigDecimal(acRule.getMaxCarryOver());
157 }
158 }
159
160 lsr.setCarryOver(carryOver);
161 //handle future leave blocks
162 assignPendingValuesToRow(lsr, ac.getAccrualCategory(), futureLeaveBlocks);
163 //compute Leave Balance
164 leaveBalance = lsr.getAccruedBalance().subtract(lsr.getPendingLeaveRequests());
165 if(lsr.getUsageLimit()!=null) { //should not set leave balance to usage limit simply because it's not null.
166 BigDecimal availableUsage = lsr.getUsageLimit().subtract(lsr.getYtdApprovedUsage().add(lsr.getPendingLeaveRequests()));
167 if(leaveBalance.compareTo( availableUsage ) > 0)
168 lsr.setLeaveBalance(availableUsage);
169 else
170 lsr.setLeaveBalance(leaveBalance);
171 } else { //no usage limit
172 lsr.setLeaveBalance(leaveBalance);
173 }
174 }
175 leaveBalance = lsr.getLeaveBalance();
176 return leaveBalance;
177 }
178
179 protected LeaveSummary getLeaveSummary(String principalId,
180 java.sql.Date startDate,
181 java.sql.Date endDate,
182 String accrualCategory,
183 boolean includeFuture) {
184 LeaveSummary ls = new LeaveSummary();
185 List<LeaveSummaryRow> rows = new ArrayList<LeaveSummaryRow>();
186
187 if(StringUtils.isEmpty(principalId) || startDate == null || endDate == null) {
188 return ls;
189 }
190
191 Set<String> leavePlans = getLeavePlans(principalId, startDate, endDate);
192 PrincipalHRAttributes pha = getPrincipalHrAttributes(principalId, startDate, endDate);
193 if (CollectionUtils.isNotEmpty(leavePlans)) {
194 for(String aLpString : leavePlans) {
195 LeavePlan lp = TkServiceLocator.getLeavePlanService().getLeavePlan(aLpString, startDate);
196 if(lp == null) {
197 continue;
198 }
199 DateFormat formatter = new SimpleDateFormat("MMMM d");
200 DateFormat formatter2 = new SimpleDateFormat("MMMM d yyyy");
201 DateTime entryEndDate = new LocalDateTime(endDate).toDateTime();
202 if (entryEndDate.getHourOfDay() == 0) {
203 entryEndDate = entryEndDate.minusDays(1);
204 }
205 String aString = formatter.format(startDate) + " - " + formatter2.format(entryEndDate.toDate());
206 ls.setPendingDatesString(aString);
207
208 LeaveCalendarDocumentHeader approvedLcdh = TkServiceLocator.getLeaveCalendarDocumentHeaderService().getMaxEndDateApprovedLeaveCalendar(principalId);
209 if(approvedLcdh != null) {
210 Date endApprovedDate = new java.sql.Date(approvedLcdh.getEndDate().getTime());
211 LocalDateTime aLocalTime = new DateTime(approvedLcdh.getEndDate()).toLocalDateTime();
212 DateTime endApprovedTime = aLocalTime.toDateTime(TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback());
213 if(endApprovedTime.getHourOfDay() == 0) {
214 endApprovedDate = TKUtils.addDates(endApprovedDate, -1);
215 }
216 String datesString = formatter.format(approvedLcdh.getBeginDate()) + " - " + formatter2.format(endApprovedDate);
217 ls.setYtdDatesString(datesString);
218 }
219
220 //until we have something that creates carry over, we need to grab everything.
221 // Calculating leave bLocks from Calendar Year start instead of Service Date
222 Map<String, LeaveBlock> carryOverBlocks = getLeaveBlockService().getLastCarryOverBlocks(principalId, startDate);
223
224 boolean filterByAccrualCategory = false;
225 if (StringUtils.isNotEmpty(accrualCategory)) {
226 filterByAccrualCategory = true;
227 //remove unwanted carry over blocks from map
228 LeaveBlock carryOverBlock = carryOverBlocks.get(accrualCategory);
229 carryOverBlocks = new HashMap<String, LeaveBlock>(1);
230 if(ObjectUtils.isNotNull(carryOverBlock))
231 carryOverBlocks.put(carryOverBlock.getAccrualCategory(), carryOverBlock);
232 }
233 List<LeaveBlock> leaveBlocks = getLeaveBlockService().getLeaveBlocksSinceCarryOver(principalId, carryOverBlocks, (new LocalDateTime(endDate)).toDateTime(), filterByAccrualCategory);
234
235 List<LeaveBlock> futureLeaveBlocks = new ArrayList<LeaveBlock>();
236 if (includeFuture) {
237 if (!filterByAccrualCategory) {
238 futureLeaveBlocks = getLeaveBlockService().getLeaveBlocks(principalId, endDate, (new LocalDateTime(endDate)).toDateTime().plusMonths(Integer.parseInt(lp.getPlanningMonths())).toDate());
239 } else {
240 futureLeaveBlocks = getLeaveBlockService().getLeaveBlocksWithAccrualCategory(principalId, endDate, (new LocalDateTime(endDate)).toDateTime().plusMonths(Integer.parseInt(lp.getPlanningMonths())).toDate(), accrualCategory);
241 }
242 }
243 Map<String, List<LeaveBlock>> leaveBlockMap = mapLeaveBlocksByAccrualCategory(leaveBlocks);
244 Map<String, List<LeaveBlock>> futureLeaveBlockMap = mapLeaveBlocksByAccrualCategory(futureLeaveBlocks);
245 List<AccrualCategory> acList = TkServiceLocator.getAccrualCategoryService().getActiveAccrualCategoriesForLeavePlan(lp.getLeavePlan(), endDate);
246 if(CollectionUtils.isNotEmpty(acList)) {
247 for(AccrualCategory ac : acList) {
248 if(ac.getShowOnGrid().equals("Y")) {
249
250 LeaveSummaryRow lsr = new LeaveSummaryRow();
251 lsr.setAccrualCategory(ac.getAccrualCategory());
252 lsr.setAccrualCategoryId(ac.getLmAccrualCategoryId());
253 //get max balances
254 AccrualCategoryRule acRule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRuleForDate(ac, endDate, pha.getServiceDate());
255 //accrual category rule id set on a leave summary row will be useful in generating a relevant balance transfer
256 //document from the leave calendar display. Could put this id in the request for balance transfer document.
257 EmployeeOverride maxUsageOverride = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverride(principalId, lp.getLeavePlan(), ac.getAccrualCategory(), "MU", endDate);
258
259 lsr.setAccrualCategoryRuleId(acRule == null ? null : acRule.getLmAccrualCategoryRuleId());
260 if(acRule != null &&
261 (acRule.getMaxBalance()!= null
262 || acRule.getMaxUsage() != null)) {
263 if (acRule.getMaxUsage() != null) {
264 lsr.setUsageLimit(new BigDecimal(acRule.getMaxUsage()).setScale(2));
265 } else {
266 lsr.setUsageLimit(null);
267 }
268
269 } else {
270 lsr.setUsageLimit(null);
271 }
272
273 if(maxUsageOverride !=null)
274 lsr.setUsageLimit(new BigDecimal(maxUsageOverride.getOverrideValue()));
275
276 //Fetching leaveblocks for accCat with type CarryOver -- This is logic according to the CO blocks created from scheduler job.
277 BigDecimal carryOver = BigDecimal.ZERO.setScale(2);
278 lsr.setCarryOver(carryOver);
279
280 //handle up to current leave blocks
281 //CalendarEntry.getEndPeriodDate passed to fetch leave block amounts on last day of Calendar period
282 assignApprovedValuesToRow(lsr, ac.getAccrualCategory(), leaveBlockMap.get(ac.getAccrualCategory()), lp, startDate, endDate);
283
284 //how about the leave blocks on the calendar entry being currently handled??
285 /* if(carryOverBlocks.containsKey(lsr.getAccrualCategory())) {
286 LeaveBlock carryOverBlock = carryOverBlocks.get(lsr.getAccrualCategory());
287 DateTime carryOverBlockLPStart = TkServiceLocator.getLeavePlanService().getFirstDayOfLeavePlan(lp.getLeavePlan(), carryOverBlock.getLeaveDate());
288 DateTime currentLPStart = TkServiceLocator.getLeavePlanService().getFirstDayOfLeavePlan(lp.getLeavePlan(), TKUtils.getCurrentDate());
289 if(carryOverBlockLPStart.equals(currentLPStart))
290 carryOver = carryOverBlock.getLeaveAmount();
291 }*/
292 //figure out past carry over values!!!
293 //We now have list of past years accrual and use (with ordered keys!!!)
294
295 //merge key sets
296 if (carryOverBlocks.containsKey(lsr.getAccrualCategory())) {
297 carryOver = carryOverBlocks.get(lsr.getAccrualCategory()).getLeaveAmount();
298 carryOver = carryOver.setScale(2);
299 }
300 Set<String> keyset = new HashSet<String>();
301 keyset.addAll(lsr.getPriorYearsUsage().keySet());
302 keyset.addAll(lsr.getPriorYearsTotalAccrued().keySet());
303 for (String key : keyset) {
304 BigDecimal value = lsr.getPriorYearsTotalAccrued().get(key);
305 if (value == null) {
306 value = BigDecimal.ZERO;
307 }
308 carryOver = carryOver.add(value);
309 BigDecimal use = lsr.getPriorYearsUsage().containsKey(key) ? lsr.getPriorYearsUsage().get(key) : BigDecimal.ZERO;
310 carryOver = carryOver.add(use);
311 if (acRule != null && acRule.getMaxCarryOver() != null && acRule.getMaxCarryOver() < carryOver.longValue()) {
312 carryOver = new BigDecimal(acRule.getMaxCarryOver());
313 }
314 }
315
316 lsr.setCarryOver(carryOver);
317 if (acRule != null && acRule.getMaxCarryOver() != null) {
318 lsr.setMaxCarryOver(new BigDecimal(acRule.getMaxCarryOver()));
319 }
320
321 //handle future leave blocks
322 assignPendingValuesToRow(lsr, ac.getAccrualCategory(), futureLeaveBlockMap.get(ac.getAccrualCategory()));
323
324 //compute Leave Balance
325 BigDecimal leaveBalance = lsr.getAccruedBalance().subtract(lsr.getPendingLeaveRequests());
326 //if leave balance is set
327 //Employee overrides have already been taken into consideration and the appropriate values
328 //for usage have been set by this point.
329 // if (acRule != null && StringUtils.equals(acRule.getMaxBalFlag(), "Y")) {
330 //there exists an accrual category rule with max balance limit imposed.
331 //max bal flag = 'Y' has no precedence here with max-bal / balance transfers implemented.
332 //unless institutions are not required to define a max balance limit for action_at_max_bal = LOSE.
333 //Possibly preferable to procure forfeiture blocks through balance transfer
334 if(lsr.getUsageLimit()!=null) { //should not set leave balance to usage limit simply because it's not null.
335 BigDecimal availableUsage = lsr.getUsageLimit().subtract(lsr.getYtdApprovedUsage().add(lsr.getPendingLeaveRequests()));
336 if(leaveBalance.compareTo( availableUsage ) > 0)
337 lsr.setLeaveBalance(availableUsage);
338 else
339 lsr.setLeaveBalance(leaveBalance);
340 } else { //no usage limit
341 lsr.setLeaveBalance(leaveBalance);
342 }
343
344 rows.add(lsr);
345 }
346 }
347 // let's check for 'empty' accrual categories
348 if (leaveBlockMap.containsKey(null)
349 || futureLeaveBlockMap.containsKey(null)) {
350 LeaveSummaryRow otherLeaveSummary = new LeaveSummaryRow();
351 //otherLeaveSummary.setAccrualCategory("Other");
352
353 assignApprovedValuesToRow(otherLeaveSummary, null, leaveBlockMap.get(null), lp, startDate, endDate);
354 BigDecimal carryOver = BigDecimal.ZERO.setScale(2);
355 for (Map.Entry<String, BigDecimal> entry : otherLeaveSummary.getPriorYearsTotalAccrued().entrySet()) {
356 carryOver = carryOver.add(entry.getValue());
357 BigDecimal use = otherLeaveSummary.getPriorYearsUsage().containsKey(entry.getKey()) ? otherLeaveSummary.getPriorYearsUsage().get(entry.getKey()) : BigDecimal.ZERO;
358 carryOver = carryOver.add(use);
359 }
360 otherLeaveSummary.setCarryOver(carryOver);
361 assignPendingValuesToRow(otherLeaveSummary, null, futureLeaveBlockMap.get(null));
362 otherLeaveSummary.setAccrualCategory("Other");
363
364 //compute Leave Balance
365 // blank the avail
366 otherLeaveSummary.setUsageLimit(null);
367 otherLeaveSummary.setLeaveBalance(null);
368
369 rows.add(otherLeaveSummary);
370 }
371 }
372 }
373 }
374 ls.setLeaveSummaryRows(rows);
375 return ls;
376 }
377
378 private PrincipalHRAttributes getPrincipalHrAttributes(String principalId, Date startDate, Date endDate) {
379 PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, startDate);
380 if(pha == null) { // principal hr attributes does not exist at the beginning of this calendar entry
381 pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, endDate);
382 }
383 return pha;
384 }
385
386 private Set<String> getLeavePlans(String principalId, Date startDate, Date endDate) {
387 Set<String> lpStrings = new HashSet<String>(); //
388
389 PrincipalHRAttributes pha = getPrincipalHrAttributes(principalId, startDate, endDate);
390 // get list of principalHrAttributes that become active during this pay period
391 List<PrincipalHRAttributes> phaList = TkServiceLocator.getPrincipalHRAttributeService()
392 .getActivePrincipalHrAttributesForRange(principalId, startDate, endDate);
393 if(pha != null) {
394 lpStrings.add(pha.getLeavePlan());
395 }
396 if(CollectionUtils.isNotEmpty(phaList)) {
397 for(PrincipalHRAttributes aPha : phaList) {
398 lpStrings.add(aPha.getLeavePlan());
399 }
400 }
401
402 return lpStrings;
403 }
404
405
406 private Map<String, List<LeaveBlock>> mapLeaveBlocksByAccrualCategory(List<LeaveBlock> leaveBlocks) {
407 Map<String, List<LeaveBlock>> map = new HashMap<String, List<LeaveBlock>>();
408 for (LeaveBlock lb : leaveBlocks) {
409 if (map.containsKey(lb.getAccrualCategory())) {
410 map.get(lb.getAccrualCategory()).add(lb);
411 } else {
412 List<LeaveBlock> splitLeaveBlocks = new ArrayList<LeaveBlock>();
413 splitLeaveBlocks.add(lb);
414 map.put(lb.getAccrualCategory(), splitLeaveBlocks);
415 }
416 }
417 return map;
418 }
419
420 /**
421 * Use with button display for balance transfers available On-Demand.
422 * @param lsr
423 * @param accrualCategoryRule
424 * @param principalId
425 */
426 private void markTransferable(LeaveSummaryRow lsr, AccrualCategoryRule accrualCategoryRule, String principalId) {
427 //return type must be changed to boolean, or an associated field element must be created for decision
428 //purposes.
429 //an accrual category's balance is transferable if the accrued balance is
430 //greater than the maximum balance allowed for the accrual category. action_at_max_balance must be TRANSFER
431 boolean transferable = false;
432 if(ObjectUtils.isNotNull(accrualCategoryRule)) {
433 if(ObjectUtils.isNotNull(accrualCategoryRule.getMaxBalance())) {
434 BigDecimal maxBalance = accrualCategoryRule.getMaxBalance();
435 BigDecimal fte = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, TKUtils.getCurrentDate());
436 BigDecimal adjustedMaxBalance = maxBalance.multiply(fte);
437 List<EmployeeOverride> overrides = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverrides(principalId, TKUtils.getCurrentDate());
438 for(EmployeeOverride override : overrides) {
439 if(StringUtils.equals(override.getOverrideType(),TkConstants.EMPLOYEE_OVERRIDE_TYPE.get("MB"))
440 && override.isActive()) {
441 adjustedMaxBalance = new BigDecimal(override.getOverrideValue());
442 break;
443 }
444 }
445 if(adjustedMaxBalance.compareTo(lsr.getAccruedBalance()) < 0) {
446 if(StringUtils.equals(accrualCategoryRule.getActionAtMaxBalance(), LMConstants.ACTION_AT_MAX_BAL.TRANSFER) &&
447 StringUtils.equals(accrualCategoryRule.getMaxBalanceActionFrequency(),LMConstants.MAX_BAL_ACTION_FREQ.ON_DEMAND))
448 transferable = true;
449 }
450 }
451 }
452 lsr.setTransferable(transferable);
453 }
454
455 /**
456 * Use with button display for balance transfer payouts available On-Demand.
457 * @param lsr
458 * @param accrualCategoryRule
459 * @param principalId
460 */
461 private void markPayoutable(LeaveSummaryRow lsr, AccrualCategoryRule accrualCategoryRule, String principalId) {
462 //return type must be changed to boolean, or an associated field element must be created for decision
463 //purposes.
464 //an accrual category's balance is transferable if max_bal_action_frequency is ON-DEMAND
465 //and action_at_max_balance is PAYOUT
466 boolean payoutable = false;
467 if(ObjectUtils.isNotNull(accrualCategoryRule)) {
468 if(ObjectUtils.isNotNull(accrualCategoryRule.getMaxBalance())) {
469 BigDecimal maxBalance = accrualCategoryRule.getMaxBalance();
470 BigDecimal fte = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, TKUtils.getCurrentDate());
471 BigDecimal adjustedMaxBalance = maxBalance.multiply(fte);
472 List<EmployeeOverride> overrides = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverrides(principalId, TKUtils.getCurrentDate());
473 for(EmployeeOverride override : overrides) {
474 if(StringUtils.equals(override.getOverrideType(),TkConstants.EMPLOYEE_OVERRIDE_TYPE.get("MB"))
475 && override.isActive()) {
476 adjustedMaxBalance = new BigDecimal(override.getOverrideValue());
477 break;
478 }
479 }
480 if(adjustedMaxBalance.compareTo(lsr.getAccruedBalance()) < 0) {
481 if(StringUtils.equals(accrualCategoryRule.getActionAtMaxBalance(), LMConstants.ACTION_AT_MAX_BAL.PAYOUT) &&
482 StringUtils.equals(accrualCategoryRule.getMaxBalanceActionFrequency(),LMConstants.MAX_BAL_ACTION_FREQ.ON_DEMAND))
483 payoutable = true;
484 }
485 }
486 }
487 lsr.setPayoutable(payoutable);
488 }
489
490 private void assignApprovedValuesToRow(LeaveSummaryRow lsr, String accrualCategory, List<LeaveBlock> approvedLeaveBlocks, LeavePlan lp, Date ytdEarnedEffectiveDate, Date effectiveDate) {
491
492 SortedMap<String, BigDecimal> yearlyAccrued = new TreeMap<String, BigDecimal>();
493 SortedMap<String, BigDecimal> yearlyUsage = new TreeMap<String, BigDecimal>();
494 BigDecimal accrualedBalance = BigDecimal.ZERO.setScale(2);
495 BigDecimal approvedUsage = BigDecimal.ZERO.setScale(2);
496 BigDecimal fmlaUsage = BigDecimal.ZERO.setScale(2);
497
498 Date cutOffDateToCheck = ytdEarnedEffectiveDate != null ? ytdEarnedEffectiveDate : effectiveDate;
499 DateTime cutOffDate = TkServiceLocator.getLeavePlanService().getFirstDayOfLeavePlan(lp.getLeavePlan(), cutOffDateToCheck).minus(1);
500
501 Timestamp priorYearCutOff = new Timestamp(cutOffDate.getMillis());
502
503 if (CollectionUtils.isNotEmpty(approvedLeaveBlocks)) {
504 // create it here so we don't need to get instance every loop iteration
505 for(LeaveBlock aLeaveBlock : approvedLeaveBlocks) {
506 // check if leave date is before the next calendar start.
507 if(!aLeaveBlock.getLeaveBlockType().equals(LMConstants.LEAVE_BLOCK_TYPE.CARRY_OVER)
508 && aLeaveBlock.getLeaveDate().getTime() < effectiveDate.getTime()) {
509 if((StringUtils.isBlank(accrualCategory) && StringUtils.isBlank(aLeaveBlock.getAccrualCategory()))
510 || (StringUtils.isNotBlank(aLeaveBlock.getAccrualCategory())
511 && StringUtils.equals(aLeaveBlock.getAccrualCategory(), accrualCategory))) {
512 if(aLeaveBlock.getLeaveAmount().compareTo(BigDecimal.ZERO) >= 0
513 && !aLeaveBlock.getLeaveBlockType().equals(LMConstants.LEAVE_BLOCK_TYPE.LEAVE_CALENDAR)) {
514 if(!(StringUtils.equals(LMConstants.REQUEST_STATUS.DISAPPROVED, aLeaveBlock.getRequestStatus()) ||
515 StringUtils.equals(LMConstants.REQUEST_STATUS.DEFERRED, aLeaveBlock.getRequestStatus()))) {
516 if (aLeaveBlock.getLeaveDate().getTime() <= priorYearCutOff.getTime()) {
517 String yearKey = getYearKey(aLeaveBlock.getLeaveDate(), lp);
518 BigDecimal co = yearlyAccrued.get(yearKey);
519 if (co == null) {
520 co = BigDecimal.ZERO.setScale(2);
521 }
522 co = co.add(aLeaveBlock.getLeaveAmount());
523 yearlyAccrued.put(yearKey, co);
524 } else if(aLeaveBlock.getLeaveDate().getTime() < ytdEarnedEffectiveDate.getTime()) {
525 accrualedBalance = accrualedBalance.add(aLeaveBlock.getLeaveAmount());
526 }
527 }
528 } else {
529 BigDecimal currentLeaveAmount = aLeaveBlock.getLeaveAmount().compareTo(BigDecimal.ZERO) > 0 ? aLeaveBlock.getLeaveAmount().negate() : aLeaveBlock.getLeaveAmount();
530 //we only want this for the current calendar!!!
531 if(!(StringUtils.equals(LMConstants.REQUEST_STATUS.DISAPPROVED, aLeaveBlock.getRequestStatus()) ||
532 StringUtils.equals(LMConstants.REQUEST_STATUS.DEFERRED, aLeaveBlock.getRequestStatus()))) {
533 if (aLeaveBlock.getLeaveDate().getTime() > priorYearCutOff.getTime()) {
534 EarnCode ec = TkServiceLocator.getEarnCodeService().getEarnCode(aLeaveBlock.getEarnCode(), aLeaveBlock.getLeaveDate());
535 if (ec != null && StringUtils.equals(ec.getAccrualBalanceAction(), LMConstants.ACCRUAL_BALANCE_ACTION.USAGE)){
536 approvedUsage = approvedUsage.add(currentLeaveAmount);
537 }
538 if(ec != null && ec.getFmla().equals("Y")) {
539 fmlaUsage = fmlaUsage.add(aLeaveBlock.getLeaveAmount());
540 }
541 } else {
542 //these usages are for previous years, to help figure out correct carry over values
543 String yearKey = getYearKey(aLeaveBlock.getLeaveDate(), lp);
544 BigDecimal use = yearlyUsage.get(yearKey);
545 if (use == null) {
546 use = BigDecimal.ZERO.setScale(2);
547 }
548 use = use.add(currentLeaveAmount);
549 yearlyUsage.put(yearKey, use);
550 }
551 }
552 }
553
554 //}
555 }
556 } else {
557 //we can actually use the carry over block!!
558
559 }
560 }
561 }
562
563 lsr.setPriorYearsTotalAccrued(yearlyAccrued);
564 lsr.setPriorYearsUsage(yearlyUsage);
565 lsr.setYtdAccruedBalance(accrualedBalance);
566 lsr.setYtdApprovedUsage(approvedUsage.negate());
567 lsr.setFmlaUsage(fmlaUsage.negate());
568
569 //lsr.setLeaveBalance(lsr.getYtdAccruedBalance().add(approvedUsage));
570 }
571
572 private String getYearKey(Date leaveDate, LeavePlan lp){
573 Calendar cal = Calendar.getInstance();
574 Calendar leavePlanCal = Calendar.getInstance();
575 cal.setTime(leaveDate);
576 String yearKey = Integer.toString(cal.get(Calendar.YEAR));
577 leavePlanCal.set(Calendar.MONTH, Integer.parseInt(lp.getCalendarYearStartMonth()) - 1);
578 leavePlanCal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(lp.getCalendarYearStartDayOfMonth()));
579 leavePlanCal.set(Calendar.YEAR, cal.get(Calendar.YEAR));
580 leavePlanCal.set(Calendar.HOUR_OF_DAY, 0);
581 leavePlanCal.set(Calendar.MINUTE, 0);
582 leavePlanCal.set(Calendar.SECOND, 0);
583 leavePlanCal.set(Calendar.MILLISECOND, 0);
584
585 if (cal.getTime().before(leavePlanCal.getTime())) {
586 yearKey = Integer.toString(cal.get(Calendar.YEAR) - 1);
587 }
588 return yearKey;
589 }
590
591 private void assignPendingValuesToRow(LeaveSummaryRow lsr, String accrualCategory, List<LeaveBlock> pendingLeaveBlocks ) {
592 BigDecimal pendingAccrual= BigDecimal.ZERO.setScale(2);
593 BigDecimal pendingRequests = BigDecimal.ZERO.setScale(2);
594 if (CollectionUtils.isNotEmpty(pendingLeaveBlocks)) {
595 for(LeaveBlock aLeaveBlock : pendingLeaveBlocks) {
596 if(!aLeaveBlock.getLeaveBlockType().equals(LMConstants.LEAVE_BLOCK_TYPE.CARRY_OVER)) {
597 if((StringUtils.isBlank(accrualCategory) && StringUtils.isBlank(aLeaveBlock.getAccrualCategory()))
598 || (StringUtils.isNotBlank(aLeaveBlock.getAccrualCategory())
599 && StringUtils.equals(aLeaveBlock.getAccrualCategory(), accrualCategory))) {
600 if(aLeaveBlock.getLeaveAmount().compareTo(BigDecimal.ZERO) >= 0) {
601 pendingAccrual = pendingAccrual.add(aLeaveBlock.getLeaveAmount());
602 } else {
603 pendingRequests = pendingRequests.add(aLeaveBlock.getLeaveAmount());
604 }
605 }
606 }
607 }
608 }
609 lsr.setPendingLeaveAccrual(pendingAccrual);
610 lsr.setPendingLeaveRequests(pendingRequests.negate());
611 }
612
613 @Override
614 public List<Date> getLeaveSummaryDates(CalendarEntries calendarEntry) {
615 List<Date> leaveSummaryDates = new ArrayList<Date>();
616
617 DateTime start = calendarEntry.getBeginLocalDateTime().toDateTime();
618 DateTime end = calendarEntry.getEndLocalDateTime().toDateTime();
619 Interval interval = new Interval(start, end);
620
621 for (DateTime day = interval.getStart(); day.isBefore(interval.getEnd()); day = day.plusDays(1)) {
622 leaveSummaryDates.add(day.toLocalDate().toDateTimeAtStartOfDay().toDate());
623 }
624
625 return leaveSummaryDates;
626 }
627
628 protected LeaveBlockService getLeaveBlockService() {
629 if (leaveBlockService == null) {
630 leaveBlockService = TkServiceLocator.getLeaveBlockService();
631 }
632 return leaveBlockService;
633 }
634
635
636 }
637