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.TKUser; 033 import org.kuali.hr.time.util.TKUtils; 034 import org.kuali.hr.time.util.TkConstants; 035 import org.kuali.rice.kns.document.MaintenanceDocument; 036 import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase; 037 import org.kuali.rice.krad.bo.PersistableBusinessObject; 038 import org.kuali.rice.krad.util.GlobalVariables; 039 import org.kuali.rice.krad.util.ObjectUtils; 040 041 public class BalanceTransferValidation extends MaintenanceDocumentRuleBase { 042 043 //the "to" and "from" accrual categories should be in the supplied principal's leave plan as of the effective date. 044 private boolean validateLeavePlan(PrincipalHRAttributes pha, 045 AccrualCategory fromAccrualCategory, AccrualCategory toAccrualCategory, Date effectiveDate) { 046 boolean isValid = true; 047 048 List<AccrualCategory> accrualCategories = TkServiceLocator.getAccrualCategoryService().getActiveAccrualCategoriesForLeavePlan(pha.getLeavePlan(), effectiveDate); 049 if(accrualCategories.size() > 0) { 050 boolean isFromInLeavePlan = false; 051 boolean isToInLeavePlan = false; 052 for(AccrualCategory activeAccrualCategory : accrualCategories) { 053 if(StringUtils.equals(activeAccrualCategory.getLmAccrualCategoryId(),fromAccrualCategory.getLmAccrualCategoryId())) { 054 isFromInLeavePlan = true; 055 } 056 if(StringUtils.equals(activeAccrualCategory.getLmAccrualCategoryId(), toAccrualCategory.getLmAccrualCategoryId())) { 057 isToInLeavePlan = true; 058 } 059 } 060 if(!isFromInLeavePlan) { 061 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory", "balanceTransfer.accrualCategory.notInLeavePlan", fromAccrualCategory.getAccrualCategory()); 062 isValid &= false; 063 } 064 if(!isToInLeavePlan) { 065 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.toAccrualCategory", "balanceTransfer.accrualCategory.notInLeavePlan", toAccrualCategory.getAccrualCategory()); 066 isValid &= false; 067 } 068 } 069 else { 070 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId", "balanceTransfer.principal.noACinLeavePlan"); 071 isValid &=false; 072 } 073 return isValid; 074 } 075 076 //See isTransferAmountUnderMaxLimit for futher validation 077 private boolean validateTransferAmount(BigDecimal transferAmount, 078 AccrualCategory debitedAccrualCategory, 079 AccrualCategory creditedAccrualCategory, String principalId, Date effectiveDate) { 080 081 if(transferAmount.compareTo(BigDecimal.ZERO) < 0 ) { 082 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.transferAmount", "balanceTransfer.amount.negative"); 083 return false; 084 } 085 086 return true; 087 } 088 089 //Effective date not more than one year in advance 090 private boolean validateEffectiveDate(Date date) { 091 if(DateUtils.addYears(TKUtils.getCurrentDate(), 1).compareTo(date) > 0) 092 return true; 093 else 094 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.effectiveDate", "balanceTransfer.effectiveDate.error"); 095 return false; 096 } 097 098 private boolean validateTransferFromAccrualCategory(AccrualCategory accrualCategory, String principalId, 099 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 }