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 }