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 }