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.leavepayout.LeavePayout; 027 import org.kuali.hr.time.earncode.EarnCode; 028 import org.kuali.hr.time.principal.PrincipalHRAttributes; 029 import org.kuali.hr.time.service.base.TkServiceLocator; 030 import org.kuali.hr.time.util.TKContext; 031 import org.kuali.hr.time.util.TKUtils; 032 import org.kuali.rice.kns.document.MaintenanceDocument; 033 import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase; 034 import org.kuali.rice.krad.bo.PersistableBusinessObject; 035 import org.kuali.rice.krad.util.GlobalVariables; 036 import org.kuali.rice.krad.util.ObjectUtils; 037 038 public class LeavePayoutValidation extends MaintenanceDocumentRuleBase { 039 040 private boolean validateAgainstLeavePlan(PrincipalHRAttributes pha, AccrualCategory fromAccrualCategory, Date effectiveDate) { 041 boolean isValid = true; 042 043 List<AccrualCategory> accrualCategories = TkServiceLocator.getAccrualCategoryService().getActiveAccrualCategoriesForLeavePlan(pha.getLeavePlan(), effectiveDate); 044 if(accrualCategories.size() > 0) { 045 boolean isFromInLeavePlan = false; 046 for(AccrualCategory activeAccrualCategory : accrualCategories) { 047 if(StringUtils.equals(activeAccrualCategory.getLmAccrualCategoryId(),fromAccrualCategory.getLmAccrualCategoryId())) { 048 isFromInLeavePlan = true; 049 } 050 } 051 if(!isFromInLeavePlan) { 052 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory", "leavePayout.accrualCategory.notInLeavePlan", fromAccrualCategory.getAccrualCategory()); 053 isValid &= false; 054 } 055 } 056 else { 057 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId", "leavePayout.principal.noACinLeavePlan"); 058 isValid &=false; 059 } 060 061 return isValid; 062 } 063 064 //Employee Overrides??? 065 /** 066 * Transfer amount must be validated against several variables, including max transfer amount, 067 * max carry over ( for the converted amount deposited into the "to" accrual category, max usage 068 * ( if transfers count as usage ). 069 * @param transferAmount 070 * @param debitedAccrualCategory 071 * @param creditedAccrualCategory 072 * @param principalId TODO 073 * @param effectiveDate TODO 074 * @return true if transfer amount is valid 075 */ 076 private boolean validatePayoutAmount(BigDecimal transferAmount, 077 AccrualCategory debitedAccrualCategory, 078 EarnCode payoutEarnCode, String principalId, Date effectiveDate) { 079 080 if(transferAmount.compareTo(BigDecimal.ZERO) < 0 ) { 081 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.transferAmount", "leavePayout.amount.negative"); 082 return false; 083 } 084 //TkServiceLocator.getAccrualCategoryService().getCurrentBalanceForPrincipal(principalId, debitedAccrualCategory, effectiveDate); 085 086 return true; 087 } 088 089 /** 090 * Are there any rules in place for effective date? i.e. not more than one year in advance... 091 * @param date 092 * @return 093 */ 094 private boolean validateEffectiveDate(Date date) { 095 //Limit on future dates? 096 if(date.getTime() > DateUtils.addYears(TKUtils.getCurrentDate(), 1).getTime()) { 097 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.effectiveDate", "leavePayout.effectiveDate.overOneYear"); 098 return false; 099 } 100 return true; 101 } 102 103 /** 104 * Is the "From" accrual category required to be over its maximum balance before a transfer can take place? 105 * The "From" accrual category must be defined in an accrual category rule as having a max bal rule. 106 * @param accrualCategory 107 * @param effectiveDate 108 * @param principalId 109 * @param acr 110 * @return 111 */ 112 private boolean validateTransferFromAccrualCategory(AccrualCategory accrualCategory, String principalId, 113 Date effectiveDate, AccrualCategoryRule acr) { 114 //accrualCategory has rules 115 PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, effectiveDate); 116 117 return true; 118 } 119 120 private boolean validatePrincipal(PrincipalHRAttributes pha, String principalId) { 121 // TODO Auto-generated method stub 122 // Principal has to have an associated leave plan. 123 // leave plan should contain the transfer "from" accrual category. 124 125 return true; 126 } 127 128 /** 129 * Is not called when saving a document, but is called on submit. 130 * 131 * The branched logic in this method contains references to accrual category rule properties 132 * that, when balance transfer is triggered through system interaction, would naturally have 133 * been checked in the process of triggering the balance transfer. i.e. existence of accrual 134 * category rule, maxBalFlag = Y, leave plan for principal id existence and accrual category 135 * existence under that leave plan. The context in which the balance transfers are triggered 136 * would themselves validate these checks. 137 * 138 */ 139 @Override 140 public boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) { 141 boolean isValid = true; 142 LOG.debug("entering custom validation for Balance Transfer"); 143 144 PersistableBusinessObject pbo = (PersistableBusinessObject) this.getNewBo(); 145 146 if(pbo instanceof LeavePayout) { 147 148 LeavePayout leavePayout = (LeavePayout) pbo; 149 150 /** 151 * Validation is basically governed by accrual category rules. Get accrual category 152 * rules for both the "To" and "From" accrual categories, pass to validators along with the 153 * values needing to be validated. 154 * 155 * Balance transfers initiated from the leave calendar display should already have all values 156 * populated, thus validated, including the accrual category rule for the "From" accrual category. 157 * 158 * Balance transfers initiated via the Maintenance tab will have no values populated. 159 */ 160 String principalId = leavePayout.getPrincipalId(); 161 Date effectiveDate = leavePayout.getEffectiveDate(); 162 String fromAccrualCategory = leavePayout.getFromAccrualCategory(); 163 EarnCode payoutEarnCode = leavePayout.getEarnCodeObj(); 164 AccrualCategory fromCat = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(fromAccrualCategory, effectiveDate); 165 PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId,effectiveDate); 166 167 boolean isDeptAdmin = TKContext.getUser().isDepartmentAdmin(); 168 boolean isSysAdmin = TKContext.getUser().isSystemAdmin(); 169 if(isDeptAdmin || isSysAdmin) { 170 isValid &= validatePayoutAmount(leavePayout.getPayoutAmount(),fromCat,payoutEarnCode, principalId, effectiveDate); 171 } 172 else { 173 if(ObjectUtils.isNotNull(pha)) { 174 if(ObjectUtils.isNotNull(pha.getLeavePlan())) { 175 AccrualCategoryRule acr = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRuleForDate(fromCat, effectiveDate, pha.getServiceDate()); 176 if(ObjectUtils.isNotNull(acr)) { 177 if(ObjectUtils.isNotNull(acr.getMaxBalFlag()) 178 && StringUtils.isNotBlank(acr.getMaxBalFlag()) 179 && StringUtils.isNotEmpty(acr.getMaxBalFlag()) 180 && StringUtils.equals(acr.getMaxBalFlag(), "Y")) { 181 if(ObjectUtils.isNotNull(payoutEarnCode)) { 182 isValid &= validatePrincipal(pha,principalId); 183 isValid &= validateEffectiveDate(effectiveDate); 184 isValid &= validateAgainstLeavePlan(pha,fromCat,effectiveDate); 185 isValid &= validateTransferFromAccrualCategory(fromCat,principalId,effectiveDate,acr); 186 isValid &= validatePayoutAmount(leavePayout.getPayoutAmount(),fromCat,payoutEarnCode, null, null); 187 } 188 else { 189 //should never be the case if accrual category rules are validated correctly. 190 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory", 191 "leavePayout.fromAccrualCategory.rules.payoutToEarnCode", 192 fromAccrualCategory); 193 isValid &= false; 194 } 195 } 196 else { 197 //max bal flag null, blank, empty, or "N" 198 GlobalVariables.getMessageMap().putError("document.newMaintinableObject.fromAccrualCategory", 199 "leavePayout.fromAccrualCategory.rules.maxBalFlag", fromAccrualCategory); 200 isValid &= false; 201 } 202 } 203 else { 204 //department admins must validate amount to transfer does not exceed current balance. 205 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory", 206 "leavePayout.fromAccrualCategory.rules.exist",fromCat.getAccrualCategory()); 207 isValid &= false; 208 } 209 } 210 else { 211 //if the principal doesn't have a leave plan, there aren't any accrual categories that can be debited/credited. 212 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId","leavePayout.principal.noLeavePlan"); 213 isValid &=false; 214 } 215 } 216 else { 217 //if the principal has no principal hr attributes, they're not a principal. 218 GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId","leavePayout.principal.noAttributes"); 219 isValid &= false; 220 } 221 } 222 } 223 return isValid; 224 } 225 226 @Override 227 protected boolean processCustomApproveDocumentBusinessRules( 228 MaintenanceDocument document) { 229 /* System.out.println("");*/ 230 return super.processCustomApproveDocumentBusinessRules(document); 231 } 232 233 }