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.kuali.hr.lm.LMConstants;
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.lm.employeeoverride.EmployeeOverride;
028    import org.kuali.hr.time.principal.PrincipalHRAttributes;
029    import org.kuali.hr.time.service.base.TkServiceLocator;
030    import org.kuali.hr.time.util.TKUtils;
031    import org.kuali.hr.time.util.TkConstants;
032    import org.kuali.rice.krad.util.GlobalVariables;
033    import org.kuali.rice.krad.util.ObjectUtils;
034    
035    public class LeavePayoutValidationUtils {
036    
037            public static boolean validatePayout(LeavePayout leavePayout) {
038                    boolean isValid = true;
039                    String principalId = leavePayout.getPrincipalId();
040                    Date effectiveDate = leavePayout.getEffectiveDate();
041                    String fromAccrualCategory = leavePayout.getFromAccrualCategory();
042                    String payoutEarnCode = leavePayout.getEarnCode();
043                    AccrualCategory fromCat = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(fromAccrualCategory, effectiveDate);
044                    AccrualCategory toCat = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(payoutEarnCode, effectiveDate);
045                    PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId,effectiveDate);
046                    
047                    if(ObjectUtils.isNotNull(pha)) {
048                            if(ObjectUtils.isNotNull(pha.getLeavePlan())) {
049                                    AccrualCategoryRule acr = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRuleForDate(fromCat, effectiveDate, pha.getServiceDate());
050                                    if(ObjectUtils.isNotNull(acr)) {
051                                            if(ObjectUtils.isNotNull(acr.getMaxBalFlag())
052                                                            && StringUtils.isNotBlank(acr.getMaxBalFlag())
053                                                            && StringUtils.isNotEmpty(acr.getMaxBalFlag())
054                                                            && StringUtils.equals(acr.getMaxBalFlag(), "Y")) {
055                                                    if(ObjectUtils.isNotNull(acr.getMaxPayoutEarnCode()) || StringUtils.equals(LMConstants.ACTION_AT_MAX_BAL.LOSE, acr.getActionAtMaxBalance())) {
056    /*                                                      isValid &= validatePrincipal(pha,principalId);
057                                                            isValid &= validateEffectiveDate(effectiveDate);
058                                                            isValid &= validateAgainstLeavePlan(pha,fromCat,toCat,effectiveDate);
059                                                            isValid &= validateTransferFromAccrualCategory(fromCat,principalId,effectiveDate,acr);
060                                                            isValid &= validateTransferToAccrualCategory(toCat,principalId,effectiveDate,acr);
061    */
062                                                            isValid &= validatePayoutAmount(leavePayout.getPayoutAmount(),fromCat,toCat, principalId, effectiveDate, acr);
063                                                    }
064                                                    else {
065                                                            //should never be the case if accrual category rules are validated correctly.
066                                                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory",
067                                                                            "leavePayout.fromAccrualCategory.rules.payoutToEarnCode",
068                                                                            fromAccrualCategory);
069                                                            isValid &= false;
070                                                    }
071                                            }
072                                            else {
073                                                    //max bal flag null, blank, empty, or "N"
074                                                    GlobalVariables.getMessageMap().putError("document.newMaintinableObject.fromAccrualCategory",
075                                                                    "leavePayout.fromAccrualCategory.rules.maxBalFlag", fromAccrualCategory);
076                                                    isValid &= false;
077                                            }
078                                    }
079                                    else {
080                                            //department admins must validate amount to transfer does not exceed current balance.
081                                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory",
082                                                            "leavePayout.fromAccrualCategory.rules.exist",fromCat.getAccrualCategory());
083                                            isValid &= false;
084                                    }
085                            }
086                            else {
087                                    //if the principal doesn't have a leave plan, there aren't any accrual categories that can be debited/credited.
088                                    GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId","leavePayout.principal.noLeavePlan");
089                                    isValid &=false;
090                            }
091                    }
092                    else  {
093                            //if the principal has no principal hr attributes, they're not a principal.
094                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId","leavePayout.principal.noAttributes");
095                            isValid &= false;
096                    }
097    /*              }*/
098                    return isValid;
099    
100            }
101    
102            private static boolean validatePayoutAmount(BigDecimal payoutAmount,
103                            AccrualCategory fromCat, AccrualCategory toCat, String principalId,
104                            Date effectiveDate, AccrualCategoryRule accrualRule) {
105    
106                    BigDecimal balance = TkServiceLocator.getAccrualCategoryService().getAccruedBalanceForPrincipal(principalId, fromCat, effectiveDate);
107                    //transfer amount must be less than the max transfer amount defined in the accrual category rule.
108                    //it cannot be negative.
109                    boolean isValid = true;
110                    
111                    BigDecimal maxPayoutAmount = null;
112                    BigDecimal adjustedMaxPayoutAmount = null;
113                    if(ObjectUtils.isNotNull(accrualRule.getMaxPayoutAmount())) {
114                            maxPayoutAmount = new BigDecimal(accrualRule.getMaxPayoutAmount());
115                            BigDecimal fullTimeEngagement = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, effectiveDate);
116                            adjustedMaxPayoutAmount = maxPayoutAmount.multiply(fullTimeEngagement);
117                    }
118                    
119                    //use override if one exists.
120                    EmployeeOverride maxPayoutAmountOverride = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverride(principalId, fromCat.getLeavePlan(), fromCat.getAccrualCategory(), "MPA", effectiveDate);
121                    if(ObjectUtils.isNotNull(maxPayoutAmountOverride))
122                            adjustedMaxPayoutAmount = new BigDecimal(maxPayoutAmountOverride.getOverrideValue());
123                                    
124                                    
125                    if(ObjectUtils.isNotNull(adjustedMaxPayoutAmount)) {
126                            if(payoutAmount.compareTo(adjustedMaxPayoutAmount) > 0) {
127                                    isValid &= false;
128                                    String fromUnitOfTime = TkConstants.UNIT_OF_TIME.get(fromCat.getUnitOfTime());
129                                    GlobalVariables.getMessageMap().putError("leavePayout.payoutAmount","leavePayout.payoutAmount.maxPayoutAmount",adjustedMaxPayoutAmount.toString(),fromUnitOfTime);
130                            }
131                    }
132                    // check for a positive amount.
133                    if(payoutAmount.compareTo(BigDecimal.ZERO) < 0 ) {
134                            isValid &= false;
135                            GlobalVariables.getMessageMap().putError("leavePayout.payoutAmount","leavePayout.payoutAmount.negative");
136                    }
137                    
138                    if(balance.subtract(payoutAmount).compareTo(BigDecimal.ZERO) < 0 ) {
139                            if(StringUtils.equals(fromCat.getEarnCodeObj().getAllowNegativeAccrualBalance(),"Y"))
140                                    isValid &= true;
141                            else {
142                                    isValid &= false;
143                                    GlobalVariables.getMessageMap().putError("leavePayout.payoutAmount", "maxBalance.amount.exceedsBalance");
144                            }
145                    }       
146                    return isValid;
147            }
148    
149            private boolean validateMaxBalance() {
150                    //This validation could assert that the payout amount, together with forfeiture
151                    //brings the balance for the given accrual category back to, or under, the balance limit
152                    //without exceeding the total accrued balance.
153                    return true;
154            }
155            
156            private boolean validateMaxCarryOver() {
157                    //This validation could assert that the payout amount, together with forfeiture
158                    //brings the balance for the given accrual category back to, or under, the max carry over limit
159                    //without exceeding the total accrued balance.
160                    return true;
161            }
162            
163    }