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 }