edu.cornell.kfs.coa.document.validation.impl.AccountReversionGlobalRule.java Source code

Java tutorial

Introduction

Here is the source code for edu.cornell.kfs.coa.document.validation.impl.AccountReversionGlobalRule.java

Source

/*
 * Copyright 2007 The Kuali Foundation
 * 
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.opensource.org/licenses/ecl2.php
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package edu.cornell.kfs.coa.document.validation.impl;

import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.kuali.kfs.coa.businessobject.FundGroup;
import org.kuali.kfs.coa.businessobject.ObjectCode;
import org.kuali.kfs.coa.businessobject.SubFundGroup;
import org.kuali.kfs.coa.document.validation.impl.GlobalDocumentRuleBase;
import org.kuali.kfs.coa.service.ObjectCodeService;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KFSKeyConstants;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.rice.core.api.util.ConcreteKeyValue;
import org.kuali.rice.core.api.util.RiceKeyConstants;
import org.kuali.kfs.coreservice.framework.parameter.ParameterService;
import org.kuali.kfs.kns.document.MaintenanceDocument;
import org.kuali.kfs.krad.bo.PersistableBusinessObject;
import org.kuali.kfs.krad.util.GlobalVariables;
import org.kuali.kfs.krad.util.ObjectUtils;

import edu.cornell.kfs.coa.businessobject.AccountReversion;
import edu.cornell.kfs.coa.businessobject.AccountReversionGlobal;
import edu.cornell.kfs.coa.businessobject.AccountReversionGlobalAccount;
import edu.cornell.kfs.coa.businessobject.AccountReversionGlobalDetail;
import edu.cornell.kfs.coa.businessobject.Reversion;
import edu.cornell.kfs.coa.businessobject.options.ReversionCodeValuesFinder;
import edu.cornell.kfs.coa.document.AccountReversionGlobalMaintainableImpl;
import edu.cornell.kfs.coa.service.AccountReversionService;
import edu.cornell.kfs.sys.CUKFSConstants;
import edu.cornell.kfs.sys.CUKFSKeyConstants;
import edu.cornell.kfs.sys.CUKFSPropertyConstants;

/**
 * 
 * This class implements the business rules for {@link AccountReversionGlobal}
 */
public class AccountReversionGlobalRule extends GlobalDocumentRuleBase {
    private static final Logger LOG = LogManager.getLogger(AccountReversionGlobalRule.class);

    private static final String GLOBAL_ACCOUNT_FIELDS_SECTION = "Edit Global Account Reversion";
    private static final String GLOBAL_DETAIL_FIELDS_SECTION = "Edit Global Account Reversion Details";

    protected AccountReversionGlobal globalAccountReversion;
    protected AccountReversionService accountReversionService;
    protected ObjectCodeService objectCodeService;

    /**
     * 
     * Constructs a AccountReversionGlobalRule
     * Pseudo-injects services 
     */
    public AccountReversionGlobalRule() {
        super();
        setAccountReversionService(SpringContext.getBean(AccountReversionService.class));
        setObjectCodeService(SpringContext.getBean(ObjectCodeService.class));
    }

    /**
     * This method sets the convenience objects like newAccount and oldAccount, so you have short and easy handles to the new and
     * old objects contained in the maintenance document. It also calls the BusinessObjectBase.refresh(), which will attempt to load
     * all sub-objects from the DB by their primary keys, if available.
     * 
     * @param document - the maintenanceDocument being evaluated
     * @see org.kuali.kfs.kns.maintenance.rules.MaintenanceDocumentRuleBase#setupConvenienceObjects()
     */
    @Override
    public void setupConvenienceObjects() {
        this.globalAccountReversion = (AccountReversionGlobal) super.getNewBo();
        for (AccountReversionGlobalDetail detail : this.globalAccountReversion.getAccountReversionGlobalDetails()) {
            detail.refreshNonUpdateableReferences();
        }
        for (AccountReversionGlobalAccount org : this.globalAccountReversion.getAccountReversionGlobalAccounts()) {
            org.refreshNonUpdateableReferences();
        }
    }

    /**
     * Calls the basic rules check on document save:
     * <ul>
     * <li>{@link AccountReversionGlobalRule#checkSimpleRules(AccountReversionGlobal)}</li>
     * </ul>
     * Does not fail on rules failure
     * @see org.kuali.kfs.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomSaveDocumentBusinessRules(org.kuali.kfs.kns.document.MaintenanceDocument)
     */
    @Override
    protected boolean processCustomSaveDocumentBusinessRules(MaintenanceDocument document) {
        checkSimpleRules(getGlobalAccountReversion());
        return true; // always return true on save
    }

    /**
     * Calls the basic rules check on document approval:
     * <ul>
     * <li>{@link AccountReversionGlobalRule#checkSimpleRules(AccountReversionGlobal)}</li>
     * </ul>
     * Fails on rules failure
     * @see org.kuali.kfs.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomApproveDocumentBusinessRules(org.kuali.kfs.kns.document.MaintenanceDocument)
     */
    @Override
    protected boolean processCustomApproveDocumentBusinessRules(MaintenanceDocument document) {
        return checkSimpleRules(getGlobalAccountReversion());
    }

    /**
     * Calls the basic rules check on document routing:
     * <ul>
     * <li>{@link AccountReversionGlobalRule#checkSimpleRules(AccountReversionGlobal)}</li>
     * </ul>
     * Fails on rules failure
     * @see org.kuali.kfs.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.kfs.kns.document.MaintenanceDocument)
     */
    @Override
    protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
        return checkSimpleRules(getGlobalAccountReversion());
    }

    /**
     * This performs rules checks whenever a new {@link AccountReversionGlobalDetail} or {@link AccountReversionGlobalAccount} is added
     * <p>
     * This includes:
     * <ul>
     * <li>{@link AccountReversionGlobalRule#checkDetailObjectCodeValidity(AccountReversionGlobal, AccountReversionGlobalDetail)}</li>
     * <li>{@link AccountReversionGlobalRule#checkDetailObjectReversionCodeValidity(AccountReversionGlobalDetail)}</li>
     * <li>ensure that the chart of accounts code and account number for {@link AccountReversionGlobalAccount} are not empty values</li>
     * <li>{@link AccountReversionGlobalRule#checkAllObjectCodesForValidity(AccountReversionGlobal, AccountReversionGlobalAccount)}</li>
     * <li>{@link AccountReversionGlobalRule#checkAccountChartValidity(AccountReversionGlobalAccount)</li>
     * <li>{@link AccountReversionGlobalRule#checkAccountValidity(AccountReversionGlobalAccount)</li>
     * <li>{@link AccountReversionGlobalRule#checkAccountIsNotAmongAcctRevAccounts(AccountReversionGlobal, AccountReversionGlobalAccount)</li>
     * </ul>
     * @see org.kuali.kfs.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomAddCollectionLineBusinessRules(org.kuali.kfs.kns.document.MaintenanceDocument,
     *      java.lang.String, org.kuali.kfs.kns.bo.PersistableBusinessObject)
     */
    @Override
    public boolean processCustomAddCollectionLineBusinessRules(MaintenanceDocument document, String collectionName,
            PersistableBusinessObject line) {
        boolean success = true;
        AccountReversionGlobal globalAcctRev = (AccountReversionGlobal) ((AccountReversionGlobalMaintainableImpl) document
                .getNewMaintainableObject()).getBusinessObject();
        if (line instanceof AccountReversionGlobalDetail) {
            AccountReversionGlobalDetail detail = (AccountReversionGlobalDetail) line;
            success &= checkDetailObjectCodeValidity(globalAcctRev, detail);
            success &= checkDetailObjectReversionCodeValidity(detail);
        } else if (line instanceof AccountReversionGlobalAccount) {
            AccountReversionGlobalAccount acct = (AccountReversionGlobalAccount) line;
            if (!checkEmptyValue(acct.getChartOfAccountsCode()) || !checkEmptyValue(acct.getAccountNumber())) {
                // Skip Most validation if chart or account are empty. The default required-field checking will populate the error map accordingly.
                success = false;
            }
            if (success) {
                success &= checkAllObjectCodesForValidity(globalAcctRev, acct);
                success &= checkAccountChartValidity(acct);
                success &= checkAccountValidity(acct);
                success &= checkAccountIsNotAmongAcctRevAccounts(globalAcctRev, acct);
            }
        }
        return success;
    }

    /**
     * Convenient convenience method to test all the simple rules in one go. Including:
     * <ul>
     * <li>{@link AccountReversionGlobalRule#checkBudgetReversionAccountPair(AccountReversionGlobal)}</li>
     * <li>{@link AccountReversionGlobalRule#checkCashReversionAccountPair(AccountReversionGlobal)}</li>
     * <li>{@link AccountReversionGlobalRule#areAllDetailsValid(AccountReversionGlobal)}</li>
     * <li>{@link AccountReversionGlobalRule#areAllOrganizationsValid(AccountReversionGlobal)</li>
     * </ul>
     * @param globalOrgRev the global organization reversion to check
     * @return true if the new global organization reversion passes all tests, false if it deviates even a tiny little bit
     */
    public boolean checkSimpleRules(AccountReversionGlobal globalAcctRev) {
        boolean success = true;

        success &= checkBudgetReversionAccountPair(globalAcctRev);
        success &= checkCashReversionAccountPair(globalAcctRev);
        success &= validateAccountFundGroup(globalAcctRev);
        success &= validateAccountSubFundGroup(globalAcctRev);

        success &= areAllDetailsValid(globalAcctRev);
        success &= areAllAccountsValid(globalAcctRev);

        return success;
    }

    /**
     * This method makes sure that if one part of the Budget Reversion Chart/Account pair is specified, both are specified, or an
     * error is thrown.
     * 
     * @param globalAcctRev the Global Account Reversion to check
     * @return true if budget reversion chart/account pair is specified correctly, false if otherwise
     */
    public boolean checkBudgetReversionAccountPair(AccountReversionGlobal globalAcctRev) {
        boolean success = true;
        if ((!StringUtils.isBlank(globalAcctRev.getBudgetReversionChartOfAccountsCode())
                && StringUtils.isBlank(globalAcctRev.getBudgetReversionAccountNumber()))
                || (StringUtils.isBlank(globalAcctRev.getBudgetReversionChartOfAccountsCode())
                        && !StringUtils.isBlank(globalAcctRev.getBudgetReversionAccountNumber()))) {
            success = false;
            GlobalVariables.getMessageMap().putError(
                    MAINTAINABLE_ERROR_PREFIX + "budgetReversionChartOfAccountsCode",
                    CUKFSKeyConstants.ERROR_DOCUMENT_GLOBAL_ACCT_REVERSION_BUDGET_REVERSION_INCOMPLETE,
                    new String[] {});
        }
        return success;
    }

    /**
     * This method makes sure that if one part of the Cash Reversion Chart/Account pair is specified, both are specified, or an
     * error is thrown.
     * 
     * @param globalAcctRev the Global Account Reversion to check
     * @return true if cash reversion chart/account pair is specified correctly, false if otherwise
     */
    public boolean checkCashReversionAccountPair(AccountReversionGlobal globalAcctRev) {
        boolean success = true;
        if ((!StringUtils.isBlank(globalAcctRev.getCashReversionFinancialChartOfAccountsCode())
                && StringUtils.isBlank(globalAcctRev.getCashReversionAccountNumber()))
                || (StringUtils.isBlank(globalAcctRev.getCashReversionFinancialChartOfAccountsCode())
                        && !StringUtils.isBlank(globalAcctRev.getCashReversionAccountNumber()))) {
            success = false;
            GlobalVariables.getMessageMap().putError(
                    MAINTAINABLE_ERROR_PREFIX + "cashReversionFinancialChartOfAccountsCode",
                    CUKFSKeyConstants.ERROR_DOCUMENT_GLOBAL_ACCT_REVERSION_CASH_REVERSION_INCOMPLETE,
                    new String[] {});
        }
        return success;
    }

    /**
     * Tests if all of the {@link AccountReversionGlobalDetail} objects associated with the given global account reversion are
     * valid.
     * 
     * @param globalAcctRev the global account reversion to check
     * @return true if valid, false otherwise
     */
    public boolean areAllDetailsValid(AccountReversionGlobal globalAcctRev) {
        boolean success = true;
        for (int i = 0; i < globalAcctRev.getAccountReversionGlobalDetails().size(); i++) {
            AccountReversionGlobalDetail detail = globalAcctRev.getAccountReversionGlobalDetails().get(i);

            String errorPath = MAINTAINABLE_ERROR_PREFIX + "accountReversionGlobalDetails[" + i + "]";
            GlobalVariables.getMessageMap().addToErrorPath(errorPath);

            if (!StringUtils.isBlank(detail.getAccountReversionObjectCode())
                    && !StringUtils.isBlank(detail.getAccountReversionCode())) {
                success &= this.checkDetailAcctReversionCategoryValidity(detail);
                success &= this.checkDetailObjectCodeValidity(globalAcctRev, detail);
                success &= this.checkDetailObjectReversionCodeValidity(detail);
            }
            GlobalVariables.getMessageMap().removeFromErrorPath(errorPath);
        }
        return success;
    }

    /**
     * Tests if the Account Reversion Category existed in the database and was active.
     * 
     * @param detail AccountReversionGlobalDetail to check
     * @return true if the category is valid, false if otherwise
     */
    public boolean checkDetailAcctReversionCategoryValidity(AccountReversionGlobalDetail detail) {
        boolean success = true;
        if (StringUtils.isBlank(detail.getAccountReversionCategoryCode())) {
            success = false;
            GlobalVariables.getMessageMap().putError(CUKFSPropertyConstants.ACCT_REVERSION_CATEGORY_CODE,
                    KFSKeyConstants.ERROR_REQUIRED, "Account Reversion Category");
        } else {
            detail.refreshReferenceObject("reversionCategory");
            if (ObjectUtils.isNull(detail.getReversionCategory()) || !detail.getReversionCategory().isActive()) {
                success = false;
                GlobalVariables.getMessageMap().putError(CUKFSPropertyConstants.ACCT_REVERSION_CATEGORY_CODE,
                        CUKFSKeyConstants.ERROR_DOCUMENT_GLOBAL_ACCT_REVERSION_INVALID_ACCT_REVERSION_CATEGORY,
                        new String[] { detail.getAccountReversionCategoryCode() });
            }
        }
        return success;
    }

    /**
     * For each account, tests if the object code in the detail exists in the system and is active
     * 
     * @param globalAcctRev the global account reversion to check
     * @param detail the AccountReversionGlobalDetail to check
     * @return true if it is valid, false if otherwise
     */
    public boolean checkDetailObjectCodeValidity(AccountReversionGlobal globalAcctRev,
            AccountReversionGlobalDetail detail) {
        boolean success = true;
        for (AccountReversionGlobalAccount acct : globalAcctRev.getAccountReversionGlobalAccounts()) {
            if (!validObjectCode(globalAcctRev.getUniversityFiscalYear(), acct.getChartOfAccountsCode(),
                    detail.getAccountReversionObjectCode())) {
                success = false;
                GlobalVariables.getMessageMap().putError("accountReversionObjectCode",
                        CUKFSKeyConstants.ERROR_DOCUMENT_GLOBAL_ACCT_REVERSION_OBJECT_CODE_INVALID,
                        new String[] { globalAcctRev.getUniversityFiscalYear().toString(),
                                acct.getChartOfAccountsCode(), detail.getAccountReversionObjectCode(),
                                acct.getChartOfAccountsCode(), acct.getAccountNumber() });
            }
        }
        return success;
    }

    /**
     * This method loops through each of the AccountReversionGlobalDetail objects, checking that the entered object codes for
     * each of them are compatible with the AccountReversionGlobalAccount specified.
     * 
     * @param globalAcctRev the global account reversion to check
     * @param acct the AccountReversionGlobalOrganization with a new chart to check against all of the object codes
     * @return true if there are no conflicts, false if otherwise
     */
    public boolean checkAllObjectCodesForValidity(AccountReversionGlobal globalAcctRev,
            AccountReversionGlobalAccount acct) {
        boolean success = true;
        for (AccountReversionGlobalDetail detail : globalAcctRev.getAccountReversionGlobalDetails()) {
            if (!validObjectCode(globalAcctRev.getUniversityFiscalYear(), acct.getChartOfAccountsCode(),
                    detail.getAccountReversionObjectCode())) {
                success = false;
                GlobalVariables.getMessageMap().putError(CUKFSPropertyConstants.ACCT_REVERSION_ACCT_NUMBER,
                        CUKFSKeyConstants.ERROR_DOCUMENT_GLOBAL_ACCT_REVERSION_OBJECT_CODE_INVALID,
                        new String[] { globalAcctRev.getUniversityFiscalYear().toString(),
                                acct.getChartOfAccountsCode(), detail.getAccountReversionObjectCode(),
                                acct.getChartOfAccountsCode(), acct.getAccountNumber() });
            }
        }
        return success;
    }

    /**
     * This method checks if an object code with the given primary key fields exists in the database.
     * 
     * @param universityFiscalYear the university fiscal year of the object code
     * @param chartOfAccountsCode the chart of accounts code of the object code
     * @param objectCode the object code itself
     * @return true if it exists (or was not filled in to begin with), false if otherwise
     */
    public boolean validObjectCode(Integer universityFiscalYear, String chartOfAccountsCode, String objectCode) {
        if (!StringUtils.isBlank(objectCode) && universityFiscalYear != null
                && !StringUtils.isBlank(chartOfAccountsCode)) {
            ObjectCode objCode = objectCodeService.getByPrimaryId(universityFiscalYear, chartOfAccountsCode,
                    objectCode);
            return (ObjectUtils.isNotNull(objCode));
        } else {
            return true; // blank object code? well, it's not required...and thus, it's a valid choice
        }
    }

    /**
     * Tests if the object reversion code is a valid code.
     * 
     * @param detail the AccountReversionGlobalDetail to check
     * @return true if it the detail is valid, false if otherwise
     */
    public boolean checkDetailObjectReversionCodeValidity(AccountReversionGlobalDetail detail) {
        boolean success = true;
        if (!StringUtils.isBlank(detail.getAccountReversionCode())) {
            boolean foundInList = false;
            // TODO Dude!! The *only* place that the acct reversion code values are defined
            // is in the lookup class, so I've got to use a web-based class to actually
            // search through the values. Is that right good & healthy?
            for (Object kvPairObj : new ReversionCodeValuesFinder().getKeyValues()) {
                ConcreteKeyValue kvPair = (ConcreteKeyValue) kvPairObj;
                if (kvPair.getKey().toString().equals(detail.getAccountReversionCode())) {
                    foundInList = true;
                    break;
                }
            }
            if (!foundInList) {
                success = false; // we've failed to find the code in the list...FAILED!
                GlobalVariables.getMessageMap().putError("accountReversionCode",
                        CUKFSKeyConstants.ERROR_DOCUMENT_GLOBAL_ACCT_REVERSION_INVALID_ACCT_REVERSION_CODE,
                        new String[] { detail.getAccountReversionCode() });
            }
        }
        return success;
    }

    /**
     * This method tests if all the AccountReversionGlobalAccount objects associated with the given global account
     * reversion pass all of their tests.
     * 
     * @param globalAcctRev the global account reversion to check
     * @return true if valid, false otherwise
     */
    public boolean areAllAccountsValid(AccountReversionGlobal globalAcctRev) {
        boolean success = true;
        if (globalAcctRev.getAccountReversionGlobalAccounts().size() == 0) {
            putFieldError(KFSConstants.MAINTENANCE_ADD_PREFIX + "accountReversionGlobalAccounts.organizationCode",
                    CUKFSKeyConstants.ERROR_DOCUMENT_GLOBAL_ACCT_REVERSION_NO_ACCOUNTS);
            success = false;
        } else {
            success &= checkAllAccountReversionsExistOrAllFieldsAreDefined(globalAcctRev);
            for (int i = 0; i < globalAcctRev.getAccountReversionGlobalAccounts().size(); i++) {
                AccountReversionGlobalAccount acct = globalAcctRev.getAccountReversionGlobalAccounts().get(i);
                String errorPath = MAINTAINABLE_ERROR_PREFIX + "accountReversionGlobalAccounts[" + i + "]";
                GlobalVariables.getMessageMap().addToErrorPath(errorPath);
                success &= checkAllObjectCodesForValidity(globalAcctRev, acct);
                success &= checkAccountValidity(acct);
                success &= checkAccountChartValidity(acct);
                success &= validateAccountFundGroup(acct);
                success &= validateAccountSubFundGroup(acct);
                GlobalVariables.getMessageMap().removeFromErrorPath(errorPath);
            }
        }
        return success;
    }

    /**
     * Tests if the the account of the given AccountReversionGlobalAccount is within the chart of the global
     * account reversion as a whole.
     * 
     * @param acct the AccountReversionGlobalAccount to check
     * @return true if valid, false otherwise
     */
    public boolean checkAccountChartValidity(AccountReversionGlobalAccount acct) {
        boolean success = true;
        if (StringUtils.isBlank(acct.getChartOfAccountsCode())) {
            if (!StringUtils.isBlank(acct.getAccountNumber())) {
                success = false;
                GlobalVariables.getMessageMap().putError(CUKFSPropertyConstants.ACCT_REVERSION_CHART_OF_ACCT_CODE,
                        KFSKeyConstants.ERROR_REQUIRED, "Chart of Accounts Code");
            }
        } else {
            acct.setChartOfAccountsCode(acct.getChartOfAccountsCode().toUpperCase());
            acct.refreshReferenceObject("chartOfAccounts");
            if (ObjectUtils.isNull(acct.getChartOfAccounts())) {
                success = false;
                GlobalVariables.getMessageMap().putError(CUKFSPropertyConstants.ACCT_REVERSION_CHART_OF_ACCT_CODE,
                        CUKFSKeyConstants.ERROR_DOCUMENT_GLOBAL_ACCT_REVERSION_INVALID_CHART,
                        new String[] { acct.getChartOfAccountsCode() });
            }
        }
        return success;
    }

    /**
     * Tests if the given AccountReversionGlobalAccount's Account is active and within the system.
     * 
     * @param acct the AccountReversionGlobalAccount to check
     * @return true if valid, false otherwise
     */
    public boolean checkAccountValidity(AccountReversionGlobalAccount acct) {
        boolean success = true;
        if (StringUtils.isBlank(acct.getAccountNumber())) {
            if (!StringUtils.isBlank(acct.getChartOfAccountsCode())) {
                success = false;
                GlobalVariables.getMessageMap().putError(CUKFSPropertyConstants.ACCT_REVERSION_ACCT_NUMBER,
                        KFSKeyConstants.ERROR_REQUIRED, "Account Number");
            }
        } else if (!StringUtils.isBlank(acct.getChartOfAccountsCode())) {
            acct.refreshReferenceObject("account");
            if (ObjectUtils.isNull(acct.getAccount())) {
                success = false;
                GlobalVariables.getMessageMap().putError(CUKFSPropertyConstants.ACCT_REVERSION_ACCT_NUMBER,
                        CUKFSKeyConstants.ERROR_DOCUMENT_GLOBAL_ACCT_REVERSION_INVALID_ACCOUNT,
                        new String[] { acct.getChartOfAccountsCode(), acct.getAccountNumber() });
            }
        }
        return success;
    }

    /**
     * Checks that either every account reversion on the document references an existing account reversion object,
     * or that all fields have been filled in so that new account reversions can be saved properly.
     * Will skip validation if a fiscal year has not been specified on the document.
     * 
     * @param globalAcctRev global Account Reversion to check
     * @return true if all referenced account reversions already exist or all reversion and detail fields are filled in, false otherwise.
     */
    public boolean checkAllAccountReversionsExistOrAllFieldsAreDefined(AccountReversionGlobal globalAcctRev) {
        boolean success = true;

        if (globalAcctRev.getUniversityFiscalYear() != null) {
            boolean allAccountReversionsExist = true;
            List<AccountReversionGlobalAccount> globalAccounts = globalAcctRev.getAccountReversionGlobalAccounts();
            for (int i = 0; allAccountReversionsExist && i < globalAccounts.size(); i++) {
                allAccountReversionsExist &= ObjectUtils.isNotNull(accountReversionService.getByPrimaryId(
                        globalAcctRev.getUniversityFiscalYear(), globalAccounts.get(i).getChartOfAccountsCode(),
                        globalAccounts.get(i).getAccountNumber()));
            }
            if (!allAccountReversionsExist) {
                // If new account reversions were specified, make sure all reversion fields are filled. (We know fiscal year is defined at this point.)
                boolean allFieldsFilled = StringUtils
                        .isNotBlank(globalAcctRev.getBudgetReversionChartOfAccountsCode())
                        && StringUtils.isNotBlank(globalAcctRev.getBudgetReversionAccountNumber())
                        && globalAcctRev.getCarryForwardByObjectCodeIndicator() != null
                        && StringUtils.isNotBlank(globalAcctRev.getCashReversionFinancialChartOfAccountsCode())
                        && StringUtils.isNotBlank(globalAcctRev.getCashReversionAccountNumber())
                        && globalAcctRev.getReversionActiveIndicator() != null;
                if (!allFieldsFilled) {
                    GlobalVariables.getMessageMap().putError(GLOBAL_ACCOUNT_FIELDS_SECTION,
                            CUKFSKeyConstants.ERROR_DOCUMENT_GLOBAL_ACCT_REVERSION_MISSING_FIELDS_FOR_NEW_REVERSION);
                    success = false;
                }
                // Also check that reversion details are all filled. (Reversion category code should already be set up by the document initialization.)
                for (AccountReversionGlobalDetail globalDetail : globalAcctRev.getAccountReversionGlobalDetails()) {
                    allFieldsFilled &= StringUtils.isNotBlank(globalDetail.getAccountReversionCategoryCode())
                            && StringUtils.isNotBlank(globalDetail.getAccountReversionCode());
                }
                if (!allFieldsFilled) {
                    GlobalVariables.getMessageMap().putError(GLOBAL_DETAIL_FIELDS_SECTION,
                            CUKFSKeyConstants.ERROR_DOCUMENT_GLOBAL_ACCT_REVERSION_MISSING_FIELDS_FOR_NEW_REVERSION_DETAIL);
                    success = false;
                }
            }
        }

        return success;
    }

    /**
     * This method checks if a newly added account is already among the accounts already listed. WARNING: only use on add
     * line rules; there's no good way to use this method when testing the entire document.
     * 
     * @param globalAcctRev the global Account Reversion to check
     * @param acctRevOrg the newly adding account reversion change account
     * @return true if account should be added as it is not currently in the collection, false if otherwise
     */
    public boolean checkAccountIsNotAmongAcctRevAccounts(AccountReversionGlobal globalAcctRev,
            AccountReversionGlobalAccount acctRevAcct) {
        boolean success = true;
        Iterator<AccountReversionGlobalAccount> iter = globalAcctRev.getAccountReversionGlobalAccounts().iterator();
        while (iter.hasNext() && success) {
            AccountReversionGlobalAccount currAcct = iter.next();
            if (areContainingSameAccounts(currAcct, acctRevAcct)) {
                success = false;
                GlobalVariables.getMessageMap().putError(CUKFSPropertyConstants.ACCT_REVERSION_ACCT_NUMBER,
                        CUKFSKeyConstants.ERROR_DOCUMENT_GLOBAL_ACCT_REVERSION_DUPLICATE_ACCOUNTS,
                        new String[] { acctRevAcct.getChartOfAccountsCode(), acctRevAcct.getAccountNumber() });
            }
        }
        return success;
    }

    /**
     * This method tests if two AccountReversionGlobalAccount objects are holding the same underlying Account.
     * 
     * @param acctRevAcctA the first AccountReversionGlobalAccount to check
     * @param acctRevAcctB the second AccountReversionGlobalAccount to check
     * @return true if they share the account, false if otherwise
     */
    public static boolean areContainingSameAccounts(AccountReversionGlobalAccount acctRevAcctA,
            AccountReversionGlobalAccount acctRevAcctB) {
        boolean containingSame = false;
        if (acctRevAcctA.getChartOfAccountsCode() != null && acctRevAcctB.getChartOfAccountsCode() != null
                && acctRevAcctA.getAccountNumber() != null && acctRevAcctB.getAccountNumber() != null) {
            containingSame = (acctRevAcctA.getChartOfAccountsCode().equals(acctRevAcctB.getChartOfAccountsCode())
                    && acctRevAcctA.getAccountNumber().equals(acctRevAcctB.getAccountNumber()));
        }
        return containingSame;
    }

    /**
     * Validates that the fund group code on the sub fund group on the reversion account is valid as defined by the allowed
     * values in SELECTION_1 system parameter.
     * 
     * @param acctRev
     * @return true if valid, false otherwise
     */
    protected boolean validateAccountFundGroup(AccountReversionGlobalAccount acctRev) {
        boolean valid = true;
        String fundGroups = SpringContext.getBean(ParameterService.class).getParameterValueAsString(Reversion.class,
                CUKFSConstants.Reversion.SELECTION_1);
        String propertyName = StringUtils.substringBefore(fundGroups, "=");
        List<String> ruleValues = Arrays.asList(StringUtils.substringAfter(fundGroups, "=").split(";"));

        if (ObjectUtils.isNotNull(ruleValues) && ruleValues.size() > 0) {
            if (ObjectUtils.isNotNull(acctRev.getAccount())
                    && ObjectUtils.isNotNull(acctRev.getAccount().getSubFundGroup())) {
                String accountFundGroupCode = acctRev.getAccount().getSubFundGroup().getFundGroupCode();

                if (!ruleValues.contains(accountFundGroupCode)) {
                    valid = false;
                    GlobalVariables.getMessageMap().putError(CUKFSPropertyConstants.ACCT_REVERSION_ACCT_NUMBER,
                            RiceKeyConstants.ERROR_DOCUMENT_INVALID_VALUE_ALLOWED_VALUES_PARAMETER,
                            new String[] {
                                    getDataDictionaryService().getAttributeLabel(FundGroup.class,
                                            KFSPropertyConstants.CODE),
                                    accountFundGroupCode,
                                    getParameterAsStringForMessage(CUKFSConstants.Reversion.SELECTION_1),
                                    getParameterValuesForMessage(ruleValues),
                                    getDataDictionaryService().getAttributeLabel(AccountReversion.class,
                                            CUKFSPropertyConstants.ACCT_REVERSION_ACCT_NUMBER) });
                }
            }
        }

        return valid;
    }

    /**
     * Validates that the sub fund group code on the reversion account is valid as defined by the allowed values in
     * SELECTION_4 system parameter.
     * 
     * @param acctRev
     * @return true if valid, false otherwise
     */
    protected boolean validateAccountSubFundGroup(AccountReversionGlobalAccount acctRev) {
        boolean valid = true;

        String subFundGroups = SpringContext.getBean(ParameterService.class)
                .getParameterValueAsString(Reversion.class, CUKFSConstants.Reversion.SELECTION_4);
        String propertyName = StringUtils.substringBefore(subFundGroups, "=");
        List<String> ruleValues = Arrays.asList(StringUtils.substringAfter(subFundGroups, "=").split(";"));

        if (ObjectUtils.isNotNull(ruleValues) && ruleValues.size() > 0) {
            if (ObjectUtils.isNotNull(acctRev.getAccount())) {
                String accountSubFundGroupCode = acctRev.getAccount().getSubFundGroupCode();

                if (ruleValues != null && ruleValues.size() > 0 && ruleValues.contains(accountSubFundGroupCode)) {
                    valid = false;
                    GlobalVariables.getMessageMap().putError(CUKFSPropertyConstants.ACCT_REVERSION_ACCT_NUMBER,
                            RiceKeyConstants.ERROR_DOCUMENT_INVALID_VALUE_DENIED_VALUES_PARAMETER,
                            new String[] {
                                    getDataDictionaryService().getAttributeLabel(SubFundGroup.class,
                                            KFSPropertyConstants.SUB_FUND_GROUP_CODE),
                                    accountSubFundGroupCode,
                                    getParameterAsStringForMessage(CUKFSConstants.Reversion.SELECTION_4),
                                    getParameterValuesForMessage(ruleValues),
                                    getDataDictionaryService().getAttributeLabel(AccountReversion.class,
                                            CUKFSPropertyConstants.ACCT_REVERSION_ACCT_NUMBER) });
                }
            }
        }

        return valid;
    }

    /**
     * Validates that the fund group code on the sub fund group on the cash and budget accounts is valid as defined by the
     * allowed values in SELECTION_1 system parameter.
     * 
     * @param globalAcctRev
     * @return
     */
    protected boolean validateAccountFundGroup(AccountReversionGlobal globalAcctRev) {
        boolean valid = true;

        String fundGroups = SpringContext.getBean(ParameterService.class).getParameterValueAsString(Reversion.class,
                CUKFSConstants.Reversion.SELECTION_1);
        String propertyName = StringUtils.substringBefore(fundGroups, "=");
        List<String> ruleValues = Arrays.asList(StringUtils.substringAfter(fundGroups, "=").split(";"));

        if (ObjectUtils.isNotNull(ruleValues) && ruleValues.size() > 0) {

            if (ObjectUtils.isNotNull(globalAcctRev.getBudgetReversionAccount())
                    && ObjectUtils.isNotNull(globalAcctRev.getBudgetReversionAccount().getSubFundGroup())) {
                String budgetAccountFundGroupCode = globalAcctRev.getBudgetReversionAccount().getSubFundGroup()
                        .getFundGroupCode();

                if (!ruleValues.contains(budgetAccountFundGroupCode)) {
                    valid = false;
                    GlobalVariables.getMessageMap().putError(
                            MAINTAINABLE_ERROR_PREFIX
                                    + CUKFSPropertyConstants.ACCT_REVERSION_BUDGET_REVERSION_ACCT_NUMBER,
                            RiceKeyConstants.ERROR_DOCUMENT_INVALID_VALUE_ALLOWED_VALUES_PARAMETER,
                            new String[] {
                                    getDataDictionaryService().getAttributeLabel(FundGroup.class,
                                            KFSPropertyConstants.CODE),
                                    budgetAccountFundGroupCode,
                                    getParameterAsStringForMessage(CUKFSConstants.Reversion.SELECTION_1),
                                    getParameterValuesForMessage(ruleValues),
                                    getDataDictionaryService().getAttributeLabel(AccountReversion.class,
                                            CUKFSPropertyConstants.ACCT_REVERSION_BUDGET_REVERSION_ACCT_NUMBER) });
                }
            }

            if (ObjectUtils.isNotNull(globalAcctRev.getCashReversionAccount())
                    && ObjectUtils.isNotNull(globalAcctRev.getCashReversionAccount().getSubFundGroup())) {

                String cashAccountFundGroupCode = globalAcctRev.getCashReversionAccount().getSubFundGroup()
                        .getFundGroupCode();

                if (!ruleValues.contains(cashAccountFundGroupCode)) {
                    valid = false;
                    GlobalVariables.getMessageMap().putError(
                            MAINTAINABLE_ERROR_PREFIX
                                    + CUKFSPropertyConstants.ACCT_REVERSION_CASH_REVERSION_ACCT_NUMBER,
                            RiceKeyConstants.ERROR_DOCUMENT_INVALID_VALUE_ALLOWED_VALUES_PARAMETER,
                            new String[] {
                                    getDataDictionaryService().getAttributeLabel(FundGroup.class,
                                            KFSPropertyConstants.CODE),
                                    cashAccountFundGroupCode,
                                    getParameterAsStringForMessage(CUKFSConstants.Reversion.SELECTION_1),
                                    getParameterValuesForMessage(ruleValues),
                                    getDataDictionaryService().getAttributeLabel(AccountReversion.class,
                                            CUKFSPropertyConstants.ACCT_REVERSION_CASH_REVERSION_ACCT_NUMBER) });
                }
            }
        }

        return valid;
    }

    /**
     * Validates that the sub fund group code on the cash and budget accounts is valid as defined by the allowed values in
     * SELECTION_4 system parameter.
     * 
     * @param globalAcctRev
     * @return
     */
    protected boolean validateAccountSubFundGroup(AccountReversionGlobal globalAcctRev) {
        boolean valid = true;

        String subFundGroups = SpringContext.getBean(ParameterService.class)
                .getParameterValueAsString(Reversion.class, CUKFSConstants.Reversion.SELECTION_4);
        String propertyName = StringUtils.substringBefore(subFundGroups, "=");
        List<String> ruleValues = Arrays.asList(StringUtils.substringAfter(subFundGroups, "=").split(";"));

        if (ObjectUtils.isNotNull(ruleValues) && ruleValues.size() > 0) {

            if (ObjectUtils.isNotNull(globalAcctRev.getBudgetReversionAccount())) {
                String budgetAccountSubFundGroupCode = globalAcctRev.getBudgetReversionAccount()
                        .getSubFundGroupCode();

                if (ruleValues.contains(budgetAccountSubFundGroupCode)) {
                    valid = false;
                    GlobalVariables.getMessageMap().putError(
                            MAINTAINABLE_ERROR_PREFIX
                                    + CUKFSPropertyConstants.ACCT_REVERSION_BUDGET_REVERSION_ACCT_NUMBER,
                            RiceKeyConstants.ERROR_DOCUMENT_INVALID_VALUE_DENIED_VALUES_PARAMETER,
                            new String[] {
                                    getDataDictionaryService().getAttributeLabel(SubFundGroup.class,
                                            KFSPropertyConstants.SUB_FUND_GROUP_CODE),
                                    budgetAccountSubFundGroupCode,
                                    getParameterAsStringForMessage(CUKFSConstants.Reversion.SELECTION_4),
                                    getParameterValuesForMessage(ruleValues),
                                    getDataDictionaryService().getAttributeLabel(AccountReversion.class,
                                            CUKFSPropertyConstants.ACCT_REVERSION_BUDGET_REVERSION_ACCT_NUMBER) });
                }
            }

            if (ObjectUtils.isNotNull(globalAcctRev.getCashReversionAccount())) {
                String cashAccountSubFundGroupCode = globalAcctRev.getCashReversionAccount().getSubFundGroupCode();

                if (ruleValues.contains(cashAccountSubFundGroupCode)) {
                    valid = false;
                    GlobalVariables.getMessageMap().putError(
                            MAINTAINABLE_ERROR_PREFIX
                                    + CUKFSPropertyConstants.ACCT_REVERSION_CASH_REVERSION_ACCT_NUMBER,
                            RiceKeyConstants.ERROR_DOCUMENT_INVALID_VALUE_DENIED_VALUES_PARAMETER,
                            new String[] {
                                    getDataDictionaryService().getAttributeLabel(SubFundGroup.class,
                                            KFSPropertyConstants.SUB_FUND_GROUP_CODE),
                                    cashAccountSubFundGroupCode,
                                    getParameterAsStringForMessage(CUKFSConstants.Reversion.SELECTION_4),
                                    getParameterValuesForMessage(ruleValues),
                                    getDataDictionaryService().getAttributeLabel(AccountReversion.class,
                                            CUKFSPropertyConstants.ACCT_REVERSION_CASH_REVERSION_ACCT_NUMBER) });
                }
            }
        }
        return valid;
    }

    /**
     * Returns a comma separated String of values
     * 
     * @param values
     * @return
     */
    public String getParameterValuesForMessage(Collection<String> values) {
        StringBuilder result = new StringBuilder();
        if (ObjectUtils.isNotNull(values) && values.size() > 0) {
            for (String value : values) {
                result.append(value);
                result.append(",");
            }
            result.replace(result.lastIndexOf(","), result.length(), KFSConstants.EMPTY_STRING);
        }

        return result.toString();
    }

    /**
     * Returns a String containing information about the given selection system parameter.
     * 
     * @return a String
     */
    private String getParameterAsStringForMessage(String selectionParamName) {
        return new StringBuilder("parameter: ").append(selectionParamName).append(", module: ").append("KFS-COA")
                .append(", component: ").append("Reversion").toString();
    }

    public void setAccountReversionService(AccountReversionService accountReversionService) {
        this.accountReversionService = accountReversionService;
    }

    public void setObjectCodeService(ObjectCodeService objectCodeService) {
        this.objectCodeService = objectCodeService;
    }

    protected AccountReversionGlobal getGlobalAccountReversion() {
        return this.globalAccountReversion;
    }
}