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                     List<Account> accountList =  checkAccountEntry(accountNumber,chartCode);
124                     for (Iterator<Account> accountIterator = accountList.iterator(); accountIterator.hasNext(); ) {
125                         Account account = accountIterator.next();
126                         AccountBalance balance = new AccountBalance();
127                         balance.setChartOfAccountsCode(account.getChartOfAccountsCode());
128                         balance.setAccountNumber(account.getAccountNumber());
129                         String fiscalYear = fieldValues.get(OLEConstants.FISCAL_YEAR).toString();
130                         balance.setUniversityFiscalYear(Integer.parseInt(fiscalYear));
131                         balance.setObjectCode(Constant.CONSOLIDATED_OBJECT_TYPE_CODE);
132                         balance.setSubAccountNumber(Constant.CONSOLIDATED_OBJECT_TYPE_CODE);
133                         balance.setSubObjectCode(Constant.CONSOLIDATED_OBJECT_TYPE_CODE);
134                         balance.setCurrentBudgetLineBalanceAmount(KualiDecimal.ZERO);
135                         balance.setAccountLineActualsBalanceAmount(KualiDecimal.ZERO);
136                         balance.setAccountLineEncumbranceBalanceAmount(KualiDecimal.ZERO);
137                         searchResultsCollection.add(balance);
138                     }
139                 }
140             }
141         return this.buildSearchResultList(searchResultsCollection, actualSize);
142     }
143 
144 
145     public List<Account> checkAccountEntry(String accountNumber,String chartCode) {
146         boolean exists = false;
147         Map searchMap = new HashMap();
148         searchMap.put(OLEConstants.ACCOUNT_NUMBER, accountNumber);
149         searchMap.put(OLEConstants.CHART_CODE,chartCode);
150         List<Account> accountList = (List) SpringContext.getBean(BusinessObjectService.class).findMatching(Account.class, searchMap);
151         return accountList;
152     }
153     /**
154      * This method builds the available account balance collection based on the input iterator
155      * 
156      * @param iterator the iterator of search results of account balance
157      * @return the account balance collection
158      */
159     private Collection buildConsolidedAvailableBalanceCollection(Iterator iterator) {
160         Collection balanceCollection = new ArrayList();
161 
162         // build available balance collection throught analyzing the input iterator
163         while (iterator.hasNext()) {
164             Object avaiableAccountBalance = iterator.next();
165 
166             if (avaiableAccountBalance.getClass().isArray()) {
167                 int i = 0;
168                 Object[] array = (Object[]) avaiableAccountBalance;
169                 AccountBalance accountBalance = new AccountBalance();
170 
171                 accountBalance.setUniversityFiscalYear(new Integer(array[i++].toString()));
172                 accountBalance.setChartOfAccountsCode(array[i++].toString());
173 
174                 accountBalance.setAccountNumber(array[i++].toString());
175                 accountBalance.setSubAccountNumber(Constant.CONSOLIDATED_SUB_ACCOUNT_NUMBER);
176 
177                 accountBalance.setObjectCode(array[i++].toString());
178                 accountBalance.setSubObjectCode(Constant.CONSOLIDATED_SUB_OBJECT_CODE);
179 
180                 String objectTypeCode = array[i++].toString();
181                 accountBalance.getFinancialObject().setFinancialObjectTypeCode(objectTypeCode);
182 
183                 KualiDecimal budgetAmount = new KualiDecimal(array[i++].toString());
184                 accountBalance.setCurrentBudgetLineBalanceAmount(budgetAmount);
185 
186                 KualiDecimal actualsAmount = new KualiDecimal(array[i++].toString());
187                 accountBalance.setAccountLineActualsBalanceAmount(actualsAmount);
188 
189                 KualiDecimal encumbranceAmount = new KualiDecimal(array[i].toString());
190                 accountBalance.setAccountLineEncumbranceBalanceAmount(encumbranceAmount);
191 
192                 KualiDecimal variance = calculateVariance(accountBalance);
193                 accountBalance.getDummyBusinessObject().setGenericAmount(variance);
194 
195                 balanceCollection.add(accountBalance);
196             }
197         }
198         return balanceCollection;
199     }
200 
201     /**
202      * This method builds the available account balance collection based on the input collection
203      * 
204      * @param collection a collection of account balance entries
205      * @return the account balance collection
206      */
207     private Collection buildDetailedAvailableBalanceCollection(Iterator iterator) {
208         Collection balanceCollection = new ArrayList();
209 
210         // build available balance collection throught analyzing the iterator above
211         while (iterator.hasNext()) {
212             AccountBalance accountBalance = (AccountBalance) iterator.next();
213 
214             if (accountBalance.getDummyBusinessObject() == null) {
215                 accountBalance.setDummyBusinessObject(new TransientBalanceInquiryAttributes());
216             }
217 
218             KualiDecimal variance = calculateVariance(accountBalance);
219             accountBalance.getDummyBusinessObject().setGenericAmount(variance);
220 
221             balanceCollection.add(accountBalance);
222         }
223         return balanceCollection;
224     }
225 
226     /**
227      * This method calculates the variance of current budget balance, actuals balance and encumbrance balance
228      * 
229      * @param balance an account balance entry
230      */
231     private KualiDecimal calculateVariance(AccountBalance balance) {
232 
233         KualiDecimal variance = new KualiDecimal(0.0);
234         KualiDecimal budgetAmount = balance.getCurrentBudgetLineBalanceAmount();
235         KualiDecimal actualsAmount = balance.getAccountLineActualsBalanceAmount();
236         KualiDecimal encumbranceAmount = balance.getAccountLineEncumbranceBalanceAmount();
237 
238         // determine if the object type code is one of the given codes
239         if (ObjectUtils.isNull(balance.getFinancialObject()) || StringUtils.isBlank(balance.getFinancialObject().getFinancialObjectTypeCode())) {
240             balance.refreshReferenceObject("financialObject"); // refresh if we need to...
241         }
242         ObjectCode financialObject = balance.getFinancialObject();
243         String objectTypeCode = (financialObject == null) ? Constant.EMPTY_STRING : financialObject.getFinancialObjectTypeCode();
244 
245         SystemOptions options = getOptionsService().getOptions(balance.getUniversityFiscalYear());
246         if (ObjectUtils.isNull(options)) {
247             options = getOptionsService().getCurrentYearOptions();
248         }
249         String[] objectTypeCodeList = new String[3];
250         objectTypeCodeList[0] = options.getFinObjTypeExpendNotExpCode();
251         objectTypeCodeList[1] = options.getFinObjTypeExpNotExpendCode();
252         objectTypeCodeList[2] = options.getFinObjTypeExpenditureexpCd();
253 
254         boolean isObjectTypeCodeInList = ArrayUtils.contains(objectTypeCodeList, objectTypeCode);
255 
256         // calculate the variance based on the object type code of the balance
257         if (isObjectTypeCodeInList) {
258             variance = budgetAmount.subtract(actualsAmount);
259             variance = variance.subtract(encumbranceAmount);
260         }
261         else {
262             variance = actualsAmount.subtract(budgetAmount);
263         }
264         return variance;
265     }
266 
267     /**
268      * Updates the collection of entries that will be applied to the results of the inquiry
269      * 
270      * @param entryCollection a collection of balance entries
271      * @param fieldValues the map containing the search fields and values
272      * @param isApproved flag whether the approved entries or all entries will be processed
273      * @param isConsolidated flag whether the results are consolidated or not
274      * @param isCostShareExcluded flag whether the user selects to see the results with cost share subaccount
275      * @see org.kuali.module.gl.web.lookupable.AbstractGLLookupableImpl#updateEntryCollection(java.util.Collection, java.util.Map,
276      *      boolean, boolean, boolean)
277      */
278     @Override
279     protected void updateEntryCollection(Collection entryCollection, Map fieldValues, boolean isApproved, boolean isConsolidated, boolean isCostShareExcluded) {
280 
281         // convert the field names of balance object into corresponding ones of pending entry object
282         Map pendingEntryFieldValues = BusinessObjectFieldConverter.convertToTransactionFieldValues(fieldValues);
283 
284         // go through the pending entries to update the balance collection
285         Iterator pendingEntryIterator = getGeneralLedgerPendingEntryService().findPendingLedgerEntriesForAccountBalance(pendingEntryFieldValues, isApproved);
286         while (pendingEntryIterator.hasNext()) {
287             GeneralLedgerPendingEntry pendingEntry = (GeneralLedgerPendingEntry) pendingEntryIterator.next();
288 
289             if (isCostShareExcluded) {
290                 if (ObjectUtils.isNotNull(pendingEntry.getSubAccount()) && ObjectUtils.isNotNull(pendingEntry.getSubAccount().getA21SubAccount())) {
291                     if (OLEConstants.SubAccountType.COST_SHARE.equals(pendingEntry.getSubAccount().getA21SubAccount().getSubAccountTypeCode())) {
292                         // Don't process this one
293                         continue;
294                     }
295                 }
296             }
297 
298             // if consolidated, change the following fields into the default values for consolidation
299             if (isConsolidated) {
300                 pendingEntry.setSubAccountNumber(Constant.CONSOLIDATED_SUB_ACCOUNT_NUMBER);
301                 pendingEntry.setFinancialSubObjectCode(Constant.CONSOLIDATED_SUB_OBJECT_CODE);
302                 pendingEntry.setFinancialObjectTypeCode(Constant.CONSOLIDATED_OBJECT_TYPE_CODE);
303             }
304 
305             AccountBalance accountBalance = postAccountBalance.findAccountBalance(entryCollection, pendingEntry);
306             postAccountBalance.updateAccountBalance(pendingEntry, accountBalance);
307 
308             // recalculate the variance after pending entries are combined into account balances
309             if (accountBalance.getDummyBusinessObject() == null) {
310                 accountBalance.setDummyBusinessObject(new TransientBalanceInquiryAttributes());
311             }
312             KualiDecimal variance = calculateVariance(accountBalance);
313             accountBalance.getDummyBusinessObject().setGenericAmount(variance);
314         }
315     }
316 
317     /**
318      * Sets the postAccountBalance attribute value.
319      * 
320      * @param postAccountBalance The postAccountBalance to set.
321      */
322     public void setPostAccountBalance(AccountBalanceCalculator postAccountBalance) {
323         this.postAccountBalance = postAccountBalance;
324     }
325 
326     /**
327      * Sets the accountBalanceService attribute value.
328      * 
329      * @param accountBalanceService The accountBalanceService to set.
330      */
331     public void setAccountBalanceService(AccountBalanceService accountBalanceService) {
332         this.accountBalanceService = accountBalanceService;
333     }
334 
335     /**
336      * Sets the optionsService attribute value
337      * 
338      * @param optionsService The optionsService to set.
339      */
340     public void setOptionsService(OptionsService optionsService) {
341         this.optionsService = optionsService;
342     }
343 
344     /**
345      * Gets the optionsService attribute. 
346      * @return Returns the optionsService.
347      */
348     public OptionsService getOptionsService() {
349         return optionsService;
350     }
351 }