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.leavepayout.validation; 017 018 import java.math.BigDecimal; 019 import java.sql.Date; 020 import java.util.List; 021 022 import org.apache.commons.lang.StringUtils; 023 import org.apache.commons.lang.time.DateUtils; 024 import org.kuali.hr.lm.accrual.AccrualCategory; 025 import org.kuali.hr.lm.accrual.AccrualCategoryRule; 026 import org.kuali.hr.lm.employeeoverride.EmployeeOverride; 027 import org.kuali.hr.lm.leavepayout.LeavePayout; 028 import org.kuali.hr.time.earncode.EarnCode; 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 LeavePayoutValidation extends MaintenanceDocumentRuleBase { 042 043 private boolean validateAgainstLeavePlan(PrincipalHRAttributes pha, AccrualCategory fromAccrualCategory, Date effectiveDate) { 044 boolean isValid = true; 045 046 List<AccrualCategory> accrualCategories = TkServiceLocator.getAccrualCategoryService().getActiveAccrualCategoriesForLeavePlan(pha.getLeavePlan(), effectiveDate); 047 if(accrualCategories.size() > 0) { 048 boolean isFromInLeavePlan = false; 049 for(AccrualCategory activeAccrualCategory : accrualCategories) { 050 if(StringUtils.equals(activeAccrualCategory.getLmAccrualCategoryId(),fromAccrualCategory.getLmAccrualCategoryId())) { 051 isFromInLeavePlan = true; 052 } 053 } 054 if(!isFromInLeavePlan) { 055 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory", "leavePayout.accrualCategory.notInLeavePlan", fromAccrualCategory.getAccrualCategory()); 056 isValid &= false; 057 } 058 } 059 else { 060 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId", "leavePayout.principal.noACinLeavePlan"); 061 isValid &=false; 062 } 063 064 return isValid; 065 } 066 067 //Employee Overrides??? 068 /** 069 * Transfer amount could be validated against several variables, including max transfer amount, 070 * max carry over ( for the converted amount deposited into the "to" accrual category, max usage 071 * ( if transfers count as usage ). 072 * @param transferAmount 073 * @param debitedAccrualCategory 074 * @param creditedAccrualCategory 075 * @param principalId TODO 076 * @param effectiveDate TODO 077 * @return true if transfer amount is valid 078 */ 079 private boolean validatePayoutAmount(BigDecimal transferAmount, 080 AccrualCategory debitedAccrualCategory, 081 EarnCode payoutEarnCode, String principalId, Date effectiveDate) { 082 083 if(transferAmount.compareTo(BigDecimal.ZERO) < 0 ) { 084 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.transferAmount", "leavePayout.amount.negative"); 085 return false; 086 } 087 //TkServiceLocator.getAccrualCategoryService().getCurrentBalanceForPrincipal(principalId, debitedAccrualCategory, effectiveDate); 088 089 return true; 090 } 091 092 /** 093 * Are there any rules in place for effective date? i.e. not more than one year in advance... 094 * @param date 095 * @return 096 */ 097 private boolean validateEffectiveDate(Date date) { 098 //Limit on future dates? 099 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 }