/*
* 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.gl.businessobject.lookup;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.coa.businessobject.ObjectCode;
import org.kuali.kfs.gl.Constant;
import org.kuali.kfs.gl.GeneralLedgerConstants;
import org.kuali.kfs.gl.OJBUtility;
import org.kuali.kfs.gl.batch.service.AccountBalanceCalculator;
import org.kuali.kfs.gl.businessobject.AccountBalance;
import org.kuali.kfs.gl.businessobject.TransientBalanceInquiryAttributes;
import org.kuali.kfs.gl.businessobject.inquiry.AccountBalanceInquirableImpl;
import org.kuali.kfs.gl.service.AccountBalanceService;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
import org.kuali.kfs.sys.businessobject.SystemOptions;
import org.kuali.kfs.sys.service.OptionsService;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.kns.lookup.HtmlData;
import org.kuali.rice.krad.bo.BusinessObject;
import org.kuali.rice.krad.util.ObjectUtils;
/**
* A class to support Account Balance lookups
*/
public class AccountBalanceLookupableHelperServiceImpl extends AbstractGeneralLedgerLookupableHelperServiceImpl {
private AccountBalanceCalculator postAccountBalance;
private AccountBalanceService accountBalanceService;
private OptionsService optionsService;
/**
* Returns the url for the account balance inquiry
* @param bo the business object with a property that an inquiry drill down url is being asked for
* @param propertyName the property of that bo that the inquiry drill down url is being asked for
* @return the URL for the inquiry
* @see org.kuali.rice.kns.lookup.Lookupable#getInquiryUrl(org.kuali.rice.krad.bo.BusinessObject, java.lang.String)
*/
@Override
public HtmlData getInquiryUrl(BusinessObject bo, String propertyName) {
return (new AccountBalanceInquirableImpl()).getInquiryUrl(bo, propertyName);
}
/**
* Given a map of fieldValues, actually searches for the appropriate account balance records to return
* @param fieldValues a map of keys for the search
* @return a List of AccountBalance records that match the search criteria
* @see org.kuali.rice.kns.lookup.Lookupable#getSearchResults(java.util.Map)
*
* KRAD Conversion: Lookupable modifies the search results based on the fields consolidated.
* But all field definitions are in data dictionary.
*/
@Override
public List<? extends BusinessObject> getSearchResults(Map<String, String> fieldValues) {
setBackLocation((String) fieldValues.get(KFSConstants.BACK_LOCATION));
setDocFormKey((String) fieldValues.get(KFSConstants.DOC_FORM_KEY));
Collection searchResultsCollection = null;
// get the pending entry option. This method must be prior to the get search results
String pendingEntryOption = this.getSelectedPendingEntryOption(fieldValues);
// KFSMI-410: added one more node for consolidationOption
String consolidationOption = (String) fieldValues.get(GeneralLedgerConstants.DummyBusinessObject.CONSOLIDATION_OPTION);
// test if the consolidation option is selected or not
boolean isConsolidated = isConsolidationSelected(fieldValues);
// get the search result collection
// KFSMI-410: added one more node for consolidationOption
if (consolidationOption.equals(Constant.EXCLUDE_SUBACCOUNTS)){
fieldValues.put(Constant.SUB_ACCOUNT_OPTION, KFSConstants.getDashSubAccountNumber());
isConsolidated = false;
}
if (isConsolidated) {
Iterator availableBalanceIterator = accountBalanceService.findConsolidatedAvailableAccountBalance(fieldValues);
searchResultsCollection = buildConsolidedAvailableBalanceCollection(availableBalanceIterator);
}
else {
Iterator availableBalanceIterator = accountBalanceService.findAvailableAccountBalance(fieldValues);
searchResultsCollection = buildDetailedAvailableBalanceCollection(availableBalanceIterator);
}
// update search results according to the selected pending entry option
updateByPendingLedgerEntry(searchResultsCollection, fieldValues, pendingEntryOption, isConsolidated, false);
// Put the search related stuff in the objects
for (Iterator iter = searchResultsCollection.iterator(); iter.hasNext();) {
AccountBalance ab = (AccountBalance) iter.next();
TransientBalanceInquiryAttributes dbo = ab.getDummyBusinessObject();
dbo.setConsolidationOption(consolidationOption);
dbo.setPendingEntryOption(pendingEntryOption);
}
// get the actual size of all qualified search results
Integer recordCount = accountBalanceService.getAvailableAccountBalanceCount(fieldValues, isConsolidated);
Long actualSize = OJBUtility.getResultActualSize(searchResultsCollection, recordCount, fieldValues, new AccountBalance());
return this.buildSearchResultList(searchResultsCollection, actualSize);
}
/**
* This method builds the available account balance collection based on the input iterator
*
* @param iterator the iterator of search results of account balance
* @return the account balance collection
*/
private Collection buildConsolidedAvailableBalanceCollection(Iterator iterator) {
Collection balanceCollection = new ArrayList();
// build available balance collection throught analyzing the input iterator
while (iterator.hasNext()) {
Object avaiableAccountBalance = iterator.next();
if (avaiableAccountBalance.getClass().isArray()) {
int i = 0;
Object[] array = (Object[]) avaiableAccountBalance;
AccountBalance accountBalance = new AccountBalance();
accountBalance.setUniversityFiscalYear(new Integer(array[i++].toString()));
accountBalance.setChartOfAccountsCode(array[i++].toString());
accountBalance.setAccountNumber(array[i++].toString());
accountBalance.setSubAccountNumber(Constant.CONSOLIDATED_SUB_ACCOUNT_NUMBER);
accountBalance.setObjectCode(array[i++].toString());
accountBalance.setSubObjectCode(Constant.CONSOLIDATED_SUB_OBJECT_CODE);
String objectTypeCode = array[i++].toString();
accountBalance.getFinancialObject().setFinancialObjectTypeCode(objectTypeCode);
KualiDecimal budgetAmount = new KualiDecimal(array[i++].toString());
accountBalance.setCurrentBudgetLineBalanceAmount(budgetAmount);
KualiDecimal actualsAmount = new KualiDecimal(array[i++].toString());
accountBalance.setAccountLineActualsBalanceAmount(actualsAmount);
KualiDecimal encumbranceAmount = new KualiDecimal(array[i].toString());
accountBalance.setAccountLineEncumbranceBalanceAmount(encumbranceAmount);
KualiDecimal variance = calculateVariance(accountBalance);
accountBalance.getDummyBusinessObject().setGenericAmount(variance);
balanceCollection.add(accountBalance);
}
}
return balanceCollection;
}
/**
* This method builds the available account balance collection based on the input collection
*
* @param collection a collection of account balance entries
* @return the account balance collection
*/
private Collection buildDetailedAvailableBalanceCollection(Iterator iterator) {
Collection balanceCollection = new ArrayList();
// build available balance collection throught analyzing the iterator above
while (iterator.hasNext()) {
AccountBalance accountBalance = (AccountBalance) iterator.next();
if (accountBalance.getDummyBusinessObject() == null) {
accountBalance.setDummyBusinessObject(new TransientBalanceInquiryAttributes());
}
KualiDecimal variance = calculateVariance(accountBalance);
accountBalance.getDummyBusinessObject().setGenericAmount(variance);
balanceCollection.add(accountBalance);
}
return balanceCollection;
}
/**
* This method calculates the variance of current budget balance, actuals balance and encumbrance balance
*
* @param balance an account balance entry
*/
private KualiDecimal calculateVariance(AccountBalance balance) {
KualiDecimal variance = new KualiDecimal(0.0);
KualiDecimal budgetAmount = balance.getCurrentBudgetLineBalanceAmount();
KualiDecimal actualsAmount = balance.getAccountLineActualsBalanceAmount();
KualiDecimal encumbranceAmount = balance.getAccountLineEncumbranceBalanceAmount();
// determine if the object type code is one of the given codes
if (ObjectUtils.isNull(balance.getFinancialObject()) || StringUtils.isBlank(balance.getFinancialObject().getFinancialObjectTypeCode())) {
balance.refreshReferenceObject("financialObject"); // refresh if we need to...
}
ObjectCode financialObject = balance.getFinancialObject();
String objectTypeCode = (financialObject == null) ? Constant.EMPTY_STRING : financialObject.getFinancialObjectTypeCode();
SystemOptions options = getOptionsService().getOptions(balance.getUniversityFiscalYear());
if (ObjectUtils.isNull(options)) {
options = getOptionsService().getCurrentYearOptions();
}
String[] objectTypeCodeList = new String[3];
objectTypeCodeList[0] = options.getFinObjTypeExpendNotExpCode();
objectTypeCodeList[1] = options.getFinObjTypeExpNotExpendCode();
objectTypeCodeList[2] = options.getFinObjTypeExpenditureexpCd();
boolean isObjectTypeCodeInList = ArrayUtils.contains(objectTypeCodeList, objectTypeCode);
// calculate the variance based on the object type code of the balance
if (isObjectTypeCodeInList) {
variance = budgetAmount.subtract(actualsAmount);
variance = variance.subtract(encumbranceAmount);
}
else {
variance = actualsAmount.subtract(budgetAmount);
}
return variance;
}
/**
* Updates the collection of entries that will be applied to the results of the inquiry
*
* @param entryCollection a collection of balance entries
* @param fieldValues the map containing the search fields and values
* @param isApproved flag whether the approved entries or all entries will be processed
* @param isConsolidated flag whether the results are consolidated or not
* @param isCostShareExcluded flag whether the user selects to see the results with cost share subaccount
* @see org.kuali.module.gl.web.lookupable.AbstractGLLookupableImpl#updateEntryCollection(java.util.Collection, java.util.Map,
* boolean, boolean, boolean)
*/
@Override
protected void updateEntryCollection(Collection entryCollection, Map fieldValues, boolean isApproved, boolean isConsolidated, boolean isCostShareExcluded) {
// convert the field names of balance object into corresponding ones of pending entry object
Map pendingEntryFieldValues = BusinessObjectFieldConverter.convertToTransactionFieldValues(fieldValues);
// go through the pending entries to update the balance collection
Iterator pendingEntryIterator = getGeneralLedgerPendingEntryService().findPendingLedgerEntriesForAccountBalance(pendingEntryFieldValues, isApproved);
while (pendingEntryIterator.hasNext()) {
GeneralLedgerPendingEntry pendingEntry = (GeneralLedgerPendingEntry) pendingEntryIterator.next();
if (isCostShareExcluded) {
if (ObjectUtils.isNotNull(pendingEntry.getSubAccount()) && ObjectUtils.isNotNull(pendingEntry.getSubAccount().getA21SubAccount())) {
if (KFSConstants.SubAccountType.COST_SHARE.equals(pendingEntry.getSubAccount().getA21SubAccount().getSubAccountTypeCode())) {
// Don't process this one
continue;
}
}
}
// if consolidated, change the following fields into the default values for consolidation
if (isConsolidated) {
pendingEntry.setSubAccountNumber(Constant.CONSOLIDATED_SUB_ACCOUNT_NUMBER);
pendingEntry.setFinancialSubObjectCode(Constant.CONSOLIDATED_SUB_OBJECT_CODE);
pendingEntry.setFinancialObjectTypeCode(Constant.CONSOLIDATED_OBJECT_TYPE_CODE);
}
AccountBalance accountBalance = postAccountBalance.findAccountBalance(entryCollection, pendingEntry);
postAccountBalance.updateAccountBalance(pendingEntry, accountBalance);
// recalculate the variance after pending entries are combined into account balances
if (accountBalance.getDummyBusinessObject() == null) {
accountBalance.setDummyBusinessObject(new TransientBalanceInquiryAttributes());
}
KualiDecimal variance = calculateVariance(accountBalance);
accountBalance.getDummyBusinessObject().setGenericAmount(variance);
}
}
/**
* Sets the postAccountBalance attribute value.
*
* @param postAccountBalance The postAccountBalance to set.
*/
public void setPostAccountBalance(AccountBalanceCalculator postAccountBalance) {
this.postAccountBalance = postAccountBalance;
}
/**
* Sets the accountBalanceService attribute value.
*
* @param accountBalanceService The accountBalanceService to set.
*/
public void setAccountBalanceService(AccountBalanceService accountBalanceService) {
this.accountBalanceService = accountBalanceService;
}
/**
* Sets the optionsService attribute value
*
* @param optionsService The optionsService to set.
*/
public void setOptionsService(OptionsService optionsService) {
this.optionsService = optionsService;
}
/**
* Gets the optionsService attribute.
* @return Returns the optionsService.
*/
public OptionsService getOptionsService() {
return optionsService;
}
}