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.leavepayout.service; 017 018 import org.apache.commons.lang.StringUtils; 019 import org.apache.commons.lang.time.DateUtils; 020 import org.joda.time.Interval; 021 import org.kuali.hr.lm.LMConstants; 022 import org.kuali.hr.lm.accrual.AccrualCategory; 023 import org.kuali.hr.lm.accrual.AccrualCategoryRule; 024 import org.kuali.hr.lm.leavepayout.LeavePayout; 025 import org.kuali.hr.lm.employeeoverride.EmployeeOverride; 026 import org.kuali.hr.lm.leaveSummary.LeaveSummary; 027 import org.kuali.hr.lm.leaveSummary.LeaveSummaryRow; 028 import org.kuali.hr.lm.leaveblock.LeaveBlock; 029 import org.kuali.hr.lm.leaveblock.LeaveBlockHistory; 030 import org.kuali.hr.lm.leavecalendar.LeaveCalendarDocument; 031 import org.kuali.hr.lm.leavepayout.LeavePayout; 032 import org.kuali.hr.lm.leavepayout.dao.LeavePayoutDao; 033 034 import org.kuali.hr.lm.leaveplan.LeavePlan; 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.TKContext; 040 import org.kuali.hr.time.util.TKUtils; 041 import org.kuali.hr.time.util.TkConstants; 042 import org.kuali.rice.kew.api.exception.WorkflowException; 043 import org.kuali.rice.kim.api.identity.principal.EntityNamePrincipalName; 044 import org.kuali.rice.kim.api.services.KimApiServiceLocator; 045 import org.kuali.rice.krad.maintenance.MaintenanceDocument; 046 import org.kuali.rice.krad.service.KRADServiceLocator; 047 import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 048 import org.kuali.rice.krad.util.KRADConstants; 049 import org.kuali.rice.krad.util.ObjectUtils; 050 051 import java.math.BigDecimal; 052 import java.sql.Date; 053 import java.util.ArrayList; 054 import java.util.Calendar; 055 import java.util.HashMap; 056 import java.util.List; 057 import java.util.Map; 058 059 public class LeavePayoutServiceImpl implements LeavePayoutService { 060 061 private LeavePayoutDao leavePayoutDao; 062 063 @Override 064 public List<LeavePayout> getAllLeavePayoutsForPrincipalId( 065 String principalId) { 066 return leavePayoutDao.getAllLeavePayoutsForPrincipalId(principalId); 067 } 068 069 @Override 070 public List<LeavePayout> getAllLeavePayoutsForPrincipalIdAsOfDate( 071 String principalId, Date effectiveDate) { 072 return leavePayoutDao.getAllLeavePayoutsForPrincipalIdAsOfDate(principalId,effectiveDate); 073 } 074 075 @Override 076 public List<LeavePayout> getAllLeavePayoutsByEffectiveDate( 077 Date effectiveDate) { 078 return leavePayoutDao.getAllLeavePayoutsByEffectiveDate(effectiveDate); 079 } 080 081 @Override 082 public LeavePayout getLeavePayoutById(String lmLeavePayoutId) { 083 return leavePayoutDao.getLeavePayoutById(lmLeavePayoutId); 084 } 085 086 public LeavePayoutDao getLeavePayoutDao() { 087 return leavePayoutDao; 088 } 089 090 public void setLeavePayoutDao(LeavePayoutDao leavePayoutDao) { 091 this.leavePayoutDao = leavePayoutDao; 092 } 093 094 @Override 095 public LeavePayout initializePayout(String principalId, 096 String accrualCategoryRule, BigDecimal accruedBalance, 097 Date effectiveDate) { 098 //Initially, principals may be allowed to edit the transfer amount when prompted to submit this balance transfer, however, 099 //a base transfer amount together with a forfeited amount is calculated to bring the balance back to its limit in accordance 100 //with transfer limits. 101 LeavePayout leavePayout = null; 102 AccrualCategoryRule accrualRule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRule(accrualCategoryRule); 103 104 if(ObjectUtils.isNotNull(accrualRule) && ObjectUtils.isNotNull(accruedBalance)) { 105 leavePayout = new LeavePayout(); 106 //Leave summary is not a requirement, per se, but the information it contains is. 107 //The only thing that is obtained from leave summary is the accrued balance of the leave summary row matching the 108 //passed accrualCategoryRules accrual category. 109 //These two objects are essential to balance transfers triggered when the employee submits their leave calendar for approval. 110 //Neither of these objects should be null, otherwise this method could not have been called. 111 AccrualCategory fromAccrualCategory = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(accrualRule.getLmAccrualCategoryId()); 112 BigDecimal fullTimeEngagement = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, effectiveDate); 113 114 // AccrualRule.maxBalance == null -> no balance limit. No balance limit -> no accrual triggered transfer / payout / lose. 115 // execution point should not be here if max balance on accrualRule is null, unless there exists an employee override. 116 BigDecimal maxBalance = accrualRule.getMaxBalance(); 117 BigDecimal adjustedMaxBalance = maxBalance.multiply(fullTimeEngagement).setScale(2); 118 119 BigDecimal maxPayoutAmount = null; 120 BigDecimal adjustedMaxPayoutAmount = null; 121 if(ObjectUtils.isNotNull(accrualRule.getMaxPayoutAmount())) { 122 maxPayoutAmount = new BigDecimal(accrualRule.getMaxPayoutAmount()); 123 adjustedMaxPayoutAmount = maxPayoutAmount.multiply(fullTimeEngagement).setScale(2); 124 } 125 else { 126 // no limit on transfer amount 127 maxPayoutAmount = new BigDecimal(Long.MAX_VALUE); 128 adjustedMaxPayoutAmount = maxPayoutAmount; 129 } 130 131 BigDecimal maxCarryOver = null; 132 BigDecimal adjustedMaxCarryOver = null; 133 if(ObjectUtils.isNotNull(accrualRule.getMaxCarryOver())) { 134 maxCarryOver = new BigDecimal(accrualRule.getMaxCarryOver()); 135 adjustedMaxCarryOver = maxCarryOver.multiply(fullTimeEngagement).setScale(2); 136 } 137 else { 138 //no limit to carry over. 139 maxCarryOver = new BigDecimal(Long.MAX_VALUE); 140 adjustedMaxCarryOver = maxCarryOver; 141 } 142 143 List<EmployeeOverride> overrides = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverrides(principalId, effectiveDate); 144 for(EmployeeOverride override : overrides) { 145 if(StringUtils.equals(override.getAccrualCategory(),fromAccrualCategory.getAccrualCategory())) { 146 if(StringUtils.equals(override.getOverrideType(),"MB")) 147 adjustedMaxBalance = new BigDecimal(override.getOverrideValue()); 148 //override values are not pro-rated for FTE. 149 if(StringUtils.equals(override.getOverrideType(),"MPA")) 150 adjustedMaxPayoutAmount = new BigDecimal(override.getOverrideValue()); 151 if(StringUtils.equals(override.getOverrideType(),"MAC")) 152 adjustedMaxCarryOver = new BigDecimal(override.getOverrideValue()); 153 } 154 } 155 156 157 BigDecimal transferAmount = accruedBalance.subtract(adjustedMaxBalance); 158 if(StringUtils.equals(accrualRule.getActionAtMaxBalance(),LMConstants.ACTION_AT_MAX_BAL.LOSE)) { 159 //TODO: REMOVE CONDITIONAL 160 //Move all time in excess of employee's fte adjusted max balance to forfeiture. 161 leavePayout.setForfeitedAmount(transferAmount); 162 //There is no transfer to another accrual category. 163 leavePayout.setPayoutAmount(BigDecimal.ZERO); 164 // to accrual category is a required field on maintenance document. Set as from accrual category 165 // to remove validation errors when routing, approving, etc. 166 leavePayout.setEarnCode(fromAccrualCategory.getEarnCode()); 167 } 168 else { 169 // ACTION_AT_MAX_BAL = PAYOUT 170 leavePayout.setEarnCode(accrualRule.getMaxPayoutEarnCode()); 171 if(transferAmount.compareTo(adjustedMaxPayoutAmount) > 0) { 172 //there's forfeiture. 173 //bring transfer amount down to the adjusted maximum transfer amount, and place excess in forfeiture. 174 //accruedBalance - adjustedMaxPayoutAmount - adjustedMaxBalance = forfeiture. 175 //transferAmount = accruedBalance - adjustedMaxBalance; forfeiture = transferAmount - adjustedMaxPayoutAmount. 176 BigDecimal forfeiture = transferAmount.subtract(adjustedMaxPayoutAmount).setScale(2); 177 forfeiture = forfeiture.stripTrailingZeros(); 178 leavePayout.setForfeitedAmount(forfeiture); 179 leavePayout.setPayoutAmount(adjustedMaxPayoutAmount); 180 } 181 else { 182 leavePayout.setPayoutAmount(transferAmount); 183 leavePayout.setForfeitedAmount(BigDecimal.ZERO); 184 } 185 } 186 187 // Max Carry Over logic for Year End transfers. 188 if(StringUtils.equals(accrualRule.getMaxBalanceActionFrequency(),LMConstants.MAX_BAL_ACTION_FREQ.YEAR_END)) { 189 if(ObjectUtils.isNotNull(maxCarryOver)) { 190 if(ObjectUtils.isNull(adjustedMaxCarryOver)) 191 adjustedMaxCarryOver = maxCarryOver.multiply(fullTimeEngagement).setScale(2); 192 //otherwise, adjustedMaxCarryOver has an employee override value, which trumps accrual rule defined MAC. 193 //At this point, transfer amount and forfeiture have been set so that the new accrued balance will be the 194 //adjusted max balance, so this amount is used to check against carry over. 195 if(adjustedMaxBalance.compareTo(adjustedMaxCarryOver) > 0) { 196 BigDecimal carryOverDiff = adjustedMaxBalance.subtract(adjustedMaxCarryOver); 197 198 if(StringUtils.equals(accrualRule.getActionAtMaxBalance(),LMConstants.ACTION_AT_MAX_BAL.LOSE)){ 199 //add carry over excess to forfeiture. 200 leavePayout.setForfeitedAmount(leavePayout.getForfeitedAmount().add(carryOverDiff)); 201 } 202 else { 203 //maximize the transfer amount. 204 BigDecimal potentialPayoutAmount = leavePayout.getPayoutAmount().add(carryOverDiff); 205 206 //Can this amount be added to the transfer amount?? 207 if(potentialPayoutAmount.compareTo(adjustedMaxPayoutAmount) <= 0) { 208 //yes 209 leavePayout.setPayoutAmount(leavePayout.getPayoutAmount().add(carryOverDiff)); 210 } 211 else { 212 //no 213 BigDecimal carryOverExcess = potentialPayoutAmount.subtract(adjustedMaxPayoutAmount); 214 //move excess to forfeiture 215 leavePayout.setForfeitedAmount(leavePayout.getForfeitedAmount().add(carryOverExcess)); 216 //the remainder (if any) can be added to the transfer amount ( unless action is LOSE ). 217 leavePayout.setPayoutAmount(leavePayout.getPayoutAmount().add(carryOverDiff.subtract(carryOverExcess))); 218 assert(leavePayout.getPayoutAmount().compareTo(adjustedMaxPayoutAmount)==0); 219 } 220 } 221 } 222 //otherwise, given balance will be at or under the max annual carry over. 223 } 224 } 225 226 leavePayout.setEffectiveDate(effectiveDate); 227 leavePayout.setAccrualCategoryRule(accrualCategoryRule); 228 leavePayout.setFromAccrualCategory(fromAccrualCategory.getAccrualCategory()); 229 leavePayout.setPrincipalId(principalId); 230 } 231 return leavePayout; 232 } 233 234 @Override 235 public LeavePayout payout(LeavePayout leavePayout) { 236 if(ObjectUtils.isNull(leavePayout)) 237 throw new RuntimeException("did not supply a valid LeavePayout object."); 238 else { 239 List<LeaveBlock> leaveBlocks = new ArrayList<LeaveBlock>(); 240 BigDecimal transferAmount = leavePayout.getPayoutAmount(); 241 LeaveBlock aLeaveBlock = null; 242 243 if(ObjectUtils.isNotNull(transferAmount)) { 244 if(transferAmount.compareTo(BigDecimal.ZERO) > 0) { 245 246 aLeaveBlock = new LeaveBlock(); 247 //Create a leave block that adds the adjusted transfer amount to the "transfer to" accrual category. 248 aLeaveBlock.setPrincipalId(leavePayout.getPrincipalId()); 249 aLeaveBlock.setLeaveDate(leavePayout.getEffectiveDate()); 250 aLeaveBlock.setEarnCode(leavePayout.getEarnCode()); 251 aLeaveBlock.setAccrualCategory(leavePayout.getEarnCodeObj().getAccrualCategory()); 252 aLeaveBlock.setDescription("Amount payed out"); 253 aLeaveBlock.setLeaveAmount(leavePayout.getPayoutAmount()); 254 aLeaveBlock.setAccrualGenerated(true); 255 aLeaveBlock.setTransactionDocId(leavePayout.getDocumentHeaderId()); 256 aLeaveBlock.setLeaveBlockType(LMConstants.LEAVE_BLOCK_TYPE.LEAVE_PAYOUT); 257 aLeaveBlock.setRequestStatus(LMConstants.REQUEST_STATUS.REQUESTED); 258 aLeaveBlock.setDocumentId(leavePayout.getLeaveCalendarDocumentId()); 259 aLeaveBlock.setBlockId(0L); 260 261 //Want to store the newly created leave block id on this maintainable object 262 //when the status of the maintenance document encapsulating this maintainable changes 263 //the id will be used to fetch and update the leave block statuses. 264 aLeaveBlock = KRADServiceLocator.getBusinessObjectService().save(aLeaveBlock); 265 266 leavePayout.setPayoutLeaveBlockId(aLeaveBlock.getLmLeaveBlockId()); 267 // save history 268 LeaveBlockHistory lbh = new LeaveBlockHistory(aLeaveBlock); 269 lbh.setAction(LMConstants.ACTION.ADD); 270 TkServiceLocator.getLeaveBlockHistoryService().saveLeaveBlockHistory(lbh); 271 leaveBlocks.add(aLeaveBlock); 272 273 //Create leave block that removes the correct transfer amount from the originating accrual category. 274 aLeaveBlock = new LeaveBlock(); 275 aLeaveBlock.setPrincipalId(leavePayout.getPrincipalId()); 276 aLeaveBlock.setLeaveDate(leavePayout.getEffectiveDate()); 277 aLeaveBlock.setEarnCode(leavePayout.getFromAccrualCategoryObj().getEarnCode()); 278 aLeaveBlock.setAccrualCategory(leavePayout.getFromAccrualCategory()); 279 aLeaveBlock.setDescription("Payout amount"); 280 aLeaveBlock.setLeaveAmount(leavePayout.getPayoutAmount().negate()); 281 aLeaveBlock.setAccrualGenerated(true); 282 aLeaveBlock.setTransactionDocId(leavePayout.getDocumentHeaderId()); 283 aLeaveBlock.setLeaveBlockType(LMConstants.LEAVE_BLOCK_TYPE.LEAVE_PAYOUT); 284 aLeaveBlock.setRequestStatus(LMConstants.REQUEST_STATUS.REQUESTED); 285 aLeaveBlock.setDocumentId(leavePayout.getLeaveCalendarDocumentId()); 286 aLeaveBlock.setBlockId(0L); 287 288 //Want to store the newly created leave block id on this maintainable object. 289 //when the status of the maintenance document encapsulating this maintainable changes 290 //the id will be used to fetch and update the leave block statuses. 291 aLeaveBlock = KRADServiceLocator.getBusinessObjectService().save(aLeaveBlock); 292 293 leavePayout.setPayoutFromLeaveBlockId(aLeaveBlock.getLmLeaveBlockId()); 294 // save history 295 lbh = new LeaveBlockHistory(aLeaveBlock); 296 lbh.setAction(LMConstants.ACTION.ADD); 297 TkServiceLocator.getLeaveBlockHistoryService().saveLeaveBlockHistory(lbh); 298 299 leaveBlocks.add(aLeaveBlock); 300 } 301 } 302 303 BigDecimal forfeitedAmount = leavePayout.getForfeitedAmount(); 304 if(ObjectUtils.isNotNull(forfeitedAmount)) { 305 //Any amount forfeited must come out of the originating accrual category in order to bring balance back to max. 306 if(forfeitedAmount.compareTo(BigDecimal.ZERO) > 0) { 307 //for balance transfers with action = lose, transfer amount must be moved to forfeitedAmount 308 aLeaveBlock = new LeaveBlock(); 309 aLeaveBlock.setPrincipalId(leavePayout.getPrincipalId()); 310 aLeaveBlock.setLeaveDate(leavePayout.getEffectiveDate()); 311 aLeaveBlock.setEarnCode(leavePayout.getFromAccrualCategoryObj().getEarnCode()); 312 aLeaveBlock.setAccrualCategory(leavePayout.getFromAccrualCategory()); 313 aLeaveBlock.setDescription("Forfeited payout amount"); 314 aLeaveBlock.setLeaveAmount(forfeitedAmount.negate()); 315 aLeaveBlock.setAccrualGenerated(true); 316 aLeaveBlock.setTransactionDocId(leavePayout.getDocumentHeaderId()); 317 aLeaveBlock.setLeaveBlockType(LMConstants.LEAVE_BLOCK_TYPE.LEAVE_PAYOUT); 318 aLeaveBlock.setRequestStatus(LMConstants.REQUEST_STATUS.REQUESTED); 319 aLeaveBlock.setDocumentId(leavePayout.getLeaveCalendarDocumentId()); 320 aLeaveBlock.setBlockId(0L); 321 322 //Want to store the newly created leave block id on this maintainable object 323 //when the status of the maintenance document encapsulating this maintainable changes 324 //the id will be used to fetch and update the leave block statuses. 325 aLeaveBlock = KRADServiceLocator.getBusinessObjectService().save(aLeaveBlock); 326 327 leavePayout.setForfeitedLeaveBlockId(aLeaveBlock.getLmLeaveBlockId()); 328 // save history 329 LeaveBlockHistory lbh = new LeaveBlockHistory(aLeaveBlock); 330 lbh.setAction(LMConstants.ACTION.ADD); 331 TkServiceLocator.getLeaveBlockHistoryService().saveLeaveBlockHistory(lbh); 332 333 leaveBlocks.add(aLeaveBlock); 334 } 335 } 336 337 return leavePayout; 338 } 339 } 340 341 @Override 342 public Map<String,ArrayList<String>> getEligiblePayouts(CalendarEntries calendarEntry, 343 String principalId) throws Exception { 344 //Employee override check here, or return base-eligible accrual categories, 345 //filtering out those that have increased balance limits due to employee override in caller? 346 //null check inserted to fix LeaveCalendarWebTest failures on kpme-trunk-build-unit #2069 347 List<String> eligibleAccrualCategories = new ArrayList<String>(); 348 Map<String, ArrayList<String>> eligibilities = new HashMap<String,ArrayList<String>>(); 349 //TODO: create map for MAX_BAL_ACTION_FREQ in LMConstants 350 eligibilities.put(LMConstants.MAX_BAL_ACTION_FREQ.LEAVE_APPROVE, new ArrayList<String>()); 351 eligibilities.put(LMConstants.MAX_BAL_ACTION_FREQ.YEAR_END, new ArrayList<String>()); 352 eligibilities.put(LMConstants.MAX_BAL_ACTION_FREQ.ON_DEMAND, new ArrayList<String>()); 353 354 PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, calendarEntry.getEndPeriodDate()); 355 List<AccrualCategory> accrualCategories = TkServiceLocator.getAccrualCategoryService().getActiveAccrualCategoriesForLeavePlan(pha.getLeavePlan(), calendarEntry.getEndPeriodDate()); 356 357 org.kuali.hr.time.calendar.Calendar leaveCalendar = pha.getLeaveCalObj(); 358 CalendarEntries thisLeaveEntry = null; 359 Interval thisEntryInterval = new Interval(calendarEntry.getBeginPeriodDate().getTime(),calendarEntry.getEndPeriodDate().getTime()); 360 Date asOfDate = TKUtils.getCurrentDate(); 361 if(TKUtils.getCurrentDate().after(DateUtils.addSeconds(calendarEntry.getEndPeriodDate(),-1))) 362 asOfDate = new Date(DateUtils.addSeconds(calendarEntry.getEndPeriodDate(), -1).getTime()); 363 if(ObjectUtils.isNotNull(leaveCalendar)) { 364 for(CalendarEntries entry : leaveCalendar.getCalendarEntries()) { 365 if(thisEntryInterval.contains(DateUtils.addDays(entry.getEndPeriodTime(),-1).getTime())) 366 thisLeaveEntry = entry; 367 } 368 } 369 // this calendar entry interval does not contain a leave calendar's rollover date. 370 if(ObjectUtils.isNull(thisLeaveEntry)) { 371 return eligibilities; 372 } 373 //TODO: Find the end period date for the corresponding leave calendar. 374 // must check if this date falls within the interval of the calendar entries begin / end. 375 // if so, get the leave blocks and calculate the accrued balance. 376 //LeaveSummary leaveSummary = TkServiceLocator.getLeaveSummaryService().getLeaveSummary(principalId, getCalendarEntry()); 377 if(!accrualCategories.isEmpty()) { 378 LeaveSummary summary = TkServiceLocator.getLeaveSummaryService().getLeaveSummary(principalId, calendarEntry); 379 //null check inserted to fix LeaveCalendarWebTst failures on kpme-trunk-build-unit #2069 380 for(AccrualCategory accrualCategory : accrualCategories) { 381 //TODO: Iterate over Accrual Categories within this calendar entry. 382 AccrualCategoryRule rule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRuleForDate(accrualCategory, DateUtils.addDays(calendarEntry.getEndPeriodDate(),-1), pha.getServiceDate()); 383 //Employee overrides... 384 if(ObjectUtils.isNotNull(rule)) { 385 if(StringUtils.equals(rule.getMaxBalFlag(),"Y")) { 386 if(StringUtils.equals(rule.getActionAtMaxBalance(), LMConstants.ACTION_AT_MAX_BAL.PAYOUT)) { 387 //There is a disagreement between the constant value LMConstants.MAX_BAL_ACTION_FREQ, and the value being 388 //set on LM_ACCRUAL_CATEGORY_RULES_T table. Temporarily have changed the constant to reflect the field 389 //value being set for MAX_BAL_ACTION_FREQ when accrual category rule records are saved. 390 if(ObjectUtils.isNotNull(rule.getMaxBalanceActionFrequency())) { 391 BigDecimal maxBalance = rule.getMaxBalance(); 392 393 LeaveSummaryRow row = summary.getLeaveSummaryRowForAccrualCategory(accrualCategory.getLmAccrualCategoryId()); 394 BigDecimal accruedBalance = row.getAccruedBalance(); 395 /* for(LeaveBlock leaveBlock : leaveBlockMap.get(accrualCategory.getAccrualCategory())) { 396 //TODO: limit leave blocks to those created on or after the calendar year period containing this calendar entry. 397 if(StringUtils.equals(leaveBlock.getRequestStatus(),LMConstants.REQUEST_STATUS.APPROVED)) 398 accruedBalance = accruedBalance.add(leaveBlock.getLeaveAmount()); 399 }*/ 400 BigDecimal fte = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, TKUtils.getCurrentDate()); 401 BigDecimal adjustedMaxBalance = maxBalance.multiply(fte); 402 BigDecimal maxAnnualCarryOver = null; 403 if(ObjectUtils.isNotNull(rule.getMaxCarryOver())) 404 maxAnnualCarryOver = new BigDecimal(rule.getMaxCarryOver()); 405 BigDecimal adjustedMaxAnnualCarryOver = null; 406 if(ObjectUtils.isNotNull(maxAnnualCarryOver)) { 407 adjustedMaxAnnualCarryOver = maxAnnualCarryOver.multiply(fte); 408 } 409 410 List<EmployeeOverride> overrides = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverrides(principalId, TKUtils.getCurrentDate()); 411 for(EmployeeOverride override : overrides) { 412 if(StringUtils.equals(override.getAccrualCategory(),accrualCategory.getAccrualCategory())) { 413 if(StringUtils.equals(override.getOverrideType(),"MB")) { 414 adjustedMaxBalance = new BigDecimal(override.getOverrideValue()); 415 } 416 if(StringUtils.equals(override.getOverrideType(),"MAC")) { 417 adjustedMaxAnnualCarryOver = new BigDecimal(override.getOverrideValue()); 418 } 419 //override values are not pro-rated. 420 } 421 } 422 //should extend a BalanceTransferBase class, or use an algorithm swapping pattern. 423 //allow institutions to extend/customize/implement their own max_bal_action_frequency types. 424 if(StringUtils.equals(rule.getMaxBalanceActionFrequency(),LMConstants.MAX_BAL_ACTION_FREQ.YEAR_END)) { 425 //For year end transfer frequencies... 426 //Should use an "asOfDate" or effective date for principalHRAttributes. If getting eligibilities for a past calendar, 427 //pha may not be the same. 428 LeavePlan lp = TkServiceLocator.getLeavePlanService().getLeavePlan(pha.getLeavePlan(),TKUtils.getCurrentDate()); 429 StringBuilder sb = new StringBuilder(""); 430 String calendarYearStart = lp.getCalendarYearStart(); 431 // mm/dd 432 sb.append(calendarYearStart+"/"); 433 if(lp.getCalendarYearStartMonth().equals("01") && calendarEntry.getBeginPeriodDate().getMonth() == 11) { 434 //a calendar may start on 01/15, with monthly intervals. 435 //calendarEntry.beginPeriodDate.year = calendarYearStart.year - 1 436 sb.append(DateUtils.toCalendar(DateUtils.addYears(calendarEntry.getBeginPeriodDate(),1)).get(Calendar.YEAR)); 437 } 438 else { 439 sb.append(DateUtils.toCalendar(calendarEntry.getBeginPeriodDateTime()).get(Calendar.YEAR)); 440 } 441 //if the calendar being submitted is the final calendar in the leave plans calendar year. 442 //must check the calendar year start month. If its the first month of the year, add a year to the date. 443 //otherwise, the end period date and the calendar year start date have the same year. 444 if(thisEntryInterval.contains(DateUtils.addDays(TKUtils.formatDateString(sb.toString()),-1).getTime())) { 445 //BigDecimal accruedBalanceLessPendingTransfers = lsr.getAccruedBalance().add(adjustment); 446 if(accruedBalance.compareTo(adjustedMaxBalance) > 0 || 447 (ObjectUtils.isNotNull(adjustedMaxAnnualCarryOver) && 448 accruedBalance.compareTo(adjustedMaxAnnualCarryOver) > 0)) { 449 eligibleAccrualCategories.add(rule.getLmAccrualCategoryRuleId()); 450 eligibilities.get(LMConstants.MAX_BAL_ACTION_FREQ.YEAR_END).add(rule.getLmAccrualCategoryRuleId()); 451 } 452 } 453 //otherwise its not transferable under year end frequency. 454 } 455 else { 456 //BigDecimal accruedBalanceLessPendingTransfers = lsr.getAccruedBalance().add(adjustment); 457 if(accruedBalance.compareTo(adjustedMaxBalance) > 0 ) { 458 eligibleAccrualCategories.add(rule.getLmAccrualCategoryRuleId()); 459 eligibilities.get(rule.getMaxBalanceActionFrequency()).add(rule.getLmAccrualCategoryRuleId()); 460 } 461 } 462 } 463 } 464 } 465 } 466 } 467 } 468 return eligibilities; 469 } 470 471 @Override 472 public void submitToWorkflow(LeavePayout leavePayout) 473 throws WorkflowException { 474 //leavePayout.setStatus(TkConstants.ROUTE_STATUS.ENROUTE); 475 EntityNamePrincipalName principalName = null; 476 if (leavePayout.getPrincipalId() != null) { 477 principalName = KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(leavePayout.getPrincipalId()); 478 } 479 480 MaintenanceDocument document = KRADServiceLocatorWeb.getMaintenanceDocumentService().setupNewMaintenanceDocument(LeavePayout.class.getName(), 481 "LeavePayoutDocumentType",KRADConstants.MAINTENANCE_NEW_ACTION); 482 483 String personName = (principalName != null && principalName.getDefaultName() != null) ? principalName.getDefaultName().getCompositeName() : StringUtils.EMPTY; 484 String date = TKUtils.formatDate(new java.sql.Date(leavePayout.getEffectiveDate().getTime())); 485 document.getDocumentHeader().setDocumentDescription(personName + " (" + leavePayout.getPrincipalId() + ") - " + date); 486 Map<String,String[]> params = new HashMap<String,String[]>(); 487 488 KRADServiceLocatorWeb.getMaintenanceDocumentService().setupMaintenanceObject(document, KRADConstants.MAINTENANCE_NEW_ACTION, params); 489 LeavePayout lpObj = (LeavePayout) document.getNewMaintainableObject().getDataObject(); 490 491 lpObj.setAccrualCategoryRule(leavePayout.getAccrualCategoryRule()); 492 lpObj.setEffectiveDate(leavePayout.getEffectiveDate()); 493 lpObj.setForfeitedAmount(leavePayout.getForfeitedAmount()); 494 lpObj.setFromAccrualCategory(leavePayout.getFromAccrualCategory()); 495 lpObj.setPrincipalId(leavePayout.getPrincipalId()); 496 lpObj.setEarnCode(leavePayout.getEarnCode()); 497 lpObj.setPayoutAmount(leavePayout.getPayoutAmount()); 498 lpObj.setDocumentHeaderId(document.getDocumentHeader().getWorkflowDocument().getDocumentId()); 499 500 document.getNewMaintainableObject().setDataObject(lpObj); 501 KRADServiceLocatorWeb.getDocumentService().saveDocument(document); 502 document.getDocumentHeader().getWorkflowDocument().saveDocument(""); 503 504 document.getDocumentHeader().getWorkflowDocument().route(""); 505 } 506 507 @Override 508 public List<LeavePayout> getLeavePayouts(String viewPrincipal, 509 Date beginPeriodDate, Date endPeriodDate) { 510 // TODO Auto-generated method stub 511 return leavePayoutDao.getLeavePayouts(viewPrincipal, beginPeriodDate, endPeriodDate); 512 } 513 514 @Override 515 public void saveOrUpdate(LeavePayout payout) { 516 leavePayoutDao.saveOrUpdate(payout); 517 } 518 }