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