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.apache.commons.lang.time.DateUtils;
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.time.earncode.EarnCode;
028    import org.kuali.hr.time.principal.PrincipalHRAttributes;
029    import org.kuali.hr.time.service.base.TkServiceLocator;
030    import org.kuali.hr.time.util.TKContext;
031    import org.kuali.hr.time.util.TKUtils;
032    import org.kuali.rice.kns.document.MaintenanceDocument;
033    import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase;
034    import org.kuali.rice.krad.bo.PersistableBusinessObject;
035    import org.kuali.rice.krad.util.GlobalVariables;
036    import org.kuali.rice.krad.util.ObjectUtils;
037    
038    public class LeavePayoutValidation extends MaintenanceDocumentRuleBase {
039    
040            private boolean validateAgainstLeavePlan(PrincipalHRAttributes pha, AccrualCategory fromAccrualCategory, Date effectiveDate) {
041                    boolean isValid = true;
042    
043                    List<AccrualCategory> accrualCategories = TkServiceLocator.getAccrualCategoryService().getActiveAccrualCategoriesForLeavePlan(pha.getLeavePlan(), effectiveDate);
044                    if(accrualCategories.size() > 0) {
045                            boolean isFromInLeavePlan = false;
046                            for(AccrualCategory activeAccrualCategory : accrualCategories) {
047                                    if(StringUtils.equals(activeAccrualCategory.getLmAccrualCategoryId(),fromAccrualCategory.getLmAccrualCategoryId())) {
048                                            isFromInLeavePlan = true;
049                                    }
050                            }
051                            if(!isFromInLeavePlan) {
052                                    GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory", "leavePayout.accrualCategory.notInLeavePlan", fromAccrualCategory.getAccrualCategory());
053                                    isValid &= false;
054                            }
055                    }
056                    else {
057                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId", "leavePayout.principal.noACinLeavePlan");
058                            isValid &=false;
059                    }
060                    
061                    return isValid;
062            }
063            
064            //Employee Overrides???
065            /**
066             * Transfer amount must be validated against several variables, including max transfer amount,
067             * max carry over ( for the converted amount deposited into the "to" accrual category, max usage
068             * ( if transfers count as usage ).
069             * @param transferAmount
070             * @param debitedAccrualCategory
071             * @param creditedAccrualCategory
072             * @param principalId TODO
073             * @param effectiveDate TODO
074             * @return true if transfer amount is valid
075             */
076            private boolean validatePayoutAmount(BigDecimal transferAmount,
077                            AccrualCategory debitedAccrualCategory,
078                            EarnCode payoutEarnCode, String principalId, Date effectiveDate) {
079                    
080                    if(transferAmount.compareTo(BigDecimal.ZERO) < 0 ) {
081                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.transferAmount", "leavePayout.amount.negative");
082                            return false;
083                    }
084                    //TkServiceLocator.getAccrualCategoryService().getCurrentBalanceForPrincipal(principalId, debitedAccrualCategory, effectiveDate);
085    
086                    return true;
087            }
088    
089            /**
090             * Are there any rules in place for effective date? i.e. not more than one year in advance...
091             * @param date
092             * @return
093             */
094            private boolean validateEffectiveDate(Date date) {
095                    //Limit on future dates?
096                    if(date.getTime() > DateUtils.addYears(TKUtils.getCurrentDate(), 1).getTime()) {
097                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.effectiveDate", "leavePayout.effectiveDate.overOneYear");
098                            return false;
099                    }
100                    return true;
101            }
102            
103            /**
104             * Is the "From" accrual category required to be over its maximum balance before a transfer can take place?
105             * The "From" accrual category must be defined in an accrual category rule as having a max bal rule.
106             * @param accrualCategory
107             * @param effectiveDate 
108             * @param principalId 
109             * @param acr 
110             * @return
111             */
112            private boolean validateTransferFromAccrualCategory(AccrualCategory accrualCategory, String principalId,
113                            Date effectiveDate, AccrualCategoryRule acr) {
114                    //accrualCategory has rules
115                    PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, effectiveDate);
116                    
117                    return true;
118            }
119            
120            private boolean validatePrincipal(PrincipalHRAttributes pha, String principalId) {
121                    // TODO Auto-generated method stub
122                    // Principal has to have an associated leave plan.
123                    // leave plan should contain the transfer "from" accrual category.
124                    
125                    return true;
126            }
127            
128            /**
129             * Is not called when saving a document, but is called on submit.
130             * 
131             * The branched logic in this method contains references to accrual category rule properties
132             * that, when balance transfer is triggered through system interaction, would naturally have
133             * been checked in the process of triggering the balance transfer. i.e. existence of accrual
134             * category rule, maxBalFlag = Y, leave plan for principal id existence and accrual category
135             * existence under that leave plan. The context in which the balance transfers are triggered
136             * would themselves validate these checks.
137             * 
138             */
139            @Override
140            public boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
141                    boolean isValid = true;
142                    LOG.debug("entering custom validation for Balance Transfer");
143    
144                    PersistableBusinessObject pbo = (PersistableBusinessObject) this.getNewBo();
145    
146                    if(pbo instanceof LeavePayout) {
147    
148                            LeavePayout leavePayout = (LeavePayout) pbo;
149    
150                            /**
151                             * Validation is basically governed by accrual category rules. Get accrual category
152                             * rules for both the "To" and "From" accrual categories, pass to validators along with the
153                             * values needing to be validated.
154                             * 
155                             * Balance transfers initiated from the leave calendar display should already have all values
156                             * populated, thus validated, including the accrual category rule for the "From" accrual category.
157                             * 
158                             * Balance transfers initiated via the Maintenance tab will have no values populated.
159                             */
160                            String principalId = leavePayout.getPrincipalId();
161                            Date effectiveDate = leavePayout.getEffectiveDate();
162                            String fromAccrualCategory = leavePayout.getFromAccrualCategory();
163                            EarnCode payoutEarnCode = leavePayout.getEarnCodeObj();
164                            AccrualCategory fromCat = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(fromAccrualCategory, effectiveDate);
165                            PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId,effectiveDate);
166                            
167                            boolean isDeptAdmin = TKContext.getUser().isDepartmentAdmin();
168                            boolean isSysAdmin = TKContext.getUser().isSystemAdmin();
169                            if(isDeptAdmin || isSysAdmin) {
170                                    isValid &= validatePayoutAmount(leavePayout.getPayoutAmount(),fromCat,payoutEarnCode, principalId, effectiveDate);
171                            }
172                            else {
173                                    if(ObjectUtils.isNotNull(pha)) {
174                                            if(ObjectUtils.isNotNull(pha.getLeavePlan())) {
175                                                    AccrualCategoryRule acr = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRuleForDate(fromCat, effectiveDate, pha.getServiceDate());
176                                                    if(ObjectUtils.isNotNull(acr)) {
177                                                            if(ObjectUtils.isNotNull(acr.getMaxBalFlag())
178                                                                            && StringUtils.isNotBlank(acr.getMaxBalFlag())
179                                                                            && StringUtils.isNotEmpty(acr.getMaxBalFlag())
180                                                                            && StringUtils.equals(acr.getMaxBalFlag(), "Y")) {
181                                                                    if(ObjectUtils.isNotNull(payoutEarnCode)) {
182                                                                            isValid &= validatePrincipal(pha,principalId);
183                                                                            isValid &= validateEffectiveDate(effectiveDate);
184                                                                            isValid &= validateAgainstLeavePlan(pha,fromCat,effectiveDate);
185                                                                            isValid &= validateTransferFromAccrualCategory(fromCat,principalId,effectiveDate,acr);
186                                                                            isValid &= validatePayoutAmount(leavePayout.getPayoutAmount(),fromCat,payoutEarnCode, null, null);
187                                                                    }
188                                                                    else {
189                                                                            //should never be the case if accrual category rules are validated correctly.
190                                                                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory",
191                                                                                            "leavePayout.fromAccrualCategory.rules.payoutToEarnCode",
192                                                                                            fromAccrualCategory);
193                                                                            isValid &= false;
194                                                                    }
195                                                            }
196                                                            else {
197                                                                    //max bal flag null, blank, empty, or "N"
198                                                                    GlobalVariables.getMessageMap().putError("document.newMaintinableObject.fromAccrualCategory",
199                                                                                    "leavePayout.fromAccrualCategory.rules.maxBalFlag", fromAccrualCategory);
200                                                                    isValid &= false;
201                                                            }
202                                                    }
203                                                    else {
204                                                            //department admins must validate amount to transfer does not exceed current balance.
205                                                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.fromAccrualCategory",
206                                                                            "leavePayout.fromAccrualCategory.rules.exist",fromCat.getAccrualCategory());
207                                                            isValid &= false;
208                                                    }
209                                            }
210                                            else {
211                                                    //if the principal doesn't have a leave plan, there aren't any accrual categories that can be debited/credited.
212                                                    GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId","leavePayout.principal.noLeavePlan");
213                                                    isValid &=false;
214                                            }
215                                    }
216                                    else  {
217                                            //if the principal has no principal hr attributes, they're not a principal.
218                                            GlobalVariables.getMessageMap().putError("document.newMaintainableObject.principalId","leavePayout.principal.noAttributes");
219                                            isValid &= false;
220                                    }
221                            }
222                    }
223                    return isValid; 
224            }
225            
226            @Override
227            protected boolean processCustomApproveDocumentBusinessRules(
228                            MaintenanceDocument document) {
229    /*              System.out.println("");*/
230                    return super.processCustomApproveDocumentBusinessRules(document);
231            }
232    
233    }