View Javadoc
1   /*
2    * Copyright 2006 The Kuali Foundation
3    * 
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    * http://www.opensource.org/licenses/ecl2.php
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.gl.batch.service.impl;
17  
18  import java.io.BufferedReader;
19  import java.io.File;
20  import java.io.FileNotFoundException;
21  import java.io.FileReader;
22  import java.io.IOException;
23  import java.io.PrintStream;
24  import java.sql.Date;
25  import java.text.NumberFormat;
26  import java.util.ArrayList;
27  import java.util.Calendar;
28  import java.util.HashMap;
29  import java.util.IdentityHashMap;
30  import java.util.Iterator;
31  import java.util.List;
32  import java.util.Map;
33  
34  import org.apache.commons.io.FileUtils;
35  import org.apache.commons.io.LineIterator;
36  import org.kuali.ole.coa.businessobject.A21SubAccount;
37  import org.kuali.ole.coa.businessobject.Account;
38  import org.kuali.ole.coa.businessobject.BalanceType;
39  import org.kuali.ole.coa.businessobject.Chart;
40  import org.kuali.ole.coa.businessobject.ObjectCode;
41  import org.kuali.ole.coa.businessobject.OffsetDefinition;
42  import org.kuali.ole.gl.GeneralLedgerConstants;
43  import org.kuali.ole.gl.ObjectHelper;
44  import org.kuali.ole.gl.batch.BatchSortUtil;
45  import org.kuali.ole.gl.batch.CollectorBatch;
46  import org.kuali.ole.gl.batch.DemergerSortComparator;
47  import org.kuali.ole.gl.batch.ScrubberSortComparator;
48  import org.kuali.ole.gl.batch.ScrubberStep;
49  import org.kuali.ole.gl.batch.service.AccountingCycleCachingService;
50  import org.kuali.ole.gl.batch.service.RunDateService;
51  import org.kuali.ole.gl.batch.service.ScrubberProcess;
52  import org.kuali.ole.gl.batch.service.impl.FilteringOriginEntryFileIterator.OriginEntryFilter;
53  import org.kuali.ole.gl.businessobject.DemergerReportData;
54  import org.kuali.ole.gl.businessobject.OriginEntryFieldUtil;
55  import org.kuali.ole.gl.businessobject.OriginEntryFull;
56  import org.kuali.ole.gl.businessobject.OriginEntryInformation;
57  import org.kuali.ole.gl.businessobject.Transaction;
58  import org.kuali.ole.gl.report.CollectorReportData;
59  import org.kuali.ole.gl.report.LedgerSummaryReport;
60  import org.kuali.ole.gl.report.PreScrubberReport;
61  import org.kuali.ole.gl.report.PreScrubberReportData;
62  import org.kuali.ole.gl.report.TransactionListingReport;
63  import org.kuali.ole.gl.service.PreScrubberService;
64  import org.kuali.ole.gl.service.ScrubberReportData;
65  import org.kuali.ole.gl.service.ScrubberValidator;
66  import org.kuali.ole.gl.service.impl.ScrubberStatus;
67  import org.kuali.ole.gl.service.impl.StringHelper;
68  import org.kuali.ole.sys.OLEConstants;
69  import org.kuali.ole.sys.OLEKeyConstants;
70  import org.kuali.ole.sys.OLEParameterKeyConstants.GlParameterConstants;
71  import org.kuali.ole.sys.OLEPropertyConstants;
72  import org.kuali.ole.sys.Message;
73  import org.kuali.ole.sys.batch.service.WrappingBatchService;
74  import org.kuali.ole.sys.businessobject.SystemOptions;
75  import org.kuali.ole.sys.businessobject.UniversityDate;
76  import org.kuali.ole.sys.context.SpringContext;
77  import org.kuali.ole.sys.exception.InvalidFlexibleOffsetException;
78  import org.kuali.ole.sys.service.DocumentNumberAwareReportWriterService;
79  import org.kuali.ole.sys.service.FlexibleOffsetAccountService;
80  import org.kuali.ole.sys.service.ReportWriterService;
81  import org.kuali.rice.core.api.config.property.ConfigurationService;
82  import org.kuali.rice.core.api.datetime.DateTimeService;
83  import org.kuali.rice.core.api.parameter.ParameterEvaluator;
84  import org.kuali.rice.core.api.parameter.ParameterEvaluatorService;
85  import org.kuali.rice.core.api.util.type.KualiDecimal;
86  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
87  import org.kuali.rice.krad.service.BusinessObjectService;
88  import org.kuali.rice.krad.service.PersistenceService;
89  import org.kuali.rice.krad.util.ObjectUtils;
90  import org.springframework.util.StringUtils;
91  
92  /**
93   * This class has the logic for the scrubber. It is required because the scrubber process needs instance variables. Instance
94   * variables in a spring service are shared between all code calling the service. This will make sure each run of the scrubber has
95   * it's own instance variables instead of being shared.
96   */
97  public class ScrubberProcessImpl implements ScrubberProcess {
98      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ScrubberProcessImpl.class);
99      
100     protected static final String TRANSACTION_TYPE_COST_SHARE_ENCUMBRANCE = "CE";
101     protected static final String TRANSACTION_TYPE_OFFSET = "O";
102     protected static final String TRANSACTION_TYPE_CAPITALIZATION = "C";
103     protected static final String TRANSACTION_TYPE_LIABILITY = "L";
104     protected static final String TRANSACTION_TYPE_TRANSFER = "T";
105     protected static final String TRANSACTION_TYPE_COST_SHARE = "CS";
106     protected static final String TRANSACTION_TYPE_OTHER = "X";
107 
108     enum GROUP_TYPE {VALID, ERROR, EXPIRED}
109     
110     protected static final String COST_SHARE_CODE = "CSHR";
111 
112     protected static final String COST_SHARE_TRANSFER_ENTRY_IND = "***";
113 
114     // These lengths are different then database field lengths, hence they are not from the DD
115     protected static final int COST_SHARE_ENCUMBRANCE_ENTRY_MAXLENGTH = 28;
116     protected static final int DEMERGER_TRANSACTION_LEDGET_ENTRY_DESCRIPTION = 33;
117     protected static final int OFFSET_MESSAGE_MAXLENGTH = 33;
118 
119     /* Services required */
120     protected FlexibleOffsetAccountService flexibleOffsetAccountService;
121     protected DateTimeService dateTimeService;
122     protected ConfigurationService configurationService;
123     protected PersistenceService persistenceService;
124     protected ScrubberValidator scrubberValidator;
125     protected RunDateService runDateService;
126     protected AccountingCycleCachingService accountingCycleCachingService;
127     protected DocumentNumberAwareReportWriterService scrubberReportWriterService;
128     protected DocumentNumberAwareReportWriterService scrubberLedgerReportWriterService;
129     protected DocumentNumberAwareReportWriterService scrubberListingReportWriterService;
130     protected DocumentNumberAwareReportWriterService preScrubberReportWriterService;
131     protected ReportWriterService scrubberBadBalanceListingReportWriterService;
132     protected ReportWriterService demergerRemovedTransactionsListingReportWriterService;
133     protected ReportWriterService demergerReportWriterService;
134     protected PreScrubberService preScrubberService;
135     
136     // these three members will only be populated when in collector mode, otherwise the memory requirements will be huge
137     protected Map<OriginEntryInformation, OriginEntryInformation> unscrubbedToScrubbedEntries = new HashMap<OriginEntryInformation, OriginEntryInformation>();
138     protected Map<Transaction, List<Message>> scrubberReportErrors = new IdentityHashMap<Transaction, List<Message>>();
139     protected LedgerSummaryReport ledgerSummaryReport = new LedgerSummaryReport();
140     
141     protected ScrubberReportData scrubberReport;
142     protected DemergerReportData demergerReport;
143     
144     /* These are all different forms of the run date for this job */
145     protected Date runDate;
146     protected Calendar runCal;
147     protected UniversityDate universityRunDate;
148     protected String offsetString;
149 
150     /* Unit Of Work info */
151     protected UnitOfWorkInfo unitOfWork;
152     protected KualiDecimal scrubCostShareAmount;
153 
154     /* Statistics for the reports */
155     protected List<Message> transactionErrors;
156 
157     /* Description names */
158     protected String offsetDescription;
159     protected String capitalizationDescription;
160     protected String liabilityDescription;
161     protected String transferDescription;
162     protected String costShareDescription;
163 
164     protected ParameterService parameterService;
165     protected BusinessObjectService businessObjectService;
166     
167     /**
168      * Whether this instance is being used to support the scrubbing of a collector batch
169      */
170     protected boolean collectorMode;
171     protected String batchFileDirectoryName;
172     
173     protected PrintStream OUTPUT_GLE_FILE_ps;
174     protected PrintStream OUTPUT_ERR_FILE_ps;
175     protected PrintStream OUTPUT_EXP_FILE_ps;
176 
177     protected String inputFile;
178     protected String validFile;
179     protected String errorFile;
180     protected String expiredFile;
181     
182     /**
183      * Scrub this single group read only. This will only output the scrubber report. It won't output any other groups.
184      * 
185      * @param group the origin entry group that should be scrubbed
186      * @param the document number of any specific entries to scrub
187      */
188     @Override
189     public void scrubGroupReportOnly(String fileName, String documentNumber) {
190         LOG.debug("scrubGroupReportOnly() started");
191         String unsortedFile = fileName;
192         this.inputFile = fileName + ".sort";
193         this.validFile = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.SCRUBBER_VALID_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION;
194         this.errorFile = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.SCRUBBER_ERROR_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION;
195         this.expiredFile = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.SCRUBBER_EXPIRED_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION;
196         String prescrubOutput = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.PRE_SCRUBBER_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION;
197         this.ledgerSummaryReport = new LedgerSummaryReport();
198         runDate = calculateRunDate(dateTimeService.getCurrentDate());
199         
200         PreScrubberReportData preScrubberReportData = null;
201         
202         // run pre-scrubber on the raw input into the sort process
203         LineIterator inputEntries = null;
204         try {
205             inputEntries = FileUtils.lineIterator(new File(unsortedFile));
206             preScrubberReportData = preScrubberService.preprocessOriginEntries(inputEntries, prescrubOutput);
207         }
208         catch (IOException e1) {
209             LOG.error("Error encountered trying to prescrub GLCP/LLCP document", e1);
210             throw new RuntimeException("Error encountered trying to prescrub GLCP/LLCP document", e1);
211         }
212         finally {
213             LineIterator.closeQuietly(inputEntries);
214         }
215         if (preScrubberReportData != null) {
216             preScrubberReportWriterService.setDocumentNumber(documentNumber);
217             ((WrappingBatchService)preScrubberReportWriterService).initialize();
218             try {
219                 new PreScrubberReport().generateReport(preScrubberReportData, preScrubberReportWriterService);
220             }
221             finally {
222                 ((WrappingBatchService)preScrubberReportWriterService).destroy();
223             }
224         }
225         BatchSortUtil.sortTextFileWithFields(prescrubOutput, inputFile, new ScrubberSortComparator());
226         
227         scrubEntries(true, documentNumber);
228         
229         // delete files
230         File deleteSortFile = new File(inputFile);
231         File deleteValidFile = new File(validFile);
232         File deleteErrorFile = new File(errorFile);
233         File deleteExpiredFile = new File(expiredFile);
234         try {
235             deleteSortFile.delete();
236             deleteValidFile.delete();
237             deleteErrorFile.delete();
238             deleteExpiredFile.delete();
239         } catch (Exception e){
240             LOG.error("scrubGroupReportOnly delete output files process Stopped: " + e.getMessage());
241             throw new RuntimeException("scrubGroupReportOnly delete output files process Stopped: " + e.getMessage(), e);
242         }
243     }
244 
245     /**
246      * Scrubs all entries in all groups and documents.
247      */
248     @Override
249     public void scrubEntries() {
250         this.inputFile = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.SCRUBBER_INPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION;
251         this.validFile = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.SCRUBBER_VALID_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION;
252         this.errorFile = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.SCRUBBER_ERROR_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION;
253         this.expiredFile = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.SCRUBBER_EXPIRED_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION;
254         runDate = calculateRunDate(dateTimeService.getCurrentDate());
255 
256         scrubEntries(false, null);
257     }
258 
259     /**
260      * Scrubs the origin entry and ID billing details if the given batch. Store all scrubber output into the collectorReportData
261      * parameter. NOTE: DO NOT CALL ANY OF THE scrub* METHODS OF THIS CLASS AFTER CALLING THIS METHOD FOR EVERY UNIQUE INSTANCE OF
262      * THIS CLASS, OR THE COLLECTOR REPORTS MAY BE CORRUPTED
263      * 
264      * @param batch the data gathered from a Collector file
265      * @param collectorReportData the statistics generated by running the Collector
266      */
267     @Override
268     public void scrubCollectorBatch(ScrubberStatus scrubberStatus, CollectorBatch batch, CollectorReportData collectorReportData) {
269         collectorMode = true;
270 
271         this.inputFile = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.COLLECTOR_SCRUBBER_INPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION;
272         this.validFile = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.COLLECTOR_SCRUBBER_VALID_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION;
273         this.errorFile = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.COLLECTOR_SCRUBBER_ERROR_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION;
274         this.expiredFile = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.COLLECTOR_SCRUBBER_EXPIRED_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION;
275         runDate = calculateRunDate(dateTimeService.getCurrentDate());
276         
277         this.ledgerSummaryReport = collectorReportData.getLedgerSummaryReport();
278         
279         // sort input file
280         String scrubberSortInputFile = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.COLLECTOR_BACKUP_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION;
281         String scrubberSortOutputFile = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.COLLECTOR_SCRUBBER_INPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION;
282         BatchSortUtil.sortTextFileWithFields(scrubberSortInputFile, scrubberSortOutputFile, new ScrubberSortComparator());
283         
284         scrubEntries(false, null);
285         
286         //sort scrubber error file for demerger
287         String demergerSortInputFile = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.COLLECTOR_SCRUBBER_ERROR_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION; 
288         String demergerSortOutputFile = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.COLLECTOR_SCRUBBER_ERROR_SORTED_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION; 
289         BatchSortUtil.sortTextFileWithFields(demergerSortInputFile, demergerSortOutputFile, new DemergerSortComparator());
290         
291         performDemerger();
292         
293         // the scrubber process has just updated several member variables of this class. Store these values for the collector report
294         collectorReportData.setBatchOriginEntryScrubberErrors(batch, scrubberReportErrors);
295         collectorReportData.setScrubberReportData(batch, scrubberReport);
296         collectorReportData.setDemergerReportData(batch, demergerReport);
297 
298         // report purpose - commented out.  If we need, the put string values for fileNames.
299         scrubberStatus.setInputFileName(GeneralLedgerConstants.BatchFileSystem.COLLECTOR_SCRUBBER_INPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION);
300         scrubberStatus.setValidFileName(GeneralLedgerConstants.BatchFileSystem.COLLECTOR_DEMERGER_VAILD_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION);
301         scrubberStatus.setErrorFileName(GeneralLedgerConstants.BatchFileSystem.COLLECTOR_DEMERGER_ERROR_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION);
302         scrubberStatus.setExpiredFileName(GeneralLedgerConstants.BatchFileSystem.COLLECTOR_SCRUBBER_EXPIRED_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION);
303         scrubberStatus.setUnscrubbedToScrubbedEntries(unscrubbedToScrubbedEntries);
304     }
305 
306     /**
307      * Scrub all entries that need it in origin entry. Put valid scrubbed entries in a scrubber valid group, put errors in a
308      * scrubber error group, and transactions with an expired account in the scrubber expired account group.
309      * @param group the specific origin entry group to scrub
310      * @param documentNumber the number of the document with entries to scrub
311      */
312     @Override
313     public void scrubEntries(boolean reportOnlyMode, String documentNumber) {
314         LOG.debug("scrubEntries() started");
315 
316         if (reportOnlyMode) {
317             scrubberReportWriterService.setDocumentNumber(documentNumber);
318             scrubberLedgerReportWriterService.setDocumentNumber(documentNumber);
319         }
320         
321         // setup an object to hold the "default" date information
322         runDate = calculateRunDate(dateTimeService.getCurrentDate());
323         runCal = Calendar.getInstance();
324         runCal.setTime(runDate);
325 
326         universityRunDate = accountingCycleCachingService.getUniversityDate(runDate);
327         if (universityRunDate == null) {
328             throw new IllegalStateException(configurationService.getPropertyValueAsString(OLEKeyConstants.ERROR_UNIV_DATE_NOT_FOUND));
329         }
330         
331         setOffsetString();
332         setDescriptions();
333         scrubberReport = new ScrubberReportData();
334         
335         try {
336             if (!collectorMode) {
337                 ((WrappingBatchService) scrubberReportWriterService).initialize();
338                 ((WrappingBatchService) scrubberLedgerReportWriterService).initialize();
339             }
340 
341             processGroup(reportOnlyMode, scrubberReport);
342 
343             if (reportOnlyMode) {
344                 generateScrubberTransactionListingReport(documentNumber, inputFile);
345             }
346             else if (collectorMode) {
347                 // defer report generation for later
348             }
349             else {
350                 generateScrubberBlankBalanceTypeCodeReport(inputFile);
351             }
352         }
353         finally {
354             if (!collectorMode) {
355                 ((WrappingBatchService) scrubberReportWriterService).destroy();
356                 ((WrappingBatchService) scrubberLedgerReportWriterService).destroy();
357             }
358         }
359     }
360 
361     /**
362      * The demerger process reads all of the documents in the error group, then moves all of the original entries for that document
363      * from the valid group to the error group. It does not move generated entries to the error group. Those are deleted. It also
364      * modifies the doc number and origin code of cost share transfers.
365      * 
366      * @param errorGroup this scrubber run's error group
367      * @param validGroup this scrubber run's valid group
368      */
369     @Override
370     public void performDemerger() {
371         LOG.debug("performDemerger() started");
372 
373         OriginEntryFieldUtil oefu = new OriginEntryFieldUtil();
374         Map<String, Integer> pMap = oefu.getFieldBeginningPositionMap();
375         
376         // Without this step, the job fails with Optimistic Lock Exceptions
377      //   persistenceService.clearCache();
378 
379         demergerReport = new DemergerReportData();
380         
381         // set runDate here again, because demerger is calling outside from scrubber
382         runDate = calculateRunDate(dateTimeService.getCurrentDate());
383         runCal = Calendar.getInstance();
384         runCal.setTime(runDate);
385         
386         // demerger called by outside from scrubber, so reset those values
387         setOffsetString();
388         setDescriptions();
389         
390         // new demerger starts
391 
392         String validOutputFilename = null; 
393         String errorOutputFilename = null;
394 
395         String demergerValidOutputFilename = null; 
396         String demergerErrorOutputFilename = null;
397       
398         if(!collectorMode){
399             validOutputFilename = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.SCRUBBER_VALID_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION; 
400             errorOutputFilename = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.SCRUBBER_ERROR_SORTED_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION;
401 
402             demergerValidOutputFilename = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.DEMERGER_VAILD_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION; 
403             demergerErrorOutputFilename = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.DEMERGER_ERROR_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION;
404 
405         } else {
406 
407             validOutputFilename = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.COLLECTOR_SCRUBBER_VALID_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION; 
408             errorOutputFilename = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.COLLECTOR_SCRUBBER_ERROR_SORTED_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION;
409 
410             demergerValidOutputFilename = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.COLLECTOR_DEMERGER_VAILD_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION; 
411             demergerErrorOutputFilename = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.COLLECTOR_DEMERGER_ERROR_OUTPUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION;
412         }
413         
414         // Without this step, the job fails with Optimistic Lock Exceptions
415     //    persistenceService.clearCache();
416         
417         FileReader INPUT_GLE_FILE = null;
418         FileReader INPUT_ERR_FILE = null;
419         BufferedReader INPUT_GLE_FILE_br;
420         BufferedReader INPUT_ERR_FILE_br;
421         PrintStream OUTPUT_DEMERGER_GLE_FILE_ps;
422         PrintStream OUTPUT_DEMERGER_ERR_FILE_ps;     
423         
424         try {
425             INPUT_GLE_FILE = new FileReader(validOutputFilename);
426             INPUT_ERR_FILE = new FileReader(errorOutputFilename);
427         }
428         catch (FileNotFoundException e) {
429             throw new RuntimeException(e);
430         }
431         try {
432             OUTPUT_DEMERGER_GLE_FILE_ps = new PrintStream(demergerValidOutputFilename);
433             OUTPUT_DEMERGER_ERR_FILE_ps = new PrintStream(demergerErrorOutputFilename);
434         }
435         catch (IOException e) {
436             throw new RuntimeException(e);
437         }
438 
439         int validSaved = 0;
440         int errorSaved = 0;
441         
442         int validReadLine = 0;
443         int errorReadLine = 0;
444         
445         boolean errorsLoading = false;
446         INPUT_GLE_FILE_br = new BufferedReader(INPUT_GLE_FILE);
447         INPUT_ERR_FILE_br = new BufferedReader(INPUT_ERR_FILE);
448         
449         try {
450             String currentValidLine = INPUT_GLE_FILE_br.readLine();
451             String currentErrorLine = INPUT_ERR_FILE_br.readLine();
452 
453             boolean meetFlag = false;
454             
455             while (currentValidLine != null || currentErrorLine != null) {
456                
457                 // Demerger only catch IOexception since demerger report doesn't display
458                 // detail error message.
459                 try{
460                    //validLine is null means that errorLine is not null 
461                    if (org.apache.commons.lang.StringUtils.isEmpty(currentValidLine)) {
462                        String errorDesc = currentErrorLine.substring(pMap.get(OLEPropertyConstants.TRANSACTION_LEDGER_ENTRY_DESC), pMap.get(OLEPropertyConstants.TRANSACTION_LEDGER_ENTRY_AMOUNT));
463                        String errorFinancialBalanceTypeCode = currentErrorLine.substring(pMap.get(OLEPropertyConstants.FINANCIAL_BALANCE_TYPE_CODE), pMap.get(OLEPropertyConstants.FINANCIAL_OBJECT_TYPE_CODE));
464                        
465                        if (!checkingBypassEntry(errorFinancialBalanceTypeCode, errorDesc, demergerReport)){
466                            createOutputEntry(currentErrorLine, OUTPUT_DEMERGER_ERR_FILE_ps);
467                            errorSaved++;    
468                        }
469                        currentErrorLine = INPUT_ERR_FILE_br.readLine();
470                        errorReadLine++;
471                        continue;
472                    }
473                    
474                    String financialBalanceTypeCode = currentValidLine.substring(pMap.get(OLEPropertyConstants.FINANCIAL_BALANCE_TYPE_CODE), pMap.get(OLEPropertyConstants.FINANCIAL_OBJECT_TYPE_CODE));
475                    String desc = currentValidLine.substring(pMap.get(OLEPropertyConstants.TRANSACTION_LEDGER_ENTRY_DESC), pMap.get(OLEPropertyConstants.TRANSACTION_LEDGER_ENTRY_AMOUNT));
476                    
477                    //errorLine is null means that validLine is not null
478                    if (org.apache.commons.lang.StringUtils.isEmpty(currentErrorLine)) {
479                        // Read all the transactions in the valid group and update the cost share transactions
480                        String updatedValidLine = checkAndSetTransactionTypeCostShare(financialBalanceTypeCode, desc, currentValidLine);
481                        createOutputEntry(updatedValidLine, OUTPUT_DEMERGER_GLE_FILE_ps);
482                        handleDemergerSaveValidEntry(updatedValidLine);
483                        validSaved++;
484                        currentValidLine = INPUT_GLE_FILE_br.readLine();
485                        validReadLine++;
486                        continue;
487                    }
488                    
489                    String compareStringFromValidEntry = currentValidLine.substring(pMap.get(OLEPropertyConstants.FINANCIAL_DOCUMENT_TYPE_CODE), pMap.get(OLEPropertyConstants.TRANSACTION_ENTRY_SEQUENCE_NUMBER)); 
490                    String compareStringFromErrorEntry = currentErrorLine.substring(pMap.get(OLEPropertyConstants.FINANCIAL_DOCUMENT_TYPE_CODE), pMap.get(OLEPropertyConstants.TRANSACTION_ENTRY_SEQUENCE_NUMBER));
491                    
492                    String errorDesc = currentErrorLine.substring(pMap.get(OLEPropertyConstants.TRANSACTION_LEDGER_ENTRY_DESC), pMap.get(OLEPropertyConstants.TRANSACTION_LEDGER_ENTRY_AMOUNT));
493                    String errorFinancialBalanceTypeCode = currentErrorLine.substring(pMap.get(OLEPropertyConstants.FINANCIAL_BALANCE_TYPE_CODE), pMap.get(OLEPropertyConstants.FINANCIAL_OBJECT_TYPE_CODE));
494                    
495                    if (compareStringFromValidEntry.compareTo(compareStringFromErrorEntry) < 0){
496                        // Read all the transactions in the valid group and update the cost share transactions
497                        String updatedValidLine = checkAndSetTransactionTypeCostShare(financialBalanceTypeCode, desc, currentValidLine);
498                        createOutputEntry(updatedValidLine, OUTPUT_DEMERGER_GLE_FILE_ps);
499                        handleDemergerSaveValidEntry(updatedValidLine);
500                        validSaved++;
501                        currentValidLine = INPUT_GLE_FILE_br.readLine();
502                        validReadLine++;
503        
504                    } else if (compareStringFromValidEntry.compareTo(compareStringFromErrorEntry) > 0) {
505                        if (!checkingBypassEntry(errorFinancialBalanceTypeCode, errorDesc, demergerReport)){
506                            createOutputEntry(currentErrorLine, OUTPUT_DEMERGER_ERR_FILE_ps);
507                            errorSaved++;
508                        }
509                        currentErrorLine = INPUT_ERR_FILE_br.readLine();
510                        errorReadLine++;
511 
512                    } else {
513                        if (!checkingBypassEntry(financialBalanceTypeCode, desc, demergerReport)){
514                            createOutputEntry(currentValidLine, OUTPUT_DEMERGER_ERR_FILE_ps);
515                            errorSaved++;
516                        }
517                        currentValidLine = INPUT_GLE_FILE_br.readLine();
518                        validReadLine++;
519                    }
520                    
521                    continue;
522                    
523                } catch (RuntimeException re) {
524                    LOG.error("performDemerger Stopped: " + re.getMessage());
525                    throw new RuntimeException("performDemerger Stopped: " + re.getMessage(), re);
526                }
527             }
528             INPUT_GLE_FILE_br.close();    
529             INPUT_ERR_FILE_br.close();
530             OUTPUT_DEMERGER_GLE_FILE_ps.close();
531             OUTPUT_DEMERGER_ERR_FILE_ps.close();
532             
533         } catch (IOException e) {
534             LOG.error("performDemerger Stopped: " + e.getMessage());
535             throw new RuntimeException("performDemerger Stopped: " + e.getMessage(), e);
536         }
537         demergerReport.setErrorTransactionWritten(errorSaved);
538         demergerReport.setErrorTransactionsRead(errorReadLine);
539         demergerReport.setValidTransactionsRead(validReadLine);
540         demergerReport.setValidTransactionsSaved(validSaved);
541         
542         if (!collectorMode) {
543             demergerReportWriterService.writeStatisticLine("SCRUBBER ERROR TRANSACTIONS READ       %,9d", demergerReport.getErrorTransactionsRead());
544             demergerReportWriterService.writeStatisticLine("SCRUBBER VALID TRANSACTIONS READ       %,9d", demergerReport.getValidTransactionsRead());
545             demergerReportWriterService.writeNewLines(1);
546             demergerReportWriterService.writeStatisticLine("DEMERGER ERRORS SAVED                  %,9d", demergerReport.getErrorTransactionsSaved());
547             demergerReportWriterService.writeStatisticLine("DEMERGER VALID TRANSACTIONS SAVED      %,9d", demergerReport.getValidTransactionsSaved());
548             demergerReportWriterService.writeStatisticLine("OFFSET TRANSACTIONS BYPASSED           %,9d", demergerReport.getOffsetTransactionsBypassed());
549             demergerReportWriterService.writeStatisticLine("CAPITALIZATION TRANSACTIONS BYPASSED   %,9d", demergerReport.getCapitalizationTransactionsBypassed());
550             demergerReportWriterService.writeStatisticLine("LIABILITY TRANSACTIONS BYPASSED        %,9d", demergerReport.getLiabilityTransactionsBypassed());
551             demergerReportWriterService.writeStatisticLine("TRANSFER TRANSACTIONS BYPASSED         %,9d", demergerReport.getTransferTransactionsBypassed());
552             demergerReportWriterService.writeStatisticLine("COST SHARE TRANSACTIONS BYPASSED       %,9d", demergerReport.getCostShareTransactionsBypassed());
553             demergerReportWriterService.writeStatisticLine("COST SHARE ENC TRANSACTIONS BYPASSED   %,9d", demergerReport.getCostShareEncumbranceTransactionsBypassed());
554             
555             generateDemergerRemovedTransactionsReport(demergerErrorOutputFilename);
556         }
557     }
558 
559     /**
560      * Determine the type of the transaction by looking at attributes
561      * 
562      * @param transaction Transaction to identify
563      * @return CE (Cost share encumbrance, O (Offset), C (apitalization), L (Liability), T (Transfer), CS (Cost Share), X (Other)
564      */
565     protected String getTransactionType(OriginEntryInformation transaction) {
566         if (TRANSACTION_TYPE_COST_SHARE_ENCUMBRANCE.equals(transaction.getFinancialBalanceTypeCode())) {
567             return TRANSACTION_TYPE_COST_SHARE_ENCUMBRANCE;
568         }
569         String desc = transaction.getTransactionLedgerEntryDescription();
570 
571         if (desc == null) {
572             return TRANSACTION_TYPE_OTHER;
573         }
574         if (desc.startsWith(offsetDescription) && desc.indexOf(COST_SHARE_TRANSFER_ENTRY_IND) > -1) {
575             return TRANSACTION_TYPE_COST_SHARE;
576         }
577         if (desc.startsWith(costShareDescription) && desc.indexOf(COST_SHARE_TRANSFER_ENTRY_IND) > -1) {
578             return TRANSACTION_TYPE_COST_SHARE;
579         }
580         if (desc.startsWith(offsetDescription)) {
581             return TRANSACTION_TYPE_OFFSET;
582         }
583         if (desc.startsWith(capitalizationDescription)) {
584             return TRANSACTION_TYPE_CAPITALIZATION;
585         }
586         if (desc.startsWith(liabilityDescription)) {
587             return TRANSACTION_TYPE_LIABILITY;
588         }
589         if (desc.startsWith(transferDescription)) {
590             return TRANSACTION_TYPE_TRANSFER;
591         }
592         return TRANSACTION_TYPE_OTHER;
593     }
594     
595     
596     protected String getTransactionType(String financialBalanceTypeCode, String desc) {
597         if (TRANSACTION_TYPE_COST_SHARE_ENCUMBRANCE.equals(financialBalanceTypeCode)) {
598             return TRANSACTION_TYPE_COST_SHARE_ENCUMBRANCE;
599         }
600         if (desc == null) {
601             return TRANSACTION_TYPE_OTHER;
602         }
603 
604         if (desc.startsWith(offsetDescription) && desc.indexOf(COST_SHARE_TRANSFER_ENTRY_IND) > -1) {
605             return TRANSACTION_TYPE_COST_SHARE;
606         }
607         if (desc.startsWith(costShareDescription) && desc.indexOf(COST_SHARE_TRANSFER_ENTRY_IND) > -1) {
608             return TRANSACTION_TYPE_COST_SHARE;
609         }
610         if (desc.startsWith(offsetDescription)) {
611             return TRANSACTION_TYPE_OFFSET;
612         }
613         if (desc.startsWith(capitalizationDescription)) {
614             return TRANSACTION_TYPE_CAPITALIZATION;
615         }
616         if (desc.startsWith(liabilityDescription)) {
617             return TRANSACTION_TYPE_LIABILITY;
618         }
619         if (desc.startsWith(transferDescription)) {
620             return TRANSACTION_TYPE_TRANSFER;
621         }
622         return TRANSACTION_TYPE_OTHER;
623     }
624     
625 
626     /**
627      * 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
628      * of Cobol relating to this.
629      * 
630      * @param originEntryGroup Group to process
631      */
632     protected void processGroup(boolean reportOnlyMode, ScrubberReportData scrubberReport) {
633         OriginEntryFull lastEntry = null;
634         scrubCostShareAmount = KualiDecimal.ZERO;
635         unitOfWork = new UnitOfWorkInfo();
636 
637         FileReader INPUT_GLE_FILE = null;
638         String GLEN_RECORD;
639         BufferedReader INPUT_GLE_FILE_br;
640         try {
641             INPUT_GLE_FILE = new FileReader(inputFile);
642         }
643         catch (FileNotFoundException e) {
644             throw new RuntimeException(e);
645         }
646         try {
647             OUTPUT_GLE_FILE_ps = new PrintStream(validFile);
648             OUTPUT_ERR_FILE_ps = new PrintStream(errorFile);
649             OUTPUT_EXP_FILE_ps = new PrintStream(expiredFile);
650         }
651         catch (IOException e) {
652             throw new RuntimeException(e);
653         }
654 
655         INPUT_GLE_FILE_br = new BufferedReader(INPUT_GLE_FILE);
656         int line = 0;
657         LOG.debug("Starting Scrubber Process process group...");
658         try {
659             while ((GLEN_RECORD = INPUT_GLE_FILE_br.readLine()) != null) {
660                 if (!org.apache.commons.lang.StringUtils.isEmpty(GLEN_RECORD) && !org.apache.commons.lang.StringUtils.isBlank(GLEN_RECORD.trim())) {
661                     line++;
662                     OriginEntryFull unscrubbedEntry = new OriginEntryFull();
663                     List<Message> tmperrors = unscrubbedEntry.setFromTextFileForBatch(GLEN_RECORD, line);
664                     scrubberReport.incrementUnscrubbedRecordsRead();
665                     transactionErrors = new ArrayList<Message>();
666 
667                     // 
668                     // This is done so if the code modifies this row, then saves it, it will be an insert,
669                     // and it won't touch the original. The Scrubber never modifies input rows/groups.
670                     // not relevant for file version
671 
672                     boolean saveErrorTransaction = false;
673                     boolean saveValidTransaction = false;
674                     boolean fatalErrorOccurred = false;
675 
676                     // Build a scrubbed entry
677                     OriginEntryFull scrubbedEntry = new OriginEntryFull();
678                     scrubbedEntry.setDocumentNumber(unscrubbedEntry.getDocumentNumber());
679                     scrubbedEntry.setOrganizationDocumentNumber(unscrubbedEntry.getOrganizationDocumentNumber());
680                     scrubbedEntry.setOrganizationReferenceId(unscrubbedEntry.getOrganizationReferenceId());
681                     scrubbedEntry.setReferenceFinancialDocumentNumber(unscrubbedEntry.getReferenceFinancialDocumentNumber());
682 
683                     Integer transactionNumber = unscrubbedEntry.getTransactionLedgerEntrySequenceNumber();
684                     scrubbedEntry.setTransactionLedgerEntrySequenceNumber(null == transactionNumber ? new Integer(0) : transactionNumber);
685                     scrubbedEntry.setTransactionLedgerEntryDescription(unscrubbedEntry.getTransactionLedgerEntryDescription());
686                     scrubbedEntry.setTransactionLedgerEntryAmount(unscrubbedEntry.getTransactionLedgerEntryAmount());
687                     scrubbedEntry.setTransactionDebitCreditCode(unscrubbedEntry.getTransactionDebitCreditCode());
688     
689                     if (!collectorMode) {
690                         ledgerSummaryReport.summarizeEntry(unscrubbedEntry); 
691                     }
692 
693                     // For Labor Scrubber  
694                     boolean laborIndicator = false;
695                     tmperrors.addAll(scrubberValidator.validateTransaction(unscrubbedEntry, scrubbedEntry, universityRunDate, laborIndicator, accountingCycleCachingService));
696                     transactionErrors.addAll(tmperrors);
697 
698 
699                     Account unscrubbedEntryAccount = accountingCycleCachingService.getAccount(unscrubbedEntry.getChartOfAccountsCode(), unscrubbedEntry.getAccountNumber());
700                     // KFSMI-173: both the expired and closed accounts rows are put in the expired account
701                     if ((unscrubbedEntryAccount != null) && (scrubberValidator.isAccountExpired(unscrubbedEntryAccount, universityRunDate) || unscrubbedEntryAccount.isClosed())) {
702                         // Make a copy of it so OJB doesn't just update the row in the original
703                         // group. It needs to make a new one in the expired group
704                         OriginEntryFull expiredEntry = OriginEntryFull.copyFromOriginEntryable(scrubbedEntry);
705                         createOutputEntry(expiredEntry, OUTPUT_EXP_FILE_ps);
706                         scrubberReport.incrementExpiredAccountFound();
707                     }
708 
709                     // the collector scrubber uses this map to apply the same changes made on an origin entry during scrubbing to
710                     // the collector detail record
711                     if (collectorMode) {
712                         unscrubbedToScrubbedEntries.put(unscrubbedEntry, scrubbedEntry);
713                     }
714 
715                     if (!isFatal(transactionErrors)) {
716                         saveValidTransaction = true;
717                         
718                         if (!collectorMode) {
719 
720                             // See if unit of work has changed
721                             if (!unitOfWork.isSameUnitOfWork(scrubbedEntry)) {
722                                 // Generate offset for last unit of work
723                                 // pass the String line for generating error files
724                                 generateOffset(lastEntry, scrubberReport);
725 
726                                 unitOfWork = new UnitOfWorkInfo(scrubbedEntry);
727                             }
728 
729                             KualiDecimal transactionAmount = scrubbedEntry.getTransactionLedgerEntryAmount();
730 
731                             ParameterEvaluator offsetFiscalPeriods = /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.OFFSET_FISCAL_PERIOD_CODES, scrubbedEntry.getUniversityFiscalPeriodCode());
732 
733                             BalanceType scrubbedEntryBalanceType = accountingCycleCachingService.getBalanceType(scrubbedEntry.getFinancialBalanceTypeCode());
734                             if (scrubbedEntryBalanceType.isFinancialOffsetGenerationIndicator() && offsetFiscalPeriods.evaluationSucceeds()) {
735                                 if (scrubbedEntry.isDebit()) {
736                                     unitOfWork.offsetAmount = unitOfWork.offsetAmount.add(transactionAmount);
737                                 }
738                                 else {
739                                     unitOfWork.offsetAmount = unitOfWork.offsetAmount.subtract(transactionAmount);
740                                 }
741                             }
742 
743                             // The sub account type code will only exist if there is a valid sub account
744                             String subAccountTypeCode = GeneralLedgerConstants.getSpaceSubAccountTypeCode();
745                             // major assumption: the a21 subaccount is proxied, so we don't want to query the database if the
746                             // subacct
747                             // number is dashes
748                             if (!OLEConstants.getDashSubAccountNumber().equals(scrubbedEntry.getSubAccountNumber())) {
749                                 A21SubAccount scrubbedEntryA21SubAccount = accountingCycleCachingService.getA21SubAccount(scrubbedEntry.getChartOfAccountsCode(), scrubbedEntry.getAccountNumber(), scrubbedEntry.getSubAccountNumber());
750                                 if (ObjectUtils.isNotNull(scrubbedEntryA21SubAccount)) {
751                                     subAccountTypeCode = scrubbedEntryA21SubAccount.getSubAccountTypeCode();
752                                 }
753                             }
754 
755                             ParameterEvaluator costShareObjectTypeCodes = /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.COST_SHARE_OBJ_TYPE_CODES, scrubbedEntry.getFinancialObjectTypeCode());
756                             ParameterEvaluator costShareEncBalanceTypeCodes = /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.COST_SHARE_ENC_BAL_TYP_CODES, scrubbedEntry.getFinancialBalanceTypeCode());
757                             ParameterEvaluator costShareEncFiscalPeriodCodes = /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.COST_SHARE_ENC_FISCAL_PERIOD_CODES, scrubbedEntry.getUniversityFiscalPeriodCode());
758                             ParameterEvaluator costShareEncDocTypeCodes = /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.COST_SHARE_ENC_DOC_TYPE_CODES, scrubbedEntry.getFinancialDocumentTypeCode().trim());
759                             ParameterEvaluator costShareFiscalPeriodCodes = /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.COST_SHARE_FISCAL_PERIOD_CODES, scrubbedEntry.getUniversityFiscalPeriodCode());
760                             Account scrubbedEntryAccount = accountingCycleCachingService.getAccount(scrubbedEntry.getChartOfAccountsCode(), scrubbedEntry.getAccountNumber());
761 
762                             if (costShareObjectTypeCodes.evaluationSucceeds() && costShareEncBalanceTypeCodes.evaluationSucceeds() && scrubbedEntryAccount.isForContractsAndGrants() && OLEConstants.SubAccountType.COST_SHARE.equals(subAccountTypeCode) && costShareEncFiscalPeriodCodes.evaluationSucceeds() && costShareEncDocTypeCodes.evaluationSucceeds()) {
763                                 TransactionError te1 = generateCostShareEncumbranceEntries(scrubbedEntry, scrubberReport);
764                                 if (te1 != null) {
765                                     List errors = new ArrayList();
766                                     errors.add(te1.message);
767                                     handleTransactionErrors(te1.transaction, errors);
768                                     saveValidTransaction = false;
769                                     saveErrorTransaction = true;
770                                 }
771                             }
772 
773                             SystemOptions scrubbedEntryOption = accountingCycleCachingService.getSystemOptions(scrubbedEntry.getUniversityFiscalYear());
774                             if (costShareObjectTypeCodes.evaluationSucceeds() && scrubbedEntryOption.getActualFinancialBalanceTypeCd().equals(scrubbedEntry.getFinancialBalanceTypeCode()) && scrubbedEntryAccount.isForContractsAndGrants() && OLEConstants.SubAccountType.COST_SHARE.equals(subAccountTypeCode) && costShareFiscalPeriodCodes.evaluationSucceeds() && costShareEncDocTypeCodes.evaluationSucceeds()) {
775                                 if (scrubbedEntry.isDebit()) {
776                                     scrubCostShareAmount = scrubCostShareAmount.subtract(transactionAmount);
777                                 }
778                                 else {
779                                     scrubCostShareAmount = scrubCostShareAmount.add(transactionAmount);
780                                 }
781                             }
782 
783                             ParameterEvaluator otherDocTypeCodes = /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.OFFSET_DOC_TYPE_CODES, scrubbedEntry.getFinancialDocumentTypeCode());
784 
785                             if (otherDocTypeCodes.evaluationSucceeds()) {
786                                 String m = processCapitalization(scrubbedEntry, scrubberReport);
787                                 if (m != null) {
788                                     saveValidTransaction = false;
789                                     saveErrorTransaction = false;
790                                     addTransactionError(m, "", Message.TYPE_FATAL);
791                                 }
792 
793                                 m = processLiabilities(scrubbedEntry, scrubberReport);
794                                 if (m != null) {
795                                     saveValidTransaction = false;
796                                     saveErrorTransaction = false;
797                                     addTransactionError(m, "", Message.TYPE_FATAL);
798                                 }
799 
800                                 m = processPlantIndebtedness(scrubbedEntry, scrubberReport);
801                                 if (m != null) {
802                                     saveValidTransaction = false;
803                                     saveErrorTransaction = false;
804                                     addTransactionError(m, "", Message.TYPE_FATAL);
805                                 }
806                             }
807 
808                             if (!scrubCostShareAmount.isZero()) {
809                                 TransactionError te = generateCostShareEntries(scrubbedEntry, scrubberReport);
810 
811                                 if (te != null) {
812                                     saveValidTransaction = false;
813                                     saveErrorTransaction = false;
814 
815                                     // Make a copy of it so OJB doesn't just update the row in the original
816                                     // group. It needs to make a new one in the error group
817                                     OriginEntryFull errorEntry = new OriginEntryFull(te.transaction);
818                                     errorEntry.setTransactionScrubberOffsetGenerationIndicator(false);
819                                     createOutputEntry(GLEN_RECORD, OUTPUT_ERR_FILE_ps);
820                                     scrubberReport.incrementErrorRecordWritten();
821                                     unitOfWork.errorsFound = true;
822 
823                                     handleTransactionError(te.transaction, te.message);
824                                 }
825                                 scrubCostShareAmount = KualiDecimal.ZERO;
826                             }
827 
828                             lastEntry = scrubbedEntry;
829                         }
830                     }
831                     else {
832                         // Error transaction
833                         saveErrorTransaction = true;
834                         fatalErrorOccurred = true;
835                     }
836                     handleTransactionErrors(OriginEntryFull.copyFromOriginEntryable(unscrubbedEntry), transactionErrors);
837  
838                     if (saveValidTransaction) {
839                         scrubbedEntry.setTransactionScrubberOffsetGenerationIndicator(false);
840                         createOutputEntry(scrubbedEntry, OUTPUT_GLE_FILE_ps);
841                         scrubberReport.incrementScrubbedRecordWritten();
842                     }
843 
844                     if (saveErrorTransaction) {
845                         // Make a copy of it so OJB doesn't just update the row in the original
846                         // group. It needs to make a new one in the error group
847                         OriginEntryFull errorEntry = OriginEntryFull.copyFromOriginEntryable(unscrubbedEntry);
848                         errorEntry.setTransactionScrubberOffsetGenerationIndicator(false);
849                         createOutputEntry(GLEN_RECORD, OUTPUT_ERR_FILE_ps);
850                         scrubberReport.incrementErrorRecordWritten();
851                         if (!fatalErrorOccurred) {
852                             // if a fatal error occurred, the creation of a new unit of work was by-passed;
853                             // therefore, it shouldn't ruin the previous unit of work
854                             unitOfWork.errorsFound = true;
855                         }
856                     }
857                 }
858             }
859 
860             if (!collectorMode) {
861                 // Generate last offset (if necessary)
862                 generateOffset(lastEntry, scrubberReport);
863             }
864 
865             INPUT_GLE_FILE_br.close();
866             INPUT_GLE_FILE.close();
867             OUTPUT_GLE_FILE_ps.close();
868             OUTPUT_ERR_FILE_ps.close();
869             OUTPUT_EXP_FILE_ps.close();
870 
871             handleEndOfScrubberReport(scrubberReport);
872 
873             if (!collectorMode) {
874                 ledgerSummaryReport.writeReport(this.scrubberLedgerReportWriterService);
875             }
876         }
877         catch (IOException e) {
878             throw new RuntimeException(e);
879         }
880     }
881 
882     /**
883      * Determines if a given error is fatal and should stop this scrubber run
884      * 
885      * @param errors errors from a scrubber run
886      * @return true if the run should be abended, false otherwise
887      */
888     protected boolean isFatal(List<Message> errors) {
889         for (Iterator<Message> iter = errors.iterator(); iter.hasNext();) {
890             Message element = iter.next();
891             if (element.getType() == Message.TYPE_FATAL) {
892                 return true;
893             }
894         }
895         return false;
896     }
897 
898     /**
899      * Generates a cost share entry and offset for the given entry and saves both to the valid group
900      * 
901      * @param scrubbedEntry the originEntry that was scrubbed
902      * @return a TransactionError initialized with any error encounted during entry generation, or (hopefully) null
903      */
904     protected TransactionError generateCostShareEntries(OriginEntryInformation scrubbedEntry, ScrubberReportData scrubberReport) {
905         // 3000-COST-SHARE to 3100-READ-OFSD in the cobol Generate Cost Share Entries
906         LOG.debug("generateCostShareEntries() started");
907         try {
908             OriginEntryFull costShareEntry = OriginEntryFull.copyFromOriginEntryable(scrubbedEntry);
909 
910             SystemOptions scrubbedEntryOption = accountingCycleCachingService.getSystemOptions(scrubbedEntry.getUniversityFiscalYear());
911             A21SubAccount scrubbedEntryA21SubAccount = accountingCycleCachingService.getA21SubAccount(scrubbedEntry.getChartOfAccountsCode(), scrubbedEntry.getAccountNumber(), scrubbedEntry.getSubAccountNumber());
912 
913             costShareEntry.setFinancialObjectCode(parameterService.getParameterValueAsString(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupParameters.COST_SHARE_OBJECT_CODE_PARM_NM));
914             costShareEntry.setFinancialSubObjectCode(OLEConstants.getDashFinancialSubObjectCode());
915             costShareEntry.setFinancialObjectTypeCode(scrubbedEntryOption.getFinancialObjectTypeTransferExpenseCd());
916             costShareEntry.setTransactionLedgerEntrySequenceNumber(new Integer(0));
917 
918             StringBuffer description = new StringBuffer();
919             description.append(costShareDescription);
920             description.append(" ").append(scrubbedEntry.getAccountNumber());
921             description.append(offsetString);
922             costShareEntry.setTransactionLedgerEntryDescription(description.toString());
923 
924             costShareEntry.setTransactionLedgerEntryAmount(scrubCostShareAmount);
925             if (scrubCostShareAmount.isPositive()) {
926                 costShareEntry.setTransactionDebitCreditCode(OLEConstants.GL_DEBIT_CODE);
927             }
928             else {
929                 costShareEntry.setTransactionDebitCreditCode(OLEConstants.GL_CREDIT_CODE);
930                 costShareEntry.setTransactionLedgerEntryAmount(scrubCostShareAmount.negated());
931             }
932 
933             costShareEntry.setTransactionDate(runDate);
934             costShareEntry.setOrganizationDocumentNumber(null);
935             costShareEntry.setProjectCode(OLEConstants.getDashProjectCode());
936             costShareEntry.setOrganizationReferenceId(null);
937             costShareEntry.setReferenceFinancialDocumentTypeCode(null);
938             costShareEntry.setReferenceFinancialSystemOriginationCode(null);
939             costShareEntry.setReferenceFinancialDocumentNumber(null);
940             costShareEntry.setFinancialDocumentReversalDate(null);
941             costShareEntry.setTransactionEncumbranceUpdateCode(null);
942 
943             createOutputEntry(costShareEntry, OUTPUT_GLE_FILE_ps);
944             scrubberReport.incrementCostShareEntryGenerated();
945 
946             OriginEntryFull costShareOffsetEntry = new OriginEntryFull(costShareEntry);
947             costShareOffsetEntry.setTransactionLedgerEntryDescription(getOffsetMessage());
948             OffsetDefinition offsetDefinition = accountingCycleCachingService.getOffsetDefinition(scrubbedEntry.getUniversityFiscalYear(), scrubbedEntry.getChartOfAccountsCode(), OLEConstants.TRANSFER_FUNDS, scrubbedEntry.getFinancialBalanceTypeCode());
949             if (offsetDefinition != null) {
950                 if (offsetDefinition.getFinancialObject() == null) {
951                     StringBuffer objectCodeKey = new StringBuffer();
952                     objectCodeKey.append(offsetDefinition.getUniversityFiscalYear());
953                     objectCodeKey.append("-").append(offsetDefinition.getChartOfAccountsCode());
954                     objectCodeKey.append("-").append(offsetDefinition.getFinancialObjectCode());
955 
956                     Message m = new Message(configurationService.getPropertyValueAsString(OLEKeyConstants.ERROR_OFFSET_DEFINITION_OBJECT_CODE_NOT_FOUND) + " (" + objectCodeKey.toString() + ")", Message.TYPE_FATAL);
957                     LOG.debug("generateCostShareEntries() Error 1 object not found");
958                     return new TransactionError(costShareEntry, m);
959                 }
960 
961                 costShareOffsetEntry.setFinancialObjectCode(offsetDefinition.getFinancialObjectCode());
962                 costShareOffsetEntry.setFinancialObject(offsetDefinition.getFinancialObject());
963                 costShareOffsetEntry.setFinancialSubObjectCode(OLEConstants.getDashFinancialSubObjectCode());
964             }
965             else {
966                 Map<Transaction, List<Message>> errors = new HashMap<Transaction, List<Message>>();
967 
968                 StringBuffer offsetKey = new StringBuffer("cost share transfer ");
969                 offsetKey.append(scrubbedEntry.getUniversityFiscalYear());
970                 offsetKey.append("-");
971                 offsetKey.append(scrubbedEntry.getChartOfAccountsCode());
972                 offsetKey.append("-TF-");
973                 offsetKey.append(scrubbedEntry.getFinancialBalanceTypeCode());
974 
975                 Message m = new Message(configurationService.getPropertyValueAsString(OLEKeyConstants.ERROR_OFFSET_DEFINITION_NOT_FOUND) + " (" + offsetKey.toString() + ")", Message.TYPE_FATAL);
976 
977                 LOG.debug("generateCostShareEntries() Error 2 offset not found");
978                 return new TransactionError(costShareEntry, m);
979             }
980 
981             costShareOffsetEntry.setFinancialObjectTypeCode(offsetDefinition.getFinancialObject().getFinancialObjectTypeCode());
982 
983             if (costShareEntry.isCredit()) {
984                 costShareOffsetEntry.setTransactionDebitCreditCode(OLEConstants.GL_DEBIT_CODE);
985             }
986             else {
987                 costShareOffsetEntry.setTransactionDebitCreditCode(OLEConstants.GL_CREDIT_CODE);
988             }
989 
990             try {
991                 flexibleOffsetAccountService.updateOffset(costShareOffsetEntry);
992             }
993             catch (InvalidFlexibleOffsetException e) {
994                 Message m = new Message(e.getMessage(), Message.TYPE_FATAL);
995                 if (LOG.isDebugEnabled()) {
996                     LOG.debug("generateCostShareEntries() Cost Share Transfer Flexible Offset Error: " + e.getMessage());
997                 }
998                 return new TransactionError(costShareEntry, m);
999             }
1000 
1001             createOutputEntry(costShareOffsetEntry, OUTPUT_GLE_FILE_ps);
1002             scrubberReport.incrementCostShareEntryGenerated();
1003 
1004             OriginEntryFull costShareSourceAccountEntry = new OriginEntryFull(costShareEntry);
1005 
1006             description = new StringBuffer();
1007             description.append(costShareDescription);
1008             description.append(" ").append(scrubbedEntry.getAccountNumber());
1009             description.append(offsetString);
1010             costShareSourceAccountEntry.setTransactionLedgerEntryDescription(description.toString());
1011 
1012             costShareSourceAccountEntry.setChartOfAccountsCode(scrubbedEntryA21SubAccount.getCostShareChartOfAccountCode());
1013             costShareSourceAccountEntry.setAccountNumber(scrubbedEntryA21SubAccount.getCostShareSourceAccountNumber());
1014 
1015             setCostShareObjectCode(costShareSourceAccountEntry, scrubbedEntry);
1016             costShareSourceAccountEntry.setSubAccountNumber(scrubbedEntryA21SubAccount.getCostShareSourceSubAccountNumber());
1017 
1018             if (StringHelper.isNullOrEmpty(costShareSourceAccountEntry.getSubAccountNumber())) {
1019                 costShareSourceAccountEntry.setSubAccountNumber(OLEConstants.getDashSubAccountNumber());
1020             }
1021 
1022             costShareSourceAccountEntry.setFinancialSubObjectCode(OLEConstants.getDashFinancialSubObjectCode());
1023             costShareSourceAccountEntry.setFinancialObjectTypeCode(scrubbedEntryOption.getFinancialObjectTypeTransferExpenseCd());
1024             costShareSourceAccountEntry.setTransactionLedgerEntrySequenceNumber(new Integer(0));
1025 
1026             costShareSourceAccountEntry.setTransactionLedgerEntryAmount(scrubCostShareAmount);
1027             if (scrubCostShareAmount.isPositive()) {
1028                 costShareSourceAccountEntry.setTransactionDebitCreditCode(OLEConstants.GL_CREDIT_CODE);
1029             }
1030             else {
1031                 costShareSourceAccountEntry.setTransactionDebitCreditCode(OLEConstants.GL_DEBIT_CODE);
1032                 costShareSourceAccountEntry.setTransactionLedgerEntryAmount(scrubCostShareAmount.negated());
1033             }
1034 
1035             costShareSourceAccountEntry.setTransactionDate(runDate);
1036             costShareSourceAccountEntry.setOrganizationDocumentNumber(null);
1037             costShareSourceAccountEntry.setProjectCode(OLEConstants.getDashProjectCode());
1038             costShareSourceAccountEntry.setOrganizationReferenceId(null);
1039             costShareSourceAccountEntry.setReferenceFinancialDocumentTypeCode(null);
1040             costShareSourceAccountEntry.setReferenceFinancialSystemOriginationCode(null);
1041             costShareSourceAccountEntry.setReferenceFinancialDocumentNumber(null);
1042             costShareSourceAccountEntry.setFinancialDocumentReversalDate(null);
1043             costShareSourceAccountEntry.setTransactionEncumbranceUpdateCode(null);
1044 
1045             createOutputEntry(costShareSourceAccountEntry, OUTPUT_GLE_FILE_ps);
1046             scrubberReport.incrementCostShareEntryGenerated();
1047 
1048             OriginEntryFull costShareSourceAccountOffsetEntry = new OriginEntryFull(costShareSourceAccountEntry);
1049             costShareSourceAccountOffsetEntry.setTransactionLedgerEntryDescription(getOffsetMessage());
1050 
1051             // Lookup the new offset definition.
1052             offsetDefinition = accountingCycleCachingService.getOffsetDefinition(scrubbedEntry.getUniversityFiscalYear(), scrubbedEntry.getChartOfAccountsCode(), OLEConstants.TRANSFER_FUNDS, scrubbedEntry.getFinancialBalanceTypeCode());
1053             if (offsetDefinition != null) {
1054                 if (offsetDefinition.getFinancialObject() == null) {
1055                     Map<Transaction, List<Message>> errors = new HashMap<Transaction, List<Message>>();
1056 
1057                     StringBuffer objectCodeKey = new StringBuffer();
1058                     objectCodeKey.append(costShareEntry.getUniversityFiscalYear());
1059                     objectCodeKey.append("-").append(scrubbedEntry.getChartOfAccountsCode());
1060                     objectCodeKey.append("-").append(scrubbedEntry.getFinancialObjectCode());
1061 
1062                     Message m = new Message(configurationService.getPropertyValueAsString(OLEKeyConstants.ERROR_OFFSET_DEFINITION_OBJECT_CODE_NOT_FOUND) + " (" + objectCodeKey.toString() + ")", Message.TYPE_FATAL);
1063 
1064                     LOG.debug("generateCostShareEntries() Error 3 object not found");
1065                     return new TransactionError(costShareSourceAccountEntry, m);
1066                 }
1067 
1068                 costShareSourceAccountOffsetEntry.setFinancialObjectCode(offsetDefinition.getFinancialObjectCode());
1069                 costShareSourceAccountOffsetEntry.setFinancialObject(offsetDefinition.getFinancialObject());
1070                 costShareSourceAccountOffsetEntry.setFinancialSubObjectCode(OLEConstants.getDashFinancialSubObjectCode());
1071             }
1072             else {
1073                 Map<Transaction, List<Message>> errors = new HashMap<Transaction, List<Message>>();
1074 
1075                 StringBuffer offsetKey = new StringBuffer("cost share transfer source ");
1076                 offsetKey.append(scrubbedEntry.getUniversityFiscalYear());
1077                 offsetKey.append("-");
1078                 offsetKey.append(scrubbedEntry.getChartOfAccountsCode());
1079                 offsetKey.append("-TF-");
1080                 offsetKey.append(scrubbedEntry.getFinancialBalanceTypeCode());
1081 
1082                 Message m = new Message(configurationService.getPropertyValueAsString(OLEKeyConstants.ERROR_OFFSET_DEFINITION_NOT_FOUND) + " (" + offsetKey.toString() + ")", Message.TYPE_FATAL);
1083 
1084                 LOG.debug("generateCostShareEntries() Error 4 offset not found");
1085                 return new TransactionError(costShareSourceAccountEntry, m);
1086             }
1087 
1088             costShareSourceAccountOffsetEntry.setFinancialObjectTypeCode(offsetDefinition.getFinancialObject().getFinancialObjectTypeCode());
1089 
1090             if (scrubbedEntry.isCredit()) {
1091                 costShareSourceAccountOffsetEntry.setTransactionDebitCreditCode(OLEConstants.GL_DEBIT_CODE);
1092             }
1093             else {
1094                 costShareSourceAccountOffsetEntry.setTransactionDebitCreditCode(OLEConstants.GL_CREDIT_CODE);
1095             }
1096 
1097             try {
1098                 flexibleOffsetAccountService.updateOffset(costShareSourceAccountOffsetEntry);
1099             }
1100             catch (InvalidFlexibleOffsetException e) {
1101                 Message m = new Message(e.getMessage(), Message.TYPE_FATAL);
1102                 if (LOG.isDebugEnabled()) {
1103                     LOG.debug("generateCostShareEntries() Cost Share Transfer Account Flexible Offset Error: " + e.getMessage());
1104                 }
1105                 return new TransactionError(costShareEntry, m);
1106             }
1107 
1108             createOutputEntry(costShareSourceAccountOffsetEntry, OUTPUT_GLE_FILE_ps);
1109             scrubberReport.incrementCostShareEntryGenerated();
1110 
1111             scrubCostShareAmount = KualiDecimal.ZERO;
1112         } catch (IOException ioe) {
1113             LOG.error("generateCostShareEntries() Stopped: " + ioe.getMessage());
1114             throw new RuntimeException("generateCostShareEntries() Stopped: " + ioe.getMessage(), ioe);
1115         }
1116         LOG.debug("generateCostShareEntries() successful");
1117         return null;
1118     }
1119 
1120     /**
1121      * Get all the transaction descriptions from the param table
1122      */
1123     protected void setDescriptions() {
1124         //TODO: move to constants class?
1125         offsetDescription = "GENERATED OFFSET";
1126         capitalizationDescription = "GENERATED CAPITALIZATION";
1127         liabilityDescription = "GENERATED LIABILITY";
1128         costShareDescription = "GENERATED COST SHARE FROM";
1129         transferDescription = "GENERATED TRANSFER FROM";
1130     }
1131 
1132     /**
1133      * Generate the flag for the end of specific descriptions. This will be used in the demerger step
1134      */
1135     protected void setOffsetString() {
1136 
1137         NumberFormat nf = NumberFormat.getInstance();
1138         nf.setMaximumFractionDigits(0);
1139         nf.setMaximumIntegerDigits(2);
1140         nf.setMinimumFractionDigits(0);
1141         nf.setMinimumIntegerDigits(2);
1142 
1143         offsetString = COST_SHARE_TRANSFER_ENTRY_IND + nf.format(runCal.get(Calendar.MONTH) + 1) + nf.format(runCal.get(Calendar.DAY_OF_MONTH));
1144     }
1145 
1146     /**
1147      * Generate the offset message with the flag at the end
1148      * 
1149      * @return a generated offset message
1150      */
1151     protected String getOffsetMessage() {
1152         String msg = offsetDescription + GeneralLedgerConstants.getSpaceTransactionLedgetEntryDescription();
1153 
1154         return msg.substring(0, OFFSET_MESSAGE_MAXLENGTH) + offsetString;
1155     }
1156 
1157     /**
1158      * Generates capitalization entries if necessary
1159      * 
1160      * @param scrubbedEntry the entry to generate capitalization entries (possibly) for
1161      * @return null if no error, message if error
1162      */
1163     protected String processCapitalization(OriginEntryInformation scrubbedEntry, ScrubberReportData scrubberReport) {
1164         
1165         try {
1166          // Lines 4694 - 4798 of the Pro Cobol listing on Confluence
1167             if (!parameterService.getParameterValueAsBoolean(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupParameters.CAPITALIZATION_IND)) {
1168                 return null;
1169             }
1170 
1171             OriginEntryFull capitalizationEntry = OriginEntryFull.copyFromOriginEntryable(scrubbedEntry);
1172             SystemOptions scrubbedEntryOption = accountingCycleCachingService.getSystemOptions(scrubbedEntry.getUniversityFiscalYear());
1173             ObjectCode scrubbedEntryObjectCode = accountingCycleCachingService.getObjectCode(scrubbedEntry.getUniversityFiscalYear(), scrubbedEntry.getChartOfAccountsCode(), scrubbedEntry.getFinancialObjectCode());
1174             Chart scrubbedEntryChart = accountingCycleCachingService.getChart(scrubbedEntry.getChartOfAccountsCode());
1175             Account scrubbedEntryAccount = accountingCycleCachingService.getAccount(scrubbedEntry.getChartOfAccountsCode(), scrubbedEntry.getAccountNumber());
1176 
1177             ParameterEvaluator documentTypeCodes = (!ObjectUtils.isNull(scrubbedEntry)) ? /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.CAPITALIZATION_DOC_TYPE_CODES, scrubbedEntry.getFinancialDocumentTypeCode()) : null;
1178             ParameterEvaluator fiscalPeriodCodes = (!ObjectUtils.isNull(scrubbedEntry)) ? /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.CAPITALIZATION_FISCAL_PERIOD_CODES, scrubbedEntry.getUniversityFiscalPeriodCode()) : null;
1179             ParameterEvaluator objectSubTypeCodes = (!ObjectUtils.isNull(scrubbedEntryObjectCode)) ? /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.CAPITALIZATION_OBJ_SUB_TYPE_CODES, scrubbedEntryObjectCode.getFinancialObjectSubTypeCode()) : null;
1180             ParameterEvaluator subFundGroupCodes = (!ObjectUtils.isNull(scrubbedEntryAccount)) ? /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.CAPITALIZATION_SUB_FUND_GROUP_CODES, scrubbedEntryAccount.getSubFundGroupCode()) : null;
1181             ParameterEvaluator chartCodes = (!ObjectUtils.isNull(scrubbedEntry)) ? /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.CAPITALIZATION_CHART_CODES, scrubbedEntry.getChartOfAccountsCode()) : null;
1182 
1183             if (scrubbedEntry.getFinancialBalanceTypeCode().equals(scrubbedEntryOption.getActualFinancialBalanceTypeCd()) && scrubbedEntry.getUniversityFiscalYear().intValue() > 1995 && (documentTypeCodes != null && documentTypeCodes.evaluationSucceeds()) && (fiscalPeriodCodes != null && fiscalPeriodCodes.evaluationSucceeds()) && (objectSubTypeCodes != null && objectSubTypeCodes.evaluationSucceeds()) && (subFundGroupCodes != null && subFundGroupCodes.evaluationSucceeds()) && (chartCodes != null && chartCodes.evaluationSucceeds())) {
1184 
1185                 String objectSubTypeCode = scrubbedEntryObjectCode.getFinancialObjectSubTypeCode();
1186 
1187                 String capitalizationObjectCode = parameterService.getSubParameterValueAsString(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupParameters.CAPITALIZATION_SUBTYPE_OBJECT, objectSubTypeCode);
1188                 if ( org.apache.commons.lang.StringUtils.isNotBlank( capitalizationObjectCode ) ) {
1189                     capitalizationEntry.setFinancialObjectCode(capitalizationObjectCode);
1190                     capitalizationEntry.setFinancialObject(accountingCycleCachingService.getObjectCode(capitalizationEntry.getUniversityFiscalYear(), capitalizationEntry.getChartOfAccountsCode(), capitalizationEntry.getFinancialObjectCode()));
1191                 }
1192 
1193                 // financialSubObjectCode should always be changed to dashes for capitalization entries
1194                 capitalizationEntry.setFinancialSubObjectCode(OLEConstants.getDashFinancialSubObjectCode());
1195 
1196                 capitalizationEntry.setFinancialObjectTypeCode(scrubbedEntryOption.getFinancialObjectTypeAssetsCd());
1197                 capitalizationEntry.setTransactionLedgerEntryDescription(capitalizationDescription);
1198 
1199                 plantFundAccountLookup(scrubbedEntry, capitalizationEntry);
1200 
1201                 capitalizationEntry.setUniversityFiscalPeriodCode(scrubbedEntry.getUniversityFiscalPeriodCode());
1202 
1203                 createOutputEntry(capitalizationEntry, OUTPUT_GLE_FILE_ps);
1204                 scrubberReport.incrementCapitalizationEntryGenerated();
1205 
1206                 // Clear out the id & the ojb version number to make sure we do an insert on the next one
1207                 capitalizationEntry.setVersionNumber(null);
1208                 capitalizationEntry.setEntryId(null);
1209                 
1210                 // Check system parameters for overriding fund balance object code; otherwise, use
1211                 // the chart fund balance object code.
1212                 String fundBalanceCode    = parameterService.getParameterValueAsString(
1213                         ScrubberStep.class, 
1214                         GlParameterConstants.CAPITALIZATION_OFFSET_CODE);
1215                 
1216                 ObjectCode fundObjectCode = getFundBalanceObjectCode(fundBalanceCode, capitalizationEntry);
1217                 
1218                 if (fundObjectCode != null) {
1219                     capitalizationEntry.setFinancialObjectTypeCode(fundObjectCode.getFinancialObjectTypeCode());
1220                     capitalizationEntry.setFinancialObjectCode(fundBalanceCode);
1221                 }
1222                 else {
1223                     capitalizationEntry.setFinancialObjectCode(scrubbedEntryChart.getFundBalanceObjectCode());
1224                     //TODO: check to see if COBOL does this - seems weird - is this saying if the object code doesn't exist use the value from options?  Shouldn't it always come from one or the other?
1225                     if (ObjectUtils.isNotNull(scrubbedEntryChart.getFundBalanceObject())) {
1226                         capitalizationEntry.setFinancialObjectTypeCode(scrubbedEntryChart.getFundBalanceObject().getFinancialObjectTypeCode());
1227                     }
1228                     else {
1229                         capitalizationEntry.setFinancialObjectTypeCode(scrubbedEntryOption.getFinObjectTypeFundBalanceCd());
1230                     }
1231                 }
1232                 
1233                 populateTransactionDebtCreditCode(scrubbedEntry, capitalizationEntry);
1234 
1235                 try {
1236                     flexibleOffsetAccountService.updateOffset(capitalizationEntry);
1237                 }
1238                 catch (InvalidFlexibleOffsetException e) {
1239                     if (LOG.isDebugEnabled()) {
1240                         LOG.debug("processCapitalization() Capitalization Flexible Offset Error: " + e.getMessage());
1241                     }
1242                     return e.getMessage();
1243                 }
1244 
1245                 createOutputEntry(capitalizationEntry, OUTPUT_GLE_FILE_ps);
1246                 scrubberReport.incrementCapitalizationEntryGenerated();
1247             }
1248         } catch (IOException ioe) {
1249             LOG.error("processCapitalization() Stopped: " + ioe.getMessage());
1250             throw new RuntimeException("processCapitalization() Stopped: " + ioe.getMessage(), ioe);
1251         }
1252         return null;
1253     }
1254     
1255     /**
1256      * Generates the plant indebtedness entries
1257      * 
1258      * @param scrubbedEntry the entry to generated plant indebtedness entries for if necessary
1259      * @return null if no error, message if error
1260      */
1261     protected String processPlantIndebtedness(OriginEntryInformation scrubbedEntry, ScrubberReportData scrubberReport) {
1262         try{
1263             // Lines 4855 - 4979 of the Pro Cobol listing on Confluence 
1264             if (!parameterService.getParameterValueAsBoolean(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupParameters.PLANT_INDEBTEDNESS_IND)) {
1265                 return null;
1266             }
1267 
1268             OriginEntryFull plantIndebtednessEntry = OriginEntryFull.copyFromOriginEntryable(scrubbedEntry);
1269 
1270             SystemOptions scrubbedEntryOption = accountingCycleCachingService.getSystemOptions(scrubbedEntry.getUniversityFiscalYear());
1271             ObjectCode scrubbedEntryObjectCode = accountingCycleCachingService.getObjectCode(scrubbedEntry.getUniversityFiscalYear(), scrubbedEntry.getChartOfAccountsCode(), scrubbedEntry.getFinancialObjectCode());
1272             Account scrubbedEntryAccount = accountingCycleCachingService.getAccount(scrubbedEntry.getChartOfAccountsCode(), scrubbedEntry.getAccountNumber());
1273             Chart scrubbedEntryChart = accountingCycleCachingService.getChart(scrubbedEntry.getChartOfAccountsCode());
1274             if (!ObjectUtils.isNull(scrubbedEntryAccount)) {
1275                 scrubbedEntryAccount.setOrganization(accountingCycleCachingService.getOrganization(scrubbedEntryAccount.getChartOfAccountsCode(), scrubbedEntryAccount.getOrganizationCode()));
1276             }
1277             
1278             ParameterEvaluator objectSubTypeCodes = (!ObjectUtils.isNull(scrubbedEntryObjectCode)) ? /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.PLANT_INDEBTEDNESS_OBJ_SUB_TYPE_CODES, scrubbedEntryObjectCode.getFinancialObjectSubTypeCode()) : null;
1279             ParameterEvaluator subFundGroupCodes = (!ObjectUtils.isNull(scrubbedEntryAccount)) ? /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.PLANT_INDEBTEDNESS_SUB_FUND_GROUP_CODES, scrubbedEntryAccount.getSubFundGroupCode()) : null;
1280 
1281             if (scrubbedEntry.getFinancialBalanceTypeCode().equals(scrubbedEntryOption.getActualFinancialBalanceTypeCd()) && (subFundGroupCodes != null && subFundGroupCodes.evaluationSucceeds()) && (objectSubTypeCodes != null && objectSubTypeCodes.evaluationSucceeds())) {
1282 
1283                 plantIndebtednessEntry.setTransactionLedgerEntryDescription(OLEConstants.PLANT_INDEBTEDNESS_ENTRY_DESCRIPTION);
1284                 populateTransactionDebtCreditCode(scrubbedEntry, plantIndebtednessEntry);
1285 
1286                 plantIndebtednessEntry.setTransactionScrubberOffsetGenerationIndicator(true);
1287                 createOutputEntry(plantIndebtednessEntry, OUTPUT_GLE_FILE_ps);
1288                 scrubberReport.incrementPlantIndebtednessEntryGenerated();
1289 
1290                 // Clear out the id & the ojb version number to make sure we do an insert on the next one
1291                 plantIndebtednessEntry.setVersionNumber(null);
1292                 plantIndebtednessEntry.setEntryId(null);
1293 
1294                 // Check system parameters for overriding fund balance object code; otherwise, use
1295                 // the chart fund balance object code.
1296                 String fundBalanceCode    = parameterService.getParameterValueAsString(
1297                         ScrubberStep.class, 
1298                         GlParameterConstants.PLANT_INDEBTEDNESS_OFFSET_CODE);
1299                 
1300                 ObjectCode fundObjectCode = getFundBalanceObjectCode(fundBalanceCode, plantIndebtednessEntry);
1301                 if (fundObjectCode != null) {
1302                     plantIndebtednessEntry.setFinancialObjectTypeCode(fundObjectCode.getFinancialObjectTypeCode());
1303                     plantIndebtednessEntry.setFinancialObjectCode(fundBalanceCode);
1304                 }
1305                 else {
1306                     plantIndebtednessEntry.setFinancialObjectTypeCode(scrubbedEntryOption.getFinObjectTypeFundBalanceCd());
1307                     plantIndebtednessEntry.setFinancialObjectCode(scrubbedEntryChart.getFundBalanceObjectCode());
1308                 }
1309                 
1310                 plantIndebtednessEntry.setTransactionDebitCreditCode(scrubbedEntry.getTransactionDebitCreditCode());
1311 
1312                 plantIndebtednessEntry.setTransactionScrubberOffsetGenerationIndicator(true);
1313                 plantIndebtednessEntry.setFinancialSubObjectCode(OLEConstants.getDashFinancialSubObjectCode());
1314 
1315                 try {
1316                     flexibleOffsetAccountService.updateOffset(plantIndebtednessEntry);
1317                 }
1318                 catch (InvalidFlexibleOffsetException e) {
1319                     LOG.error("processPlantIndebtedness() Flexible Offset Exception (1)", e);
1320                     if (LOG.isDebugEnabled()) {
1321                         LOG.debug("processPlantIndebtedness() Plant Indebtedness Flexible Offset Error: " + e.getMessage());
1322                     }
1323                     return e.getMessage();
1324                 }
1325 
1326                 createOutputEntry(plantIndebtednessEntry, OUTPUT_GLE_FILE_ps);
1327                 scrubberReport.incrementPlantIndebtednessEntryGenerated();
1328 
1329                 // Clear out the id & the ojb version number to make sure we do an insert on the next one
1330                 plantIndebtednessEntry.setVersionNumber(null);
1331                 plantIndebtednessEntry.setEntryId(null);
1332 
1333                 plantIndebtednessEntry.setFinancialObjectCode(scrubbedEntry.getFinancialObjectCode());
1334                 plantIndebtednessEntry.setFinancialObjectTypeCode(scrubbedEntry.getFinancialObjectTypeCode());
1335                 plantIndebtednessEntry.setTransactionDebitCreditCode(scrubbedEntry.getTransactionDebitCreditCode());
1336 
1337                 plantIndebtednessEntry.setTransactionLedgerEntryDescription(scrubbedEntry.getTransactionLedgerEntryDescription());
1338 
1339                 plantIndebtednessEntry.setAccountNumber(scrubbedEntry.getAccountNumber());
1340                 plantIndebtednessEntry.setSubAccountNumber(scrubbedEntry.getSubAccountNumber());
1341 
1342                 plantIndebtednessEntry.setAccountNumber(scrubbedEntryAccount.getOrganization().getCampusPlantAccountNumber());
1343                 plantIndebtednessEntry.setChartOfAccountsCode(scrubbedEntryAccount.getOrganization().getCampusPlantChartCode());
1344 
1345                 plantIndebtednessEntry.setSubAccountNumber(OLEConstants.getDashSubAccountNumber());
1346                 plantIndebtednessEntry.setFinancialSubObjectCode(OLEConstants.getDashFinancialSubObjectCode());
1347 
1348                 StringBuffer litGenPlantXferFrom = new StringBuffer();
1349                 litGenPlantXferFrom.append(transferDescription + " ");
1350                 litGenPlantXferFrom.append(scrubbedEntry.getChartOfAccountsCode()).append(" ");
1351                 litGenPlantXferFrom.append(scrubbedEntry.getAccountNumber());
1352                 plantIndebtednessEntry.setTransactionLedgerEntryDescription(litGenPlantXferFrom.toString());
1353 
1354                 createOutputEntry(plantIndebtednessEntry, OUTPUT_GLE_FILE_ps);
1355                 scrubberReport.incrementPlantIndebtednessEntryGenerated();
1356 
1357                 // Clear out the id & the ojb version number to make sure we do an insert on the next one
1358                 plantIndebtednessEntry.setVersionNumber(null);
1359                 plantIndebtednessEntry.setEntryId(null);
1360 
1361                 plantIndebtednessEntry.setFinancialObjectCode(scrubbedEntryChart.getFundBalanceObjectCode());
1362                 plantIndebtednessEntry.setFinancialObjectTypeCode(scrubbedEntryOption.getFinObjectTypeFundBalanceCd());
1363                 plantIndebtednessEntry.setFinancialSubObjectCode(OLEConstants.getDashFinancialSubObjectCode());
1364 
1365                 populateTransactionDebtCreditCode(scrubbedEntry, plantIndebtednessEntry);
1366 
1367                 try {
1368                     flexibleOffsetAccountService.updateOffset(plantIndebtednessEntry);
1369                 }
1370                 catch (InvalidFlexibleOffsetException e) {
1371                     LOG.error("processPlantIndebtedness() Flexible Offset Exception (2)", e);
1372                     LOG.debug("processPlantIndebtedness() Plant Indebtedness Flexible Offset Error: " + e.getMessage());
1373                     return e.getMessage();
1374                 }
1375 
1376                 createOutputEntry(plantIndebtednessEntry, OUTPUT_GLE_FILE_ps);
1377                 scrubberReport.incrementPlantIndebtednessEntryGenerated();
1378             }
1379         } catch (IOException ioe) {
1380             LOG.error("processPlantIndebtedness() Stopped: " + ioe.getMessage());
1381             throw new RuntimeException("processPlantIndebtedness() Stopped: " + ioe.getMessage(), ioe);
1382         }
1383         return null;
1384     }
1385 
1386     /**
1387      * Generate the liability entries for the entry if necessary
1388      * 
1389      * @param scrubbedEntry the entry to generate liability entries for if necessary
1390      * @return null if no error, message if error
1391      */
1392     protected String processLiabilities(OriginEntryInformation scrubbedEntry, ScrubberReportData scrubberReport) {
1393         try{
1394             // Lines 4799 to 4839 of the Pro Cobol list of the scrubber on Confluence
1395             if (!parameterService.getParameterValueAsBoolean(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupParameters.LIABILITY_IND)) {
1396                 return null;
1397             }
1398 
1399             Chart scrubbedEntryChart = accountingCycleCachingService.getChart(scrubbedEntry.getChartOfAccountsCode());
1400             SystemOptions scrubbedEntryOption = accountingCycleCachingService.getSystemOptions(scrubbedEntry.getUniversityFiscalYear());
1401             ObjectCode scrubbedEntryFinancialObject = accountingCycleCachingService.getObjectCode(scrubbedEntry.getUniversityFiscalYear(), scrubbedEntry.getChartOfAccountsCode(), scrubbedEntry.getFinancialObjectCode());
1402             Account scrubbedEntryAccount = accountingCycleCachingService.getAccount(scrubbedEntry.getChartOfAccountsCode(), scrubbedEntry.getAccountNumber());
1403 
1404             ParameterEvaluator chartCodes = (!ObjectUtils.isNull(scrubbedEntry)) ? /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.LIABILITY_CHART_CODES, scrubbedEntry.getChartOfAccountsCode()) : null;
1405             ParameterEvaluator docTypeCodes = (!ObjectUtils.isNull(scrubbedEntry)) ? /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.LIABILITY_DOC_TYPE_CODES, scrubbedEntry.getFinancialDocumentTypeCode()) : null;
1406             ParameterEvaluator fiscalPeriods = (!ObjectUtils.isNull(scrubbedEntry)) ? /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.LIABILITY_FISCAL_PERIOD_CODES, scrubbedEntry.getUniversityFiscalPeriodCode()) : null;
1407             ParameterEvaluator objSubTypeCodes = (!ObjectUtils.isNull(scrubbedEntryFinancialObject)) ? /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.LIABILITY_OBJ_SUB_TYPE_CODES, scrubbedEntryFinancialObject.getFinancialObjectSubTypeCode()) : null;
1408             ParameterEvaluator subFundGroupCodes = (!ObjectUtils.isNull(scrubbedEntryAccount)) ? /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.LIABILITY_SUB_FUND_GROUP_CODES, scrubbedEntryAccount.getSubFundGroupCode()) : null;
1409 
1410             if (scrubbedEntry.getFinancialBalanceTypeCode().equals(scrubbedEntryOption.getActualFinancialBalanceTypeCd()) && scrubbedEntry.getUniversityFiscalYear().intValue() > 1995 && (docTypeCodes != null && docTypeCodes.evaluationSucceeds()) && (fiscalPeriods != null && fiscalPeriods.evaluationSucceeds()) && (objSubTypeCodes != null && objSubTypeCodes.evaluationSucceeds()) && (subFundGroupCodes != null && subFundGroupCodes.evaluationSucceeds()) && (chartCodes != null && chartCodes.evaluationSucceeds())) {
1411                 OriginEntryFull liabilityEntry = OriginEntryFull.copyFromOriginEntryable(scrubbedEntry);
1412 
1413                 liabilityEntry.setFinancialObjectCode(parameterService.getParameterValueAsString(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupParameters.LIABILITY_OBJECT_CODE));
1414                 liabilityEntry.setFinancialObjectTypeCode(scrubbedEntryOption.getFinObjectTypeLiabilitiesCode());
1415 
1416                 liabilityEntry.setTransactionDebitCreditCode(scrubbedEntry.getTransactionDebitCreditCode());
1417                 liabilityEntry.setTransactionLedgerEntryDescription(liabilityDescription);
1418                 plantFundAccountLookup(scrubbedEntry, liabilityEntry);
1419 
1420                 createOutputEntry(liabilityEntry, OUTPUT_GLE_FILE_ps);
1421                 scrubberReport.incrementLiabilityEntryGenerated();
1422 
1423                 // Clear out the id & the ojb version number to make sure we do an insert on the next one
1424                 liabilityEntry.setVersionNumber(null);
1425                 liabilityEntry.setEntryId(null);
1426 
1427                 // Check system parameters for overriding fund balance object code; otherwise, use
1428                 // the chart fund balance object code.
1429                 String fundBalanceCode    = parameterService.getParameterValueAsString(
1430                         ScrubberStep.class, 
1431                         GlParameterConstants.LIABILITY_OFFSET_CODE);
1432                 
1433                 ObjectCode fundObjectCode = getFundBalanceObjectCode(fundBalanceCode, liabilityEntry);
1434                 if (fundObjectCode != null) {
1435                     liabilityEntry.setFinancialObjectTypeCode(fundObjectCode.getFinancialObjectTypeCode());
1436                     liabilityEntry.setFinancialObjectCode(fundBalanceCode);
1437                 }
1438                 else {
1439                     // ... and now generate the offset half of the liability entry
1440                     liabilityEntry.setFinancialObjectCode(scrubbedEntryChart.getFundBalanceObjectCode());
1441                     if (ObjectUtils.isNotNull(scrubbedEntryChart.getFundBalanceObject())) {
1442                         liabilityEntry.setFinancialObjectTypeCode(scrubbedEntryChart.getFundBalanceObject().getFinancialObjectTypeCode());
1443                     }
1444                     else {
1445                         liabilityEntry.setFinancialObjectTypeCode(scrubbedEntryOption.getFinObjectTypeFundBalanceCd());
1446                     }
1447                 }
1448                 
1449                 if (liabilityEntry.isDebit()) {
1450                     liabilityEntry.setTransactionDebitCreditCode(OLEConstants.GL_CREDIT_CODE);
1451                 }
1452                 else {
1453                     liabilityEntry.setTransactionDebitCreditCode(OLEConstants.GL_DEBIT_CODE);
1454                 }
1455 
1456                 try {
1457                     flexibleOffsetAccountService.updateOffset(liabilityEntry);
1458                 }
1459                 catch (InvalidFlexibleOffsetException e) {
1460                     if (LOG.isDebugEnabled()) {
1461                         LOG.debug("processLiabilities() Liability Flexible Offset Error: " + e.getMessage());
1462                     }
1463                     return e.getMessage();
1464                 }
1465 
1466                 createOutputEntry(liabilityEntry, OUTPUT_GLE_FILE_ps);
1467                 scrubberReport.incrementLiabilityEntryGenerated();
1468             }
1469         } catch (IOException ioe) {
1470             LOG.error("processLiabilities() Stopped: " + ioe.getMessage());
1471             throw new RuntimeException("processLiabilities() Stopped: " + ioe.getMessage(), ioe);
1472         } 
1473         return null;
1474     }
1475     
1476     /**
1477      * 
1478      * This method...
1479      * @param fundBalanceCodeParameter
1480      * @param originEntryFull
1481      * @return
1482      */
1483     protected ObjectCode getFundBalanceObjectCode(String fundBalanceCode, OriginEntryFull originEntryFull)
1484     {
1485         ObjectCode fundBalanceObjectCode = null;
1486         if (fundBalanceCode != null) {
1487             Map<String, Object> criteriaMap = new HashMap<String, Object>();
1488             criteriaMap.put("universityFiscalYear", originEntryFull.getUniversityFiscalYear());
1489             criteriaMap.put("chartOfAccountsCode", originEntryFull.getChartOfAccountsCode());
1490             criteriaMap.put("financialObjectCode",  fundBalanceCode);
1491             
1492             fundBalanceObjectCode = ((ObjectCode) businessObjectService.findByPrimaryKey(ObjectCode.class, criteriaMap));
1493         }
1494         
1495         return fundBalanceObjectCode;
1496     }
1497     
1498     /**
1499      * 
1500      * This method...
1501      * @param scrubbedEntry
1502      * @param fullEntry
1503      */
1504     protected void populateTransactionDebtCreditCode(OriginEntryInformation scrubbedEntry, OriginEntryFull fullEntry)
1505     {
1506         if (scrubbedEntry.isDebit()) {
1507             fullEntry.setTransactionDebitCreditCode(OLEConstants.GL_CREDIT_CODE);
1508         }
1509         else {
1510             fullEntry.setTransactionDebitCreditCode(OLEConstants.GL_DEBIT_CODE);
1511         }
1512     }
1513     
1514     /**
1515      * Updates the entries with the proper chart and account for the plant fund
1516      * 
1517      * @param scrubbedEntry basis for plant fund entry
1518      * @param liabilityEntry liability entry
1519      */
1520     protected void plantFundAccountLookup(OriginEntryInformation scrubbedEntry, OriginEntryFull liabilityEntry) {
1521         // 4000-PLANT-FUND-ACCT to 4000-PLANT-FUND-ACCT-EXIT in cobol
1522         
1523         liabilityEntry.setSubAccountNumber(OLEConstants.getDashSubAccountNumber());
1524         ObjectCode scrubbedEntryObjectCode = accountingCycleCachingService.getObjectCode(scrubbedEntry.getUniversityFiscalYear(), scrubbedEntry.getChartOfAccountsCode(), scrubbedEntry.getFinancialObjectCode());
1525         Account scrubbedEntryAccount = accountingCycleCachingService.getAccount(scrubbedEntry.getChartOfAccountsCode(), scrubbedEntry.getAccountNumber());
1526         scrubbedEntryAccount.setOrganization(accountingCycleCachingService.getOrganization(scrubbedEntryAccount.getChartOfAccountsCode(), scrubbedEntryAccount.getOrganizationCode()));
1527 
1528         if (!ObjectUtils.isNull(scrubbedEntryAccount) && !ObjectUtils.isNull(scrubbedEntryObjectCode)) {
1529             String objectSubTypeCode = scrubbedEntryObjectCode.getFinancialObjectSubTypeCode();
1530             ParameterEvaluator campusObjSubTypeCodes = /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.PLANT_FUND_CAMPUS_OBJECT_SUB_TYPE_CODES, objectSubTypeCode);
1531             ParameterEvaluator orgObjSubTypeCodes = /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.PLANT_FUND_ORG_OBJECT_SUB_TYPE_CODES, objectSubTypeCode);
1532 
1533             if (campusObjSubTypeCodes.evaluationSucceeds()) {
1534                 liabilityEntry.setAccountNumber(scrubbedEntryAccount.getOrganization().getCampusPlantAccountNumber());
1535                 liabilityEntry.setChartOfAccountsCode(scrubbedEntryAccount.getOrganization().getCampusPlantChartCode());
1536             }
1537             else if (orgObjSubTypeCodes.evaluationSucceeds()) {
1538                 liabilityEntry.setAccountNumber(scrubbedEntryAccount.getOrganization().getOrganizationPlantAccountNumber());
1539                 liabilityEntry.setChartOfAccountsCode(scrubbedEntryAccount.getOrganization().getOrganizationPlantChartCode());
1540             }
1541         }
1542     }
1543 
1544     /**
1545      * The purpose of this method is to generate a "Cost Share Encumbrance"
1546      * transaction for the current transaction and its offset. The cost share chart and account for current transaction are obtained
1547      * from the CA_A21_SUB_ACCT_T table. This method calls the method SET-OBJECT-2004 to get the Cost Share Object Code. It then
1548      * writes out the cost share transaction. Next it read the GL_OFFSET_DEFN_T table for the offset object code that corresponds to
1549      * the cost share object code. In addition to the object code it needs to get subobject code. It then reads the CA_OBJECT_CODE_T
1550      * table to make sure the offset object code found in the GL_OFFSET_DEFN_T is valid and to get the object type code associated
1551      * with this object code. It writes out the offset transaction and returns.
1552      * 
1553      * @param scrubbedEntry the entry to perhaps create a cost share encumbrance for 
1554      * @return a message if there was an error encountered generating the entries, or (hopefully) null if no errors were encountered
1555      */
1556     protected TransactionError generateCostShareEncumbranceEntries(OriginEntryInformation scrubbedEntry, ScrubberReportData scrubberReport) {
1557         try{
1558             // 3200-COST-SHARE-ENC to 3200-CSE-EXIT in the COBOL
1559             LOG.debug("generateCostShareEncumbranceEntries() started");
1560 
1561             OriginEntryFull costShareEncumbranceEntry = OriginEntryFull.copyFromOriginEntryable(scrubbedEntry);
1562 
1563             // First 28 characters of the description, padding to 28 if shorter)
1564             StringBuffer buffer = new StringBuffer((scrubbedEntry.getTransactionLedgerEntryDescription() + GeneralLedgerConstants.getSpaceTransactionLedgetEntryDescription()).substring(0, COST_SHARE_ENCUMBRANCE_ENTRY_MAXLENGTH));
1565 
1566             buffer.append("FR-");
1567             buffer.append(costShareEncumbranceEntry.getChartOfAccountsCode());
1568             buffer.append(costShareEncumbranceEntry.getAccountNumber());
1569 
1570             costShareEncumbranceEntry.setTransactionLedgerEntryDescription(buffer.toString());
1571 
1572             A21SubAccount scrubbedEntryA21SubAccount = accountingCycleCachingService.getA21SubAccount(scrubbedEntry.getChartOfAccountsCode(), scrubbedEntry.getAccountNumber(), scrubbedEntry.getSubAccountNumber());
1573             SystemOptions scrubbedEntryOption = accountingCycleCachingService.getSystemOptions(scrubbedEntry.getUniversityFiscalYear());
1574 
1575             costShareEncumbranceEntry.setChartOfAccountsCode(scrubbedEntryA21SubAccount.getCostShareChartOfAccountCode());
1576             costShareEncumbranceEntry.setAccountNumber(scrubbedEntryA21SubAccount.getCostShareSourceAccountNumber());
1577             costShareEncumbranceEntry.setSubAccountNumber(scrubbedEntryA21SubAccount.getCostShareSourceSubAccountNumber());
1578 
1579             if (!StringUtils.hasText(costShareEncumbranceEntry.getSubAccountNumber())) {
1580                 costShareEncumbranceEntry.setSubAccountNumber(OLEConstants.getDashSubAccountNumber());
1581             }
1582 
1583             costShareEncumbranceEntry.setFinancialBalanceTypeCode(scrubbedEntryOption.getCostShareEncumbranceBalanceTypeCd());
1584             setCostShareObjectCode(costShareEncumbranceEntry, scrubbedEntry);
1585             costShareEncumbranceEntry.setFinancialSubObjectCode(OLEConstants.getDashFinancialSubObjectCode());
1586             costShareEncumbranceEntry.setTransactionLedgerEntrySequenceNumber(new Integer(0));
1587 
1588             if (!StringUtils.hasText(scrubbedEntry.getTransactionDebitCreditCode())) {
1589                 if (scrubbedEntry.getTransactionLedgerEntryAmount().isPositive()) {
1590                     costShareEncumbranceEntry.setTransactionDebitCreditCode(OLEConstants.GL_DEBIT_CODE);
1591                 }
1592                 else {
1593                     costShareEncumbranceEntry.setTransactionDebitCreditCode(OLEConstants.GL_CREDIT_CODE);
1594                     costShareEncumbranceEntry.setTransactionLedgerEntryAmount(scrubbedEntry.getTransactionLedgerEntryAmount().negated());
1595                 }
1596             }
1597 
1598             costShareEncumbranceEntry.setTransactionDate(runDate);
1599 
1600             costShareEncumbranceEntry.setTransactionScrubberOffsetGenerationIndicator(true);
1601             createOutputEntry(costShareEncumbranceEntry, OUTPUT_GLE_FILE_ps);
1602             scrubberReport.incrementCostShareEncumbranceGenerated();
1603 
1604             OriginEntryFull costShareEncumbranceOffsetEntry = new OriginEntryFull(costShareEncumbranceEntry);
1605             costShareEncumbranceOffsetEntry.setTransactionLedgerEntryDescription(offsetDescription);
1606             OffsetDefinition offset = accountingCycleCachingService.getOffsetDefinition(costShareEncumbranceEntry.getUniversityFiscalYear(), costShareEncumbranceEntry.getChartOfAccountsCode(), costShareEncumbranceEntry.getFinancialDocumentTypeCode(), costShareEncumbranceEntry.getFinancialBalanceTypeCode());
1607 
1608             if (offset != null) {
1609                 if (offset.getFinancialObject() == null) {
1610                     StringBuffer offsetKey = new StringBuffer();
1611                     offsetKey.append(offset.getUniversityFiscalYear());
1612                     offsetKey.append("-");
1613                     offsetKey.append(offset.getChartOfAccountsCode());
1614                     offsetKey.append("-");
1615                     offsetKey.append(offset.getFinancialObjectCode());
1616 
1617                     LOG.debug("generateCostShareEncumbranceEntries() object code not found");
1618                     return new TransactionError(costShareEncumbranceEntry, new Message(configurationService.getPropertyValueAsString(OLEKeyConstants.ERROR_NO_OBJECT_FOR_OBJECT_ON_OFSD) + "(" + offsetKey.toString() + ")", Message.TYPE_FATAL));
1619                 }
1620                 costShareEncumbranceOffsetEntry.setFinancialObjectCode(offset.getFinancialObjectCode());
1621                 costShareEncumbranceOffsetEntry.setFinancialObject(offset.getFinancialObject());
1622                 costShareEncumbranceOffsetEntry.setFinancialSubObjectCode(OLEConstants.getDashFinancialSubObjectCode());
1623             }
1624             else {
1625                 StringBuffer offsetKey = new StringBuffer("Cost share encumbrance ");
1626                 offsetKey.append(costShareEncumbranceEntry.getUniversityFiscalYear());
1627                 offsetKey.append("-");
1628                 offsetKey.append(costShareEncumbranceEntry.getChartOfAccountsCode());
1629                 offsetKey.append("-");
1630                 offsetKey.append(costShareEncumbranceEntry.getFinancialDocumentTypeCode());
1631                 offsetKey.append("-");
1632                 offsetKey.append(costShareEncumbranceEntry.getFinancialBalanceTypeCode());
1633 
1634                 LOG.debug("generateCostShareEncumbranceEntries() offset not found");
1635                 return new TransactionError(costShareEncumbranceEntry, new Message(configurationService.getPropertyValueAsString(OLEKeyConstants.ERROR_OFFSET_DEFINITION_NOT_FOUND) + "(" + offsetKey.toString() + ")", Message.TYPE_FATAL));
1636             }
1637 
1638             costShareEncumbranceOffsetEntry.setFinancialObjectTypeCode(offset.getFinancialObject().getFinancialObjectTypeCode());
1639 
1640             if (costShareEncumbranceEntry.isCredit()) {
1641                 costShareEncumbranceOffsetEntry.setTransactionDebitCreditCode(OLEConstants.GL_DEBIT_CODE);
1642             }
1643             else {
1644                 costShareEncumbranceOffsetEntry.setTransactionDebitCreditCode(OLEConstants.GL_CREDIT_CODE);
1645             }
1646 
1647             costShareEncumbranceOffsetEntry.setTransactionDate(runDate);
1648             costShareEncumbranceOffsetEntry.setOrganizationDocumentNumber(null);
1649             costShareEncumbranceOffsetEntry.setProjectCode(OLEConstants.getDashProjectCode());
1650             costShareEncumbranceOffsetEntry.setOrganizationReferenceId(null);
1651             costShareEncumbranceOffsetEntry.setReferenceFinancialDocumentTypeCode(null);
1652             costShareEncumbranceOffsetEntry.setReferenceFinancialSystemOriginationCode(null);
1653             costShareEncumbranceOffsetEntry.setReferenceFinancialDocumentNumber(null);
1654             costShareEncumbranceOffsetEntry.setReversalDate(null);
1655             costShareEncumbranceOffsetEntry.setTransactionEncumbranceUpdateCode(null);
1656 
1657             costShareEncumbranceOffsetEntry.setTransactionScrubberOffsetGenerationIndicator(true);
1658 
1659             try {
1660                 flexibleOffsetAccountService.updateOffset(costShareEncumbranceOffsetEntry);
1661             }
1662             catch (InvalidFlexibleOffsetException e) {
1663                 Message m = new Message(e.getMessage(), Message.TYPE_FATAL);
1664                 if (LOG.isDebugEnabled()) {
1665                     LOG.debug("generateCostShareEncumbranceEntries() Cost Share Encumbrance Flexible Offset Error: " + e.getMessage());
1666                 }
1667                 return new TransactionError(costShareEncumbranceOffsetEntry, m);
1668             }
1669 
1670             createOutputEntry(costShareEncumbranceOffsetEntry, OUTPUT_GLE_FILE_ps);
1671             scrubberReport.incrementCostShareEncumbranceGenerated();
1672         } catch (IOException ioe) {
1673             LOG.error("generateCostShareEncumbranceEntries() Stopped: " + ioe.getMessage());
1674             throw new RuntimeException("generateCostShareEncumbranceEntries() Stopped: " + ioe.getMessage(), ioe);
1675         } 
1676         LOG.debug("generateCostShareEncumbranceEntries() returned successfully");
1677         return null;
1678     }
1679 
1680     /**
1681      * Sets the proper cost share object code in an entry and its offset
1682      * 
1683      * @param costShareEntry GL Entry for cost share
1684      * @param originEntry Scrubbed GL Entry that this is based on
1685      */
1686     @Override
1687     public void setCostShareObjectCode(OriginEntryFull costShareEntry, OriginEntryInformation originEntry) {
1688         ObjectCode originEntryFinancialObject = accountingCycleCachingService.getObjectCode(originEntry.getUniversityFiscalYear(), originEntry.getChartOfAccountsCode(), originEntry.getFinancialObjectCode());
1689 
1690         if (originEntryFinancialObject == null) {
1691             addTransactionError(configurationService.getPropertyValueAsString(OLEKeyConstants.ERROR_OBJECT_CODE_NOT_FOUND), originEntry.getFinancialObjectCode(), Message.TYPE_FATAL);
1692         }
1693 
1694         String originEntryObjectLevelCode = (originEntryFinancialObject == null) ? "" : originEntryFinancialObject.getFinancialObjectLevelCode();
1695 
1696         String financialOriginEntryObjectCode = originEntry.getFinancialObjectCode();
1697         //String originEntryObjectCode = scrubberProcessObjectCodeOverride.getOriginEntryObjectCode(originEntryObjectLevelCode, financialOriginEntryObjectCode);
1698 
1699         // General rules
1700         String param = parameterService.getSubParameterValueAsString(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupParameters.COST_SHARE_OBJECT_CODE_BY_LEVEL_PARM_NM, originEntryObjectLevelCode);
1701         if (param == null) {
1702             param = parameterService.getSubParameterValueAsString(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupParameters.COST_SHARE_OBJECT_CODE_BY_LEVEL_PARM_NM, "DEFAULT");
1703             if (param == null) {
1704                 throw new RuntimeException("Unable to determine cost sharing object code from object level.  Default entry missing.");
1705             }
1706         }
1707         financialOriginEntryObjectCode = param;
1708 
1709         // Lookup the new object code
1710         ObjectCode objectCode = accountingCycleCachingService.getObjectCode(costShareEntry.getUniversityFiscalYear(), costShareEntry.getChartOfAccountsCode(), financialOriginEntryObjectCode);
1711         if (objectCode != null) {
1712             costShareEntry.setFinancialObjectTypeCode(objectCode.getFinancialObjectTypeCode());
1713             costShareEntry.setFinancialObjectCode(financialOriginEntryObjectCode);
1714         }
1715         else {
1716             addTransactionError(configurationService.getPropertyValueAsString(OLEKeyConstants.ERROR_COST_SHARE_OBJECT_NOT_FOUND), costShareEntry.getFinancialObjectCode(), Message.TYPE_FATAL);
1717         }
1718     }
1719 
1720     /**
1721      * The purpose of this method is to build the actual offset transaction. It does this by performing the following steps: 1.
1722      * Getting the offset object code and offset subobject code from the GL Offset Definition Table. 2. For the offset object code
1723      * it needs to get the associated object type, object subtype, and object active code. 
1724      * 
1725      * @param scrubbedEntry entry to determine if an offset is needed for
1726      * @return true if an offset would be needed for this entry, false otherwise
1727      */
1728     protected boolean generateOffset(OriginEntryInformation scrubbedEntry, ScrubberReportData scrubberReport) {
1729         OriginEntryFull offsetEntry = new OriginEntryFull();
1730         try{
1731          // This code is 3000-OFFSET to SET-OBJECT-2004 in the Cobol
1732             LOG.debug("generateOffset() started");
1733 
1734             // There was no previous unit of work so we need no offset
1735             if (scrubbedEntry == null) {
1736                 return true;
1737             }
1738 
1739             // If there was an error, don't generate an offset since the record was pulled
1740             // and the rest of the document's records will be demerged
1741             if (unitOfWork.errorsFound == true) {
1742                 return true;
1743             }
1744             
1745             // If the offset amount is zero, don't bother to lookup the offset definition ...
1746             if (unitOfWork.offsetAmount.isZero()) {
1747                 return true;
1748             }
1749 
1750             ParameterEvaluator docTypeRule = /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.OFFSET_DOC_TYPE_CODES, scrubbedEntry.getFinancialDocumentTypeCode());
1751             if (!docTypeRule.evaluationSucceeds()) {
1752                 return true;
1753             }
1754             
1755             // do nothing if flexible offset is enabled and scrubber offset indicator of the document
1756             // type code is turned off in the document type table
1757             if (flexibleOffsetAccountService.getEnabled() && !shouldScrubberGenerateOffsetsForDocType(scrubbedEntry.getFinancialDocumentTypeCode())) {
1758                 return true;
1759             }
1760             
1761             // Create an offset
1762             offsetEntry = OriginEntryFull.copyFromOriginEntryable(scrubbedEntry);
1763             offsetEntry.setTransactionLedgerEntryDescription(offsetDescription);
1764 
1765             //of course this method should go elsewhere, not in ScrubberValidator!
1766             OffsetDefinition offsetDefinition = accountingCycleCachingService.getOffsetDefinition(scrubbedEntry.getUniversityFiscalYear(), scrubbedEntry.getChartOfAccountsCode(), scrubbedEntry.getFinancialDocumentTypeCode(), scrubbedEntry.getFinancialBalanceTypeCode());
1767             if (offsetDefinition != null) {
1768                 if (offsetDefinition.getFinancialObject() == null) {
1769                     StringBuffer offsetKey = new StringBuffer(offsetDefinition.getUniversityFiscalYear());
1770                     offsetKey.append("-");
1771                     offsetKey.append(offsetDefinition.getChartOfAccountsCode());
1772                     offsetKey.append("-");
1773                     offsetKey.append(offsetDefinition.getFinancialObjectCode());
1774 
1775                     putTransactionError(offsetEntry, configurationService.getPropertyValueAsString(OLEKeyConstants.ERROR_OFFSET_DEFINITION_OBJECT_CODE_NOT_FOUND), offsetKey.toString(), Message.TYPE_FATAL);
1776 
1777                     createOutputEntry(offsetEntry, OUTPUT_ERR_FILE_ps);
1778                     scrubberReport.incrementErrorRecordWritten();
1779                     return false;
1780                 }
1781 
1782                 offsetEntry.setFinancialObject(offsetDefinition.getFinancialObject());
1783                 offsetEntry.setFinancialObjectCode(offsetDefinition.getFinancialObjectCode());
1784 
1785                 offsetEntry.setFinancialSubObject(null);
1786                 offsetEntry.setFinancialSubObjectCode(OLEConstants.getDashFinancialSubObjectCode());
1787             }
1788             else {
1789                 StringBuffer sb = new StringBuffer("Unit of work offset ");
1790                 sb.append(scrubbedEntry.getUniversityFiscalYear());
1791                 sb.append("-");
1792                 sb.append(scrubbedEntry.getChartOfAccountsCode());
1793                 sb.append("-");
1794                 sb.append(scrubbedEntry.getFinancialDocumentTypeCode());
1795                 sb.append("-");
1796                 sb.append(scrubbedEntry.getFinancialBalanceTypeCode());
1797 
1798                 putTransactionError(offsetEntry, configurationService.getPropertyValueAsString(OLEKeyConstants.ERROR_OFFSET_DEFINITION_NOT_FOUND), sb.toString(), Message.TYPE_FATAL);
1799 
1800                 createOutputEntry(offsetEntry, OUTPUT_ERR_FILE_ps);
1801                 scrubberReport.incrementErrorRecordWritten();
1802                 return false;
1803             }
1804 
1805             offsetEntry.setFinancialObjectTypeCode(offsetEntry.getFinancialObject().getFinancialObjectTypeCode());
1806             offsetEntry.setTransactionLedgerEntryAmount(unitOfWork.offsetAmount);
1807 
1808             if (unitOfWork.offsetAmount.isPositive()) {
1809                 offsetEntry.setTransactionDebitCreditCode(OLEConstants.GL_CREDIT_CODE);
1810             }
1811             else {
1812                 offsetEntry.setTransactionDebitCreditCode(OLEConstants.GL_DEBIT_CODE);
1813                 offsetEntry.setTransactionLedgerEntryAmount(unitOfWork.offsetAmount.negated());
1814             }
1815 
1816             offsetEntry.setOrganizationDocumentNumber(null);
1817             offsetEntry.setOrganizationReferenceId(null);
1818             offsetEntry.setReferenceFinancialDocumentTypeCode(null);
1819             offsetEntry.setReferenceFinancialSystemOriginationCode(null);
1820             offsetEntry.setReferenceFinancialDocumentNumber(null);
1821             offsetEntry.setTransactionEncumbranceUpdateCode(null);
1822             offsetEntry.setProjectCode(OLEConstants.getDashProjectCode());
1823             offsetEntry.setTransactionDate(runDate);
1824 
1825             try {
1826                 flexibleOffsetAccountService.updateOffset(offsetEntry);
1827             }
1828             catch (InvalidFlexibleOffsetException e) {
1829                 if (LOG.isDebugEnabled()) {
1830                     LOG.debug("generateOffset() Offset Flexible Offset Error: " + e.getMessage());
1831                 }
1832                 putTransactionError(offsetEntry, e.getMessage(), "", Message.TYPE_FATAL);
1833                 return true;
1834             }
1835 
1836             createOutputEntry(offsetEntry, OUTPUT_GLE_FILE_ps);
1837             scrubberReport.incrementOffsetEntryGenerated();
1838             
1839         } catch (IOException ioe) {
1840             LOG.error("generateOffset() Stopped: " + ioe.getMessage());
1841             throw new RuntimeException("generateOffset() Stopped: " + ioe.getMessage(), ioe);
1842         }
1843         
1844         return true;
1845     }
1846 
1847     
1848     protected void createOutputEntry(OriginEntryInformation entry, PrintStream ps) throws IOException {
1849         try {
1850             ps.printf("%s\n", entry.getLine());
1851         } catch (Exception e) {
1852             throw new IOException(e.toString());
1853         }
1854     }
1855     
1856     protected void createOutputEntry(String line, PrintStream ps) throws IOException {
1857         try {
1858             ps.printf("%s\n", line);
1859         } catch (Exception e) {
1860             throw new IOException(e.toString());
1861         }
1862     }
1863     
1864     /**
1865      * Add an error message to the list of messages for this transaction
1866      * 
1867      * @param errorMessage Error message
1868      * @param errorValue Value that is in error
1869      * @param type Type of error (Fatal or Warning)
1870      */
1871     protected void addTransactionError(String errorMessage, String errorValue, int type) {
1872         transactionErrors.add(new Message(errorMessage + " (" + errorValue + ")", type));
1873     }
1874 
1875     /**
1876      * Puts a transaction error into this instance's collection of errors
1877      * 
1878      * @param s a transaction that caused a scrubber error
1879      * @param errorMessage the message of what caused the error
1880      * @param errorValue the value in error
1881      * @param type the type of error
1882      */
1883     protected void putTransactionError(Transaction s, String errorMessage, String errorValue, int type) {
1884         Message m = new Message(errorMessage + "(" + errorValue + ")", type);
1885         scrubberReportWriterService.writeError(s, m);
1886     }
1887     
1888     /**
1889      * Determines if the scrubber should generate offsets for the given document type
1890      * @param docTypeCode the document type code to check if it generates scrubber offsets
1891      * @return true if the scrubber should generate offsets for this doc type, false otherwise
1892      */
1893     protected boolean shouldScrubberGenerateOffsetsForDocType(String docTypeCode) {
1894         return /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.DOCUMENT_TYPES_REQUIRING_FLEXIBLE_OFFSET_BALANCING_ENTRIES, docTypeCode).evaluationSucceeds();
1895     }
1896 
1897     /**
1898      * A class to hold the current unit of work the scrubber is using
1899      */
1900     class UnitOfWorkInfo {
1901         // Unit of work key
1902         public Integer univFiscalYr = 0;
1903         public String finCoaCd = "";
1904         public String accountNbr = "";
1905         public String subAcctNbr = "";
1906         public String finBalanceTypCd = "";
1907         public String fdocTypCd = "";
1908         public String fsOriginCd = "";
1909         public String fdocNbr = "";
1910         public Date fdocReversalDt = new Date(dateTimeService.getCurrentDate().getTime());
1911         public String univFiscalPrdCd = "";
1912 
1913         // Data about unit of work
1914         public boolean errorsFound = false;
1915         public KualiDecimal offsetAmount = KualiDecimal.ZERO;
1916         public String scrbFinCoaCd;
1917         public String scrbAccountNbr;
1918 
1919         /**
1920          * Constructs a ScrubberProcess.UnitOfWorkInfo instance
1921          */
1922         public UnitOfWorkInfo() {
1923         }
1924 
1925         /**
1926          * Constructs a ScrubberProcess.UnitOfWorkInfo instance
1927          * @param e an origin entry belonging to this unit of work
1928          */
1929         public UnitOfWorkInfo(OriginEntryInformation e) {
1930             univFiscalYr = e.getUniversityFiscalYear();
1931             finCoaCd = e.getChartOfAccountsCode();
1932             accountNbr = e.getAccountNumber();
1933             subAcctNbr = e.getSubAccountNumber();
1934             finBalanceTypCd = e.getFinancialBalanceTypeCode();
1935             fdocTypCd = e.getFinancialDocumentTypeCode();
1936             fsOriginCd = e.getFinancialSystemOriginationCode();
1937             fdocNbr = e.getDocumentNumber();
1938             fdocReversalDt = e.getFinancialDocumentReversalDate();
1939             univFiscalPrdCd = e.getUniversityFiscalPeriodCode();
1940         }
1941 
1942         /**
1943          * Determines if an entry belongs to this unit of work
1944          * 
1945          * @param e the entry to check
1946          * @return true if it belongs to this unit of work, false otherwise
1947          */
1948         public boolean isSameUnitOfWork(OriginEntryInformation e) {
1949             // Compare the key fields
1950             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());
1951         }
1952 
1953         /**
1954          * Converts this unit of work info to a String
1955          * @return a String representation of this UnitOfWorkInfo
1956          * @see java.lang.Object#toString()
1957          */
1958         @Override
1959         public String toString() {
1960             return univFiscalYr + finCoaCd + accountNbr + subAcctNbr + finBalanceTypCd + fdocTypCd + fsOriginCd + fdocNbr + fdocReversalDt + univFiscalPrdCd;
1961         }
1962 
1963         /**
1964          * Generates the beginning of an OriginEntryFull, based on the unit of work info
1965          * 
1966          * @return a partially initialized OriginEntryFull
1967          */
1968         public OriginEntryFull getOffsetTemplate() {
1969             OriginEntryFull e = new OriginEntryFull();
1970             e.setUniversityFiscalYear(univFiscalYr);
1971             e.setChartOfAccountsCode(finCoaCd);
1972             e.setAccountNumber(accountNbr);
1973             e.setSubAccountNumber(subAcctNbr);
1974             e.setFinancialBalanceTypeCode(finBalanceTypCd);
1975             e.setFinancialDocumentTypeCode(fdocTypCd);
1976             e.setFinancialSystemOriginationCode(fsOriginCd);
1977             e.setDocumentNumber(fdocNbr);
1978             e.setFinancialDocumentReversalDate(fdocReversalDt);
1979             e.setUniversityFiscalPeriodCode(univFiscalPrdCd);
1980             return e;
1981         }
1982     }
1983 
1984     /**
1985      * An internal class to hold errors encountered by the scrubber
1986      */
1987     class TransactionError {
1988         public Transaction transaction;
1989         public Message message;
1990 
1991         /**
1992          * Constructs a ScrubberProcess.TransactionError instance
1993          * @param t the transaction that had the error
1994          * @param m a message about the error
1995          */
1996         public TransactionError(Transaction t, Message m) {
1997             transaction = t;
1998             message = m;
1999         }
2000     }
2001 
2002     /**
2003      * This method modifies the run date if it is before the cutoff time specified by the RunTimeService See
2004      * KULRNE-70 This method is public to facilitate unit testing
2005      * 
2006      * @param currentDate the date the scrubber should report as having run on
2007      * @return the run date
2008      */
2009     @Override
2010     public Date calculateRunDate(java.util.Date currentDate) {
2011         return new Date(runDateService.calculateRunDate(currentDate).getTime());
2012     }
2013 
2014     protected boolean checkingBypassEntry (String financialBalanceTypeCode, String desc, DemergerReportData demergerReport){
2015         String transactionType = getTransactionType(financialBalanceTypeCode, desc);
2016         
2017         if (TRANSACTION_TYPE_COST_SHARE_ENCUMBRANCE.equals(transactionType)) {
2018             demergerReport.incrementCostShareEncumbranceTransactionsBypassed();
2019             return true;
2020         }
2021         else if (TRANSACTION_TYPE_OFFSET.equals(transactionType)) {
2022             demergerReport.incrementOffsetTransactionsBypassed();
2023             return true;
2024         }
2025         else if (TRANSACTION_TYPE_CAPITALIZATION.equals(transactionType)) {
2026             demergerReport.incrementCapitalizationTransactionsBypassed();
2027             return true;
2028         }
2029         else if (TRANSACTION_TYPE_LIABILITY.equals(transactionType)) {
2030             demergerReport.incrementLiabilityTransactionsBypassed();
2031             return true;
2032         }
2033         else if (TRANSACTION_TYPE_TRANSFER.equals(transactionType)) {
2034             demergerReport.incrementTransferTransactionsBypassed();
2035             return true;
2036         }
2037         else if (TRANSACTION_TYPE_COST_SHARE.equals(transactionType)) {
2038             demergerReport.incrementCostShareTransactionsBypassed();
2039             return true;
2040         }
2041         
2042         return false;
2043     }
2044     
2045     
2046     protected String checkAndSetTransactionTypeCostShare (String financialBalanceTypeCode, String desc, String currentValidLine){
2047         
2048         // Read all the transactions in the valid group and update the cost share transactions
2049         String transactionType = getTransactionType(financialBalanceTypeCode, desc);
2050         if (TRANSACTION_TYPE_COST_SHARE.equals(transactionType)) {
2051             OriginEntryFull transaction = new OriginEntryFull();
2052             transaction.setFromTextFileForBatch(currentValidLine, 0);
2053             
2054             transaction.setFinancialDocumentTypeCode(OLEConstants.TRANSFER_FUNDS);
2055             transaction.setFinancialSystemOriginationCode(OLEConstants.SubAccountType.COST_SHARE);
2056             StringBuffer docNbr = new StringBuffer(COST_SHARE_CODE);
2057             
2058             docNbr.append(desc.substring(36, 38));
2059             docNbr.append("/");
2060             docNbr.append(desc.substring(38, 40));
2061             transaction.setDocumentNumber(docNbr.toString());
2062             transaction.setTransactionLedgerEntryDescription(desc.substring(0, DEMERGER_TRANSACTION_LEDGET_ENTRY_DESCRIPTION));
2063             
2064             currentValidLine = transaction.getLine();
2065        }
2066         
2067         return currentValidLine;
2068         
2069     }
2070     
2071     
2072     /**
2073      * Generates the scrubber listing report for the GLCP document
2074      * @param documentNumber the document number of the GLCP document
2075      */
2076     protected void generateScrubberTransactionListingReport(String documentNumber, String inputFileName) {
2077         try {
2078             scrubberListingReportWriterService.setDocumentNumber(documentNumber);
2079         ((WrappingBatchService) scrubberListingReportWriterService).initialize();
2080         new TransactionListingReport().generateReport(scrubberListingReportWriterService, new OriginEntryFileIterator(new File(inputFileName)));
2081         } finally {
2082         ((WrappingBatchService) scrubberListingReportWriterService).destroy();
2083     }
2084     }
2085     
2086     /**
2087      * Generates the scrubber report that lists out the input origin entries with blank balance type codes.
2088      */
2089     protected void generateScrubberBlankBalanceTypeCodeReport(String inputFileName) {
2090         OriginEntryFilter blankBalanceTypeFilter = new OriginEntryFilter() {
2091             /**
2092              * @see org.kuali.ole.gl.batch.service.impl.FilteringOriginEntryFileIterator.OriginEntryFilter#accept(org.kuali.ole.gl.businessobject.OriginEntryFull)
2093              */
2094             @Override
2095             public boolean accept(OriginEntryFull originEntry) {
2096                 boolean acceptFlag = false;
2097                 String financialBalancetype = originEntry.getFinancialBalanceTypeCode();
2098                 BalanceType originEntryBalanceType = accountingCycleCachingService.getBalanceType(financialBalancetype);
2099                 if (ObjectUtils.isNull(originEntryBalanceType)) {
2100                     acceptFlag = true;
2101                     for (int i= 0; i < financialBalancetype.length(); i++) {
2102                         if (financialBalancetype.charAt(i) != ' ') { acceptFlag = false; break;}
2103                     }
2104                 }
2105                 return acceptFlag;
2106             }
2107         };
2108         Iterator<OriginEntryFull> blankBalanceOriginEntries = new FilteringOriginEntryFileIterator(new File(inputFileName), blankBalanceTypeFilter);
2109         new TransactionListingReport().generateReport(scrubberBadBalanceListingReportWriterService, blankBalanceOriginEntries);   
2110     }
2111     
2112     protected void generateDemergerRemovedTransactionsReport(String errorFileName) {
2113         OriginEntryFileIterator removedTransactions = new OriginEntryFileIterator(new File(errorFileName));
2114         new TransactionListingReport().generateReport(demergerRemovedTransactionsListingReportWriterService, removedTransactions);
2115     }
2116     
2117     protected void handleTransactionError(Transaction errorTransaction, Message message) {
2118         if (collectorMode) {
2119             List<Message> messages = scrubberReportErrors.get(errorTransaction);
2120             if (messages == null) {
2121                 messages = new ArrayList<Message>();
2122                 scrubberReportErrors.put(errorTransaction, messages);
2123             }
2124             messages.add(message);
2125         }
2126         else {
2127             scrubberReportWriterService.writeError(errorTransaction, message);
2128         }
2129     }
2130     
2131     protected void handleTransactionErrors(Transaction errorTransaction, List<Message> messages) {
2132         if (collectorMode) {
2133             for (Message message : messages) {
2134                 handleTransactionError(errorTransaction, message);
2135             }
2136         }
2137         else {
2138             if (LOG.isDebugEnabled()) {
2139                 LOG.debug("Errors on transaction: "+errorTransaction);
2140                 for (Message message: messages) {
2141                     LOG.debug(message);
2142                 }
2143             }
2144             scrubberReportWriterService.writeError(errorTransaction, messages);
2145         }
2146     }
2147     
2148     protected void handleEndOfScrubberReport(ScrubberReportData scrubberReport) {
2149         if (!collectorMode) {
2150             scrubberReportWriterService.writeStatisticLine("UNSCRUBBED RECORDS READ              %,9d", scrubberReport.getNumberOfUnscrubbedRecordsRead());
2151             scrubberReportWriterService.writeStatisticLine("SCRUBBED RECORDS WRITTEN             %,9d", scrubberReport.getNumberOfScrubbedRecordsWritten());
2152             scrubberReportWriterService.writeStatisticLine("ERROR RECORDS WRITTEN                %,9d", scrubberReport.getNumberOfErrorRecordsWritten());
2153             scrubberReportWriterService.writeStatisticLine("OFFSET ENTRIES GENERATED             %,9d", scrubberReport.getNumberOfOffsetEntriesGenerated());
2154             scrubberReportWriterService.writeStatisticLine("CAPITALIZATION ENTRIES GENERATED     %,9d", scrubberReport.getNumberOfCapitalizationEntriesGenerated());
2155             scrubberReportWriterService.writeStatisticLine("LIABILITY ENTRIES GENERATED          %,9d", scrubberReport.getNumberOfLiabilityEntriesGenerated());
2156             scrubberReportWriterService.writeStatisticLine("PLANT INDEBTEDNESS ENTRIES GENERATED %,9d", scrubberReport.getNumberOfPlantIndebtednessEntriesGenerated());
2157             scrubberReportWriterService.writeStatisticLine("COST SHARE ENTRIES GENERATED         %,9d", scrubberReport.getNumberOfCostShareEntriesGenerated());
2158             scrubberReportWriterService.writeStatisticLine("COST SHARE ENC ENTRIES GENERATED     %,9d", scrubberReport.getNumberOfCostShareEncumbrancesGenerated());
2159             scrubberReportWriterService.writeStatisticLine("TOTAL OUTPUT RECORDS WRITTEN         %,9d", scrubberReport.getTotalNumberOfRecordsWritten());
2160             scrubberReportWriterService.writeStatisticLine("EXPIRED ACCOUNTS FOUND               %,9d", scrubberReport.getNumberOfExpiredAccountsFound());
2161         }
2162     }
2163     
2164     protected void handleDemergerSaveValidEntry(String entryString) {
2165         if (collectorMode) {
2166             OriginEntryInformation tempEntry = new OriginEntryFull(entryString);
2167             ledgerSummaryReport.summarizeEntry(tempEntry);
2168         }
2169     }
2170 
2171     /**
2172      * Sets the batchFileDirectoryName attribute value.
2173      * @param batchFileDirectoryName The batchFileDirectoryName to set.
2174      */
2175     public void setBatchFileDirectoryName(String batchFileDirectoryName) {
2176         this.batchFileDirectoryName = batchFileDirectoryName;
2177     }
2178 
2179     /**
2180      * Gets the transferDescription attribute. 
2181      * @return Returns the transferDescription.
2182      */
2183     public String getTransferDescription() {
2184         return transferDescription;
2185     }
2186 
2187     /**
2188      * Sets the transferDescription attribute value.
2189      * @param transferDescription The transferDescription to set.
2190      */
2191     public void setTransferDescription(String transferDescription) {
2192         this.transferDescription = transferDescription;
2193     }
2194 
2195     /**
2196      * Sets the dateTimeService attribute value.
2197      * @param dateTimeService The dateTimeService to set.
2198      */
2199     public void setDateTimeService(DateTimeService dateTimeService) {
2200         this.dateTimeService = dateTimeService;
2201     }
2202 
2203     /**
2204      * Sets the flexibleOffsetAccountService attribute value.
2205      * @param flexibleOffsetAccountService The flexibleOffsetAccountService to set.
2206      */
2207     public void setFlexibleOffsetAccountService(FlexibleOffsetAccountService flexibleOffsetAccountService) {
2208         this.flexibleOffsetAccountService = flexibleOffsetAccountService;
2209     }
2210 
2211     /**
2212      * Sets the configurationService attribute value.
2213      * @param configurationService The configurationService to set.
2214      */
2215     public void setConfigurationService(ConfigurationService configurationService) {
2216         this.configurationService = configurationService;
2217     }
2218 
2219     /**
2220      * Sets the persistenceService attribute value.
2221      * @param persistenceService The persistenceService to set.
2222      */
2223     public void setPersistenceService(PersistenceService persistenceService) {
2224         this.persistenceService = persistenceService;
2225     }
2226 
2227     /**
2228      * Sets the scrubberValidator attribute value.
2229      * @param scrubberValidator The scrubberValidator to set.
2230      */
2231     public void setScrubberValidator(ScrubberValidator scrubberValidator) {
2232         this.scrubberValidator = scrubberValidator;
2233     }
2234 
2235     /**
2236      * Sets the accountingCycleCachingService attribute value.
2237      * @param accountingCycleCachingService The accountingCycleCachingService to set.
2238      */
2239     public void setAccountingCycleCachingService(AccountingCycleCachingService accountingCycleCachingService) {
2240         this.accountingCycleCachingService = accountingCycleCachingService;
2241     }
2242 
2243     /**
2244      * Sets the scrubberReportWriterService attribute value.
2245      * @param scrubberReportWriterService The scrubberReportWriterService to set.
2246      */
2247     public void setScrubberReportWriterService(DocumentNumberAwareReportWriterService scrubberReportWriterService) {
2248         this.scrubberReportWriterService = scrubberReportWriterService;
2249     }
2250 
2251     /**
2252      * Sets the scrubberLedgerReportWriterService attribute value.
2253      * @param scrubberLedgerReportWriterService The scrubberLedgerReportWriterService to set.
2254      */
2255     public void setScrubberLedgerReportWriterService(DocumentNumberAwareReportWriterService scrubberLedgerReportWriterService) {
2256         this.scrubberLedgerReportWriterService = scrubberLedgerReportWriterService;
2257     }
2258 
2259     /**
2260      * Sets the scrubberListingReportWriterService attribute value.
2261      * @param scrubberListingReportWriterService The scrubberListingReportWriterService to set.
2262      */
2263     public void setScrubberListingReportWriterService(DocumentNumberAwareReportWriterService scrubberListingReportWriterService) {
2264         this.scrubberListingReportWriterService = scrubberListingReportWriterService;
2265     }
2266 
2267     /**
2268      * Sets the scrubberBadBalanceListingReportWriterService attribute value.
2269      * @param scrubberBadBalanceListingReportWriterService The scrubberBadBalanceListingReportWriterService to set.
2270      */
2271     public void setScrubberBadBalanceListingReportWriterService(ReportWriterService scrubberBadBalanceListingReportWriterService) {
2272         this.scrubberBadBalanceListingReportWriterService = scrubberBadBalanceListingReportWriterService;
2273     }
2274 
2275     /**
2276      * Sets the demergerRemovedTransactionsListingReportWriterService attribute value.
2277      * @param demergerRemovedTransactionsListingReportWriterService The demergerRemovedTransactionsListingReportWriterService to set.
2278      */
2279     public void setDemergerRemovedTransactionsListingReportWriterService(ReportWriterService demergerRemovedTransactionsListingReportWriterService) {
2280         this.demergerRemovedTransactionsListingReportWriterService = demergerRemovedTransactionsListingReportWriterService;
2281     }
2282 
2283     /**
2284      * Sets the demergerReportWriterService attribute value.
2285      * @param demergerReportWriterService The demergerReportWriterService to set.
2286      */
2287     public void setDemergerReportWriterService(ReportWriterService demergerReportWriterService) {
2288         this.demergerReportWriterService = demergerReportWriterService;
2289     }
2290 
2291     /**
2292      * Sets the preScrubberService attribute value.
2293      * @param preScrubberService The preScrubberService to set.
2294      */
2295     public void setPreScrubberService(PreScrubberService preScrubberService) {
2296         this.preScrubberService = preScrubberService;
2297     }
2298 
2299     /**
2300      * Sets the parameterService attribute value.
2301      * @param parameterService The parameterService to set.
2302      */
2303     public void setParameterService(ParameterService parameterService) {
2304         this.parameterService = parameterService;
2305     }
2306 
2307     /**
2308      * Sets the runDateService attribute value.
2309      * @param runDateService The runDateService to set.
2310      */
2311     public void setRunDateService(RunDateService runDateService) {
2312         this.runDateService = runDateService;
2313     }
2314 
2315     /**
2316      * Gets the flexibleOffsetAccountService attribute. 
2317      * @return Returns the flexibleOffsetAccountService.
2318      */
2319     public FlexibleOffsetAccountService getFlexibleOffsetAccountService() {
2320         return flexibleOffsetAccountService;
2321     }
2322 
2323     /**
2324      * Gets the dateTimeService attribute. 
2325      * @return Returns the dateTimeService.
2326      */
2327     public DateTimeService getDateTimeService() {
2328         return dateTimeService;
2329     }
2330 
2331     /**
2332      * Gets the configurationService attribute. 
2333      * @return Returns the configurationService.
2334      */
2335     public ConfigurationService getConfigurationService() {
2336         return configurationService;
2337     }
2338 
2339     /**
2340      * Gets the persistenceService attribute. 
2341      * @return Returns the persistenceService.
2342      */
2343     public PersistenceService getPersistenceService() {
2344         return persistenceService;
2345     }
2346 
2347     /**
2348      * Gets the scrubberValidator attribute. 
2349      * @return Returns the scrubberValidator.
2350      */
2351     public ScrubberValidator getScrubberValidator() {
2352         return scrubberValidator;
2353     }
2354 
2355     /**
2356      * Gets the runDateService attribute. 
2357      * @return Returns the runDateService.
2358      */
2359     public RunDateService getRunDateService() {
2360         return runDateService;
2361     }
2362 
2363     /**
2364      * Gets the accountingCycleCachingService attribute. 
2365      * @return Returns the accountingCycleCachingService.
2366      */
2367     public AccountingCycleCachingService getAccountingCycleCachingService() {
2368         return accountingCycleCachingService;
2369     }
2370 
2371     /**
2372      * Gets the scrubberReportWriterService attribute. 
2373      * @return Returns the scrubberReportWriterService.
2374      */
2375     public DocumentNumberAwareReportWriterService getScrubberReportWriterService() {
2376         return scrubberReportWriterService;
2377     }
2378 
2379     /**
2380      * Gets the scrubberLedgerReportWriterService attribute. 
2381      * @return Returns the scrubberLedgerReportWriterService.
2382      */
2383     public DocumentNumberAwareReportWriterService getScrubberLedgerReportWriterService() {
2384         return scrubberLedgerReportWriterService;
2385     }
2386 
2387     /**
2388      * Gets the scrubberListingReportWriterService attribute. 
2389      * @return Returns the scrubberListingReportWriterService.
2390      */
2391     public DocumentNumberAwareReportWriterService getScrubberListingReportWriterService() {
2392         return scrubberListingReportWriterService;
2393     }
2394 
2395     /**
2396      * Gets the scrubberBadBalanceListingReportWriterService attribute. 
2397      * @return Returns the scrubberBadBalanceListingReportWriterService.
2398      */
2399     public ReportWriterService getScrubberBadBalanceListingReportWriterService() {
2400         return scrubberBadBalanceListingReportWriterService;
2401     }
2402 
2403     /**
2404      * Gets the demergerRemovedTransactionsListingReportWriterService attribute. 
2405      * @return Returns the demergerRemovedTransactionsListingReportWriterService.
2406      */
2407     public ReportWriterService getDemergerRemovedTransactionsListingReportWriterService() {
2408         return demergerRemovedTransactionsListingReportWriterService;
2409     }
2410 
2411     /**
2412      * Gets the demergerReportWriterService attribute. 
2413      * @return Returns the demergerReportWriterService.
2414      */
2415     public ReportWriterService getDemergerReportWriterService() {
2416         return demergerReportWriterService;
2417     }
2418 
2419     /**
2420      * Gets the preScrubberService attribute. 
2421      * @return Returns the preScrubberService.
2422      */
2423     public PreScrubberService getPreScrubberService() {
2424         return preScrubberService;
2425     }
2426 
2427     /**
2428      * Gets the parameterService attribute. 
2429      * @return Returns the parameterService.
2430      */
2431     public ParameterService getParameterService() {
2432         return parameterService;
2433     }
2434 
2435     /**
2436      * Sets the preScrubberReportWriterService attribute value.
2437      * @param preScrubberReportWriterService The preScrubberReportWriterService to set.
2438      */
2439     public void setPreScrubberReportWriterService(DocumentNumberAwareReportWriterService preScrubberReportWriterService) {
2440         this.preScrubberReportWriterService = preScrubberReportWriterService;
2441     }
2442 
2443     /**
2444      * Sets the businessObjectService attribute value.
2445      * @param businessObjectService The businessObjectService to set.
2446      */
2447     public void setBusinessObjectService(BusinessObjectService businessObjectService) {
2448         this.businessObjectService = businessObjectService;
2449     }
2450     
2451 }