View Javadoc

1   /**
2    * Copyright 2004-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.hr.lm.leavepayout.validation;
17  
18  import java.math.BigDecimal;
19  import java.sql.Date;
20  import java.util.List;
21  
22  import org.apache.commons.lang.StringUtils;
23  import org.apache.commons.lang.time.DateUtils;
24  import org.kuali.hr.lm.accrual.AccrualCategory;
25  import org.kuali.hr.lm.accrual.AccrualCategoryRule;
26  import org.kuali.hr.lm.employeeoverride.EmployeeOverride;
27  import org.kuali.hr.lm.leavepayout.LeavePayout;
28  import org.kuali.hr.time.earncode.EarnCode;
29  import org.kuali.hr.time.principal.PrincipalHRAttributes;
30  import org.kuali.hr.time.service.base.TkServiceLocator;
31  import org.kuali.hr.time.util.TKContext;
32  import org.kuali.hr.time.util.TKUser;
33  import org.kuali.hr.time.util.TKUtils;
34  import org.kuali.hr.time.util.TkConstants;
35  import org.kuali.rice.kns.document.MaintenanceDocument;
36  import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase;
37  import org.kuali.rice.krad.bo.PersistableBusinessObject;
38  import org.kuali.rice.krad.util.GlobalVariables;
39  import org.kuali.rice.krad.util.ObjectUtils;
40  
41  public class LeavePayoutValidation extends MaintenanceDocumentRuleBase {
42  
43  	private boolean validateAgainstLeavePlan(PrincipalHRAttributes pha, AccrualCategory fromAccrualCategory, Date effectiveDate) {
44  		boolean isValid = true;
45  
46  		List<AccrualCategory> accrualCategories = TkServiceLocator.getAccrualCategoryService().getActiveAccrualCategoriesForLeavePlan(pha.getLeavePlan(), effectiveDate);
47  		if(accrualCategories.size() > 0) {
48  			boolean isFromInLeavePlan = false;
49  			for(AccrualCategory activeAccrualCategory : accrualCategories) {
50  				if(StringUtils.equals(activeAccrualCategory.getLmAccrualCategoryId(),fromAccrualCategory.getLmAccrualCategoryId())) {
51  					isFromInLeavePlan = true;
52  				}
53  			}
54  			if(!isFromInLeavePlan) {
55  				GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory", "leavePayout.accrualCategory.notInLeavePlan", fromAccrualCategory.getAccrualCategory());
56  				isValid &= false;
57  			}
58  		}
59  		else {
60  			GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId", "leavePayout.principal.noACinLeavePlan");
61  			isValid &=false;
62  		}
63  		
64  		return isValid;
65  	}
66  	
67  	//Employee Overrides???
68  	/**
69  	 * Transfer amount could be validated against several variables, including max transfer amount,
70  	 * max carry over ( for the converted amount deposited into the "to" accrual category, max usage
71  	 * ( if transfers count as usage ).
72  	 * @param transferAmount
73  	 * @param debitedAccrualCategory
74  	 * @param creditedAccrualCategory
75  	 * @param principalId TODO
76  	 * @param effectiveDate TODO
77  	 * @return true if transfer amount is valid
78  	 */
79  	private boolean validatePayoutAmount(BigDecimal transferAmount,
80  			AccrualCategory debitedAccrualCategory,
81  			EarnCode payoutEarnCode, String principalId, Date effectiveDate) {
82  		
83  		if(transferAmount.compareTo(BigDecimal.ZERO) < 0 ) {
84  			GlobalVariables.getMessageMap().putError("document.newMaintainableObject.transferAmount", "leavePayout.amount.negative");
85  			return false;
86  		}
87  		//TkServiceLocator.getAccrualCategoryService().getCurrentBalanceForPrincipal(principalId, debitedAccrualCategory, effectiveDate);
88  
89  		return true;
90  	}
91  
92  	/**
93  	 * Are there any rules in place for effective date? i.e. not more than one year in advance...
94  	 * @param date
95  	 * @return
96  	 */
97  	private boolean validateEffectiveDate(Date date) {
98  		//Limit on future dates?
99  		if(date.getTime() > DateUtils.addYears(TKUtils.getCurrentDate(), 1).getTime()) {
100 			GlobalVariables.getMessageMap().putError("document.newMaintainableObject.effectiveDate", "leavePayout.effectiveDate.overOneYear");
101 			return false;
102 		}
103 		return true;
104 	}
105 	
106 	/**
107 	 * Is the "From" accrual category required to be over its maximum balance before a transfer can take place?
108 	 * The "From" accrual category must be defined in an accrual category rule as having a max bal rule.
109 	 * @param accrualCategory
110 	 * @param effectiveDate 
111 	 * @param principalId 
112 	 * @param acr 
113 	 * @return
114 	 */
115 	private boolean validateTransferFromAccrualCategory(AccrualCategory accrualCategory, String principalId,
116 			Date effectiveDate, AccrualCategoryRule acr) {
117 		//accrualCategory has rules
118 		//PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, effectiveDate);
119 		
120 		return true;
121 	}
122 	
123 	private boolean validatePrincipal(PrincipalHRAttributes pha, String principalId) {
124 		// TODO Auto-generated method stub
125 		// Principal has to have an associated leave plan.
126 		// leave plan should contain the transfer "from" accrual category.
127 		
128 		return true;
129 	}
130 	
131 	//transfer amount must be under max limit when submitted via max balance triggered action or by a work area approver.
132 	private boolean isPayoutAmountUnderMaxLimit(String principalId, Date effectiveDate, String accrualCategory,
133 				BigDecimal payoutAmount, AccrualCategoryRule accrualRule, String leavePlan) {
134 	
135 		if(ObjectUtils.isNotNull(accrualRule)) {
136 
137 			BigDecimal maxPayoutAmount = null;
138 			if(ObjectUtils.isNotNull(accrualRule.getMaxTransferAmount())) {
139 				maxPayoutAmount = new BigDecimal(accrualRule.getMaxTransferAmount());
140 			}
141 			if(ObjectUtils.isNotNull(maxPayoutAmount)) {
142 				EmployeeOverride eo = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverride(principalId, leavePlan, accrualCategory, TkConstants.EMPLOYEE_OVERRIDE_TYPE.get("MPA"), effectiveDate);
143 				if(ObjectUtils.isNotNull(eo))
144 					if(ObjectUtils.isNull(eo.getOverrideValue()))
145 						maxPayoutAmount = new BigDecimal(Long.MAX_VALUE);
146 					else
147 						maxPayoutAmount = new BigDecimal(eo.getOverrideValue());
148 				else {
149 					BigDecimal fteSum = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, effectiveDate);
150 					maxPayoutAmount = maxPayoutAmount.multiply(fteSum);
151 				}
152 				if(payoutAmount.compareTo(maxPayoutAmount) > 0) {
153 					GlobalVariables.getMessageMap().putError("document.newMaintainableObject.payoutAmount","leavePayout.exceeds.payoutLimit");
154 					return false;
155 				}
156 			}
157 		}
158 		return true;
159 	}
160 	
161 	@Override
162 	public boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
163 		boolean isValid = true;
164 		LOG.debug("entering custom validation for Balance Transfer");
165 
166 		PersistableBusinessObject pbo = (PersistableBusinessObject) this.getNewBo();
167 
168 		if(pbo instanceof LeavePayout) {
169 
170 			LeavePayout leavePayout = (LeavePayout) pbo;
171 
172 			/**
173 			 * Validation is basically governed by accrual category rules. Get accrual category
174 			 * rules for both the "To" and "From" accrual categories, pass to validators along with the
175 			 * values needing to be validated.
176 			 * 
177 			 * Balance transfers initiated from the leave calendar display should already have all values
178 			 * populated, thus validated, including the accrual category rule for the "From" accrual category.
179 			 * 
180 			 * Balance transfers initiated via the Maintenance tab will have no values populated.
181 			 */
182 			String principalId = leavePayout.getPrincipalId();
183 			Date effectiveDate = leavePayout.getEffectiveDate();
184 			String fromAccrualCategory = leavePayout.getFromAccrualCategory();
185 			EarnCode payoutEarnCode = leavePayout.getEarnCodeObj();
186 			AccrualCategory fromCat = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(fromAccrualCategory, effectiveDate);
187 			PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId,effectiveDate);
188 			
189 			boolean isDeptAdmin = TKUser.isDepartmentAdmin();
190 			boolean isSysAdmin = TKUser.isSystemAdmin();
191 			if(isDeptAdmin || isSysAdmin) {
192 				isValid &= validatePayoutAmount(leavePayout.getPayoutAmount(),fromCat,payoutEarnCode, principalId, effectiveDate);
193 			}
194 			else {
195 				if(ObjectUtils.isNotNull(pha)) {
196 					if(ObjectUtils.isNotNull(pha.getLeavePlan())) {
197 						AccrualCategoryRule acr = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRuleForDate(fromCat, effectiveDate, pha.getServiceDate());
198 						if(ObjectUtils.isNotNull(acr)) {
199 							if(ObjectUtils.isNotNull(acr.getMaxBalFlag())
200 									&& StringUtils.isNotBlank(acr.getMaxBalFlag())
201 									&& StringUtils.isNotEmpty(acr.getMaxBalFlag())
202 									&& StringUtils.equals(acr.getMaxBalFlag(), "Y")) {
203 								if(ObjectUtils.isNotNull(payoutEarnCode)) {
204 									isValid &= validatePrincipal(pha,principalId);
205 									isValid &= validateEffectiveDate(effectiveDate);
206 									isValid &= validateAgainstLeavePlan(pha,fromCat,effectiveDate);
207 									isValid &= validateTransferFromAccrualCategory(fromCat,principalId,effectiveDate,acr);
208 									isValid &= validatePayoutAmount(leavePayout.getPayoutAmount(),fromCat,payoutEarnCode, null, null);
209 									isValid &= isPayoutAmountUnderMaxLimit(principalId, effectiveDate, fromAccrualCategory, leavePayout.getPayoutAmount(), acr, pha.getLeavePlan());
210 								}
211 								else {
212 									//should never be the case if accrual category rules are validated correctly.
213 									GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory",
214 											"leavePayout.fromAccrualCategory.rules.payoutToEarnCode",
215 											fromAccrualCategory);
216 									isValid &= false;
217 								}
218 							}
219 							else {
220 								//max bal flag null, blank, empty, or "N"
221 								GlobalVariables.getMessageMap().putError("document.newMaintinableObject.fromAccrualCategory",
222 										"leavePayout.fromAccrualCategory.rules.maxBalFlag", fromAccrualCategory);
223 								isValid &= false;
224 							}
225 						}
226 						else {
227 							//department admins must validate amount to transfer does not exceed current balance.
228 							GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory",
229 									"leavePayout.fromAccrualCategory.rules.exist",fromCat.getAccrualCategory());
230 							isValid &= false;
231 						}
232 					}
233 					else {
234 						//if the principal doesn't have a leave plan, there aren't any accrual categories that can be debited/credited.
235 						GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId","leavePayout.principal.noLeavePlan");
236 						isValid &=false;
237 					}
238 				}
239 				else  {
240 					//if the principal has no principal hr attributes, they're not a principal.
241 					GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId","leavePayout.principal.noAttributes");
242 					isValid &= false;
243 				}
244 			}
245 		}
246 		return isValid;
247 	}
248 
249 }