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 }