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.setForfeitedAmount(leavePayout.getForfeitedAmount());
360 		lpObj.setFromAccrualCategory(leavePayout.getFromAccrualCategory());
361 		lpObj.setPrincipalId(leavePayout.getPrincipalId());
362 		lpObj.setEarnCode(leavePayout.getEarnCode());
363 		lpObj.setPayoutAmount(leavePayout.getPayoutAmount());
364 		lpObj.setDocumentHeaderId(document.getDocumentHeader().getWorkflowDocument().getDocumentId());
365 		
366 		document.getNewMaintainableObject().setDataObject(lpObj);
367 		KRADServiceLocatorWeb.getDocumentService().saveDocument(document);
368 		document.getDocumentHeader().getWorkflowDocument().saveDocument("");
369 
370 		document.getDocumentHeader().getWorkflowDocument().route("");
371 	}
372 
373 	@Override
374 	public List<LeavePayout> getLeavePayouts(String viewPrincipal,
375 			Date beginPeriodDate, Date endPeriodDate) {
376 		
377 		return leavePayoutDao.getLeavePayouts(viewPrincipal, beginPeriodDate, endPeriodDate);
378 	}
379 
380 	@Override
381 	public void saveOrUpdate(LeavePayout payout) {
382 		leavePayoutDao.saveOrUpdate(payout);
383 	}
384 }