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.module.ld.businessobject.lookup;
017    
018    import static org.apache.commons.collections.IteratorUtils.toList;
019    
020    import java.util.ArrayList;
021    import java.util.Collection;
022    import java.util.Collections;
023    import java.util.HashMap;
024    import java.util.List;
025    import java.util.Map;
026    
027    import org.apache.commons.lang.StringUtils;
028    import org.kuali.kfs.gl.Constant;
029    import org.kuali.kfs.gl.businessobject.TransientBalanceInquiryAttributes;
030    import org.kuali.kfs.integration.ld.LaborLedgerObject;
031    import org.kuali.kfs.module.ld.LaborKeyConstants;
032    import org.kuali.kfs.module.ld.businessobject.AccountStatusCurrentFunds;
033    import org.kuali.kfs.module.ld.businessobject.July1PositionFunding;
034    import org.kuali.kfs.module.ld.businessobject.LaborObject;
035    import org.kuali.kfs.module.ld.businessobject.LedgerBalance;
036    import org.kuali.kfs.module.ld.businessobject.inquiry.AbstractLaborInquirableImpl;
037    import org.kuali.kfs.module.ld.businessobject.inquiry.CurrentFundsInquirableImpl;
038    import org.kuali.kfs.module.ld.businessobject.inquiry.PositionDataDetailsInquirableImpl;
039    import org.kuali.kfs.module.ld.dataaccess.LaborDao;
040    import org.kuali.kfs.module.ld.service.LaborInquiryOptionsService;
041    import org.kuali.kfs.module.ld.service.LaborLedgerBalanceService;
042    import org.kuali.kfs.sys.KFSConstants;
043    import org.kuali.kfs.sys.KFSPropertyConstants;
044    import org.kuali.kfs.sys.ObjectUtil;
045    import org.kuali.rice.kns.bo.BusinessObject;
046    import org.kuali.rice.kns.lookup.AbstractLookupableHelperServiceImpl;
047    import org.kuali.rice.kns.lookup.CollectionIncomplete;
048    import org.kuali.rice.kns.lookup.HtmlData;
049    import org.kuali.rice.kns.lookup.HtmlData.AnchorHtmlData;
050    import org.kuali.rice.kns.service.BusinessObjectService;
051    import org.kuali.rice.kns.util.BeanPropertyComparator;
052    import org.kuali.rice.kns.util.GlobalVariables;
053    import org.kuali.rice.kns.util.KualiDecimal;
054    import org.kuali.rice.kns.web.ui.Row;
055    
056    /**
057     * Service implementation for the CurrentFundsLookupableHelperServiceImpl class is the front-end for all current funds balance
058     * inquiry processing.
059     */
060    public class CurrentFundsLookupableHelperServiceImpl extends AbstractLookupableHelperServiceImpl {
061        private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(CurrentFundsLookupableHelperServiceImpl.class);
062        private LaborDao laborDao;
063        private LaborLedgerBalanceService balanceService;
064        private LaborInquiryOptionsService laborInquiryOptionsService;
065        private BusinessObjectService businessObjectService;
066    
067        /**
068         * @see org.kuali.rice.kns.lookup.Lookupable#getInquiryUrl(org.kuali.rice.kns.bo.BusinessObject, java.lang.String)
069         */
070        @Override
071        public HtmlData getInquiryUrl(BusinessObject bo, String propertyName) {
072            if (KFSPropertyConstants.POSITION_NUMBER.equals(propertyName)) {
073                LedgerBalance balance = (LedgerBalance) bo;
074                AbstractLaborInquirableImpl positionDataDetailsInquirable = new PositionDataDetailsInquirableImpl();
075    
076                Map<String, String> fieldValues = new HashMap<String, String>();
077                fieldValues.put(propertyName, balance.getPositionNumber());
078    
079                BusinessObject positionData = positionDataDetailsInquirable.getBusinessObject(fieldValues);
080    
081                return positionData == null ? new AnchorHtmlData(KFSConstants.EMPTY_STRING, KFSConstants.EMPTY_STRING) : positionDataDetailsInquirable.getInquiryUrl(positionData, propertyName);
082            }
083            return (new CurrentFundsInquirableImpl()).getInquiryUrl(bo, propertyName);
084        }
085    
086        /**
087         * Gets a list with the fields that will be displayed on page
088         * 
089         * @param fieldValues list of fields that are used as a key to filter out data
090         * @see org.kuali.rice.kns.lookup.Lookupable#getSearchResults(java.util.Map)
091         */
092        @Override
093        public List getSearchResults(Map fieldValues) {
094            LOG.info("getSearchResults() - Entry");
095    
096            boolean unbounded = false;
097            Long actualCountIfTruncated = new Long(0);
098    
099            setBackLocation((String) fieldValues.get(KFSConstants.BACK_LOCATION));
100            setDocFormKey((String) fieldValues.get(KFSConstants.DOC_FORM_KEY));
101    
102            // get the pending entry option. This method must be prior to the get search results
103            String pendingEntryOption = laborInquiryOptionsService.getSelectedPendingEntryOption(fieldValues);
104    
105            // get the consolidation option
106            boolean isConsolidated = laborInquiryOptionsService.isConsolidationSelected(fieldValues, (Collection<Row>) getRows());
107    
108            String searchObjectCodeVal = (String) fieldValues.get(KFSPropertyConstants.FINANCIAL_OBJECT_CODE);
109            // Check for a valid labor object code for this inquiry
110            if (StringUtils.isNotBlank(searchObjectCodeVal)) {
111                Map objectCodeFieldValues = new HashMap();
112                objectCodeFieldValues.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, fieldValues.get(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR));
113                objectCodeFieldValues.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, fieldValues.get(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE));
114                objectCodeFieldValues.put(KFSPropertyConstants.FINANCIAL_OBJECT_CODE, searchObjectCodeVal);
115    
116                LaborLedgerObject foundObjectCode = (LaborLedgerObject) businessObjectService.findByPrimaryKey(LaborObject.class, objectCodeFieldValues);
117    
118                if (foundObjectCode == null) {
119                    GlobalVariables.getMessageMap().putError(KFSPropertyConstants.FINANCIAL_OBJECT_CODE, LaborKeyConstants.ERROR_INVALID_LABOR_OBJECT_CODE, "2");
120                    return new CollectionIncomplete(new ArrayList(), actualCountIfTruncated);
121                }
122            }
123    
124            // Parse the map and call the DAO to process the inquiry
125            Collection<AccountStatusCurrentFunds> searchResultsCollection = buildCurrentFundsCollection(toList(laborDao.getCurrentFunds(fieldValues, isConsolidated)), isConsolidated, pendingEntryOption);
126    
127            // update search results according to the selected pending entry option
128            laborInquiryOptionsService.updateCurrentFundsByPendingLedgerEntry(searchResultsCollection, fieldValues, pendingEntryOption, isConsolidated);
129    
130            // gets the July1st budget amount column.
131            Collection<July1PositionFunding> july1PositionFundings = laborDao.getJuly1(fieldValues);
132            this.updateJuly1BalanceAmount(searchResultsCollection, july1PositionFundings, isConsolidated);
133    
134            // sort list if default sort column given
135            List searchResults = (List) searchResultsCollection;
136            List defaultSortColumns = getDefaultSortColumns();
137            if (defaultSortColumns.size() > 0) {
138                Collections.sort(searchResults, new BeanPropertyComparator(defaultSortColumns, true));
139            }
140            return new CollectionIncomplete(searchResults, actualCountIfTruncated);
141        }
142    
143        /**
144         * Adds the july1 budget amount to each account found in the
145         * 
146         * @param searchResultsCollection collection with the list of current funds where the amount is added
147         * @param july1PositionFundings collection of current funds with july1st budget amounts
148         * @param isConsolidated
149         */
150        private void updateJuly1BalanceAmount(Collection<AccountStatusCurrentFunds> searchResultsCollection, Collection<July1PositionFunding> july1PositionFundings, boolean isConsolidated) {
151            for (July1PositionFunding july1PositionFunding : july1PositionFundings) {
152                for (AccountStatusCurrentFunds accountStatus : searchResultsCollection) {
153                    boolean found = ObjectUtil.equals(accountStatus, july1PositionFunding, accountStatus.getKeyFieldList(isConsolidated));
154                    if (found) {
155                        accountStatus.setJuly1BudgetAmount(accountStatus.getJuly1BudgetAmount().add(july1PositionFunding.getJuly1BudgetAmount()));
156                    }
157                }
158            }
159        }
160    
161        /**
162         * Returns a list with the current funds.
163         * 
164         * @param iterator the iterator of search results of account status
165         * @param isConsolidated determine if the consolidated result is desired
166         * @param pendingEntryOption the given pending entry option that can be no, approved or all
167         * @return the current funds collection
168         */
169        private Collection<AccountStatusCurrentFunds> buildCurrentFundsCollection(Collection collection, boolean isConsolidated, String pendingEntryOption) {
170            Collection<AccountStatusCurrentFunds> retval = null;
171    
172            if (isConsolidated) {
173                retval = buildCosolidatedCurrentFundsCollection(collection, pendingEntryOption);
174            }
175            else {
176                retval = buildDetailedCurrentFundsCollection(collection, pendingEntryOption);
177            }
178            return retval;
179        }
180    
181        /**
182         * Builds the current funds collection with consolidation option from an iterator
183         * 
184         * @param iterator
185         * @param pendingEntryOption the selected pending entry option
186         * @return the consolidated current funds collection
187         */
188        private Collection<AccountStatusCurrentFunds> buildCosolidatedCurrentFundsCollection(Collection collection, String pendingEntryOption) {
189            Collection<AccountStatusCurrentFunds> retval = new ArrayList<AccountStatusCurrentFunds>();
190            for (Object collectionEntry : collection) {
191                if (collectionEntry.getClass().isArray()) {
192                    int i = 0;
193                    Object[] array = (Object[]) collectionEntry;
194                    AccountStatusCurrentFunds cf = new AccountStatusCurrentFunds();
195                    LOG.debug("element length " + array.length);
196                    for (Object element : array) {
197                        LOG.debug("I found this element " + element);
198                    }
199    
200                    if (AccountStatusCurrentFunds.class.isAssignableFrom(getBusinessObjectClass())) {
201                        try {
202                            cf = (AccountStatusCurrentFunds) getBusinessObjectClass().newInstance();
203                        }
204                        catch (Exception e) {
205                            LOG.warn("Using " + AccountStatusCurrentFunds.class + " for results because I couldn't instantiate the " + getBusinessObjectClass());
206                        }
207                    }
208                    else {
209                        LOG.warn("Using " + AccountStatusCurrentFunds.class + " for results because I couldn't instantiate the " + getBusinessObjectClass());
210                    }
211    
212                    cf.setUniversityFiscalYear(new Integer(array[i++].toString()));
213                    cf.setChartOfAccountsCode(array[i++].toString());
214                    cf.setAccountNumber(array[i++].toString());
215    
216                    String subAccountNumber = Constant.CONSOLIDATED_SUB_ACCOUNT_NUMBER;
217                    cf.setSubAccountNumber(subAccountNumber);
218    
219                    cf.setBalanceTypeCode(array[i++].toString());
220                    cf.setFinancialObjectCode(array[i++].toString());
221    
222                    cf.setEmplid(array[i++].toString());
223                    cf.setPositionNumber(array[i++].toString());
224    
225                    cf.setFinancialSubObjectCode(Constant.CONSOLIDATED_SUB_OBJECT_CODE);
226                    cf.setObjectTypeCode(Constant.CONSOLIDATED_OBJECT_TYPE_CODE);
227    
228                    cf.setAccountLineAnnualBalanceAmount(new KualiDecimal(array[i++].toString()));
229                    cf.setBeginningBalanceLineAmount(new KualiDecimal(array[i++].toString()));
230                    cf.setContractsGrantsBeginningBalanceAmount(new KualiDecimal(array[i++].toString()));
231    
232                    cf.setMonth1Amount(new KualiDecimal(array[i++].toString()));
233    
234                    cf.setDummyBusinessObject(new TransientBalanceInquiryAttributes());
235                    cf.getDummyBusinessObject().setPendingEntryOption(pendingEntryOption);
236                    cf.setOutstandingEncum(getOutstandingEncum(cf));
237    
238                    cf.getDummyBusinessObject().setPendingEntryOption(pendingEntryOption);
239                    cf.getDummyBusinessObject().setConsolidationOption(Constant.CONSOLIDATION);
240    
241                    retval.add(cf);
242                }
243            }
244            return retval;
245        }
246    
247        /**
248         * Builds the current funds collection with detail option from an iterator
249         * 
250         * @param iterator the current funds iterator
251         * @param pendingEntryOption the selected pending entry option
252         * @return the detailed balance collection
253         */
254        private Collection<AccountStatusCurrentFunds> buildDetailedCurrentFundsCollection(Collection collection, String pendingEntryOption) {
255            Collection<AccountStatusCurrentFunds> retval = new ArrayList<AccountStatusCurrentFunds>();
256    
257            for (LedgerBalance balance : ((Collection<LedgerBalance>) collection)) {
258                AccountStatusCurrentFunds cf = new AccountStatusCurrentFunds();
259                ObjectUtil.buildObject(cf, balance);
260    
261                cf.setDummyBusinessObject(new TransientBalanceInquiryAttributes());
262                cf.getDummyBusinessObject().setPendingEntryOption(pendingEntryOption);
263                cf.setOutstandingEncum(getOutstandingEncum(cf));
264    
265                cf.getDummyBusinessObject().setPendingEntryOption(pendingEntryOption);
266                cf.getDummyBusinessObject().setConsolidationOption(Constant.DETAIL);
267    
268                retval.add(cf);
269            }
270            return retval;
271        }
272    
273        /**
274         * Gets the outstanding encumbrance amount
275         * 
276         * @param AccountStatusCurrentFunds
277         * @param Map fieldValues
278         */
279        private KualiDecimal getOutstandingEncum(AccountStatusCurrentFunds bo) {
280            Map<String, String> fieldValues = new HashMap();
281            fieldValues.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, bo.getUniversityFiscalYear().toString());
282            fieldValues.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, bo.getChartOfAccountsCode());
283            fieldValues.put(KFSPropertyConstants.ACCOUNT_NUMBER, bo.getAccountNumber());
284    
285            if (!bo.getSubAccountNumber().equals(Constant.CONSOLIDATED_SUB_ACCOUNT_NUMBER)) {
286                fieldValues.put(KFSPropertyConstants.SUB_ACCOUNT_NUMBER, bo.getSubAccountNumber());
287            }
288    
289            fieldValues.put(KFSPropertyConstants.FINANCIAL_OBJECT_CODE, bo.getFinancialObjectCode());
290    
291            if (!bo.getFinancialSubObjectCode().equals(Constant.CONSOLIDATED_SUB_OBJECT_CODE)) {
292                fieldValues.put(KFSPropertyConstants.FINANCIAL_SUB_OBJECT_CODE, bo.getFinancialSubObjectCode());
293            }
294            fieldValues.put(KFSPropertyConstants.FINANCIAL_BALANCE_TYPE_CODE, KFSConstants.BALANCE_TYPE_INTERNAL_ENCUMBRANCE); // Encumberance
295            // Balance
296            // Type
297            fieldValues.put(KFSPropertyConstants.EMPLID, bo.getEmplid());
298            LOG.debug("using " + fieldValues.values());
299            LOG.debug("using " + fieldValues.keySet());
300            return (KualiDecimal) laborDao.getEncumbranceTotal(fieldValues);
301        }
302    
303        /**
304         * Sets the balanceService attribute value.
305         * 
306         * @param balanceService The balanceService to set.
307         */
308        public void setBalanceService(LaborLedgerBalanceService balanceService) {
309            this.balanceService = balanceService;
310        }
311    
312        /**
313         * Sets the laborDao attribute value.
314         * 
315         * @param laborDao The laborDao to set.
316         */
317        public void setLaborDao(LaborDao laborDao) {
318            this.laborDao = laborDao;
319        }
320    
321        /**
322         * Sets the laborInquiryOptionsService attribute value.
323         * 
324         * @param laborInquiryOptionsService The laborInquiryOptionsService to set.
325         */
326        public void setLaborInquiryOptionsService(LaborInquiryOptionsService laborInquiryOptionsService) {
327            this.laborInquiryOptionsService = laborInquiryOptionsService;
328        }
329    
330        /**
331         * Sets the businessObjectService attribute value.
332         * 
333         * @param businessObjectService The businessObjectService to set.
334         */
335        public void setBusinessObjectService(BusinessObjectService businessObjectService) {
336            this.businessObjectService = businessObjectService;
337        }
338    
339    }