/* * The Kuali Financial System, a comprehensive financial management system for higher education. * * Copyright 2005-2014 The Kuali Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.kuali.kfs.gl.dataaccess.impl; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.ojb.broker.query.Criteria; import org.apache.ojb.broker.query.QueryByCriteria; import org.apache.ojb.broker.query.QueryFactory; import org.apache.ojb.broker.query.ReportQueryByCriteria; import org.kuali.kfs.gl.businessobject.OriginEntryFull; import org.kuali.kfs.gl.businessobject.OriginEntryGroup; import org.kuali.kfs.gl.businessobject.OriginEntryInformation; import org.kuali.kfs.gl.dataaccess.OriginEntryDao; import org.kuali.kfs.sys.KFSConstants; import org.kuali.kfs.sys.KFSPropertyConstants; import org.kuali.kfs.sys.util.TransactionalServiceUtils; import org.kuali.rice.core.api.util.type.KualiDecimal; import org.kuali.rice.core.framework.persistence.ojb.dao.PlatformAwareDaoBaseOjb; /** * An OJB implementation of the OriginEntryDao */ public class OriginEntryDaoOjb extends PlatformAwareDaoBaseOjb implements OriginEntryDao { private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(OriginEntryDaoOjb.class); private static final String ENTRY_GROUP_ID = "entryGroupId"; private static final String ENTRY_ID = "entryId"; private static final String FINANCIAL_BALANCE_TYPE_CODE = "financialBalanceTypeCode"; private static final String CHART_OF_ACCOUNTS_CODE = "chartOfAccountsCode"; private static final String ACCOUNT_NUMBER = "accountNumber"; private static final String SUB_ACCOUNT_NUMBER = "subAccountNumber"; private static final String FINANCIAL_DOCUMENT_TYPE_CODE = "financialDocumentTypeCode"; private static final String FINANCIAL_SYSTEM_ORIGINATION_CODE = "financialSystemOriginationCode"; private static final String FINANCIAL_DOCUMENT_REVERSAL_DATE = "financialDocumentReversalDate"; private static final String UNIVERSITY_FISCAL_PERIOD_CODE = "universityFiscalPeriodCode"; private static final String UNIVERSITY_FISCAL_YEAR = "universityFiscalYear"; private static final String FINANCIAL_OBJECT_CODE = "financialObjectCode"; private static final String FINANCIAL_SUB_OBJECT_CODE = "financialSubObjectCode"; private static final String FINANCIAL_OBJECT_TYPE_CODE = "financialObjectTypeCode"; private static final String TRANSACTION_LEDGER_ENTRY_SEQUENCE_NUMBER = "transactionLedgerEntrySequenceNumber"; private static final String TRANSACTION_LEDGER_ENTRY_DESCRIPTION = "transactionLedgerEntryDescription"; private static final String TRANSACTION_LEDGER_ENTRY_AMOUNT = "transactionLedgerEntryAmount"; private static final String TRANSACTION_DEBIT_CREDIT_CODE = "transactionDebitCreditCode"; private Class entryClass; /** * Sets the class of the origin entries this class deals with. This makes this particular * class very flexible; instances of it can deal with OriginEntryLites as well as they deal * with OriginEntryFulls. * * @param entryClass the class of OriginEntries this instance will use for OJB operations */ public void setEntryClass(Class entryClass) { this.entryClass = entryClass; } /** * Gets the entryClass attribute. * * @return Returns the entryClass. */ public Class getEntryClass() { return entryClass; } /** * Constructs a OriginEntryDaoOjb instance */ public OriginEntryDaoOjb() { super(); } /** * Get the total amount of transactions in a group * @param the id of the origin entry group to total * @param isCredit whether the total should be of credits or not * @return the sum of all queried origin entries * @see org.kuali.kfs.gl.dataaccess.OriginEntryDao#getGroupTotal(java.lang.Integer, boolean) */ public KualiDecimal getGroupTotal(Integer groupId, boolean isCredit) { LOG.debug("getGroupTotal() started"); Criteria crit = new Criteria(); crit.addEqualTo(OriginEntryDaoOjb.ENTRY_GROUP_ID, groupId); if (isCredit) { crit.addEqualTo(OriginEntryDaoOjb.TRANSACTION_DEBIT_CREDIT_CODE, KFSConstants.GL_CREDIT_CODE); } else { crit.addNotEqualTo(OriginEntryDaoOjb.TRANSACTION_DEBIT_CREDIT_CODE, KFSConstants.GL_CREDIT_CODE); } ReportQueryByCriteria q = QueryFactory.newReportQuery(entryClass, crit); q.setAttributes(new String[] { "SUM(" + OriginEntryDaoOjb.TRANSACTION_LEDGER_ENTRY_AMOUNT + ")" }); Iterator i = getPersistenceBrokerTemplate().getReportQueryIteratorByQuery(q); if (i.hasNext()) { Object[] data = (Object[]) TransactionalServiceUtils.retrieveFirstAndExhaustIterator(i); return (KualiDecimal) data[0]; } else { return null; } } /** * Counts the number of entries in a group * @param the id of an origin entry group * @return the count of the entries in that group * @see org.kuali.kfs.gl.dataaccess.OriginEntryDao#getGroupCount(java.lang.Integer) */ public Integer getGroupCount(Integer groupId) { LOG.debug("getGroupCount() started"); Criteria crit = new Criteria(); crit.addEqualTo(OriginEntryDaoOjb.ENTRY_GROUP_ID, groupId); ReportQueryByCriteria q = QueryFactory.newReportQuery(entryClass, crit); q.setAttributes(new String[] { "count(*)" }); Iterator i = getPersistenceBrokerTemplate().getReportQueryIteratorByQuery(q); if (i.hasNext()) { Object[] data = (Object[]) TransactionalServiceUtils.retrieveFirstAndExhaustIterator(i); if (data[0] instanceof BigDecimal) { return ((BigDecimal) data[0]).intValue(); } else { return ((Long) data[0]).intValue(); } } else { return null; } } /** * Counts of rows of all the origin entry groups * * @return iterator of Object[] {[BigDecimal id,BigDecimal count]} * @see org.kuali.kfs.gl.dataaccess.OriginEntryDao#getGroupCounts() */ public Iterator getGroupCounts() { LOG.debug("getGroupCounts() started"); Criteria crit = new Criteria(); ReportQueryByCriteria q = QueryFactory.newReportQuery(entryClass, crit); q.setAttributes(new String[] { ENTRY_GROUP_ID, "count(*)" }); q.addGroupBy(ENTRY_GROUP_ID); return getPersistenceBrokerTemplate().getReportQueryIteratorByQuery(q); } /** * Delete an entry from the database * @param oe the entry to delete * @see org.kuali.kfs.gl.dataaccess.OriginEntryDao#deleteEntry(org.kuali.kfs.gl.businessobject.OriginEntryInformation) */ public void deleteEntry(OriginEntryInformation oe) { LOG.debug("deleteEntry() started"); getPersistenceBrokerTemplate().delete(oe); } /** * Return an iterator of keys of all documents referenced by origin entries in a given group * * @param oeg Group the origin entry group to find entries in, by origin entry * @return Iterator of java.lang.Object[] with report data about all of the distinct document numbers/type code/origination code combinations of origin entries in the group * @see org.kuali.kfs.gl.dataaccess.OriginEntryDao#getDocumentsByGroup(org.kuali.kfs.gl.businessobject.OriginEntryGroup) */ public Iterator getDocumentsByGroup(OriginEntryGroup oeg) { LOG.debug("getDocumentsByGroup() started"); Criteria criteria = new Criteria(); criteria.addEqualTo(ENTRY_GROUP_ID, oeg.getId()); ReportQueryByCriteria q = QueryFactory.newReportQuery(entryClass, criteria); q.setAttributes(new String[] { KFSPropertyConstants.DOCUMENT_NUMBER, "financialDocumentTypeCode", "financialSystemOriginationCode" }); q.setDistinct(true); return getPersistenceBrokerTemplate().getReportQueryIteratorByQuery(q); } /** * Iterator of entries that match criteria * * @param searchCriteria Map of field, value pairs * @return collection of entries * @see org.kuali.kfs.gl.dataaccess.OriginEntryDao#getMatchingEntries(java.util.Map) */ public Iterator<OriginEntryFull> getMatchingEntries(Map searchCriteria) { LOG.debug("getMatchingEntries() started"); Criteria criteria = new Criteria(); for (Iterator iter = searchCriteria.keySet().iterator(); iter.hasNext();) { String element = (String) iter.next(); criteria.addEqualTo(element, searchCriteria.get(element)); } QueryByCriteria qbc = QueryFactory.newQuery(entryClass, criteria); qbc.addOrderByAscending(ENTRY_GROUP_ID); return getPersistenceBrokerTemplate().getIteratorByQuery(qbc); } /** * Get bad balance entries * * @param groups a Collection of groups to remove bad entries in * @return an Iterator of no good, won't use, bad balance entries * @see org.kuali.kfs.gl.dataaccess.OriginEntryDao#getBadBalanceEntries(java.util.Collection) */ public Iterator<OriginEntryFull> getBadBalanceEntries(Collection groups) { LOG.debug("getBadBalanceEntries() started"); if (groups.size() <= 0) { return null; } Collection ids = new ArrayList(); for (Iterator iter = groups.iterator(); iter.hasNext();) { OriginEntryGroup element = (OriginEntryGroup) iter.next(); ids.add(element.getId()); } Criteria crit1 = new Criteria(); crit1.addIn(ENTRY_GROUP_ID, ids); Criteria crit2 = new Criteria(); crit2.addIsNull(FINANCIAL_BALANCE_TYPE_CODE); Criteria crit3 = new Criteria(); crit3.addEqualTo(FINANCIAL_BALANCE_TYPE_CODE, " "); crit2.addOrCriteria(crit3); crit1.addAndCriteria(crit2); QueryByCriteria qbc = QueryFactory.newQuery(entryClass, crit1); qbc.addOrderByAscending(UNIVERSITY_FISCAL_YEAR); qbc.addOrderByAscending(CHART_OF_ACCOUNTS_CODE); qbc.addOrderByAscending(ACCOUNT_NUMBER); qbc.addOrderByAscending(FINANCIAL_OBJECT_CODE); qbc.addOrderByAscending(FINANCIAL_OBJECT_TYPE_CODE); qbc.addOrderByAscending(FINANCIAL_BALANCE_TYPE_CODE); qbc.addOrderByAscending(UNIVERSITY_FISCAL_PERIOD_CODE); qbc.addOrderByAscending(FINANCIAL_DOCUMENT_TYPE_CODE); qbc.addOrderByAscending(FINANCIAL_SYSTEM_ORIGINATION_CODE); qbc.addOrderByAscending(KFSPropertyConstants.DOCUMENT_NUMBER); return getPersistenceBrokerTemplate().getIteratorByQuery(qbc); } /** * This method is special because of the order by. It is used in the scrubber. The getMatchingEntries wouldn't work because of * the required order by. * * @param OriginEntryGroup the originEntryGroup that holds the origin entries to find * @param sort the sort order to sort entries by, defined in OriginEntryDao * * @return an Iterator of whichever flavor of OriginEntries this instance uses */ public <T> Iterator<T> getEntriesByGroup(OriginEntryGroup oeg, int sort) { LOG.debug("getEntriesByGroup() started"); // clear cache because the GLCP document class saves to the origin entry table and // reads from it (via this method) in the same transaction. If the clearCache line is // deleted, then the references to OriginEntries returned by this method will be null. getPersistenceBrokerTemplate().clearCache(); Criteria criteria = new Criteria(); criteria.addEqualTo(ENTRY_GROUP_ID, oeg.getId()); QueryByCriteria qbc = QueryFactory.newQuery(entryClass, criteria); if (sort == OriginEntryDao.SORT_DOCUMENT) { qbc.addOrderByAscending(FINANCIAL_DOCUMENT_TYPE_CODE); qbc.addOrderByAscending(FINANCIAL_SYSTEM_ORIGINATION_CODE); qbc.addOrderByAscending(KFSPropertyConstants.DOCUMENT_NUMBER); qbc.addOrderByAscending(CHART_OF_ACCOUNTS_CODE); qbc.addOrderByAscending(ACCOUNT_NUMBER); qbc.addOrderByAscending(SUB_ACCOUNT_NUMBER); qbc.addOrderByAscending(FINANCIAL_BALANCE_TYPE_CODE); qbc.addOrderByAscending(FINANCIAL_DOCUMENT_REVERSAL_DATE); qbc.addOrderByAscending(UNIVERSITY_FISCAL_PERIOD_CODE); qbc.addOrderByAscending(UNIVERSITY_FISCAL_YEAR); // The above order by fields are required by the scrubber process. Adding these // fields makes the data in the exact same order as the COBOL scrubber. qbc.addOrderByAscending(FINANCIAL_OBJECT_CODE); qbc.addOrderByAscending(FINANCIAL_SUB_OBJECT_CODE); qbc.addOrderByAscending(FINANCIAL_BALANCE_TYPE_CODE); qbc.addOrderByAscending(FINANCIAL_OBJECT_TYPE_CODE); qbc.addOrderByAscending(UNIVERSITY_FISCAL_PERIOD_CODE); qbc.addOrderByAscending(FINANCIAL_DOCUMENT_TYPE_CODE); qbc.addOrderByAscending(FINANCIAL_SYSTEM_ORIGINATION_CODE); qbc.addOrderByAscending(KFSPropertyConstants.DOCUMENT_NUMBER); qbc.addOrderByAscending(TRANSACTION_LEDGER_ENTRY_SEQUENCE_NUMBER); qbc.addOrderByAscending(TRANSACTION_LEDGER_ENTRY_DESCRIPTION); qbc.addOrderByAscending(TRANSACTION_LEDGER_ENTRY_AMOUNT); qbc.addOrderByAscending(TRANSACTION_DEBIT_CREDIT_CODE); } else if (sort == OriginEntryDao.SORT_REPORT) { qbc.addOrderByAscending(FINANCIAL_DOCUMENT_TYPE_CODE); qbc.addOrderByAscending(FINANCIAL_SYSTEM_ORIGINATION_CODE); qbc.addOrderByAscending(KFSPropertyConstants.DOCUMENT_NUMBER); qbc.addOrderByAscending(TRANSACTION_DEBIT_CREDIT_CODE); qbc.addOrderByAscending(CHART_OF_ACCOUNTS_CODE); qbc.addOrderByAscending(ACCOUNT_NUMBER); qbc.addOrderByAscending(FINANCIAL_OBJECT_CODE); } else if (sort == OriginEntryDao.SORT_LISTING_REPORT) { qbc.addOrderByAscending(UNIVERSITY_FISCAL_YEAR); qbc.addOrderByAscending(CHART_OF_ACCOUNTS_CODE); qbc.addOrderByAscending(ACCOUNT_NUMBER); qbc.addOrderByAscending(FINANCIAL_OBJECT_CODE); qbc.addOrderByAscending(FINANCIAL_OBJECT_TYPE_CODE); qbc.addOrderByAscending(FINANCIAL_BALANCE_TYPE_CODE); qbc.addOrderByAscending(UNIVERSITY_FISCAL_PERIOD_CODE); qbc.addOrderByAscending(FINANCIAL_DOCUMENT_TYPE_CODE); qbc.addOrderByAscending(FINANCIAL_SYSTEM_ORIGINATION_CODE); qbc.addOrderByAscending(KFSPropertyConstants.DOCUMENT_NUMBER); qbc.addOrderByAscending(TRANSACTION_LEDGER_ENTRY_DESCRIPTION); } else { qbc.addOrderByAscending(CHART_OF_ACCOUNTS_CODE); qbc.addOrderByAscending(ACCOUNT_NUMBER); qbc.addOrderByAscending(SUB_ACCOUNT_NUMBER); qbc.addOrderByAscending(FINANCIAL_OBJECT_CODE); qbc.addOrderByAscending(FINANCIAL_OBJECT_TYPE_CODE); qbc.addOrderByAscending(UNIVERSITY_FISCAL_PERIOD_CODE); qbc.addOrderByAscending(FINANCIAL_DOCUMENT_TYPE_CODE); qbc.addOrderByAscending(FINANCIAL_SYSTEM_ORIGINATION_CODE); qbc.addOrderByAscending(KFSPropertyConstants.DOCUMENT_NUMBER); qbc.addOrderByAscending(TRANSACTION_LEDGER_ENTRY_DESCRIPTION); } return getPersistenceBrokerTemplate().getIteratorByQuery(qbc); } /** * This method should only be used in unit tests. It loads all the gl_origin_entry_t rows in memory into a collection. This * won't work for production because there would be too many rows to load into memory. * * @return a collection of OriginEntryFulls */ public Collection<OriginEntryFull> testingGetAllEntries() { LOG.debug("testingGetAllEntries() started"); Criteria criteria = new Criteria(); QueryByCriteria qbc = QueryFactory.newQuery(entryClass, criteria); qbc.addOrderByAscending(ENTRY_GROUP_ID); qbc.addOrderByAscending(ENTRY_ID); return getPersistenceBrokerTemplate().getCollectionByQuery(qbc); } /** * Delete entries matching searchCriteria search criteria. * * @param searchCriteria a map of criteria to use as keys for building a query */ public void deleteMatchingEntries(Map searchCriteria) { LOG.debug("deleteMatchingEntries() started"); Criteria criteria = new Criteria(); for (Iterator iter = searchCriteria.keySet().iterator(); iter.hasNext();) { String element = (String) iter.next(); criteria.addEqualTo(element, searchCriteria.get(element)); } QueryByCriteria qbc = QueryFactory.newQuery(entryClass, criteria); getPersistenceBrokerTemplate().deleteByQuery(qbc); // This is required because deleteByQuery leaves the cache alone so future queries // could return origin entries that don't exist. Clearing the cache makes OJB // go back to the database for everything to make sure valid data is returned. getPersistenceBrokerTemplate().clearCache(); } /** * Delete all the groups in the list. This will delete the entries. The OriginEntryGroupDao has a method to delete the groups, * and one has to use both to really delete the whole group * * @param groups a Collection of Origin Entry Groups to delete entries in * @see org.kuali.kfs.gl.dataaccess.OriginEntryDao#deleteGroups(java.util.Collection) */ public void deleteGroups(Collection<OriginEntryGroup> groups) { LOG.debug("deleteGroups() started"); if (groups == null || groups.size() <= 0) { return; } List ids = new ArrayList(); for (Iterator iter = groups.iterator(); iter.hasNext();) { OriginEntryGroup element = (OriginEntryGroup) iter.next(); ids.add(element.getId()); } Criteria criteria = new Criteria(); criteria.addIn(ENTRY_GROUP_ID, ids); QueryByCriteria qbc = QueryFactory.newQuery(entryClass, criteria); getPersistenceBrokerTemplate().deleteByQuery(qbc); // This is required because deleteByQuery leaves the cache alone so future queries // could return origin entries that don't exist. Clearing the cache makes OJB // go back to the database for everything to make sure valid data is returned. getPersistenceBrokerTemplate().clearCache(); } /** * Collection of entries that match criteria * * @param searchCriteria Map of field, value pairs * @return collection of entries * @see org.kuali.kfs.gl.dataaccess.OriginEntryDao#getMatchingEntriesByCollection(java.util.Map) */ public Collection<OriginEntryFull> getMatchingEntriesByCollection(Map searchCriteria) { LOG.debug("getMatchingEntries() started"); Criteria criteria = new Criteria(); for (Iterator iter = searchCriteria.keySet().iterator(); iter.hasNext();) { String element = (String) iter.next(); criteria.addEqualTo(element, searchCriteria.get(element)); } QueryByCriteria qbc = QueryFactory.newQuery(entryClass, criteria); qbc.addOrderByAscending(ENTRY_GROUP_ID); return getPersistenceBrokerTemplate().getCollectionByQuery(qbc); } /** * get the summarized information of the entries that belong to the entry groups with the given group ids * * @param groupIdList the ids of origin entry groups * @return a set of summarized information of the entries within the specified groups * @see org.kuali.kfs.gl.dataaccess.OriginEntryDao#getSummaryByGroupId(java.util.List) */ public Iterator getSummaryByGroupId(Collection groupIdList) { LOG.debug("getSummaryByGroupId() started"); if (groupIdList == null || groupIdList.size() <= 0) { return null; } Collection ids = new ArrayList(); for (Iterator iter = groupIdList.iterator(); iter.hasNext();) { OriginEntryGroup element = (OriginEntryGroup) iter.next(); ids.add(element.getId()); } Criteria criteria = new Criteria(); criteria.addIn(KFSPropertyConstants.ENTRY_GROUP_ID, ids); ReportQueryByCriteria query = QueryFactory.newReportQuery(entryClass, criteria); String attributeList[] = { KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, KFSPropertyConstants.UNIVERSITY_FISCAL_PERIOD_CODE, KFSPropertyConstants.FINANCIAL_BALANCE_TYPE_CODE, KFSPropertyConstants.FINANCIAL_SYSTEM_ORIGINATION_CODE, KFSPropertyConstants.TRANSACTION_DEBIT_CREDIT_CODE, "sum(" + KFSPropertyConstants.TRANSACTION_LEDGER_ENTRY_AMOUNT + ")", "count(*)" }; String groupList[] = { KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, KFSPropertyConstants.UNIVERSITY_FISCAL_PERIOD_CODE, KFSPropertyConstants.FINANCIAL_BALANCE_TYPE_CODE, KFSPropertyConstants.FINANCIAL_SYSTEM_ORIGINATION_CODE, KFSPropertyConstants.TRANSACTION_DEBIT_CREDIT_CODE }; query.setAttributes(attributeList); query.addGroupBy(groupList); // add the sorting criteria for (int i = 0; i < groupList.length; i++) { query.addOrderByAscending(groupList[i]); } return getPersistenceBrokerTemplate().getReportQueryIteratorByQuery(query); } /** * Fetches an entry for the given entryId, or returns a newly created on * * @param entryId an entry id to find an entry for * @return the entry for the given entry id, or a newly created entry * @see org.kuali.kfs.gl.dataaccess.OriginEntryDao#getExactMatchingEntry(java.lang.Integer) */ public OriginEntryFull getExactMatchingEntry(Integer entryId) { LOG.debug("getMatchingEntries() started"); OriginEntryFull oe = new OriginEntryFull(); // in case of no matching entry try { oe = (OriginEntryFull) getPersistenceBrokerTemplate().getObjectById(entryClass, entryId); } catch (Exception e) { } return oe; } /** * get the summarized information of poster input entries that belong to the entry groups with the given group id list * * @param groups the origin entry groups * @return a set of summarized information of poster input entries within the specified groups * @see org.kuali.kfs.gl.dataaccess.OriginEntryDao#getPosterOutputSummaryByGroupId(java.util.Collection) */ public Iterator getPosterOutputSummaryByGroupId(Collection groups) { LOG.debug("getPosterInputSummaryByGroupId() started"); if (groups == null || groups.size() <= 0) { return null; } Collection ids = new ArrayList(); for (Iterator iter = groups.iterator(); iter.hasNext();) { OriginEntryGroup element = (OriginEntryGroup) iter.next(); ids.add(element.getId()); } Criteria criteria = new Criteria(); criteria.addIn(KFSPropertyConstants.ENTRY_GROUP_ID, ids); String fundGroupCode = KFSPropertyConstants.ACCOUNT + "." + KFSPropertyConstants.SUB_FUND_GROUP + "." + KFSPropertyConstants.FUND_GROUP_CODE; ReportQueryByCriteria query = QueryFactory.newReportQuery(entryClass, criteria); String attributeList[] = { KFSPropertyConstants.FINANCIAL_BALANCE_TYPE_CODE, KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, KFSPropertyConstants.UNIVERSITY_FISCAL_PERIOD_CODE, fundGroupCode, KFSPropertyConstants.FINANCIAL_OBJECT_TYPE_CODE, KFSPropertyConstants.TRANSACTION_DEBIT_CREDIT_CODE, "sum(" + KFSPropertyConstants.TRANSACTION_LEDGER_ENTRY_AMOUNT + ")" }; String groupList[] = { KFSPropertyConstants.FINANCIAL_BALANCE_TYPE_CODE, KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, KFSPropertyConstants.UNIVERSITY_FISCAL_PERIOD_CODE, fundGroupCode, KFSPropertyConstants.FINANCIAL_OBJECT_TYPE_CODE, KFSPropertyConstants.TRANSACTION_DEBIT_CREDIT_CODE }; query.setAttributes(attributeList); query.addGroupBy(groupList); // add the sorting criteria for (int i = 0; i < groupList.length; i++) { query.addOrderByAscending(groupList[i]); } return getPersistenceBrokerTemplate().getReportQueryIteratorByQuery(query); } }