/*
* The Kuali Financial System, a comprehensive financial management system for higher education.
*
* Copyright 2005-2014 The Kuali Foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kuali.kfs.coa.document.validation.impl;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.coa.businessobject.A21IndirectCostRecoveryAccount;
import org.kuali.kfs.coa.businessobject.Account;
import org.kuali.kfs.coa.businessobject.Chart;
import org.kuali.kfs.coa.businessobject.IndirectCostRecoveryAccount;
import org.kuali.kfs.sys.KFSKeyConstants;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.document.validation.impl.KfsMaintenanceDocumentRuleBase;
import org.kuali.rice.kns.document.MaintenanceDocument;
import org.kuali.rice.kns.service.DictionaryValidationService;
import org.kuali.rice.krad.bo.PersistableBusinessObject;
import org.kuali.rice.krad.service.BusinessObjectService;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.ObjectUtils;
/**
* Business rule(s) applicable to AccountMaintenance documents.
*/
abstract public class IndirectCostRecoveryAccountsRule extends KfsMaintenanceDocumentRuleBase {
protected static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(IndirectCostRecoveryAccountsRule.class);
protected static final BigDecimal BD100 = new BigDecimal(100);
private List<? extends IndirectCostRecoveryAccount> activeIndirectCostRecoveryAccountList;
protected String boFieldPath;
/**
* Custom processing for adding collection lines
*
* @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomAddCollectionLineBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument, java.lang.String, org.kuali.rice.krad.bo.PersistableBusinessObject)
*/
public boolean processCustomAddCollectionLineBusinessRules(MaintenanceDocument document, String collectionName, PersistableBusinessObject bo) {
boolean success = true;
// this incoming bo needs to be refreshed because it doesn't have its subobjects setup
bo.refreshNonUpdateableReferences();
if (bo instanceof IndirectCostRecoveryAccount || bo instanceof A21IndirectCostRecoveryAccount) {
IndirectCostRecoveryAccount account = (IndirectCostRecoveryAccount) bo;
success &= checkIndirectCostRecoveryAccount(account);
}
return success;
}
/**
* This method calls the rule: KFSPropertyConstants.INDIRECT_COST_RECOVERY_ACCOUNT
*
* @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
*/
protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
boolean success = true;
return success = checkIndirectCostRecoveryAccountDistributions();
}
/**
* This method checks if the ICR collection should or should not be filled
* error message is not handled in the function
*
* @param expectFilled
* @return
*/
protected boolean checkICRCollectionExist(boolean expectFilled) {
boolean success = true;
success = expectFilled != activeIndirectCostRecoveryAccountList.isEmpty();
//double check each of the account/coa codes are not blank
if (!success && expectFilled){
for (IndirectCostRecoveryAccount account : activeIndirectCostRecoveryAccountList){
success &= StringUtils.isNotBlank(account.getIndirectCostRecoveryAccountNumber())
&& StringUtils.isNotBlank(account.getIndirectCostRecoveryFinCoaCode());
}
}
return success;
}
/**
* This method checks if the ICR collection should or should not be filled
* add error message if validation is not successful
*
* @param expectFilled
* @param errorMessage
* @param args
* @return
*/
protected boolean checkICRCollectionExistWithErrorMessage(boolean expectFilled, String errorMessage, String args) {
boolean success = true;
success = checkICRCollectionExist(expectFilled);
if (!success){
putFieldError(boFieldPath, errorMessage, args);
}
return success;
}
/**
* Check valid IndirectCostRecovery Account
*
* @return
*/
protected boolean checkIndirectCostRecoveryAccount(IndirectCostRecoveryAccount icrAccount) {
boolean success = true;
//check for empty values on the ICR account
// The chart and account must exist in the database.
String chartOfAccountsCode = icrAccount.getIndirectCostRecoveryFinCoaCode();
String accountNumber = icrAccount.getIndirectCostRecoveryAccountNumber();
BigDecimal icraAccountLinePercentage = ObjectUtils.isNotNull(icrAccount.getAccountLinePercent()) ? icrAccount.getAccountLinePercent() : BigDecimal.ZERO;
return checkIndirectCostRecoveryAccount(chartOfAccountsCode, accountNumber, icraAccountLinePercentage);
}
protected boolean checkIndirectCostRecoveryAccount(String chartOfAccountsCode, String accountNumber, BigDecimal icraAccountLinePercentage) {
boolean success = true;
if (StringUtils.isBlank(chartOfAccountsCode)) {
GlobalVariables.getMessageMap().putError(KFSPropertyConstants.ICR_CHART_OF_ACCOUNTS_CODE, KFSKeyConstants.ERROR_REQUIRED,
getDDAttributeLabel(KFSPropertyConstants.ICR_CHART_OF_ACCOUNTS_CODE));
success &= false;
}
if (StringUtils.isBlank(accountNumber)) {
GlobalVariables.getMessageMap().putError(KFSPropertyConstants.ICR_ACCOUNT_NUMBER, KFSKeyConstants.ERROR_REQUIRED,
getDDAttributeLabel(KFSPropertyConstants.ICR_ACCOUNT_NUMBER));
success &= false;
}
if (StringUtils.isNotBlank(chartOfAccountsCode) && StringUtils.isNotBlank(accountNumber)) {
Map<String, String> chartAccountMap = new HashMap<String, String>();
chartAccountMap.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, chartOfAccountsCode);
if (SpringContext.getBean(BusinessObjectService.class).countMatching(Chart.class, chartAccountMap) < 1) {
GlobalVariables.getMessageMap().putError(KFSPropertyConstants.ICR_CHART_OF_ACCOUNTS_CODE, KFSKeyConstants.ERROR_EXISTENCE, chartOfAccountsCode);
success &= false;
}
chartAccountMap.put(KFSPropertyConstants.ACCOUNT_NUMBER, accountNumber);
if (SpringContext.getBean(BusinessObjectService.class).countMatching(Account.class, chartAccountMap) < 1) {
GlobalVariables.getMessageMap().putError(KFSPropertyConstants.ICR_ACCOUNT_NUMBER, KFSKeyConstants.ERROR_EXISTENCE, chartOfAccountsCode + "-" + accountNumber);
success &= false;
}
}
//check the percent line
if (icraAccountLinePercentage.compareTo(BigDecimal.ZERO) <= 0 || icraAccountLinePercentage.compareTo(BD100) == 1){
GlobalVariables.getMessageMap().putError(KFSPropertyConstants.ICR_ACCOUNT_LINE_PERCENT,
KFSKeyConstants.ERROR_DOCUMENT_ACCMAINT_ICR_ACCOUNT_INVALID_LINE_PERCENT);
success &= false;
}
return success;
}
/**
* Check the collection list of indirect cost recovery account
*
* 1. Check each account with rule: checkIndirectCostRecoveryAccount
* 2. Total distributions from all the account should be 100
*
* @param document
* @return
*/
protected boolean checkIndirectCostRecoveryAccountDistributions() {
boolean result = true;
if (ObjectUtils.isNull(activeIndirectCostRecoveryAccountList) || (activeIndirectCostRecoveryAccountList.size() == 0)) {
return result;
}
DictionaryValidationService dvService = super.getDictionaryValidationService();
int i=0;
BigDecimal totalDistribution = BigDecimal.ZERO;
for (IndirectCostRecoveryAccount icra : activeIndirectCostRecoveryAccountList){
String errorPath = MAINTAINABLE_ERROR_PREFIX + boFieldPath + "[" + i++ + "]";
GlobalVariables.getMessageMap().addToErrorPath(errorPath);
checkIndirectCostRecoveryAccount(icra);
GlobalVariables.getMessageMap().removeFromErrorPath(errorPath);
totalDistribution = totalDistribution.add(icra.getAccountLinePercent());
}
//check the total distribution is 100
if (totalDistribution.compareTo(BD100) != 0){
putFieldError(boFieldPath, KFSKeyConstants.ERROR_DOCUMENT_ACCMAINT_ICR_ACCOUNT_TOTAL_NOT_100_PERCENT);
result &= false;
}
return result;
}
/**
* Get the attribute label from DataDictionary
* @param attribute
* @return
*/
protected String getDDAttributeLabel(String attribute){
return ddService.getAttributeLabel(IndirectCostRecoveryAccount.class, attribute);
}
public List<? extends IndirectCostRecoveryAccount> getActiveIndirectCostRecoveryAccountList() {
return activeIndirectCostRecoveryAccountList;
}
public void setActiveIndirectCostRecoveryAccountList(List<? extends IndirectCostRecoveryAccount> indirectCostRecoveryAccountList) {
this.activeIndirectCostRecoveryAccountList = indirectCostRecoveryAccountList;
}
public String getBoFieldPath() {
return boFieldPath;
}
public void setBoFieldPath(String boFieldPath) {
this.boFieldPath = boFieldPath;
}
}