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    }