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