001 /* 002 * Copyright 2011 The Kuali Foundation. 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.kfs.module.ld.batch.service.impl; 017 018 import java.io.BufferedReader; 019 import java.io.File; 020 import java.io.FileNotFoundException; 021 import java.io.FileReader; 022 import java.io.IOException; 023 import java.io.PrintStream; 024 import java.sql.Date; 025 import java.text.NumberFormat; 026 import java.util.ArrayList; 027 import java.util.Calendar; 028 import java.util.Collection; 029 import java.util.Iterator; 030 import java.util.List; 031 import java.util.Map; 032 import java.util.StringTokenizer; 033 034 import org.apache.commons.io.FileUtils; 035 import org.apache.commons.io.LineIterator; 036 import org.apache.commons.lang.StringUtils; 037 import org.kuali.kfs.coa.businessobject.A21SubAccount; 038 import org.kuali.kfs.coa.businessobject.Account; 039 import org.kuali.kfs.coa.businessobject.BalanceType; 040 import org.kuali.kfs.coa.service.ObjectCodeService; 041 import org.kuali.kfs.coa.service.OffsetDefinitionService; 042 import org.kuali.kfs.gl.GeneralLedgerConstants; 043 import org.kuali.kfs.gl.ObjectHelper; 044 import org.kuali.kfs.gl.batch.BatchSortUtil; 045 import org.kuali.kfs.gl.batch.ScrubberStep; 046 import org.kuali.kfs.gl.businessobject.DemergerReportData; 047 import org.kuali.kfs.gl.businessobject.OriginEntryGroup; 048 import org.kuali.kfs.gl.businessobject.OriginEntryStatistics; 049 import org.kuali.kfs.gl.businessobject.Transaction; 050 import org.kuali.kfs.gl.report.LedgerSummaryReport; 051 import org.kuali.kfs.gl.report.PreScrubberReport; 052 import org.kuali.kfs.gl.report.PreScrubberReportData; 053 import org.kuali.kfs.gl.report.TransactionListingReport; 054 import org.kuali.kfs.gl.service.OriginEntryGroupService; 055 import org.kuali.kfs.gl.service.PreScrubberService; 056 import org.kuali.kfs.gl.service.ScrubberReportData; 057 import org.kuali.kfs.gl.service.ScrubberValidator; 058 import org.kuali.kfs.module.ld.LaborConstants; 059 import org.kuali.kfs.module.ld.batch.LaborScrubberSortComparator; 060 import org.kuali.kfs.module.ld.batch.LaborScrubberStep; 061 import org.kuali.kfs.module.ld.batch.service.LaborAccountingCycleCachingService; 062 import org.kuali.kfs.module.ld.businessobject.LaborOriginEntry; 063 import org.kuali.kfs.module.ld.businessobject.LaborOriginEntryFieldUtil; 064 import org.kuali.kfs.module.ld.service.LaborOriginEntryService; 065 import org.kuali.kfs.module.ld.util.FilteringLaborOriginEntryFileIterator; 066 import org.kuali.kfs.module.ld.util.LaborOriginEntryFileIterator; 067 import org.kuali.kfs.module.ld.util.FilteringLaborOriginEntryFileIterator.LaborOriginEntryFilter; 068 import org.kuali.kfs.sys.KFSKeyConstants; 069 import org.kuali.kfs.sys.KFSPropertyConstants; 070 import org.kuali.kfs.sys.Message; 071 import org.kuali.kfs.sys.KFSParameterKeyConstants.LdParameterConstants; 072 import org.kuali.kfs.sys.batch.service.WrappingBatchService; 073 import org.kuali.kfs.sys.businessobject.UniversityDate; 074 import org.kuali.kfs.sys.context.SpringContext; 075 import org.kuali.kfs.sys.dataaccess.UniversityDateDao; 076 import org.kuali.kfs.sys.service.DocumentNumberAwareReportWriterService; 077 import org.kuali.kfs.sys.service.FlexibleOffsetAccountService; 078 import org.kuali.kfs.sys.service.ReportWriterService; 079 import org.kuali.rice.kns.service.DateTimeService; 080 import org.kuali.rice.kns.service.KualiConfigurationService; 081 import org.kuali.rice.kns.service.ParameterEvaluator; 082 import org.kuali.rice.kns.service.ParameterService; 083 import org.kuali.rice.kns.service.PersistenceService; 084 import org.kuali.rice.kns.util.KualiDecimal; 085 import org.kuali.rice.kns.util.ObjectUtils; 086 087 /** 088 * This class has the logic for the scrubber. It is required because the scrubber process needs instance variables. Instance 089 * variables in a spring service are shared between all code calling the service. This will make sure each run of the scrubber has 090 * it's own instance variables instead of being shared. 091 */ 092 public class LaborScrubberProcess { 093 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(LaborScrubberProcess.class); 094 095 // 40 spaces - used for filling in descriptions with spaces 096 private static String SPACES = " "; 097 098 /* Services required */ 099 private FlexibleOffsetAccountService flexibleOffsetAccountService; 100 private LaborOriginEntryService laborOriginEntryService; 101 private OriginEntryGroupService originEntryGroupService; 102 private DateTimeService dateTimeService; 103 private OffsetDefinitionService offsetDefinitionService; 104 private ObjectCodeService objectCodeService; 105 private KualiConfigurationService kualiConfigurationService; 106 private UniversityDateDao universityDateDao; 107 private PersistenceService persistenceService; 108 private ScrubberValidator scrubberValidator; 109 private LaborAccountingCycleCachingService laborAccountingCycleCachingService; 110 private PreScrubberService laborPreScrubberService; 111 112 private DocumentNumberAwareReportWriterService laborMainReportWriterService; 113 private DocumentNumberAwareReportWriterService laborLedgerReportWriterService; 114 private ReportWriterService laborBadBalanceTypeReportWriterService; 115 private ReportWriterService laborErrorListingReportWriterService; 116 private DocumentNumberAwareReportWriterService laborGeneratedTransactionsReportWriterService; 117 private ReportWriterService laborDemergerReportWriterService; 118 private DocumentNumberAwareReportWriterService laborPreScrubberReportWriterService; 119 private ParameterService parameterService; 120 121 private String batchFileDirectoryName; 122 123 enum GROUP_TYPE { 124 VALID, ERROR, EXPIRED 125 } 126 127 /* These are all different forms of the run date for this job */ 128 private Date runDate; 129 private Calendar runCal; 130 private UniversityDate universityRunDate; 131 private String offsetString; 132 133 /* 134 * These fields are used to control whether the job was run before some set time, if so, the rundate of the job will be set to 135 * 11:59 PM of the previous day 136 */ 137 private Integer cutoffHour; 138 private Integer cutoffMinute; 139 private Integer cutoffSecond; 140 141 /* These are the output groups */ 142 private OriginEntryGroup validGroup; 143 private OriginEntryGroup errorGroup; 144 private OriginEntryGroup expiredGroup; 145 146 /* Unit Of Work info */ 147 private UnitOfWorkInfo unitOfWork; 148 private KualiDecimal scrubCostShareAmount; 149 private ScrubberReportData scrubberReport; 150 151 /* Description names */ 152 private String offsetDescription; 153 private String capitalizationDescription; 154 private String liabilityDescription; 155 private String transferDescription; 156 private String costShareDescription; 157 158 private String inputFile; 159 private String validFile; 160 private String errorFile; 161 private String expiredFile; 162 163 /** 164 * These parameters are all the dependencies. 165 */ 166 public LaborScrubberProcess(FlexibleOffsetAccountService flexibleOffsetAccountService, 167 LaborAccountingCycleCachingService laborAccountingCycleCachingService, 168 LaborOriginEntryService laborOriginEntryService, 169 OriginEntryGroupService originEntryGroupService, 170 DateTimeService dateTimeService, 171 OffsetDefinitionService offsetDefinitionService, 172 ObjectCodeService objectCodeService, 173 KualiConfigurationService kualiConfigurationService, 174 UniversityDateDao universityDateDao, 175 PersistenceService persistenceService, 176 ScrubberValidator scrubberValidator, 177 String batchFileDirectoryName, 178 DocumentNumberAwareReportWriterService laborMainReportWriterService, 179 DocumentNumberAwareReportWriterService laborLedgerReportWriterService, 180 ReportWriterService laborBadBalanceTypeReportWriterService, 181 ReportWriterService laborErrorListingReportWriterService, 182 DocumentNumberAwareReportWriterService laborGeneratedTransactionsReportWriterService, 183 ReportWriterService laborDemergerReportWriterService, 184 PreScrubberService laborPreScrubberService, 185 DocumentNumberAwareReportWriterService laborPreScrubberReportWriterService, 186 ParameterService parameterService) { 187 super(); 188 this.flexibleOffsetAccountService = flexibleOffsetAccountService; 189 this.laborAccountingCycleCachingService = laborAccountingCycleCachingService; 190 this.laborOriginEntryService = laborOriginEntryService; 191 this.originEntryGroupService = originEntryGroupService; 192 this.dateTimeService = dateTimeService; 193 this.offsetDefinitionService = offsetDefinitionService; 194 this.objectCodeService = objectCodeService; 195 this.kualiConfigurationService = kualiConfigurationService; 196 this.universityDateDao = universityDateDao; 197 this.persistenceService = persistenceService; 198 this.scrubberValidator = scrubberValidator; 199 this.batchFileDirectoryName = batchFileDirectoryName; 200 this.laborMainReportWriterService = laborMainReportWriterService; 201 this.laborLedgerReportWriterService = laborLedgerReportWriterService; 202 this.laborBadBalanceTypeReportWriterService = laborBadBalanceTypeReportWriterService; 203 this.laborErrorListingReportWriterService = laborErrorListingReportWriterService; 204 this.laborGeneratedTransactionsReportWriterService = laborGeneratedTransactionsReportWriterService; 205 this.laborDemergerReportWriterService = laborDemergerReportWriterService; 206 this.laborPreScrubberService = laborPreScrubberService; 207 this.laborPreScrubberReportWriterService = laborPreScrubberReportWriterService; 208 this.parameterService = parameterService; 209 210 cutoffHour = null; 211 cutoffMinute = null; 212 cutoffSecond = null; 213 214 initCutoffTime(); 215 } 216 217 /** 218 * Scrub this single group read only. This will only output the scrubber report. It won't output any other groups. 219 * 220 * @param group 221 */ 222 public void scrubGroupReportOnly(String fileName, String documentNumber) { 223 String unsortedFile = fileName; 224 this.inputFile = fileName + ".sort"; 225 this.validFile = batchFileDirectoryName + File.separator + LaborConstants.BatchFileSystem.SCRUBBER_VALID_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION; 226 this.errorFile = batchFileDirectoryName + File.separator + LaborConstants.BatchFileSystem.SCRUBBER_ERROR_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION; 227 this.expiredFile = batchFileDirectoryName + File.separator + LaborConstants.BatchFileSystem.SCRUBBER_EXPIRED_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION; 228 String prescrubOutput = batchFileDirectoryName + File.separator + LaborConstants.BatchFileSystem.PRE_SCRUBBER_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION; 229 230 PreScrubberReportData preScrubberReportData = null; 231 // run pre-scrubber on the raw input into the sort process 232 LineIterator inputEntries = null; 233 try { 234 inputEntries = FileUtils.lineIterator(new File(unsortedFile)); 235 preScrubberReportData = laborPreScrubberService.preprocessOriginEntries(inputEntries, prescrubOutput); 236 } 237 catch (IOException e1) { 238 LOG.error("Error encountered trying to prescrub GLCP/LLCP document", e1); 239 throw new RuntimeException("Error encountered trying to prescrub GLCP/LLCP document", e1); 240 } 241 finally { 242 LineIterator.closeQuietly(inputEntries); 243 } 244 if (preScrubberReportData != null) { 245 laborPreScrubberReportWriterService.setDocumentNumber(documentNumber); 246 ((WrappingBatchService)laborPreScrubberReportWriterService).initialize(); 247 try { 248 new PreScrubberReport().generateReport(preScrubberReportData, laborPreScrubberReportWriterService); 249 } 250 finally { 251 ((WrappingBatchService)laborPreScrubberReportWriterService).destroy(); 252 } 253 } 254 BatchSortUtil.sortTextFileWithFields(prescrubOutput, inputFile, new LaborScrubberSortComparator()); 255 256 scrubEntries(true, documentNumber); 257 258 File deleteSortFile = new File(inputFile); 259 File deleteValidFile = new File(validFile); 260 File deleteErrorFile = new File(errorFile); 261 File deleteExpiredFile = new File(expiredFile); 262 try { 263 deleteSortFile.delete(); 264 deleteValidFile.delete(); 265 deleteErrorFile.delete(); 266 deleteExpiredFile.delete(); 267 } 268 catch (Exception e) { 269 LOG.error("scrubGroupReportOnly delete output files process Stopped: " + e.getMessage()); 270 throw new RuntimeException("scrubGroupReportOnly delete output files process Stopped: " + e.getMessage(), e); 271 } 272 } 273 274 public void scrubEntries() { 275 this.inputFile = batchFileDirectoryName + File.separator + LaborConstants.BatchFileSystem.SCRUBBER_INPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION; 276 this.validFile = batchFileDirectoryName + File.separator + LaborConstants.BatchFileSystem.SCRUBBER_VALID_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION; 277 this.errorFile = batchFileDirectoryName + File.separator + LaborConstants.BatchFileSystem.SCRUBBER_ERROR_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION; 278 this.expiredFile = batchFileDirectoryName + File.separator + LaborConstants.BatchFileSystem.SCRUBBER_EXPIRED_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION; 279 280 scrubEntries(false, null); 281 } 282 283 /** 284 * Scrub all entries that need it in origin entry. Put valid scrubbed entries in a scrubber valid group, put errors in a 285 * scrubber error group, and transactions with an expired account in the scrubber expired account group. 286 */ 287 public void scrubEntries(boolean reportOnlyMode, String documentNumber) { 288 LOG.debug("scrubEntries() started"); 289 290 if (reportOnlyMode) { 291 laborMainReportWriterService.setDocumentNumber(documentNumber); 292 laborLedgerReportWriterService.setDocumentNumber(documentNumber); 293 laborGeneratedTransactionsReportWriterService.setDocumentNumber(documentNumber); 294 } 295 296 // setup an object to hold the "default" date information 297 runDate = calculateRunDate(dateTimeService.getCurrentDate()); 298 runCal = Calendar.getInstance(); 299 runCal.setTime(runDate); 300 301 universityRunDate = laborAccountingCycleCachingService.getUniversityDate(runDate); 302 if (universityRunDate == null) { 303 throw new IllegalStateException(kualiConfigurationService.getPropertyString(KFSKeyConstants.ERROR_UNIV_DATE_NOT_FOUND)); 304 } 305 setOffsetString(); 306 setDescriptions(); 307 308 try { 309 ((WrappingBatchService) laborMainReportWriterService).initialize(); 310 ((WrappingBatchService) laborLedgerReportWriterService).initialize(); 311 if (reportOnlyMode) { 312 ((WrappingBatchService) laborGeneratedTransactionsReportWriterService).initialize(); 313 } 314 315 scrubberReport = new ScrubberReportData(); 316 processGroup(); 317 318 // Run the reports 319 if (reportOnlyMode) { 320 generateScrubberTransactionsOnline(); 321 } 322 else { 323 generateScrubberBadBalanceTypeListingReport(); 324 } 325 } 326 finally { 327 ((WrappingBatchService) laborMainReportWriterService).destroy(); 328 ((WrappingBatchService) laborLedgerReportWriterService).destroy(); 329 if (reportOnlyMode) { 330 ((WrappingBatchService) laborGeneratedTransactionsReportWriterService).destroy(); 331 } 332 } 333 } 334 335 /** 336 * Determine the type of the transaction by looking at attributes 337 * 338 * @param transaction Transaction to identify 339 * @return CE (Cost share encumbrance, O (Offset), C (apitalization), L (Liability), T (Transfer), CS (Cost Share), X (Other) 340 */ 341 protected String getTransactionType(LaborOriginEntry transaction) { 342 if ("CE".equals(transaction.getFinancialBalanceTypeCode())) { 343 return "CE"; 344 } 345 String desc = transaction.getTransactionLedgerEntryDescription(); 346 347 if (desc == null) { 348 return "X"; 349 } 350 351 if (desc.startsWith(offsetDescription) && desc.indexOf("***") > -1) { 352 return "CS"; 353 } 354 if (desc.startsWith(costShareDescription) && desc.indexOf("***") > -1) { 355 return "CS"; 356 } 357 if (desc.startsWith(offsetDescription)) { 358 return "O"; 359 } 360 if (desc.startsWith(capitalizationDescription)) { 361 return "C"; 362 } 363 if (desc.startsWith(liabilityDescription)) { 364 return "L"; 365 } 366 if (desc.startsWith(transferDescription)) { 367 return "T"; 368 } 369 return "X"; 370 } 371 372 /** 373 * This will process a group of origin entries. The COBOL code was refactored a lot to get this so there isn't a 1 to 1 section 374 * of Cobol relating to this. 375 * 376 * @param originEntryGroup Group to process 377 */ 378 protected void processGroup() { 379 ParameterService parameterService = SpringContext.getBean(ParameterService.class); 380 LaborOriginEntry lastEntry = null; 381 scrubCostShareAmount = KualiDecimal.ZERO; 382 unitOfWork = new UnitOfWorkInfo(); 383 FileReader INPUT_GLE_FILE = null; 384 String GLEN_RECORD; 385 BufferedReader INPUT_GLE_FILE_br; 386 PrintStream OUTPUT_GLE_FILE_ps; 387 PrintStream OUTPUT_ERR_FILE_ps; 388 PrintStream OUTPUT_EXP_FILE_ps; 389 try { 390 INPUT_GLE_FILE = new FileReader(inputFile); 391 } 392 catch (FileNotFoundException e) { 393 throw new RuntimeException("Unable to find input file: " + inputFile, e); 394 } 395 try { 396 OUTPUT_GLE_FILE_ps = new PrintStream(validFile); 397 OUTPUT_ERR_FILE_ps = new PrintStream(errorFile); 398 OUTPUT_EXP_FILE_ps = new PrintStream(expiredFile); 399 } 400 catch (IOException e) { 401 throw new RuntimeException("Problem opening output files", e); 402 } 403 404 INPUT_GLE_FILE_br = new BufferedReader(INPUT_GLE_FILE); 405 LOG.info("Starting Scrubber Process process group..."); 406 407 int lineNumber = 0; 408 int loadedCount = 0; 409 boolean errorsLoading = false; 410 411 LedgerSummaryReport laborLedgerSummaryReport = new LedgerSummaryReport(); 412 LaborOriginEntry unscrubbedEntry = new LaborOriginEntry(); 413 List<Message> tmperrors = new ArrayList<Message>(); 414 try { 415 String currentLine = INPUT_GLE_FILE_br.readLine(); 416 417 while (currentLine != null) { 418 boolean saveErrorTransaction = false; 419 boolean saveValidTransaction = false; 420 LaborOriginEntry scrubbedEntry = new LaborOriginEntry(); 421 try { 422 lineNumber++; 423 424 if (!StringUtils.isEmpty(currentLine) && !StringUtils.isBlank(currentLine.trim())) { 425 unscrubbedEntry = new LaborOriginEntry(); 426 tmperrors = unscrubbedEntry.setFromTextFileForBatch(currentLine, lineNumber); 427 loadedCount++; 428 429 // just test entry with the entry loaded above 430 scrubberReport.incrementUnscrubbedRecordsRead(); 431 List<Message> transactionErrors = new ArrayList<Message>(); 432 433 // This is done so if the code modifies this row, then saves it, it will be an insert, 434 // and it won't touch the original. The Scrubber never modifies input rows/groups. 435 unscrubbedEntry.setGroup(null); 436 unscrubbedEntry.setVersionNumber(null); 437 unscrubbedEntry.setEntryId(null); 438 saveErrorTransaction = false; 439 saveValidTransaction = false; 440 441 // Build a scrubbed entry 442 // Labor has more fields 443 buildScrubbedEntry(unscrubbedEntry, scrubbedEntry); 444 445 // For Labor Scrubber 446 boolean laborIndicator = true; 447 laborLedgerSummaryReport.summarizeEntry(unscrubbedEntry); 448 449 try { 450 tmperrors.addAll(scrubberValidator.validateTransaction(unscrubbedEntry, scrubbedEntry, universityRunDate, laborIndicator, laborAccountingCycleCachingService)); 451 } 452 catch (Exception e) { 453 transactionErrors.add(new Message(e.toString() + " occurred for this record.", Message.TYPE_FATAL)); 454 saveValidTransaction = false; 455 } 456 transactionErrors.addAll(tmperrors); 457 458 // Expired account? 459 Account unscrubbedEntryAccount = laborAccountingCycleCachingService.getAccount(unscrubbedEntry.getChartOfAccountsCode(), unscrubbedEntry.getAccountNumber()); 460 if (ObjectUtils.isNotNull(unscrubbedEntry.getAccount()) && (scrubberValidator.isAccountExpired(unscrubbedEntryAccount, universityRunDate) || unscrubbedEntryAccount.isClosed())) { 461 // Make a copy of it so OJB doesn't just update the row in the original 462 // group. It needs to make a new one in the expired group 463 LaborOriginEntry expiredEntry = new LaborOriginEntry(scrubbedEntry); 464 465 createOutputEntry(expiredEntry, OUTPUT_EXP_FILE_ps); 466 scrubberReport.incrementExpiredAccountFound(); 467 } 468 469 if (!isFatal(transactionErrors)) { 470 saveValidTransaction = true; 471 472 // See if unit of work has changed 473 if (!unitOfWork.isSameUnitOfWork(scrubbedEntry)) { 474 // Generate offset for last unit of work 475 unitOfWork = new UnitOfWorkInfo(scrubbedEntry); 476 } 477 KualiDecimal transactionAmount = scrubbedEntry.getTransactionLedgerEntryAmount(); 478 ParameterEvaluator offsetFiscalPeriods = SpringContext.getBean(ParameterService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.OFFSET_FISCAL_PERIOD_CODES, scrubbedEntry.getUniversityFiscalPeriodCode()); 479 BalanceType scrubbedEntryBalanceType = laborAccountingCycleCachingService.getBalanceType(scrubbedEntry.getFinancialBalanceTypeCode()); 480 if (scrubbedEntryBalanceType.isFinancialOffsetGenerationIndicator() && offsetFiscalPeriods.evaluationSucceeds()) { 481 if (scrubbedEntry.isDebit()) { 482 unitOfWork.offsetAmount = unitOfWork.offsetAmount.add(transactionAmount); 483 } 484 else { 485 unitOfWork.offsetAmount = unitOfWork.offsetAmount.subtract(transactionAmount); 486 } 487 } 488 489 // The sub account type code will only exist if there is a valid sub account 490 // TODO: GLConstants.getSpaceSubAccountTypeCode(); 491 String subAccountTypeCode = " "; 492 493 A21SubAccount scrubbedEntryA21SubAccount = laborAccountingCycleCachingService.getA21SubAccount(scrubbedEntry.getChartOfAccountsCode(), scrubbedEntry.getAccountNumber(), scrubbedEntry.getSubAccountNumber()); 494 if (ObjectUtils.isNotNull(scrubbedEntryA21SubAccount)) { 495 subAccountTypeCode = scrubbedEntryA21SubAccount.getSubAccountTypeCode(); 496 } 497 498 if (transactionErrors.size() > 0) { 499 this.laborMainReportWriterService.writeError(unscrubbedEntry, transactionErrors); 500 } 501 502 lastEntry = scrubbedEntry; 503 } 504 else { 505 // Error transaction 506 saveErrorTransaction = true; 507 this.laborMainReportWriterService.writeError(unscrubbedEntry, transactionErrors); 508 } 509 510 511 if (saveValidTransaction) { 512 scrubbedEntry.setTransactionScrubberOffsetGenerationIndicator(false); 513 createOutputEntry(scrubbedEntry, OUTPUT_GLE_FILE_ps); 514 scrubberReport.incrementScrubbedRecordWritten(); 515 } 516 517 if (saveErrorTransaction) { 518 // Make a copy of it so OJB doesn't just update the row in the original 519 // group. It needs to make a new one in the error group 520 LaborOriginEntry errorEntry = new LaborOriginEntry(unscrubbedEntry); 521 errorEntry.setTransactionScrubberOffsetGenerationIndicator(false); 522 createOutputEntry(currentLine, OUTPUT_ERR_FILE_ps); 523 scrubberReport.incrementErrorRecordWritten(); 524 } 525 } 526 currentLine = INPUT_GLE_FILE_br.readLine(); 527 528 } 529 catch (IOException ioe) { 530 // catch here again, it should be from postSingleEntryIntoLaborLedger 531 LOG.error("processGroup() stopped due to: " + ioe.getMessage() + " on line number : " + loadedCount, ioe); 532 throw new RuntimeException("processGroup() stopped due to: " + ioe.getMessage() + " on line number : " + loadedCount, ioe); 533 } 534 } 535 INPUT_GLE_FILE_br.close(); 536 INPUT_GLE_FILE.close(); 537 OUTPUT_GLE_FILE_ps.close(); 538 OUTPUT_ERR_FILE_ps.close(); 539 OUTPUT_EXP_FILE_ps.close(); 540 541 this.laborMainReportWriterService.writeStatisticLine("UNSCRUBBED RECORDS READ %,9d", scrubberReport.getNumberOfUnscrubbedRecordsRead()); 542 this.laborMainReportWriterService.writeStatisticLine("SCRUBBED RECORDS WRITTEN %,9d", scrubberReport.getNumberOfScrubbedRecordsWritten()); 543 this.laborMainReportWriterService.writeStatisticLine("ERROR RECORDS WRITTEN %,9d", scrubberReport.getNumberOfErrorRecordsWritten()); 544 this.laborMainReportWriterService.writeStatisticLine("TOTAL OUTPUT RECORDS WRITTEN %,9d", scrubberReport.getTotalNumberOfRecordsWritten()); 545 this.laborMainReportWriterService.writeStatisticLine("EXPIRED ACCOUNTS FOUND %,9d", scrubberReport.getNumberOfExpiredAccountsFound()); 546 547 laborLedgerSummaryReport.writeReport(this.laborLedgerReportWriterService); 548 } 549 catch (IOException ioe) { 550 LOG.error("processGroup() stopped due to: " + ioe.getMessage(), ioe); 551 throw new RuntimeException("processGroup() stopped due to: " + ioe.getMessage(), ioe); 552 } 553 } 554 555 556 protected boolean isFatal(List<Message> errors) { 557 for (Iterator<Message> iter = errors.iterator(); iter.hasNext();) { 558 Message element = iter.next(); 559 if (element.getType() == Message.TYPE_FATAL) { 560 return true; 561 } 562 } 563 return false; 564 } 565 566 /** 567 * Get all the transaction descriptions from the param table 568 */ 569 protected void setDescriptions() { 570 offsetDescription = kualiConfigurationService.getPropertyString(KFSKeyConstants.MSG_GENERATED_OFFSET); 571 capitalizationDescription = kualiConfigurationService.getPropertyString(KFSKeyConstants.MSG_GENERATED_CAPITALIZATION); 572 liabilityDescription = kualiConfigurationService.getPropertyString(KFSKeyConstants.MSG_GENERATED_LIABILITY); 573 costShareDescription = kualiConfigurationService.getPropertyString(KFSKeyConstants.MSG_GENERATED_COST_SHARE); 574 transferDescription = kualiConfigurationService.getPropertyString(KFSKeyConstants.MSG_GENERATED_TRANSFER); 575 } 576 577 /** 578 * Generate the flag for the end of specific descriptions. This will be used in the demerger step 579 */ 580 protected void setOffsetString() { 581 582 NumberFormat nf = NumberFormat.getInstance(); 583 nf.setMaximumFractionDigits(0); 584 nf.setMaximumIntegerDigits(2); 585 nf.setMinimumFractionDigits(0); 586 nf.setMinimumIntegerDigits(2); 587 588 offsetString = "***" + nf.format(runCal.get(Calendar.MONTH) + 1) + nf.format(runCal.get(Calendar.DAY_OF_MONTH)); 589 } 590 591 /** 592 * Generate the offset message with the flag at the end 593 * 594 * @return Offset message 595 */ 596 protected String getOffsetMessage() { 597 String msg = offsetDescription + SPACES; 598 599 return msg.substring(0, 33) + offsetString; 600 } 601 602 class UnitOfWorkInfo { 603 // Unit of work key 604 public Integer univFiscalYr = 0; 605 public String finCoaCd = ""; 606 public String accountNbr = ""; 607 public String subAcctNbr = ""; 608 public String finBalanceTypCd = ""; 609 public String fdocTypCd = ""; 610 public String fsOriginCd = ""; 611 public String fdocNbr = ""; 612 public Date fdocReversalDt = new Date(dateTimeService.getCurrentDate().getTime()); 613 public String univFiscalPrdCd = ""; 614 615 // Data about unit of work 616 public boolean entryMode = true; 617 public KualiDecimal offsetAmount = KualiDecimal.ZERO; 618 public String scrbFinCoaCd; 619 public String scrbAccountNbr; 620 621 public UnitOfWorkInfo() { 622 } 623 624 public UnitOfWorkInfo(LaborOriginEntry e) { 625 univFiscalYr = e.getUniversityFiscalYear(); 626 finCoaCd = e.getChartOfAccountsCode(); 627 accountNbr = e.getAccountNumber(); 628 subAcctNbr = e.getSubAccountNumber(); 629 finBalanceTypCd = e.getFinancialBalanceTypeCode(); 630 fdocTypCd = e.getFinancialDocumentTypeCode(); 631 fsOriginCd = e.getFinancialSystemOriginationCode(); 632 fdocNbr = e.getDocumentNumber(); 633 fdocReversalDt = e.getFinancialDocumentReversalDate(); 634 univFiscalPrdCd = e.getUniversityFiscalPeriodCode(); 635 } 636 637 public boolean isSameUnitOfWork(LaborOriginEntry e) { 638 // Compare the key fields 639 return univFiscalYr.equals(e.getUniversityFiscalYear()) && finCoaCd.equals(e.getChartOfAccountsCode()) && accountNbr.equals(e.getAccountNumber()) && subAcctNbr.equals(e.getSubAccountNumber()) && finBalanceTypCd.equals(e.getFinancialBalanceTypeCode()) && fdocTypCd.equals(e.getFinancialDocumentTypeCode()) && fsOriginCd.equals(e.getFinancialSystemOriginationCode()) && fdocNbr.equals(e.getDocumentNumber()) && ObjectHelper.isEqual(fdocReversalDt, e.getFinancialDocumentReversalDate()) && univFiscalPrdCd.equals(e.getUniversityFiscalPeriodCode()); 640 } 641 642 public String toString() { 643 return univFiscalYr + finCoaCd + accountNbr + subAcctNbr + finBalanceTypCd + fdocTypCd + fsOriginCd + fdocNbr + fdocReversalDt + univFiscalPrdCd; 644 } 645 646 public LaborOriginEntry getOffsetTemplate() { 647 LaborOriginEntry e = new LaborOriginEntry(); 648 e.setUniversityFiscalYear(univFiscalYr); 649 e.setChartOfAccountsCode(finCoaCd); 650 e.setAccountNumber(accountNbr); 651 e.setSubAccountNumber(subAcctNbr); 652 e.setFinancialBalanceTypeCode(finBalanceTypCd); 653 e.setFinancialDocumentTypeCode(fdocTypCd); 654 e.setFinancialSystemOriginationCode(fsOriginCd); 655 e.setDocumentNumber(fdocNbr); 656 e.setFinancialDocumentReversalDate(fdocReversalDt); 657 e.setUniversityFiscalPeriodCode(univFiscalPrdCd); 658 return e; 659 } 660 } 661 662 class TransactionError { 663 public Transaction transaction; 664 public Message message; 665 666 public TransactionError(Transaction t, Message m) { 667 transaction = t; 668 message = m; 669 } 670 } 671 672 protected void setCutoffTimeForPreviousDay(int hourOfDay, int minuteOfDay, int secondOfDay) { 673 this.cutoffHour = hourOfDay; 674 this.cutoffMinute = minuteOfDay; 675 this.cutoffSecond = secondOfDay; 676 677 LOG.info("Setting cutoff time to hour: " + hourOfDay + ", minute: " + minuteOfDay + ", second: " + secondOfDay); 678 } 679 680 protected void setCutoffTime(String cutoffTime) { 681 if (StringUtils.isBlank(cutoffTime)) { 682 LOG.debug("Cutoff time is blank"); 683 unsetCutoffTimeForPreviousDay(); 684 } 685 else { 686 cutoffTime = cutoffTime.trim(); 687 LOG.debug("Cutoff time value found: " + cutoffTime); 688 StringTokenizer st = new StringTokenizer(cutoffTime, ":", false); 689 690 try { 691 String hourStr = st.nextToken(); 692 String minuteStr = st.nextToken(); 693 String secondStr = st.nextToken(); 694 695 int hourInt = Integer.parseInt(hourStr, 10); 696 int minuteInt = Integer.parseInt(minuteStr, 10); 697 int secondInt = Integer.parseInt(secondStr, 10); 698 699 if (hourInt < 0 || hourInt > 23 || minuteInt < 0 || minuteInt > 59 || secondInt < 0 || secondInt > 59) { 700 throw new IllegalArgumentException("Cutoff time must be in the format \"HH:mm:ss\", where HH, mm, ss are defined in the java.text.SimpleDateFormat class. In particular, 0 <= hour <= 23, 0 <= minute <= 59, and 0 <= second <= 59"); 701 } 702 setCutoffTimeForPreviousDay(hourInt, minuteInt, secondInt); 703 } 704 catch (Exception e) { 705 throw new IllegalArgumentException("Cutoff time should either be null, or in the format \"HH:mm:ss\", where HH, mm, ss are defined in the java.text.SimpleDateFormat class.",e); 706 } 707 } 708 } 709 710 711 public void unsetCutoffTimeForPreviousDay() { 712 this.cutoffHour = null; 713 this.cutoffMinute = null; 714 this.cutoffSecond = null; 715 } 716 717 /** 718 * This method modifies the run date if it is before the cutoff time specified by calling the setCutoffTimeForPreviousDay 719 * method. See KULRNE-70 This method is public to facilitate unit testing 720 * 721 * @param currentDate 722 * @return 723 */ 724 public java.sql.Date calculateRunDate(java.util.Date currentDate) { 725 Calendar currentCal = Calendar.getInstance(); 726 currentCal.setTime(currentDate); 727 728 if (isCurrentDateBeforeCutoff(currentCal)) { 729 // time to set the date to the previous day's last minute/second 730 currentCal.add(Calendar.DAY_OF_MONTH, -1); 731 // per old COBOL code (see KULRNE-70), 732 // the time is set to 23:59:59 (assuming 0 ms) 733 currentCal.set(Calendar.HOUR_OF_DAY, 23); 734 currentCal.set(Calendar.MINUTE, 59); 735 currentCal.set(Calendar.SECOND, 59); 736 currentCal.set(Calendar.MILLISECOND, 0); 737 return new java.sql.Date(currentCal.getTimeInMillis()); 738 } 739 return new java.sql.Date(currentDate.getTime()); 740 } 741 742 protected boolean isCurrentDateBeforeCutoff(Calendar currentCal) { 743 if (cutoffHour != null && cutoffMinute != null && cutoffSecond != null) { 744 // if cutoff date is not properly defined 745 // 24 hour clock (i.e. hour is 0 - 23) 746 747 // clone the calendar so we get the same month, day, year 748 // then change the hour, minute, second fields 749 // then see if the cutoff is before or after 750 Calendar cutoffTime = (Calendar) currentCal.clone(); 751 cutoffTime.setLenient(false); 752 cutoffTime.set(Calendar.HOUR_OF_DAY, cutoffHour); 753 cutoffTime.set(Calendar.MINUTE, cutoffMinute); 754 cutoffTime.set(Calendar.SECOND, cutoffSecond); 755 cutoffTime.set(Calendar.MILLISECOND, 0); 756 757 return currentCal.before(cutoffTime); 758 } 759 // if cutoff date is not properly defined, then it is considered to be after the cutoff 760 return false; 761 } 762 763 protected void initCutoffTime() { 764 String cutoffTime = SpringContext.getBean(ParameterService.class).getParameterValue(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupParameters.SCRUBBER_CUTOFF_TIME); 765 if (StringUtils.isBlank(cutoffTime)) { 766 LOG.debug("Cutoff time system parameter not found"); 767 unsetCutoffTimeForPreviousDay(); 768 return; 769 } 770 setCutoffTime(cutoffTime); 771 } 772 773 protected void buildScrubbedEntry(LaborOriginEntry unscrubbedEntry, LaborOriginEntry scrubbedEntry) { 774 scrubbedEntry.setDocumentNumber(unscrubbedEntry.getDocumentNumber()); 775 scrubbedEntry.setOrganizationDocumentNumber(unscrubbedEntry.getOrganizationDocumentNumber()); 776 scrubbedEntry.setOrganizationReferenceId(unscrubbedEntry.getOrganizationReferenceId()); 777 scrubbedEntry.setReferenceFinancialDocumentNumber(unscrubbedEntry.getReferenceFinancialDocumentNumber()); 778 779 Integer transactionNumber = unscrubbedEntry.getTransactionLedgerEntrySequenceNumber(); 780 scrubbedEntry.setTransactionLedgerEntrySequenceNumber(null == transactionNumber ? new Integer(0) : transactionNumber); 781 scrubbedEntry.setTransactionLedgerEntryDescription(unscrubbedEntry.getTransactionLedgerEntryDescription()); 782 scrubbedEntry.setTransactionLedgerEntryAmount(unscrubbedEntry.getTransactionLedgerEntryAmount()); 783 scrubbedEntry.setTransactionDebitCreditCode(unscrubbedEntry.getTransactionDebitCreditCode()); 784 785 // For Labor's more fields 786 // It might be changed based on Labor Scrubber's business rule 787 scrubbedEntry.setPositionNumber(unscrubbedEntry.getPositionNumber()); 788 scrubbedEntry.setTransactionPostingDate(unscrubbedEntry.getTransactionPostingDate()); 789 scrubbedEntry.setPayPeriodEndDate(unscrubbedEntry.getPayPeriodEndDate()); 790 scrubbedEntry.setTransactionTotalHours(unscrubbedEntry.getTransactionTotalHours()); 791 scrubbedEntry.setPayrollEndDateFiscalYear(unscrubbedEntry.getPayrollEndDateFiscalYear()); 792 scrubbedEntry.setPayrollEndDateFiscalPeriodCode(unscrubbedEntry.getPayrollEndDateFiscalPeriodCode()); 793 scrubbedEntry.setFinancialDocumentApprovedCode(unscrubbedEntry.getFinancialDocumentApprovedCode()); 794 scrubbedEntry.setTransactionEntryOffsetCode(unscrubbedEntry.getTransactionEntryOffsetCode()); 795 scrubbedEntry.setTransactionEntryProcessedTimestamp(unscrubbedEntry.getTransactionEntryProcessedTimestamp()); 796 scrubbedEntry.setEmplid(unscrubbedEntry.getEmplid()); 797 scrubbedEntry.setEmployeeRecord(unscrubbedEntry.getEmployeeRecord()); 798 scrubbedEntry.setEarnCode(unscrubbedEntry.getEarnCode()); 799 scrubbedEntry.setPayGroup(unscrubbedEntry.getPayGroup()); 800 scrubbedEntry.setSalaryAdministrationPlan(unscrubbedEntry.getSalaryAdministrationPlan()); 801 scrubbedEntry.setGrade(unscrubbedEntry.getGrade()); 802 scrubbedEntry.setRunIdentifier(unscrubbedEntry.getRunIdentifier()); 803 scrubbedEntry.setLaborLedgerOriginalChartOfAccountsCode(unscrubbedEntry.getLaborLedgerOriginalChartOfAccountsCode()); 804 scrubbedEntry.setLaborLedgerOriginalAccountNumber(unscrubbedEntry.getLaborLedgerOriginalAccountNumber()); 805 scrubbedEntry.setLaborLedgerOriginalSubAccountNumber(unscrubbedEntry.getLaborLedgerOriginalSubAccountNumber()); 806 scrubbedEntry.setLaborLedgerOriginalFinancialObjectCode(unscrubbedEntry.getLaborLedgerOriginalFinancialObjectCode()); 807 scrubbedEntry.setLaborLedgerOriginalFinancialSubObjectCode(unscrubbedEntry.getLaborLedgerOriginalFinancialSubObjectCode()); 808 scrubbedEntry.setHrmsCompany(unscrubbedEntry.getHrmsCompany()); 809 scrubbedEntry.setSetid(unscrubbedEntry.getSetid()); 810 scrubbedEntry.setTransactionDateTimeStamp(unscrubbedEntry.getTransactionDateTimeStamp()); 811 scrubbedEntry.setReferenceFinancialDocumentTypeCode(unscrubbedEntry.getReferenceFinancialDocumentTypeCode()); 812 scrubbedEntry.setReferenceFinancialSystemOrigination(unscrubbedEntry.getReferenceFinancialSystemOrigination()); 813 scrubbedEntry.setPayrollEndDateFiscalPeriod(unscrubbedEntry.getPayrollEndDateFiscalPeriod()); 814 } 815 816 /** 817 * The demerger process reads all of the documents in the error group, then moves all of the original entries for that document 818 * from the valid group to the error group. It does not move generated entries to the error group. Those are deleted. It also 819 * modifies the doc number and origin code of cost share transfers. 820 * 821 * @param errorGroup 822 * @param validGroup 823 */ 824 public void performDemerger() { 825 LOG.debug("performDemerger() started"); 826 LaborOriginEntryFieldUtil loefu = new LaborOriginEntryFieldUtil(); 827 Map<String, Integer> pMap = loefu.getFieldBeginningPositionMap(); 828 829 String validOutputFilename = batchFileDirectoryName + File.separator + LaborConstants.BatchFileSystem.SCRUBBER_VALID_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION; 830 String errorOutputFilename = batchFileDirectoryName + File.separator + LaborConstants.BatchFileSystem.SCRUBBER_ERROR_SORTED_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION; 831 runDate = calculateRunDate(dateTimeService.getCurrentDate()); 832 833 // Without this step, the job fails with Optimistic Lock Exceptions 834 persistenceService.clearCache(); 835 836 DemergerReportData demergerReport = new DemergerReportData(); 837 838 OriginEntryStatistics eOes = laborOriginEntryService.getStatistics(errorOutputFilename); 839 demergerReport.setErrorTransactionsRead(eOes.getRowCount()); 840 841 // 842 // Get the list of document type codes from the parameter. If the 843 // current document type matches any of parameter defined type codes, 844 // then demerge all other entries for this document; otherwise, only 845 // pull the entry with the error and don't demerge anything else. 846 // 847 List<String> demergeDocumentTypes = parameterService.getParameterValues( 848 LaborScrubberStep.class, 849 LdParameterConstants.DEMERGE_DOCUMENT_TYPES); 850 851 // Read all the documents from the error group and move all non-generated 852 // transactions for these documents from the valid group into the error group 853 854 FileReader INPUT_GLE_FILE = null; 855 FileReader INPUT_ERR_FILE = null; 856 BufferedReader INPUT_GLE_FILE_br; 857 BufferedReader INPUT_ERR_FILE_br; 858 PrintStream OUTPUT_DEMERGER_GLE_FILE_ps; 859 PrintStream OUTPUT_DEMERGER_ERR_FILE_ps; 860 PrintStream reportPrintStream; 861 862 String demergerValidOutputFilename = batchFileDirectoryName + File.separator + LaborConstants.BatchFileSystem.DEMERGER_VAILD_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION; 863 String demergerErrorOutputFilename = batchFileDirectoryName + File.separator + LaborConstants.BatchFileSystem.DEMERGER_ERROR_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION; 864 865 try { 866 INPUT_GLE_FILE = new FileReader(validOutputFilename); 867 INPUT_ERR_FILE = new FileReader(errorOutputFilename); 868 } 869 catch (FileNotFoundException e) { 870 throw new RuntimeException("Unable to open input files", e); 871 } 872 try { 873 OUTPUT_DEMERGER_GLE_FILE_ps = new PrintStream(demergerValidOutputFilename); 874 OUTPUT_DEMERGER_ERR_FILE_ps = new PrintStream(demergerErrorOutputFilename); 875 } 876 catch (IOException e) { 877 throw new RuntimeException("Unable to open output files",e); 878 } 879 880 Collection<LaborOriginEntry> validEntryCollection = new ArrayList<LaborOriginEntry>(); 881 882 int validRead = 0; 883 int errorRead = 0; 884 885 int validSaved = 0; 886 int errorSaved = 0; 887 888 boolean errorsLoading = false; 889 LaborOriginEntry laborOriginEntry = null; 890 INPUT_GLE_FILE_br = new BufferedReader(INPUT_GLE_FILE); 891 INPUT_ERR_FILE_br = new BufferedReader(INPUT_ERR_FILE); 892 893 try { 894 String currentValidLine = INPUT_GLE_FILE_br.readLine(); 895 String currentErrorLine = INPUT_ERR_FILE_br.readLine(); 896 897 boolean meetFlag = false; 898 while (currentValidLine != null || currentErrorLine != null) { 899 // validLine is null means that errorLine is not null 900 if (StringUtils.isEmpty(currentValidLine)) { 901 createOutputEntry(currentErrorLine, OUTPUT_DEMERGER_ERR_FILE_ps); 902 currentErrorLine = INPUT_ERR_FILE_br.readLine(); 903 errorRead++; 904 errorSaved++; 905 906 continue; 907 } 908 909 // errorLine is null means that validLine is not null 910 if (StringUtils.isEmpty(currentErrorLine)) { 911 createOutputEntry(currentValidLine, OUTPUT_DEMERGER_GLE_FILE_ps); 912 currentValidLine = INPUT_GLE_FILE_br.readLine(); 913 validRead++; 914 validSaved++; 915 916 continue; 917 } 918 919 String documentTypeCode = currentErrorLine.substring(pMap.get(KFSPropertyConstants.FINANCIAL_DOCUMENT_TYPE_CODE), pMap.get(KFSPropertyConstants.FINANCIAL_SYSTEM_ORIGINATION_CODE)); 920 if (documentTypeCode != null) { 921 documentTypeCode = documentTypeCode.trim(); 922 } 923 924 if (demergeDocumentTypes.contains(documentTypeCode)) { 925 String compareStringFromValidEntry = currentValidLine.substring(pMap.get(KFSPropertyConstants.FINANCIAL_DOCUMENT_TYPE_CODE), pMap.get(KFSPropertyConstants.TRANSACTION_ENTRY_SEQUENCE_NUMBER)); 926 String compareStringFromErrorEntry = currentErrorLine.substring(pMap.get(KFSPropertyConstants.FINANCIAL_DOCUMENT_TYPE_CODE), pMap.get(KFSPropertyConstants.TRANSACTION_ENTRY_SEQUENCE_NUMBER)); 927 928 if (compareStringFromValidEntry.compareTo(compareStringFromErrorEntry) < 0) { 929 createOutputEntry(currentValidLine, OUTPUT_DEMERGER_GLE_FILE_ps); 930 currentValidLine = INPUT_GLE_FILE_br.readLine(); 931 validRead++; 932 validSaved++; 933 934 } 935 else if (compareStringFromValidEntry.compareTo(compareStringFromErrorEntry) > 0) { 936 createOutputEntry(currentErrorLine, OUTPUT_DEMERGER_ERR_FILE_ps); 937 currentErrorLine = INPUT_ERR_FILE_br.readLine(); 938 errorRead++; 939 errorSaved++; 940 } 941 else { 942 createOutputEntry(currentValidLine, OUTPUT_DEMERGER_ERR_FILE_ps); 943 currentValidLine = INPUT_GLE_FILE_br.readLine(); 944 validRead++; 945 errorSaved++; 946 } 947 948 continue; 949 } 950 createOutputEntry(currentErrorLine, OUTPUT_DEMERGER_ERR_FILE_ps); 951 currentErrorLine = INPUT_ERR_FILE_br.readLine(); 952 errorRead++; 953 errorSaved++; 954 } 955 956 } 957 catch (Exception e) { 958 LOG.error("performDemerger() s" + "topped due to: " + e.getMessage(), e); 959 throw new RuntimeException("performDemerger() stopped due to: " + e.getMessage(), e); 960 } 961 finally { 962 963 try { 964 if (INPUT_GLE_FILE_br != null) { 965 INPUT_GLE_FILE_br.close(); 966 } 967 } 968 catch (IOException e) { 969 LOG.error("performDemerger() s" + "Failed to close resources due to: " + e.getMessage(), e); 970 } 971 972 try { 973 if (INPUT_ERR_FILE_br != null) { 974 INPUT_ERR_FILE_br.close(); 975 } 976 } 977 catch (IOException e) { 978 LOG.error("performDemerger() s" + "Failed to close resources due to: " + e.getMessage(), e); 979 } 980 981 OUTPUT_DEMERGER_GLE_FILE_ps.close(); 982 OUTPUT_DEMERGER_ERR_FILE_ps.close(); 983 } 984 985 demergerReport.setValidTransactionsRead(validRead); 986 demergerReport.setValidTransactionsSaved(validSaved); 987 demergerReport.setErrorTransactionsRead(errorRead); 988 demergerReport.setErrorTransactionWritten(errorSaved); 989 990 this.laborDemergerReportWriterService.writeStatisticLine("SCRUBBER ERROR TRANSACTIONS READ %,9d", demergerReport.getErrorTransactionsRead()); 991 this.laborDemergerReportWriterService.writeStatisticLine("SCRUBBER VALID TRANSACTIONS READ %,9d", demergerReport.getValidTransactionsRead()); 992 this.laborDemergerReportWriterService.writeStatisticLine("DEMERGER ERRORS SAVED %,9d", demergerReport.getErrorTransactionsSaved()); 993 this.laborDemergerReportWriterService.writeStatisticLine("DEMERGER VALID TRANSACTIONS SAVED %,9d", demergerReport.getValidTransactionsSaved()); 994 995 this.generateScrubberErrorListingReport(demergerErrorOutputFilename); 996 } 997 998 999 protected void createOutputEntry(LaborOriginEntry entry, PrintStream ps) throws IOException { 1000 try { 1001 ps.printf("%s\n", entry.getLine()); 1002 } 1003 catch (Exception e) { 1004 throw new IOException(e.toString(),e); 1005 } 1006 } 1007 1008 protected void createOutputEntry(String line, PrintStream ps) throws IOException { 1009 try { 1010 ps.printf("%s\n", line); 1011 } 1012 catch (Exception e) { 1013 throw new IOException(e.toString(),e); 1014 } 1015 } 1016 1017 protected boolean checkEntry(LaborOriginEntry validEntry, LaborOriginEntry errorEntry, String documentTypeCode) { 1018 String documentNumber = errorEntry.getDocumentNumber(); 1019 String originationCode = errorEntry.getFinancialSystemOriginationCode(); 1020 1021 if (validEntry.getDocumentNumber().equals(documentNumber) && validEntry.getFinancialDocumentTypeCode().equals(documentTypeCode) && validEntry.getFinancialSystemOriginationCode().equals(originationCode)) { 1022 return true; 1023 } 1024 return false; 1025 } 1026 1027 /** 1028 * Generates a transaction listing report for labor origin entries that were valid 1029 */ 1030 protected void generateScrubberTransactionsOnline() { 1031 try { 1032 Iterator<LaborOriginEntry> generatedTransactions = new LaborOriginEntryFileIterator(new File(inputFile)); 1033 1034 ((WrappingBatchService) laborGeneratedTransactionsReportWriterService).initialize(); 1035 new TransactionListingReport().generateReport(laborGeneratedTransactionsReportWriterService, generatedTransactions); 1036 } 1037 finally { 1038 ((WrappingBatchService) laborGeneratedTransactionsReportWriterService).destroy(); 1039 } 1040 } 1041 1042 /** 1043 * Generates a transaction listing report for labor origin entries with bad balance types 1044 */ 1045 protected void generateScrubberBadBalanceTypeListingReport() { 1046 LaborOriginEntryFilter blankBalanceTypeFilter = new LaborOriginEntryFilter() { 1047 public boolean accept(LaborOriginEntry originEntry) { 1048 BalanceType originEntryBalanceType = laborAccountingCycleCachingService.getBalanceType(originEntry.getFinancialBalanceTypeCode()); 1049 return ObjectUtils.isNull(originEntryBalanceType); 1050 } 1051 }; 1052 try { 1053 ((WrappingBatchService) laborBadBalanceTypeReportWriterService).initialize(); 1054 Iterator<LaborOriginEntry> blankBalanceOriginEntries = new FilteringLaborOriginEntryFileIterator(new File(inputFile), blankBalanceTypeFilter); 1055 new TransactionListingReport().generateReport(laborBadBalanceTypeReportWriterService, blankBalanceOriginEntries); 1056 } 1057 finally { 1058 ((WrappingBatchService) laborBadBalanceTypeReportWriterService).destroy(); 1059 } 1060 } 1061 1062 /** 1063 * Generates a transaction listing report for labor origin entries with errors 1064 */ 1065 protected void generateScrubberErrorListingReport(String errorFileName) { 1066 try { 1067 ((WrappingBatchService) laborErrorListingReportWriterService).initialize(); 1068 Iterator<LaborOriginEntry> removedTransactions = new LaborOriginEntryFileIterator(new File(errorFileName)); 1069 new TransactionListingReport().generateReport(laborErrorListingReportWriterService, removedTransactions); 1070 } 1071 finally { 1072 ((WrappingBatchService) laborErrorListingReportWriterService).destroy(); 1073 } 1074 } 1075 }