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 }