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 }