001    /*
002     * Copyright 2011 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.kfs.module.endow.document.validation.impl;
017    
018    import java.math.BigDecimal;
019    import java.util.List;
020    
021    import org.apache.commons.lang.StringUtils;
022    import org.kuali.kfs.module.endow.EndowConstants;
023    import org.kuali.kfs.module.endow.EndowKeyConstants;
024    import org.kuali.kfs.module.endow.EndowPropertyConstants;
025    import org.kuali.kfs.module.endow.businessobject.ClassCode;
026    import org.kuali.kfs.module.endow.businessobject.EndowmentTransactionCode;
027    import org.kuali.kfs.module.endow.businessobject.FeeClassCode;
028    import org.kuali.kfs.module.endow.businessobject.FeeEndowmentTransactionCode;
029    import org.kuali.kfs.module.endow.businessobject.FeeMethod;
030    import org.kuali.kfs.module.endow.businessobject.FeePaymentType;
031    import org.kuali.kfs.module.endow.businessobject.FeeSecurity;
032    import org.kuali.kfs.module.endow.businessobject.FeeTransaction;
033    import org.kuali.kfs.module.endow.businessobject.Security;
034    import org.kuali.kfs.module.endow.document.service.ClassCodeService;
035    import org.kuali.kfs.module.endow.document.service.EndowmentTransactionCodeService;
036    import org.kuali.kfs.module.endow.document.service.FeeMethodService;
037    import org.kuali.kfs.module.endow.document.service.SecurityService;
038    import org.kuali.kfs.sys.context.SpringContext;
039    import org.kuali.rice.kns.bo.PersistableBusinessObject;
040    import org.kuali.rice.kns.document.Document;
041    import org.kuali.rice.kns.document.MaintenanceDocument;
042    import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase;
043    import org.kuali.rice.kns.rule.event.ApproveDocumentEvent;
044    import org.kuali.rice.kns.util.GlobalVariables;
045    import org.kuali.rice.kns.util.KNSConstants;
046    import org.kuali.rice.kns.util.KualiDecimal;
047    import org.kuali.rice.kns.util.MessageMap;
048    import org.kuali.rice.kns.util.ObjectUtils;
049    
050    public class FeeMethodRule extends MaintenanceDocumentRuleBase {
051    
052        /**
053         * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRule#processRouteDocument(org.kuali.rice.kns.document.Document)
054         */
055        @Override
056        public boolean processRouteDocument(Document document) {
057            boolean isValid = true;
058            isValid &= super.processRouteDocument(document);
059            MessageMap errorMap = GlobalVariables.getMessageMap();
060            isValid &= errorMap.hasNoErrors();
061    
062            if (!isValid) {
063                return isValid;
064            }
065    
066            isValid &= validationRulesPassedForFeeMethod(document);
067    
068            return isValid;
069        }
070    
071        /**
072         * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRule#processApproveDocument(ApproveDocumentEvent)
073         */
074        @Override
075        public boolean processApproveDocument(ApproveDocumentEvent approveEvent) {
076            boolean isValid = true;
077            isValid &= super.processApproveDocument(approveEvent);
078            MessageMap errorMap = GlobalVariables.getMessageMap();
079            isValid &= errorMap.hasNoErrors();
080    
081            if (!isValid) {
082                return isValid;
083            }
084    
085            Document document = (Document) approveEvent.getDocument();
086            isValid &= validationRulesPassedForFeeMethod(document);
087    
088            return isValid;
089        }
090    
091        /**
092         * This method will validate the custom business rules for fee method
093         * 
094         * @param feeMethod
095         * @return true if rules are passed else return false
096         */
097        private boolean validationRulesPassedForFeeMethod(Document document) {
098            boolean rulesPassed = true;
099    
100            MaintenanceDocument maintenanceDocument = (MaintenanceDocument) document;
101            FeeMethod feeMethod = (FeeMethod) maintenanceDocument.getNewMaintainableObject().getBusinessObject();
102    
103            rulesPassed &= checkFeeTransactionTypeCodeValue(feeMethod); // rule #2
104            rulesPassed &= recordExistsInFeeTransactionType(feeMethod); // rule #3
105            rulesPassed &= recordExistsInFeeEndowmentTransactionType(feeMethod); // rule #4
106            rulesPassed &= checkFeeBalanceTypeCodeValue(feeMethod); // rule #5
107            rulesPassed &= recordExistsInFeeClassCode(feeMethod); // rule #6
108            rulesPassed &= recordExistsInFeeSecurity(feeMethod); // rule #7
109            rulesPassed &= checkFeePaymentTypeCodeValue(feeMethod); // rule #8
110            rulesPassed &= recordExistsInFeePaymentType(feeMethod); // rule #9
111            rulesPassed &= checkRateDefinitionAndFeeBalanceTypes(feeMethod); // rule #12 and rule#14
112            rulesPassed &= recordExistsInEndowmentTransactionCode(feeMethod); // rule #12 and rule#14
113            rulesPassed &= checkCorpusToMarketTolerance(feeMethod); // rule #16
114            rulesPassed &= checkFeeRateAndBreakpointAmounts(feeMethod); // rule #17, rule #18, rule #19
115            rulesPassed &= validFeeTransactionTypeEntered(feeMethod); // rule #20
116            rulesPassed &= checkFrequencyCodeNotChangedIfFeeMethodUsedOnAnyKemid(document);
117            rulesPassed &= validFeeExpenseETranCodeEntered(feeMethod); //new rule added by Norm. Per KULENDOW-564
118            
119            return rulesPassed;
120        }
121    
122        /**
123         * This method will check that if Fee By Transaction Type or Fee By ETran Code or both set to Y and Fee Type is set to T.
124         * 
125         * @param feeMethod
126         * @return true if Fee By Transaction Type field or Fee By ETran Code field or both set to Y AND fee type code is T else return
127         *         false
128         */
129        private boolean checkFeeTransactionTypeCodeValue(FeeMethod feeMethod) {
130            boolean isValidTransactionCode = true;
131    
132            if (feeMethod.getFeeByTransactionType() || feeMethod.getFeeByETranCode()) {
133                if (ObjectUtils.isNotNull(feeMethod.getFeeTypeCode()) && !EndowConstants.FeeMethod.FEE_TYPE_CODE_VALUE_FOR_TRANSACTIONS.equalsIgnoreCase(feeMethod.getFeeTypeCode())) {
134                    putFieldError(EndowPropertyConstants.FEE_TYPE_CODE, EndowKeyConstants.FeeMethodConstants.ERROR_INVALID_FEE_TYPE_CODE_FOR_TRANSACTIONS_ENTERED);
135                    return false;
136                }
137            }
138    
139            return isValidTransactionCode;
140        }
141    
142        /**
143         * This method will check that if Fee By Class Code or Fee By Security or both set to Yes and Fee Type is set to B.
144         * 
145         * @param feeMethod
146         * @return true if Fee By Transaction Type field or Fee By ETran Code field or both set to Y AND fee type code is B else return
147         *         false
148         */
149        private boolean checkFeeBalanceTypeCodeValue(FeeMethod feeMethod) {
150            boolean isValidTransactionCode = true;
151    
152            if (feeMethod.getFeeByClassCode() || feeMethod.getFeeBySecurityCode()) {
153                if (ObjectUtils.isNotNull(feeMethod.getFeeTypeCode()) && !EndowConstants.FeeMethod.FEE_TYPE_CODE_VALUE_FOR_BALANCES.equalsIgnoreCase(feeMethod.getFeeTypeCode())) {
154                    putFieldError(EndowPropertyConstants.FEE_TYPE_CODE, EndowKeyConstants.FeeMethodConstants.ERROR_INVALID_FEE_TYPE_CODE_FOR_BALANCE_ENTERED);
155                    return false;
156                }
157            }
158    
159            return isValidTransactionCode;
160        }
161    
162        /**
163         * This method will check that if Fee Type is set to value P then fee base code should be I
164         * 
165         * @param feeMethod
166         * @return true if Fee Type is P and Fee Base code is I else return false
167         */
168        private boolean checkFeePaymentTypeCodeValue(FeeMethod feeMethod) {
169            boolean isValidBaseCode = true;
170    
171            if (ObjectUtils.isNotNull(feeMethod.getFeeTypeCode()) && EndowConstants.FeeMethod.FEE_TYPE_CODE_VALUE_FOR_PAYMENTS.equalsIgnoreCase(feeMethod.getFeeTypeCode())) {
172                if (!EndowConstants.FeeMethod.FEE_BASE_CD_VALUE.equalsIgnoreCase(feeMethod.getFeeBaseCode())) {
173                    putFieldError(EndowPropertyConstants.FEE_BASE_CODE, EndowKeyConstants.FeeMethodConstants.ERROR_INVALID_FEE_BASE_CODE_FOR_PAYMENTS_ENTERED);
174                    return false;
175                }
176            }
177    
178            return isValidBaseCode;
179        }
180    
181        /**
182         * This method will check if at least one record exists in Fee Transaction Type collection with INCL checked on when Fee
183         * Transaction Type Code is checked on
184         * 
185         * @param feeMethod
186         * @return true if Fee Transaction Type code checked on and at least one record with INCL flag is checked on else return false
187         */
188        private boolean recordExistsInFeeTransactionType(FeeMethod feeMethod) {
189            boolean recordExists = false;
190    
191            List<FeeTransaction> feeTransactions = (List<FeeTransaction>) feeMethod.getFeeTransactions();
192    
193            for (FeeTransaction feeTransactionsRecord : feeTransactions) {
194                if (feeTransactionsRecord.getInclude()) {
195                    recordExists = true;
196                    break;
197                }
198            }
199            
200            if (feeMethod.getFeeByTransactionType()) {
201                if (!recordExists) {
202                    putFieldError(EndowPropertyConstants.FEE_BY_TRANSACTION_TYPE_CODE, EndowKeyConstants.FeeMethodConstants.ERROR_NO_RECORDS_WITH_YES_IN_FEE_TRANSACTION_TYPE);
203                    return false;
204                }
205            }
206            //However, If Fee Transaction Type code is not checked, there can be no records in END_FEE_TRAN_DOC_TYP_T with the field INCL set to Yes 
207            else {
208                if (recordExists) {
209                    putFieldError(EndowPropertyConstants.FEE_BY_TRANSACTION_TYPE_CODE, EndowKeyConstants.FeeMethodConstants.ERROR_RECORDS_WITH_YES_IN_FEE_TRANSACTION_TYPE);
210                    return false;
211                }
212            }
213            
214            return true;
215        }
216    
217        /**
218         * This method will check if at least one record exists in Fee Class Codes collection with INCL checked on when Fee Class code
219         * is checked on.
220         * 
221         * @param feeMethod
222         * @return true if Fee Class code is checked on and at least one record with INCL flag is checked on else return false
223         */
224        private boolean recordExistsInFeeClassCode(FeeMethod feeMethod) {
225            boolean recordExists = false;
226            
227            List<FeeClassCode> feeClassCodes = (List<FeeClassCode>) feeMethod.getFeeClassCodes();
228    
229            for (FeeClassCode feeClassCodeRecord : feeClassCodes) {
230                if (feeClassCodeRecord.getInclude()) {
231                    recordExists = true;
232                    break;
233                }
234            }
235    
236            if (feeMethod.getFeeByClassCode()) {
237                if (!recordExists) {
238                    putFieldError(EndowPropertyConstants.FEE_BY_CLASS_CODE, EndowKeyConstants.FeeMethodConstants.ERROR_NO_RECORDS_WITH_YES_IN_FEE_CLASS_CODE);
239                    return false;
240                }
241            }
242            else {
243                if (recordExists) {
244                    putFieldError(EndowPropertyConstants.FEE_BY_CLASS_CODE, EndowKeyConstants.FeeMethodConstants.ERROR_RECORDS_WITH_YES_IN_FEE_CLASS_CODE);
245                    return false;
246                }
247            }
248            
249            return true;
250        }
251    
252        /**
253         * This method will check if at least one record exists in Fee Endowment Transaction Codes collection with INCL checked on when
254         * Fee Endowment Transaction Code is checked on.
255         * 
256         * @param feeMethod
257         * @return true if Fee EndowmentTransaction Type is checked on and at least one record with INCL flag is checked on else return
258         *         false
259         */
260        private boolean recordExistsInFeeEndowmentTransactionType(FeeMethod feeMethod) {
261            boolean recordExists = false;
262    
263            List<FeeEndowmentTransactionCode> feeEndowmentTransactionCodes = (List<FeeEndowmentTransactionCode>) feeMethod.getFeeEndowmentTransactionCodes();
264    
265            for (FeeEndowmentTransactionCode feeEndowmentTransactionCodesRecord : feeEndowmentTransactionCodes) {
266                if (feeEndowmentTransactionCodesRecord.getInclude()) {
267                    recordExists = true;
268                    break;
269                }
270            }
271            
272            if (feeMethod.getFeeByETranCode()) {
273                if (!recordExists) {
274                    putFieldError(EndowPropertyConstants.FEE_BY_ENDOWMENT_TRANSACTION_TYPE_CODE, EndowKeyConstants.FeeMethodConstants.ERROR_NO_RECORDS_WITH_YES_IN_FEE_ENDOWMENT_TRANSACTION_CODE);
275                    return false;                
276                }
277            }
278            //However, If etran code is not checked, there can be no records in etrancode table with the field INCL set to Yes 
279            else {
280                if (recordExists) {
281                    putFieldError(EndowPropertyConstants.FEE_BY_ENDOWMENT_TRANSACTION_TYPE_CODE, EndowKeyConstants.FeeMethodConstants.ERROR_RECORDS_WITH_YES_IN_FEE_ENDOWMENT_TRANSACTION_CODE);
282                    return false;
283                }
284            }
285    
286            return true;
287        }
288    
289        /**
290         * This method will check if at least one record exists in Fee Security collection with INCL checked on when Fee Class Code is
291         * checked on.
292         * 
293         * @param feeMethod
294         * @return true if Fee Security Code is checked on and at least one record with INCL flag is checked on else return false
295         */
296        private boolean recordExistsInFeeSecurity(FeeMethod feeMethod) {
297            boolean recordExists = false;
298            List<FeeSecurity> feeSecurity = (List<FeeSecurity>) feeMethod.getFeeSecurity();
299    
300            for (FeeSecurity feeSecurityRecord : feeSecurity) {
301                if (feeSecurityRecord.getInclude()) {
302                    recordExists = true;
303                    break;
304                }
305            }
306    
307            if (feeMethod.getFeeBySecurityCode()) {
308                if (!recordExists) {
309                    putFieldError(EndowPropertyConstants.FEE_BY_SECURITY_CODE, EndowKeyConstants.FeeMethodConstants.ERROR_NO_RECORDS_WITH_YES_IN_FEE_SECURITY_CODE);
310                    return false;
311                }
312            }
313            else {
314                if (recordExists) {
315                    putFieldError(EndowPropertyConstants.FEE_BY_ENDOWMENT_TRANSACTION_TYPE_CODE, EndowKeyConstants.FeeMethodConstants.ERROR_RECORDS_WITH_YES_IN_FEE_SECURITY_CODE);
316                    return false;
317                }
318            }
319    
320            return true;
321        }
322    
323        /**
324         * This method will check if at least one record exists in Fee Payment Type collection with INCL checked on when Fee Type Code
325         * is checked on.
326         * 
327         * @param feeMethod
328         * @return true if at least one record with INCL flag is checked on else return false
329         */
330        private boolean recordExistsInFeePaymentType(FeeMethod feeMethod) {
331            boolean recordExists = true;
332    
333            if (ObjectUtils.isNotNull(feeMethod.getFeeTypeCode()) && EndowConstants.FeeMethod.FEE_TYPE_CODE_VALUE_FOR_PAYMENTS.equalsIgnoreCase(feeMethod.getFeeTypeCode())) {
334                recordExists = false;
335                List<FeePaymentType> feePaymentTypes = (List<FeePaymentType>) feeMethod.getFeePaymentTypes();
336    
337                for (FeePaymentType feePaymentTypeRecord : feePaymentTypes) {
338                    if (feePaymentTypeRecord.getInclude()) {
339                        recordExists = true;
340                        break;
341                    }
342                }
343    
344                if (!recordExists) {
345                    putFieldError(EndowPropertyConstants.FEE_BASE_CODE, EndowKeyConstants.FeeMethodConstants.ERROR_NO_RECORDS_WITH_YES_IN_FEE_PAYMENT_TYPE);
346                }
347            }
348    
349            return recordExists;
350        }
351    
352        /**
353         * This method will check rate definition and fee balance types based on fee type code
354         * 
355         * @param feeMethod
356         * @return true if ((rate definition is C AND fee type code is B AND fee balances types are AU or CU and/or MU) or (rate
357         *         definition is V AND fee type code is B AND fee balances types are AMV or CMV and/or MMV)) else return false
358         */
359        private boolean checkRateDefinitionAndFeeBalanceTypes(FeeMethod feeMethod) {
360            boolean isValid = true;
361    
362            String feeRateDefinitionCode = feeMethod.getFeeRateDefinitionCode();
363    
364            if (ObjectUtils.isNotNull(feeRateDefinitionCode)) {
365                // if rate definition is C and type fee code is B
366                if (EndowConstants.FeeMethod.FEE_RATE_DEFINITION_CODE_FOR_COUNT.equalsIgnoreCase(feeRateDefinitionCode) && EndowConstants.FeeMethod.FEE_TYPE_CODE_VALUE_FOR_BALANCES.equalsIgnoreCase(feeMethod.getFeeTypeCode())) {
367                    String feeBalanceTypeCode = feeMethod.getFeeBalanceTypeCode();
368    
369                    if (!EndowConstants.FeeBalanceTypes.FEE_BALANCE_TYPE_VALUE_FOR_AVERAGE_UNITS.equalsIgnoreCase(feeBalanceTypeCode) && !EndowConstants.FeeBalanceTypes.FEE_BALANCE_TYPE_VALUE_FOR_CURRENT_UNITS.equalsIgnoreCase(feeBalanceTypeCode) && !EndowConstants.FeeBalanceTypes.FEE_BALANCE_TYPE_VALUE_FOR_MONTH_END_UNITS.equalsIgnoreCase(feeBalanceTypeCode)) {
370                        putFieldError(EndowPropertyConstants.FEE_BALANCE_TYPES_CODE, EndowKeyConstants.FeeMethodConstants.ERROR_INVALID_FEE_BALANCE_TYPE_CODE_WHEN_COUNT_ENTERED);
371                        return false;
372                    }
373                }
374    
375                // if rate definition is V and type fee code is B
376                if (EndowConstants.FeeMethod.FEE_RATE_DEFINITION_CODE_FOR_VALUE.equalsIgnoreCase(feeRateDefinitionCode) && EndowConstants.FeeMethod.FEE_TYPE_CODE_VALUE_FOR_BALANCES.equalsIgnoreCase(feeMethod.getFeeTypeCode())) {
377                    String feeBalanceTypeCode = feeMethod.getFeeBalanceTypeCode();
378    
379                    if (!EndowConstants.FeeBalanceTypes.FEE_BALANCE_TYPE_VALUE_FOR_AVERAGE_MARKET_VALUE.equalsIgnoreCase(feeBalanceTypeCode) && !EndowConstants.FeeBalanceTypes.FEE_BALANCE_TYPE_VALUE_FOR_CURRENT_MARKET_VALUE.equalsIgnoreCase(feeBalanceTypeCode) && !EndowConstants.FeeBalanceTypes.FEE_BALANCE_TYPE_VALUE_FOR_MONTH_END_MARKET_VALUE.equalsIgnoreCase(feeBalanceTypeCode)) {
380                        putFieldError(EndowPropertyConstants.FEE_BALANCE_TYPES_CODE, EndowKeyConstants.FeeMethodConstants.ERROR_INVALID_FEE_BALANCE_TYPE_CODE_WHEN_VALUE_ENTERED);
381                        return false;
382                    }
383                }
384            }
385    
386            return isValid;
387        }
388    
389        /**
390         * This method will check if record exists in Endowment Transaction Code table
391         * 
392         * @param feeMethod feeMethod object
393         * @return true if Fee Endowment Transaction code exists else return false
394         */
395        private boolean recordExistsInEndowmentTransactionCode(FeeMethod feeMethod) {
396            boolean recordExists = true;
397    
398            String endowmentTransactionCode = feeMethod.getFeeExpenseETranCode();
399    
400            EndowmentTransactionCodeService endowmentTransactionCodeService = SpringContext.getBean(EndowmentTransactionCodeService.class);
401            EndowmentTransactionCode endowmentTransaction = endowmentTransactionCodeService.getByPrimaryKey(endowmentTransactionCode.toUpperCase());
402    
403            if (ObjectUtils.isNull(endowmentTransaction)) {
404                putFieldError(EndowPropertyConstants.FEE_ENDOWMENT_TRANSACTION_TYPE_CODE_ATTRIBUTE, EndowKeyConstants.FeeMethodConstants.ERROR_NO_RECORD_IN_ENDOWMENT_TRANSACTION_CODE);
405                return false;
406            }
407    
408            return recordExists;
409        }
410    
411        /**
412         * This method will check that if corpus to market tolerance is 0 or > 1
413         * 
414         * @param feeMethod
415         * @return true if corpus to market tolerance is 0 (default) or > 1.00 else return false
416         */
417        private boolean checkCorpusToMarketTolerance(FeeMethod feeMethod) {
418            boolean isValid = true;
419    
420            if (ObjectUtils.isNotNull(feeMethod.getCorpusPctTolerance())) {
421                KualiDecimal corputPctToTolerance = feeMethod.getCorpusPctTolerance();
422                if (corputPctToTolerance.isLessThan(KualiDecimal.ZERO)) {
423                    putFieldError(EndowPropertyConstants.CORPUS_TO_PCT_TOLERANCE, EndowKeyConstants.FeeMethodConstants.ERROR_CORPUS_PCT_TO_TOLERANCE_NEGATIVE);
424                    return false;
425                }
426                if (!corputPctToTolerance.isZero() && !corputPctToTolerance.isGreaterThan(new KualiDecimal("1.0"))) {
427                    putFieldError(EndowPropertyConstants.CORPUS_TO_PCT_TOLERANCE, EndowKeyConstants.FeeMethodConstants.ERROR_CORPUS_PCT_TO_TOLERANCE_MUST_BE_GREATER_THAN_ONE);
428                    return false;
429                }
430            }
431    
432            return isValid;
433        }
434    
435        /**
436         * This method will check the fee rate and fee rate breakpoints
437         * 
438         * @param feeMethod
439         * @return true fee rate and fee rate breakpoints pass the validations else return false
440         */
441        private boolean checkFeeRateAndBreakpointAmounts(FeeMethod feeMethod) {
442            boolean isValid = true;
443    
444            // no negative values for first, second, and third rate fields
445            if (feeMethod.getFirstFeeRate().compareTo(BigDecimal.ZERO) == -1) {
446                putFieldError(EndowPropertyConstants.FIRST_FEE_RATE, EndowKeyConstants.FeeMethodConstants.ERROR_FIRST_FEE_RATE_CAN_NOT_BE_NEGATIVE);
447                return false;
448            }
449            if (feeMethod.getSecondFeeRate().compareTo(BigDecimal.ZERO) == -1) {
450                putFieldError(EndowPropertyConstants.SECOND_FEE_RATE, EndowKeyConstants.FeeMethodConstants.ERROR_SECOND_FEE_RATE_CAN_NOT_BE_NEGATIVE);
451                return false;
452            }
453    
454            if (feeMethod.getThirdFeeRate().compareTo(BigDecimal.ZERO) == -1) {
455                putFieldError(EndowPropertyConstants.THIRD_FEE_RATE, EndowKeyConstants.FeeMethodConstants.ERROR_THIRD_FEE_RATE_CAN_NOT_BE_NEGATIVE);
456                return false;
457            }
458    
459            // no negative values for first, second fee breakpoint values.
460            if (feeMethod.getFirstFeeBreakpoint().isLessThan(KualiDecimal.ZERO)) {
461                putFieldError(EndowPropertyConstants.FIRST_FEE_BREAK_POINT, EndowKeyConstants.FeeMethodConstants.ERROR_FIRST_FEE_BREAK_POINT_MUST_BE_GREATER_THAN_OR_ZERO);
462                return false;
463            }
464    
465            if (feeMethod.getSecondFeeBreakpoint().isLessThan(KualiDecimal.ZERO)) {
466                putFieldError(EndowPropertyConstants.SECOND_FEE_BREAK_POINT, EndowKeyConstants.FeeMethodConstants.ERROR_SECOND_FEE_BREAK_POINT_MUST_BE_GREATER_THAN_OR_ZERO);
467                return false;
468            }
469    
470            if (feeMethod.getFirstFeeBreakpoint().isLessThan(EndowConstants.FeeMethod.FEE_RATE_DEFAULT_VALUE) && feeMethod.getSecondFeeRate().compareTo(BigDecimal.ZERO) == 0) {
471                putFieldError(EndowPropertyConstants.SECOND_FEE_RATE, EndowKeyConstants.FeeMethodConstants.ERROR_SECOND_FEE_RATE_MUST_BE_GREATER_THAN_OR_ZERO);
472                return false;
473            }
474    
475            if (feeMethod.getSecondFeeRate().compareTo(BigDecimal.ZERO) == 1 && feeMethod.getFirstFeeBreakpoint().isGreaterEqual(feeMethod.getSecondFeeBreakpoint())) {
476                putFieldError(EndowPropertyConstants.FIRST_FEE_BREAK_POINT, EndowKeyConstants.FeeMethodConstants.ERROR_FIRST_FEE_BREAK_POINT_MUST_BE_LESS_THAN_SECOND_FEE_BREAK_POINT);
477                return false;
478            }
479    
480            if (feeMethod.getThirdFeeRate().compareTo(BigDecimal.ZERO) == 1 && feeMethod.getSecondFeeBreakpoint().isGreaterEqual(EndowConstants.FeeMethod.FEE_RATE_DEFAULT_VALUE)) {
481                putFieldError(EndowPropertyConstants.SECOND_FEE_BREAK_POINT, EndowKeyConstants.FeeMethodConstants.ERROR_SECOND_FEE_BREAK_POINT_MUST_BE_LESS_THAN_MAX_FEE_BREAK_POINT);
482                return false;
483            }
484    
485            if (feeMethod.getSecondFeeBreakpoint().isLessThan(EndowConstants.FeeMethod.FEE_RATE_DEFAULT_VALUE) && (feeMethod.getThirdFeeRate().compareTo(BigDecimal.ZERO) == 0)) {
486                putFieldError(EndowPropertyConstants.THIRD_FEE_RATE, EndowKeyConstants.FeeMethodConstants.ERROR_THIRD_FEE_RATE_MUST_BE_GREATER_THAN_ZERO);
487                return false;
488            }
489    
490            return isValid;
491        }
492    
493        /**
494         * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processAddCollectionLineBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument,
495         *      java.lang.String, org.kuali.rice.kns.bo.PersistableBusinessObject)
496         */
497        @Override
498        public boolean processAddCollectionLineBusinessRules(MaintenanceDocument document, String collectionName, PersistableBusinessObject bo) {
499            boolean isValid = true;
500            isValid &= super.processCustomAddCollectionLineBusinessRules(document, collectionName, bo);
501            MessageMap errorMap = GlobalVariables.getMessageMap();
502            isValid &= errorMap.hasNoErrors();
503    
504            if (!isValid) {
505                return isValid;
506            }
507    
508            FeeMethod feeMethod = (FeeMethod) document.getNewMaintainableObject().getBusinessObject();
509    
510            // process add line for feeClassCodes collection
511            if (collectionName.equalsIgnoreCase(EndowPropertyConstants.FEE_CLASS_CODES_COLLECTION_NAME)) {
512                FeeClassCode feeClassCode = (FeeClassCode) bo;
513                bo.refreshReferenceObject(EndowPropertyConstants.FEE_CLASS_CODE_REF);
514    
515                if (isEmptyFeeClassCode(bo)) {
516                    return false;
517                }
518                if (duplicateFeeClassCodeEntered(feeMethod, bo)) {
519                    return false;
520                }
521                if (!validateFeeClassCode(bo)) {
522                    isValid = false;
523                }
524            }
525    
526            // process add line for feeSecurity collection
527            if (collectionName.equalsIgnoreCase(EndowPropertyConstants.FEE_SECURITY_COLLECTION_NAME)) {
528                FeeSecurity feeSecurity = (FeeSecurity) bo;
529                bo.refreshReferenceObject(EndowPropertyConstants.FEE_SECURITY_REF);
530    
531                if (isEmptyFeeSecurityCode(bo)) {
532                    return false;
533                }
534                if (duplicateFeeSecurityCodeEntered(feeMethod, bo)) {
535                    return false;
536                }
537                if (!validateFeeSecurityCode(bo)) {
538                    isValid = false;
539                }
540            }
541    
542            // process add line for feeTransaction collection
543            if (collectionName.equalsIgnoreCase(EndowPropertyConstants.FEE_TRANSACTION_TYPE_COLLECTION_NAME)) {
544                FeeTransaction feeTransaction = (FeeTransaction) bo;
545                // bo.refreshReferenceObject(EndowPropertyConstants.FEE_TRANSACTION_ARCHIVE_REF);
546    
547                if (isEmptyFeeTransactionDocumentTypeName(bo)) {
548                    return false;
549                }
550                if (duplicateFeeTransactionDocumentTypeName(feeMethod, bo)) {
551                    return false;
552                }
553            }
554    
555            // process add line for feeEndowmentTransaction collection
556            if (collectionName.equalsIgnoreCase(EndowPropertyConstants.FEE_ENDOWMENT_TRANSACTION_CODE_COLLECTION_NAME)) {
557                FeeEndowmentTransactionCode feeEndowmentTransactionCode = (FeeEndowmentTransactionCode) bo;
558                bo.refreshReferenceObject(EndowPropertyConstants.FEE_ENDOWMENT_TRANSACTION_CODE_REF);
559    
560                if (isEmptyFeeEndowmentTransactionCode(bo)) {
561                    return false;
562                }
563                if (duplicateFeeEndowmentTransactionCode(feeMethod, bo)) {
564                    return false;
565                }
566                if (!validateFeeEndowmentTransactionCode(bo)) {
567                    isValid = false;
568                }
569            }
570    
571            // process add line for feePaymentType collection
572            if (collectionName.equalsIgnoreCase(EndowPropertyConstants.FEE_PAYMENT_TYPE_COLLECTION_NAME)) {
573                if (isEmptyFeePaymentTypeCode(bo)) {
574                    return false;
575                }
576                if (duplicateFeePaymentTypeCode(feeMethod, bo)) {
577                    return false;
578                }
579            }
580    
581            return isValid;
582        }
583    
584        /**
585         * This method checks to make sure that fee class code is not empty
586         * 
587         * @param feeClassCode The object feeClassCode
588         * @return isValid is true if fee class code is empty else return false
589         */
590        private boolean isEmptyFeeClassCode(PersistableBusinessObject bo) {
591            boolean isValid = false;
592    
593            FeeClassCode feeClass = (FeeClassCode) bo;
594    
595            String feeClassCode = feeClass.getFeeClassCode();
596            if (ObjectUtils.isNull(feeClassCode)) {
597                putFieldError(EndowPropertyConstants.FEE_CLASS_CODE_ATTRIBUTE, EndowKeyConstants.FeeMethodConstants.ERROR_BLANK_FEE_CLASS_CODE_ENTERED);
598                return true;
599            }
600    
601            return isValid;
602        }
603    
604        /**
605         * This method checks to make sure that fee class code exists in ClassCode
606         * 
607         * @param feeClassCode The object feeClassCode
608         * @return isValid is true if fee class code exists in the database else return false
609         */
610        private boolean validateFeeClassCode(PersistableBusinessObject bo) {
611            boolean isValid = true;
612    
613            FeeClassCode feeClass = (FeeClassCode) bo;
614            String feeClassCode = feeClass.getFeeClassCode();
615    
616            ClassCodeService classCodeService = SpringContext.getBean(ClassCodeService.class);
617            ClassCode classCode = classCodeService.getByPrimaryKey(feeClassCode);
618    
619            if (ObjectUtils.isNull(classCode)) {
620                putFieldError(EndowPropertyConstants.FEE_CLASS_CODE_ATTRIBUTE, EndowKeyConstants.FeeMethodConstants.ERROR_INVALID_FEE_CLASS_CODE_ENTERED);
621                return false;
622            }
623    
624            return isValid;
625        }
626    
627        /**
628         * This method checks to make sure that fee class code is not a duplicate in the collection list Compare the entered fee class
629         * code on the line to the fee class codes in the list to make sure it is not a duplicate.
630         * 
631         * @param feeMethod, bo
632         * @return isDuplicate is true if fee class code is in the list already else return false
633         */
634        private boolean duplicateFeeClassCodeEntered(FeeMethod feeMethod, PersistableBusinessObject bo) {
635            boolean isDuplicate = false;
636    
637            FeeClassCode feeClass = (FeeClassCode) bo;
638            String feeClassCode = feeClass.getFeeClassCode();
639    
640            List<FeeClassCode> feeClassCodes = (List<FeeClassCode>) feeMethod.getFeeClassCodes();
641    
642            for (FeeClassCode feeClassCodeRecord : feeClassCodes) {
643                if (feeClassCodeRecord.getFeeClassCode().equalsIgnoreCase(feeClassCode)) {
644                    isDuplicate = true;
645                    putFieldError(EndowPropertyConstants.FEE_CLASS_CODE_ATTRIBUTE, EndowKeyConstants.FeeMethodConstants.ERROR_DUPLICATE_FEE_CLASS_CODE_ENTERED);
646                }
647            }
648    
649            return isDuplicate;
650        }
651    
652        /**
653         * This method checks to make sure that fee security code is not empty
654         * 
655         * @param bo
656         * @return isValid is true if fee security code is empty else return false
657         */
658        private boolean isEmptyFeeSecurityCode(PersistableBusinessObject bo) {
659            boolean isValid = false;
660    
661            FeeSecurity feeSecurity = (FeeSecurity) bo;
662    
663            String feeSecurityCode = feeSecurity.getSecurityCode();
664            if (ObjectUtils.isNull(feeSecurityCode)) {
665                putFieldError(EndowPropertyConstants.FEE_SECURITY_CODE_ATTRIBUTE, EndowKeyConstants.FeeMethodConstants.ERROR_BLANK_FEE_SECURITY_CODE_ENTERED);
666                return true;
667            }
668    
669            return isValid;
670        }
671    
672        /**
673         * This method checks to make sure that fee security code exists in Security
674         * 
675         * @param bo The bo to be mapped into feeSecurity
676         * @return isValid is true if fee security code is in the database else return false
677         */
678        private boolean validateFeeSecurityCode(PersistableBusinessObject bo) {
679            boolean isValid = true;
680    
681            FeeSecurity feeSecurity = (FeeSecurity) bo;
682            String securityCode = feeSecurity.getSecurityCode();
683    
684            SecurityService securityService = SpringContext.getBean(SecurityService.class);
685            Security security = securityService.getByPrimaryKey(securityCode);
686    
687            if (ObjectUtils.isNull(security)) {
688                putFieldError(EndowPropertyConstants.FEE_SECURITY_CODE_ATTRIBUTE, EndowKeyConstants.FeeMethodConstants.ERROR_INVALID_FEE_SECURITY_CODE_ENTERED);
689                return false;
690            }
691    
692            return isValid;
693        }
694    
695        /**
696         * This method checks to make sure that fee security code is not a duplicate in the collection list Compare the entered fee
697         * security code on the line to the fee security codes in the list to make sure it is not a duplicate.
698         * 
699         * @param feeMethod, bo
700         * @return isDuplicate is true if fee security code in the list else return false
701         */
702        private boolean duplicateFeeSecurityCodeEntered(FeeMethod feeMethod, PersistableBusinessObject bo) {
703            boolean isDuplicate = false;
704    
705            FeeSecurity feeSecurityCode = (FeeSecurity) bo;
706            String securityCode = feeSecurityCode.getSecurityCode();
707    
708            List<FeeSecurity> feeSecurity = (List<FeeSecurity>) feeMethod.getFeeSecurity();
709    
710            for (FeeSecurity feeSecurityRecord : feeSecurity) {
711                if (feeSecurityRecord.getSecurityCode().equalsIgnoreCase(securityCode)) {
712                    isDuplicate = true;
713                    putFieldError(EndowPropertyConstants.FEE_SECURITY_CODE_ATTRIBUTE, EndowKeyConstants.FeeMethodConstants.ERROR_DUPLICATE_FEE_SECURITY_CODE_ENTERED);
714                }
715            }
716    
717            return isDuplicate;
718        }
719    
720        /**
721         * This method checks to make sure that fee transaction type code is not empty
722         * 
723         * @param bo bo to be mapped into feeTransaction
724         * @return isValid is true if fee transaction document name is empty else return false
725         */
726        private boolean isEmptyFeeTransactionDocumentTypeName(PersistableBusinessObject bo) {
727            boolean isValid = false;
728    
729            FeeTransaction feeTransaction = (FeeTransaction) bo;
730            String feeTransactionTypeName = feeTransaction.getDocumentTypeName();
731    
732            if (ObjectUtils.isNull(feeTransactionTypeName)) {
733                putFieldError(EndowPropertyConstants.FEE_TRANSACTION_DOCUMENT_TYPE_NAME_ATTRIBUTE, EndowKeyConstants.FeeMethodConstants.ERROR_BLANK_DOCUMENT_TYPE_NAME_ENTERED);
734                return true;
735            }
736    
737            return isValid;
738        }
739    
740        /**
741         * This method checks to make sure that fee class code is not a duplicate in the collection list Compare the entered fee
742         * transaction type code on the line to the fee transaction type codes in the list to make sure it is not a duplicate
743         * 
744         * @param feeMethod, bo
745         * @return isDuplicate is true if fee transaction document type name is already in the list else return false
746         */
747        private boolean duplicateFeeTransactionDocumentTypeName(FeeMethod feeMethod, PersistableBusinessObject bo) {
748            boolean isDuplicate = false;
749    
750            FeeTransaction feeTransaction = (FeeTransaction) bo;
751            String feeTransactionDocumentTypeName = feeTransaction.getDocumentTypeName();
752    
753            List<FeeTransaction> feeTransactions = (List<FeeTransaction>) feeMethod.getFeeTransactions();
754    
755            for (FeeTransaction feeTransactionsRecord : feeTransactions) {
756                if (feeTransactionsRecord.getDocumentTypeName().equalsIgnoreCase(feeTransactionDocumentTypeName)) {
757                    isDuplicate = true;
758                    putFieldError(EndowPropertyConstants.FEE_TRANSACTION_DOCUMENT_TYPE_NAME_ATTRIBUTE, EndowKeyConstants.FeeMethodConstants.ERROR_DUPLICATE_TRANSACTION_TYPE_NAME_ENTERED);
759                }
760            }
761    
762            return isDuplicate;
763        }
764    
765        /**
766         * This method checks to make sure that fee endowment transaction code is not empty
767         * 
768         * @param bo bo will be mapped to feeEndowmentTransactionCode
769         * @return isValid is true if fee endowment transaction code is empty else return false
770         */
771        private boolean isEmptyFeeEndowmentTransactionCode(PersistableBusinessObject bo) {
772            boolean isValid = false;
773    
774            FeeEndowmentTransactionCode feeEndowmentTransactionCode = (FeeEndowmentTransactionCode) bo;
775            String feeEndowmentCode = feeEndowmentTransactionCode.getEndowmentTransactionCode();
776    
777            if (ObjectUtils.isNull(feeEndowmentCode)) {
778                putFieldError(EndowPropertyConstants.FEE_ENDOWMENT_TRANSACTION_TYPE_CODE_ATTRIBUTE, EndowKeyConstants.FeeMethodConstants.ERROR_BLANK_ENDOWMENT_TRANSACTION_CODE_ENTERED);
779                return true;
780            }
781    
782            return isValid;
783        }
784    
785        /**
786         * This method checks to make sure that fee endowment transaction code exists in Endowment Transaction Code
787         * 
788         * @param bo bo to be mapped into feeEndowmentTransactionCode
789         * @return isValid is true if fee endowment transaction code is in the database else return false
790         */
791        private boolean validateFeeEndowmentTransactionCode(PersistableBusinessObject bo) {
792            boolean isValid = true;
793    
794            FeeEndowmentTransactionCode feeEndowmentTransactionCode = (FeeEndowmentTransactionCode) bo;
795            String feeEndowmentCode = feeEndowmentTransactionCode.getEndowmentTransactionCode();
796    
797            EndowmentTransactionCodeService endowmentTransactionCodeService = SpringContext.getBean(EndowmentTransactionCodeService.class);
798            EndowmentTransactionCode endowmentTransactionCode = endowmentTransactionCodeService.getByPrimaryKey(feeEndowmentCode);
799    
800            if (ObjectUtils.isNull(endowmentTransactionCode)) {
801                putFieldError(EndowPropertyConstants.FEE_ENDOWMENT_TRANSACTION_TYPE_CODE_ATTRIBUTE, EndowKeyConstants.FeeMethodConstants.ERROR_INVALID_ENDOWMENT_TRANSACTION_CODE_ENTERED);
802                return false;
803            }
804    
805            return isValid;
806        }
807    
808        /**
809         * This method checks to make sure that fee endowment transaction code is not a duplicate in the collection list Compare the
810         * entered fee endowment transaction code on the line to the fee endowment transaction codes in the list to make sure it is not
811         * a duplicate.
812         * 
813         * @param feeMethod, bo
814         * @return isDuplicate is true if fee endowment transaction code in the list else return false
815         */
816        private boolean duplicateFeeEndowmentTransactionCode(FeeMethod feeMethod, PersistableBusinessObject bo) {
817            boolean isDuplicate = false;
818    
819            FeeEndowmentTransactionCode feeEndowmentTransactionCode = (FeeEndowmentTransactionCode) bo;
820            String feeEndowmentCode = feeEndowmentTransactionCode.getEndowmentTransactionCode();
821    
822            List<FeeEndowmentTransactionCode> feeEndowmentTransactionCodes = (List<FeeEndowmentTransactionCode>) feeMethod.getFeeEndowmentTransactionCodes();
823    
824            for (FeeEndowmentTransactionCode feeEndowmentTransactionCodesRecord : feeEndowmentTransactionCodes) {
825                if (feeEndowmentTransactionCodesRecord.getEndowmentTransactionCode().equalsIgnoreCase(feeEndowmentCode)) {
826                    isDuplicate = true;
827                    putFieldError(EndowPropertyConstants.FEE_ENDOWMENT_TRANSACTION_TYPE_CODE_ATTRIBUTE, EndowKeyConstants.FeeMethodConstants.ERROR_DUPLICATE_ENDOWMENT_TRANSACTION_CODE_ENTERED);
828                }
829            }
830    
831            return isDuplicate;
832        }
833    
834        /**
835         * This method checks to make sure that fee payment type code is not empty
836         * 
837         * @param bo bo to be mapped into feePaymentTypeCode
838         * @return isValid is true if fee payment type code is empty else return false
839         */
840        private boolean isEmptyFeePaymentTypeCode(PersistableBusinessObject bo) {
841            boolean isValid = false;
842    
843            FeePaymentType feePaymentType = (FeePaymentType) bo;
844            String feePaymentTypeCode = feePaymentType.getPaymentTypeCode();
845    
846            if (ObjectUtils.isNull(feePaymentTypeCode)) {
847                putFieldError(EndowPropertyConstants.FEE_PAYMENT_TYPE_CODE, EndowKeyConstants.FeeMethodConstants.ERROR_BLANK_PAYMENT_TYPE_CODE_ENTERED);
848                return true;
849            }
850    
851            return isValid;
852        }
853    
854        /**
855         * This method checks to make sure that fee payment type code is not a duplicate in the collection list Compare the entered fee
856         * payment type code on the line to the fee payment type codes in the list to make sure it is not a duplicate.
857         * 
858         * @param feeMethod, bo
859         * @return isDuplicate is true if fee payment type code in the list else return false
860         */
861        private boolean duplicateFeePaymentTypeCode(FeeMethod feeMethod, PersistableBusinessObject bo) {
862            boolean isDuplicate = false;
863    
864            FeePaymentType feePaymentType = (FeePaymentType) bo;
865            String feePaymentTypeCode = feePaymentType.getPaymentTypeCode();
866    
867            List<FeePaymentType> feePaymentTypes = (List<FeePaymentType>) feeMethod.getFeePaymentTypes();
868    
869            for (FeePaymentType feePaymentTypesRecord : feePaymentTypes) {
870                if (feePaymentTypesRecord.getPaymentTypeCode().equalsIgnoreCase(feePaymentTypeCode)) {
871                    isDuplicate = true;
872                    putFieldError(EndowPropertyConstants.FEE_PAYMENT_TYPE_CODE, EndowKeyConstants.FeeMethodConstants.ERROR_DUPLICATE_PAYMENT_TYPE_CODE_ENTERED);
873                }
874            }
875    
876            return isDuplicate;
877        }
878    
879        /**
880         * Check that the frequency code on the Fee Method did not change if the Fee Method is used on at least one KEMID.
881         * 
882         * @param document
883         * @return true if valid, false otherwise
884         */
885        private boolean checkFrequencyCodeNotChangedIfFeeMethodUsedOnAnyKemid(Document document) {
886            MaintenanceDocument maintenanceDocument = (MaintenanceDocument) document;
887            boolean isValid = true;
888    
889            if (KNSConstants.MAINTENANCE_EDIT_ACTION.equals(maintenanceDocument.getNewMaintainableObject().getMaintenanceAction())) {
890                FeeMethod newFeeMethod = (FeeMethod) maintenanceDocument.getNewMaintainableObject().getBusinessObject();
891                FeeMethod oldFeeMethod = (FeeMethod) maintenanceDocument.getOldMaintainableObject().getBusinessObject();
892                String feeMethodCode = newFeeMethod.getCode();
893    
894                if (!StringUtils.equalsIgnoreCase(newFeeMethod.getFeeFrequencyCode(), oldFeeMethod.getFeeFrequencyCode())) {
895                    FeeMethodService feeMethodService = SpringContext.getBean(FeeMethodService.class);
896                    if (feeMethodService.isFeeMethodUsedOnAnyKemid(feeMethodCode)) {
897                        putFieldError(EndowPropertyConstants.FEE_METHOD_FREQUENCY_CODE, EndowKeyConstants.FeeMethodConstants.ERROR_FEE_MTHD_FREQ_CD_CANNOT_BE_CHANGED_IF_FEE_USED_ON_ANY_KEMID);
898                        isValid = false;
899                    }
900                }
901            }
902    
903            return isValid;
904        }
905    
906        /**
907         * This method will check if Fee Transaction Type collection does not have type as EHVA
908         * 
909         * @param feeMethod
910         * @return true if Fee Transaction Type code is not EHVA else return false
911         */
912        private boolean validFeeTransactionTypeEntered(FeeMethod feeMethod) {
913            boolean valid = true;
914    
915            if (feeMethod.getFeeByTransactionType()) {
916                valid = true;
917                List<FeeTransaction> feeTransactions = (List<FeeTransaction>) feeMethod.getFeeTransactions();
918    
919                for (FeeTransaction feeTransactionsRecord : feeTransactions) {
920                    if (feeTransactionsRecord.getDocumentTypeName().equals(EndowConstants.FeeMethod.ENDOWMENT_HISTORY_VALUE_ADJUSTMENT)) {
921                        valid = false;
922                        break;
923                    }
924                }
925                if (!valid) {
926                    putFieldError(EndowPropertyConstants.FEE_TRANSACTION_DOCUMENT_TYPE_NAME_ATTRIBUTE, EndowKeyConstants.FeeMethodConstants.ERROR_INVALID_TRANSACTION_DOCUMENT_TYPE_CODE_ENTERED);
927                }
928            }
929    
930            return valid;
931        }
932    
933        /**
934         * Check to see if fee method etran code has a valiD expense etran code type = E
935         */
936        private boolean validFeeExpenseETranCodeEntered(FeeMethod feeMethod) {
937            boolean valid = true;
938            
939            if (!EndowConstants.EndowmentTransactionTypeCodes.EXPENSE_TYPE_CODE.equalsIgnoreCase(feeMethod.getEndowmentTransactionCode().getEndowmentTransactionTypeCode())) {
940                valid = false;
941                putFieldError(EndowPropertyConstants.FEE_EXPENSE_ENDOWMENT_TRANSACTION_CODE, EndowKeyConstants.FeeMethodConstants.ERROR_INVALID_TRANSACTION_TYPE_CODE_FOR_EXPENSE_ETRANCODE);
942            }
943            
944            return valid;
945        }
946    }