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.gl.businessobject.lookup; 017 018 import java.util.ArrayList; 019 import java.util.Collection; 020 import java.util.Iterator; 021 import java.util.List; 022 import java.util.Map; 023 024 import org.apache.commons.lang.ArrayUtils; 025 import org.apache.commons.lang.StringUtils; 026 import org.kuali.kfs.coa.businessobject.ObjectCode; 027 import org.kuali.kfs.gl.Constant; 028 import org.kuali.kfs.gl.GeneralLedgerConstants; 029 import org.kuali.kfs.gl.OJBUtility; 030 import org.kuali.kfs.gl.batch.service.AccountBalanceCalculator; 031 import org.kuali.kfs.gl.businessobject.AccountBalance; 032 import org.kuali.kfs.gl.businessobject.TransientBalanceInquiryAttributes; 033 import org.kuali.kfs.gl.businessobject.inquiry.AccountBalanceInquirableImpl; 034 import org.kuali.kfs.gl.service.AccountBalanceService; 035 import org.kuali.kfs.sys.KFSConstants; 036 import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry; 037 import org.kuali.kfs.sys.businessobject.SystemOptions; 038 import org.kuali.kfs.sys.service.OptionsService; 039 import org.kuali.rice.kns.bo.BusinessObject; 040 import org.kuali.rice.kns.lookup.HtmlData; 041 import org.kuali.rice.kns.util.KualiDecimal; 042 import org.kuali.rice.kns.util.ObjectUtils; 043 044 /** 045 * A class to support Account Balance lookups 046 */ 047 public class AccountBalanceLookupableHelperServiceImpl extends AbstractGeneralLedgerLookupableHelperServiceImpl { 048 049 private AccountBalanceCalculator postAccountBalance; 050 private AccountBalanceService accountBalanceService; 051 private OptionsService optionsService; 052 053 /** 054 * Returns the url for the account balance inquiry 055 * @param bo the business object with a property that an inquiry drill down url is being asked for 056 * @param propertyName the property of that bo that the inquiry drill down url is being asked for 057 * @return the URL for the inquiry 058 * @see org.kuali.rice.kns.lookup.Lookupable#getInquiryUrl(org.kuali.rice.kns.bo.BusinessObject, java.lang.String) 059 */ 060 @Override 061 public HtmlData getInquiryUrl(BusinessObject bo, String propertyName) { 062 return (new AccountBalanceInquirableImpl()).getInquiryUrl(bo, propertyName); 063 } 064 065 /** 066 * Given a map of fieldValues, actually searches for the appropriate account balance records to return 067 * @param fieldValues a map of keys for the search 068 * @return a List of AccountBalance records that match the search criteria 069 * @see org.kuali.rice.kns.lookup.Lookupable#getSearchResults(java.util.Map) 070 */ 071 public List getSearchResults(Map fieldValues) { 072 setBackLocation((String) fieldValues.get(KFSConstants.BACK_LOCATION)); 073 setDocFormKey((String) fieldValues.get(KFSConstants.DOC_FORM_KEY)); 074 075 Collection searchResultsCollection = null; 076 077 // get the pending entry option. This method must be prior to the get search results 078 String pendingEntryOption = this.getSelectedPendingEntryOption(fieldValues); 079 080 // test if the consolidation option is selected or not 081 String consolidationOption = (String) fieldValues.get(GeneralLedgerConstants.DummyBusinessObject.CONSOLIDATION_OPTION); 082 boolean isConsolidated = isConsolidationSelected(fieldValues); 083 084 // get the search result collection 085 if (isConsolidated) { 086 Iterator availableBalanceIterator = accountBalanceService.findConsolidatedAvailableAccountBalance(fieldValues); 087 searchResultsCollection = buildConsolidedAvailableBalanceCollection(availableBalanceIterator); 088 } 089 else { 090 Iterator availableBalanceIterator = accountBalanceService.findAvailableAccountBalance(fieldValues); 091 searchResultsCollection = buildDetailedAvailableBalanceCollection(availableBalanceIterator); 092 } 093 094 // update search results according to the selected pending entry option 095 updateByPendingLedgerEntry(searchResultsCollection, fieldValues, pendingEntryOption, isConsolidated, false); 096 097 // Put the search related stuff in the objects 098 for (Iterator iter = searchResultsCollection.iterator(); iter.hasNext();) { 099 AccountBalance ab = (AccountBalance) iter.next(); 100 TransientBalanceInquiryAttributes dbo = ab.getDummyBusinessObject(); 101 dbo.setConsolidationOption(consolidationOption); 102 dbo.setPendingEntryOption(pendingEntryOption); 103 } 104 105 // get the actual size of all qualified search results 106 Integer recordCount = accountBalanceService.getAvailableAccountBalanceCount(fieldValues, isConsolidated); 107 Long actualSize = OJBUtility.getResultActualSize(searchResultsCollection, recordCount, fieldValues, new AccountBalance()); 108 109 return this.buildSearchResultList(searchResultsCollection, actualSize); 110 } 111 112 113 /** 114 * This method builds the available account balance collection based on the input iterator 115 * 116 * @param iterator the iterator of search results of account balance 117 * @return the account balance collection 118 */ 119 private Collection buildConsolidedAvailableBalanceCollection(Iterator iterator) { 120 Collection balanceCollection = new ArrayList(); 121 122 // build available balance collection throught analyzing the input iterator 123 while (iterator.hasNext()) { 124 Object avaiableAccountBalance = iterator.next(); 125 126 if (avaiableAccountBalance.getClass().isArray()) { 127 int i = 0; 128 Object[] array = (Object[]) avaiableAccountBalance; 129 AccountBalance accountBalance = new AccountBalance(); 130 131 accountBalance.setUniversityFiscalYear(new Integer(array[i++].toString())); 132 accountBalance.setChartOfAccountsCode(array[i++].toString()); 133 134 accountBalance.setAccountNumber(array[i++].toString()); 135 accountBalance.setSubAccountNumber(Constant.CONSOLIDATED_SUB_ACCOUNT_NUMBER); 136 137 accountBalance.setObjectCode(array[i++].toString()); 138 accountBalance.setSubObjectCode(Constant.CONSOLIDATED_SUB_OBJECT_CODE); 139 140 String objectTypeCode = array[i++].toString(); 141 accountBalance.getFinancialObject().setFinancialObjectTypeCode(objectTypeCode); 142 143 KualiDecimal budgetAmount = new KualiDecimal(array[i++].toString()); 144 accountBalance.setCurrentBudgetLineBalanceAmount(budgetAmount); 145 146 KualiDecimal actualsAmount = new KualiDecimal(array[i++].toString()); 147 accountBalance.setAccountLineActualsBalanceAmount(actualsAmount); 148 149 KualiDecimal encumbranceAmount = new KualiDecimal(array[i].toString()); 150 accountBalance.setAccountLineEncumbranceBalanceAmount(encumbranceAmount); 151 152 KualiDecimal variance = calculateVariance(accountBalance); 153 accountBalance.getDummyBusinessObject().setGenericAmount(variance); 154 155 balanceCollection.add(accountBalance); 156 } 157 } 158 return balanceCollection; 159 } 160 161 /** 162 * This method builds the available account balance collection based on the input collection 163 * 164 * @param collection a collection of account balance entries 165 * @return the account balance collection 166 */ 167 private Collection buildDetailedAvailableBalanceCollection(Iterator iterator) { 168 Collection balanceCollection = new ArrayList(); 169 170 // build available balance collection throught analyzing the iterator above 171 while (iterator.hasNext()) { 172 AccountBalance accountBalance = (AccountBalance) iterator.next(); 173 174 if (accountBalance.getDummyBusinessObject() == null) { 175 accountBalance.setDummyBusinessObject(new TransientBalanceInquiryAttributes()); 176 } 177 178 KualiDecimal variance = calculateVariance(accountBalance); 179 accountBalance.getDummyBusinessObject().setGenericAmount(variance); 180 181 balanceCollection.add(accountBalance); 182 } 183 return balanceCollection; 184 } 185 186 /** 187 * This method calculates the variance of current budget balance, actuals balance and encumbrance balance 188 * 189 * @param balance an account balance entry 190 */ 191 private KualiDecimal calculateVariance(AccountBalance balance) { 192 193 KualiDecimal variance = new KualiDecimal(0.0); 194 KualiDecimal budgetAmount = balance.getCurrentBudgetLineBalanceAmount(); 195 KualiDecimal actualsAmount = balance.getAccountLineActualsBalanceAmount(); 196 KualiDecimal encumbranceAmount = balance.getAccountLineEncumbranceBalanceAmount(); 197 198 // determine if the object type code is one of the given codes 199 if (ObjectUtils.isNull(balance.getFinancialObject()) || StringUtils.isBlank(balance.getFinancialObject().getFinancialObjectTypeCode())) { 200 balance.refreshReferenceObject("financialObject"); // refresh if we need to... 201 } 202 ObjectCode financialObject = balance.getFinancialObject(); 203 String objectTypeCode = (financialObject == null) ? Constant.EMPTY_STRING : financialObject.getFinancialObjectTypeCode(); 204 205 SystemOptions options = getOptionsService().getOptions(balance.getUniversityFiscalYear()); 206 if (ObjectUtils.isNull(options)) { 207 options = getOptionsService().getCurrentYearOptions(); 208 } 209 String[] objectTypeCodeList = new String[3]; 210 objectTypeCodeList[0] = options.getFinObjTypeExpendNotExpCode(); 211 objectTypeCodeList[1] = options.getFinObjTypeExpNotExpendCode(); 212 objectTypeCodeList[2] = options.getFinObjTypeExpenditureexpCd(); 213 214 boolean isObjectTypeCodeInList = ArrayUtils.contains(objectTypeCodeList, objectTypeCode); 215 216 // calculate the variance based on the object type code of the balance 217 if (isObjectTypeCodeInList) { 218 variance = budgetAmount.subtract(actualsAmount); 219 variance = variance.subtract(encumbranceAmount); 220 } 221 else { 222 variance = actualsAmount.subtract(budgetAmount); 223 } 224 return variance; 225 } 226 227 /** 228 * Updates the collection of entries that will be applied to the results of the inquiry 229 * 230 * @param entryCollection a collection of balance entries 231 * @param fieldValues the map containing the search fields and values 232 * @param isApproved flag whether the approved entries or all entries will be processed 233 * @param isConsolidated flag whether the results are consolidated or not 234 * @param isCostShareExcluded flag whether the user selects to see the results with cost share subaccount 235 * @see org.kuali.module.gl.web.lookupable.AbstractGLLookupableImpl#updateEntryCollection(java.util.Collection, java.util.Map, 236 * boolean, boolean, boolean) 237 */ 238 @Override 239 protected void updateEntryCollection(Collection entryCollection, Map fieldValues, boolean isApproved, boolean isConsolidated, boolean isCostShareExcluded) { 240 241 // convert the field names of balance object into corresponding ones of pending entry object 242 Map pendingEntryFieldValues = BusinessObjectFieldConverter.convertToTransactionFieldValues(fieldValues); 243 244 // go through the pending entries to update the balance collection 245 Iterator pendingEntryIterator = getGeneralLedgerPendingEntryService().findPendingLedgerEntriesForAccountBalance(pendingEntryFieldValues, isApproved); 246 while (pendingEntryIterator.hasNext()) { 247 GeneralLedgerPendingEntry pendingEntry = (GeneralLedgerPendingEntry) pendingEntryIterator.next(); 248 249 if (isCostShareExcluded) { 250 if (ObjectUtils.isNotNull(pendingEntry.getSubAccount()) && ObjectUtils.isNotNull(pendingEntry.getSubAccount().getA21SubAccount())) { 251 if (KFSConstants.SubAccountType.COST_SHARE.equals(pendingEntry.getSubAccount().getA21SubAccount().getSubAccountTypeCode())) { 252 // Don't process this one 253 continue; 254 } 255 } 256 } 257 258 // if consolidated, change the following fields into the default values for consolidation 259 if (isConsolidated) { 260 pendingEntry.setSubAccountNumber(Constant.CONSOLIDATED_SUB_ACCOUNT_NUMBER); 261 pendingEntry.setFinancialSubObjectCode(Constant.CONSOLIDATED_SUB_OBJECT_CODE); 262 pendingEntry.setFinancialObjectTypeCode(Constant.CONSOLIDATED_OBJECT_TYPE_CODE); 263 } 264 265 AccountBalance accountBalance = postAccountBalance.findAccountBalance(entryCollection, pendingEntry); 266 postAccountBalance.updateAccountBalance(pendingEntry, accountBalance); 267 268 // recalculate the variance after pending entries are combined into account balances 269 if (accountBalance.getDummyBusinessObject() == null) { 270 accountBalance.setDummyBusinessObject(new TransientBalanceInquiryAttributes()); 271 } 272 KualiDecimal variance = calculateVariance(accountBalance); 273 accountBalance.getDummyBusinessObject().setGenericAmount(variance); 274 } 275 } 276 277 /** 278 * Sets the postAccountBalance attribute value. 279 * 280 * @param postAccountBalance The postAccountBalance to set. 281 */ 282 public void setPostAccountBalance(AccountBalanceCalculator postAccountBalance) { 283 this.postAccountBalance = postAccountBalance; 284 } 285 286 /** 287 * Sets the accountBalanceService attribute value. 288 * 289 * @param accountBalanceService The accountBalanceService to set. 290 */ 291 public void setAccountBalanceService(AccountBalanceService accountBalanceService) { 292 this.accountBalanceService = accountBalanceService; 293 } 294 295 /** 296 * Sets the optionsService attribute value 297 * 298 * @param optionsService The optionsService to set. 299 */ 300 public void setOptionsService(OptionsService optionsService) { 301 this.optionsService = optionsService; 302 } 303 304 /** 305 * Gets the optionsService attribute. 306 * @return Returns the optionsService. 307 */ 308 public OptionsService getOptionsService() { 309 return optionsService; 310 } 311 }