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.balancetransfer.validation;
017
018 import java.math.BigDecimal;
019 import java.sql.Date;
020 import java.util.Calendar;
021 import java.util.List;
022
023 import org.apache.commons.lang.StringUtils;
024 import org.apache.commons.lang.time.DateUtils;
025 import org.kuali.hr.lm.accrual.AccrualCategory;
026 import org.kuali.hr.lm.accrual.AccrualCategoryRule;
027 import org.kuali.hr.lm.balancetransfer.BalanceTransfer;
028 import org.kuali.hr.lm.employeeoverride.EmployeeOverride;
029 import org.kuali.hr.time.principal.PrincipalHRAttributes;
030 import org.kuali.hr.time.service.base.TkServiceLocator;
031 import org.kuali.hr.time.util.TKContext;
032 import org.kuali.hr.time.util.TKUtils;
033 import org.kuali.hr.time.util.TkConstants;
034 import org.kuali.rice.kns.document.MaintenanceDocument;
035 import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase;
036 import org.kuali.rice.krad.bo.PersistableBusinessObject;
037 import org.kuali.rice.krad.util.GlobalVariables;
038 import org.kuali.rice.krad.util.ObjectUtils;
039
040 public class BalanceTransferValidation extends MaintenanceDocumentRuleBase {
041
042 //the "to" and "from" accrual categories should be in the supplied principal's leave plan as of the effective date.
043 private boolean validateLeavePlan(PrincipalHRAttributes pha,
044 AccrualCategory fromAccrualCategory, AccrualCategory toAccrualCategory, Date effectiveDate) {
045 boolean isValid = true;
046
047 List<AccrualCategory> accrualCategories = TkServiceLocator.getAccrualCategoryService().getActiveAccrualCategoriesForLeavePlan(pha.getLeavePlan(), effectiveDate);
048 if(accrualCategories.size() > 0) {
049 boolean isFromInLeavePlan = false;
050 boolean isToInLeavePlan = false;
051 for(AccrualCategory activeAccrualCategory : accrualCategories) {
052 if(StringUtils.equals(activeAccrualCategory.getLmAccrualCategoryId(),fromAccrualCategory.getLmAccrualCategoryId())) {
053 isFromInLeavePlan = true;
054 }
055 if(StringUtils.equals(activeAccrualCategory.getLmAccrualCategoryId(), toAccrualCategory.getLmAccrualCategoryId())) {
056 isToInLeavePlan = true;
057 }
058 }
059 if(!isFromInLeavePlan) {
060 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory", "balanceTransfer.accrualCategory.notInLeavePlan", fromAccrualCategory.getAccrualCategory());
061 isValid &= false;
062 }
063 if(!isToInLeavePlan) {
064 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.toAccrualCategory", "balanceTransfer.accrualCategory.notInLeavePlan", toAccrualCategory.getAccrualCategory());
065 isValid &= false;
066 }
067 }
068 else {
069 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId", "balanceTransfer.principal.noACinLeavePlan");
070 isValid &=false;
071 }
072 return isValid;
073 }
074
075 //See isTransferAmountUnderMaxLimit for futher validation
076 private boolean validateTransferAmount(BigDecimal transferAmount,
077 AccrualCategory debitedAccrualCategory,
078 AccrualCategory creditedAccrualCategory, String principalId, Date effectiveDate) {
079
080 if(transferAmount.compareTo(BigDecimal.ZERO) < 0 ) {
081 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.transferAmount", "balanceTransfer.amount.negative");
082 return false;
083 }
084
085 return true;
086 }
087
088 //Effective date not more than one year in advance
089 private boolean validateEffectiveDate(Date date) {
090 if(DateUtils.addYears(TKUtils.getCurrentDate(), 1).compareTo(date) > 0)
091 return true;
092 else
093 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.effectiveDate", "balanceTransfer.effectiveDate.error");
094 return false;
095 }
096
097 private boolean validateTransferFromAccrualCategory(AccrualCategory accrualCategory, String principalId,
098 Date effectiveDate, AccrualCategoryRule acr) {
099 //accrualCategory has rules
100 PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, effectiveDate);
101
102 return true;
103 }
104
105 //Transfer to accrual category should match the value defined in the accrual category rule
106 private boolean validateTransferToAccrualCategory(AccrualCategory accrualCategory, String principalId, Date effectiveDate, AccrualCategoryRule acr) {
107 AccrualCategory maxBalTranToAccCat = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(acr.getMaxBalanceTransferToAccrualCategory(),effectiveDate);
108 if(!StringUtils.equals(maxBalTranToAccCat.getLmAccrualCategoryId(),accrualCategory.getLmAccrualCategoryId())) {
109 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.toAccrualCategory", "balanceTransfer.toAccrualCategory.noMatch",accrualCategory.getAccrualCategory());
110 return false;
111 }
112 return true;
113 }
114
115 //no validation
116 private boolean validatePrincipal(PrincipalHRAttributes pha, String principalId) {
117 return true;
118 }
119
120 //transfer amount must be under max limit when submitted via max balance triggered action or by a work area approver.
121 private boolean isTransferAmountUnderMaxLimit(String principalId, Date effectiveDate, String accrualCategory,
122 BigDecimal transferAmount, AccrualCategoryRule accrualRule, String leavePlan) {
123
124 if(ObjectUtils.isNotNull(accrualRule)) {
125
126 BigDecimal maxTransferAmount = null;
127 if(ObjectUtils.isNotNull(accrualRule.getMaxTransferAmount())) {
128 maxTransferAmount = new BigDecimal(accrualRule.getMaxTransferAmount());
129 }
130 if(ObjectUtils.isNotNull(maxTransferAmount)) {
131 EmployeeOverride eo = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverride(principalId, leavePlan, accrualCategory, TkConstants.EMPLOYEE_OVERRIDE_TYPE.get("MTA"), effectiveDate);
132 if(ObjectUtils.isNotNull(eo))
133 maxTransferAmount = new BigDecimal(eo.getOverrideValue());
134 else {
135 BigDecimal fteSum = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, effectiveDate);
136 maxTransferAmount = maxTransferAmount.multiply(fteSum);
137 }
138 if(transferAmount.compareTo(maxTransferAmount) > 0) {
139 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.transferAmount","balanceTransfer.exceeds.transferLimit");
140 return false;
141 }
142 }
143 }
144 return true;
145 }
146
147 @Override
148 public boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
149 boolean isValid = super.processCustomRouteDocumentBusinessRules(document);
150 LOG.debug("entering custom validation for Balance Transfer");
151
152 PersistableBusinessObject pbo = (PersistableBusinessObject) this.getNewBo();
153
154 if(pbo instanceof BalanceTransfer) {
155
156 BalanceTransfer balanceTransfer = (BalanceTransfer) pbo;
157
158 // if this balance transfer is on a system scheduled time off, then don't do further validation
159 if(StringUtils.isNotEmpty(balanceTransfer.getSstoId())) {
160 isValid &= BalanceTransferValidationUtils.validateSstoTranser(balanceTransfer);
161 return isValid;
162 }
163 if(isValid) {
164
165 /**
166 * Validation is basically governed by accrual category rules. Get accrual category
167 * rules for both the "To" and "From" accrual categories, pass to validators along with the
168 * values needing to be validated.
169 *
170 * Balance transfers initiated from the leave calendar display should already have all values
171 * populated, thus validated, including the accrual category rule for the "From" accrual category.
172 *
173 * Balance transfers initiated via the Maintenance tab will have no values populated.
174 */
175 String principalId = balanceTransfer.getPrincipalId();
176 Date effectiveDate = balanceTransfer.getEffectiveDate();
177 String fromAccrualCategory = balanceTransfer.getFromAccrualCategory();
178 String toAccrualCategory = balanceTransfer.getToAccrualCategory();
179 AccrualCategory fromCat = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(fromAccrualCategory, effectiveDate);
180 AccrualCategory toCat = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(toAccrualCategory, effectiveDate);
181 PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId,effectiveDate);
182
183 boolean isDeptAdmin = TKContext.getUser().isDepartmentAdmin();
184 boolean isSysAdmin = TKContext.getUser().isSystemAdmin();
185 if(isDeptAdmin || isSysAdmin) {
186 isValid &= validateTransferAmount(balanceTransfer.getTransferAmount(),fromCat,toCat, principalId, effectiveDate);
187 }
188 else {
189 if(ObjectUtils.isNotNull(pha)) {
190 if(ObjectUtils.isNotNull(pha.getLeavePlan())) {
191 AccrualCategoryRule acr = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRuleForDate(fromCat,
192 effectiveDate, pha.getServiceDate());
193 if(ObjectUtils.isNotNull(acr)) {
194 if(StringUtils.isNotBlank(acr.getMaxBalFlag())
195 && StringUtils.equals(acr.getMaxBalFlag(), "Y")) {
196 if(ObjectUtils.isNotNull(toCat)) {
197
198 isValid &= validatePrincipal(pha,principalId);
199 isValid &= validateEffectiveDate(effectiveDate);
200 isValid &= validateLeavePlan(pha,fromCat,toCat,effectiveDate);
201 isValid &= validateTransferFromAccrualCategory(fromCat,principalId,effectiveDate,acr);
202 isValid &= validateTransferToAccrualCategory(toCat,principalId,effectiveDate,acr);
203 isValid &= validateTransferAmount(balanceTransfer.getTransferAmount(),fromCat,toCat, null, null);
204 isValid &= isTransferAmountUnderMaxLimit(principalId,effectiveDate,fromAccrualCategory,balanceTransfer.getTransferAmount(),acr,pha.getLeavePlan());
205 }
206 else {
207 //should never be the case if accrual category rules are validated correctly.
208 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory",
209 "balanceTransfer.fromAccrualCategory.rules.transferToAccrualCategory",
210 fromAccrualCategory);
211 isValid &= false;
212 }
213 }
214 else {
215 //max bal flag null, blank, empty, or "N"
216 GlobalVariables.getMessageMap().putError("document.newMaintinableObject.fromAccrualCategory",
217 "balanceTransfer.fromAccrualCategory.rules.maxBalFlag", fromAccrualCategory);
218 isValid &= false;
219 }
220 }
221 else {
222 //department admins must validate amount to transfer does not exceed current balance.
223 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory",
224 "balanceTransfer.fromAccrualCategory.rules.exist",fromCat.getAccrualCategory());
225 isValid &= false;
226 }
227 }
228 else {
229 //if the principal doesn't have a leave plan, there aren't any accrual categories that can be debited/credited.
230 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId","balanceTransfer.principal.noLeavePlan");
231 isValid &=false;
232 }
233 }
234 else {
235 //if the principal has no principal hr attributes, they're not a principal.
236 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId","balanceTransfer.principal.noAttributes");
237 isValid &= false;
238 }
239 }
240 }
241 }
242 return isValid;
243 }
244
245 @Override
246 protected boolean processCustomApproveDocumentBusinessRules(
247 MaintenanceDocument document) {
248 /* System.out.println("");*/
249 return super.processCustomApproveDocumentBusinessRules(document);
250 }
251
252 }