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.bc.batch.dataaccess.impl;
017    
018    import java.lang.reflect.InvocationTargetException;
019    import java.sql.Date;
020    import java.util.HashMap;
021    import java.util.HashSet;
022    import java.util.Iterator;
023    
024    import org.apache.commons.beanutils.PropertyUtils;
025    import org.apache.log4j.Logger;
026    import org.apache.ojb.broker.query.Criteria;
027    import org.apache.ojb.broker.query.QueryByCriteria;
028    import org.apache.ojb.broker.query.ReportQueryByCriteria;
029    import org.kuali.kfs.coa.businessobject.Account;
030    import org.kuali.kfs.coa.businessobject.AccountingPeriod;
031    import org.kuali.kfs.coa.businessobject.SubFundGroup;
032    import org.kuali.kfs.module.bc.BCConstants;
033    import org.kuali.kfs.module.bc.batch.dataaccess.GeneralLedgerBudgetLoadDao;
034    import org.kuali.kfs.module.bc.businessobject.BudgetConstructionHeader;
035    import org.kuali.kfs.module.bc.businessobject.BudgetConstructionMonthly;
036    import org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionGeneralLedger;
037    import org.kuali.kfs.sys.KFSConstants;
038    import org.kuali.kfs.sys.KFSPropertyConstants;
039    import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
040    import org.kuali.kfs.sys.service.HomeOriginationService;
041    import org.kuali.rice.kns.service.DateTimeService;
042    import org.kuali.rice.kns.util.KualiInteger;
043    
044    public class GeneralLedgerBudgetLoadDaoOjb extends BudgetConstructionBatchHelperDaoOjb implements GeneralLedgerBudgetLoadDao {
045    
046        /* turn on the logger for the persistence broker */
047        private static Logger LOG = org.apache.log4j.Logger.getLogger(GeneralLedgerBudgetLoadDaoOjb.class);
048    
049        private DateTimeService dateTimeService;
050        private HomeOriginationService homeOriginationService;
051    
052        /*
053         * see GeneralLedgerBudgetLoadDao.LoadGeneralLedgerFromBudget
054         */
055        public void loadGeneralLedgerFromBudget(Integer fiscalYear) {
056            /**
057             * this method calls a series of steps that load the general ledger from the budget into the general ledger pending entry
058             * table. this method takes a fiscal year as input, but all that is required is that this object be a key labeling the
059             * bduget construction general ledger rows for the budget period to be loaded. it need not be an actual fiscal year.
060             */
061            //
062            // set up the global variables
063            // this is a single object that can be passed to all methods that need it, to make the code "thread safe"
064            // (1) the fiscal year to load
065            // (2) the initial sequence numbers for each document to be loaded
066            // (3) the run date (which will be the transaction date)
067            // (4) the "origination code", which comes from the database
068            DaoGlobalVariables daoGlobalVariables = new DaoGlobalVariables(fiscalYear);
069            /**
070             * initiliaze the counter variables
071             */
072            DiagnosticCounters diagnosticCounters = new DiagnosticCounters();
073            /**
074             * make sure all the accounting periods for the load year are open, so the entry lines we create can be posted
075             */
076            openAllAccountingPeriods(fiscalYear);
077            /**
078             * process pending budget construction general ledger rows
079             */
080            loadPendingBudgetConstructionGeneralLedger(daoGlobalVariables, diagnosticCounters);
081            /**
082             * process budget construction monthly budget rows
083             */
084            loadBudgetConstructionMonthlyBudget(daoGlobalVariables, diagnosticCounters);
085            // write out the counts for verification
086            diagnosticCounters.writeDiagnosticCounters();
087        }
088    
089        /*******************************************************************************************************************************
090         * methods to do the actual load *
091         ******************************************************************************************************************************/
092    
093        /**
094         * build a hashmap containing the next entry sequence number to use for each document (document number) to be loaded from budget
095         * construction to the general ledger
096         * 
097         * @param target fiscal year for the budget load
098         * @return HashMapap keyed on document number containing the next entry sequence number to use for the key
099         */
100    
101        protected HashMap<String, Integer> entrySequenceNumber(Integer requestYear) {
102            HashMap<String, Integer> nextEntrySequenceNumber;
103            // set up the query: each distinct document number in the source load table
104            Criteria criteriaID = new Criteria();
105            criteriaID.addEqualTo(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, requestYear);
106            ReportQueryByCriteria queryID = new ReportQueryByCriteria(BudgetConstructionHeader.class, criteriaID);
107            queryID.setAttributes(new String[] { KFSPropertyConstants.DOCUMENT_NUMBER });
108    
109            nextEntrySequenceNumber = new HashMap<String, Integer>(hashCapacity(queryID));
110    
111            // OK. build the hash map
112            // there are NO entries for these documents yet, so we initialize the entry sequence number to 0
113            Iterator documentNumbersToLoad = getPersistenceBrokerTemplate().getReportQueryIteratorByQuery(queryID);
114            while (documentNumbersToLoad.hasNext()) {
115                Object[] resultRow = (Object[]) documentNumbersToLoad.next();
116                nextEntrySequenceNumber.put((String) resultRow[0], new Integer(0));
117            }
118    
119            return nextEntrySequenceNumber;
120        }
121    
122        /**
123         * This method creates a new generalLedgerPendingEntry object and initializes it with the default settings for the budget
124         * construction general ledger load.
125         * 
126         * @param requestYear
127         * @param financialSystemOriginationCode
128         * @return intiliazed GeneralLedgerPendingEntry business object
129         */
130    
131        protected GeneralLedgerPendingEntry getNewPendingEntryWithDefaults(DaoGlobalVariables daoGlobalVariables) {
132            GeneralLedgerPendingEntry newRow = new GeneralLedgerPendingEntry();
133            newRow.setUniversityFiscalYear(daoGlobalVariables.getRequestYear());
134            newRow.setTransactionLedgerEntryDescription(BCConstants.BC_TRN_LDGR_ENTR_DESC);
135            newRow.setFinancialDocumentTypeCode(BCConstants.BUDGET_CONSTRUCTION_BEGINNING_BALANCE_DOCUMENT_TYPE);
136            newRow.setFinancialDocumentApprovedCode(KFSConstants.PENDING_ENTRY_APPROVED_STATUS_CODE.APPROVED);
137            newRow.setTransactionDate(daoGlobalVariables.getTransactionDate());
138            newRow.setTransactionEntryOffsetIndicator(false);
139            newRow.setFinancialSystemOriginationCode(daoGlobalVariables.getFinancialSystemOriginationcode());
140            // the fields below are set to null
141            newRow.setOrganizationDocumentNumber(null);
142            newRow.setProjectCode(null);
143            newRow.setOrganizationReferenceId(null);
144            newRow.setReferenceFinancialDocumentTypeCode(null);
145            newRow.setReferenceOriginationCode(null);
146            newRow.setReferenceFinancialDocumentNumber(null);
147            newRow.setFinancialDocumentReversalDate(null);
148            newRow.setTransactionEncumbranceUpdateCode(null);
149            newRow.setAcctSufficientFundsFinObjCd(null);
150            newRow.setTransactionDebitCreditCode(null);
151            newRow.setTransactionEntryProcessedTs(null);
152            return newRow;
153        }
154    
155        protected void loadBudgetConstructionMonthlyBudget(DaoGlobalVariables daoGlobalVariables, DiagnosticCounters diagnosticCounters) {
156            QueryByCriteria queryID = queryForBudgetConstructionMonthly(daoGlobalVariables.getRequestYear());
157            Iterator<BudgetConstructionMonthly> monthlyBudgetRows = getPersistenceBrokerTemplate().getIteratorByQuery(queryID);
158            while (monthlyBudgetRows.hasNext()) {
159                BudgetConstructionMonthly monthlyBudgetIn = monthlyBudgetRows.next();
160                diagnosticCounters.increaseBudgetConstructionMonthlyBudgetRead();
161                if (daoGlobalVariables.shouldThisAccountLoad(monthlyBudgetIn.getAccountNumber() + monthlyBudgetIn.getChartOfAccountsCode())) {
162                    GeneralLedgerPendingEntry newRow = getNewPendingEntryWithDefaults(daoGlobalVariables);
163                    writeGeneralLedgerPendingEntryFromMonthly(newRow, monthlyBudgetIn, daoGlobalVariables, diagnosticCounters);
164                }
165                else {
166                    diagnosticCounters.increaseBudgetConstructionMonthlyBudgetSkipped();
167                }
168            }
169        }
170    
171        /**
172         * This method loads all the eligible pending budget construction general ledger rows
173         * 
174         * @param daoGlobalVariables
175         * @param diagnosticCounters
176         */
177        protected void loadPendingBudgetConstructionGeneralLedger(DaoGlobalVariables daoGlobalVariables, DiagnosticCounters diagnosticCounters) {
178            QueryByCriteria queryID = queryForPendingBudgetConstructionGeneralLedger(daoGlobalVariables.getRequestYear());
179            Iterator<PendingBudgetConstructionGeneralLedger> pbglRows = getPersistenceBrokerTemplate().getIteratorByQuery(queryID);
180            while (pbglRows.hasNext()) {
181                PendingBudgetConstructionGeneralLedger pbglIn = pbglRows.next();
182                diagnosticCounters.increaseBudgetConstructionPendingGeneralLedgerRead();
183                if (daoGlobalVariables.shouldThisAccountLoad(pbglIn.getAccountNumber() + pbglIn.getChartOfAccountsCode())) {
184                    GeneralLedgerPendingEntry newRow = getNewPendingEntryWithDefaults(daoGlobalVariables);
185                    writeGeneralLedgerPendingEntryFromAnnual(newRow, pbglIn, daoGlobalVariables, diagnosticCounters);
186                }
187                else {
188                    diagnosticCounters.increaseBudgetConstructionPendingGeneralLedgerSkipped();
189                }
190            }
191        }
192    
193        /**
194         * This method builds the query to fetch the monthly budget general ledger lines to be loaded
195         * 
196         * @param fiscalYear : the year to be loaded
197         * @return query for fetching monthly budget rows
198         */
199        protected QueryByCriteria queryForBudgetConstructionMonthly(Integer fiscalYear) {
200            // we only select rows which have non-zero budget amounts
201            // on this object, proxy=true, so we can do a regular query for a business object instead of a report query
202            Criteria criteriaID = new Criteria();
203            criteriaID.addEqualTo(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, fiscalYear);
204            // we want to test for at least one non-zero monthly amount
205            Criteria orCriteriaID = new Criteria();
206            Iterator<String[]> monthlyPeriods = BCConstants.BC_MONTHLY_AMOUNTS.iterator();
207            while (monthlyPeriods.hasNext()) {
208                // the first array element is the amount field name (the second is the corresponding accounting period)
209                String monthlyAmountName = monthlyPeriods.next()[0];
210                Criteria amountCriteria = new Criteria();
211                amountCriteria.addNotEqualTo(monthlyAmountName, new KualiInteger(0));
212                orCriteriaID.addOrCriteria(amountCriteria);
213            }
214            criteriaID.addAndCriteria(orCriteriaID);
215            QueryByCriteria queryID = new QueryByCriteria(BudgetConstructionMonthly.class, criteriaID);
216            return queryID;
217        }
218    
219        /**
220         * This method builds the query to fetch the pending budget construction general ledger rows to be loaded
221         * 
222         * @param fiscalYear: the year to be loaded
223         * @return query for fetching pending budget construction GL rows
224         */
225    
226        protected QueryByCriteria queryForPendingBudgetConstructionGeneralLedger(Integer fiscalYear) {
227            // we only select rows which have non-zero budget amounts
228            // on this object, proxy=true, so we can do a regular query for a business object instead of a report query
229            Criteria criteriaID = new Criteria();
230            criteriaID.addEqualTo(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, fiscalYear);
231            criteriaID.addNotEqualTo(KFSPropertyConstants.ACCOUNT_LINE_ANNUAL_BALANCE_AMOUNT, new KualiInteger(0));
232            QueryByCriteria queryID = new QueryByCriteria(PendingBudgetConstructionGeneralLedger.class, criteriaID);
233            return queryID;
234        }
235    
236        /**
237         * complete the pending entry row based on the data returned from the DB store it to the DB
238         * 
239         * @param newRow
240         * @param source annual budget construction GL row
241         * @param object containing global constants
242         */
243        protected void writeGeneralLedgerPendingEntryFromAnnual(GeneralLedgerPendingEntry newRow, PendingBudgetConstructionGeneralLedger pbgl, DaoGlobalVariables daoGlobalVariables, DiagnosticCounters diagnosticCounters) {
244            /**
245             * first get the document number
246             */
247            String incomingDocumentNumber = pbgl.getDocumentNumber();
248            /**
249             * write a base budget row
250             */
251            newRow.setFinancialBalanceTypeCode(KFSConstants.BALANCE_TYPE_BASE_BUDGET);
252            newRow.setUniversityFiscalPeriodCode(KFSConstants.PERIOD_CODE_BEGINNING_BALANCE);
253            /**
254             * set the variable fields
255             */
256            newRow.setTransactionLedgerEntrySequenceNumber(daoGlobalVariables.getNextSequenceNumber(incomingDocumentNumber));
257            newRow.setDocumentNumber(incomingDocumentNumber); // document number
258            newRow.setChartOfAccountsCode(pbgl.getChartOfAccountsCode()); // chart of accounts
259            newRow.setAccountNumber(pbgl.getAccountNumber()); // account number
260            newRow.setSubAccountNumber(pbgl.getSubAccountNumber()); // sub account number
261            newRow.setFinancialObjectCode(pbgl.getFinancialObjectCode()); // object code
262            newRow.setFinancialSubObjectCode(pbgl.getFinancialSubObjectCode()); // sub object code
263            newRow.setFinancialObjectTypeCode(pbgl.getFinancialObjectTypeCode()); // object type code
264            /**
265             * the budget works with whole numbers--we must convert to decimal for the general ledger
266             */
267            newRow.setTransactionLedgerEntryAmount(pbgl.getAccountLineAnnualBalanceAmount().kualiDecimalValue());
268            /**
269             * now we store the base budget value
270             */
271            getPersistenceBrokerTemplate().store(newRow);
272            diagnosticCounters.increaseGeneralLedgerBaseBudgetWritten();
273            /**
274             * the same row needs to be written as a current budget item we change only the balance type and the sequence number
275             */
276            newRow.setFinancialBalanceTypeCode(KFSConstants.BALANCE_TYPE_CURRENT_BUDGET);
277            newRow.setTransactionLedgerEntrySequenceNumber(daoGlobalVariables.getNextSequenceNumber(incomingDocumentNumber));
278            /**
279             * store the current budget value
280             */
281            getPersistenceBrokerTemplate().store(newRow);
282            diagnosticCounters.increasGenneralLedgerCurrentBudgetWritten();
283        }
284    
285        protected void writeGeneralLedgerPendingEntryFromMonthly(GeneralLedgerPendingEntry newRow, BudgetConstructionMonthly pbglMonthly, DaoGlobalVariables daoGlobalVariables, DiagnosticCounters diagnosticCounters) {
286            /**
287             * first get the document number
288             */
289            String incomingDocumentNumber = pbglMonthly.getDocumentNumber();
290            /**
291             * write a base budget row
292             */
293            newRow.setFinancialBalanceTypeCode(KFSConstants.BALANCE_TYPE_MONTHLY_BUDGET);
294            /**
295             * set the variable fields
296             */
297            newRow.setDocumentNumber(incomingDocumentNumber); // document number
298            newRow.setChartOfAccountsCode(pbglMonthly.getChartOfAccountsCode()); // chart of accounts
299            newRow.setAccountNumber(pbglMonthly.getAccountNumber()); // account number
300            newRow.setSubAccountNumber(pbglMonthly.getSubAccountNumber()); // sub account number
301            newRow.setFinancialObjectCode(pbglMonthly.getFinancialObjectCode()); // object code
302            newRow.setFinancialSubObjectCode(pbglMonthly.getFinancialSubObjectCode()); // sub object code
303            newRow.setFinancialObjectTypeCode(pbglMonthly.getFinancialObjectTypeCode()); // object type code
304    
305            /**
306             * we have to loop through the monthly array, and write an MB row for each monthly row with a non-zero amount (we do this to
307             * write less code. we hope that the extra hit from reflection won't be too bad)
308             */
309            Iterator<String[]> monthlyPeriodAmounts = BCConstants.BC_MONTHLY_AMOUNTS.iterator();
310            while (monthlyPeriodAmounts.hasNext()) {
311                String[] monthlyPeriodProperties = monthlyPeriodAmounts.next();
312                KualiInteger monthlyAmount;
313                try {
314                    monthlyAmount = (KualiInteger) PropertyUtils.getSimpleProperty(pbglMonthly, monthlyPeriodProperties[0]);
315                }
316                catch (IllegalAccessException ex) {
317                    LOG.error(String.format("\nunable to use get method to access value of %s in %s\n", monthlyPeriodProperties[0], BudgetConstructionMonthly.class.getName()), ex);
318                    diagnosticCounters.writeDiagnosticCounters();
319                    throw new RuntimeException(ex);
320                }
321                catch (InvocationTargetException ex) {
322                    LOG.error(String.format("\nunable to invoke get method for %s in %s\n", monthlyPeriodProperties[0], BudgetConstructionMonthly.class.getName()), ex);
323                    diagnosticCounters.writeDiagnosticCounters();
324                    throw new RuntimeException(ex);
325                }
326                catch (NoSuchMethodException ex) {
327                    LOG.error(String.format("\nNO get method found for %s in %s ???\n", monthlyPeriodProperties[0], BudgetConstructionMonthly.class.getName()), ex);
328                    diagnosticCounters.writeDiagnosticCounters();
329                    throw new RuntimeException(ex);
330                }
331                if (!(monthlyAmount.isZero())) {
332                    newRow.setTransactionLedgerEntrySequenceNumber(daoGlobalVariables.getNextSequenceNumber(incomingDocumentNumber));
333                    newRow.setUniversityFiscalPeriodCode(monthlyPeriodProperties[1]); // accounting period
334                    newRow.setTransactionLedgerEntryAmount(monthlyAmount.kualiDecimalValue()); // amount
335                    getPersistenceBrokerTemplate().store(newRow);
336                    diagnosticCounters.increaseBudgetConstructionMonthlyBudgetWritten();
337                }
338            }
339        }
340    
341    
342        /*******************************************************************************************************************************
343         * * This section build the list of accounts that SHOULD NOT be loaded to the general ledger * (This may seem strange--why build
344         * a budget if you aren't going to load it--but in the FIS the budget * loaded to payroll as well. For grant accounts, the FIS
345         * allowed people to set salaries for the new year * so those would load to payroll. But, the actual budget for a grant account
346         * was not necessarily done on a * fiscal year basis, and was not part of the university's operating budget, so there was no
347         * "base budget" * for a grant account to load to the general ledger.) * (1) We will inhibit the load to the general ledger of
348         * all accounts in given sub fund groups * (2) (We WILL allow closed accounts to load. There should not be any--they should have
349         * been filtered * out in the budget application, but if there are, they will be caught by the GL scrubber. We want * people to
350         * have a record of this kind of load failure, so it can be corrected. * * *
351         ******************************************************************************************************************************/
352    
353        /**
354         * get a list of accounts that should not be loaded from the budget to the General Ledger
355         * 
356         * @return hashset of accounts NOT to be loaded
357         */
358    
359        protected HashSet<String> getAccountsNotToBeLoaded() {
360            HashSet<String> bannedAccounts;
361            /**
362             * list of subfunds which should not be loaded
363             */
364            HashSet<String> bannedSubFunds = getSubFundsNotToBeLoaded();
365            /**
366             * query for the subfund property for each account in the DB
367             */
368            ReportQueryByCriteria queryID = new ReportQueryByCriteria(Account.class, org.apache.ojb.broker.query.ReportQueryByCriteria.CRITERIA_SELECT_ALL);
369            queryID.setAttributes(new String[] { KFSPropertyConstants.ACCOUNT_NUMBER, KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, KFSPropertyConstants.SUB_FUND_GROUP_CODE });
370            bannedAccounts = new HashSet<String>(hashCapacity(queryID));
371            /**
372             * use the results to build a hash set of accounts which should NOT be loaded (that is, their subfunds are in the list of
373             * subfunds we do not want
374             */
375            Iterator accountProperties = getPersistenceBrokerTemplate().getReportQueryIteratorByQuery(queryID);
376            while (accountProperties.hasNext()) {
377                Object[] selectListValues = (Object[]) accountProperties.next();
378                /**
379                 * we will add an account/chart to the list if it has a no-load subfundgroup
380                 */
381                if (bannedSubFunds.contains((String) selectListValues[2])) {
382                    /**
383                     * hash content is account number concatenated with chart (the key of the chart of accounts table)
384                     */
385                    bannedAccounts.add(((String) selectListValues[0]) + ((String) selectListValues[1]));
386                }
387            }
388            return bannedAccounts;
389        }
390    
391        /**
392         * build a hash set of subfunds whose accounts should NOT be loaded this can be done by either a list of FUND groups and/or a
393         * list of subfund groups
394         * 
395         * @see org.kuali.kfs.module.bc.BCConstants to initialize the String[] array(s) as desired
396         * @return list of subfunds whose accounts will NOT be loaded
397         */
398        protected HashSet<String> getSubFundsNotToBeLoaded() {
399            HashSet<String> bannedSubFunds;
400            if (BCConstants.NO_BC_GL_LOAD_FUND_GROUPS.size() != 0) {
401                /**
402                 * look for subfunds in the banned fund groups
403                 */
404                Criteria criteriaID = new Criteria();
405                criteriaID.addIn(KFSPropertyConstants.FUND_GROUP_CODE, BCConstants.NO_BC_GL_LOAD_FUND_GROUPS);
406                ReportQueryByCriteria queryID = new ReportQueryByCriteria(SubFundGroup.class, criteriaID);
407                queryID.setAttributes(new String[] { KFSPropertyConstants.SUB_FUND_GROUP_CODE });
408                /**
409                 * set the size of the hashset based on the number of rows the query will return
410                 */
411                bannedSubFunds = new HashSet<String>(hashCapacity(queryID) + BCConstants.NO_BC_GL_LOAD_SUBFUND_GROUPS.size());
412                Iterator subfundsForBannedFunds = getPersistenceBrokerTemplate().getReportQueryIteratorByQuery(queryID);
413                /**
414                 * add the subfunds for the fund groups to be skipped to the hash set
415                 */
416                while (subfundsForBannedFunds.hasNext()) {
417                    bannedSubFunds.add((String) ((Object[]) subfundsForBannedFunds.next())[0]);
418                }
419            }
420            else {
421                bannedSubFunds = new HashSet<String>(BCConstants.NO_BC_GL_LOAD_SUBFUND_GROUPS.size() + 1);
422            }
423            /**
424             * now add the specific sub funds we don't want from the hard-coded array in BCConstants to the hash set
425             */
426            Iterator<String> additionalBannedSubFunds = BCConstants.NO_BC_GL_LOAD_SUBFUND_GROUPS.iterator();
427            while (additionalBannedSubFunds.hasNext()) {
428                bannedSubFunds.add(additionalBannedSubFunds.next());
429            }
430            return bannedSubFunds;
431        }
432    
433        /*******************************************************************************************************************************
434         * This section sets all the accounting periods for the coming year to open. * The monthly budget will load by accounting
435         * period. If some are not open, some monthly rows will error * out in the scrubber. Current FIS procedure is to prevent this
436         * from happening, by opening all the * accounting periods and letting the university chart manager close them after the budget
437         * is loaded if that * is desirable for some reason. If an institution prefers another policy, just don't call these methods. *
438         * But, even if we let the scrubber fail, there will be no way to load the monthly rows from the error tables* unless the
439         * corresponding accounting periods are open. *
440         ******************************************************************************************************************************/
441    
442        /**
443         * this method makes sure all accounting periods inn the target fiscal year are open
444         * 
445         * @param request fiscal year (or other fiscal period) which is the TARGET of the load
446         */
447        protected void openAllAccountingPeriods(Integer requestYear) {
448            Criteria criteriaID = new Criteria();
449            criteriaID.addEqualTo(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, requestYear);
450            criteriaID.addNotEqualTo(KFSPropertyConstants.UNIVERSITY_FISCAL_PERIOD_STATUS_CODE, "Y");
451            QueryByCriteria queryID = new QueryByCriteria(AccountingPeriod.class, criteriaID);
452            Iterator<AccountingPeriod> unopenPeriods = getPersistenceBrokerTemplate().getIteratorByQuery(queryID);
453            int periodsOpened = 0;
454            while (unopenPeriods.hasNext()) {
455                AccountingPeriod periodToOpen = unopenPeriods.next();
456                periodToOpen.setActive(true);
457                getPersistenceBrokerTemplate().store(periodToOpen);
458                periodsOpened = periodsOpened + 1;
459            }
460            LOG.warn(String.format("\n\naccounting periods for %d changed to open status: %d", requestYear, new Integer(periodsOpened)));
461        }
462    
463        /*******************************************************************************************************************************
464         * These two classes are containers so we can make certain variables accessible to all methods without making them global to the *
465         * outer class and without cluttering up the method signatures. *
466         ******************************************************************************************************************************/
467    
468        /**
469         * This class keeps a set of counters and provides a method to print them out This allows us to set up thread-local counters in
470         * the unlikely event this code is run by more than one thread
471         */
472        protected class DiagnosticCounters {
473            long budgetConstructionPendingGeneralLedgerRead = 0;
474            long budgetConstructionPendingGeneralLedgerSkipped = 0;
475            long generalLedgerBaseBudgetWritten = 0;
476            long generalLedgerCurrentBudgetWritten = 0;
477    
478            long budgetConstructionMonthlyBudgetRead = 0;
479            long budgetConstructionMonthlyBudgetSkipped = 0;
480            long budgetConstructionMonthlyBudgetWritten = 0;
481    
482            public void increaseBudgetConstructionPendingGeneralLedgerRead() {
483                budgetConstructionPendingGeneralLedgerRead++;
484            }
485    
486            public void increaseBudgetConstructionPendingGeneralLedgerSkipped() {
487                budgetConstructionPendingGeneralLedgerSkipped++;
488            }
489    
490            public void increaseGeneralLedgerBaseBudgetWritten() {
491                generalLedgerBaseBudgetWritten++;
492            }
493    
494            public void increasGenneralLedgerCurrentBudgetWritten() {
495                generalLedgerCurrentBudgetWritten++;
496            }
497    
498            public void increaseBudgetConstructionMonthlyBudgetRead() {
499                budgetConstructionMonthlyBudgetRead++;
500            }
501    
502            public void increaseBudgetConstructionMonthlyBudgetSkipped() {
503                budgetConstructionMonthlyBudgetSkipped++;
504            }
505    
506            public void increaseBudgetConstructionMonthlyBudgetWritten() {
507                budgetConstructionMonthlyBudgetWritten++;
508            }
509    
510            public void writeDiagnosticCounters() {
511                LOG.warn(String.format("\n\nPending Budget Construction General Ledger Load\n"));
512                LOG.warn(String.format("\n  pending budget construction GL rows read:        %,d", budgetConstructionPendingGeneralLedgerRead));
513                LOG.warn(String.format("\n  pending budget construction GL rows skipped:     %,d", budgetConstructionPendingGeneralLedgerSkipped));
514                LOG.warn(String.format("\n\n  base budget rows written:                        %,d", generalLedgerBaseBudgetWritten));
515                LOG.warn(String.format("\n  current budget rows written:                     %,d", generalLedgerCurrentBudgetWritten));
516                LOG.warn(String.format("\n\n  pending budget construction monthly rows read:   %,d", budgetConstructionMonthlyBudgetRead));
517                LOG.warn(String.format("\n  pending budget construction monthly rows skipped: %,d", budgetConstructionMonthlyBudgetSkipped));
518                LOG.warn(String.format("\n  pending budget construction monthly rows written: %,d", budgetConstructionMonthlyBudgetWritten));
519            }
520        }
521    
522        /**
523         * This class allows us to create global variables and pass them around. This should make the code thread safe, in the unlikely
524         * event it is called by more than one thread. it also allows us to fetch constants and build datas stuctures from the DB once
525         * upon instantiation of this class, and make them available for the duration of the run
526         * 
527         * @param requestYear
528         * @param <documentNumber, ledger sequence number> HashMap
529         * @param current SQL Date (which will be the transaction date in the general ledger entry rows we create)
530         * @param the "financial system Origination Code" for this database
531         */
532        protected class DaoGlobalVariables {
533            private Integer requestYear;
534            private HashMap<String, Integer> entrySequenceNumber;
535            private Date transactionDate;
536            private String financialSystemOriginationCode;
537            private HashSet<String> accountsNotToBeLoaded;
538    
539            public DaoGlobalVariables(Integer requestYear) {
540                this.requestYear = requestYear;
541                this.entrySequenceNumber = entrySequenceNumber(requestYear);
542                // this.transactionDate = SpringContext.getBean(DateTimeService.class).getCurrentSqlDate();
543                this.transactionDate = dateTimeService.getCurrentSqlDate();
544                this.financialSystemOriginationCode = homeOriginationService.getHomeOrigination().getFinSystemHomeOriginationCode();
545                this.accountsNotToBeLoaded = getAccountsNotToBeLoaded();
546            }
547    
548            public Integer getRequestYear() {
549                return this.requestYear;
550            }
551    
552            /**
553             * return the next available sequence number for the input key and update "next available"
554             */
555            public Integer getNextSequenceNumber(String seqKey) {
556                Integer newSeqNumber = entrySequenceNumber.get(seqKey);
557                entrySequenceNumber.put(seqKey, new Integer(newSeqNumber.intValue() + 1));
558                return newSeqNumber;
559            }
560    
561            public Date getTransactionDate() {
562                return this.transactionDate;
563            }
564    
565            public String getFinancialSystemOriginationcode() {
566                return this.financialSystemOriginationCode;
567            }
568    
569            public boolean shouldThisAccountLoad(String accountAndChart) {
570                return (!accountsNotToBeLoaded.contains(accountAndChart));
571            }
572        }
573    
574        public void setDateTimeService(DateTimeService dateTimeService) {
575            this.dateTimeService = dateTimeService;
576        }
577    
578        public void setHomeOriginationService(HomeOriginationService homeOriginationService) {
579            this.homeOriginationService = homeOriginationService;
580        }
581    
582    }