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.File;
19  import java.io.IOException;
20  import java.io.PrintStream;
21  import java.sql.Date;
22  import java.util.ArrayList;
23  import java.util.Collection;
24  import java.util.Iterator;
25  import java.util.LinkedHashMap;
26  import java.util.Map;
27  
28  import org.apache.commons.lang.StringUtils;
29  import org.kuali.ole.gl.GeneralLedgerConstants;
30  import org.kuali.ole.gl.businessobject.Entry;
31  import org.kuali.ole.gl.businessobject.OriginEntryFull;
32  import org.kuali.ole.gl.businessobject.OriginEntryInformation;
33  import org.kuali.ole.gl.businessobject.PendingEntrySummary;
34  import org.kuali.ole.gl.report.LedgerSummaryReport;
35  import org.kuali.ole.gl.service.NightlyOutService;
36  import org.kuali.ole.gl.service.OriginEntryGroupService;
37  import org.kuali.ole.gl.service.OriginEntryService;
38  import org.kuali.ole.sys.OLEConstants;
39  import org.kuali.ole.sys.OLEPropertyConstants;
40  import org.kuali.ole.sys.businessobject.GeneralLedgerPendingEntry;
41  import org.kuali.ole.sys.service.GeneralLedgerPendingEntryService;
42  import org.kuali.ole.sys.service.ReportWriterService;
43  import org.kuali.rice.core.api.datetime.DateTimeService;
44  import org.kuali.rice.core.api.util.type.KualiDecimal;
45  import org.kuali.rice.core.web.format.CurrencyFormatter;
46  import org.kuali.rice.kns.service.DataDictionaryService;
47  import org.springframework.transaction.annotation.Transactional;
48  
49  /**
50   * This class implements the nightly out batch job.
51   */
52  @Transactional
53  public class NightlyOutServiceImpl implements NightlyOutService {
54      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(NightlyOutServiceImpl.class);
55  
56      private GeneralLedgerPendingEntryService generalLedgerPendingEntryService;
57      private OriginEntryService originEntryService;
58      private DateTimeService dateTimeService;
59      private OriginEntryGroupService originEntryGroupService;
60      private String batchFileDirectoryName;
61      private ReportWriterService pendingEntryListReportWriterService;
62      private ReportWriterService pendingEntrySummaryReportWriterService;
63      private DataDictionaryService dataDictionaryService;
64      
65      /**
66       * Constructs a NightlyOutServiceImpl instance
67       */
68      public NightlyOutServiceImpl() {
69      }
70  
71      /**
72       * Deletes all the pending general ledger entries that have now been copied to origin entries
73       * @see org.kuali.ole.gl.service.NightlyOutService#deleteCopiedPendingLedgerEntries()
74       */
75      public void deleteCopiedPendingLedgerEntries() {
76          LOG.debug("deleteCopiedPendingLedgerEntries() started");
77  
78          generalLedgerPendingEntryService.deleteByFinancialDocumentApprovedCode(OLEConstants.PENDING_ENTRY_APPROVED_STATUS_CODE.PROCESSED);
79      }
80  
81      /**
82       * Copies the approved pending ledger entries to origin entry table and generates a report
83       * @see org.kuali.ole.gl.service.NightlyOutService#copyApprovedPendingLedgerEntries()
84       */
85      public void copyApprovedPendingLedgerEntries() {
86          if (LOG.isInfoEnabled()) {
87              LOG.info("copyApprovedPendingLedgerEntries() started");
88          }
89          Date today = new Date(dateTimeService.getCurrentTimestamp().getTime());
90          
91          Iterator pendingEntries = generalLedgerPendingEntryService.findApprovedPendingLedgerEntries();
92          String outputFile = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.NIGHTLY_OUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION ;
93          PrintStream outputFilePs = null;
94          
95          try {
96              outputFilePs  = new PrintStream(outputFile);
97          }
98          catch (IOException ioe) {
99              throw new RuntimeException("Cannot open output file "+outputFile+" for writing", ioe);
100         }
101         
102         EntryListReport entryListReport = new EntryListReport();
103         LedgerSummaryReport nightlyOutLedgerSummaryReport = new LedgerSummaryReport();
104         
105         Collection<OriginEntryFull> group = new ArrayList<OriginEntryFull>();
106         while (pendingEntries.hasNext()) {
107             // get one pending entry
108             GeneralLedgerPendingEntry pendingEntry = (GeneralLedgerPendingEntry) pendingEntries.next();
109             
110             OriginEntryFull entry = new OriginEntryFull(pendingEntry);
111             
112             // write entry to reports
113             entryListReport.writeEntry(entry, pendingEntryListReportWriterService);
114             nightlyOutLedgerSummaryReport.summarizeEntry(entry);
115 
116             group.add(entry);
117             
118             // copy the pending entry to text file
119                 outputFilePs.printf("%s\n", entry.getLine());
120 
121             // update the pending entry to indicate it has been copied
122             pendingEntry.setFinancialDocumentApprovedCode(OLEConstants.PENDING_ENTRY_APPROVED_STATUS_CODE.PROCESSED);
123             pendingEntry.setTransactionDate(today);
124             
125             generalLedgerPendingEntryService.save(pendingEntry);
126         }
127         
128         outputFilePs.close();
129         
130         //create done file    
131         String doneFileName = outputFile.replace(GeneralLedgerConstants.BatchFileSystem.EXTENSION, GeneralLedgerConstants.BatchFileSystem.DONE_FILE_EXTENSION);
132         File doneFile = new File (doneFileName);
133         if (!doneFile.exists()){
134             try {
135                 doneFile.createNewFile();
136             } catch (IOException e) {
137                 throw new RuntimeException();
138             }
139         }
140         
141         // finish writing reports
142         entryListReport.writeReportFooter(pendingEntryListReportWriterService);
143         nightlyOutLedgerSummaryReport.writeReport(pendingEntrySummaryReportWriterService);
144     }
145     
146     
147 
148     public void setGeneralLedgerPendingEntryService(GeneralLedgerPendingEntryService generalLedgerPendingEntryService) {
149         this.generalLedgerPendingEntryService = generalLedgerPendingEntryService;
150     }
151 
152     public void setOriginEntryService(OriginEntryService originEntryService) {
153         this.originEntryService = originEntryService;
154     }
155 
156     public void setOriginEntryGroupService(OriginEntryGroupService originEntryGroupService) {
157         this.originEntryGroupService = originEntryGroupService;
158     }
159 
160     public void setDateTimeService(DateTimeService dateTimeService) {
161         this.dateTimeService = dateTimeService;
162     }
163 
164     public void setBatchFileDirectoryName(String batchFileDirectoryName) {
165         this.batchFileDirectoryName = batchFileDirectoryName;
166     }
167     
168     /**
169      * Gets the pendingEntryListReportWriterService attribute. 
170      * @return Returns the pendingEntryListReportWriterService.
171      */
172     public ReportWriterService getPendingEntryListReportWriterService() {
173         return pendingEntryListReportWriterService;
174     }
175 
176     /**
177      * Sets the pendingEntryListReportWriterService attribute value.
178      * @param pendingEntryListReportWriterService The pendingEntryListReportWriterService to set.
179      */
180     public void setPendingEntryListReportWriterService(ReportWriterService pendingEntryListReportWriterService) {
181         this.pendingEntryListReportWriterService = pendingEntryListReportWriterService;
182     }
183 
184     /**
185      * Gets the pendingEntrySummaryReportWriterService attribute. 
186      * @return Returns the pendingEntrySummaryReportWriterService.
187      */
188     public ReportWriterService getPendingEntrySummaryReportWriterService() {
189         return pendingEntrySummaryReportWriterService;
190     }
191 
192     /**
193      * Sets the pendingEntrySummaryReportWriterService attribute value.
194      * @param pendingEntrySummaryReportWriterService The pendingEntrySummaryReportWriterService to set.
195      */
196     public void setPendingEntrySummaryReportWriterService(ReportWriterService pendingEntrySummaryReportWriterService) {
197         this.pendingEntrySummaryReportWriterService = pendingEntrySummaryReportWriterService;
198     }
199 
200     /**
201      * Gets the dataDictionaryService attribute. 
202      * @return Returns the dataDictionaryService.
203      */
204     public DataDictionaryService getDataDictionaryService() {
205         return dataDictionaryService;
206     }
207 
208     /**
209      * Sets the dataDictionaryService attribute value.
210      * @param dataDictionaryService The dataDictionaryService to set.
211      */
212     public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
213         this.dataDictionaryService = dataDictionaryService;
214     }
215 
216     /**
217      * A helper class which writes out the nightly out entry list report
218      */
219     protected class EntryListReport {
220         private PendingEntrySummary pendingEntrySummary;
221         private EntryReportTotalLine totalLine;
222         private Map<String, EntryReportDocumentTypeTotalLine> documentTypeTotals;
223         private EntryReportDocumentNumberTotalLine documentNumberTotal;
224         private int entryCount = 0;
225         private String suppressKey = "";
226         
227         /**
228          * Constructs a NightlyOutServiceImpl
229          */
230         public EntryListReport() {
231             pendingEntrySummary = new PendingEntrySummary();
232             totalLine = new EntryReportTotalLine();
233             documentTypeTotals = new LinkedHashMap<String, EntryReportDocumentTypeTotalLine>();
234         }
235         
236         /**
237          * Writes an entry to the list report
238          * @param entry the entry to write
239          * @param reportWriterService the reportWriterService to write the entry to
240          */
241         public void writeEntry(OriginEntryInformation entry, ReportWriterService reportWriterService) {
242             pendingEntrySummary.setOriginEntry(entry);
243             if (pendingEntrySummary.getSuppressableFieldsAsKey().equals(suppressKey)) {
244                 pendingEntrySummary.suppressCommonFields(true);
245             }
246             else if (StringUtils.isNotBlank(suppressKey)) {
247                 writeDocumentTotalLine(documentNumberTotal, reportWriterService);
248                 documentNumberTotal = new EntryReportDocumentNumberTotalLine(pendingEntrySummary.getConstantDocumentNumber());
249             }
250             
251             if (StringUtils.isBlank(suppressKey)) {
252                 documentNumberTotal = new EntryReportDocumentNumberTotalLine(pendingEntrySummary.getConstantDocumentNumber());
253                 reportWriterService.writeTableHeader(pendingEntrySummary);
254             }
255             suppressKey = pendingEntrySummary.getSuppressableFieldsAsKey();
256             
257             reportWriterService.writeTableRow(pendingEntrySummary);
258             
259             addPendingEntryToDocumentType(pendingEntrySummary, documentTypeTotals);
260             addSummaryToTotal(pendingEntrySummary, documentNumberTotal);
261             addSummaryToTotal(pendingEntrySummary, totalLine);
262             entryCount += 1;
263         }
264         
265         /**
266          * Adds the given pending entry summary to the appropriate doc type's line total
267          * @param pendingEntrySummary the pending entry summary to add
268          * @param docTypeTotals the Map of doc type line total helpers to add the summary to
269          */
270         protected void addPendingEntryToDocumentType(PendingEntrySummary pendingEntrySummary, Map<String, EntryReportDocumentTypeTotalLine> docTypeTotals) {
271             EntryReportDocumentTypeTotalLine docTypeTotal = docTypeTotals.get(pendingEntrySummary.getConstantDocumentTypeCode());
272             if (docTypeTotal == null) {
273                 docTypeTotal = new EntryReportDocumentTypeTotalLine(pendingEntrySummary.getConstantDocumentTypeCode());
274                 docTypeTotals.put(pendingEntrySummary.getConstantDocumentTypeCode(), docTypeTotal);
275             }
276             addSummaryToTotal(pendingEntrySummary, docTypeTotal);
277         }
278         
279         
280         /**
281          * Adds the given summary to the correct credit, debit, or budget total in the total line
282          * @param pendingEntrySummary the summary to add
283          * @param totalLine the entry report total line which holds debit, credit, and budget sum totals
284          */
285         protected void addSummaryToTotal(PendingEntrySummary pendingEntrySummary, EntryReportTotalLine totalLine) {
286             if (pendingEntrySummary.getDebitAmount() != null) {
287                 totalLine.addDebitAmount(pendingEntrySummary.getDebitAmount());
288             }
289             if (pendingEntrySummary.getCreditAmount() != null) {
290                 totalLine.addCreditAmount(pendingEntrySummary.getCreditAmount());
291             }
292             if (pendingEntrySummary.getBudgetAmount() != null) {
293                 totalLine.addBudgetAmount(pendingEntrySummary.getBudgetAmount());
294             }
295         }
296         
297         /**
298          * Writes totals for the document number we just finished writing out
299          * 
300          * @param documentNumberTotal EntryReportDocumentNumberTotalLine containing totals to write
301          * @param reportWriterService ReportWriterService for writing output to report
302          */
303         protected void writeDocumentTotalLine(EntryReportDocumentNumberTotalLine documentNumberTotal, ReportWriterService reportWriterService) {
304             final CurrencyFormatter formatter = new CurrencyFormatter();
305             final int amountLength = getDataDictionaryService().getAttributeMaxLength(Entry.class, OLEPropertyConstants.TRANSACTION_LEDGER_ENTRY_AMOUNT);
306             
307             reportWriterService.writeNewLines(1);
308             reportWriterService.writeFormattedMessageLine("                                          Total: %"+amountLength+"s %"+amountLength+"s %"+amountLength+"s", formatter.format(documentNumberTotal.getCreditAmount()), formatter.format(documentNumberTotal.getDebitAmount()), formatter.format(documentNumberTotal.getBudgetAmount()));
309             reportWriterService.writeNewLines(1);
310         }
311         
312         /**
313          * Completes the footer summary information for the report
314          * @param reportWriterService the reportWriterService to write the footer to
315          */
316         public void writeReportFooter(ReportWriterService reportWriterService) {
317             final CurrencyFormatter formatter = new CurrencyFormatter();
318             final int amountLength = getDataDictionaryService().getAttributeMaxLength(Entry.class, OLEPropertyConstants.TRANSACTION_LEDGER_ENTRY_AMOUNT);
319           
320             reportWriterService.writeNewLines(1);
321             for (String documentTypeCode : documentTypeTotals.keySet()) {
322                 final EntryReportDocumentTypeTotalLine docTypeTotal = documentTypeTotals.get(documentTypeCode);
323                 reportWriterService.writeFormattedMessageLine("       Totals for Document Type %4s Cnt %6d: %"+amountLength+"s %"+amountLength+"s %"+amountLength+"s",documentTypeCode, docTypeTotal.getEntryCount(), formatter.format(docTypeTotal.getCreditAmount()), formatter.format(docTypeTotal.getDebitAmount()), formatter.format(docTypeTotal.getBudgetAmount()));
324             }
325             
326             reportWriterService.writeNewLines(1);
327             reportWriterService.writeFormattedMessageLine("                        Grand Totals Cnt %6d: %"+amountLength+"s %"+amountLength+"s %"+amountLength+"s", new Integer(entryCount), formatter.format(totalLine.getCreditAmount()), formatter.format(totalLine.getDebitAmount()), formatter.format(totalLine.getBudgetAmount()));
328         }
329         
330         /**
331          * Summarizes entries for the pending entry view
332          */
333         protected class EntryReportTotalLine {
334             private KualiDecimal debitAmount = new KualiDecimal("0");
335             private KualiDecimal creditAmount = new KualiDecimal("0");
336             private KualiDecimal budgetAmount = new KualiDecimal("0");
337             
338             /**
339              * @return the debit total
340              */
341             public KualiDecimal getDebitAmount() {
342                 return debitAmount;
343             }
344             
345             /**
346              * @return the credit total
347              */
348             public KualiDecimal getCreditAmount() {
349                 return creditAmount;
350             }
351             
352             /**
353              * @return the budget total
354              */
355             public KualiDecimal getBudgetAmount() {
356                 return budgetAmount;
357             }
358             
359             /**
360              * Adds the given amount to the debit total
361              * @param debitAmount the amount to add to the debit total
362              */
363             public void addDebitAmount(KualiDecimal debitAmount) {
364                 this.debitAmount = this.debitAmount.add(debitAmount);
365             }
366             
367             /**
368              * Adds the given amount to the credit total
369              * @param creditAmount the amount to add to the credit total
370              */
371             public void addCreditAmount(KualiDecimal creditAmount) {
372                 this.creditAmount = this.creditAmount.add(creditAmount);
373             }
374             
375             /**
376              * Adds the given amount to the budget total
377              * @param budgetAmount the amount to add to the budget total
378              */
379             public void addBudgetAmount(KualiDecimal budgetAmount) {
380                 this.budgetAmount = this.budgetAmount.add(budgetAmount);
381             }
382         }
383         
384         /**
385          * Summarizes pending entry data per document type
386          */
387         protected class EntryReportDocumentTypeTotalLine extends EntryReportTotalLine {
388             private String documentTypeCode;
389             private int entryCount = 0;
390             
391             /**
392              * Constructs a NightlyOutServiceImpl
393              * @param documentTypeCode the document type code to 
394              */
395             public EntryReportDocumentTypeTotalLine(String documentTypeCode) {
396                 this.documentTypeCode = documentTypeCode;
397             }
398             
399             /**
400              * @return the document type associated with this summarizer
401              */
402             public String getDocumentTypeCode() {
403                 return this.documentTypeCode;
404             }
405             
406             /**
407              * @return the number of entries associated with the current document type
408              */
409             public int getEntryCount() {
410                 return this.entryCount;
411             }
412 
413             /**
414              * Overridden to automagically udpate the entry count
415              * @see org.kuali.ole.gl.batch.service.impl.NightlyOutServiceImpl.EntryReportTotalLine#addBudgetAmount(org.kuali.rice.core.api.util.type.KualiDecimal)
416              */
417             @Override
418             public void addBudgetAmount(KualiDecimal budgetAmount) {
419                 super.addBudgetAmount(budgetAmount);
420                 entryCount += 1;
421             }
422 
423             /**
424              * Overridden to automagically update the entry count
425              * @see org.kuali.ole.gl.batch.service.impl.NightlyOutServiceImpl.EntryReportTotalLine#addCreditAmount(org.kuali.rice.core.api.util.type.KualiDecimal)
426              */
427             @Override
428             public void addCreditAmount(KualiDecimal creditAmount) {
429                 super.addCreditAmount(creditAmount);
430                 entryCount += 1;
431             }
432 
433             /**
434              * Overridden to automagically update the entry count
435              * @see org.kuali.ole.gl.batch.service.impl.NightlyOutServiceImpl.EntryReportTotalLine#addDebitAmount(org.kuali.rice.core.api.util.type.KualiDecimal)
436              */
437             @Override
438             public void addDebitAmount(KualiDecimal debitAmount) {
439                 super.addDebitAmount(debitAmount);
440                 entryCount += 1;
441             }
442         }
443         
444         /**
445          * Summarizes pending entry data per document number
446          */
447         protected class EntryReportDocumentNumberTotalLine extends EntryReportTotalLine {
448             private String documentNumber;
449             private int entryCount = 0;
450             
451             /**
452              * Constructs a NightlyOutServiceImpl
453              * @param documentNumber the document number to total
454              */
455             public EntryReportDocumentNumberTotalLine(String documentNumber) {
456                 this.documentNumber = documentNumber;
457             }
458             
459             /**
460              * @return the document number associated with this summarizer
461              */
462             public String getDocumentNumber() {
463                 return this.documentNumber;
464             }
465             
466             /**
467              * @return the number of entries associated with the current document number
468              */
469             public int getEntryCount() {
470                 return this.entryCount;
471             }
472 
473             /**
474              * Overridden to automagically udpate the entry count
475              * @see org.kuali.ole.gl.batch.service.impl.NightlyOutServiceImpl.EntryReportTotalLine#addBudgetAmount(org.kuali.rice.core.api.util.type.KualiDecimal)
476              */
477             @Override
478             public void addBudgetAmount(KualiDecimal budgetAmount) {
479                 super.addBudgetAmount(budgetAmount);
480                 entryCount += 1;
481             }
482 
483             /**
484              * Overridden to automagically update the entry count
485              * @see org.kuali.ole.gl.batch.service.impl.NightlyOutServiceImpl.EntryReportTotalLine#addCreditAmount(org.kuali.rice.core.api.util.type.KualiDecimal)
486              */
487             @Override
488             public void addCreditAmount(KualiDecimal creditAmount) {
489                 super.addCreditAmount(creditAmount);
490                 entryCount += 1;
491             }
492 
493             /**
494              * Overridden to automagically update the entry count
495              * @see org.kuali.ole.gl.batch.service.impl.NightlyOutServiceImpl.EntryReportTotalLine#addDebitAmount(org.kuali.rice.core.api.util.type.KualiDecimal)
496              */
497             @Override
498             public void addDebitAmount(KualiDecimal debitAmount) {
499                 super.addDebitAmount(debitAmount);
500                 entryCount += 1;
501             }
502         }
503     }
504 }