001/* 002 * Copyright 2006 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 */ 016package org.kuali.ole.gl.batch.service.impl; 017 018import java.io.File; 019import java.io.IOException; 020import java.io.PrintStream; 021import java.sql.Date; 022import java.util.ArrayList; 023import java.util.Collection; 024import java.util.Iterator; 025import java.util.LinkedHashMap; 026import java.util.Map; 027 028import org.apache.commons.lang.StringUtils; 029import org.kuali.ole.gl.GeneralLedgerConstants; 030import org.kuali.ole.gl.businessobject.Entry; 031import org.kuali.ole.gl.businessobject.OriginEntryFull; 032import org.kuali.ole.gl.businessobject.OriginEntryInformation; 033import org.kuali.ole.gl.businessobject.PendingEntrySummary; 034import org.kuali.ole.gl.report.LedgerSummaryReport; 035import org.kuali.ole.gl.service.NightlyOutService; 036import org.kuali.ole.gl.service.OriginEntryGroupService; 037import org.kuali.ole.gl.service.OriginEntryService; 038import org.kuali.ole.sys.OLEConstants; 039import org.kuali.ole.sys.OLEPropertyConstants; 040import org.kuali.ole.sys.businessobject.GeneralLedgerPendingEntry; 041import org.kuali.ole.sys.service.GeneralLedgerPendingEntryService; 042import org.kuali.ole.sys.service.ReportWriterService; 043import org.kuali.rice.core.api.datetime.DateTimeService; 044import org.kuali.rice.core.api.util.type.KualiDecimal; 045import org.kuali.rice.core.web.format.CurrencyFormatter; 046import org.kuali.rice.kns.service.DataDictionaryService; 047import org.springframework.transaction.annotation.Transactional; 048 049/** 050 * This class implements the nightly out batch job. 051 */ 052@Transactional 053public class NightlyOutServiceImpl implements NightlyOutService { 054 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(NightlyOutServiceImpl.class); 055 056 private GeneralLedgerPendingEntryService generalLedgerPendingEntryService; 057 private OriginEntryService originEntryService; 058 private DateTimeService dateTimeService; 059 private OriginEntryGroupService originEntryGroupService; 060 private String batchFileDirectoryName; 061 private ReportWriterService pendingEntryListReportWriterService; 062 private ReportWriterService pendingEntrySummaryReportWriterService; 063 private DataDictionaryService dataDictionaryService; 064 065 /** 066 * Constructs a NightlyOutServiceImpl instance 067 */ 068 public NightlyOutServiceImpl() { 069 } 070 071 /** 072 * Deletes all the pending general ledger entries that have now been copied to origin entries 073 * @see org.kuali.ole.gl.service.NightlyOutService#deleteCopiedPendingLedgerEntries() 074 */ 075 public void deleteCopiedPendingLedgerEntries() { 076 LOG.debug("deleteCopiedPendingLedgerEntries() started"); 077 078 generalLedgerPendingEntryService.deleteByFinancialDocumentApprovedCode(OLEConstants.PENDING_ENTRY_APPROVED_STATUS_CODE.PROCESSED); 079 } 080 081 /** 082 * Copies the approved pending ledger entries to origin entry table and generates a report 083 * @see org.kuali.ole.gl.service.NightlyOutService#copyApprovedPendingLedgerEntries() 084 */ 085 public void copyApprovedPendingLedgerEntries() { 086 if (LOG.isInfoEnabled()) { 087 LOG.info("copyApprovedPendingLedgerEntries() started"); 088 } 089 Date today = new Date(dateTimeService.getCurrentTimestamp().getTime()); 090 091 Iterator pendingEntries = generalLedgerPendingEntryService.findApprovedPendingLedgerEntries(); 092 String outputFile = batchFileDirectoryName + File.separator + GeneralLedgerConstants.BatchFileSystem.NIGHTLY_OUT_FILE + GeneralLedgerConstants.BatchFileSystem.EXTENSION ; 093 PrintStream outputFilePs = null; 094 095 try { 096 outputFilePs = new PrintStream(outputFile); 097 } 098 catch (IOException ioe) { 099 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}