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.gl.batch.service.impl; 017 018 import java.io.File; 019 import java.io.FileNotFoundException; 020 import java.io.PrintStream; 021 import java.sql.Date; 022 import java.util.ArrayList; 023 import java.util.Iterator; 024 import java.util.List; 025 import java.util.Map; 026 027 import org.kuali.kfs.coa.businessobject.AccountIntf; 028 import org.kuali.kfs.coa.businessobject.CarryForwardReversionProcessOrganizationInfo; 029 import org.kuali.kfs.coa.businessobject.ClosedAccountOrganizationReversion; 030 import org.kuali.kfs.coa.businessobject.ObjectCode; 031 import org.kuali.kfs.coa.businessobject.OrganizationReversion; 032 import org.kuali.kfs.coa.businessobject.OrganizationReversionCategory; 033 import org.kuali.kfs.coa.businessobject.OrganizationReversionCategoryInfo; 034 import org.kuali.kfs.coa.service.OrganizationReversionService; 035 import org.kuali.kfs.coa.service.PriorYearAccountService; 036 import org.kuali.kfs.gl.GeneralLedgerConstants; 037 import org.kuali.kfs.gl.batch.service.OrganizationReversionCategoryLogic; 038 import org.kuali.kfs.gl.batch.service.OrganizationReversionProcess; 039 import org.kuali.kfs.gl.batch.service.OrganizationReversionUnitOfWorkService; 040 import org.kuali.kfs.gl.batch.service.impl.exception.FatalErrorException; 041 import org.kuali.kfs.gl.businessobject.Balance; 042 import org.kuali.kfs.gl.businessobject.OrgReversionUnitOfWork; 043 import org.kuali.kfs.gl.businessobject.OrgReversionUnitOfWorkCategoryAmount; 044 import org.kuali.kfs.gl.businessobject.OriginEntryFull; 045 import org.kuali.kfs.gl.report.LedgerSummaryReport; 046 import org.kuali.kfs.gl.service.BalanceService; 047 import org.kuali.kfs.gl.service.OriginEntryService; 048 import org.kuali.kfs.sys.KFSConstants; 049 import org.kuali.kfs.sys.KFSKeyConstants; 050 import org.kuali.kfs.sys.KFSPropertyConstants; 051 import org.kuali.kfs.sys.businessobject.SystemOptions; 052 import org.kuali.kfs.sys.context.SpringContext; 053 import org.kuali.kfs.sys.service.FlexibleOffsetAccountService; 054 import org.kuali.kfs.sys.service.OptionsService; 055 import org.kuali.kfs.sys.service.ReportWriterService; 056 import org.kuali.kfs.sys.service.impl.KfsParameterConstants; 057 import org.kuali.rice.kns.service.DateTimeService; 058 import org.kuali.rice.kns.service.KualiConfigurationService; 059 import org.kuali.rice.kns.service.ParameterService; 060 import org.kuali.rice.kns.service.PersistenceService; 061 import org.kuali.rice.kns.util.KualiDecimal; 062 import org.kuali.rice.kns.util.ObjectUtils; 063 import org.springframework.beans.factory.InitializingBean; 064 import org.springframework.transaction.annotation.Transactional; 065 066 /** 067 * This class actually runs the year end organization reversion process 068 */ 069 @Transactional 070 public class OrganizationReversionProcessImpl implements OrganizationReversionProcess, InitializingBean { 071 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(OrganizationReversionProcessImpl.class); 072 073 // Services 074 private OrganizationReversionService organizationReversionService; 075 private BalanceService balanceService; 076 private OriginEntryService originEntryService; 077 private PersistenceService persistenceService; 078 private DateTimeService dateTimeService; 079 private OrganizationReversionCategoryLogic cashOrganizationReversionCategoryLogic; 080 private PriorYearAccountService priorYearAccountService; 081 private OrganizationReversionUnitOfWorkService orgReversionUnitOfWorkService; 082 private FlexibleOffsetAccountService flexibleOffsetAccountService; 083 private ParameterService parameterService; 084 private KualiConfigurationService configurationService; 085 086 private String batchFileDirectoryName; 087 private String outputFileName; 088 private OrgReversionUnitOfWork unitOfWork; 089 private Map<String, OrganizationReversionCategoryLogic> categories; 090 private List<OrganizationReversionCategory> categoryList; 091 private CarryForwardReversionProcessOrganizationInfo organizationReversion; 092 private AccountIntf account; 093 094 private Map jobParameters; 095 private Map<String, Integer> organizationReversionCounts; 096 097 private boolean usePriorYearInformation; 098 099 private boolean holdGeneratedOriginEntries = false; 100 private List<OriginEntryFull> generatedOriginEntries; 101 102 public String CARRY_FORWARD_OBJECT_CODE; 103 public String DEFAULT_FINANCIAL_DOCUMENT_TYPE_CODE; 104 public String DEFAULT_FINANCIAL_SYSTEM_ORIGINATION_CODE; 105 public String DEFAULT_FINANCIAL_BALANCE_TYPE_CODE; 106 public String DEFAULT_FINANCIAL_BALANCE_TYPE_CODE_YEAR_END; 107 public String DEFAULT_DOCUMENT_NUMBER_PREFIX; 108 109 private String CASH_REVERTED_TO_MESSAGE; 110 private String FUND_BALANCE_REVERTED_TO_MESSAGE; 111 private String CASH_REVERTED_FROM_MESSAGE; 112 private String FUND_BALANCE_REVERTED_FROM_MESSAGE; 113 private String FUND_CARRIED_MESSAGE; 114 private String FUND_REVERTED_TO_MESSAGE; 115 private String FUND_REVERTED_FROM_MESSAGE; 116 117 private SystemOptions systemOptions; 118 private Integer paramFiscalYear; 119 120 private LedgerSummaryReport ledgerReport; 121 122 private PrintStream outputPs; 123 124 /** 125 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() 126 */ 127 public void afterPropertiesSet() throws Exception { 128 this.CARRY_FORWARD_OBJECT_CODE = getParameterService().getParameterValue(OrganizationReversion.class, GeneralLedgerConstants.OrganizationReversionProcess.CARRY_FORWARD_OBJECT_CODE); 129 this.DEFAULT_FINANCIAL_DOCUMENT_TYPE_CODE = getParameterService().getParameterValue(KfsParameterConstants.GENERAL_LEDGER_BATCH.class, GeneralLedgerConstants.ANNUAL_CLOSING_DOCUMENT_TYPE); 130 this.DEFAULT_FINANCIAL_SYSTEM_ORIGINATION_CODE = getParameterService().getParameterValue(OrganizationReversion.class, GeneralLedgerConstants.OrganizationReversionProcess.DEFAULT_FINANCIAL_SYSTEM_ORIGINATION_CODE); 131 this.DEFAULT_FINANCIAL_BALANCE_TYPE_CODE = getParameterService().getParameterValue(OrganizationReversion.class, GeneralLedgerConstants.OrganizationReversionProcess.DEFAULT_FINANCIAL_BALANCE_TYPE_CODE); 132 this.DEFAULT_FINANCIAL_BALANCE_TYPE_CODE_YEAR_END = getParameterService().getParameterValue(OrganizationReversion.class, GeneralLedgerConstants.OrganizationReversionProcess.DEFAULT_FINANCIAL_BALANCE_TYPE_CODE_YEAR_END); 133 this.DEFAULT_DOCUMENT_NUMBER_PREFIX = getParameterService().getParameterValue(OrganizationReversion.class, GeneralLedgerConstants.OrganizationReversionProcess.DEFAULT_DOCUMENT_NUMBER_PREFIX); 134 this.CASH_REVERTED_TO_MESSAGE = getConfigurationService().getPropertyString(KFSKeyConstants.OrganizationReversionProcess.CASH_REVERTED_TO); 135 this.FUND_BALANCE_REVERTED_TO_MESSAGE = getConfigurationService().getPropertyString(KFSKeyConstants.OrganizationReversionProcess.FUND_BALANCE_REVERTED_TO); 136 this.CASH_REVERTED_FROM_MESSAGE = getConfigurationService().getPropertyString(KFSKeyConstants.OrganizationReversionProcess.CASH_REVERTED_FROM); 137 this.FUND_BALANCE_REVERTED_FROM_MESSAGE = getConfigurationService().getPropertyString(KFSKeyConstants.OrganizationReversionProcess.FUND_BALANCE_REVERTED_FROM); 138 this.FUND_CARRIED_MESSAGE = getConfigurationService().getPropertyString(KFSKeyConstants.OrganizationReversionProcess.FUND_CARRIED); 139 this.FUND_REVERTED_TO_MESSAGE = getConfigurationService().getPropertyString(KFSKeyConstants.OrganizationReversionProcess.FUND_REVERTED_TO); 140 this.FUND_REVERTED_FROM_MESSAGE = getConfigurationService().getPropertyString(KFSKeyConstants.OrganizationReversionProcess.FUND_REVERTED_FROM); 141 142 outputFileName = getBatchFileDirectoryName() + File.separator + (usePriorYearInformation ? GeneralLedgerConstants.BatchFileSystem.ORGANIZATION_REVERSION_CLOSING_FILE : GeneralLedgerConstants.BatchFileSystem.ORGANIZATION_REVERSION_PRE_CLOSING_FILE) + GeneralLedgerConstants.BatchFileSystem.EXTENSION; 143 } 144 145 /** 146 * This evilly named method actually runs the organization reversion process. 147 */ 148 public void organizationReversionProcess(Map jobParameters, Map<String, Integer> organizationReversionCounts) { 149 if (LOG.isDebugEnabled()) { 150 LOG.debug("organizationReversionProcess() started"); 151 } 152 this.jobParameters = jobParameters; 153 this.organizationReversionCounts = organizationReversionCounts; 154 155 LOG.info("Initializing the process"); 156 initializeProcess(); 157 158 //create files 159 File outputFile = new File(outputFileName); 160 161 try { 162 outputPs = new PrintStream(outputFile); 163 164 Iterator<Balance> balances = getBalanceService().findOrganizationReversionBalancesForFiscalYear((Integer) jobParameters.get(KFSConstants.UNIV_FISCAL_YR), usePriorYearInformation); 165 processBalances(balances); 166 167 outputPs.close(); 168 } catch (FileNotFoundException e) { 169 throw new RuntimeException("Organization Reversion File Files doesn't exist " + outputFileName); 170 } 171 172 } 173 174 /** 175 * Given a list of balances, this method generates the origin entries for the organization reversion/carry forward process, and saves those 176 * to an initialized origin entry group 177 * 178 * @param balances an iterator of balances to process; each balance returned by the iterator will be processed by this method 179 */ 180 public void processBalances(Iterator<Balance> balances) { 181 boolean skipToNextUnitOfWork = false; 182 unitOfWork = new OrgReversionUnitOfWork(); 183 unitOfWork.setCategories(categoryList); 184 185 Balance bal; 186 while (balances.hasNext()) { 187 bal = balances.next(); 188 if (LOG.isDebugEnabled()) { 189 LOG.debug("BALANCE SELECTED: " + bal.getUniversityFiscalYear() + bal.getChartOfAccountsCode() + bal.getAccountNumber() + bal.getSubAccountNumber() + bal.getObjectCode() + bal.getSubObjectCode() + bal.getBalanceTypeCode() + bal.getObjectTypeCode() + " " + bal.getAccountLineAnnualBalanceAmount().add(bal.getBeginningBalanceLineAmount())); 190 } 191 192 try { 193 if (!unitOfWork.isInitialized()) { 194 unitOfWork.setFields(bal.getChartOfAccountsCode(), bal.getAccountNumber(), bal.getSubAccountNumber()); 195 retrieveCurrentReversionAndAccount(bal); 196 } 197 else if (!unitOfWork.wouldHold(bal)) { 198 if (!skipToNextUnitOfWork) { 199 calculateTotals(); 200 List<OriginEntryFull> originEntriesToWrite = generateOutputOriginEntries(); 201 summarizeOriginEntries(originEntriesToWrite); 202 if (holdGeneratedOriginEntries) { 203 generatedOriginEntries.addAll(originEntriesToWrite); 204 } 205 int recordsWritten = writeOriginEntries(originEntriesToWrite); 206 incrementCount("recordsWritten", recordsWritten); 207 getOrgReversionUnitOfWorkService().save(unitOfWork); 208 } 209 unitOfWork.setFields(bal.getChartOfAccountsCode(), bal.getAccountNumber(), bal.getSubAccountNumber()); 210 retrieveCurrentReversionAndAccount(bal); 211 skipToNextUnitOfWork = false; 212 } 213 if (skipToNextUnitOfWork) { 214 continue; // if there is no org reversion or an org reversion detail is missing or the balances are off for 215 // this unit of work, 216 // just skip all the balances until we change unit of work 217 } 218 calculateBucketAmounts(bal); 219 } 220 catch (FatalErrorException fee) { 221 LOG.info(fee.getMessage()); 222 skipToNextUnitOfWork = true; 223 } 224 } 225 // save the final unit of work 226 if (!skipToNextUnitOfWork && getBalancesSelected() > 0) { 227 try { 228 calculateTotals(); 229 List<OriginEntryFull> originEntriesToWrite = generateOutputOriginEntries(); 230 summarizeOriginEntries(originEntriesToWrite); 231 if (holdGeneratedOriginEntries) { 232 generatedOriginEntries.addAll(originEntriesToWrite); 233 } 234 int recordsWritten = writeOriginEntries(originEntriesToWrite); 235 incrementCount("recordsWritten", recordsWritten); 236 getOrgReversionUnitOfWorkService().save(unitOfWork); 237 } 238 catch (FatalErrorException fee) { 239 LOG.info(fee.getMessage()); 240 } 241 } 242 243 } 244 245 /** 246 * Given a balance, returns the current organization reversion record and account or prior year account for the balance; it sets them 247 * to private properties 248 * 249 * @param bal the balance to find the account/prior year account and organization reversion record for 250 * @throws FatalErrorException if an organization reversion record cannot be found in the database 251 */ 252 protected void retrieveCurrentReversionAndAccount(Balance bal) throws FatalErrorException { 253 // initialize the account 254 if ((account == null) || (!bal.getChartOfAccountsCode().equals(account.getChartOfAccountsCode())) || (!bal.getAccountNumber().equals(account.getAccountNumber()))) { 255 if (usePriorYearInformation) { 256 account = getPriorYearAccountService().getByPrimaryKey(bal.getChartOfAccountsCode(), bal.getAccountNumber()); 257 } 258 else { 259 account = bal.getAccount(); 260 } 261 } 262 263 if ((organizationReversion == null) || (!organizationReversion.getChartOfAccountsCode().equals(bal.getChartOfAccountsCode())) || (!organizationReversion.getOrganizationCode().equals(account.getOrganizationCode()))) { 264 if (LOG.isDebugEnabled()) { 265 LOG.debug("Organization Reversion Service: " + getOrganizationReversionService() + "; fiscal year: " + (Integer) jobParameters.get(KFSConstants.UNIV_FISCAL_YR) + "; account: " + account + "; account organization code: " + account.getOrganizationCode() + "; balance: " + bal + "; balance chart: " + bal.getChartOfAccountsCode()); 266 } 267 organizationReversion = getOrganizationReversionService().getByPrimaryId((Integer) jobParameters.get(KFSConstants.UNIV_FISCAL_YR), bal.getChartOfAccountsCode(), account.getOrganizationCode()); 268 } 269 270 if (organizationReversion == null) { 271 // we can't find an organization reversion for this balance? Throw exception 272 throw new FatalErrorException("No Organization Reversion found for: " + (Integer) jobParameters.get(KFSConstants.UNIV_FISCAL_YR) + "-" + bal.getChartOfAccountsCode() + "-" + account.getOrganizationCode()); 273 } 274 275 if (account.isClosed()) { 276 organizationReversion = new ClosedAccountOrganizationReversion(organizationReversion); 277 } 278 } 279 280 /** 281 * This method initializes several properties needed for the process to run correctly 282 */ 283 public void initializeProcess() { 284 285 // clear out summary tables 286 LOG.info("destroying all unit of work summaries"); 287 orgReversionUnitOfWorkService.destroyAllUnitOfWorkSummaries(); 288 289 categories = getOrganizationReversionService().getCategories(); 290 categoryList = getOrganizationReversionService().getCategoryList(); 291 292 this.paramFiscalYear = (Integer) jobParameters.get(KFSConstants.UNIV_FISCAL_YR); 293 294 organizationReversionCounts.put("balancesRead", balanceService.countBalancesForFiscalYear(paramFiscalYear)); 295 organizationReversionCounts.put("balancesSelected", new Integer(0)); 296 organizationReversionCounts.put("recordsWritten", new Integer(0)); 297 298 this.systemOptions = SpringContext.getBean(OptionsService.class).getOptions(paramFiscalYear); 299 300 ledgerReport = new LedgerSummaryReport(); 301 } 302 303 /** 304 * Depending on the category that this balance belongs to, adds the balance to the appropriate bucket 305 * 306 * @param bal the current balance to process 307 */ 308 protected void calculateBucketAmounts(Balance bal) { 309 getPersistenceService().retrieveReferenceObject(bal, "financialObject"); 310 311 if (LOG.isDebugEnabled()) { 312 LOG.debug("CONSIDERING IF TO ADD BALANCE: " + bal.getUniversityFiscalYear() + bal.getChartOfAccountsCode() + bal.getAccountNumber() + bal.getSubAccountNumber() + bal.getObjectCode() + bal.getSubObjectCode() + bal.getBalanceTypeCode() + bal.getObjectTypeCode() + " " + bal.getAccountLineAnnualBalanceAmount().add(bal.getBeginningBalanceLineAmount())); 313 } 314 315 if (getCashOrganizationReversionCategoryLogic().containsObjectCode(bal.getFinancialObject()) && bal.getBalanceTypeCode().equals(systemOptions.getActualFinancialBalanceTypeCd())) { 316 unitOfWork.addTotalCash(bal.getBeginningBalanceLineAmount()); 317 unitOfWork.addTotalCash(bal.getAccountLineAnnualBalanceAmount()); 318 incrementCount("balancesSelected"); 319 if (LOG.isDebugEnabled()) { 320 LOG.debug("ADDING BALANCE TO CASH: " + bal.getUniversityFiscalYear() + bal.getChartOfAccountsCode() + bal.getAccountNumber() + bal.getSubAccountNumber() + bal.getObjectCode() + bal.getSubObjectCode() + bal.getBalanceTypeCode() + bal.getObjectTypeCode() + " " + bal.getAccountLineAnnualBalanceAmount().add(bal.getBeginningBalanceLineAmount()) + " TO CASH, TOTAL CASH NOW = " + unitOfWork.getTotalCash()); 321 } 322 } 323 else { 324 for (OrganizationReversionCategory cat : categoryList) { 325 OrganizationReversionCategoryLogic logic = categories.get(cat.getOrganizationReversionCategoryCode()); 326 if (logic.containsObjectCode(bal.getFinancialObject())) { 327 if (systemOptions.getActualFinancialBalanceTypeCd().equals(bal.getBalanceTypeCode())) { 328 // Actual 329 unitOfWork.addActualAmount(cat.getOrganizationReversionCategoryCode(), bal.getBeginningBalanceLineAmount()); 330 unitOfWork.addActualAmount(cat.getOrganizationReversionCategoryCode(), bal.getAccountLineAnnualBalanceAmount()); 331 incrementCount("balancesSelected"); 332 if (LOG.isDebugEnabled()) { 333 LOG.debug("ADDING BALANCE TO ACTUAL: " + bal.getUniversityFiscalYear() + bal.getChartOfAccountsCode() + bal.getAccountNumber() + bal.getSubAccountNumber() + bal.getObjectCode() + bal.getSubObjectCode() + bal.getBalanceTypeCode() + bal.getObjectTypeCode() + " " + bal.getAccountLineAnnualBalanceAmount().add(bal.getBeginningBalanceLineAmount()) + " TO ACTUAL, ACTUAL FOR CATEGORY " + cat.getOrganizationReversionCategoryName() + " NOW = " + unitOfWork.getCategoryAmounts().get(cat.getOrganizationReversionCategoryCode()).getActual()); 334 } 335 } 336 else if (systemOptions.getFinObjTypeExpenditureexpCd().equals(bal.getBalanceTypeCode()) || systemOptions.getCostShareEncumbranceBalanceTypeCd().equals(bal.getBalanceTypeCode()) || systemOptions.getIntrnlEncumFinBalanceTypCd().equals(bal.getBalanceTypeCode())) { 337 // Encumbrance 338 KualiDecimal amount = bal.getBeginningBalanceLineAmount().add(bal.getAccountLineAnnualBalanceAmount()); 339 if (amount.isPositive()) { 340 unitOfWork.addEncumbranceAmount(cat.getOrganizationReversionCategoryCode(), amount); 341 incrementCount("balancesSelected"); 342 if (LOG.isDebugEnabled()) { 343 LOG.debug("ADDING BALANCE TO ENCUMBRANCE: " + bal.getUniversityFiscalYear() + bal.getChartOfAccountsCode() + bal.getAccountNumber() + bal.getSubAccountNumber() + bal.getObjectCode() + bal.getSubObjectCode() + bal.getBalanceTypeCode() + bal.getObjectTypeCode() + " " + bal.getAccountLineAnnualBalanceAmount().add(bal.getBeginningBalanceLineAmount()) + " TO ENCUMBRANCE, ENCUMBRANCE FOR CATEGORY " + cat.getOrganizationReversionCategoryName() + " NOW = " + unitOfWork.getCategoryAmounts().get(cat.getOrganizationReversionCategoryCode()).getEncumbrance()); 344 } 345 } 346 } 347 else if (KFSConstants.BALANCE_TYPE_CURRENT_BUDGET.equals(bal.getBalanceTypeCode())) { 348 // Budget 349 if (!CARRY_FORWARD_OBJECT_CODE.equals(bal.getObjectCode())) { 350 unitOfWork.addBudgetAmount(cat.getOrganizationReversionCategoryCode(), bal.getBeginningBalanceLineAmount()); 351 unitOfWork.addBudgetAmount(cat.getOrganizationReversionCategoryCode(), bal.getAccountLineAnnualBalanceAmount()); 352 incrementCount("balancesSelected"); 353 if (LOG.isDebugEnabled()) { 354 LOG.debug("ADDING BALANCE TO BUDGET: " + bal.getUniversityFiscalYear() + bal.getChartOfAccountsCode() + bal.getAccountNumber() + bal.getSubAccountNumber() + bal.getObjectCode() + bal.getSubObjectCode() + bal.getBalanceTypeCode() + bal.getObjectTypeCode() + " " + bal.getAccountLineAnnualBalanceAmount().add(bal.getBeginningBalanceLineAmount()) + " TO CURRENT BUDGET, CURRENT BUDGET FOR CATEGORY " + cat.getOrganizationReversionCategoryName() + " NOW = " + unitOfWork.getCategoryAmounts().get(cat.getOrganizationReversionCategoryCode()).getBudget()); 355 } 356 } 357 } 358 break; 359 } 360 } 361 } 362 } 363 364 /** 365 * This method determines which origin entries (reversion, cash reversion, or carry forward) need to be generated for the current unit of work, 366 * and then delegates to the origin entry generation methods to create those entries 367 * 368 * @return a list of OriginEntries which need to be written 369 * @throws FatalErrorException thrown if object codes are missing in any of the generation methods 370 */ 371 public List<OriginEntryFull> generateOutputOriginEntries() throws FatalErrorException { 372 List<OriginEntryFull> originEntriesToWrite = new ArrayList<OriginEntryFull>(); 373 if (unitOfWork.getTotalReversion().compareTo(KualiDecimal.ZERO) != 0) { 374 generateReversions(originEntriesToWrite); 375 } 376 if ((unitOfWork.getTotalCarryForward().compareTo(KualiDecimal.ZERO) != 0)) { 377 if (!organizationReversion.isCarryForwardByObjectCodeIndicator()) { 378 generateCarryForwards(originEntriesToWrite); 379 } 380 else { 381 generateMany(originEntriesToWrite); 382 } 383 } 384 if (unitOfWork.getTotalCash().compareTo(KualiDecimal.ZERO) != 0) { 385 generateCashReversions(originEntriesToWrite); 386 } 387 return originEntriesToWrite; 388 } 389 390 /** 391 * This method writes a list of OriginEntryFulls to a given origin entry group 392 * 393 * @param writeGroup the origin entry group to write to 394 * @param originEntriesToWrite a list of origin entry fulls to write 395 * @return the count of origin entries that were written 396 */ 397 protected int writeOriginEntries(List<OriginEntryFull> originEntriesToWrite) { 398 int originEntriesWritten = 0; 399 400 for (OriginEntryFull originEntry : originEntriesToWrite) { 401 getOriginEntryService().createEntry(originEntry, outputPs); 402 originEntriesWritten += 1; 403 } 404 405 return originEntriesWritten; 406 } 407 408 /** 409 * This method starts the creation of an origin entry, by setting fields that are the same in every Org Rev origin entries 410 * 411 * @return an OriginEntryFull partially filled out with constant information 412 */ 413 protected OriginEntryFull getEntry() { 414 OriginEntryFull entry = new OriginEntryFull(); 415 entry.setUniversityFiscalYear((Integer) jobParameters.get(KFSConstants.UNIV_FISCAL_YR)); 416 entry.setUniversityFiscalPeriodCode(KFSConstants.MONTH13); 417 entry.setFinancialDocumentTypeCode(DEFAULT_FINANCIAL_DOCUMENT_TYPE_CODE); 418 entry.setFinancialSystemOriginationCode(DEFAULT_FINANCIAL_SYSTEM_ORIGINATION_CODE); 419 entry.setTransactionLedgerEntrySequenceNumber(1); 420 entry.setTransactionDebitCreditCode(KFSConstants.GL_BUDGET_CODE); 421 entry.setTransactionDate((Date) jobParameters.get(KFSConstants.TRANSACTION_DT)); 422 entry.setProjectCode(KFSConstants.getDashProjectCode()); 423 return entry; 424 } 425 426 /** 427 * This method generates cash reversion origin entries for the current organization reversion, and adds them to the given list 428 * 429 * @param originEntriesToWrite a list of OriginEntryFulls to stick generated origin entries into 430 * @throws FatalErrorException thrown if an origin entry's object code can't be found 431 */ 432 public void generateCashReversions(List<OriginEntryFull> originEntriesToWrite) throws FatalErrorException { 433 int entriesWritten = 0; 434 435 // Reversion of cash from the actual account in the fiscal year ending (balance type of NB) 436 OriginEntryFull entry = getEntry(); 437 entry.refreshReferenceObject("option"); 438 439 entry.setChartOfAccountsCode(unitOfWork.chartOfAccountsCode); 440 entry.setAccountNumber(unitOfWork.accountNumber); 441 entry.setSubAccountNumber(unitOfWork.subAccountNumber); 442 entry.setFinancialObjectCode(organizationReversion.getOrganizationChartCashObjectCode()); 443 entry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode()); 444 entry.setFinancialBalanceTypeCode(systemOptions.getNominalFinancialBalanceTypeCd()); 445 446 getPersistenceService().retrieveReferenceObject(entry, KFSPropertyConstants.FINANCIAL_OBJECT); 447 if (ObjectUtils.isNull(entry.getFinancialObject())) { 448 throw new FatalErrorException("Object Code for Entry not found: " + entry); 449 } 450 451 entry.setDocumentNumber(DEFAULT_DOCUMENT_NUMBER_PREFIX + entry.getAccountNumber()); 452 entry.setTransactionLedgerEntryDescription(CASH_REVERTED_TO_MESSAGE + " " + organizationReversion.getCashReversionAccountNumber()); 453 entry.setTransactionLedgerEntryAmount(unitOfWork.getTotalCash()); 454 if (unitOfWork.getTotalCash().compareTo(KualiDecimal.ZERO) > 0) { 455 entry.setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE); 456 } 457 else { 458 entry.setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE); 459 entry.setTransactionLedgerEntryAmount(unitOfWork.getTotalCash().negated()); 460 } 461 entry.setFinancialObjectTypeCode(entry.getFinancialObject().getFinancialObjectTypeCode()); 462 463 // 3468 MOVE TRN-LDGR-ENTR-AMT TO WS-AMT-W-PERIOD 464 // 3469 WS-AMT-N. 465 // 3470 MOVE WS-AMT-X TO TRN-AMT-RED-X. 466 467 originEntriesToWrite.add(entry); 468 469 // Reversion of fund balance, starting with the actual account, to match the cash that was reverted (balance type of NB) 470 entry = getEntry(); 471 entry.setChartOfAccountsCode(unitOfWork.chartOfAccountsCode); 472 entry.setAccountNumber(unitOfWork.accountNumber); 473 entry.setSubAccountNumber(unitOfWork.subAccountNumber); 474 entry.setFinancialObjectCode((String) jobParameters.get(KFSConstants.FUND_BAL_OBJECT_CD)); 475 entry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode()); 476 entry.setFinancialBalanceTypeCode(DEFAULT_FINANCIAL_BALANCE_TYPE_CODE); 477 478 getPersistenceService().retrieveReferenceObject(entry, KFSPropertyConstants.FINANCIAL_OBJECT); 479 if (ObjectUtils.isNull(entry.getFinancialObject())) { 480 throw new FatalErrorException("Object Code for Entry not found: " + entry); 481 } 482 483 entry.setDocumentNumber(DEFAULT_DOCUMENT_NUMBER_PREFIX + unitOfWork.accountNumber); 484 entry.setTransactionLedgerEntryDescription(FUND_BALANCE_REVERTED_TO_MESSAGE + organizationReversion.getCashReversionAccountNumber()); 485 entry.setTransactionLedgerEntryAmount(unitOfWork.getTotalCash().abs()); 486 if (unitOfWork.getTotalCash().compareTo(KualiDecimal.ZERO) > 0) { 487 entry.setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE); 488 } 489 else { 490 entry.setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE); 491 } 492 entry.setFinancialObjectTypeCode(entry.getFinancialObject().getFinancialObjectTypeCode()); 493 494 // 3570 MOVE TRN-LDGR-ENTR-AMT TO WS-AMT-W-PERIOD 495 // 3571 WS-AMT-N. 496 // 3572 MOVE WS-AMT-X TO TRN-AMT-RED-X. 497 498 getFlexibleOffsetAccountService().updateOffset(entry); 499 originEntriesToWrite.add(entry); 500 501 // Reversion of cash to the cash reversion account in the fiscal year ending (balance type of NB) 502 entry = getEntry(); 503 entry.setChartOfAccountsCode(organizationReversion.getCashReversionFinancialChartOfAccountsCode()); 504 entry.setAccountNumber(organizationReversion.getCashReversionAccountNumber()); 505 entry.setSubAccountNumber(KFSConstants.getDashSubAccountNumber()); 506 entry.setFinancialObjectCode(organizationReversion.getCashReversionChartCashObjectCode()); 507 entry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode()); 508 entry.setFinancialBalanceTypeCode(DEFAULT_FINANCIAL_BALANCE_TYPE_CODE); 509 510 getPersistenceService().retrieveReferenceObject(entry, KFSPropertyConstants.FINANCIAL_OBJECT); 511 if (ObjectUtils.isNull(entry.getFinancialObject())) { 512 throw new FatalErrorException("Object Code for Entry not found: " + entry); 513 } 514 515 entry.setDocumentNumber(DEFAULT_DOCUMENT_NUMBER_PREFIX + unitOfWork.accountNumber); 516 entry.setTransactionLedgerEntryDescription(CASH_REVERTED_FROM_MESSAGE + unitOfWork.accountNumber + " " + unitOfWork.subAccountNumber); 517 entry.setTransactionLedgerEntryAmount(unitOfWork.getTotalCash()); 518 if (unitOfWork.getTotalCash().compareTo(KualiDecimal.ZERO) > 0) { 519 entry.setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE); 520 } 521 else { 522 entry.setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE); 523 entry.setTransactionLedgerEntryAmount(unitOfWork.getTotalCash().negated()); 524 } 525 entry.setFinancialObjectTypeCode(entry.getFinancialObject().getFinancialObjectTypeCode()); 526 527 // 3668 MOVE TRN-LDGR-ENTR-AMT TO WS-AMT-W-PERIOD 528 // 3669 WS-AMT-N. 529 // 3670 MOVE WS-AMT-X TO TRN-AMT-RED-X. 530 531 originEntriesToWrite.add(entry); 532 533 // Reversion of fund balance, starting with the cash reversion account, to match the cash that was reverted (balance type of NB) 534 entry = getEntry(); 535 entry.setChartOfAccountsCode(organizationReversion.getCashReversionFinancialChartOfAccountsCode()); 536 entry.setAccountNumber(organizationReversion.getCashReversionAccountNumber()); 537 entry.setSubAccountNumber(KFSConstants.getDashSubAccountNumber()); 538 entry.setFinancialObjectCode((String) jobParameters.get(KFSConstants.FUND_BAL_OBJECT_CD)); 539 entry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode()); 540 entry.setFinancialBalanceTypeCode(DEFAULT_FINANCIAL_BALANCE_TYPE_CODE); 541 542 getPersistenceService().retrieveReferenceObject(entry, KFSPropertyConstants.FINANCIAL_OBJECT); 543 if (ObjectUtils.isNull(entry.getFinancialObject())) { 544 throw new FatalErrorException("Object Code for Entry not found: " + entry); 545 } 546 547 entry.setDocumentNumber(DEFAULT_DOCUMENT_NUMBER_PREFIX + unitOfWork.accountNumber); 548 entry.setTransactionLedgerEntryDescription(FUND_BALANCE_REVERTED_FROM_MESSAGE + unitOfWork.accountNumber + " " + unitOfWork.subAccountNumber); 549 entry.setTransactionLedgerEntryAmount(unitOfWork.getTotalCash()); 550 if (unitOfWork.getTotalCash().compareTo(KualiDecimal.ZERO) > 0) { 551 entry.setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE); 552 } 553 else { 554 entry.setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE); 555 entry.setTransactionLedgerEntryAmount(unitOfWork.getTotalCash().negated()); 556 } 557 entry.setFinancialObjectTypeCode(entry.getFinancialObject().getFinancialObjectTypeCode()); 558 559 // 3768 MOVE TRN-LDGR-ENTR-AMT TO WS-AMT-W-PERIOD 560 // 3769 WS-AMT-N. 561 // 3770 MOVE WS-AMT-X TO TRN-AMT-RED-X. 562 563 getFlexibleOffsetAccountService().updateOffset(entry); 564 originEntriesToWrite.add(entry); 565 } 566 567 /** 568 * Generates carry forward origin entries on a category by category basis (if the organization reversion record asks for that), assuming carry 569 * forwards are required for the current unit of work 570 * 571 * @param originEntriesToWrite a list of origin entries to write, which any generated origin entries should be added to 572 * @throws FatalErrorException thrown if an object code cannot be found 573 */ 574 public void generateMany(List<OriginEntryFull> originEntriesToWrite) throws FatalErrorException { 575 int originEntriesCreated = 0; 576 for (Iterator<OrganizationReversionCategory> iter = categoryList.iterator(); iter.hasNext();) { 577 OrganizationReversionCategory cat = iter.next(); 578 OrganizationReversionCategoryInfo detail = organizationReversion.getOrganizationReversionDetail(cat.getOrganizationReversionCategoryCode()); 579 OrgReversionUnitOfWorkCategoryAmount amount = unitOfWork.amounts.get(cat.getOrganizationReversionCategoryCode()); 580 581 if (!amount.getCarryForward().isZero()) { 582 KualiDecimal commonAmount = amount.getCarryForward(); 583 String commonObject = detail.getOrganizationReversionObjectCode(); 584 585 OriginEntryFull entry = getEntry(); 586 entry.setUniversityFiscalYear((Integer) jobParameters.get(KFSConstants.UNIV_FISCAL_YR) + 1); 587 entry.setChartOfAccountsCode(unitOfWork.chartOfAccountsCode); 588 entry.setAccountNumber(unitOfWork.accountNumber); 589 entry.setSubAccountNumber(unitOfWork.subAccountNumber); 590 entry.setFinancialObjectCode((String) jobParameters.get(KFSConstants.BEG_BUD_CASH_OBJECT_CD)); 591 entry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode()); 592 entry.setFinancialBalanceTypeCode(KFSConstants.BALANCE_TYPE_CURRENT_BUDGET); 593 594 getPersistenceService().retrieveReferenceObject(entry, KFSPropertyConstants.FINANCIAL_OBJECT); 595 if (ObjectUtils.isNull(entry.getFinancialObject())) { 596 throw new FatalErrorException("Object Code for Entry not found: " + entry); 597 } 598 599 ObjectCode objectCode = entry.getFinancialObject(); 600 entry.setFinancialObjectTypeCode(objectCode.getFinancialObjectTypeCode()); 601 entry.setUniversityFiscalPeriodCode(KFSConstants.MONTH1); 602 entry.setDocumentNumber(DEFAULT_DOCUMENT_NUMBER_PREFIX + unitOfWork.accountNumber); 603 entry.setTransactionLedgerEntryDescription(FUND_CARRIED_MESSAGE + (Integer) jobParameters.get(KFSConstants.UNIV_FISCAL_YR)); 604 entry.setTransactionLedgerEntryAmount(commonAmount); 605 606 // 3259 MOVE TRN-LDGR-ENTR-AMT TO WS-AMT-W-PERIOD 607 // 3260 WS-AMT-N. 608 // 3261 MOVE WS-AMT-X TO TRN-AMT-RED-X. 609 610 originEntriesToWrite.add(entry); 611 612 entry = getEntry(); 613 entry.setUniversityFiscalYear((Integer) jobParameters.get(KFSConstants.UNIV_FISCAL_YR) + 1); 614 entry.setChartOfAccountsCode(unitOfWork.chartOfAccountsCode); 615 entry.setAccountNumber(unitOfWork.accountNumber); 616 entry.setSubAccountNumber(unitOfWork.subAccountNumber); 617 618 entry.setFinancialObjectCode(commonObject); 619 entry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode()); 620 entry.setFinancialBalanceTypeCode(KFSConstants.BALANCE_TYPE_CURRENT_BUDGET); 621 622 getPersistenceService().retrieveReferenceObject(entry, KFSPropertyConstants.FINANCIAL_OBJECT); 623 if (ObjectUtils.isNull(entry.getFinancialObject())) { 624 throw new FatalErrorException("Object Code for Entry not found: " + entry); 625 } 626 627 objectCode = entry.getFinancialObject(); 628 entry.setFinancialObjectTypeCode(objectCode.getFinancialObjectTypeCode()); 629 entry.setUniversityFiscalPeriodCode(KFSConstants.MONTH1); 630 entry.setDocumentNumber(DEFAULT_DOCUMENT_NUMBER_PREFIX + unitOfWork.accountNumber); 631 entry.setTransactionLedgerEntryDescription(FUND_CARRIED_MESSAGE + (Integer) jobParameters.get(KFSConstants.UNIV_FISCAL_YR)); 632 entry.setTransactionLedgerEntryAmount(commonAmount); 633 634 // 3343 MOVE TRN-LDGR-ENTR-AMT TO WS-AMT-W-PERIOD 635 // 3344 WS-AMT-N. 636 // 3345 MOVE WS-AMT-X TO TRN-AMT-RED-X. 637 638 originEntriesToWrite.add(entry); 639 } 640 } 641 } 642 643 /** 644 * If carry forwards need to be generated for this unit of work, this method will generate the origin entries to accomplish those object codes. 645 * Note: this will only be called if the organization reversion record tells the process to munge all carry forwards for all categories 646 * together; if the organization reversion record does not call for such a thing, then generateMany will be called 647 * 648 * @param originEntriesToWrite a list of origin entries to write, that any generated origin entries should be added to 649 * @throws FatalErrorException thrown if the current object code can't be found in the database 650 */ 651 public void generateCarryForwards(List<OriginEntryFull> originEntriesToWrite) throws FatalErrorException { 652 int originEntriesWritten = 0; 653 654 OriginEntryFull entry = getEntry(); 655 entry.setUniversityFiscalYear((Integer) jobParameters.get(KFSConstants.UNIV_FISCAL_YR) + 1); 656 entry.setChartOfAccountsCode(unitOfWork.chartOfAccountsCode); 657 entry.setAccountNumber(unitOfWork.accountNumber); 658 entry.setSubAccountNumber(unitOfWork.subAccountNumber); 659 entry.setFinancialObjectCode((String) jobParameters.get(KFSConstants.BEG_BUD_CASH_OBJECT_CD)); 660 entry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode()); 661 entry.setFinancialBalanceTypeCode(KFSConstants.BALANCE_TYPE_CURRENT_BUDGET); 662 663 getPersistenceService().retrieveReferenceObject(entry, KFSPropertyConstants.FINANCIAL_OBJECT); 664 if (ObjectUtils.isNull(entry.getFinancialObject())) { 665 throw new FatalErrorException("Object Code for Entry not found: " + entry); 666 } 667 668 ObjectCode objectCode = entry.getFinancialObject(); 669 entry.setFinancialObjectTypeCode(objectCode.getFinancialObjectTypeCode()); 670 entry.setUniversityFiscalPeriodCode(KFSConstants.MONTH1); 671 entry.setFinancialDocumentTypeCode(DEFAULT_FINANCIAL_DOCUMENT_TYPE_CODE); 672 entry.setFinancialSystemOriginationCode(DEFAULT_FINANCIAL_SYSTEM_ORIGINATION_CODE); 673 entry.setDocumentNumber(DEFAULT_DOCUMENT_NUMBER_PREFIX + unitOfWork.accountNumber); 674 entry.setTransactionLedgerEntrySequenceNumber(1); 675 entry.setTransactionLedgerEntryDescription(FUND_CARRIED_MESSAGE + (Integer) jobParameters.get(KFSConstants.UNIV_FISCAL_YR)); 676 entry.setTransactionLedgerEntryAmount(unitOfWork.getTotalCarryForward()); 677 entry.setTransactionDate((Date) jobParameters.get(KFSConstants.TRANSACTION_DT)); 678 entry.setProjectCode(KFSConstants.getDashProjectCode()); 679 // 2995 MOVE TRN-LDGR-ENTR-AMT TO WS-AMT-W-PERIOD 680 // 2996 WS-AMT-N. 681 // 2997 MOVE WS-AMT-X TO TRN-AMT-RED-X. 682 683 originEntriesToWrite.add(entry); 684 685 entry = getEntry(); 686 entry.setUniversityFiscalYear((Integer) jobParameters.get(KFSConstants.UNIV_FISCAL_YR) + 1); 687 entry.setChartOfAccountsCode(unitOfWork.chartOfAccountsCode); 688 entry.setAccountNumber(unitOfWork.accountNumber); 689 entry.setSubAccountNumber(unitOfWork.subAccountNumber); 690 entry.setFinancialObjectCode((String) jobParameters.get(KFSConstants.UNALLOC_OBJECT_CD)); 691 692 getPersistenceService().retrieveReferenceObject(entry, KFSPropertyConstants.FINANCIAL_OBJECT); 693 if (ObjectUtils.isNull(entry.getFinancialObject())) { 694 throw new FatalErrorException("Object Code for Entry not found: " + entry); 695 } 696 697 objectCode = entry.getFinancialObject(); 698 entry.setFinancialObjectTypeCode(objectCode.getFinancialObjectTypeCode()); 699 entry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode()); 700 entry.setFinancialBalanceTypeCode(KFSConstants.BALANCE_TYPE_CURRENT_BUDGET); 701 entry.setUniversityFiscalPeriodCode(KFSConstants.MONTH1); 702 entry.setDocumentNumber(DEFAULT_DOCUMENT_NUMBER_PREFIX + unitOfWork.accountNumber); 703 entry.setTransactionLedgerEntryDescription(FUND_CARRIED_MESSAGE + (Integer) jobParameters.get(KFSConstants.UNIV_FISCAL_YR)); 704 entry.setTransactionLedgerEntryAmount(unitOfWork.getTotalCarryForward()); 705 706 // 3079 MOVE TRN-LDGR-ENTR-AMT TO WS-AMT-W-PERIOD 707 // 3080 WS-AMT-N. 708 // 3081 MOVE WS-AMT-X TO TRN-AMT-RED-X. 709 710 originEntriesToWrite.add(entry); 711 712 } 713 714 /** 715 * If reversions are necessary, this will generate the origin entries for those reversions 716 * 717 * @param originEntriesToWrite the list of origin entries to add reversions into 718 * @throws FatalErrorException thrown if object code if the entry can't be found 719 */ 720 public void generateReversions(List<OriginEntryFull> originEntriesToWrite) throws FatalErrorException { 721 int originEntriesWritten = 0; 722 723 OriginEntryFull entry = getEntry(); 724 entry.setChartOfAccountsCode(unitOfWork.chartOfAccountsCode); 725 entry.setAccountNumber(unitOfWork.accountNumber); 726 entry.setSubAccountNumber(unitOfWork.subAccountNumber); 727 entry.setFinancialObjectCode((String) jobParameters.get(KFSConstants.UNALLOC_OBJECT_CD)); 728 entry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode()); 729 entry.setFinancialBalanceTypeCode(DEFAULT_FINANCIAL_BALANCE_TYPE_CODE_YEAR_END); 730 731 getPersistenceService().retrieveReferenceObject(entry, KFSPropertyConstants.FINANCIAL_OBJECT); 732 if (ObjectUtils.isNull(entry.getFinancialObject())) { 733 throw new FatalErrorException("Object Code for Entry not found: " + entry); 734 } 735 736 ObjectCode objectCode = entry.getFinancialObject(); 737 entry.setFinancialObjectTypeCode(objectCode.getFinancialObjectTypeCode()); 738 739 entry.setUniversityFiscalPeriodCode(KFSConstants.MONTH13); 740 741 entry.setDocumentNumber(DEFAULT_DOCUMENT_NUMBER_PREFIX + entry.getAccountNumber()); 742 743 entry.setTransactionLedgerEntryDescription(FUND_REVERTED_TO_MESSAGE + organizationReversion.getBudgetReversionAccountNumber()); 744 entry.setTransactionLedgerEntryAmount(unitOfWork.getTotalReversion().negated()); 745 746 originEntriesToWrite.add(entry); 747 748 entry = getEntry(); 749 entry.setChartOfAccountsCode(organizationReversion.getBudgetReversionChartOfAccountsCode()); 750 entry.setAccountNumber(organizationReversion.getBudgetReversionAccountNumber()); 751 entry.setSubAccountNumber(KFSConstants.getDashSubAccountNumber()); 752 entry.setFinancialObjectCode((String) jobParameters.get(KFSConstants.UNALLOC_OBJECT_CD)); 753 entry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode()); 754 entry.setFinancialBalanceTypeCode(DEFAULT_FINANCIAL_BALANCE_TYPE_CODE_YEAR_END); 755 entry.setFinancialObjectTypeCode(objectCode.getFinancialObjectTypeCode()); 756 entry.setUniversityFiscalPeriodCode(KFSConstants.MONTH13); 757 entry.setDocumentNumber(DEFAULT_DOCUMENT_NUMBER_PREFIX + unitOfWork.accountNumber); 758 if (unitOfWork.accountNumber.equals(KFSConstants.getDashSubAccountNumber())) { 759 entry.setTransactionLedgerEntryDescription(FUND_REVERTED_FROM_MESSAGE + unitOfWork.accountNumber); 760 } 761 else { 762 entry.setTransactionLedgerEntryDescription(FUND_REVERTED_FROM_MESSAGE + unitOfWork.accountNumber + " " + unitOfWork.subAccountNumber); 763 } 764 entry.setTransactionLedgerEntryAmount(unitOfWork.getTotalReversion()); 765 766 // 2899 MOVE TRN-LDGR-ENTR-AMT TO WS-AMT-W-PERIOD 767 // 2900 WS-AMT-N. 768 // 2901 MOVE WS-AMT-X TO TRN-AMT-RED-X. 769 770 originEntriesToWrite.add(entry); 771 } 772 773 /** 774 * This method calculates the totals for a given unit of work's reversion 775 * 776 * @throws FatalErrorException 777 */ 778 public void calculateTotals() throws FatalErrorException { 779 /* 780 * How this works: At the start, in the clearCalculationTotals(), both the unit of work's totalAvailable and totalReversion 781 * are set to the available amounts from each of the category amounts. Then, as the logic is applied, the totalCarryForward 782 * is added to and the totalReversion is subtracted from. Let's look at a simple example: Let's say you've got an amount for 783 * C01, which has $2000 available, no encumbrances, that's all you've got. This means that at the end of 784 * clearCalculationTotals(), there's $2000 in totalAvailable, $2000 in totalReversion, and $0 in totalCarryForward. Now, C01, 785 * let's say, is for code A. So, look below at the if that catches Code A. You'll note that it adds the available amount to 786 * totalCarryForward, it's own carryForward, the negated available to totalReversion, and that, done, it sets available to 787 * $0. With our example, that means that $2000 is in totalCarryForward (and in the amount's carryForward), the 788 * totalReversion has been knocked down to $0, and the available is $0. So, carry forward origin entries get created, and 789 * reversions do not. This is also why you don't see a block about calculating R2 totals below...the process has a natural 790 * inclination towards creating R2 (ie, ignore encumbrances and revert all available) entries. 791 */ 792 793 // clear out the unit of work totals we're going to calculate values in, in preperation for applying rules 794 clearCalculationTotals(); 795 796 // For each category, apply the rules 797 for (OrganizationReversionCategory category : categoryList) { 798 String categoryCode = category.getOrganizationReversionCategoryCode(); 799 OrganizationReversionCategoryLogic logic = categories.get(categoryCode); 800 OrgReversionUnitOfWorkCategoryAmount amount = unitOfWork.amounts.get(categoryCode); 801 802 OrganizationReversionCategoryInfo detail = organizationReversion.getOrganizationReversionDetail(categoryCode); 803 804 if (detail == null) { 805 throw new FatalErrorException("Organization Reversion " + organizationReversion.getUniversityFiscalYear() + "-" + organizationReversion.getChartOfAccountsCode() + "-" + organizationReversion.getOrganizationCode() + " does not have a detail for category " + categoryCode); 806 } 807 String ruleCode = detail.getOrganizationReversionCode(); 808 809 if (LOG.isDebugEnabled()) { 810 LOG.debug("Unit of Work: " + unitOfWork.getChartOfAccountsCode() + unitOfWork.getAccountNumber() + unitOfWork.getSubAccountNumber() + ", category " + category.getOrganizationReversionCategoryName() + ": budget = " + amount.getBudget() + "; actual = " + amount.getActual() + "; encumbrance = " + amount.getEncumbrance() + "; available = " + amount.getAvailable() + "; apply rule code " + ruleCode); 811 } 812 813 if (KFSConstants.RULE_CODE_R1.equals(ruleCode) || KFSConstants.RULE_CODE_N1.equals(ruleCode) || KFSConstants.RULE_CODE_C1.equals(ruleCode)) { 814 if (amount.getAvailable().compareTo(KualiDecimal.ZERO) > 0) { // do we have budget left? 815 if (amount.getAvailable().compareTo(amount.getEncumbrance()) > 0) { // is it more than enough to cover our 816 // encumbrances? 817 unitOfWork.addTotalCarryForward(amount.getEncumbrance()); 818 amount.addCarryForward(amount.getEncumbrance()); 819 unitOfWork.addTotalReversion(amount.getEncumbrance().negated()); 820 amount.addAvailable(amount.getEncumbrance().negated()); 821 } 822 else { 823 // there's not enough available left to cover the encumbrances; cover what we can 824 unitOfWork.addTotalCarryForward(amount.getAvailable()); 825 amount.addCarryForward(amount.getAvailable()); 826 unitOfWork.addTotalReversion(amount.getAvailable().negated()); 827 amount.setAvailable(KualiDecimal.ZERO); 828 } 829 } 830 } 831 832 if (KFSConstants.RULE_CODE_A.equals(ruleCode)) { 833 unitOfWork.addTotalCarryForward(amount.getAvailable()); 834 amount.addCarryForward(amount.getAvailable()); 835 unitOfWork.addTotalReversion(amount.getAvailable().negated()); 836 amount.setAvailable(KualiDecimal.ZERO); 837 } 838 839 if (KFSConstants.RULE_CODE_C1.equals(ruleCode) || KFSConstants.RULE_CODE_C2.equals(ruleCode)) { 840 if (amount.getAvailable().compareTo(KualiDecimal.ZERO) > 0) { 841 unitOfWork.addTotalCarryForward(amount.getAvailable()); 842 amount.addCarryForward(amount.getAvailable()); 843 unitOfWork.addTotalReversion(amount.getAvailable().negated()); 844 amount.setAvailable(KualiDecimal.ZERO); 845 } 846 } 847 848 if (KFSConstants.RULE_CODE_N1.equals(ruleCode) || KFSConstants.RULE_CODE_N2.equals(ruleCode)) { 849 if (amount.getAvailable().compareTo(KualiDecimal.ZERO) < 0) { 850 unitOfWork.addTotalCarryForward(amount.getAvailable()); 851 amount.addCarryForward(amount.getAvailable()); 852 unitOfWork.addTotalReversion(amount.getAvailable().negated()); 853 amount.setAvailable(KualiDecimal.ZERO); 854 } 855 } 856 857 if (LOG.isDebugEnabled()) { 858 LOG.debug("Totals Now: " + unitOfWork.getChartOfAccountsCode() + unitOfWork.getAccountNumber() + unitOfWork.getSubAccountNumber() + ", total cash now " + unitOfWork.getTotalCash() + ": total available = " + unitOfWork.getTotalAvailable() + "; total reversion = " + unitOfWork.getTotalReversion() + "; total carry forward = " + unitOfWork.getTotalCarryForward()); 859 } 860 } 861 } 862 863 /** 864 * This method clears the unit of work's amounts to what they should be before each category bucket is calculated; specifically, 865 * the total available for each category is calculated, and the total available and total reversion are set to the sum of all 866 * available from each category bucket. The total carry forward is set to 0. 867 */ 868 protected void clearCalculationTotals() { 869 // Initialize all the amounts before applying the proper rule 870 KualiDecimal totalAvailable = KualiDecimal.ZERO; 871 for (OrganizationReversionCategory category : categoryList) { 872 OrganizationReversionCategoryLogic logic = categories.get(category.getOrganizationReversionCategoryCode()); 873 874 OrgReversionUnitOfWorkCategoryAmount amount = unitOfWork.amounts.get(category.getOrganizationReversionCategoryCode()); 875 if (logic.isExpense()) { 876 amount.setAvailable(amount.getBudget().subtract(amount.getActual())); 877 } 878 else { 879 amount.setAvailable(amount.getActual().subtract(amount.getBudget())); 880 } 881 totalAvailable = totalAvailable.add(amount.getAvailable()); 882 amount.setCarryForward(KualiDecimal.ZERO); 883 } 884 unitOfWork.setTotalAvailable(totalAvailable); 885 unitOfWork.setTotalReversion(totalAvailable); 886 unitOfWork.setTotalCarryForward(KualiDecimal.ZERO); 887 } 888 889 /** 890 * Summarizes the given origin entries to the ledger report 891 * @param originEntries the List of originEntries to summarize 892 */ 893 protected void summarizeOriginEntries(List<OriginEntryFull> originEntries) { 894 for (OriginEntryFull originEntry: originEntries) { 895 ledgerReport.summarizeEntry(originEntry); 896 } 897 } 898 899 public OrgReversionUnitOfWork getUnitOfWork() { 900 return unitOfWork; 901 } 902 903 public void setUnitOfWork(OrgReversionUnitOfWork unitOfWork) { 904 this.unitOfWork = unitOfWork; 905 } 906 907 public List<OrganizationReversionCategory> getCategoryList() { 908 return this.categoryList; 909 } 910 911 /** 912 * Gets the generatedOriginEntries attribute. 913 * 914 * @return Returns the generatedOriginEntries. 915 */ 916 public List<OriginEntryFull> getGeneratedOriginEntries() { 917 return generatedOriginEntries; 918 } 919 920 /** 921 * Sets the holdGeneratedOriginEntries attribute value. 922 * 923 * @param holdGeneratedOriginEntries The holdGeneratedOriginEntries to set. 924 */ 925 public void setHoldGeneratedOriginEntries(boolean holdGeneratedOriginEntries) { 926 this.holdGeneratedOriginEntries = holdGeneratedOriginEntries; 927 this.generatedOriginEntries = new ArrayList<OriginEntryFull>(); 928 } 929 930 /** 931 * Returns the total number of balances for the previous fiscal year 932 * 933 * @return the total number of balances for the previous fiscal year 934 */ 935 public int getBalancesRead() { 936 return organizationReversionCounts.get("balancesRead").intValue(); 937 } 938 939 /** 940 * Returns the total number of balances selected for inclusion in this process 941 * 942 * @return the total number of balances selected for inclusion in this process 943 */ 944 public int getBalancesSelected() { 945 return organizationReversionCounts.get("balancesSelected").intValue(); 946 } 947 948 /** 949 * Returns the total number of origin entries written by this process 950 * 951 * @return the total number of origin entries written by this process 952 */ 953 public int getRecordsWritten() { 954 return organizationReversionCounts.get("recordsWritten").intValue(); 955 } 956 957 /** 958 * Used mainly for unit testing, this method allows a way to change the output group of a org reversion process run 959 * 960 * @param outputGroup 961 */ 962 public void setOutputFileName(String outputFileName) { 963 this.outputFileName = outputFileName; 964 } 965 966 /** 967 * Increments one of the totals held in the count map this process uses for reported statistics 968 * 969 * @param countName the name of the count to increment 970 */ 971 private void incrementCount(String countName) { 972 incrementCount(countName, 1); 973 } 974 975 /** 976 * Increments one of the totals held in the count map this process uses for reported statistics by a given increment 977 * 978 * @param countName the name of the count to increment 979 * @param increment the amount to increment 980 */ 981 protected void incrementCount(String countName, int increment) { 982 Integer count = organizationReversionCounts.get(countName); 983 if (countName.equals("recordsWritten")) { 984 int countAsInt = count.intValue(); 985 // add by 1, so we're guaranteed to hit the 1000th 986 for (int i = 1; i <= increment; i++) { 987 countAsInt += 1; 988 if (countAsInt % 1000 == 0) { 989 LOG.info(" ORIGIN ENTRIES INSERTED = "+countAsInt); 990 } else if (countAsInt == 367471) { 991 LOG.info(" YOU HAVE ACHIEVED 367471 ORIGIN ENTRIES INSERTED! TRIUMPH IS YOURS! "); 992 } 993 } 994 organizationReversionCounts.put(countName, new Integer(countAsInt)); 995 } else { 996 organizationReversionCounts.put(countName, new Integer(count.intValue() + increment)); 997 } 998 } 999 1000 /** 1001 * Writes out the encapsulated origin entry ledger report to the given reportWriterService 1002 * @param reportWriterService the report to write the ledger summary report to 1003 */ 1004 public void writeLedgerSummaryReport(ReportWriterService reportWriterService) { 1005 ledgerReport.writeReport(reportWriterService); 1006 } 1007 1008 /** 1009 * Gets the organizationReversionService attribute. 1010 * @return Returns the organizationReversionService. 1011 */ 1012 public OrganizationReversionService getOrganizationReversionService() { 1013 return organizationReversionService; 1014 } 1015 1016 /** 1017 * Sets the organizationReversionService attribute value. 1018 * @param organizationReversionService The organizationReversionService to set. 1019 */ 1020 public void setOrganizationReversionService(OrganizationReversionService organizationReversionService) { 1021 this.organizationReversionService = organizationReversionService; 1022 } 1023 1024 /** 1025 * Gets the balanceService attribute. 1026 * @return Returns the balanceService. 1027 */ 1028 public BalanceService getBalanceService() { 1029 return balanceService; 1030 } 1031 1032 /** 1033 * Sets the balanceService attribute value. 1034 * @param balanceService The balanceService to set. 1035 */ 1036 public void setBalanceService(BalanceService balanceService) { 1037 this.balanceService = balanceService; 1038 } 1039 1040 /** 1041 * Gets the originEntryService attribute. 1042 * @return Returns the originEntryService. 1043 */ 1044 public OriginEntryService getOriginEntryService() { 1045 return originEntryService; 1046 } 1047 1048 /** 1049 * Sets the originEntryService attribute value. 1050 * @param originEntryService The originEntryService to set. 1051 */ 1052 public void setOriginEntryService(OriginEntryService originEntryService) { 1053 this.originEntryService = originEntryService; 1054 } 1055 1056 /** 1057 * Gets the persistenceService attribute. 1058 * @return Returns the persistenceService. 1059 */ 1060 public PersistenceService getPersistenceService() { 1061 return persistenceService; 1062 } 1063 1064 /** 1065 * Sets the persistenceService attribute value. 1066 * @param persistenceService The persistenceService to set. 1067 */ 1068 public void setPersistenceService(PersistenceService persistenceService) { 1069 this.persistenceService = persistenceService; 1070 } 1071 1072 /** 1073 * Gets the dateTimeService attribute. 1074 * @return Returns the dateTimeService. 1075 */ 1076 public DateTimeService getDateTimeService() { 1077 return dateTimeService; 1078 } 1079 1080 /** 1081 * Sets the dateTimeService attribute value. 1082 * @param dateTimeService The dateTimeService to set. 1083 */ 1084 public void setDateTimeService(DateTimeService dateTimeService) { 1085 this.dateTimeService = dateTimeService; 1086 } 1087 1088 /** 1089 * Gets the priorYearAccountService attribute. 1090 * @return Returns the priorYearAccountService. 1091 */ 1092 public PriorYearAccountService getPriorYearAccountService() { 1093 return priorYearAccountService; 1094 } 1095 1096 /** 1097 * Sets the priorYearAccountService attribute value. 1098 * @param priorYearAccountService The priorYearAccountService to set. 1099 */ 1100 public void setPriorYearAccountService(PriorYearAccountService priorYearAccountService) { 1101 this.priorYearAccountService = priorYearAccountService; 1102 } 1103 1104 /** 1105 * Gets the orgReversionUnitOfWorkService attribute. 1106 * @return Returns the orgReversionUnitOfWorkService. 1107 */ 1108 public OrganizationReversionUnitOfWorkService getOrgReversionUnitOfWorkService() { 1109 return orgReversionUnitOfWorkService; 1110 } 1111 1112 /** 1113 * Sets the orgReversionUnitOfWorkService attribute value. 1114 * @param orgReversionUnitOfWorkService The orgReversionUnitOfWorkService to set. 1115 */ 1116 public void setOrgReversionUnitOfWorkService(OrganizationReversionUnitOfWorkService orgReversionUnitOfWorkService) { 1117 this.orgReversionUnitOfWorkService = orgReversionUnitOfWorkService; 1118 } 1119 1120 /** 1121 * Gets the flexibleOffsetAccountService attribute. 1122 * @return Returns the flexibleOffsetAccountService. 1123 */ 1124 public FlexibleOffsetAccountService getFlexibleOffsetAccountService() { 1125 return flexibleOffsetAccountService; 1126 } 1127 1128 /** 1129 * Sets the flexibleOffsetAccountService attribute value. 1130 * @param flexibleOffsetAccountService The flexibleOffsetAccountService to set. 1131 */ 1132 public void setFlexibleOffsetAccountService(FlexibleOffsetAccountService flexibleOffsetAccountService) { 1133 this.flexibleOffsetAccountService = flexibleOffsetAccountService; 1134 } 1135 1136 /** 1137 * Gets the parameterService attribute. 1138 * @return Returns the parameterService. 1139 */ 1140 public ParameterService getParameterService() { 1141 return parameterService; 1142 } 1143 1144 /** 1145 * Sets the parameterService attribute value. 1146 * @param parameterService The parameterService to set. 1147 */ 1148 public void setParameterService(ParameterService parameterService) { 1149 this.parameterService = parameterService; 1150 } 1151 1152 /** 1153 * Gets the configurationService attribute. 1154 * @return Returns the configurationService. 1155 */ 1156 public KualiConfigurationService getConfigurationService() { 1157 return configurationService; 1158 } 1159 1160 /** 1161 * Sets the configurationService attribute value. 1162 * @param configurationService The configurationService to set. 1163 */ 1164 public void setConfigurationService(KualiConfigurationService configurationService) { 1165 this.configurationService = configurationService; 1166 } 1167 1168 /** 1169 * Gets the usePriorYearInformation attribute. 1170 * @return Returns the usePriorYearInformation. 1171 */ 1172 public boolean isUsePriorYearInformation() { 1173 return usePriorYearInformation; 1174 } 1175 1176 /** 1177 * Sets the usePriorYearInformation attribute value. 1178 * @param usePriorYearInformation The usePriorYearInformation to set. 1179 */ 1180 public void setUsePriorYearInformation(boolean endOfYear) { 1181 this.usePriorYearInformation = endOfYear; 1182 } 1183 1184 /** 1185 * Gets the cashOrganizationReversionCategoryLogic attribute. 1186 * @return Returns the cashOrganizationReversionCategoryLogic. 1187 */ 1188 public OrganizationReversionCategoryLogic getCashOrganizationReversionCategoryLogic() { 1189 return cashOrganizationReversionCategoryLogic; 1190 } 1191 1192 /** 1193 * Sets the cashOrganizationReversionCategoryLogic attribute value. 1194 * @param cashOrganizationReversionCategoryLogic The cashOrganizationReversionCategoryLogic to set. 1195 */ 1196 public void setCashOrganizationReversionCategoryLogic(OrganizationReversionCategoryLogic cashOrganizationReversionCategoryLogic) { 1197 this.cashOrganizationReversionCategoryLogic = cashOrganizationReversionCategoryLogic; 1198 } 1199 1200 1201 /** 1202 * Gets the batchFileDirectoryName attribute. 1203 * @return Returns the batchFileDirectoryName. 1204 */ 1205 public String getBatchFileDirectoryName() { 1206 return batchFileDirectoryName; 1207 } 1208 1209 /** 1210 * Sets the batchFileDirectoryName attribute value. 1211 * @param batchFileDirectoryName The batchFileDirectoryName to set. 1212 */ 1213 public void setBatchFileDirectoryName(String batchFileDirectoryName) { 1214 this.batchFileDirectoryName = batchFileDirectoryName; 1215 } 1216 1217 /** 1218 * Sets the jobParameters attribute value. 1219 * @param jobParameters The jobParameters to set. 1220 */ 1221 public void setJobParameters(Map jobParameters) { 1222 this.jobParameters = jobParameters; 1223 } 1224 1225 /** 1226 * Sets the organizationReversionCounts attribute value. 1227 * @param organizationReversionCounts The organizationReversionCounts to set. 1228 */ 1229 public void setOrganizationReversionCounts(Map<String, Integer> organizationReversionCounts) { 1230 this.organizationReversionCounts = organizationReversionCounts; 1231 } 1232 1233 }