View Javadoc
1   /*
2    * Copyright 2009 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.service.impl;
17  
18  import java.io.IOException;
19  import java.io.PrintStream;
20  import java.util.Collection;
21  import java.util.Iterator;
22  import java.util.LinkedHashMap;
23  import java.util.Map;
24  import java.util.Map.Entry;
25  import java.util.Set;
26  import java.util.TreeSet;
27  
28  import org.apache.commons.lang.StringUtils;
29  import org.kuali.ole.coa.businessobject.Account;
30  import org.kuali.ole.coa.service.AccountService;
31  import org.kuali.ole.gl.GeneralLedgerConstants;
32  import org.kuali.ole.gl.report.PreScrubberReportData;
33  import org.kuali.ole.gl.service.PreScrubberService;
34  import org.kuali.ole.sys.OLEConstants.SystemGroupParameterNames;
35  import org.kuali.ole.sys.context.SpringContext;
36  import org.kuali.ole.sys.service.impl.OleParameterConstants;
37  import org.kuali.ole.sys.util.TransactionalServiceUtils;
38  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
39  
40  /**
41   * This class assumes that an account number may only belong to one chart code (i.e. as if the account number were the only primary key column of the account table)
42   * Based on that assumption, this code will attempt to fill in the chart code for an origin entry if it is blank and the account number is valid
43   */
44  public class PreScrubberServiceImpl implements PreScrubberService {
45      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PreScrubberServiceImpl.class);
46      
47      private int maxCacheSize = 10000;
48      private ParameterService parameterService;
49      
50      public PreScrubberReportData preprocessOriginEntries(Iterator<String> inputOriginEntries, String outputFileName) throws IOException {
51          PrintStream outputStream = new PrintStream(outputFileName);
52          
53          Map<String, String> chartCodeCache = new LinkedHashMap<String, String>() {
54              @Override
55              protected boolean removeEldestEntry(Entry<String, String> eldest) {
56                  return size() > getMaxCacheSize();
57              }
58          };
59          
60          Set<String> nonExistentAccountCache = new TreeSet<String>();
61          Set<String> multipleAccountCache = new TreeSet<String>();
62          
63          AccountService accountService = SpringContext.getBean(AccountService.class);
64          ParameterService parameterService = SpringContext.getBean(ParameterService.class);
65          boolean fillInChartCodesIfSpaces = deriveChartOfAccountsCodeIfSpaces();
66          
67          int inputLines = 0;
68          int outputLines = 0;
69          
70          try {
71              while (inputOriginEntries.hasNext()) {
72                  inputLines++;
73                  
74                  String originEntry = inputOriginEntries.next();
75                  String outputLine = originEntry;
76                  if (fillInChartCodesIfSpaces && originEntry.length() >= getExclusiveAccountNumberEndPosition()) {
77                      String chartOfAccountsCode = originEntry.substring(getInclusiveChartOfAccountsCodeStartPosition(), getExclusiveChartOfAccountsCodeEndPosition());
78                      if (GeneralLedgerConstants.getSpaceChartOfAccountsCode().equals(chartOfAccountsCode)) {
79                          // blank chart code... try to find the chart code
80                          String accountNumber = originEntry.substring(getInclusiveAccountNumberStartPosition(), getExclusiveAccountNumberEndPosition());
81                          if (StringUtils.isNotEmpty(accountNumber)) {
82                              String replacementChartOfAccountsCode = null;
83                              boolean nonExistent = false;
84                              boolean multipleFound = false;
85                              
86                              if (chartCodeCache.containsKey(accountNumber))
87                                  replacementChartOfAccountsCode = chartCodeCache.get(accountNumber);
88                              else if (nonExistentAccountCache.contains(accountNumber))
89                                  nonExistent = true;
90                              else if (multipleAccountCache.contains(accountNumber))
91                                  multipleFound = true;
92                              else {
93                                  Collection<Account> results = accountService.getAccountsForAccountNumber(accountNumber);
94                                  
95                                  if (results.isEmpty()) {
96                                      nonExistent = true;
97                                      nonExistentAccountCache.add(accountNumber);
98                                      LOG.warn("Could not find account record for account number " + accountNumber);
99                                  }
100                                 else {
101                                     Iterator<Account> accounts = results.iterator();
102                                     Account account = accounts.next();
103                                     if (accounts.hasNext()) {
104                                         LOG.warn("Multiple chart codes found for account number " + accountNumber + ", not filling in chart code for this account");
105                                         TransactionalServiceUtils.exhaustIterator(accounts);
106                                         multipleAccountCache.add(accountNumber);
107                                         multipleFound = true;
108                                     }
109                                     else {
110                                         replacementChartOfAccountsCode = account.getChartOfAccountsCode();
111                                         chartCodeCache.put(accountNumber, replacementChartOfAccountsCode);
112                                     }
113                                 }
114                             }
115                             
116                             if (!nonExistent && !multipleFound) {
117                                 StringBuilder buf = new StringBuilder(originEntry.length());
118                                 buf.append(originEntry.substring(0, getInclusiveChartOfAccountsCodeStartPosition()));
119                                 buf.append(replacementChartOfAccountsCode);
120                                 buf.append(originEntry.subSequence(getExclusiveChartOfAccountsCodeEndPosition(), originEntry.length()));
121                                 outputLine = buf.toString();
122                             }
123                         }
124                     }
125                 }
126                 outputStream.printf("%s\n", outputLine);
127                 outputLines++;
128             }
129         }
130         finally {
131             outputStream.close();
132         }
133         return new PreScrubberReportData(inputLines, outputLines, nonExistentAccountCache, multipleAccountCache);
134     }
135     
136     /**
137      * Returns the position of the chart of accounts code on an origin entry line
138      * @return
139      */
140     protected int getInclusiveChartOfAccountsCodeStartPosition() {
141         return 4;
142     }
143     
144     /**
145      * Returns the position of the end of the chart of accounts code on an origin entry line,   
146      * @return
147      */
148     protected int getExclusiveChartOfAccountsCodeEndPosition() {
149         return 6;
150     }
151     
152     /**
153      * Returns the position of the chart of accounts code on an origin entry line
154      * @return
155      */
156     protected int getInclusiveAccountNumberStartPosition() {
157         return 6;
158     }
159     
160     /**
161      * Returns the position of the end of the chart of accounts code on an origin entry line,   
162      * @return
163      */
164     protected int getExclusiveAccountNumberEndPosition() {
165         return 13;
166     }
167     
168     public int getMaxCacheSize() {
169         return maxCacheSize;
170     }
171 
172     public void setMaxCacheSize(int maxCacheSize) {
173         this.maxCacheSize = maxCacheSize;
174     }
175     
176     /**
177      * @return
178      */
179     public boolean deriveChartOfAccountsCodeIfSpaces() {
180         return !parameterService.getParameterValueAsBoolean(OleParameterConstants.FINANCIAL_SYSTEM_ALL.class, SystemGroupParameterNames.ACCOUNTS_CAN_CROSS_CHARTS_IND);
181     }
182 
183     /**
184      * Sets the parameterService attribute value.
185      * @param parameterService The parameterService to set.
186      */
187     public void setParameterService(ParameterService parameterService) {
188         this.parameterService = parameterService;
189     }
190 }