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