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.balancetransfer.validation;
17  
18  import java.math.BigDecimal;
19  import java.sql.Date;
20  import java.util.Calendar;
21  import java.util.List;
22  
23  import org.apache.commons.lang.StringUtils;
24  import org.apache.commons.lang.time.DateUtils;
25  import org.kuali.hr.lm.accrual.AccrualCategory;
26  import org.kuali.hr.lm.accrual.AccrualCategoryRule;
27  import org.kuali.hr.lm.balancetransfer.BalanceTransfer;
28  import org.kuali.hr.lm.employeeoverride.EmployeeOverride;
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 BalanceTransferValidation extends MaintenanceDocumentRuleBase {
42  
43  	//the "to" and "from" accrual categories should be in the supplied principal's leave plan as of the effective date.
44  	private boolean validateLeavePlan(PrincipalHRAttributes pha,
45  			AccrualCategory fromAccrualCategory, AccrualCategory toAccrualCategory, Date effectiveDate) {
46  		boolean isValid = true;
47  		
48  		List<AccrualCategory> accrualCategories = TkServiceLocator.getAccrualCategoryService().getActiveAccrualCategoriesForLeavePlan(pha.getLeavePlan(), effectiveDate);
49  		if(accrualCategories.size() > 0) {
50  			boolean isFromInLeavePlan = false;
51  			boolean isToInLeavePlan = false;
52  			for(AccrualCategory activeAccrualCategory : accrualCategories) {
53  				if(StringUtils.equals(activeAccrualCategory.getLmAccrualCategoryId(),fromAccrualCategory.getLmAccrualCategoryId())) {
54  					isFromInLeavePlan = true;
55  				}
56  				if(StringUtils.equals(activeAccrualCategory.getLmAccrualCategoryId(), toAccrualCategory.getLmAccrualCategoryId())) {
57  					isToInLeavePlan = true;
58  				}
59  			}
60  			if(!isFromInLeavePlan) {
61  				GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory", "balanceTransfer.accrualCategory.notInLeavePlan", fromAccrualCategory.getAccrualCategory());
62  				isValid &= false;
63  			}
64  			if(!isToInLeavePlan) {
65  				GlobalVariables.getMessageMap().putError("document.newMaintainableObject.toAccrualCategory", "balanceTransfer.accrualCategory.notInLeavePlan", toAccrualCategory.getAccrualCategory());
66  				isValid &= false;			
67  			}
68  		}
69  		else {
70  			GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId", "balanceTransfer.principal.noACinLeavePlan");
71  			isValid &=false;
72  		}
73  		return isValid;
74  	}
75  	
76  	//See isTransferAmountUnderMaxLimit for futher validation
77  	private boolean validateTransferAmount(BigDecimal transferAmount,
78  			AccrualCategory debitedAccrualCategory,
79  			AccrualCategory creditedAccrualCategory, String principalId, Date effectiveDate) {
80  		
81  		if(transferAmount.compareTo(BigDecimal.ZERO) < 0 ) {
82  			GlobalVariables.getMessageMap().putError("document.newMaintainableObject.transferAmount", "balanceTransfer.amount.negative");
83  			return false;
84  		}
85  
86  		return true;
87  	}
88  
89  	//Effective date not more than one year in advance
90  	private boolean validateEffectiveDate(Date date) {
91  		if(DateUtils.addYears(TKUtils.getCurrentDate(), 1).compareTo(date) > 0)
92  			return true;
93  		else
94  			GlobalVariables.getMessageMap().putError("document.newMaintainableObject.effectiveDate", "balanceTransfer.effectiveDate.error");
95  		return false;
96  	}
97  	
98  	private boolean validateTransferFromAccrualCategory(AccrualCategory accrualCategory, String principalId,
99  			Date effectiveDate, AccrualCategoryRule acr) {
100 		//accrualCategory has rules
101 		//PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, effectiveDate);
102 		
103 		return true;
104 	}
105 	
106 	//Transfer to accrual category should match the value defined in the accrual category rule
107 	private boolean validateTransferToAccrualCategory(AccrualCategory accrualCategory, String principalId, Date effectiveDate, AccrualCategoryRule acr) {
108 		AccrualCategory maxBalTranToAccCat = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(acr.getMaxBalanceTransferToAccrualCategory(),effectiveDate);
109 		if(!StringUtils.equals(maxBalTranToAccCat.getLmAccrualCategoryId(),accrualCategory.getLmAccrualCategoryId())) {
110 			GlobalVariables.getMessageMap().putError("document.newMaintainableObject.toAccrualCategory", "balanceTransfer.toAccrualCategory.noMatch",accrualCategory.getAccrualCategory());
111 			return false;
112 		}
113 		return true;
114 	}
115 
116 	//no validation
117 	private boolean validatePrincipal(PrincipalHRAttributes pha, String principalId) {
118 		return true;
119 	}
120 	
121 	//transfer amount must be under max limit when submitted via max balance triggered action or by a work area approver.
122 	private boolean isTransferAmountUnderMaxLimit(String principalId, Date effectiveDate, String accrualCategory,
123 			BigDecimal transferAmount, AccrualCategoryRule accrualRule, String leavePlan) {
124 	
125 		if(ObjectUtils.isNotNull(accrualRule)) {
126 
127 			BigDecimal maxTransferAmount = null;
128 			if(ObjectUtils.isNotNull(accrualRule.getMaxTransferAmount())) {
129 				maxTransferAmount = new BigDecimal(accrualRule.getMaxTransferAmount());
130 			}
131 			if(ObjectUtils.isNotNull(maxTransferAmount)) {
132 				EmployeeOverride eo = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverride(principalId, leavePlan, accrualCategory, TkConstants.EMPLOYEE_OVERRIDE_TYPE.get("MTA"), effectiveDate);
133 				if(ObjectUtils.isNotNull(eo))
134 					if(ObjectUtils.isNull(eo.getOverrideValue()))
135 						maxTransferAmount = new BigDecimal(Long.MAX_VALUE);
136 					else
137 						maxTransferAmount = new BigDecimal(eo.getOverrideValue());
138 				else {
139 					BigDecimal fteSum = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, effectiveDate);
140 					maxTransferAmount = maxTransferAmount.multiply(fteSum);
141 				}
142 				if(transferAmount.compareTo(maxTransferAmount) > 0) {
143 					GlobalVariables.getMessageMap().putError("document.newMaintainableObject.transferAmount","balanceTransfer.exceeds.transferLimit");
144 					return false;
145 				}
146 			}
147 		}
148 		return true;
149 	}
150 	
151 	@Override
152 	public boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
153 		boolean isValid = super.processCustomRouteDocumentBusinessRules(document);
154 		LOG.debug("entering custom validation for Balance Transfer");
155 
156 		PersistableBusinessObject pbo = (PersistableBusinessObject) this.getNewBo();
157 
158 		if(pbo instanceof BalanceTransfer) {
159 
160 			BalanceTransfer balanceTransfer = (BalanceTransfer) pbo;
161 			
162 			// if this balance transfer is on a system scheduled time off, then don't do further validation
163 			if(StringUtils.isNotEmpty(balanceTransfer.getSstoId())) {
164 				isValid &= BalanceTransferValidationUtils.validateSstoTranser(balanceTransfer);
165 				return isValid;
166 			}
167 			if(isValid) {
168 
169 				/**
170 				 * Validation is basically governed by accrual category rules. Get accrual category
171 				 * rules for both the "To" and "From" accrual categories, pass to validators along with the
172 				 * values needing to be validated.
173 				 * 
174 				 * Balance transfers initiated from the leave calendar display should already have all values
175 				 * populated, thus validated, including the accrual category rule for the "From" accrual category.
176 				 * 
177 				 * Balance transfers initiated via the Maintenance tab will have no values populated.
178 				 */
179 				String principalId = balanceTransfer.getPrincipalId();
180 				Date effectiveDate = balanceTransfer.getEffectiveDate();
181 				String fromAccrualCategory = balanceTransfer.getFromAccrualCategory();
182 				String toAccrualCategory = balanceTransfer.getToAccrualCategory();
183 				AccrualCategory fromCat = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(fromAccrualCategory, effectiveDate);
184 				AccrualCategory toCat = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(toAccrualCategory, effectiveDate);
185 				PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId,effectiveDate);
186 				
187 				boolean isDeptAdmin = TKUser.isDepartmentAdmin();
188 				boolean isSysAdmin = TKUser.isSystemAdmin();
189 				if(isDeptAdmin || isSysAdmin) {
190 					isValid &= validateTransferAmount(balanceTransfer.getTransferAmount(),fromCat,toCat, principalId, effectiveDate);
191 				}
192 				else {
193 					if(ObjectUtils.isNotNull(pha)) {
194 						if(ObjectUtils.isNotNull(pha.getLeavePlan())) {
195 							AccrualCategoryRule acr = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRuleForDate(fromCat,
196 									effectiveDate, pha.getServiceDate());
197 							if(ObjectUtils.isNotNull(acr)) {
198 								if(StringUtils.isNotBlank(acr.getMaxBalFlag())
199 										&& StringUtils.equals(acr.getMaxBalFlag(), "Y")) {
200 									if(ObjectUtils.isNotNull(toCat)) {
201 										
202 										isValid &= validatePrincipal(pha,principalId);
203 										isValid &= validateEffectiveDate(effectiveDate);
204 										isValid &= validateLeavePlan(pha,fromCat,toCat,effectiveDate);
205 										isValid &= validateTransferFromAccrualCategory(fromCat,principalId,effectiveDate,acr);
206 										isValid &= validateTransferToAccrualCategory(toCat,principalId,effectiveDate,acr);
207 										isValid &= validateTransferAmount(balanceTransfer.getTransferAmount(),fromCat,toCat, null, null);
208 										isValid &= isTransferAmountUnderMaxLimit(principalId,effectiveDate,fromAccrualCategory,balanceTransfer.getTransferAmount(),acr,pha.getLeavePlan());
209 									}
210 									else {
211 										//should never be the case if accrual category rules are validated correctly.
212 										GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory",
213 												"balanceTransfer.fromAccrualCategory.rules.transferToAccrualCategory",
214 												fromAccrualCategory);
215 										isValid &= false;
216 									}
217 								}
218 								else {
219 									//max bal flag null, blank, empty, or "N"
220 									GlobalVariables.getMessageMap().putError("document.newMaintinableObject.fromAccrualCategory",
221 											"balanceTransfer.fromAccrualCategory.rules.maxBalFlag", fromAccrualCategory);
222 									isValid &= false;
223 								}
224 							}
225 							else {
226 								//department admins must validate amount to transfer does not exceed current balance.
227 								GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory",
228 										"balanceTransfer.fromAccrualCategory.rules.exist",fromCat.getAccrualCategory());
229 								isValid &= false;
230 							}
231 						}
232 						else {
233 							//if the principal doesn't have a leave plan, there aren't any accrual categories that can be debited/credited.
234 							GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId","balanceTransfer.principal.noLeavePlan");
235 							isValid &=false;
236 						}
237 					}
238 					else  {
239 						//if the principal has no principal hr attributes, they're not a principal.
240 						GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId","balanceTransfer.principal.noAttributes");
241 						isValid &= false;
242 					}
243 				}
244 			}
245 		}
246 		return isValid; 
247 	}
248 }