001 /*
002 * Copyright 2011 The Kuali Foundation.
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.kuali.kfs.gl.document;
018
019 import java.io.File;
020 import java.io.FilenameFilter;
021 import java.text.DateFormat;
022 import java.text.SimpleDateFormat;
023 import java.util.ArrayList;
024 import java.util.Arrays;
025 import java.util.Collections;
026 import java.util.Date;
027 import java.util.Iterator;
028 import java.util.LinkedHashMap;
029 import java.util.List;
030
031 import org.apache.commons.lang.StringUtils;
032 import org.kuali.kfs.gl.GeneralLedgerConstants;
033 import org.kuali.kfs.gl.batch.CorrectionProcessScrubberStep;
034 import org.kuali.kfs.gl.businessobject.CorrectionChangeGroup;
035 import org.kuali.kfs.gl.document.service.CorrectionDocumentService;
036 import org.kuali.kfs.gl.service.OriginEntryGroupService;
037 import org.kuali.kfs.sys.KFSConstants;
038 import org.kuali.kfs.sys.KFSPropertyConstants;
039 import org.kuali.kfs.sys.batch.BatchSpringContext;
040 import org.kuali.kfs.sys.batch.Step;
041 import org.kuali.kfs.sys.context.ProxyUtils;
042 import org.kuali.kfs.sys.context.SpringContext;
043 import org.kuali.kfs.sys.document.AmountTotaling;
044 import org.kuali.kfs.sys.document.FinancialSystemTransactionalDocumentBase;
045 import org.kuali.rice.kew.dto.DocumentRouteLevelChangeDTO;
046 import org.kuali.rice.kew.dto.DocumentRouteStatusChangeDTO;
047 import org.kuali.rice.kns.service.DateTimeService;
048 import org.kuali.rice.kns.util.KualiDecimal;
049 import org.kuali.rice.kns.util.ObjectUtils;
050 import org.springframework.aop.framework.Advised;
051 import org.springframework.aop.support.AopUtils;
052
053 /**
054 * The General Ledger Correction Document, a document that allows editing and processing of origin entry groups and the origin
055 * entries within them.
056 */
057 public class GeneralLedgerCorrectionProcessDocument extends FinancialSystemTransactionalDocumentBase implements AmountTotaling {
058 protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(GeneralLedgerCorrectionProcessDocument.class);
059
060 protected String correctionTypeCode; // CorrectionDocumentService.CORRECTION_TYPE_MANUAL or
061 protected boolean correctionSelection; // false if all input rows should be in the output, true if only selected rows should be
062 // in the output
063 protected boolean correctionFileDelete; // false if the file should be processed by scrubber, true if the file should not be
064 // processed by scrubber
065 protected Integer correctionRowCount; // Row count in output group
066 protected KualiDecimal correctionDebitTotalAmount; // Debit amount total in output group
067 protected KualiDecimal correctionCreditTotalAmount; // Credit amount total in output group
068 protected KualiDecimal correctionBudgetTotalAmount; // Budget amount total in output group
069 protected String correctionInputFileName; // input file name
070 protected String correctionOutputFileName; // output file name
071 protected String correctionScriptText; // Not used
072 protected Integer correctionChangeGroupNextLineNumber;
073
074 protected List<CorrectionChangeGroup> correctionChangeGroup;
075
076 public GeneralLedgerCorrectionProcessDocument() {
077 super();
078 correctionChangeGroupNextLineNumber = new Integer(0);
079
080 correctionChangeGroup = new ArrayList<CorrectionChangeGroup>();
081 }
082
083 /**
084 * Returns a Map representation of the primary key of this document
085 *
086 * @return a Map that represents the database key of this document
087 * @see org.kuali.rice.kns.bo.BusinessObjectBase#toStringMapper()
088 */
089 @Override
090 protected LinkedHashMap toStringMapper() {
091 LinkedHashMap m = new LinkedHashMap();
092 m.put(KFSPropertyConstants.DOCUMENT_NUMBER, this.documentNumber);
093 return m;
094 }
095
096 /**
097 * Returns the editing method to use on the origin entries in the document, either "Manual Edit," "Using Criteria," "Remove
098 * Group from Processing," or "Not Available"
099 *
100 * @return the String representation of the method this document is using
101 */
102 public String getMethod() {
103 if (CorrectionDocumentService.CORRECTION_TYPE_MANUAL.equals(correctionTypeCode)) {
104 return "Manual Edit";
105 }
106 else if (CorrectionDocumentService.CORRECTION_TYPE_CRITERIA.equals(correctionTypeCode)) {
107 return "Using Criteria";
108 }
109 else if (CorrectionDocumentService.CORRECTION_TYPE_REMOVE_GROUP_FROM_PROCESSING.equals(correctionTypeCode)) {
110 return "Remove Group from Processing";
111 }
112 else {
113 return KFSConstants.NOT_AVAILABLE_STRING;
114 }
115 }
116
117 /**
118 * Returns the source of the origin entries this document uses: either an uploaded file of origin entries or the database
119 *
120 * @return a String with the name of the system in use
121 */
122 public String getSystem() {
123 if (correctionInputFileName != null) {
124 return "File Upload";
125 }
126 else {
127 return "Database";
128 }
129 }
130
131 /**
132 * This method...
133 *
134 * @param ccg
135 */
136 public void addCorrectionChangeGroup(CorrectionChangeGroup ccg) {
137 ccg.setDocumentNumber(documentNumber);
138 ccg.setCorrectionChangeGroupLineNumber(correctionChangeGroupNextLineNumber++);
139 correctionChangeGroup.add(ccg);
140 }
141
142 /**
143 * This method...
144 *
145 * @param changeNumber
146 */
147 public void removeCorrectionChangeGroup(int changeNumber) {
148 for (Iterator iter = correctionChangeGroup.iterator(); iter.hasNext();) {
149 CorrectionChangeGroup element = (CorrectionChangeGroup) iter.next();
150 if (changeNumber == element.getCorrectionChangeGroupLineNumber().intValue()) {
151 iter.remove();
152 }
153 }
154 }
155
156 /**
157 * This method...
158 *
159 * @param groupNumber
160 * @return
161 */
162 public CorrectionChangeGroup getCorrectionChangeGroupItem(int groupNumber) {
163 for (Iterator iter = correctionChangeGroup.iterator(); iter.hasNext();) {
164 CorrectionChangeGroup element = (CorrectionChangeGroup) iter.next();
165 if (groupNumber == element.getCorrectionChangeGroupLineNumber().intValue()) {
166 return element;
167 }
168 }
169
170 CorrectionChangeGroup ccg = new CorrectionChangeGroup(documentNumber, groupNumber);
171 correctionChangeGroup.add(ccg);
172
173 return ccg;
174 }
175
176
177 public void doRouteStatusChange(DocumentRouteStatusChangeDTO statusChangeEvent) {
178 super.doRouteStatusChange(statusChangeEvent);
179 if (getDocumentHeader().getWorkflowDocument().stateIsProcessed()) {
180 if (LOG.isDebugEnabled()) {
181 LOG.debug("GLCP Route status Change: " + statusChangeEvent);
182 }
183 CorrectionDocumentService correctionDocumentService = SpringContext.getBean(CorrectionDocumentService.class);
184 OriginEntryGroupService originEntryGroupService = SpringContext.getBean(OriginEntryGroupService.class);
185
186 String docId = getDocumentHeader().getDocumentNumber();
187 GeneralLedgerCorrectionProcessDocument doc = correctionDocumentService.findByCorrectionDocumentHeaderId(docId);
188
189 String correctionType = doc.getCorrectionTypeCode();
190 if (CorrectionDocumentService.CORRECTION_TYPE_REMOVE_GROUP_FROM_PROCESSING.equals(correctionType)) {
191
192 String dataFileName = doc.getCorrectionInputFileName();
193 String doneFileName = dataFileName.replace(GeneralLedgerConstants.BatchFileSystem.EXTENSION, GeneralLedgerConstants.BatchFileSystem.DONE_FILE_EXTENSION);
194 originEntryGroupService.deleteFile(doneFileName);
195
196 }
197 else if (CorrectionDocumentService.CORRECTION_TYPE_MANUAL.equals(correctionType)
198 || CorrectionDocumentService.CORRECTION_TYPE_CRITERIA.equals(correctionType)) {
199 // KFSMI-5760 - apparently, this node can be executed more than once, which results in multiple
200 // files being created. We need to check for the existence of a file with the proper
201 // name pattern and abort the rest of this if found
202 synchronized ( CorrectionDocumentService.class ) {
203 if ( !checkForExistingOutputDocument( doc.getDocumentNumber() ) ) {
204 // save the output file to originEntry directory when correctionFileDelete is false
205 DateTimeService dateTimeService = SpringContext.getBean(DateTimeService.class);
206 Date today = dateTimeService.getCurrentDate();
207
208 // generate output file and set file name
209 String outputFileName = "";
210 if (!correctionFileDelete) {
211 outputFileName = correctionDocumentService.createOutputFileForProcessing(doc.getDocumentNumber(), today);
212 }
213 doc.setCorrectionOutputFileName(outputFileName);
214 Step step = BatchSpringContext.getStep(CorrectionProcessScrubberStep.STEP_NAME);
215 CorrectionProcessScrubberStep correctionStep = (CorrectionProcessScrubberStep) ProxyUtils.getTargetIfProxied(step);
216 correctionStep.setDocumentId(docId);
217
218 try {
219 step.execute(getClass().getName(), dateTimeService.getCurrentDate());
220 }
221 catch (Exception e) {
222 LOG.error("GLCP scrubber encountered error:", e);
223 throw new RuntimeException("GLCP scrubber encountered error:", e);
224 }
225
226 correctionStep = (CorrectionProcessScrubberStep) ProxyUtils.getTargetIfProxied(step);
227 correctionStep.setDocumentId(null);
228
229 correctionDocumentService.generateCorrectionReport(this);
230 correctionDocumentService.aggregateCorrectionDocumentReports(this);
231 } else {
232 LOG.warn( "Attempt to re-process final GLCP operations for document: " + doc.getDocumentNumber() + " File with that document number already exists." );
233 }
234 }
235 }
236 else {
237 LOG.error("GLCP doc " + doc.getDocumentNumber() + " has an unknown correction type code: " + correctionType);
238 }
239 }
240 }
241
242 /**
243 * Returns true if an existing document like "glcp_output.docNum" is found.
244 */
245 protected boolean checkForExistingOutputDocument( String documentNumber ) {
246 CorrectionDocumentService correctionDocumentService = SpringContext.getBean(CorrectionDocumentService.class);
247 String[] filenamesFound = correctionDocumentService.findExistingCorrectionOutputFilesForDocument(documentNumber);
248 if ( LOG.isInfoEnabled() ) {
249 LOG.info( "Scanned for output files for document: " + documentNumber );
250 LOG.info( "Files Found: " + Arrays.toString(filenamesFound));
251 }
252 return filenamesFound != null && filenamesFound.length > 0;
253 }
254
255
256 /**
257 * Waits for the event of the route level changing to "Approve" and at that point, saving all the entries as origin entries in a
258 * newly created origin entry group, then scrubbing those entries
259 *
260 * @param cahnge a representation of the route level changed that just occurred
261 * @see org.kuali.rice.kns.document.DocumentBase#handleRouteLevelChange(org.kuali.rice.kew.clientapp.vo.DocumentRouteLevelChangeDTO)
262 */
263 @Override
264 public void doRouteLevelChange(DocumentRouteLevelChangeDTO change) {
265 super.doRouteLevelChange(change);
266 }
267
268 /**
269 * Returns the total dollar amount associated with this document
270 *
271 * @return if credit total is zero, debit total, otherwise credit total
272 */
273 public KualiDecimal getTotalDollarAmount() {
274 return getCorrectionCreditTotalAmount().add(getCorrectionDebitTotalAmount());
275 }
276
277 /**
278 * Sets this document's document number, but also sets the document number on all children objects
279 *
280 * @param documentNumber the document number for this document
281 * @see org.kuali.rice.kns.document.DocumentBase#setDocumentNumber(java.lang.String)
282 */
283 @Override
284 public void setDocumentNumber(String documentNumber) {
285 super.setDocumentNumber(documentNumber);
286
287 for (Iterator iter = correctionChangeGroup.iterator(); iter.hasNext();) {
288 CorrectionChangeGroup element = (CorrectionChangeGroup) iter.next();
289 element.setDocumentNumber(documentNumber);
290 }
291 }
292
293 public String getCorrectionTypeCode() {
294 return correctionTypeCode;
295 }
296
297 public void setCorrectionTypeCode(String correctionTypeCode) {
298 this.correctionTypeCode = correctionTypeCode;
299 }
300
301 public boolean getCorrectionSelection() {
302 return correctionSelection;
303 }
304
305 public void setCorrectionSelection(boolean correctionSelection) {
306 this.correctionSelection = correctionSelection;
307 }
308
309 public boolean getCorrectionFileDelete() {
310 return correctionFileDelete;
311 }
312
313 public void setCorrectionFileDelete(boolean correctionFileDelete) {
314 this.correctionFileDelete = correctionFileDelete;
315 }
316
317 public Integer getCorrectionRowCount() {
318 return correctionRowCount;
319 }
320
321 public void setCorrectionRowCount(Integer correctionRowCount) {
322 this.correctionRowCount = correctionRowCount;
323 }
324
325 public Integer getCorrectionChangeGroupNextLineNumber() {
326 return correctionChangeGroupNextLineNumber;
327 }
328
329 public void setCorrectionChangeGroupNextLineNumber(Integer correctionChangeGroupNextLineNumber) {
330 this.correctionChangeGroupNextLineNumber = correctionChangeGroupNextLineNumber;
331 }
332
333 public KualiDecimal getCorrectionDebitTotalAmount() {
334 if (ObjectUtils.isNull(correctionDebitTotalAmount)) {
335 return KualiDecimal.ZERO;
336 }
337
338 return correctionDebitTotalAmount;
339 }
340
341 public void setCorrectionDebitTotalAmount(KualiDecimal correctionDebitTotalAmount) {
342 this.correctionDebitTotalAmount = correctionDebitTotalAmount;
343 }
344
345 public KualiDecimal getCorrectionCreditTotalAmount() {
346 if (ObjectUtils.isNull(correctionCreditTotalAmount)) {
347 return KualiDecimal.ZERO;
348 }
349
350 return correctionCreditTotalAmount;
351 }
352
353 public void setCorrectionCreditTotalAmount(KualiDecimal correctionCreditTotalAmount) {
354 this.correctionCreditTotalAmount = correctionCreditTotalAmount;
355 }
356
357 public KualiDecimal getCorrectionBudgetTotalAmount() {
358 return correctionBudgetTotalAmount;
359 }
360
361 public void setCorrectionBudgetTotalAmount(KualiDecimal correctionBudgetTotalAmount) {
362 this.correctionBudgetTotalAmount = correctionBudgetTotalAmount;
363 }
364
365 public String getCorrectionInputFileName() {
366 return correctionInputFileName;
367 }
368
369 public void setCorrectionInputFileName(String correctionInputFileName) {
370 this.correctionInputFileName = correctionInputFileName;
371 }
372
373 public String getCorrectionOutputFileName() {
374 return correctionOutputFileName;
375 }
376
377 public void setCorrectionOutputFileName(String correctionOutputFileName) {
378 this.correctionOutputFileName = correctionOutputFileName;
379 }
380
381 public List<CorrectionChangeGroup> getCorrectionChangeGroup() {
382 Collections.sort(correctionChangeGroup);
383 return correctionChangeGroup;
384 }
385
386 public void setCorrectionChangeGroup(List<CorrectionChangeGroup> correctionChangeGroup) {
387 this.correctionChangeGroup = correctionChangeGroup;
388 }
389
390 protected String buildFileExtensionWithDate(Date date) {
391 String dateFormatStr = ".yyyy-MMM-dd.HH-mm-ss";
392 DateFormat dateFormat = new SimpleDateFormat(dateFormatStr);
393
394 return dateFormat.format(date) + GeneralLedgerConstants.BatchFileSystem.EXTENSION;
395
396
397 }
398 }