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 org.apache.commons.lang.StringUtils;
021    import org.kuali.hr.lm.LMConstants;
022    import org.kuali.hr.lm.accrual.AccrualCategory;
023    import org.kuali.hr.lm.accrual.AccrualCategoryRule;
024    import org.kuali.hr.lm.balancetransfer.BalanceTransfer;
025    import org.kuali.hr.lm.employeeoverride.EmployeeOverride;
026    import org.kuali.hr.lm.timeoff.SystemScheduledTimeOff;
027    import org.kuali.hr.time.principal.PrincipalHRAttributes;
028    import org.kuali.hr.time.service.base.TkServiceLocator;
029    import org.kuali.hr.time.util.TkConstants;
030    import org.kuali.rice.krad.util.GlobalVariables;
031    import org.kuali.rice.krad.util.ObjectUtils;
032    
033    public class BalanceTransferValidationUtils {
034    
035            public static boolean validateTransfer(BalanceTransfer balanceTransfer) {
036                    boolean isValid = true;
037                    if(StringUtils.isNotEmpty(balanceTransfer.getSstoId())) {
038                            return isValid && validateSstoTranser(balanceTransfer) ;
039                    }
040                    String principalId = balanceTransfer.getPrincipalId();
041                    Date effectiveDate = balanceTransfer.getEffectiveDate();
042                    String fromAccrualCategory = balanceTransfer.getFromAccrualCategory();
043                    String toAccrualCategory = balanceTransfer.getToAccrualCategory();
044                    AccrualCategory fromCat = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(fromAccrualCategory, effectiveDate);
045                    AccrualCategory toCat = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(toAccrualCategory, effectiveDate);
046                    PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId,effectiveDate);
047                    
048                    if(ObjectUtils.isNotNull(pha)) {
049                            if(ObjectUtils.isNotNull(pha.getLeavePlan())) {
050                                    AccrualCategoryRule acr = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRuleForDate(fromCat,
051                                                    effectiveDate, pha.getServiceDate());
052                                    if(ObjectUtils.isNotNull(acr)) {
053                                            if(ObjectUtils.isNotNull(acr.getMaxBalFlag())
054                                                            && StringUtils.isNotBlank(acr.getMaxBalFlag())
055                                                            && StringUtils.isNotEmpty(acr.getMaxBalFlag())
056                                                            && StringUtils.equals(acr.getMaxBalFlag(), "Y")) {
057                                                    if(ObjectUtils.isNotNull(acr.getMaxBalanceTransferToAccrualCategory()) || StringUtils.equals(LMConstants.ACTION_AT_MAX_BAL.LOSE, acr.getActionAtMaxBalance())) {
058    /*                                                      isValid &= validatePrincipal(pha,principalId);
059                                                            isValid &= validateEffectiveDate(effectiveDate);
060                                                            isValid &= validateAgainstLeavePlan(pha,fromCat,toCat,effectiveDate);
061                                                            isValid &= validateTransferFromAccrualCategory(fromCat,principalId,effectiveDate,acr);
062                                                            isValid &= validateTransferToAccrualCategory(toCat,principalId,effectiveDate,acr);*/
063                                                            isValid &= validateMaxCarryOver(balanceTransfer.getAmountTransferred(),toCat,principalId,effectiveDate,acr, pha);
064                                                            isValid &= validateTransferAmount(balanceTransfer.getTransferAmount(),fromCat,toCat, principalId, effectiveDate, acr);
065                                                            isValid &= validateForfeitedAmount(balanceTransfer.getForfeitedAmount());
066                                                    }
067                                                    else {
068                                                            //should never be the case if accrual category rules are validated correctly.
069                                                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory",
070                                                                            "balanceTransfer.fromAccrualCategory.rules.transferToAccrualCategory",
071                                                                            fromAccrualCategory);
072                                                            isValid &= false;
073                                                    }
074                                            }
075                                            else {
076                                                    //max bal flag null, blank, empty, or "N"
077                                                    GlobalVariables.getMessageMap().putError("document.newMaintinableObject.fromAccrualCategory",
078                                                                    "balanceTransfer.fromAccrualCategory.rules.maxBalFlag", fromAccrualCategory);
079                                                    isValid &= false;
080                                            }
081                                    }
082                                    else {
083                                            //department admins must validate amount to transfer does not exceed current balance.
084                                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory",
085                                                            "balanceTransfer.fromAccrualCategory.rules.exist",fromCat.getAccrualCategory());
086                                            isValid &= false;
087                                    }
088                            }
089                            else {
090                                    //if the principal doesn't have a leave plan, there aren't any accrual categories that can be debited/credited.
091                                    GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId","balanceTransfer.principal.noLeavePlan");
092                                    isValid &=false;
093                            }
094                    }
095                    else  {
096                            //if the principal has no principal hr attributes, they're not a principal.
097                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId","balanceTransfer.principal.noAttributes");
098                            isValid &= false;
099                    }
100    /*              }*/
101                    return isValid;
102    
103            }
104    
105            private static boolean validateForfeitedAmount(BigDecimal forfeitedAmount) {
106                    return forfeitedAmount.compareTo(BigDecimal.ZERO) >= 0 ? true: false;
107            }
108    
109            private boolean validateMaxBalance() {
110                    //This validation could assert that the payout amount, together with forfeiture
111                    //brings the balance for the given accrual category back to, or under, the balance limit
112                    //without exceeding the total accrued balance.
113                    return true;
114            }
115            
116            private boolean validateMaxCarryOver() {
117                    //This validation could assert that the payout amount, together with forfeiture
118                    //brings the balance for the given accrual category back to, or under, the max carry over limit
119                    //without exceeding the total accrued balance.
120                    return true;
121            }
122            
123            private static boolean validateMaxCarryOver(BigDecimal amountTransferred,
124                            AccrualCategory toCat, String principalId, Date effectiveDate,
125                            AccrualCategoryRule acr, PrincipalHRAttributes pha) {
126    /*              List<AccrualCategoryRule> rules = toCat.getAccrualCategoryRules();
127                    Date serviceDate = pha.getServiceDate();
128                    Interval interval = new Interval(serviceDate.getTime(), effectiveDate.getTime());
129                    for(AccrualCategoryRule rule : rules) {
130                            String unitOfTime = rule.getServiceUnitOfTime();
131                            if(StringUtils.equals(unitOfTime, LMConstants.SERVICE_TIME_MONTHS))
132                    }*/
133                    return true;
134            }
135    
136            private static boolean validateTransferAmount(BigDecimal transferAmount,
137                            AccrualCategory fromCat, AccrualCategory toCat, String principalId,
138                            Date effectiveDate, AccrualCategoryRule accrualRule) {
139    
140                    //transfer amount must be less than the max transfer amount defined in the accrual category rule.
141                    //it cannot be negative.
142                    boolean isValid = true;
143                    
144                    BigDecimal balance = TkServiceLocator.getAccrualCategoryService().getAccruedBalanceForPrincipal(principalId, fromCat, effectiveDate);
145                    
146                    BigDecimal maxTransferAmount = null;
147                    BigDecimal adjustedMaxTransferAmount = null;
148                    if(ObjectUtils.isNotNull(accrualRule.getMaxTransferAmount())) {
149                            maxTransferAmount = new BigDecimal(accrualRule.getMaxTransferAmount());
150                            BigDecimal fullTimeEngagement = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, effectiveDate);
151                            adjustedMaxTransferAmount = maxTransferAmount.multiply(fullTimeEngagement);
152                    }
153                    
154                    //use override if one exists.
155                    EmployeeOverride maxTransferAmountOverride = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverride(principalId, fromCat.getLeavePlan(), fromCat.getAccrualCategory(), "MTA", effectiveDate);
156                    if(ObjectUtils.isNotNull(maxTransferAmountOverride))
157                            adjustedMaxTransferAmount = new BigDecimal(maxTransferAmountOverride.getOverrideValue());
158                                    
159                    if(ObjectUtils.isNotNull(adjustedMaxTransferAmount)) {
160                            if(transferAmount.compareTo(adjustedMaxTransferAmount) > 0) {
161                                    isValid &= false;
162                                    String fromUnitOfTime = TkConstants.UNIT_OF_TIME.get(fromCat.getUnitOfTime());
163                                    GlobalVariables.getMessageMap().putError("balanceTransfer.transferAmount","balanceTransfer.transferAmount.maxTransferAmount",adjustedMaxTransferAmount.toString(),fromUnitOfTime);
164                            }
165                    }
166                    // check for a positive amount.
167                    if(transferAmount.compareTo(BigDecimal.ZERO) < 0 ) {
168                            isValid &= false;
169                            GlobalVariables.getMessageMap().putError("balanceTransfer.transferAmount","balanceTransfer.transferAmount.negative");
170                    }
171                    
172                    if(balance.subtract(transferAmount).compareTo(BigDecimal.ZERO) < 0 ) {
173                            if(StringUtils.equals(fromCat.getEarnCodeObj().getAllowNegativeAccrualBalance(),"Y"))
174                                    isValid &= true;
175                            else {
176                                    isValid &= false;
177                                    GlobalVariables.getMessageMap().putError("balanceTransfer.transferAmount", "maxBalance.amount.exceedsBalance");
178                            }
179                    }
180                    
181                    return isValid;
182            }
183            
184            public static boolean validateSstoTranser(BalanceTransfer bt) {
185                    // make sure from accrual category is consistent with the ssto's
186                    SystemScheduledTimeOff ssto = TkServiceLocator.getSysSchTimeOffService().getSystemScheduledTimeOff(bt.getSstoId());
187                    if(ssto == null) {
188                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory", "balanceTransfer.transferSSTO.sstoDoesNotExis", bt.getSstoId());
189                            return false;
190                    }
191                    if(!ssto.getAccrualCategory().equals(bt.getFromAccrualCategory())) {
192                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory", "balanceTransfer.transferSSTO.fromACWrong", bt.getFromAccrualCategory(), ssto.getAccrualCategory());
193                            return false;
194                    }
195                    
196                    if(bt.getFromAccrualCategory().equals(bt.getToAccrualCategory())) {
197                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory", "balanceTransfer.transferSSTO.fromAndToACTheSame");
198                            return false;
199                    }
200                    
201                    AccrualCategory fromAC = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(bt.getFromAccrualCategory(), bt.getEffectiveDate());
202                    if(fromAC == null) {
203                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory", "balanceTransfer.transferSSTO.acDoesNotExist", bt.getFromAccrualCategory());
204                            return false;
205                    }
206                    AccrualCategory toAC = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(bt.getToAccrualCategory(), bt.getEffectiveDate());
207                    if(toAC == null) {
208                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.toAccrualCategory", "balanceTransfer.transferSSTO.acDoesNotExist", bt.getToAccrualCategory());
209                            return false;
210                    }
211                    // make sure the leave plan of from/to accrual categories are consistent with the employee's leave plan
212                    PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(bt.getPrincipalId(),bt.getEffectiveDate());
213                    if(StringUtils.isNotEmpty(fromAC.getLeavePlan())){
214                            if(!fromAC.getLeavePlan().equals(pha.getLeavePlan())) {
215                                    GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory", "balanceTransfer.transferSSTO.wrongACLeavePlan", fromAC.getLeavePlan(), pha.getLeavePlan());
216                                    return false;
217                            }
218                    }
219                    if(StringUtils.isNotEmpty(toAC.getLeavePlan())){
220                            if(!fromAC.getLeavePlan().equals(pha.getLeavePlan())) {
221                                    GlobalVariables.getMessageMap().putError("document.newMaintainableObject.toAccrualCategory", "balanceTransfer.transferSSTO.wrongACLeavePlan", toAC.getLeavePlan(), pha.getLeavePlan());
222                                    return false;
223                            }
224                    }
225                    
226                    return true;
227            }
228    
229    }