/* * 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.module.bc.batch.dataaccess.impl; import java.lang.reflect.InvocationTargetException; import java.sql.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import org.apache.commons.beanutils.PropertyUtils; import org.apache.log4j.Logger; import org.apache.ojb.broker.query.Criteria; import org.apache.ojb.broker.query.QueryByCriteria; import org.apache.ojb.broker.query.ReportQueryByCriteria; import org.kuali.kfs.coa.businessobject.Account; import org.kuali.kfs.coa.businessobject.AccountingPeriod; import org.kuali.kfs.coa.businessobject.SubFundGroup; import org.kuali.kfs.module.bc.BCConstants; import org.kuali.kfs.module.bc.batch.dataaccess.GeneralLedgerBudgetLoadDao; import org.kuali.kfs.module.bc.businessobject.BudgetConstructionHeader; import org.kuali.kfs.module.bc.businessobject.BudgetConstructionMonthly; import org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionGeneralLedger; import org.kuali.kfs.sys.KFSConstants; import org.kuali.kfs.sys.KFSPropertyConstants; import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry; import org.kuali.rice.core.api.util.type.KualiInteger; public class GeneralLedgerBudgetLoadDaoOjb extends BudgetConstructionBatchHelperDaoOjb implements GeneralLedgerBudgetLoadDao { /* turn on the logger for the persistence broker */ private static Logger LOG = org.apache.log4j.Logger.getLogger(GeneralLedgerBudgetLoadDaoOjb.class); /* * see GeneralLedgerBudgetLoadDao.LoadGeneralLedgerFromBudget */ public void loadGeneralLedgerFromBudget(Integer fiscalYear, Date currentSqlDate, String financialSystemOriginationCode) { /** * this method calls a series of steps that load the general ledger from the budget into the general ledger pending entry * table. this method takes a fiscal year as input, but all that is required is that this object be a key labeling the * bduget construction general ledger rows for the budget period to be loaded. it need not be an actual fiscal year. */ // // set up the global variables // this is a single object that can be passed to all methods that need it, to make the code "thread safe" // (1) the fiscal year to load // (2) the initial sequence numbers for each document to be loaded // (3) the run date (which will be the transaction date) // (4) the "origination code", which comes from the database DaoGlobalVariables daoGlobalVariables = new DaoGlobalVariables(fiscalYear, currentSqlDate, financialSystemOriginationCode); /** * initiliaze the counter variables */ DiagnosticCounters diagnosticCounters = new DiagnosticCounters(); /** * make sure all the accounting periods for the load year are open, so the entry lines we create can be posted */ openAllAccountingPeriods(fiscalYear); /** * process pending budget construction general ledger rows */ loadPendingBudgetConstructionGeneralLedger(daoGlobalVariables, diagnosticCounters); /** * process budget construction monthly budget rows */ loadBudgetConstructionMonthlyBudget(daoGlobalVariables, diagnosticCounters); // write out the counts for verification diagnosticCounters.writeDiagnosticCounters(); } /******************************************************************************************************************************* * methods to do the actual load * ******************************************************************************************************************************/ /** * build a hashmap containing the next entry sequence number to use for each document (document number) to be loaded from budget * construction to the general ledger * * @param target fiscal year for the budget load * @return HashMapap keyed on document number containing the next entry sequence number to use for the key */ protected HashMap<String, Integer> entrySequenceNumber(Integer requestYear) { HashMap<String, Integer> nextEntrySequenceNumber; // set up the query: each distinct document number in the source load table Criteria criteriaID = new Criteria(); criteriaID.addEqualTo(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, requestYear); ReportQueryByCriteria queryID = new ReportQueryByCriteria(BudgetConstructionHeader.class, criteriaID); queryID.setAttributes(new String[] { KFSPropertyConstants.DOCUMENT_NUMBER }); nextEntrySequenceNumber = new HashMap<String, Integer>(hashCapacity(queryID)); // OK. build the hash map // there are NO entries for these documents yet, so we initialize the entry sequence number to 0 Iterator documentNumbersToLoad = getPersistenceBrokerTemplate().getReportQueryIteratorByQuery(queryID); while (documentNumbersToLoad.hasNext()) { Object[] resultRow = (Object[]) documentNumbersToLoad.next(); nextEntrySequenceNumber.put((String) resultRow[0], new Integer(0)); } return nextEntrySequenceNumber; } /** * This method creates a new generalLedgerPendingEntry object and initializes it with the default settings for the budget * construction general ledger load. * * @param requestYear * @param financialSystemOriginationCode * @return intiliazed GeneralLedgerPendingEntry business object */ protected GeneralLedgerPendingEntry getNewPendingEntryWithDefaults(DaoGlobalVariables daoGlobalVariables) { GeneralLedgerPendingEntry newRow = new GeneralLedgerPendingEntry(); newRow.setUniversityFiscalYear(daoGlobalVariables.getRequestYear()); newRow.setTransactionLedgerEntryDescription(BCConstants.BC_TRN_LDGR_ENTR_DESC); newRow.setFinancialDocumentTypeCode(BCConstants.BUDGET_CONSTRUCTION_BEGINNING_BALANCE_DOCUMENT_TYPE); newRow.setFinancialDocumentApprovedCode(KFSConstants.PENDING_ENTRY_APPROVED_STATUS_CODE.APPROVED); newRow.setTransactionDate(daoGlobalVariables.getTransactionDate()); newRow.setTransactionEntryOffsetIndicator(false); newRow.setFinancialSystemOriginationCode(daoGlobalVariables.getFinancialSystemOriginationcode()); // the fields below are set to null newRow.setOrganizationDocumentNumber(null); newRow.setProjectCode(null); newRow.setOrganizationReferenceId(null); newRow.setReferenceFinancialDocumentTypeCode(null); newRow.setReferenceOriginationCode(null); newRow.setReferenceFinancialDocumentNumber(null); newRow.setFinancialDocumentReversalDate(null); newRow.setTransactionEncumbranceUpdateCode(null); newRow.setAcctSufficientFundsFinObjCd(null); newRow.setTransactionDebitCreditCode(null); newRow.setTransactionEntryProcessedTs(null); return newRow; } protected void loadBudgetConstructionMonthlyBudget(DaoGlobalVariables daoGlobalVariables, DiagnosticCounters diagnosticCounters) { QueryByCriteria queryID = queryForBudgetConstructionMonthly(daoGlobalVariables.getRequestYear()); Iterator<BudgetConstructionMonthly> monthlyBudgetRows = getPersistenceBrokerTemplate().getIteratorByQuery(queryID); while (monthlyBudgetRows.hasNext()) { BudgetConstructionMonthly monthlyBudgetIn = monthlyBudgetRows.next(); diagnosticCounters.increaseBudgetConstructionMonthlyBudgetRead(); if (daoGlobalVariables.shouldThisAccountLoad(monthlyBudgetIn.getAccountNumber() + monthlyBudgetIn.getChartOfAccountsCode())) { GeneralLedgerPendingEntry newRow = getNewPendingEntryWithDefaults(daoGlobalVariables); writeGeneralLedgerPendingEntryFromMonthly(newRow, monthlyBudgetIn, daoGlobalVariables, diagnosticCounters); } else { diagnosticCounters.increaseBudgetConstructionMonthlyBudgetSkipped(); } } } /** * This method loads all the eligible pending budget construction general ledger rows * * @param daoGlobalVariables * @param diagnosticCounters */ protected void loadPendingBudgetConstructionGeneralLedger(DaoGlobalVariables daoGlobalVariables, DiagnosticCounters diagnosticCounters) { QueryByCriteria queryID = queryForPendingBudgetConstructionGeneralLedger(daoGlobalVariables.getRequestYear()); Iterator<PendingBudgetConstructionGeneralLedger> pbglRows = getPersistenceBrokerTemplate().getIteratorByQuery(queryID); while (pbglRows.hasNext()) { PendingBudgetConstructionGeneralLedger pbglIn = pbglRows.next(); diagnosticCounters.increaseBudgetConstructionPendingGeneralLedgerRead(); if (daoGlobalVariables.shouldThisAccountLoad(pbglIn.getAccountNumber() + pbglIn.getChartOfAccountsCode())) { GeneralLedgerPendingEntry newRow = getNewPendingEntryWithDefaults(daoGlobalVariables); writeGeneralLedgerPendingEntryFromAnnual(newRow, pbglIn, daoGlobalVariables, diagnosticCounters); } else { diagnosticCounters.increaseBudgetConstructionPendingGeneralLedgerSkipped(); } } } /** * This method builds the query to fetch the monthly budget general ledger lines to be loaded * * @param fiscalYear : the year to be loaded * @return query for fetching monthly budget rows */ protected QueryByCriteria queryForBudgetConstructionMonthly(Integer fiscalYear) { // we only select rows which have non-zero budget amounts // on this object, proxy=true, so we can do a regular query for a business object instead of a report query Criteria criteriaID = new Criteria(); criteriaID.addEqualTo(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, fiscalYear); // we want to test for at least one non-zero monthly amount Criteria orCriteriaID = new Criteria(); Iterator<String[]> monthlyPeriods = BCConstants.BC_MONTHLY_AMOUNTS.iterator(); while (monthlyPeriods.hasNext()) { // the first array element is the amount field name (the second is the corresponding accounting period) String monthlyAmountName = monthlyPeriods.next()[0]; Criteria amountCriteria = new Criteria(); amountCriteria.addNotEqualTo(monthlyAmountName, new KualiInteger(0)); orCriteriaID.addOrCriteria(amountCriteria); } criteriaID.addAndCriteria(orCriteriaID); QueryByCriteria queryID = new QueryByCriteria(BudgetConstructionMonthly.class, criteriaID); return queryID; } /** * This method builds the query to fetch the pending budget construction general ledger rows to be loaded * * @param fiscalYear: the year to be loaded * @return query for fetching pending budget construction GL rows */ protected QueryByCriteria queryForPendingBudgetConstructionGeneralLedger(Integer fiscalYear) { // we only select rows which have non-zero budget amounts // on this object, proxy=true, so we can do a regular query for a business object instead of a report query Criteria criteriaID = new Criteria(); criteriaID.addEqualTo(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, fiscalYear); criteriaID.addNotEqualTo(KFSPropertyConstants.ACCOUNT_LINE_ANNUAL_BALANCE_AMOUNT, new KualiInteger(0)); QueryByCriteria queryID = new QueryByCriteria(PendingBudgetConstructionGeneralLedger.class, criteriaID); return queryID; } /** * complete the pending entry row based on the data returned from the DB store it to the DB * * @param newRow * @param source annual budget construction GL row * @param object containing global constants */ protected void writeGeneralLedgerPendingEntryFromAnnual(GeneralLedgerPendingEntry newRow, PendingBudgetConstructionGeneralLedger pbgl, DaoGlobalVariables daoGlobalVariables, DiagnosticCounters diagnosticCounters) { /** * first get the document number */ String incomingDocumentNumber = pbgl.getDocumentNumber(); /** * write a base budget row */ newRow.setFinancialBalanceTypeCode(KFSConstants.BALANCE_TYPE_BASE_BUDGET); newRow.setUniversityFiscalPeriodCode(KFSConstants.PERIOD_CODE_BEGINNING_BALANCE); /** * set the variable fields */ newRow.setTransactionLedgerEntrySequenceNumber(daoGlobalVariables.getNextSequenceNumber(incomingDocumentNumber)); newRow.setDocumentNumber(incomingDocumentNumber); // document number newRow.setChartOfAccountsCode(pbgl.getChartOfAccountsCode()); // chart of accounts newRow.setAccountNumber(pbgl.getAccountNumber()); // account number newRow.setSubAccountNumber(pbgl.getSubAccountNumber()); // sub account number newRow.setFinancialObjectCode(pbgl.getFinancialObjectCode()); // object code newRow.setFinancialSubObjectCode(pbgl.getFinancialSubObjectCode()); // sub object code newRow.setFinancialObjectTypeCode(pbgl.getFinancialObjectTypeCode()); // object type code /** * the budget works with whole numbers--we must convert to decimal for the general ledger */ newRow.setTransactionLedgerEntryAmount(pbgl.getAccountLineAnnualBalanceAmount().kualiDecimalValue()); /** * now we store the base budget value */ getPersistenceBrokerTemplate().store(newRow); diagnosticCounters.increaseGeneralLedgerBaseBudgetWritten(); /** * the same row needs to be written as a current budget item we change only the balance type and the sequence number */ newRow.setFinancialBalanceTypeCode(KFSConstants.BALANCE_TYPE_CURRENT_BUDGET); newRow.setTransactionLedgerEntrySequenceNumber(daoGlobalVariables.getNextSequenceNumber(incomingDocumentNumber)); /** * store the current budget value */ getPersistenceBrokerTemplate().store(newRow); diagnosticCounters.increasGenneralLedgerCurrentBudgetWritten(); } protected void writeGeneralLedgerPendingEntryFromMonthly(GeneralLedgerPendingEntry newRow, BudgetConstructionMonthly pbglMonthly, DaoGlobalVariables daoGlobalVariables, DiagnosticCounters diagnosticCounters) { /** * first get the document number */ String incomingDocumentNumber = pbglMonthly.getDocumentNumber(); /** * write a base budget row */ newRow.setFinancialBalanceTypeCode(KFSConstants.BALANCE_TYPE_MONTHLY_BUDGET); /** * set the variable fields */ newRow.setDocumentNumber(incomingDocumentNumber); // document number newRow.setChartOfAccountsCode(pbglMonthly.getChartOfAccountsCode()); // chart of accounts newRow.setAccountNumber(pbglMonthly.getAccountNumber()); // account number newRow.setSubAccountNumber(pbglMonthly.getSubAccountNumber()); // sub account number newRow.setFinancialObjectCode(pbglMonthly.getFinancialObjectCode()); // object code newRow.setFinancialSubObjectCode(pbglMonthly.getFinancialSubObjectCode()); // sub object code newRow.setFinancialObjectTypeCode(pbglMonthly.getFinancialObjectTypeCode()); // object type code /** * 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 * write less code. we hope that the extra hit from reflection won't be too bad) */ Iterator<String[]> monthlyPeriodAmounts = BCConstants.BC_MONTHLY_AMOUNTS.iterator(); while (monthlyPeriodAmounts.hasNext()) { String[] monthlyPeriodProperties = monthlyPeriodAmounts.next(); KualiInteger monthlyAmount; try { monthlyAmount = (KualiInteger) PropertyUtils.getSimpleProperty(pbglMonthly, monthlyPeriodProperties[0]); } catch (IllegalAccessException ex) { LOG.error(String.format("\nunable to use get method to access value of %s in %s\n", monthlyPeriodProperties[0], BudgetConstructionMonthly.class.getName()), ex); diagnosticCounters.writeDiagnosticCounters(); throw new RuntimeException(ex); } catch (InvocationTargetException ex) { LOG.error(String.format("\nunable to invoke get method for %s in %s\n", monthlyPeriodProperties[0], BudgetConstructionMonthly.class.getName()), ex); diagnosticCounters.writeDiagnosticCounters(); throw new RuntimeException(ex); } catch (NoSuchMethodException ex) { LOG.error(String.format("\nNO get method found for %s in %s ???\n", monthlyPeriodProperties[0], BudgetConstructionMonthly.class.getName()), ex); diagnosticCounters.writeDiagnosticCounters(); throw new RuntimeException(ex); } if (!(monthlyAmount.isZero())) { newRow.setTransactionLedgerEntrySequenceNumber(daoGlobalVariables.getNextSequenceNumber(incomingDocumentNumber)); newRow.setUniversityFiscalPeriodCode(monthlyPeriodProperties[1]); // accounting period newRow.setTransactionLedgerEntryAmount(monthlyAmount.kualiDecimalValue()); // amount getPersistenceBrokerTemplate().store(newRow); diagnosticCounters.increaseBudgetConstructionMonthlyBudgetWritten(); } } } /******************************************************************************************************************************* * * This section build the list of accounts that SHOULD NOT be loaded to the general ledger * (This may seem strange--why build * 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 * allowed people to set salaries for the new year * so those would load to payroll. But, the actual budget for a grant account * was not necessarily done on a * fiscal year basis, and was not part of the university's operating budget, so there was no * "base budget" * for a grant account to load to the general ledger.) * (1) We will inhibit the load to the general ledger of * all accounts in given sub fund groups * (2) (We WILL allow closed accounts to load. There should not be any--they should have * been filtered * out in the budget application, but if there are, they will be caught by the GL scrubber. We want * people to * have a record of this kind of load failure, so it can be corrected. * * * ******************************************************************************************************************************/ /** * get a list of accounts that should not be loaded from the budget to the General Ledger * * @return hashset of accounts NOT to be loaded */ protected HashSet<String> getAccountsNotToBeLoaded() { HashSet<String> bannedAccounts; /** * list of subfunds which should not be loaded */ HashSet<String> bannedSubFunds = getSubFundsNotToBeLoaded(); /** * query for the subfund property for each account in the DB */ ReportQueryByCriteria queryID = new ReportQueryByCriteria(Account.class, org.apache.ojb.broker.query.ReportQueryByCriteria.CRITERIA_SELECT_ALL); queryID.setAttributes(new String[] { KFSPropertyConstants.ACCOUNT_NUMBER, KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, KFSPropertyConstants.SUB_FUND_GROUP_CODE }); bannedAccounts = new HashSet<String>(hashCapacity(queryID)); /** * use the results to build a hash set of accounts which should NOT be loaded (that is, their subfunds are in the list of * subfunds we do not want */ Iterator accountProperties = getPersistenceBrokerTemplate().getReportQueryIteratorByQuery(queryID); while (accountProperties.hasNext()) { Object[] selectListValues = (Object[]) accountProperties.next(); /** * we will add an account/chart to the list if it has a no-load subfundgroup */ if (bannedSubFunds.contains((String) selectListValues[2])) { /** * hash content is account number concatenated with chart (the key of the chart of accounts table) */ bannedAccounts.add(((String) selectListValues[0]) + ((String) selectListValues[1])); } } return bannedAccounts; } /** * 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 * list of subfund groups * * @see org.kuali.kfs.module.bc.BCConstants to initialize the String[] array(s) as desired * @return list of subfunds whose accounts will NOT be loaded */ protected HashSet<String> getSubFundsNotToBeLoaded() { HashSet<String> bannedSubFunds; if (BCConstants.NO_BC_GL_LOAD_FUND_GROUPS.size() != 0) { /** * look for subfunds in the banned fund groups */ Criteria criteriaID = new Criteria(); criteriaID.addIn(KFSPropertyConstants.FUND_GROUP_CODE, BCConstants.NO_BC_GL_LOAD_FUND_GROUPS); ReportQueryByCriteria queryID = new ReportQueryByCriteria(SubFundGroup.class, criteriaID); queryID.setAttributes(new String[] { KFSPropertyConstants.SUB_FUND_GROUP_CODE }); /** * set the size of the hashset based on the number of rows the query will return */ bannedSubFunds = new HashSet<String>(hashCapacity(queryID) + BCConstants.NO_BC_GL_LOAD_SUBFUND_GROUPS.size()); Iterator subfundsForBannedFunds = getPersistenceBrokerTemplate().getReportQueryIteratorByQuery(queryID); /** * add the subfunds for the fund groups to be skipped to the hash set */ while (subfundsForBannedFunds.hasNext()) { bannedSubFunds.add((String) ((Object[]) subfundsForBannedFunds.next())[0]); } } else { bannedSubFunds = new HashSet<String>(BCConstants.NO_BC_GL_LOAD_SUBFUND_GROUPS.size() + 1); } /** * now add the specific sub funds we don't want from the hard-coded array in BCConstants to the hash set */ Iterator<String> additionalBannedSubFunds = BCConstants.NO_BC_GL_LOAD_SUBFUND_GROUPS.iterator(); while (additionalBannedSubFunds.hasNext()) { bannedSubFunds.add(additionalBannedSubFunds.next()); } return bannedSubFunds; } /******************************************************************************************************************************* * This section sets all the accounting periods for the coming year to open. * The monthly budget will load by accounting * period. If some are not open, some monthly rows will error * out in the scrubber. Current FIS procedure is to prevent this * from happening, by opening all the * accounting periods and letting the university chart manager close them after the budget * is loaded if that * is desirable for some reason. If an institution prefers another policy, just don't call these methods. * * But, even if we let the scrubber fail, there will be no way to load the monthly rows from the error tables* unless the * corresponding accounting periods are open. * ******************************************************************************************************************************/ /** * this method makes sure all accounting periods inn the target fiscal year are open * * @param request fiscal year (or other fiscal period) which is the TARGET of the load */ protected void openAllAccountingPeriods(Integer requestYear) { Criteria criteriaID = new Criteria(); criteriaID.addEqualTo(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, requestYear); criteriaID.addNotEqualTo(KFSPropertyConstants.UNIVERSITY_FISCAL_PERIOD_STATUS_CODE, "Y"); QueryByCriteria queryID = new QueryByCriteria(AccountingPeriod.class, criteriaID); Iterator<AccountingPeriod> unopenPeriods = getPersistenceBrokerTemplate().getIteratorByQuery(queryID); int periodsOpened = 0; while (unopenPeriods.hasNext()) { AccountingPeriod periodToOpen = unopenPeriods.next(); periodToOpen.setActive(true); getPersistenceBrokerTemplate().store(periodToOpen); periodsOpened = periodsOpened + 1; } LOG.warn(String.format("\n\naccounting periods for %d changed to open status: %d", requestYear, new Integer(periodsOpened))); } /******************************************************************************************************************************* * These two classes are containers so we can make certain variables accessible to all methods without making them global to the * * outer class and without cluttering up the method signatures. * ******************************************************************************************************************************/ /** * 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 * the unlikely event this code is run by more than one thread */ protected class DiagnosticCounters { long budgetConstructionPendingGeneralLedgerRead = 0; long budgetConstructionPendingGeneralLedgerSkipped = 0; long generalLedgerBaseBudgetWritten = 0; long generalLedgerCurrentBudgetWritten = 0; long budgetConstructionMonthlyBudgetRead = 0; long budgetConstructionMonthlyBudgetSkipped = 0; long budgetConstructionMonthlyBudgetWritten = 0; public void increaseBudgetConstructionPendingGeneralLedgerRead() { budgetConstructionPendingGeneralLedgerRead++; } public void increaseBudgetConstructionPendingGeneralLedgerSkipped() { budgetConstructionPendingGeneralLedgerSkipped++; } public void increaseGeneralLedgerBaseBudgetWritten() { generalLedgerBaseBudgetWritten++; } public void increasGenneralLedgerCurrentBudgetWritten() { generalLedgerCurrentBudgetWritten++; } public void increaseBudgetConstructionMonthlyBudgetRead() { budgetConstructionMonthlyBudgetRead++; } public void increaseBudgetConstructionMonthlyBudgetSkipped() { budgetConstructionMonthlyBudgetSkipped++; } public void increaseBudgetConstructionMonthlyBudgetWritten() { budgetConstructionMonthlyBudgetWritten++; } public void writeDiagnosticCounters() { LOG.warn(String.format("\n\nPending Budget Construction General Ledger Load\n")); LOG.warn(String.format("\n pending budget construction GL rows read: %,d", budgetConstructionPendingGeneralLedgerRead)); LOG.warn(String.format("\n pending budget construction GL rows skipped: %,d", budgetConstructionPendingGeneralLedgerSkipped)); LOG.warn(String.format("\n\n base budget rows written: %,d", generalLedgerBaseBudgetWritten)); LOG.warn(String.format("\n current budget rows written: %,d", generalLedgerCurrentBudgetWritten)); LOG.warn(String.format("\n\n pending budget construction monthly rows read: %,d", budgetConstructionMonthlyBudgetRead)); LOG.warn(String.format("\n pending budget construction monthly rows skipped: %,d", budgetConstructionMonthlyBudgetSkipped)); LOG.warn(String.format("\n pending budget construction monthly rows written: %,d", budgetConstructionMonthlyBudgetWritten)); } } /** * This class allows us to create global variables and pass them around. This should make the code thread safe, in the unlikely * event it is called by more than one thread. it also allows us to fetch constants and build datas stuctures from the DB once * upon instantiation of this class, and make them available for the duration of the run * * @param requestYear * @param <documentNumber, ledger sequence number> HashMap * @param current SQL Date (which will be the transaction date in the general ledger entry rows we create) * @param the "financial system Origination Code" for this database */ protected class DaoGlobalVariables { private Integer requestYear; private HashMap<String, Integer> entrySequenceNumber; private Date transactionDate; private String financialSystemOriginationCode; private HashSet<String> accountsNotToBeLoaded; public DaoGlobalVariables(Integer requestYear, Date currentSqlDate, String financialSystemOriginationCode) { this.requestYear = requestYear; this.entrySequenceNumber = entrySequenceNumber(requestYear); this.transactionDate = currentSqlDate; this.financialSystemOriginationCode = financialSystemOriginationCode; this.accountsNotToBeLoaded = getAccountsNotToBeLoaded(); } public Integer getRequestYear() { return this.requestYear; } /** * return the next available sequence number for the input key and update "next available" */ public Integer getNextSequenceNumber(String seqKey) { Integer newSeqNumber = entrySequenceNumber.get(seqKey); entrySequenceNumber.put(seqKey, new Integer(newSeqNumber.intValue() + 1)); return newSeqNumber; } public Date getTransactionDate() { return this.transactionDate; } public String getFinancialSystemOriginationcode() { return this.financialSystemOriginationCode; } public boolean shouldThisAccountLoad(String accountAndChart) { return (!accountsNotToBeLoaded.contains(accountAndChart)); } } }