View Javadoc
1   /*
2    * Copyright 2006 The Kuali Foundation
3    * 
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    * http://www.opensource.org/licenses/ecl2.php
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.gl.businessobject.lookup;
17  
18  import java.util.*;
19  
20  import org.apache.commons.lang.ArrayUtils;
21  import org.apache.commons.lang.StringUtils;
22  import org.kuali.ole.coa.businessobject.Account;
23  import org.kuali.ole.coa.businessobject.ObjectCode;
24  import org.kuali.ole.gl.Constant;
25  import org.kuali.ole.gl.GeneralLedgerConstants;
26  import org.kuali.ole.gl.OJBUtility;
27  import org.kuali.ole.gl.batch.service.AccountBalanceCalculator;
28  import org.kuali.ole.gl.businessobject.AccountBalance;
29  import org.kuali.ole.gl.businessobject.TransientBalanceInquiryAttributes;
30  import org.kuali.ole.gl.businessobject.inquiry.AccountBalanceInquirableImpl;
31  import org.kuali.ole.gl.service.AccountBalanceService;
32  import org.kuali.ole.sys.OLEConstants;
33  import org.kuali.ole.sys.businessobject.GeneralLedgerPendingEntry;
34  import org.kuali.ole.sys.businessobject.SystemOptions;
35  import org.kuali.ole.sys.context.SpringContext;
36  import org.kuali.ole.sys.service.OptionsService;
37  import org.kuali.rice.core.api.util.type.KualiDecimal;
38  import org.kuali.rice.kns.lookup.HtmlData;
39  import org.kuali.rice.krad.bo.BusinessObject;
40  import org.kuali.rice.krad.service.BusinessObjectService;
41  import org.kuali.rice.krad.util.ObjectUtils;
42  
43  /**
44   * A class to support Account Balance lookups
45   */
46  public class AccountBalanceLookupableHelperServiceImpl extends AbstractGeneralLedgerLookupableHelperServiceImpl {
47  
48      private AccountBalanceCalculator postAccountBalance;
49      private AccountBalanceService accountBalanceService;
50      private OptionsService optionsService;
51  
52      /**
53       * Returns the url for the account balance inquiry
54       * @param bo the business object with a property that an inquiry drill down url is being asked for
55       * @param propertyName the property of that bo that the inquiry drill down url is being asked for
56       * @return the URL for the inquiry
57       * @see org.kuali.rice.kns.lookup.Lookupable#getInquiryUrl(org.kuali.rice.krad.bo.BusinessObject, java.lang.String)
58       */
59      @Override
60      public HtmlData getInquiryUrl(BusinessObject bo, String propertyName) {
61          return (new AccountBalanceInquirableImpl()).getInquiryUrl(bo, propertyName);
62      }
63  
64      /**
65       * Given a map of fieldValues, actually searches for the appropriate account balance records to return
66       * @param fieldValues a map of keys for the search
67       * @return a List of AccountBalance records that match the search criteria
68       * @see org.kuali.rice.kns.lookup.Lookupable#getSearchResults(java.util.Map)
69       * 
70       * KRAD Conversion: Lookupable modifies the search results based on the fields consolidated.
71       * But all field definitions are in data dictionary.
72       */
73      public List getSearchResults(Map fieldValues) {
74          setBackLocation((String) fieldValues.get(OLEConstants.BACK_LOCATION));
75          setDocFormKey((String) fieldValues.get(OLEConstants.DOC_FORM_KEY));
76  
77          Collection searchResultsCollection = null;
78  
79          // get the pending entry option. This method must be prior to the get search results
80          String pendingEntryOption = this.getSelectedPendingEntryOption(fieldValues);
81  
82          // KFSMI-410: added one more node for consolidationOption
83          String consolidationOption = (String) fieldValues.get(GeneralLedgerConstants.DummyBusinessObject.CONSOLIDATION_OPTION);
84          // test if the consolidation option is selected or not
85          boolean isConsolidated = isConsolidationSelected(fieldValues); 
86          
87          // get the search result collection
88          // KFSMI-410: added one more node for consolidationOption
89          if (consolidationOption.equals(Constant.EXCLUDE_SUBACCOUNTS)){
90              fieldValues.put(Constant.SUB_ACCOUNT_OPTION, OLEConstants.getDashSubAccountNumber());
91              isConsolidated = false;
92          } 
93          
94          if (isConsolidated) {
95              Iterator availableBalanceIterator = accountBalanceService.findConsolidatedAvailableAccountBalance(fieldValues);
96              searchResultsCollection = buildConsolidedAvailableBalanceCollection(availableBalanceIterator);
97          }
98          else {
99              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 }