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.TKUtils;
033    import org.kuali.hr.time.util.TkConstants;
034    import org.kuali.rice.kns.document.MaintenanceDocument;
035    import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase;
036    import org.kuali.rice.krad.bo.PersistableBusinessObject;
037    import org.kuali.rice.krad.util.GlobalVariables;
038    import org.kuali.rice.krad.util.ObjectUtils;
039    
040    public class BalanceTransferValidation extends MaintenanceDocumentRuleBase {
041    
042            //the "to" and "from" accrual categories should be in the supplied principal's leave plan as of the effective date.
043            private boolean validateLeavePlan(PrincipalHRAttributes pha,
044                            AccrualCategory fromAccrualCategory, AccrualCategory toAccrualCategory, Date effectiveDate) {
045                    boolean isValid = true;
046                    
047                    List<AccrualCategory> accrualCategories = TkServiceLocator.getAccrualCategoryService().getActiveAccrualCategoriesForLeavePlan(pha.getLeavePlan(), effectiveDate);
048                    if(accrualCategories.size() > 0) {
049                            boolean isFromInLeavePlan = false;
050                            boolean isToInLeavePlan = false;
051                            for(AccrualCategory activeAccrualCategory : accrualCategories) {
052                                    if(StringUtils.equals(activeAccrualCategory.getLmAccrualCategoryId(),fromAccrualCategory.getLmAccrualCategoryId())) {
053                                            isFromInLeavePlan = true;
054                                    }
055                                    if(StringUtils.equals(activeAccrualCategory.getLmAccrualCategoryId(), toAccrualCategory.getLmAccrualCategoryId())) {
056                                            isToInLeavePlan = true;
057                                    }
058                            }
059                            if(!isFromInLeavePlan) {
060                                    GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory", "balanceTransfer.accrualCategory.notInLeavePlan", fromAccrualCategory.getAccrualCategory());
061                                    isValid &= false;
062                            }
063                            if(!isToInLeavePlan) {
064                                    GlobalVariables.getMessageMap().putError("document.newMaintainableObject.toAccrualCategory", "balanceTransfer.accrualCategory.notInLeavePlan", toAccrualCategory.getAccrualCategory());
065                                    isValid &= false;                   
066                            }
067                    }
068                    else {
069                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId", "balanceTransfer.principal.noACinLeavePlan");
070                            isValid &=false;
071                    }
072                    return isValid;
073            }
074            
075            //See isTransferAmountUnderMaxLimit for futher validation
076            private boolean validateTransferAmount(BigDecimal transferAmount,
077                            AccrualCategory debitedAccrualCategory,
078                            AccrualCategory creditedAccrualCategory, String principalId, Date effectiveDate) {
079                    
080                    if(transferAmount.compareTo(BigDecimal.ZERO) < 0 ) {
081                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.transferAmount", "balanceTransfer.amount.negative");
082                            return false;
083                    }
084    
085                    return true;
086            }
087    
088            //Effective date not more than one year in advance
089            private boolean validateEffectiveDate(Date date) {
090                    if(DateUtils.addYears(TKUtils.getCurrentDate(), 1).compareTo(date) > 0)
091                            return true;
092                    else
093                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.effectiveDate", "balanceTransfer.effectiveDate.error");
094                    return false;
095            }
096            
097            private boolean validateTransferFromAccrualCategory(AccrualCategory accrualCategory, String principalId,
098                            Date effectiveDate, AccrualCategoryRule acr) {
099                    //accrualCategory has rules
100                    PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, effectiveDate);
101                    
102                    return true;
103            }
104            
105            //Transfer to accrual category should match the value defined in the accrual category rule
106            private boolean validateTransferToAccrualCategory(AccrualCategory accrualCategory, String principalId, Date effectiveDate, AccrualCategoryRule acr) {
107                    AccrualCategory maxBalTranToAccCat = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(acr.getMaxBalanceTransferToAccrualCategory(),effectiveDate);
108                    if(!StringUtils.equals(maxBalTranToAccCat.getLmAccrualCategoryId(),accrualCategory.getLmAccrualCategoryId())) {
109                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.toAccrualCategory", "balanceTransfer.toAccrualCategory.noMatch",accrualCategory.getAccrualCategory());
110                            return false;
111                    }
112                    return true;
113            }
114    
115            //no validation
116            private boolean validatePrincipal(PrincipalHRAttributes pha, String principalId) {
117                    return true;
118            }
119            
120            //transfer amount must be under max limit when submitted via max balance triggered action or by a work area approver.
121            private boolean isTransferAmountUnderMaxLimit(String principalId, Date effectiveDate, String accrualCategory,
122                            BigDecimal transferAmount, AccrualCategoryRule accrualRule, String leavePlan) {
123            
124                    if(ObjectUtils.isNotNull(accrualRule)) {
125    
126                            BigDecimal maxTransferAmount = null;
127                            if(ObjectUtils.isNotNull(accrualRule.getMaxTransferAmount())) {
128                                    maxTransferAmount = new BigDecimal(accrualRule.getMaxTransferAmount());
129                            }
130                            if(ObjectUtils.isNotNull(maxTransferAmount)) {
131                                    EmployeeOverride eo = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverride(principalId, leavePlan, accrualCategory, TkConstants.EMPLOYEE_OVERRIDE_TYPE.get("MTA"), effectiveDate);
132                                    if(ObjectUtils.isNotNull(eo))
133                                            maxTransferAmount = new BigDecimal(eo.getOverrideValue());
134                                    else {
135                                            BigDecimal fteSum = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, effectiveDate);
136                                            maxTransferAmount = maxTransferAmount.multiply(fteSum);
137                                    }
138                                    if(transferAmount.compareTo(maxTransferAmount) > 0) {
139                                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.transferAmount","balanceTransfer.exceeds.transferLimit");
140                                            return false;
141                                    }
142                            }
143                    }
144                    return true;
145            }
146            
147            @Override
148            public boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
149                    boolean isValid = super.processCustomRouteDocumentBusinessRules(document);
150                    LOG.debug("entering custom validation for Balance Transfer");
151    
152                    PersistableBusinessObject pbo = (PersistableBusinessObject) this.getNewBo();
153    
154                    if(pbo instanceof BalanceTransfer) {
155    
156                            BalanceTransfer balanceTransfer = (BalanceTransfer) pbo;
157                            
158                            // if this balance transfer is on a system scheduled time off, then don't do further validation
159                            if(StringUtils.isNotEmpty(balanceTransfer.getSstoId())) {
160                                    isValid &= BalanceTransferValidationUtils.validateSstoTranser(balanceTransfer);
161                                    return isValid;
162                            }
163                            if(isValid) {
164    
165                                    /**
166                                     * Validation is basically governed by accrual category rules. Get accrual category
167                                     * rules for both the "To" and "From" accrual categories, pass to validators along with the
168                                     * values needing to be validated.
169                                     * 
170                                     * Balance transfers initiated from the leave calendar display should already have all values
171                                     * populated, thus validated, including the accrual category rule for the "From" accrual category.
172                                     * 
173                                     * Balance transfers initiated via the Maintenance tab will have no values populated.
174                                     */
175                                    String principalId = balanceTransfer.getPrincipalId();
176                                    Date effectiveDate = balanceTransfer.getEffectiveDate();
177                                    String fromAccrualCategory = balanceTransfer.getFromAccrualCategory();
178                                    String toAccrualCategory = balanceTransfer.getToAccrualCategory();
179                                    AccrualCategory fromCat = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(fromAccrualCategory, effectiveDate);
180                                    AccrualCategory toCat = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(toAccrualCategory, effectiveDate);
181                                    PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId,effectiveDate);
182                                    
183                                    boolean isDeptAdmin = TKContext.getUser().isDepartmentAdmin();
184                                    boolean isSysAdmin = TKContext.getUser().isSystemAdmin();
185                                    if(isDeptAdmin || isSysAdmin) {
186                                            isValid &= validateTransferAmount(balanceTransfer.getTransferAmount(),fromCat,toCat, principalId, effectiveDate);
187                                    }
188                                    else {
189                                            if(ObjectUtils.isNotNull(pha)) {
190                                                    if(ObjectUtils.isNotNull(pha.getLeavePlan())) {
191                                                            AccrualCategoryRule acr = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRuleForDate(fromCat,
192                                                                            effectiveDate, pha.getServiceDate());
193                                                            if(ObjectUtils.isNotNull(acr)) {
194                                                                    if(StringUtils.isNotBlank(acr.getMaxBalFlag())
195                                                                                    && StringUtils.equals(acr.getMaxBalFlag(), "Y")) {
196                                                                            if(ObjectUtils.isNotNull(toCat)) {
197                                                                                    
198                                                                                    isValid &= validatePrincipal(pha,principalId);
199                                                                                    isValid &= validateEffectiveDate(effectiveDate);
200                                                                                    isValid &= validateLeavePlan(pha,fromCat,toCat,effectiveDate);
201                                                                                    isValid &= validateTransferFromAccrualCategory(fromCat,principalId,effectiveDate,acr);
202                                                                                    isValid &= validateTransferToAccrualCategory(toCat,principalId,effectiveDate,acr);
203                                                                                    isValid &= validateTransferAmount(balanceTransfer.getTransferAmount(),fromCat,toCat, null, null);
204                                                                                    isValid &= isTransferAmountUnderMaxLimit(principalId,effectiveDate,fromAccrualCategory,balanceTransfer.getTransferAmount(),acr,pha.getLeavePlan());
205                                                                            }
206                                                                            else {
207                                                                                    //should never be the case if accrual category rules are validated correctly.
208                                                                                    GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory",
209                                                                                                    "balanceTransfer.fromAccrualCategory.rules.transferToAccrualCategory",
210                                                                                                    fromAccrualCategory);
211                                                                                    isValid &= false;
212                                                                            }
213                                                                    }
214                                                                    else {
215                                                                            //max bal flag null, blank, empty, or "N"
216                                                                            GlobalVariables.getMessageMap().putError("document.newMaintinableObject.fromAccrualCategory",
217                                                                                            "balanceTransfer.fromAccrualCategory.rules.maxBalFlag", fromAccrualCategory);
218                                                                            isValid &= false;
219                                                                    }
220                                                            }
221                                                            else {
222                                                                    //department admins must validate amount to transfer does not exceed current balance.
223                                                                    GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory",
224                                                                                    "balanceTransfer.fromAccrualCategory.rules.exist",fromCat.getAccrualCategory());
225                                                                    isValid &= false;
226                                                            }
227                                                    }
228                                                    else {
229                                                            //if the principal doesn't have a leave plan, there aren't any accrual categories that can be debited/credited.
230                                                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId","balanceTransfer.principal.noLeavePlan");
231                                                            isValid &=false;
232                                                    }
233                                            }
234                                            else  {
235                                                    //if the principal has no principal hr attributes, they're not a principal.
236                                                    GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId","balanceTransfer.principal.noAttributes");
237                                                    isValid &= false;
238                                            }
239                                    }
240                            }
241                    }
242                    return isValid; 
243            }
244            
245            @Override
246            protected boolean processCustomApproveDocumentBusinessRules(
247                            MaintenanceDocument document) {
248    /*              System.out.println("");*/
249                    return super.processCustomApproveDocumentBusinessRules(document);
250            }
251    
252    }