1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.hr.lm.leavepayout.service;
17
18 import org.apache.commons.lang.StringUtils;
19 import org.apache.commons.lang.time.DateUtils;
20 import org.joda.time.DateTime;
21 import org.joda.time.Interval;
22 import org.kuali.hr.lm.LMConstants;
23 import org.kuali.hr.lm.accrual.AccrualCategory;
24 import org.kuali.hr.lm.accrual.AccrualCategoryRule;
25 import org.kuali.hr.lm.leavepayout.LeavePayout;
26 import org.kuali.hr.lm.employeeoverride.EmployeeOverride;
27 import org.kuali.hr.lm.leaveSummary.LeaveSummary;
28 import org.kuali.hr.lm.leaveSummary.LeaveSummaryRow;
29 import org.kuali.hr.lm.leaveblock.LeaveBlock;
30 import org.kuali.hr.lm.leaveblock.LeaveBlockHistory;
31 import org.kuali.hr.lm.leavecalendar.LeaveCalendarDocument;
32 import org.kuali.hr.lm.leavepayout.LeavePayout;
33 import org.kuali.hr.lm.leavepayout.dao.LeavePayoutDao;
34
35 import org.kuali.hr.lm.leaveplan.LeavePlan;
36 import org.kuali.hr.time.calendar.CalendarEntries;
37 import org.kuali.hr.time.earncode.EarnCode;
38 import org.kuali.hr.time.principal.PrincipalHRAttributes;
39 import org.kuali.hr.time.service.base.TkServiceLocator;
40 import org.kuali.hr.time.util.TKContext;
41 import org.kuali.hr.time.util.TKUtils;
42 import org.kuali.hr.time.util.TkConstants;
43 import org.kuali.rice.kew.api.exception.WorkflowException;
44 import org.kuali.rice.kim.api.identity.principal.EntityNamePrincipalName;
45 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
46 import org.kuali.rice.krad.maintenance.MaintenanceDocument;
47 import org.kuali.rice.krad.service.KRADServiceLocator;
48 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
49 import org.kuali.rice.krad.util.KRADConstants;
50 import org.kuali.rice.krad.util.ObjectUtils;
51
52 import edu.emory.mathcs.backport.java.util.Collections;
53
54 import java.math.BigDecimal;
55 import java.sql.Date;
56 import java.util.ArrayList;
57 import java.util.Calendar;
58 import java.util.Comparator;
59 import java.util.HashMap;
60 import java.util.HashSet;
61 import java.util.List;
62 import java.util.Map;
63 import java.util.Set;
64
65 public class LeavePayoutServiceImpl implements LeavePayoutService {
66
67 private LeavePayoutDao leavePayoutDao;
68
69 @Override
70 public List<LeavePayout> getAllLeavePayoutsForPrincipalId(
71 String principalId) {
72 return leavePayoutDao.getAllLeavePayoutsForPrincipalId(principalId);
73 }
74
75 @Override
76 public List<LeavePayout> getAllLeavePayoutsForPrincipalIdAsOfDate(
77 String principalId, Date effectiveDate) {
78 return leavePayoutDao.getAllLeavePayoutsForPrincipalIdAsOfDate(principalId,effectiveDate);
79 }
80
81 @Override
82 public List<LeavePayout> getAllLeavePayoutsByEffectiveDate(
83 Date effectiveDate) {
84 return leavePayoutDao.getAllLeavePayoutsByEffectiveDate(effectiveDate);
85 }
86
87 @Override
88 public LeavePayout getLeavePayoutById(String lmLeavePayoutId) {
89 return leavePayoutDao.getLeavePayoutById(lmLeavePayoutId);
90 }
91
92 public LeavePayoutDao getLeavePayoutDao() {
93 return leavePayoutDao;
94 }
95
96 public void setLeavePayoutDao(LeavePayoutDao leavePayoutDao) {
97 this.leavePayoutDao = leavePayoutDao;
98 }
99
100 @Override
101 public LeavePayout initializePayout(String principalId,
102 String accrualCategoryRule, BigDecimal accruedBalance,
103 Date effectiveDate) {
104
105
106
107 LeavePayout leavePayout = null;
108 AccrualCategoryRule accrualRule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRule(accrualCategoryRule);
109
110 if(ObjectUtils.isNotNull(accrualRule) && ObjectUtils.isNotNull(accruedBalance)) {
111 leavePayout = new LeavePayout();
112
113
114
115
116
117 AccrualCategory fromAccrualCategory = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(accrualRule.getLmAccrualCategoryId());
118 BigDecimal fullTimeEngagement = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, effectiveDate);
119
120
121
122 BigDecimal maxBalance = accrualRule.getMaxBalance();
123 BigDecimal adjustedMaxBalance = maxBalance.multiply(fullTimeEngagement).setScale(2);
124
125 BigDecimal maxPayoutAmount = null;
126 BigDecimal adjustedMaxPayoutAmount = null;
127 if(ObjectUtils.isNotNull(accrualRule.getMaxPayoutAmount())) {
128 maxPayoutAmount = new BigDecimal(accrualRule.getMaxPayoutAmount());
129 adjustedMaxPayoutAmount = maxPayoutAmount.multiply(fullTimeEngagement).setScale(2);
130 }
131 else {
132
133 maxPayoutAmount = new BigDecimal(Long.MAX_VALUE);
134 adjustedMaxPayoutAmount = maxPayoutAmount;
135 }
136
137 BigDecimal maxCarryOver = null;
138 BigDecimal adjustedMaxCarryOver = null;
139 if(ObjectUtils.isNotNull(accrualRule.getMaxCarryOver())) {
140 maxCarryOver = new BigDecimal(accrualRule.getMaxCarryOver());
141 adjustedMaxCarryOver = maxCarryOver.multiply(fullTimeEngagement).setScale(2);
142 }
143 else {
144
145 maxCarryOver = new BigDecimal(Long.MAX_VALUE);
146 adjustedMaxCarryOver = maxCarryOver;
147 }
148
149 EmployeeOverride maxBalanceOverride = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverride(principalId, fromAccrualCategory.getLeavePlan(), fromAccrualCategory.getAccrualCategory(), "MB", effectiveDate);
150 EmployeeOverride maxPayoutAmountOverride = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverride(principalId, fromAccrualCategory.getLeavePlan(), fromAccrualCategory.getAccrualCategory(), "MPA", effectiveDate);
151 EmployeeOverride maxAnnualCarryOverOverride = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverride(principalId, fromAccrualCategory.getLeavePlan(), fromAccrualCategory.getAccrualCategory(), "MAC", effectiveDate);
152
153 if(maxBalanceOverride != null)
154 adjustedMaxBalance = new BigDecimal(maxBalanceOverride.getOverrideValue());
155 if(maxPayoutAmountOverride != null)
156 adjustedMaxPayoutAmount = new BigDecimal(maxPayoutAmountOverride.getOverrideValue());
157 if(maxAnnualCarryOverOverride != null)
158 adjustedMaxCarryOver = new BigDecimal(maxAnnualCarryOverOverride.getOverrideValue());
159
160
161 BigDecimal transferAmount = accruedBalance.subtract(adjustedMaxBalance);
162 if(transferAmount.compareTo(adjustedMaxPayoutAmount) > 0) {
163
164
165
166
167 BigDecimal forfeiture = transferAmount.subtract(adjustedMaxPayoutAmount).setScale(2);
168 forfeiture = forfeiture.stripTrailingZeros();
169 leavePayout.setForfeitedAmount(forfeiture);
170 leavePayout.setPayoutAmount(adjustedMaxPayoutAmount);
171 }
172 else {
173 leavePayout.setPayoutAmount(transferAmount);
174 leavePayout.setForfeitedAmount(BigDecimal.ZERO);
175 }
176
177 assert(adjustedMaxBalance.compareTo(accruedBalance.subtract(leavePayout.getPayoutAmount().add(leavePayout.getForfeitedAmount()))) == 0);
178
179
180 if(StringUtils.equals(accrualRule.getMaxBalanceActionFrequency(),LMConstants.MAX_BAL_ACTION_FREQ.YEAR_END)) {
181 if(ObjectUtils.isNotNull(maxCarryOver)) {
182
183 if(ObjectUtils.isNull(adjustedMaxCarryOver))
184 adjustedMaxCarryOver = maxCarryOver.multiply(fullTimeEngagement).setScale(2);
185
186
187
188 if(adjustedMaxBalance.compareTo(adjustedMaxCarryOver) > 0) {
189 BigDecimal carryOverDiff = adjustedMaxBalance.subtract(adjustedMaxCarryOver);
190
191 if(StringUtils.equals(accrualRule.getActionAtMaxBalance(),LMConstants.ACTION_AT_MAX_BAL.LOSE)){
192
193 leavePayout.setForfeitedAmount(leavePayout.getForfeitedAmount().add(carryOverDiff));
194 }
195 else {
196
197 BigDecimal potentialPayoutAmount = leavePayout.getPayoutAmount().add(carryOverDiff);
198
199
200 if(potentialPayoutAmount.compareTo(adjustedMaxPayoutAmount) <= 0) {
201
202 leavePayout.setPayoutAmount(leavePayout.getPayoutAmount().add(carryOverDiff));
203 }
204 else {
205
206 BigDecimal carryOverExcess = potentialPayoutAmount.subtract(adjustedMaxPayoutAmount);
207
208 leavePayout.setForfeitedAmount(leavePayout.getForfeitedAmount().add(carryOverExcess));
209
210 leavePayout.setPayoutAmount(leavePayout.getPayoutAmount().add(carryOverDiff.subtract(carryOverExcess)));
211
212 assert(adjustedMaxCarryOver.compareTo(accruedBalance.subtract(leavePayout.getPayoutAmount().add(leavePayout.getForfeitedAmount()))) == 0);
213 }
214 }
215 }
216
217 }
218 }
219
220 leavePayout.setEffectiveDate(effectiveDate);
221 leavePayout.setAccrualCategoryRule(accrualCategoryRule);
222 leavePayout.setFromAccrualCategory(fromAccrualCategory.getAccrualCategory());
223 leavePayout.setPrincipalId(principalId);
224 leavePayout.setEarnCode(accrualRule.getMaxPayoutEarnCode());
225
226 }
227 return leavePayout;
228 }
229
230 @Override
231 public LeavePayout payout(LeavePayout leavePayout) {
232 if(ObjectUtils.isNull(leavePayout))
233 throw new RuntimeException("did not supply a valid LeavePayout object.");
234 else {
235 List<LeaveBlock> leaveBlocks = new ArrayList<LeaveBlock>();
236 BigDecimal transferAmount = leavePayout.getPayoutAmount();
237 LeaveBlock aLeaveBlock = null;
238
239 if(ObjectUtils.isNotNull(transferAmount)) {
240 if(transferAmount.compareTo(BigDecimal.ZERO) > 0) {
241
242 aLeaveBlock = new LeaveBlock();
243
244 aLeaveBlock.setPrincipalId(leavePayout.getPrincipalId());
245 aLeaveBlock.setLeaveDate(leavePayout.getEffectiveDate());
246 aLeaveBlock.setEarnCode(leavePayout.getEarnCode());
247 aLeaveBlock.setAccrualCategory(leavePayout.getEarnCodeObj().getAccrualCategory());
248 aLeaveBlock.setDescription("Amount payed out");
249 aLeaveBlock.setLeaveAmount(leavePayout.getPayoutAmount());
250 aLeaveBlock.setAccrualGenerated(true);
251 aLeaveBlock.setTransactionDocId(leavePayout.getDocumentHeaderId());
252 aLeaveBlock.setLeaveBlockType(LMConstants.LEAVE_BLOCK_TYPE.LEAVE_PAYOUT);
253 aLeaveBlock.setRequestStatus(LMConstants.REQUEST_STATUS.REQUESTED);
254 aLeaveBlock.setDocumentId(leavePayout.getLeaveCalendarDocumentId());
255 aLeaveBlock.setBlockId(0L);
256
257
258
259
260 aLeaveBlock = KRADServiceLocator.getBusinessObjectService().save(aLeaveBlock);
261
262 leavePayout.setPayoutLeaveBlockId(aLeaveBlock.getLmLeaveBlockId());
263
264 LeaveBlockHistory lbh = new LeaveBlockHistory(aLeaveBlock);
265 lbh.setAction(LMConstants.ACTION.ADD);
266 TkServiceLocator.getLeaveBlockHistoryService().saveLeaveBlockHistory(lbh);
267 leaveBlocks.add(aLeaveBlock);
268
269
270 aLeaveBlock = new LeaveBlock();
271 aLeaveBlock.setPrincipalId(leavePayout.getPrincipalId());
272 aLeaveBlock.setLeaveDate(leavePayout.getEffectiveDate());
273 aLeaveBlock.setEarnCode(leavePayout.getFromAccrualCategoryObj().getEarnCode());
274 aLeaveBlock.setAccrualCategory(leavePayout.getFromAccrualCategory());
275 aLeaveBlock.setDescription("Payout amount");
276 aLeaveBlock.setLeaveAmount(leavePayout.getPayoutAmount().negate());
277 aLeaveBlock.setAccrualGenerated(true);
278 aLeaveBlock.setTransactionDocId(leavePayout.getDocumentHeaderId());
279 aLeaveBlock.setLeaveBlockType(LMConstants.LEAVE_BLOCK_TYPE.LEAVE_PAYOUT);
280 aLeaveBlock.setRequestStatus(LMConstants.REQUEST_STATUS.REQUESTED);
281 aLeaveBlock.setDocumentId(leavePayout.getLeaveCalendarDocumentId());
282 aLeaveBlock.setBlockId(0L);
283
284
285
286
287 aLeaveBlock = KRADServiceLocator.getBusinessObjectService().save(aLeaveBlock);
288
289 leavePayout.setPayoutFromLeaveBlockId(aLeaveBlock.getLmLeaveBlockId());
290
291 lbh = new LeaveBlockHistory(aLeaveBlock);
292 lbh.setAction(LMConstants.ACTION.ADD);
293 TkServiceLocator.getLeaveBlockHistoryService().saveLeaveBlockHistory(lbh);
294
295 leaveBlocks.add(aLeaveBlock);
296 }
297 }
298
299 BigDecimal forfeitedAmount = leavePayout.getForfeitedAmount();
300 if(ObjectUtils.isNotNull(forfeitedAmount)) {
301
302 if(forfeitedAmount.compareTo(BigDecimal.ZERO) > 0) {
303
304 aLeaveBlock = new LeaveBlock();
305 aLeaveBlock.setPrincipalId(leavePayout.getPrincipalId());
306 aLeaveBlock.setLeaveDate(leavePayout.getEffectiveDate());
307 aLeaveBlock.setEarnCode(leavePayout.getFromAccrualCategoryObj().getEarnCode());
308 aLeaveBlock.setAccrualCategory(leavePayout.getFromAccrualCategory());
309 aLeaveBlock.setDescription("Forfeited payout amount");
310 aLeaveBlock.setLeaveAmount(forfeitedAmount.negate());
311 aLeaveBlock.setAccrualGenerated(true);
312 aLeaveBlock.setTransactionDocId(leavePayout.getDocumentHeaderId());
313 aLeaveBlock.setLeaveBlockType(LMConstants.LEAVE_BLOCK_TYPE.LEAVE_PAYOUT);
314 aLeaveBlock.setRequestStatus(LMConstants.REQUEST_STATUS.REQUESTED);
315 aLeaveBlock.setDocumentId(leavePayout.getLeaveCalendarDocumentId());
316 aLeaveBlock.setBlockId(0L);
317
318
319
320
321 aLeaveBlock = KRADServiceLocator.getBusinessObjectService().save(aLeaveBlock);
322
323 leavePayout.setForfeitedLeaveBlockId(aLeaveBlock.getLmLeaveBlockId());
324
325 LeaveBlockHistory lbh = new LeaveBlockHistory(aLeaveBlock);
326 lbh.setAction(LMConstants.ACTION.ADD);
327 TkServiceLocator.getLeaveBlockHistoryService().saveLeaveBlockHistory(lbh);
328
329 leaveBlocks.add(aLeaveBlock);
330 }
331 }
332
333 return leavePayout;
334 }
335 }
336
337 @Override
338 public void submitToWorkflow(LeavePayout leavePayout)
339 throws WorkflowException {
340
341 EntityNamePrincipalName principalName = null;
342 if (leavePayout.getPrincipalId() != null) {
343 principalName = KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(leavePayout.getPrincipalId());
344 }
345
346 MaintenanceDocument document = KRADServiceLocatorWeb.getMaintenanceDocumentService().setupNewMaintenanceDocument(LeavePayout.class.getName(),
347 "LeavePayoutDocumentType",KRADConstants.MAINTENANCE_NEW_ACTION);
348
349 String personName = (principalName != null && principalName.getDefaultName() != null) ? principalName.getDefaultName().getCompositeName() : StringUtils.EMPTY;
350 String date = TKUtils.formatDate(new java.sql.Date(leavePayout.getEffectiveDate().getTime()));
351 document.getDocumentHeader().setDocumentDescription(personName + " (" + leavePayout.getPrincipalId() + ") - " + date);
352 Map<String,String[]> params = new HashMap<String,String[]>();
353
354 KRADServiceLocatorWeb.getMaintenanceDocumentService().setupMaintenanceObject(document, KRADConstants.MAINTENANCE_NEW_ACTION, params);
355 LeavePayout lpObj = (LeavePayout) document.getNewMaintainableObject().getDataObject();
356
357 lpObj.setAccrualCategoryRule(leavePayout.getAccrualCategoryRule());
358 lpObj.setEffectiveDate(leavePayout.getEffectiveDate());
359 lpObj.setLeaveCalendarDocumentId(leavePayout.getLeaveCalendarDocumentId());
360 lpObj.setForfeitedAmount(leavePayout.getForfeitedAmount());
361 lpObj.setFromAccrualCategory(leavePayout.getFromAccrualCategory());
362 lpObj.setPrincipalId(leavePayout.getPrincipalId());
363 lpObj.setEarnCode(leavePayout.getEarnCode());
364 lpObj.setPayoutAmount(leavePayout.getPayoutAmount());
365 lpObj.setDocumentHeaderId(document.getDocumentHeader().getWorkflowDocument().getDocumentId());
366
367 document.getNewMaintainableObject().setDataObject(lpObj);
368 KRADServiceLocatorWeb.getDocumentService().saveDocument(document);
369 document.getDocumentHeader().getWorkflowDocument().saveDocument("");
370
371 document.getDocumentHeader().getWorkflowDocument().route("");
372 }
373
374 @Override
375 public List<LeavePayout> getLeavePayouts(String viewPrincipal,
376 Date beginPeriodDate, Date endPeriodDate) {
377
378 return leavePayoutDao.getLeavePayouts(viewPrincipal, beginPeriodDate, endPeriodDate);
379 }
380
381 @Override
382 public void saveOrUpdate(LeavePayout payout) {
383 leavePayoutDao.saveOrUpdate(payout);
384 }
385 }