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.coa.document.validation.impl;
017    
018    import java.math.BigDecimal;
019    import java.util.HashMap;
020    import java.util.HashSet;
021    import java.util.List;
022    import java.util.Map;
023    import java.util.Set;
024    
025    import org.apache.commons.lang.StringUtils;
026    import org.kuali.kfs.coa.businessobject.Account;
027    import org.kuali.kfs.coa.businessobject.Chart;
028    import org.kuali.kfs.coa.businessobject.IndirectCostRecoveryRate;
029    import org.kuali.kfs.coa.businessobject.IndirectCostRecoveryRateDetail;
030    import org.kuali.kfs.coa.businessobject.ObjectCode;
031    import org.kuali.kfs.coa.businessobject.SubAccount;
032    import org.kuali.kfs.coa.businessobject.SubObjectCode;
033    import org.kuali.kfs.gl.GeneralLedgerConstants;
034    import org.kuali.kfs.sys.KFSConstants;
035    import org.kuali.kfs.sys.KFSKeyConstants;
036    import org.kuali.kfs.sys.KFSPropertyConstants;
037    import org.kuali.kfs.sys.businessobject.SystemOptions;
038    import org.kuali.kfs.sys.context.SpringContext;
039    import org.kuali.rice.kns.bo.PersistableBusinessObject;
040    import org.kuali.rice.kns.document.MaintenanceDocument;
041    import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase;
042    import org.kuali.rice.kns.service.DataDictionaryService;
043    import org.kuali.rice.kns.util.GlobalVariables;
044    import org.kuali.rice.kns.util.KNSConstants;
045    import org.kuali.rice.kns.util.RiceKeyConstants;
046    
047    
048    public class IndirectCostRecoveryRateRule extends MaintenanceDocumentRuleBase {
049    
050        protected static final String MAINTAINABLE_DETAIL_ERROR_PATH = KNSConstants.MAINTENANCE_NEW_MAINTAINABLE + "indirectCostRecoveryRateDetails";
051        protected static final String MAINTAINABLE_DETAIL_ADDLINE_ERROR_PATH = "add.indirectCostRecoveryRateDetails";
052        
053        protected IndirectCostRecoveryRate indirectCostRecoveryRate;
054        protected IndirectCostRecoveryRateDetail indirectCostRecoveryRateDetail;
055        protected List<IndirectCostRecoveryRateDetail> indirectCostRecoveryRateDetails;
056        
057        public void setupConvenienceObjects() {
058            indirectCostRecoveryRate = (IndirectCostRecoveryRate) super.getNewBo();
059            indirectCostRecoveryRateDetails = indirectCostRecoveryRate.getIndirectCostRecoveryRateDetails();
060        }
061        
062        @Override
063        protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
064            boolean success = true;
065    
066            BigDecimal awardIndrCostRcvyRatePctCredits = new BigDecimal(0);
067            BigDecimal awardIndrCostRcvyRatePctDebits = new BigDecimal(0);
068            
069            if (!processYear()) {
070                success = false;
071            }
072            else {
073                for(int i = 0;i<indirectCostRecoveryRateDetails.size();i++) {
074                    if(indirectCostRecoveryRateDetails.get(i).isActive()) {
075                        GlobalVariables.getMessageMap().addToErrorPath(MAINTAINABLE_DETAIL_ERROR_PATH + "[" + i + "]");
076                        success &= processCollectionLine(indirectCostRecoveryRateDetails.get(i));
077                        GlobalVariables.getMessageMap().removeFromErrorPath(MAINTAINABLE_DETAIL_ERROR_PATH + "[" + i + "]");
078                        
079                        if(indirectCostRecoveryRateDetails.get(i).isActive()) {
080                            if(KFSConstants.GL_CREDIT_CODE.equals(indirectCostRecoveryRateDetails.get(i).getTransactionDebitIndicator())) {
081                                awardIndrCostRcvyRatePctCredits = awardIndrCostRcvyRatePctCredits.add(indirectCostRecoveryRateDetails.get(i).getAwardIndrCostRcvyRatePct());
082                            }
083                            if(KFSConstants.GL_DEBIT_CODE.equals(indirectCostRecoveryRateDetails.get(i).getTransactionDebitIndicator())) {
084                                awardIndrCostRcvyRatePctDebits = awardIndrCostRcvyRatePctDebits.add(indirectCostRecoveryRateDetails.get(i).getAwardIndrCostRcvyRatePct());
085                            }                    
086                        }
087                    }
088                }
089            }
090            success &= checkCreditsAndDebits(awardIndrCostRcvyRatePctCredits, awardIndrCostRcvyRatePctDebits);
091            
092            return success;
093        }
094        
095        public boolean checkCreditsAndDebits(BigDecimal credits, BigDecimal debits) {
096            boolean success = true;
097    
098            // global errors, in KeyConstants or KFSconstants or something, use one for the top of the page (mark doc once)
099            // include the key word active (informing that only active records are considered)
100            if(!(credits.compareTo(debits) == 0)) {
101                GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(KNSConstants.GLOBAL_ERRORS, KFSKeyConstants.IndirectCostRecovery.ERROR_DOCUMENT_ICR_RATE_PERCENTS_NOT_EQUAL,
102                        SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.AWARD_INDR_COST_RCVY_RATE_PCT));
103                success = false;
104            }
105    
106            return success;
107        }
108     
109        @Override
110        public boolean processCustomAddCollectionLineBusinessRules(MaintenanceDocument document, String collectionName, PersistableBusinessObject line) {
111            boolean success = true;
112            IndirectCostRecoveryRateDetail item = (IndirectCostRecoveryRateDetail) line;
113            success &= processCollectionLine(item);
114            return success;
115        }
116        
117        public boolean processCollectionLine(IndirectCostRecoveryRateDetail item) {
118            boolean success = true;
119            
120            success &= validateWildcards(item);
121            if(success) {
122                success &= checkExistence(item) && checkRateFormat(item);
123            }
124                    
125            return success;
126        }
127        
128        public boolean checkExistence(IndirectCostRecoveryRateDetail item) {
129            boolean success = 
130                processYear() && 
131                processChart(item) && 
132                processAccount(item) && 
133                processSubAccount(item) && 
134                processObjectCode(item) && 
135                processSubObjectCode(item);
136            return success;
137        }
138        
139        public boolean processYear() {
140            boolean success = true;
141            Map pkMap = new HashMap();
142            Integer year = indirectCostRecoveryRate.getUniversityFiscalYear();
143            pkMap.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, year);
144            if(!checkExistenceFromTable(SystemOptions.class, pkMap)) {
145                GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(KNSConstants.MAINTENANCE_NEW_MAINTAINABLE + KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR,
146                        RiceKeyConstants.ERROR_EXISTENCE, 
147                        SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRate.class, KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR));
148                success = false;
149            }
150            return success;
151        }
152        
153        protected boolean processChart(IndirectCostRecoveryRateDetail item) {
154            boolean success = true;
155            Map pkMap = new HashMap();
156            String chart = item.getChartOfAccountsCode();
157            if(StringUtils.isNotBlank(chart)) {
158                if(!propertyIsWildcard(chart)) {
159                    pkMap.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, chart);
160                    if(!checkExistenceFromTable(Chart.class, pkMap)) {
161                        logErrorUtility(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, RiceKeyConstants.ERROR_EXISTENCE);
162                        success = false;
163                    }
164                }
165            }
166            return success;
167        }
168        
169        protected boolean processAccount(IndirectCostRecoveryRateDetail item) {
170            boolean success = true;
171            Map pkMap = new HashMap();
172            String chart = item.getChartOfAccountsCode();
173            String acct = item.getAccountNumber();
174            if(StringUtils.isNotBlank(acct)) {
175                if(!propertyIsWildcard(chart)) {
176                    pkMap.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, chart);    
177                }
178                if(!propertyIsWildcard(acct)) {
179                    pkMap.put(KFSPropertyConstants.ACCOUNT_NUMBER, acct);
180                    if(!checkExistenceFromTable(Account.class, pkMap)) {
181                        logErrorUtility(KFSPropertyConstants.ACCOUNT_NUMBER, RiceKeyConstants.ERROR_EXISTENCE);
182                        success = false;
183                    }
184                }
185            }
186            return success;
187        }
188        
189        protected boolean processSubAccount(IndirectCostRecoveryRateDetail item) {
190            boolean success = true;
191            Map pkMap = new HashMap();
192            String chart = item.getChartOfAccountsCode();
193            String acct = item.getAccountNumber();
194            String subAcct = item.getSubAccountNumber();
195            if(StringUtils.isNotBlank(subAcct) && !StringUtils.containsOnly(subAcct, "-")) { // if doesn't contain only dashes
196                if(!propertyIsWildcard(chart)) {
197                    pkMap.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, chart);    
198                }
199                if(!propertyIsWildcard(acct)) {
200                    pkMap.put(KFSPropertyConstants.ACCOUNT_NUMBER, acct);    
201                }
202                if(!propertyIsWildcard(subAcct) && StringUtils.isNotBlank(subAcct) && !StringUtils.containsOnly(subAcct, "-")) {
203                    pkMap.put(KFSPropertyConstants.SUB_ACCOUNT_NUMBER, subAcct);
204                    if(!checkExistenceFromTable(SubAccount.class, pkMap)) {
205                        logErrorUtility(KFSPropertyConstants.SUB_ACCOUNT_NUMBER, RiceKeyConstants.ERROR_EXISTENCE);
206                        success = false;
207                    }
208                }
209            }
210            return success;
211        }
212        
213        protected boolean processObjectCode(IndirectCostRecoveryRateDetail item) {
214            boolean success = true;
215            Map pkMap = new HashMap();
216            Integer year = indirectCostRecoveryRate.getUniversityFiscalYear();
217            String chart = item.getChartOfAccountsCode();
218            String objCd = item.getFinancialObjectCode();
219            pkMap.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, year);
220            if(StringUtils.isNotBlank(objCd)) {
221                if(!propertyIsWildcard(chart)) {
222                    pkMap.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, chart);    
223                }
224                if(!propertyIsWildcard(objCd)) {
225                    pkMap.put(KFSPropertyConstants.FINANCIAL_OBJECT_CODE, objCd);
226                    if(!checkExistenceFromTable(ObjectCode.class, pkMap)) {
227                        logErrorUtility(KFSPropertyConstants.FINANCIAL_OBJECT_CODE, RiceKeyConstants.ERROR_EXISTENCE);
228                        success = false;
229                    }
230                }
231            }
232            return success;
233        }
234        
235        protected boolean processSubObjectCode(IndirectCostRecoveryRateDetail item) { // chart being a wildcard implies account number being a wildcard, redundant checking?
236            boolean success = true;
237            Map pkMap = new HashMap();
238            Integer year = indirectCostRecoveryRate.getUniversityFiscalYear();
239            String chart = item.getChartOfAccountsCode();
240            String acct = item.getAccountNumber();
241            String objCd = item.getFinancialObjectCode();
242            String subObjCd = item.getFinancialSubObjectCode();
243            if(StringUtils.isNotBlank(subObjCd) && !propertyIsWildcard(subObjCd) && !StringUtils.containsOnly(subObjCd, "-")) {
244                pkMap.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, year);
245                if(!propertyIsWildcard(chart)) {
246                    pkMap.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, chart);    
247                }
248                if(!propertyIsWildcard(acct)) {
249                    pkMap.put(KFSPropertyConstants.ACCOUNT_NUMBER, acct);    
250                }
251                if(!propertyIsWildcard(objCd)) {
252                    pkMap.put(KFSPropertyConstants.FINANCIAL_OBJECT_CODE, objCd);
253                }
254                pkMap.put(KFSPropertyConstants.FINANCIAL_SUB_OBJECT_CODE, subObjCd);
255                if(!GeneralLedgerConstants.PosterService.SYMBOL_USE_EXPENDITURE_ENTRY.equals(subObjCd) && !checkExistenceFromTable(SubObjectCode.class, pkMap)) {
256                    logErrorUtility(KFSPropertyConstants.FINANCIAL_SUB_OBJECT_CODE, RiceKeyConstants.ERROR_EXISTENCE);
257                    success = false;
258                }
259            }
260            return success;
261        }
262        
263        public boolean checkExistenceFromTable(Class clazz, Map fieldValues) {
264            return getBoService().countMatching(clazz, fieldValues) != 0;
265        }
266        
267        public boolean validateWildcards(IndirectCostRecoveryRateDetail item) {
268            boolean success = false;
269            if(!itemUsesWildcard(item) || (itemUsesWildcard(item) && itemPassesWildcardRules(item))) {
270                success = true;
271            }
272            return success;
273        }
274        
275        public boolean itemPassesWildcardRules(IndirectCostRecoveryRateDetail item) {
276            boolean success = false;
277            String[] wildcards = {GeneralLedgerConstants.PosterService.SYMBOL_USE_EXPENDITURE_ENTRY, GeneralLedgerConstants.PosterService.SYMBOL_USE_ICR_FROM_ACCOUNT};
278            
279            success = !itemUsesWildcard(item) || checkWildcardRules(item);
280            
281            return success;
282        }
283        
284        public boolean itemUsesWildcard(IndirectCostRecoveryRateDetail item) {
285            boolean success = false;
286            String chart = item.getChartOfAccountsCode();
287            String acct = item.getAccountNumber();
288            String subAcct = item.getSubAccountNumber();
289            String objCd = item.getFinancialObjectCode();
290            String subObjCd = item.getFinancialSubObjectCode();
291            String[] fields = {chart,acct,subAcct,objCd,subObjCd};
292            
293            for(int i=0;i<fields.length;i++) {
294                success |= propertyIsWildcard(fields[i]);
295            }
296            
297            return success;
298        }
299        
300        public boolean propertyIsWildcard(String property) {
301            return GeneralLedgerConstants.PosterService.SYMBOL_USE_EXPENDITURE_ENTRY.equals(property) || GeneralLedgerConstants.PosterService.SYMBOL_USE_ICR_FROM_ACCOUNT.equals(property);
302        }
303    
304        protected boolean checkAccountNumberWildcardRules(IndirectCostRecoveryRateDetail item) {
305            String accountNumber = item.getAccountNumber();
306            boolean success = true;
307            if (!accountNumber.equals(item.getChartOfAccountsCode())) {
308                GlobalVariables.getMessageMap().putError(
309                        KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE,
310                        KFSKeyConstants.IndirectCostRecovery.ERROR_DOCUMENT_ICR_WILDCARDS_MUST_MATCH,
311                        SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.ACCOUNT_NUMBER),
312                        SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE)
313                        );
314                success = false;
315            }
316            
317            String subAccountNumber = item.getSubAccountNumber();
318            
319            // If # is entered on account, then # "must" be entered for chart and sub account "must" be dashes.
320            if (GeneralLedgerConstants.PosterService.SYMBOL_USE_ICR_FROM_ACCOUNT.equals(accountNumber)) {
321                if (!GeneralLedgerConstants.PosterService.SYMBOL_USE_ICR_FROM_ACCOUNT.equals(item.getChartOfAccountsCode()) && !StringUtils.containsOnly(subAccountNumber, KFSConstants.DASH)) {
322                    GlobalVariables.getMessageMap().putError(
323                            KFSPropertyConstants.SUB_ACCOUNT_NUMBER, 
324                            KFSKeyConstants.IndirectCostRecovery.ERROR_DOCUMENT_ICR_FIELD_MUST_BE_DASHES,
325                            SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.ACCOUNT_NUMBER),
326                            GeneralLedgerConstants.PosterService.SYMBOL_USE_ICR_FROM_ACCOUNT,
327                            SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.SUB_ACCOUNT_NUMBER));
328                    success = false;
329                }
330            }
331            
332            if (GeneralLedgerConstants.PosterService.SYMBOL_USE_EXPENDITURE_ENTRY.equals(accountNumber)) {
333                if (!(StringUtils.equals(GeneralLedgerConstants.PosterService.SYMBOL_USE_EXPENDITURE_ENTRY, subAccountNumber) || !StringUtils.containsOnly(subAccountNumber, KFSConstants.DASH))) {
334                    GlobalVariables.getMessageMap().putError(KFSPropertyConstants.SUB_ACCOUNT_NUMBER, 
335                            KFSKeyConstants.IndirectCostRecovery.ERROR_DOCUMENT_ICR_ACCOUNT_USE_EXPENDITURE_ENTRY_WILDCARD_RESTRICTION_ON_SUB_ACCOUNT);
336                    success = false;
337                }
338            }
339            return success;
340        }
341        
342        protected boolean checkAccountNumberNotWildcardRules(IndirectCostRecoveryRateDetail item) {
343            boolean success = true;
344            if (propertyIsWildcard(item.getSubAccountNumber())) {
345                if (!GeneralLedgerConstants.PosterService.SYMBOL_USE_ICR_FROM_ACCOUNT.equals(item.getSubAccountNumber())) {
346                    GlobalVariables.getMessageMap().putError(KFSPropertyConstants.SUB_ACCOUNT_NUMBER, KFSKeyConstants.IndirectCostRecovery.ERROR_DOCUMENT_ICR_CANNOT_BE_WILDCARD,
347                            SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.SUB_ACCOUNT_NUMBER),
348                            SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.ACCOUNT_NUMBER));
349                    success = false;
350                }
351            }
352            return success;
353        }
354    
355        protected boolean checkObjectCodeWildcardRules(IndirectCostRecoveryRateDetail item) {
356            String financialObjectCode = item.getFinancialObjectCode();
357            boolean success = true;
358            if (propertyIsWildcard(financialObjectCode)) {
359                if (GeneralLedgerConstants.PosterService.SYMBOL_USE_ICR_FROM_ACCOUNT.equals(financialObjectCode)) {
360                    GlobalVariables.getMessageMap().putError(
361                            KFSPropertyConstants.FINANCIAL_OBJECT_CODE,
362                            KFSKeyConstants.IndirectCostRecovery.ERROR_DOCUMENT_ICR_WILDCARD_NOT_VALID,
363                            GeneralLedgerConstants.PosterService.SYMBOL_USE_ICR_FROM_ACCOUNT,
364                            SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.FINANCIAL_OBJECT_CODE),
365                            GeneralLedgerConstants.PosterService.SYMBOL_USE_EXPENDITURE_ENTRY
366                            );
367                    success = false;
368                } 
369                else {
370                    if (!GeneralLedgerConstants.PosterService.SYMBOL_USE_EXPENDITURE_ENTRY.equals(item.getChartOfAccountsCode())) {
371                        GlobalVariables.getMessageMap().putError(
372                                KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE,
373                                KFSKeyConstants.IndirectCostRecovery.ERROR_DOCUMENT_ICR_WILDCARDS_MUST_MATCH,
374                                SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.FINANCIAL_OBJECT_CODE),
375                                SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE)
376                                );
377                        success = false;
378                    }
379                }
380            }
381            return success;
382        }
383        
384        protected boolean checkSubObjectWildcardRules(IndirectCostRecoveryRateDetail item) {
385            String financialSubObjectCode = item.getFinancialSubObjectCode();
386            boolean success = true;
387            if (propertyIsWildcard(financialSubObjectCode)) {
388                if (GeneralLedgerConstants.PosterService.SYMBOL_USE_ICR_FROM_ACCOUNT.equals(financialSubObjectCode)) {
389                    GlobalVariables.getMessageMap().putError(
390                            KFSPropertyConstants.FINANCIAL_SUB_OBJECT_CODE,
391                            KFSKeyConstants.IndirectCostRecovery.ERROR_DOCUMENT_ICR_WILDCARD_NOT_VALID,
392                            GeneralLedgerConstants.PosterService.SYMBOL_USE_ICR_FROM_ACCOUNT,
393                            SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.FINANCIAL_SUB_OBJECT_CODE),
394                            GeneralLedgerConstants.PosterService.SYMBOL_USE_EXPENDITURE_ENTRY
395                            );
396                    success = false;
397                }
398                else {
399                    if (!GeneralLedgerConstants.PosterService.SYMBOL_USE_EXPENDITURE_ENTRY.equals(item.getChartOfAccountsCode())) {
400                        GlobalVariables.getMessageMap().putError(
401                                KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE,
402                                KFSKeyConstants.IndirectCostRecovery.ERROR_DOCUMENT_ICR_WILDCARDS_MUST_MATCH,
403                                SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.FINANCIAL_SUB_OBJECT_CODE),
404                                SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE)
405                                );
406                        success = false;
407                    }
408                    if (!GeneralLedgerConstants.PosterService.SYMBOL_USE_EXPENDITURE_ENTRY.equals(item.getAccountNumber())) {
409                        GlobalVariables.getMessageMap().putError(
410                                KFSPropertyConstants.ACCOUNT_NUMBER,
411                                KFSKeyConstants.IndirectCostRecovery.ERROR_DOCUMENT_ICR_WILDCARDS_MUST_MATCH,
412                                SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.FINANCIAL_SUB_OBJECT_CODE),
413                                SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.ACCOUNT_NUMBER)
414                                );
415                        success = false;
416                    }
417                    if (!GeneralLedgerConstants.PosterService.SYMBOL_USE_EXPENDITURE_ENTRY.equals(item.getFinancialObjectCode())) {
418                        GlobalVariables.getMessageMap().putError(
419                                KFSPropertyConstants.FINANCIAL_OBJECT_CODE,
420                                KFSKeyConstants.IndirectCostRecovery.ERROR_DOCUMENT_ICR_WILDCARDS_MUST_MATCH,
421                                SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.FINANCIAL_SUB_OBJECT_CODE),
422                                SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.FINANCIAL_OBJECT_CODE)
423                                );
424                        success = false;
425                    }
426                }
427            }
428            return success;
429        }
430        
431        protected boolean checkSubAccountWildcardRules(IndirectCostRecoveryRateDetail item) {
432            boolean success = true;
433            String subAccountNumber = item.getSubAccountNumber();
434            if (GeneralLedgerConstants.PosterService.SYMBOL_USE_ICR_FROM_ACCOUNT.equals(subAccountNumber)) {
435                GlobalVariables.getMessageMap().putError(
436                        KFSPropertyConstants.SUB_ACCOUNT_NUMBER,
437                        KFSKeyConstants.IndirectCostRecovery.ERROR_DOCUMENT_ICR_WILDCARD_NOT_VALID,
438                        GeneralLedgerConstants.PosterService.SYMBOL_USE_ICR_FROM_ACCOUNT,
439                        SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.SUB_ACCOUNT_NUMBER),
440                        GeneralLedgerConstants.PosterService.SYMBOL_USE_EXPENDITURE_ENTRY
441                        );
442                success = false;
443            }
444            else {
445                if (!GeneralLedgerConstants.PosterService.SYMBOL_USE_EXPENDITURE_ENTRY.equals(item.getChartOfAccountsCode())) {
446                    GlobalVariables.getMessageMap().putError(
447                            KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE,
448                            KFSKeyConstants.IndirectCostRecovery.ERROR_DOCUMENT_ICR_WILDCARDS_MUST_MATCH,
449                            SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.SUB_ACCOUNT_NUMBER),
450                            SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE)
451                            );
452                    success = false;
453                }
454                if (!GeneralLedgerConstants.PosterService.SYMBOL_USE_EXPENDITURE_ENTRY.equals(item.getAccountNumber())) {
455                    GlobalVariables.getMessageMap().putError(
456                            KFSPropertyConstants.ACCOUNT_NUMBER,
457                            KFSKeyConstants.IndirectCostRecovery.ERROR_DOCUMENT_ICR_WILDCARDS_MUST_MATCH,
458                            SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.SUB_ACCOUNT_NUMBER),
459                            SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.ACCOUNT_NUMBER)
460                            );
461                    success = false;
462                }
463                
464            }
465            return success;
466        }
467        
468        /*
469         ** If @ is entered on chart, then @ "must" be entered for account and the sub account "must" be either @ or dashes. 
470         ** If @ is entered on account, then @ "must" be entered for chart and "may" be entered for sub account.
471         ** If @ is entered on sub account, then @ "must" be entered for chart and account.
472         ** If @ is entered on object code, then @ "must" be entered for chart and "may" be entered for account, sub account and sub object. 
473         * (This rule is the murkiest as enter @ in account, sub account, and sub object kicks of some of the other rules in this section).
474         ** If @ is entered on sub object code, then @ "must" be entered for chart, account, and object code and "may" be entered for sub account.
475    
476         ** If # is entered on chart, then # "must" be entered for account and sub account "must" be dashes.
477         ** If # is entered on account, then # "must" be entered for chart and sub account "must" be dashes.
478         ** # can not be entered on the sub account.
479         ** # can not be entered on the object code.
480         ** # can not be entered on the sub object code.
481         * 
482         */
483        
484        public boolean checkWildcardRules(IndirectCostRecoveryRateDetail item) {
485            boolean success = checkAtMostOneWildcardUsed(item);
486            if (success) {
487                if (propertyIsWildcard(item.getFinancialObjectCode())) {
488                    success &= checkObjectCodeWildcardRules(item); // verified
489                }
490                else {
491                }
492                if (propertyIsWildcard(item.getAccountNumber())) {
493                    success &= checkAccountNumberWildcardRules(item); // verified
494                }
495                else {
496                    success &= checkAccountNumberNotWildcardRules(item);
497                }
498                if (propertyIsWildcard(item.getFinancialSubObjectCode())) {
499                    success &= checkSubObjectWildcardRules(item); // verified
500                }
501                
502                if (propertyIsWildcard(item.getSubAccountNumber())) {
503                    success &= checkSubAccountWildcardRules(item); // verified
504                }
505                
506                if (!(propertyIsWildcard(item.getFinancialObjectCode()) || propertyIsWildcard(item.getAccountNumber()))) {
507                    // chart code can't be the only wildcard on the item
508                    if (success && propertyIsWildcard(item.getChartOfAccountsCode())) {
509                        success = false;
510                        GlobalVariables.getMessageMap().putError(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, KFSKeyConstants.IndirectCostRecovery.ERROR_DOCUMENT_ICR_CHART_CODE_NOT_ONLY_WILDCARD,
511                                SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE),
512                                SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.ACCOUNT_NUMBER),
513                                SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, KFSPropertyConstants.FINANCIAL_OBJECT_CODE));
514                    }
515                }
516            }
517            return success;
518        }
519    
520        protected boolean checkAtMostOneWildcardUsed(IndirectCostRecoveryRateDetail item) {
521            String chart = item.getChartOfAccountsCode();
522            String acct = item.getAccountNumber();
523            String subAcct = item.getSubAccountNumber();
524            String objCd = item.getFinancialObjectCode();
525            String subObjCd = item.getFinancialSubObjectCode();
526            
527            boolean success = true;
528            String errorPropertyName = null;
529            
530            Set<String> wildcards = new HashSet<String>();
531            if (propertyIsWildcard(chart)) {
532                wildcards.add(chart);
533                errorPropertyName = KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE;
534            }
535            if (success && propertyIsWildcard(acct)) {
536                wildcards.add(acct);
537                if (wildcards.size() > 1) {
538                    success = false;
539                    errorPropertyName = KFSPropertyConstants.ACCOUNT_NUMBER;
540                }
541            }
542            if (success && propertyIsWildcard(subAcct)) {
543                wildcards.add(subAcct);
544                if (wildcards.size() > 1) {
545                    success = false;
546                    errorPropertyName = KFSPropertyConstants.SUB_ACCOUNT_NUMBER;
547                }
548            }
549            if (success && propertyIsWildcard(objCd)) {
550                wildcards.add(objCd);
551                if (wildcards.size() > 1) {
552                    success = false;
553                    errorPropertyName = KFSPropertyConstants.FINANCIAL_OBJECT_CODE;
554                }
555            }
556            if (success && propertyIsWildcard(subObjCd)) {
557                wildcards.add(subObjCd);
558                if (wildcards.size() > 1) {
559                    success = false;
560                    errorPropertyName = KFSPropertyConstants.FINANCIAL_SUB_OBJECT_CODE;
561                }
562            }
563            
564            if (!success) {
565                GlobalVariables.getMessageMap().putError(
566                        errorPropertyName,
567                        KFSKeyConstants.IndirectCostRecovery.ERROR_DOCUMENT_ICR_MULTIPLE_WILDCARDS_ON_ITEM);
568            }
569            return success;
570        }
571        
572        public boolean checkRateFormat(IndirectCostRecoveryRateDetail item) {
573            boolean success = true;
574            BigDecimal zero = new BigDecimal(0.00);
575            if(!(item.getAwardIndrCostRcvyRatePct() == null)) {
576                if(item.getAwardIndrCostRcvyRatePct().scale() > 3) {
577                    logErrorUtility(KFSPropertyConstants.AWARD_INDR_COST_RCVY_RATE_PCT, KFSKeyConstants.IndirectCostRecovery.ERROR_DOCUMENT_ICR_RATE_PERCENT_INVALID_FORMAT_SCALE);
578                    success = false;
579                }
580                if(item.getAwardIndrCostRcvyRatePct().compareTo(zero) < 0) {
581                    logErrorUtility(KFSPropertyConstants.AWARD_INDR_COST_RCVY_RATE_PCT, KFSKeyConstants.IndirectCostRecovery.ERROR_DOCUMENT_ICR_RATE_PERCENT_INVALID_FORMAT_ZERO);
582                    success = false;
583                }            
584            } else {
585                
586            }
587            return success;
588        }
589        
590        public void logErrorUtility(String propertyName, String errorKey) {
591            GlobalVariables.getMessageMap().putError(propertyName, errorKey, SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(IndirectCostRecoveryRateDetail.class, propertyName));
592        }
593        
594    }